free-coding-models 0.1.20 β†’ 0.1.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.
@@ -201,7 +201,7 @@ async function promptModeSelection(latestVersion) {
201
201
  options.push({
202
202
  label: 'Read Changelogs',
203
203
  icon: 'πŸ“‹',
204
- description: 'Open the GitHub releases page in your browser',
204
+ description: 'Open local CHANGELOG.md file',
205
205
  })
206
206
  }
207
207
 
@@ -328,10 +328,13 @@ const spinCell = (f, o = 0) => chalk.dim.yellow(FRAMES[(f + o) % FRAMES.length].
328
328
 
329
329
  // πŸ“– renderTable: mode param controls footer hint text (opencode vs openclaw)
330
330
  function renderTable(results, pendingPings, frame, cursor = null, sortColumn = 'avg', sortDirection = 'asc', pingInterval = PING_INTERVAL, lastPingTime = Date.now(), mode = 'opencode', tierFilter = null) {
331
- const up = results.filter(r => r.status === 'up').length
332
- const down = results.filter(r => r.status === 'down').length
333
- const timeout = results.filter(r => r.status === 'timeout').length
334
- const pending = results.filter(r => r.status === 'pending').length
331
+ // πŸ“– Filter out hidden models for display
332
+ const visibleResults = results.filter(r => !r.hidden)
333
+
334
+ const up = visibleResults.filter(r => r.status === 'up').length
335
+ const down = visibleResults.filter(r => r.status === 'down').length
336
+ const timeout = visibleResults.filter(r => r.status === 'timeout').length
337
+ const pending = visibleResults.filter(r => r.status === 'pending').length
335
338
 
336
339
  // πŸ“– Calculate seconds until next ping
337
340
  const timeSinceLastPing = Date.now() - lastPingTime
@@ -372,7 +375,7 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
372
375
  const W_UPTIME = 6
373
376
 
374
377
  // πŸ“– Sort models using the shared helper
375
- const sorted = sortResults(results, sortColumn, sortDirection)
378
+ const sorted = sortResults(visibleResults, sortColumn, sortDirection)
376
379
 
377
380
  const lines = [
378
381
  '',
@@ -1041,11 +1044,12 @@ async function main() {
1041
1044
  runUpdate(latestVersion)
1042
1045
  }
1043
1046
 
1044
- // πŸ“– Handle "Read Changelogs" selection β€” open GitHub releases in browser
1047
+ // πŸ“– Handle "Read Changelogs" selection β€” open local CHANGELOG.md file
1045
1048
  if (mode === 'changelogs') {
1046
1049
  const { exec } = await import('child_process')
1047
- exec('open https://github.com/vava-nessa/free-coding-models/releases')
1048
- console.log(chalk.dim(' πŸ“‹ Opening changelogs in browser…'))
1050
+ const changelogPath = join(process.cwd(), 'CHANGELOG.md')
1051
+ exec(`open "${changelogPath}"`)
1052
+ console.log(chalk.dim(' πŸ“‹ Opening local changelogs file…'))
1049
1053
  process.exit(0)
1050
1054
  }
1051
1055
 
@@ -1055,21 +1059,33 @@ async function main() {
1055
1059
  console.log()
1056
1060
  }
1057
1061
 
1058
- // πŸ“– Filter models to only show top tiers if BEST mode is active
1062
+ // πŸ“– Create results array with all models initially visible
1059
1063
  let results = MODELS.map(([modelId, label, tier], i) => ({
1060
1064
  idx: i + 1, modelId, label, tier,
1061
1065
  status: 'pending',
1062
1066
  pings: [], // πŸ“– All ping results (ms or 'TIMEOUT')
1063
1067
  httpCode: null,
1068
+ hidden: false, // πŸ“– Simple flag to hide/show models
1064
1069
  }))
1065
1070
 
1071
+ // πŸ“– Apply filters by setting hidden flag
1066
1072
  if (bestMode) {
1067
- results = results.filter(r => r.tier === 'S+' || r.tier === 'S' || r.tier === 'A+')
1073
+ results.forEach(r => {
1074
+ r.hidden = !(r.tier === 'S+' || r.tier === 'S' || r.tier === 'A+')
1075
+ })
1068
1076
  }
1069
1077
 
1070
- // πŸ“– Apply tier letter filter if --tier X was given
1078
+ // πŸ“– Apply tier letter filter if --tier X was given (just sets initial state)
1079
+ // πŸ“– User can still change filter with E/D keys later
1071
1080
  if (tierFilter) {
1072
- results = filterByTierOrExit(results, tierFilter)
1081
+ const tierSet = TIER_LETTER_MAP[tierFilter]
1082
+ if (!tierSet) {
1083
+ console.error(chalk.red(` βœ– Unknown tier "${tierFilter}". Valid tiers: S, A, B, C`))
1084
+ process.exit(1)
1085
+ }
1086
+ results.forEach(r => {
1087
+ r.hidden = !tierSet.includes(r.tier)
1088
+ })
1073
1089
  }
1074
1090
 
1075
1091
  // πŸ“– Add interactive selection state - cursor index and user's choice
@@ -1105,38 +1121,17 @@ async function main() {
1105
1121
  process.on('SIGINT', () => exit(0))
1106
1122
  process.on('SIGTERM', () => exit(0))
1107
1123
 
1108
- // πŸ“– Apply tier filter to results based on current state.tierFilter
1109
- // πŸ“– Preserves existing ping history when filtering
1124
+ // πŸ“– Apply tier filter based on current state.tierFilter
1125
+ // πŸ“– This preserves all ping history by merging with existing results
1110
1126
  function applyTierFilter() {
1111
- const allModels = MODELS.map(([modelId, label, tier], i) => ({
1112
- idx: i + 1, modelId, label, tier,
1113
- status: 'pending',
1114
- pings: [],
1115
- httpCode: null,
1116
- }))
1117
-
1118
- if (!state.tierFilter) {
1119
- return allModels
1120
- }
1121
-
1122
- // πŸ“– Filter models by tier and preserve existing ping history
1123
- const filteredModels = allModels.filter(model =>
1124
- TIER_LETTER_MAP[state.tierFilter].includes(model.tier)
1125
- )
1127
+ const tierSet = state.tierFilter ? TIER_LETTER_MAP[state.tierFilter] : null
1126
1128
 
1127
- // πŸ“– Try to preserve existing ping data from current results
1128
- return filteredModels.map(model => {
1129
- const existingResult = state.results.find(r => r.modelId === model.modelId)
1130
- if (existingResult) {
1131
- return {
1132
- ...model,
1133
- status: existingResult.status,
1134
- pings: [...existingResult.pings],
1135
- httpCode: existingResult.httpCode
1136
- }
1137
- }
1138
- return model
1129
+ // πŸ“– Simple approach: just update hidden flag on existing results
1130
+ state.results.forEach(r => {
1131
+ r.hidden = tierSet ? !tierSet.includes(r.tier) : false
1139
1132
  })
1133
+
1134
+ return state.results // πŸ“– Return same array, just updated hidden flags
1140
1135
  }
1141
1136
 
1142
1137
  // πŸ“– Setup keyboard input for interactive selection during pings
@@ -1294,14 +1289,14 @@ async function main() {
1294
1289
  }
1295
1290
 
1296
1291
  // πŸ“– Initial ping of all models
1297
- const initialPing = Promise.all(results.map(r => pingModel(r)))
1292
+ const initialPing = Promise.all(state.results.map(r => pingModel(r)))
1298
1293
 
1299
1294
  // πŸ“– Continuous ping loop with dynamic interval (adjustable with W/X keys)
1300
1295
  const schedulePing = () => {
1301
1296
  state.pingIntervalObj = setTimeout(async () => {
1302
1297
  state.lastPingTime = Date.now()
1303
1298
 
1304
- results.forEach(r => {
1299
+ state.results.forEach(r => {
1305
1300
  pingModel(r).catch(() => {
1306
1301
  // Individual ping failures don't crash the loop
1307
1302
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.1.20",
3
+ "version": "0.1.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",