smoothie-code 2.0.2 → 2.0.3

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/config.json CHANGED
@@ -1,19 +1,12 @@
1
1
  {
2
2
  "openrouter_models": [
3
3
  {
4
- "id": "google/gemma-4-26b-a4b-it",
5
- "label": "Google: Gemma 4 26B A4B "
6
- },
7
- {
8
- "id": "qwen/qwen3.6-plus:free",
9
- "label": "Qwen: Qwen3.6 Plus (free)"
10
- },
11
- {
12
- "id": "z-ai/glm-5v-turbo",
13
- "label": "Z.ai: GLM 5V Turbo"
4
+ "id": "openai/gpt-5.4-nano",
5
+ "label": "OpenAI: GPT-5.4 Nano"
14
6
  }
15
7
  ],
16
8
  "auto_blend": true,
17
9
  "leaderboard": true,
18
- "github": "hotairbag"
10
+ "github": "hotairbag",
11
+ "auto_review": true
19
12
  }
@@ -173,72 +173,110 @@ async function cmdPick(apiKey, configPath) {
173
173
  claude: ['anthropic'],
174
174
  codex: ['openai'],
175
175
  gemini: ['google'],
176
- cursor: [], // Cursor uses various models, nothing to exclude
176
+ cursor: [],
177
177
  };
178
178
  const excluded = excludePrefixes[platform] || [];
179
179
  topModels = topModels.filter(m => !excluded.some(prefix => m.id.startsWith(prefix + '/')));
180
- console.log(' \x1b[90mSee trending: https://openrouter.ai/rankings/programming\x1b[0m');
181
- // Default selection: first 3
180
+ // Track selection + any extra models added by ID
182
181
  const selected = new Set([0, 1, 2]);
183
- // Print list with selection markers
184
- console.log('');
185
- for (let i = 0; i < topModels.length; i++) {
186
- const check = selected.has(i) ? '\x1b[32m✓\x1b[0m' : ' ';
187
- const num = String(i + 1).padStart(2, ' ');
188
- console.log(` ${check} ${num}. ${topModels[i].label}`);
189
- }
190
- console.log('');
182
+ const extras = [];
191
183
  const rl = createInterface({ input: process.stdin, output: process.stdout });
192
- const answer = await promptQ(rl, ' Toggle numbers, paste model ID, or Enter to confirm: ');
193
- rl.close();
194
- const input = answer.trim();
195
- // Collect any pasted model IDs (contain '/')
196
- const pastedIds = [];
197
- const toggleNums = [];
198
- if (input) {
184
+ function printList() {
185
+ console.log('');
186
+ console.log(' \x1b[90mSee trending: https://openrouter.ai/rankings/programming\x1b[0m');
187
+ console.log('');
188
+ for (let i = 0; i < topModels.length; i++) {
189
+ const check = selected.has(i) ? '\x1b[32m✓\x1b[0m' : ' ';
190
+ const num = String(i + 1).padStart(2, ' ');
191
+ console.log(` ${check} ${num}. ${topModels[i].label}`);
192
+ }
193
+ for (const e of extras) {
194
+ console.log(` \x1b[32m✓\x1b[0m + ${e.label}`);
195
+ }
196
+ const count = selected.size + extras.length;
197
+ const names = [...selected].map(i => topModels[i].label).concat(extras.map(e => e.label));
198
+ console.log('');
199
+ console.log(` \x1b[90mSelected: ${names.join(', ')} (${count} model${count !== 1 ? 's' : ''})\x1b[0m`);
200
+ console.log(' \x1b[90m1-${topModels.length} toggle · model/id add · Enter save · reset defaults\x1b[0m'.replace('${topModels.length}', String(topModels.length)));
201
+ console.log('');
202
+ }
203
+ printList();
204
+ // Interactive loop
205
+ while (true) {
206
+ const answer = await promptQ(rl, ' > ');
207
+ const input = answer.trim();
208
+ if (input === '' || input === 'done') {
209
+ // Save and exit
210
+ if (selected.size + extras.length === 0) {
211
+ console.log(' \x1b[33mNeed at least 1 model\x1b[0m');
212
+ continue;
213
+ }
214
+ break;
215
+ }
216
+ if (input === 'reset') {
217
+ selected.clear();
218
+ selected.add(0);
219
+ selected.add(1);
220
+ selected.add(2);
221
+ extras.length = 0;
222
+ printList();
223
+ continue;
224
+ }
225
+ // Process each token (supports "4 5 7" in one line)
226
+ let changed = false;
199
227
  for (const token of input.split(/\s+/)) {
200
228
  if (token.includes('/')) {
201
- // Looks like a model ID
229
+ // Model ID
202
230
  const entry = await lookupModel(apiKey, token);
203
231
  if (entry) {
204
- pastedIds.push(entry);
205
- console.log(` ✓ Added ${entry.label}`);
232
+ if (!extras.some(e => e.id === entry.id)) {
233
+ extras.push(entry);
234
+ console.log(` \x1b[32m✓\x1b[0m Added ${entry.label}`);
235
+ }
236
+ else {
237
+ console.log(` Already added: ${entry.label}`);
238
+ }
239
+ changed = true;
206
240
  }
207
241
  else {
208
- console.log(` Unknown: ${token}`);
242
+ console.log(` \x1b[31m✗\x1b[0m Unknown model: ${token}`);
209
243
  }
210
244
  }
211
245
  else {
212
246
  const n = parseInt(token, 10);
213
- if (n >= 1 && n <= topModels.length)
214
- toggleNums.push(n);
247
+ if (n >= 1 && n <= topModels.length) {
248
+ const idx = n - 1;
249
+ if (selected.has(idx)) {
250
+ if (selected.size + extras.length <= 1) {
251
+ console.log(' \x1b[33mNeed at least 1 model\x1b[0m');
252
+ }
253
+ else {
254
+ selected.delete(idx);
255
+ changed = true;
256
+ }
257
+ }
258
+ else {
259
+ selected.add(idx);
260
+ changed = true;
261
+ }
262
+ }
215
263
  }
216
264
  }
217
- }
218
- // If user typed numbers, use exactly those (not toggle, just select)
219
- let finalSelection;
220
- if (toggleNums.length > 0) {
221
- finalSelection = toggleNums.map((n) => topModels[n - 1]);
222
- }
223
- else if (pastedIds.length > 0) {
224
- // Keep defaults + add pasted
225
- finalSelection = [...selected].map((i) => topModels[i]);
226
- }
227
- else {
228
- // Enter with no input → use defaults
229
- finalSelection = [...selected].map((i) => topModels[i]);
230
- }
231
- // Merge pasted IDs
232
- for (const p of pastedIds) {
233
- if (!finalSelection.some((m) => m.id === p.id)) {
234
- finalSelection.push(p);
265
+ if (changed) {
266
+ printList();
235
267
  }
236
268
  }
269
+ rl.close();
270
+ // Build final selection
271
+ const finalSelection = [
272
+ ...[...selected].map(i => topModels[i]),
273
+ ...extras,
274
+ ];
237
275
  // Preserve existing config fields (like auto_review)
238
276
  const existing = loadConfig(configPath);
239
277
  existing.openrouter_models = finalSelection;
240
278
  saveConfig(configPath, existing);
241
- console.log(` ${finalSelection.map((m) => m.label).join(', ')}`);
279
+ console.log(` \x1b[32m✓\x1b[0m ${finalSelection.map(m => m.label).join(', ')}`);
242
280
  }
243
281
  // ── Main ────────────────────────────────────────────────────────────
244
282
  async function main() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoothie-code",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {