free-coding-models 0.3.19 → 0.3.22

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/CHANGELOG.md CHANGED
@@ -1,7 +1,50 @@
1
1
  # Changelog
2
-
3
2
  ---
4
3
 
4
+ ## 0.3.22
5
+
6
+ ### Added
7
+ - **Hierarchical command palette navigation**: Ctrl+P now opens an improved command palette with expandable/collapsible categories and subcategories for better organization.
8
+ - **Arrow key navigation in command palette**: Use left/right arrows to expand and collapse categories, up/down to navigate, Enter to execute or toggle.
9
+ - **Rich color scheme for command palette**: Categories use bold blue headers, subcategories use bold text, commands show keyboard shortcuts.
10
+ - **Visual expand/collapse indicators**: ▼ shows expanded categories, ▶ shows collapsed, • marks individual commands.
11
+ - **Concise default view**: Categories are collapsed by default (except Filters) showing less detail on first open.
12
+ - **Colored tier filters**: Tier filters (S+, S, A+, A, A-, B+, B, C) now display with their corresponding TUI colors for quick identification.
13
+ - **Specific provider filters**: Added 13 individual provider filters (NVIDIA NIM, Groq, Cerebras, SambaNova, OpenRouter, Together AI, DeepInfra, Fireworks, Hyperbolic, Google AI, Hugging Face) with their signature provider colors.
14
+ - **Filter by model category**: New "Filter by model" category with autocomplete that dynamically shows top 20 visible models, complete with provider icons and colored model names.
15
+ - **Improved search cursor**: Cursor placement fixed to appear right after the `>` prompt instead of after placeholder text for natural typing flow.
16
+ - **Lightning bolt in title**: Command Palette title now shows ⚡ emoji for better visibility and visual appeal.
17
+
18
+ ### Changed
19
+ - **Footer layout cleanup**: Added consistent spacing before `F Toggle Favorite`, moved `N Changelog • Ctrl+C Exit` to a dedicated final footer line for better readability.
20
+ - **Removed Y key binding**: Install Endpoints is no longer a direct hotkey — accessible only via Settings (`P`) or Command Palette (`Ctrl+P`). The `Y` key is now free/unbound.
21
+ - **Removed proxy notice from footer**: The "Proxy is temporarily disabled" banner has been fully retired from the TUI footer and its backing constant removed.
22
+ - **Command palette fuzzy search fix**: `buildCommandPaletteEntries()` now expands all categories so child commands are visible to fuzzy search.
23
+ - **Flat Pages + Actions in ⚡️ Command Palette**: page entries and action entries are now listed directly at root level (no extra submenu depth) for faster access.
24
+ - **Command explanations after shortcuts**: command palette rows now show concise English hints after hotkeys (example: `(Z) — Change target AI Coding CLI Tool.`).
25
+ - **⚡️ emoji consistency**: user-facing Command Palette mentions now consistently render as `⚡️ Command Palette` in TUI and docs.
26
+ - **README Quick Start flow**: install section now explicitly adds “create a free account on one of the providers” with a direct anchor link.
27
+ - **Provider section renamed for clarity**: Quick Start provider table now lives under `List of Free AI Providers`.
28
+ - **Quick Start CTA highlight**: added a large green `USE ⚡️ COMMAND PALETTE / CTRL+P` badge right after the command palette instruction.
29
+
30
+ ### Fixed
31
+ - **Double emoji display bug**: Fixed duplicate emoji icons in command palette entries (was showing `▶ 🔍 🔍 Filters` instead of `▶ 🔍 Filters`).
32
+ - **Provider cycle command behavior**: `Cycle provider` in ⚡️ Command Palette now correctly cycles providers again instead of resetting to `All`.
33
+
34
+ ## 0.3.21
35
+
36
+ ### Changed
37
+ - **Responsive table layout**: the TUI now adapts to narrow terminals by progressively shrinking columns and hiding least-useful ones. Compact mode shortens headers (`Lat. P`, `Avg. P`, `StaB.`, `PrOD…`) and tightens Provider (10 cols), Health (13 cols), Latest Ping (10 cols), and Avg Ping (8 cols). Below compact, Rank → Up% → Tier → Stability are hidden one by one. Minimum usable width: ~116 cols.
38
+ - **SWE% column tightened**: reduced from 9 to 6 columns wide — trims excess right padding without losing data.
39
+ - **Provider truncation in compact mode**: provider names longer than 5 chars are truncated to 4 chars + `…` (e.g. `Cere…`).
40
+ - **Health truncation in compact mode**: status text longer than 6 chars is truncated with `…` (e.g. `🔥 429 TR…`).
41
+ - **Removed "Used" column from TUI**: the token usage column has been removed from the main table as it was outdated.
42
+ - **Width guardrail threshold tightened**: the narrow-terminal warning now triggers only below 80 columns (instead of broader widths) and keeps the same 2-second auto-hide behavior.
43
+ - **Width warning is always enforced in narrow terminals**: removed the `Small Width Warnings` Settings toggle and the `--disable-widths-warning` runtime flag so the startup guardrail stays consistent.
44
+
45
+ ### Fixed
46
+ - **`--premium` filter lock-in**: premium now applies a resettable startup preset (S-tier + verdict sort) instead of hard-locking an extra hidden elite-only filter.
47
+
5
48
  ## 0.3.19
6
49
 
7
50
  ### Added
package/README.md CHANGED
@@ -22,12 +22,14 @@ npm install -g free-coding-models
22
22
  free-coding-models
23
23
  ```
24
24
 
25
+ create a free account on one of the [providers](#-list-of-free-ai-providers)
26
+
25
27
  </p>
26
28
 
27
29
  <p align="center">
28
30
  <a href="#-why-this-tool">Why</a> •
29
31
  <a href="#-quick-start">Quick Start</a> •
30
- <a href="#-providers">Providers</a> •
32
+ <a href="#-list-of-free-ai-providers">Providers</a> •
31
33
  <a href="#-usage">Usage</a> •
32
34
  <a href="#-tui-keys">TUI Keys</a> •
33
35
  <a href="#-contributing">Contributing</a>
@@ -55,7 +57,9 @@ It then writes the model you pick directly into your coding tool's config — so
55
57
 
56
58
  ## ⚡ Quick Start
57
59
 
58
- **① Get a free API key** — you only need one to get started:
60
+ ### 🟢 List of Free AI Providers
61
+
62
+ Create a free account on one provider below to get started:
59
63
 
60
64
  **160 coding models** across 20 providers, ranked by [SWE-bench Verified](https://www.swebench.com).
61
65
 
@@ -94,7 +98,7 @@ It then writes the model you pick directly into your coding tool's config — so
94
98
  | **A-/B+** | 30–40% | Smaller tasks, constrained infra |
95
99
  | **B/C** | < 30% | Code completion, edge/minimal setups |
96
100
 
97
- **② Install and run:**
101
+ **① Install and run:**
98
102
 
99
103
  ```bash
100
104
  npm install -g free-coding-models
@@ -103,9 +107,15 @@ free-coding-models
103
107
 
104
108
  On first run, you'll be prompted to enter your API key(s). You can skip providers and add more later with **`P`**.
105
109
 
110
+ Use ⚡️ Command Palette! with **Ctrl+P**.
111
+
112
+ <p align="center">
113
+ <img src="https://img.shields.io/badge/USE_%E2%9A%A1%EF%B8%8F%20COMMAND%20PALETTE-CTRL%2BP-22c55e?style=for-the-badge" alt="Use ⚡️ Command Palette with Ctrl+P">
114
+ </p>
115
+
106
116
  Need to fix contrast because your terminal theme is fighting the TUI? Press **`G`** at any time to cycle **Auto → Dark → Light**. The switch recolors the full interface live: table, Settings, Help, Smart Recommend, Feedback, and Changelog.
107
117
 
108
- **③ Pick a model and launch your tool:**
118
+ **② Pick a model and launch your tool:**
109
119
 
110
120
  ```
111
121
  ↑↓ navigate → Enter to launch
@@ -133,7 +143,7 @@ free-coding-models --goose --tier S
133
143
  # "I want NVIDIA's top models only"
134
144
  free-coding-models --origin nvidia --tier S
135
145
 
136
- # "Show me only elite models that are currently healthy"
146
+ # "Start with an elite-focused preset, then adjust filters live"
137
147
  free-coding-models --premium
138
148
 
139
149
  # "I want to script this — give me JSON"
@@ -176,10 +186,9 @@ Press **`Z`** in the TUI to cycle between tools without restarting.
176
186
  | `E` | Toggle configured-only mode |
177
187
  | `F` | Favorite / unfavorite model |
178
188
  | `G` | Cycle global theme (`Auto → Dark → Light`) |
179
- | `Ctrl+P` | Open command palette (search + run actions) |
189
+ | `Ctrl+P` | Open ⚡️ command palette (search + run actions) |
180
190
  | `R/S/C/M/O/L/A/H/V/B/U` | Sort columns |
181
191
  | `P` | Settings (API keys, providers, updates, theme) |
182
- | `Y` | Install Endpoints (push provider into tool config) |
183
192
  | `Q` | Smart Recommend overlay |
184
193
  | `N` | Changelog |
185
194
  | `W` | Cycle ping cadence |
@@ -201,7 +210,7 @@ Press **`Z`** in the TUI to cycle between tools without restarting.
201
210
  - **Configured-only default** — only shows providers you have keys for
202
211
  - **Keyless latency** — models ping even without an API key (show 🔑 NO KEY)
203
212
  - **Smart Recommend** — questionnaire picks the best model for your task type
204
- - **Command Palette** — `Ctrl+P` opens a searchable action launcher for filters, sorting, overlays, and quick toggles
213
+ - **⚡️ Command Palette** — `Ctrl+P` opens a searchable action launcher for filters, sorting, overlays, and quick toggles
205
214
  - **Install Endpoints** — push a full provider catalog into any tool's config (`Y`)
206
215
  - **Missing tool bootstrap** — detect absent CLIs, offer one-click install, then continue the selected launch automatically
207
216
  - **Width guardrail** — shows a warning instead of a broken table in narrow terminals
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.3.19",
3
+ "version": "0.3.22",
4
4
  "description": "Find the fastest coding LLM models in seconds — ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
5
5
  "keywords": [
6
6
  "nvidia",
package/src/app.js CHANGED
@@ -105,7 +105,7 @@ import { usageForRow as _usageForRow } from '../src/usage-reader.js'
105
105
  import { buildProviderModelTokenKey, loadTokenUsageByProviderModel } from '../src/token-usage-reader.js'
106
106
  import { parseOpenRouterResponse, fetchProviderQuota as _fetchProviderQuotaFromModule } from '../src/provider-quota-fetchers.js'
107
107
  import { isKnownQuotaTelemetry } from '../src/quota-capabilities.js'
108
- import { ALT_ENTER, ALT_LEAVE, ALT_HOME, PING_TIMEOUT, PING_INTERVAL, FPS, COL_MODEL, COL_MS, CELL_W, FRAMES, TIER_CYCLE, SETTINGS_OVERLAY_BG, HELP_OVERLAY_BG, RECOMMEND_OVERLAY_BG, OVERLAY_PANEL_WIDTH, TABLE_HEADER_LINES, TABLE_FOOTER_LINES, TABLE_FIXED_LINES, msCell, spinCell } from '../src/constants.js'
108
+ import { ALT_ENTER, ALT_LEAVE, ALT_HOME, PING_TIMEOUT, PING_INTERVAL, FPS, COL_MODEL, COL_MS, CELL_W, FRAMES, TIER_CYCLE, SETTINGS_OVERLAY_BG, HELP_OVERLAY_BG, RECOMMEND_OVERLAY_BG, OVERLAY_PANEL_WIDTH, TABLE_HEADER_LINES, TABLE_FOOTER_LINES, TABLE_FIXED_LINES, WIDTH_WARNING_MIN_COLS, msCell, spinCell } from '../src/constants.js'
109
109
  import { TIER_COLOR } from '../src/tier-colors.js'
110
110
  import { resolveCloudflareUrl, buildPingRequest, ping, extractQuotaPercent, getProviderQuotaPercentCached, usagePlaceholderForProvider } from '../src/ping.js'
111
111
  import { runFiableMode, filterByTierOrExit, fetchOpenRouterFreeModels } from '../src/analysis.js'
@@ -192,9 +192,8 @@ export async function runApp(cliArgs, config) {
192
192
  if (cliArgs.pingInterval) config.settings.pingInterval = cliArgs.pingInterval
193
193
  if (cliArgs.hideUnconfigured) config.settings.hideUnconfiguredModels = true
194
194
  if (cliArgs.showUnconfigured) config.settings.hideUnconfiguredModels = false
195
- if (cliArgs.disableWidthsWarning) config.settings.disableWidthsWarning = true
196
195
 
197
- // 📖 Apply premium mode: show only S‑tier models sorted by verdict
196
+ // 📖 Apply premium mode as an initial, user-resettable view preset.
198
197
  if (cliArgs.premiumMode) {
199
198
  config.settings.tierFilter = 'S'
200
199
  config.settings.sortColumn = 'verdict'
@@ -383,13 +382,11 @@ export async function runApp(cliArgs, config) {
383
382
  mode, // 📖 'opencode' or 'openclaw' — controls Enter action
384
383
  tierFilterMode: 0, // 📖 Index into TIER_CYCLE (0=All, 1=S+, 2=S, ...)
385
384
  originFilterMode: 0, // 📖 Index into ORIGIN_CYCLE (0=All, then providers)
386
- premiumMode: cliArgs.premiumMode, // 📖 Special elite-only mode: S/S+ only, Health UP only, Perfect/Normal/Slow verdict only.
387
385
  hideUnconfiguredModels: config.settings?.hideUnconfiguredModels === true, // 📖 Hide providers with no configured API key when true.
388
- disableWidthsWarning: config.settings?.disableWidthsWarning ?? false, // 📖 Cached for runtime checks; keep it in sync with config.settings.
389
386
  scrollOffset: 0, // 📖 First visible model index in viewport
390
387
  terminalRows: process.stdout.rows || 24, // 📖 Current terminal height
391
388
  terminalCols: process.stdout.columns || 80, // 📖 Current terminal width
392
- widthWarningStartedAt: (process.stdout.columns || 80) < 166 && !(config.settings?.disableWidthsWarning ?? false) ? now : null, // 📖 Start immediately only when warnings are enabled in a narrow viewport.
389
+ widthWarningStartedAt: (process.stdout.columns || 80) < WIDTH_WARNING_MIN_COLS ? now : null, // 📖 Start immediately in very narrow viewports.
393
390
  widthWarningDismissed: false, // 📖 Esc hides the narrow-terminal warning early for the current narrow-width session.
394
391
  widthWarningShowCount: 0, // 📖 Counter for how many times the narrow-terminal warning has been shown (max 2 per session).
395
392
  // 📖 Settings screen state (P key opens it)
@@ -412,6 +409,7 @@ export async function runApp(cliArgs, config) {
412
409
  commandPaletteScrollOffset: 0, // 📖 Vertical scroll offset for the command palette result viewport.
413
410
  commandPaletteResults: [], // 📖 Cached fuzzy-filtered command entries for the command palette.
414
411
  commandPaletteFrozenTable: null, // 📖 Frozen table snapshot rendered behind the command palette overlay.
412
+ commandPaletteExpandedIds: new Set(['filters']), // 📖 Set of expanded category/subcategory IDs (filters expanded by default for quick access).
415
413
  helpVisible: false, // 📖 Whether the help overlay (K key) is active
416
414
  settingsScrollOffset: 0, // 📖 Vertical scroll offset for Settings overlay viewport
417
415
  helpScrollOffset: 0, // 📖 Vertical scroll offset for Help overlay viewport
@@ -460,17 +458,17 @@ export async function runApp(cliArgs, config) {
460
458
  changelogPhase: 'index', // 📖 'index' (all versions) | 'details' (specific version)
461
459
  changelogCursor: 0, // 📖 Selected row in index phase
462
460
  changelogSelectedVersion: null, // 📖 Which version to show details for
461
+ // 📖 Custom text filter (Ctrl+P palette → type text → Enter). Ephemeral — not saved to config.
462
+ customTextFilter: null, // 📖 Active free-text filter string (null = off). Matches model name, ctx, provider key/name.
463
463
  }
464
464
 
465
465
  // 📖 Re-clamp viewport on terminal resize
466
466
  process.stdout.on('resize', () => {
467
467
  const prevCols = state.terminalCols
468
- const widthsWarningDisabled = state.config.settings?.disableWidthsWarning === true
469
468
  state.terminalRows = process.stdout.rows || 24
470
469
  state.terminalCols = process.stdout.columns || 80
471
- state.disableWidthsWarning = widthsWarningDisabled
472
- if (state.terminalCols < 166 && !widthsWarningDisabled) {
473
- if (prevCols >= 166 || state.widthWarningDismissed) {
470
+ if (state.terminalCols < WIDTH_WARNING_MIN_COLS) {
471
+ if (prevCols >= WIDTH_WARNING_MIN_COLS || state.widthWarningDismissed) {
474
472
  state.widthWarningStartedAt = Date.now()
475
473
  state.widthWarningDismissed = false
476
474
  state.widthWarningShowCount++ // 📖 Increment counter when showing the warning again
@@ -630,15 +628,10 @@ export async function runApp(cliArgs, config) {
630
628
  outputResults = outputResults.filter(r => ['S+', 'S', 'A+'].includes(r.tier))
631
629
  }
632
630
 
633
- // 📖 Apply premium mode filter if specified: elite-only (S/S+, UP, Good Verdict)
631
+ // 📖 Apply premium mode as a preselected tier family in JSON mode as well.
634
632
  if (cliArgs.premiumMode) {
635
- outputResults = outputResults.filter(r => {
636
- const isEliteTier = r.tier === 'S' || r.tier === 'S+'
637
- const isHealthUp = r.status === 'up'
638
- const verdict = getVerdict(r)
639
- const isGoodVerdict = ['Perfect', 'Normal', 'Slow'].includes(verdict)
640
- return isEliteTier && isHealthUp && isGoodVerdict
641
- })
633
+ const premiumTiers = TIER_LETTER_MAP.S || ['S+', 'S']
634
+ outputResults = outputResults.filter(r => premiumTiers.includes(r.tier))
642
635
  }
643
636
 
644
637
  // 📖 Sort by avg ping (ascending)
@@ -700,19 +693,22 @@ export async function runApp(cliArgs, config) {
700
693
  const allowedTiers = (activeTier && TIER_LETTER_MAP[activeTier]) ? TIER_LETTER_MAP[activeTier] : [activeTier]
701
694
  const tierHide = activeTier !== null && !allowedTiers.includes(r.tier)
702
695
  const originHide = activeOrigin !== null && r.providerKey !== activeOrigin
703
- r.hidden = tierHide || originHide
704
-
705
- // 📖 Premium Mode: elite-only constraints (Health UP, Good Verdict, S/S+ only)
706
- if (state.premiumMode && !r.hidden) {
707
- const isEliteTier = r.tier === 'S' || r.tier === 'S+'
708
- const isHealthUp = r.status === 'up'
709
- const verdict = getVerdict(r)
710
- const isGoodVerdict = ['Perfect', 'Normal', 'Slow'].includes(verdict)
711
-
712
- if (!isEliteTier || !isHealthUp || !isGoodVerdict) {
713
- r.hidden = true
714
- }
696
+ if (tierHide || originHide) {
697
+ r.hidden = true
698
+ return
699
+ }
700
+ // 📖 Custom text filter case-insensitive includes match against model name, ctx, provider key, and provider display name.
701
+ if (state.customTextFilter) {
702
+ const q = state.customTextFilter.toLowerCase()
703
+ const providerName = (sources[r.providerKey]?.name || '').toLowerCase()
704
+ const match = (r.label || '').toLowerCase().includes(q)
705
+ || (r.ctx || '').toLowerCase().includes(q)
706
+ || (r.providerKey || '').toLowerCase().includes(q)
707
+ || providerName.includes(q)
708
+ r.hidden = !match
709
+ return
715
710
  }
711
+ r.hidden = false
716
712
  })
717
713
  return state.results
718
714
  }
@@ -900,8 +896,7 @@ export async function runApp(cliArgs, config) {
900
896
  state.settingsUpdateLatestVersion,
901
897
  false,
902
898
  state.startupLatestVersion,
903
- state.versionAlertsEnabled,
904
- state.config.settings?.disableWidthsWarning ?? false
899
+ state.versionAlertsEnabled
905
900
  )
906
901
  }
907
902
  tableContent = state.commandPaletteFrozenTable
@@ -933,8 +928,7 @@ export async function runApp(cliArgs, config) {
933
928
  state.settingsUpdateLatestVersion,
934
929
  false,
935
930
  state.startupLatestVersion,
936
- state.versionAlertsEnabled,
937
- state.config.settings?.disableWidthsWarning ?? false
931
+ state.versionAlertsEnabled
938
932
  )
939
933
  }
940
934
 
@@ -972,7 +966,7 @@ export async function runApp(cliArgs, config) {
972
966
  const initialVisible = state.results.filter(r => !r.hidden)
973
967
  state.visibleSorted = sortResultsWithPinnedFavorites(initialVisible, state.sortColumn, state.sortDirection)
974
968
 
975
- process.stdout.write(ALT_HOME + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilterMode, state.scrollOffset, state.terminalRows, state.terminalCols, state.originFilterMode, null, state.pingMode, state.pingModeSource, state.hideUnconfiguredModels, state.widthWarningStartedAt, state.widthWarningDismissed, state.widthWarningShowCount, state.settingsUpdateState, state.settingsUpdateLatestVersion, false, state.startupLatestVersion, state.versionAlertsEnabled, state.config.settings?.disableWidthsWarning ?? false))
969
+ process.stdout.write(ALT_HOME + renderTable(state.results, state.pendingPings, state.frame, state.cursor, state.sortColumn, state.sortDirection, state.pingInterval, state.lastPingTime, state.mode, state.tierFilterMode, state.scrollOffset, state.terminalRows, state.terminalCols, state.originFilterMode, null, state.pingMode, state.pingModeSource, state.hideUnconfiguredModels, state.widthWarningStartedAt, state.widthWarningDismissed, state.widthWarningShowCount, state.settingsUpdateState, state.settingsUpdateLatestVersion, false, state.startupLatestVersion, state.versionAlertsEnabled))
976
970
  if (process.stdout.isTTY) {
977
971
  process.stdout.flush && process.stdout.flush()
978
972
  }
package/src/cli-help.js CHANGED
@@ -26,14 +26,13 @@ const ANALYSIS_FLAGS = [
26
26
  { flag: '--json', description: 'Output results as JSON for scripts/automation' },
27
27
  { flag: '--tier <S|A|B|C>', description: 'Filter models by tier family' },
28
28
  { flag: '--recommend', description: 'Open Smart Recommend immediately on startup' },
29
- { flag: '--premium', description: 'Show only S/S+ models with perfect health and good verdict' },
29
+ { flag: '--premium', description: 'Start with S-tier filter + verdict sort (you can reset it in-app)' },
30
30
  { flag: '--sort <column>', description: 'Sort by column (rank, tier, origin, model, ping, avg, swe, ctx, condition, verdict, uptime, stability, usage)' },
31
31
  { flag: '--desc | --asc', description: 'Set sort direction (descending or ascending)' },
32
32
  { flag: '--origin <provider>', description: 'Filter models by provider origin' },
33
33
  { flag: '--ping-interval <ms>', description: 'Override ping interval in milliseconds' },
34
34
  { flag: '--hide-unconfigured', description: 'Hide models without configured API keys' },
35
35
  { flag: '--show-unconfigured', description: 'Show all models regardless of API key config' },
36
- { flag: '--disable-widths-warning', description: 'Disable terminal width warning' },
37
36
  ]
38
37
 
39
38
  const CONFIG_FLAGS = [