codemaxxing 0.4.14 → 0.4.15

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
@@ -46,6 +46,11 @@ curl -fsSL -o $env:TEMP\install-codemaxxing.bat https://raw.githubusercontent.co
46
46
  npm update -g codemaxxing
47
47
  ```
48
48
 
49
+ If that doesn't get the latest version:
50
+ ```bash
51
+ npm install -g codemaxxing@latest
52
+ ```
53
+
49
54
  ## Quick Start
50
55
 
51
56
  ### 1. Start Your LLM
package/dist/index.js CHANGED
@@ -175,6 +175,35 @@ function App() {
175
175
  pasteEvents.on("paste", handler);
176
176
  return () => { pasteEvents.off("paste", handler); };
177
177
  }, []);
178
+ // Refresh the connection banner to reflect current provider status
179
+ const refreshConnectionBanner = useCallback(async () => {
180
+ const info = [];
181
+ const cliArgs = parseCLIArgs();
182
+ const rawConfig = loadConfig();
183
+ const config = applyOverrides(rawConfig, cliArgs);
184
+ const provider = config.provider;
185
+ if (provider.model === "auto" || (provider.baseUrl === "http://localhost:1234/v1" && !cliArgs.baseUrl)) {
186
+ const detected = await detectLocalProvider();
187
+ if (detected) {
188
+ info.push(`✔ Connected to ${detected.baseUrl} → ${detected.model}`);
189
+ }
190
+ else {
191
+ const ollamaUp = await isOllamaRunning();
192
+ info.push(ollamaUp ? "Ollama running (no model loaded)" : "✗ No local LLM server found");
193
+ }
194
+ }
195
+ else {
196
+ info.push(`Provider: ${provider.baseUrl}`);
197
+ info.push(`Model: ${provider.model}`);
198
+ }
199
+ const cwd = process.cwd();
200
+ if (isGitRepo(cwd)) {
201
+ const branch = getBranch(cwd);
202
+ const status = getStatus(cwd);
203
+ info.push(`Git: ${branch} (${status})`);
204
+ }
205
+ setConnectionInfo(info);
206
+ }, []);
178
207
  // Connect/reconnect to LLM provider
179
208
  const connectToProvider = useCallback(async (isRetry = false) => {
180
209
  const cliArgs = parseCLIArgs();
@@ -664,6 +693,7 @@ function App() {
664
693
  await new Promise(r => setTimeout(r, 1000));
665
694
  if (await isOllamaRunning()) {
666
695
  addMsg("info", "Ollama is running.");
696
+ await refreshConnectionBanner();
667
697
  return;
668
698
  }
669
699
  }
@@ -679,6 +709,8 @@ function App() {
679
709
  addMsg("info", "Stopping Ollama...");
680
710
  const result = await stopOllama();
681
711
  addMsg(result.ok ? "info" : "error", result.ok ? `\u2705 ${result.message}` : `\u274C ${result.message}`);
712
+ if (result.ok)
713
+ await refreshConnectionBanner();
682
714
  return;
683
715
  }
684
716
  if (trimmed === "/ollama pull") {
@@ -1037,7 +1069,7 @@ function App() {
1037
1069
  }
1038
1070
  setLoading(false);
1039
1071
  setStreaming(false);
1040
- }, [agent, exit]);
1072
+ }, [agent, exit, refreshConnectionBanner]);
1041
1073
  useInput((inputChar, key) => {
1042
1074
  // Handle slash command navigation
1043
1075
  if (showSuggestionsRef.current) {
@@ -1367,7 +1399,7 @@ function App() {
1367
1399
  const pullModels = [
1368
1400
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
1369
1401
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
1370
- { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "Fast but limited quality" },
1402
+ { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
1371
1403
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium quality, needs 48GB+" },
1372
1404
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
1373
1405
  { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
@@ -1931,7 +1963,7 @@ function App() {
1931
1963
  })(), themePicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Choose a theme:" }), listThemes().map((key, i) => (_jsxs(Text, { children: [i === themePickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === themePickerIndex ? theme.colors.suggestion : theme.colors.primary, bold: true, children: key }), _jsxs(Text, { color: theme.colors.muted, children: [" — ", THEMES[key].description] }), key === theme.name.toLowerCase() ? _jsx(Text, { color: theme.colors.muted, children: " (current)" }) : null] }, key))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), sessionPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.secondary, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Resume a session:" }), sessionPicker.map((s, i) => (_jsxs(Text, { children: [i === sessionPickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === sessionPickerIndex ? theme.colors.suggestion : theme.colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), deleteSessionPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.error, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.error, children: "Delete a session:" }), deleteSessionPicker.map((s, i) => (_jsxs(Text, { children: [i === deleteSessionPickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === deleteSessionPickerIndex ? theme.colors.suggestion : theme.colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), deleteSessionConfirm && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.warning, paddingX: 1, marginBottom: 0, children: [_jsxs(Text, { bold: true, color: theme.colors.warning, children: ["Delete session ", deleteSessionConfirm.id, "?"] }), _jsxs(Text, { color: theme.colors.muted, children: [" ", deleteSessionConfirm.display] }), _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.error, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: theme.colors.success, bold: true, children: "[n]" }), _jsx(Text, { children: "o" })] })] })), ollamaDeletePicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Delete which model?" }), _jsx(Text, { children: "" }), ollamaDeletePicker.models.map((m, i) => (_jsxs(Text, { children: [" ", i === ollamaDeletePickerIndex ? _jsx(Text, { color: theme.colors.primary, bold: true, children: "▸ " }) : " ", _jsx(Text, { color: i === ollamaDeletePickerIndex ? theme.colors.primary : undefined, children: m.name }), _jsxs(Text, { color: theme.colors.muted, children: [" (", (m.size / (1024 * 1024 * 1024)).toFixed(1), " GB)"] })] }, m.name))), _jsx(Text, { children: "" }), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter to delete · Esc cancel" })] })), ollamaPullPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Download which model?" }), _jsx(Text, { children: "" }), [
1932
1964
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
1933
1965
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
1934
- { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "Fast but limited quality" },
1966
+ { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
1935
1967
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
1936
1968
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
1937
1969
  { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
@@ -7,7 +7,7 @@ export interface RecommendedModel {
7
7
  vramOptimal: number;
8
8
  description: string;
9
9
  speed: string;
10
- quality: "good" | "great" | "best";
10
+ quality: "limited" | "good" | "great" | "best";
11
11
  }
12
12
  export type ModelFit = "perfect" | "good" | "tight" | "skip";
13
13
  export interface ScoredModel extends RecommendedModel {
@@ -6,9 +6,9 @@ const MODELS = [
6
6
  size: 2,
7
7
  ramRequired: 8,
8
8
  vramOptimal: 4,
9
- description: "Lightweight, fast coding model",
9
+ description: "\u26A0\uFE0F May not support tool calling well",
10
10
  speed: "~60 tok/s on M1",
11
- quality: "good",
11
+ quality: "limited",
12
12
  },
13
13
  {
14
14
  name: "Qwen 2.5 Coder 7B",
@@ -84,7 +84,7 @@ function scoreModel(model, ramGB, vramGB) {
84
84
  return "tight";
85
85
  return "skip";
86
86
  }
87
- const qualityOrder = { best: 3, great: 2, good: 1 };
87
+ const qualityOrder = { best: 3, great: 2, good: 1, limited: 0 };
88
88
  const fitOrder = { perfect: 4, good: 3, tight: 2, skip: 1 };
89
89
  export function getRecommendations(hardware) {
90
90
  const ramGB = hardware.ram / (1024 * 1024 * 1024);
@@ -136,7 +136,9 @@ function mapLlmfitQuality(params_b) {
136
136
  return "best";
137
137
  if (params_b >= 7)
138
138
  return "great";
139
- return "good";
139
+ if (params_b >= 5)
140
+ return "good";
141
+ return "limited";
140
142
  }
141
143
  /** Get recommendations using llmfit if available, otherwise fall back to hardcoded list */
142
144
  export function getRecommendationsWithLlmfit(hardware) {
@@ -296,7 +296,8 @@ export async function stopOllama() {
296
296
  /** Delete a model from disk */
297
297
  export function deleteModel(modelId) {
298
298
  try {
299
- execSync(`ollama rm ${modelId}`, { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
299
+ const bin = findOllamaBinary();
300
+ execSync(`"${bin}" rm ${modelId}`, { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
300
301
  return { ok: true, message: `Deleted ${modelId}` };
301
302
  }
302
303
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemaxxing",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "Open-source terminal coding agent. Connect any LLM. Max your code.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.tsx CHANGED
@@ -215,6 +215,37 @@ function App() {
215
215
  return () => { pasteEvents.off("paste", handler); };
216
216
  }, []);
217
217
 
218
+ // Refresh the connection banner to reflect current provider status
219
+ const refreshConnectionBanner = useCallback(async () => {
220
+ const info: string[] = [];
221
+ const cliArgs = parseCLIArgs();
222
+ const rawConfig = loadConfig();
223
+ const config = applyOverrides(rawConfig, cliArgs);
224
+ const provider = config.provider;
225
+
226
+ if (provider.model === "auto" || (provider.baseUrl === "http://localhost:1234/v1" && !cliArgs.baseUrl)) {
227
+ const detected = await detectLocalProvider();
228
+ if (detected) {
229
+ info.push(`✔ Connected to ${detected.baseUrl} → ${detected.model}`);
230
+ } else {
231
+ const ollamaUp = await isOllamaRunning();
232
+ info.push(ollamaUp ? "Ollama running (no model loaded)" : "✗ No local LLM server found");
233
+ }
234
+ } else {
235
+ info.push(`Provider: ${provider.baseUrl}`);
236
+ info.push(`Model: ${provider.model}`);
237
+ }
238
+
239
+ const cwd = process.cwd();
240
+ if (isGitRepo(cwd)) {
241
+ const branch = getBranch(cwd);
242
+ const status = getStatus(cwd);
243
+ info.push(`Git: ${branch} (${status})`);
244
+ }
245
+
246
+ setConnectionInfo(info);
247
+ }, []);
248
+
218
249
  // Connect/reconnect to LLM provider
219
250
  const connectToProvider = useCallback(async (isRetry = false) => {
220
251
  const cliArgs = parseCLIArgs();
@@ -708,6 +739,7 @@ function App() {
708
739
  await new Promise(r => setTimeout(r, 1000));
709
740
  if (await isOllamaRunning()) {
710
741
  addMsg("info", "Ollama is running.");
742
+ await refreshConnectionBanner();
711
743
  return;
712
744
  }
713
745
  }
@@ -723,6 +755,7 @@ function App() {
723
755
  addMsg("info", "Stopping Ollama...");
724
756
  const result = await stopOllama();
725
757
  addMsg(result.ok ? "info" : "error", result.ok ? `\u2705 ${result.message}` : `\u274C ${result.message}`);
758
+ if (result.ok) await refreshConnectionBanner();
726
759
  return;
727
760
  }
728
761
  if (trimmed === "/ollama pull") {
@@ -1071,7 +1104,7 @@ function App() {
1071
1104
 
1072
1105
  setLoading(false);
1073
1106
  setStreaming(false);
1074
- }, [agent, exit]);
1107
+ }, [agent, exit, refreshConnectionBanner]);
1075
1108
 
1076
1109
  useInput((inputChar, key) => {
1077
1110
  // Handle slash command navigation
@@ -1387,7 +1420,7 @@ function App() {
1387
1420
  const pullModels = [
1388
1421
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
1389
1422
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
1390
- { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "Fast but limited quality" },
1423
+ { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
1391
1424
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium quality, needs 48GB+" },
1392
1425
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
1393
1426
  { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
@@ -2204,7 +2237,7 @@ function App() {
2204
2237
  {[
2205
2238
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
2206
2239
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
2207
- { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "Fast but limited quality" },
2240
+ { id: "qwen2.5-coder:3b", name: "Qwen 2.5 Coder 3B", size: "2 GB", desc: "\u26A0\uFE0F Basic \u2014 may struggle with tool calls" },
2208
2241
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
2209
2242
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
2210
2243
  { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
@@ -9,7 +9,7 @@ export interface RecommendedModel {
9
9
  vramOptimal: number; // Optimal VRAM in GB (0 = CPU fine)
10
10
  description: string; // One-liner
11
11
  speed: string; // e.g., "~45 tok/s on M1"
12
- quality: "good" | "great" | "best";
12
+ quality: "limited" | "good" | "great" | "best";
13
13
  }
14
14
 
15
15
  export type ModelFit = "perfect" | "good" | "tight" | "skip";
@@ -25,9 +25,9 @@ const MODELS: RecommendedModel[] = [
25
25
  size: 2,
26
26
  ramRequired: 8,
27
27
  vramOptimal: 4,
28
- description: "Lightweight, fast coding model",
28
+ description: "\u26A0\uFE0F May not support tool calling well",
29
29
  speed: "~60 tok/s on M1",
30
- quality: "good",
30
+ quality: "limited",
31
31
  },
32
32
  {
33
33
  name: "Qwen 2.5 Coder 7B",
@@ -103,7 +103,7 @@ function scoreModel(model: RecommendedModel, ramGB: number, vramGB: number): Mod
103
103
  return "skip";
104
104
  }
105
105
 
106
- const qualityOrder: Record<string, number> = { best: 3, great: 2, good: 1 };
106
+ const qualityOrder: Record<string, number> = { best: 3, great: 2, good: 1, limited: 0 };
107
107
  const fitOrder: Record<string, number> = { perfect: 4, good: 3, tight: 2, skip: 1 };
108
108
 
109
109
  export function getRecommendations(hardware: HardwareInfo): ScoredModel[] {
@@ -168,10 +168,11 @@ function mapLlmfitFit(fit: string): ModelFit {
168
168
  }
169
169
  }
170
170
 
171
- function mapLlmfitQuality(params_b: number): "good" | "great" | "best" {
171
+ function mapLlmfitQuality(params_b: number): "limited" | "good" | "great" | "best" {
172
172
  if (params_b >= 14) return "best";
173
173
  if (params_b >= 7) return "great";
174
- return "good";
174
+ if (params_b >= 5) return "good";
175
+ return "limited";
175
176
  }
176
177
 
177
178
  /** Get recommendations using llmfit if available, otherwise fall back to hardcoded list */
@@ -319,7 +319,8 @@ export async function stopOllama(): Promise<{ ok: boolean; message: string }> {
319
319
  /** Delete a model from disk */
320
320
  export function deleteModel(modelId: string): { ok: boolean; message: string } {
321
321
  try {
322
- execSync(`ollama rm ${modelId}`, { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
322
+ const bin = findOllamaBinary();
323
+ execSync(`"${bin}" rm ${modelId}`, { stdio: ["pipe", "pipe", "pipe"], timeout: 30000 });
323
324
  return { ok: true, message: `Deleted ${modelId}` };
324
325
  } catch (err: any) {
325
326
  return { ok: false, message: `Failed to delete ${modelId}: ${err.stderr?.toString().trim() || err.message}` };