codemaxxing 0.4.13 → 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") {
@@ -730,7 +762,23 @@ function App() {
730
762
  return;
731
763
  }
732
764
  if (trimmed === "/ollama delete") {
733
- // No model specified show picker of installed models
765
+ // Ensure Ollama is running so we can list models
766
+ let running = await isOllamaRunning();
767
+ if (!running) {
768
+ addMsg("info", "Starting Ollama to list models...");
769
+ startOllama();
770
+ for (let i = 0; i < 10; i++) {
771
+ await new Promise(r => setTimeout(r, 1000));
772
+ if (await isOllamaRunning()) {
773
+ running = true;
774
+ break;
775
+ }
776
+ }
777
+ if (!running) {
778
+ addMsg("error", "Could not start Ollama. Start it manually first.");
779
+ return;
780
+ }
781
+ }
734
782
  const models = await listInstalledModelsDetailed();
735
783
  if (models.length === 0) {
736
784
  addMsg("info", "No models installed.");
@@ -1021,7 +1069,7 @@ function App() {
1021
1069
  }
1022
1070
  setLoading(false);
1023
1071
  setStreaming(false);
1024
- }, [agent, exit]);
1072
+ }, [agent, exit, refreshConnectionBanner]);
1025
1073
  useInput((inputChar, key) => {
1026
1074
  // Handle slash command navigation
1027
1075
  if (showSuggestionsRef.current) {
@@ -1351,7 +1399,7 @@ function App() {
1351
1399
  const pullModels = [
1352
1400
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
1353
1401
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
1354
- { 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" },
1355
1403
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium quality, needs 48GB+" },
1356
1404
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
1357
1405
  { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
@@ -1915,7 +1963,7 @@ function App() {
1915
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: "" }), [
1916
1964
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
1917
1965
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
1918
- { 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" },
1919
1967
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
1920
1968
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
1921
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.13",
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") {
@@ -770,7 +803,20 @@ function App() {
770
803
  return;
771
804
  }
772
805
  if (trimmed === "/ollama delete") {
773
- // No model specified show picker of installed models
806
+ // Ensure Ollama is running so we can list models
807
+ let running = await isOllamaRunning();
808
+ if (!running) {
809
+ addMsg("info", "Starting Ollama to list models...");
810
+ startOllama();
811
+ for (let i = 0; i < 10; i++) {
812
+ await new Promise(r => setTimeout(r, 1000));
813
+ if (await isOllamaRunning()) { running = true; break; }
814
+ }
815
+ if (!running) {
816
+ addMsg("error", "Could not start Ollama. Start it manually first.");
817
+ return;
818
+ }
819
+ }
774
820
  const models = await listInstalledModelsDetailed();
775
821
  if (models.length === 0) {
776
822
  addMsg("info", "No models installed.");
@@ -1058,7 +1104,7 @@ function App() {
1058
1104
 
1059
1105
  setLoading(false);
1060
1106
  setStreaming(false);
1061
- }, [agent, exit]);
1107
+ }, [agent, exit, refreshConnectionBanner]);
1062
1108
 
1063
1109
  useInput((inputChar, key) => {
1064
1110
  // Handle slash command navigation
@@ -1374,7 +1420,7 @@ function App() {
1374
1420
  const pullModels = [
1375
1421
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
1376
1422
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
1377
- { 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" },
1378
1424
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium quality, needs 48GB+" },
1379
1425
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
1380
1426
  { id: "codellama:7b", name: "CodeLlama 7B", size: "4 GB", desc: "Meta's coding model" },
@@ -2191,7 +2237,7 @@ function App() {
2191
2237
  {[
2192
2238
  { id: "qwen2.5-coder:7b", name: "Qwen 2.5 Coder 7B", size: "5 GB", desc: "Best balance of speed & quality" },
2193
2239
  { id: "qwen2.5-coder:14b", name: "Qwen 2.5 Coder 14B", size: "9 GB", desc: "Higher quality, needs 16GB+ RAM" },
2194
- { 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" },
2195
2241
  { id: "qwen2.5-coder:32b", name: "Qwen 2.5 Coder 32B", size: "20 GB", desc: "Premium, needs 48GB+" },
2196
2242
  { id: "deepseek-coder-v2:16b", name: "DeepSeek Coder V2", size: "9 GB", desc: "Strong alternative" },
2197
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}` };