cascade-ai 0.12.3 → 0.12.5

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/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { GoogleGenAI, HarmBlockThreshold, HarmCategory } from '@google/genai';
4
4
  import { render, Box, Text, useStdout, useApp, useInput, Static } from 'ink';
5
5
  import { Command } from 'commander';
6
6
  import React2, { useState, useReducer, useRef, useCallback, useEffect, useMemo } from 'react';
7
- import chalk11 from 'chalk';
7
+ import chalk9 from 'chalk';
8
8
  import dotenv from 'dotenv';
9
9
  import fs21 from 'fs';
10
10
  import path23 from 'path';
@@ -40,50 +40,21 @@ import parser from 'socket.io-msgpack-parser';
40
40
  import jwt from 'jsonwebtoken';
41
41
 
42
42
  // Cascade AI — Multi-tier AI Orchestration System
43
- var __create = Object.create;
44
43
  var __defProp = Object.defineProperty;
45
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
46
44
  var __getOwnPropNames = Object.getOwnPropertyNames;
47
- var __getProtoOf = Object.getPrototypeOf;
48
- var __hasOwnProp = Object.prototype.hasOwnProperty;
49
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
50
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
51
- }) : x)(function(x) {
52
- if (typeof require !== "undefined") return require.apply(this, arguments);
53
- throw Error('Dynamic require of "' + x + '" is not supported');
54
- });
55
45
  var __esm = (fn, res) => function __init() {
56
46
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
57
47
  };
58
- var __commonJS = (cb, mod) => function __require2() {
59
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
60
- };
61
48
  var __export = (target, all) => {
62
49
  for (var name in all)
63
50
  __defProp(target, name, { get: all[name], enumerable: true });
64
51
  };
65
- var __copyProps = (to, from, except, desc) => {
66
- if (from && typeof from === "object" || typeof from === "function") {
67
- for (let key of __getOwnPropNames(from))
68
- if (!__hasOwnProp.call(to, key) && key !== except)
69
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
70
- }
71
- return to;
72
- };
73
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
74
- // If the importer is in node compatibility mode or this is not an ESM
75
- // file that has been converted to a CommonJS file using a Babel-
76
- // compatible transform (i.e. "__esModule" has not been set), then set
77
- // "default" to the CommonJS "module.exports" for node compatibility.
78
- __defProp(target, "default", { value: mod, enumerable: true }) ,
79
- mod
80
- ));
81
52
 
82
53
  // src/constants.ts
83
54
  var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
84
55
  var init_constants = __esm({
85
56
  "src/constants.ts"() {
86
- CASCADE_VERSION = "0.9.6";
57
+ CASCADE_VERSION = "0.12.5";
87
58
  CASCADE_CONFIG_FILE = ".cascade/config.json";
88
59
  CASCADE_DB_FILE = ".cascade/memory.db";
89
60
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
@@ -399,63 +370,6 @@ var init_constants = __esm({
399
370
  }
400
371
  });
401
372
 
402
- // node_modules/keytar/build/Release/keytar.node
403
- var keytar_default;
404
- var init_keytar = __esm({
405
- "node_modules/keytar/build/Release/keytar.node"() {
406
- keytar_default = "./keytar-VMICNFEJ.node";
407
- }
408
- });
409
-
410
- // node-file:/home/runner/work/Cascade-AI/Cascade-AI/node_modules/keytar/build/Release/keytar.node
411
- var require_keytar = __commonJS({
412
- "node-file:/home/runner/work/Cascade-AI/Cascade-AI/node_modules/keytar/build/Release/keytar.node"(exports, module) {
413
- init_keytar();
414
- try {
415
- module.exports = __require(keytar_default);
416
- } catch {
417
- }
418
- }
419
- });
420
-
421
- // node_modules/keytar/lib/keytar.js
422
- var require_keytar2 = __commonJS({
423
- "node_modules/keytar/lib/keytar.js"(exports, module) {
424
- var keytar = require_keytar();
425
- function checkRequired(val, name) {
426
- if (!val || val.length <= 0) {
427
- throw new Error(name + " is required.");
428
- }
429
- }
430
- module.exports = {
431
- getPassword: function(service, account) {
432
- checkRequired(service, "Service");
433
- checkRequired(account, "Account");
434
- return keytar.getPassword(service, account);
435
- },
436
- setPassword: function(service, account, password) {
437
- checkRequired(service, "Service");
438
- checkRequired(account, "Account");
439
- checkRequired(password, "Password");
440
- return keytar.setPassword(service, account, password);
441
- },
442
- deletePassword: function(service, account) {
443
- checkRequired(service, "Service");
444
- checkRequired(account, "Account");
445
- return keytar.deletePassword(service, account);
446
- },
447
- findPassword: function(service) {
448
- checkRequired(service, "Service");
449
- return keytar.findPassword(service);
450
- },
451
- findCredentials: function(service) {
452
- checkRequired(service, "Service");
453
- return keytar.findCredentials(service);
454
- }
455
- };
456
- }
457
- });
458
-
459
373
  // src/providers/base.ts
460
374
  var BaseProvider;
461
375
  var init_base = __esm({
@@ -1465,7 +1379,7 @@ var PBKDF2_ITERATIONS = 1e5;
1465
1379
  var KEYTAR_SERVICE = "cascade-ai";
1466
1380
  async function loadKeytar() {
1467
1381
  try {
1468
- const mod = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
1382
+ const mod = await import('keytar');
1469
1383
  const candidate = mod.default ?? mod;
1470
1384
  if (typeof candidate.getPassword !== "function") return null;
1471
1385
  return candidate;
@@ -3189,15 +3103,15 @@ function SafeTextInput(props) {
3189
3103
  const displayValue = mask ? mask.repeat(value.length) : value;
3190
3104
  let rendered;
3191
3105
  if (displayValue.length === 0) {
3192
- rendered = showCursor && focus ? placeholder.length > 0 ? chalk11.inverse(placeholder[0]) + chalk11.grey(placeholder.slice(1)) : chalk11.inverse(" ") : placeholder.length > 0 ? chalk11.grey(placeholder) : "";
3106
+ rendered = showCursor && focus ? placeholder.length > 0 ? chalk9.inverse(placeholder[0]) + chalk9.grey(placeholder.slice(1)) : chalk9.inverse(" ") : placeholder.length > 0 ? chalk9.grey(placeholder) : "";
3193
3107
  } else if (!showCursor || !focus) {
3194
3108
  rendered = displayValue;
3195
3109
  } else {
3196
3110
  let out = "";
3197
3111
  for (let i = 0; i < displayValue.length; i++) {
3198
- out += i === cursorOffset ? chalk11.inverse(displayValue[i]) : displayValue[i];
3112
+ out += i === cursorOffset ? chalk9.inverse(displayValue[i]) : displayValue[i];
3199
3113
  }
3200
- if (cursorOffset >= displayValue.length) out += chalk11.inverse(" ");
3114
+ if (cursorOffset >= displayValue.length) out += chalk9.inverse(" ");
3201
3115
  rendered = out;
3202
3116
  }
3203
3117
  return /* @__PURE__ */ jsx(Text, { children: rendered });
@@ -4047,7 +3961,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
4047
3961
  }
4048
3962
  for (const tier of ["T1", "T2", "T3"]) {
4049
3963
  const override = tier === "T1" ? config.models.t1 : tier === "T2" ? config.models.t2 : config.models.t3;
4050
- if (!override) continue;
3964
+ if (!override || override === "auto") continue;
4051
3965
  const model = this.selector.selectForTier(tier, override);
4052
3966
  if (!model) {
4053
3967
  const knownProviders = ["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"];
@@ -11450,7 +11364,7 @@ function StatusBarInternal({
11450
11364
  const savedStr = savedUsd > 0 ? ` \xB7 saved $${savedUsd.toFixed(4)}` : "";
11451
11365
  const rightStr = ` ${formatTokens(tokens)} \xB7 $${costUsd.toFixed(4)}${savedStr} ${isExecuting ? "\u26A1" : "\xB7"} `;
11452
11366
  const gap = Math.max(0, width - leftStr.length - rightStr.length);
11453
- const line = chalk11.bgHex(theme.colors.primary).hex(theme.colors.background)(chalk11.bold(leftStr) + " ".repeat(gap) + rightStr);
11367
+ const line = chalk9.bgHex(theme.colors.primary).hex(theme.colors.background)(chalk9.bold(leftStr) + " ".repeat(gap) + rightStr);
11454
11368
  return /* @__PURE__ */ jsx(Text, { children: line });
11455
11369
  }
11456
11370
  function formatTokens(n) {
@@ -11861,7 +11775,7 @@ function renderContent(content, theme) {
11861
11775
  i
11862
11776
  );
11863
11777
  }
11864
- const rendered = part.replace(/\*\*(.*?)\*\*/g, (_, t) => chalk11.bold(t)).replace(/`(.*?)`/g, (_, t) => chalk11.bgHex(theme.colors.border)(t));
11778
+ const rendered = part.replace(/\*\*(.*?)\*\*/g, (_, t) => chalk9.bold(t)).replace(/`(.*?)`/g, (_, t) => chalk9.bgHex(theme.colors.border)(t));
11865
11779
  return /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: rendered }, i);
11866
11780
  });
11867
11781
  }
@@ -14067,20 +13981,20 @@ async function initCommand(workspacePath = process.cwd()) {
14067
13981
  const mdPath = path23.join(workspacePath, "CASCADE.md");
14068
13982
  if (!await fileExists(mdPath)) {
14069
13983
  await createDefaultCascadeMd(workspacePath);
14070
- spin.succeed(chalk11.green("Created CASCADE.md"));
13984
+ spin.succeed(chalk9.green("Created CASCADE.md"));
14071
13985
  }
14072
13986
  const ignorePath = path23.join(workspacePath, ".cascadeignore");
14073
13987
  if (!await fileExists(ignorePath)) {
14074
13988
  await createDefaultIgnoreFile(workspacePath);
14075
- spin.succeed(chalk11.green("Created .cascadeignore"));
13989
+ spin.succeed(chalk9.green("Created .cascadeignore"));
14076
13990
  }
14077
13991
  spin.stop();
14078
13992
  console.log();
14079
- console.log(chalk11.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
13993
+ console.log(chalk9.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
14080
13994
  console.log();
14081
13995
  const configPath = path23.join(workspacePath, CASCADE_CONFIG_FILE);
14082
13996
  if (await fileExists(configPath)) {
14083
- console.log(chalk11.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
13997
+ console.log(chalk9.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
14084
13998
  console.log();
14085
13999
  }
14086
14000
  const config = await runSetupWizard(workspacePath);
@@ -14088,10 +14002,10 @@ async function initCommand(workspacePath = process.cwd()) {
14088
14002
  await cm.load();
14089
14003
  await cm.updateConfig(config);
14090
14004
  console.log();
14091
- console.log(chalk11.green(" \u25C8 Setup complete! Run `cascade` to start."));
14005
+ console.log(chalk9.green(" \u25C8 Setup complete! Run `cascade` to start."));
14092
14006
  console.log();
14093
14007
  } catch (err) {
14094
- console.error(chalk11.red(`Init failed: ${err instanceof Error ? err.message : String(err)}`));
14008
+ console.error(chalk9.red(`Init failed: ${err instanceof Error ? err.message : String(err)}`));
14095
14009
  process.exit(1);
14096
14010
  }
14097
14011
  }
@@ -14237,7 +14151,7 @@ function maskSecret(secret) {
14237
14151
 
14238
14152
  // src/cli/commands/doctor.ts
14239
14153
  async function doctorCommand() {
14240
- console.log(chalk11.magenta("\n \u25C8 Cascade Doctor \u2014 System Diagnostics\n"));
14154
+ console.log(chalk9.magenta("\n \u25C8 Cascade Doctor \u2014 System Diagnostics\n"));
14241
14155
  const checks = [];
14242
14156
  const nodeVersion = process.versions.node;
14243
14157
  const [major] = nodeVersion.split(".").map(Number);
@@ -14301,7 +14215,7 @@ async function doctorCommand() {
14301
14215
  });
14302
14216
  let keystoreBackend = "file (AES-256-GCM)";
14303
14217
  try {
14304
- await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
14218
+ await import('keytar');
14305
14219
  keystoreBackend = "keytar (OS keychain)";
14306
14220
  } catch {
14307
14221
  }
@@ -14329,22 +14243,22 @@ async function doctorCommand() {
14329
14243
  } catch {
14330
14244
  }
14331
14245
  for (const c of checks) {
14332
- const icon = c.ok ? chalk11.green(" \u2713") : chalk11.yellow(" \u25CB");
14333
- const label = c.ok ? chalk11.white(c.label) : chalk11.gray(c.label);
14334
- const detail = c.detail ? chalk11.gray(` \u2014 ${c.detail}`) : "";
14246
+ const icon = c.ok ? chalk9.green(" \u2713") : chalk9.yellow(" \u25CB");
14247
+ const label = c.ok ? chalk9.white(c.label) : chalk9.gray(c.label);
14248
+ const detail = c.detail ? chalk9.gray(` \u2014 ${c.detail}`) : "";
14335
14249
  console.log(`${icon} ${label}${detail}`);
14336
14250
  }
14337
14251
  const failures = checks.filter((c) => !c.ok);
14338
14252
  console.log();
14339
14253
  if (failures.length === 0) {
14340
- console.log(chalk11.green(" All checks passed!\n"));
14254
+ console.log(chalk9.green(" All checks passed!\n"));
14341
14255
  } else {
14342
14256
  const critical = failures.filter((c) => c.label.includes("Node") || c.label.includes("API key"));
14343
14257
  if (critical.length) {
14344
- console.log(chalk11.yellow(` ${critical.length} issue(s) need attention.
14258
+ console.log(chalk9.yellow(` ${critical.length} issue(s) need attention.
14345
14259
  `));
14346
14260
  } else {
14347
- console.log(chalk11.gray(` ${failures.length} optional item(s) not configured.
14261
+ console.log(chalk9.gray(` ${failures.length} optional item(s) not configured.
14348
14262
  `));
14349
14263
  }
14350
14264
  }
@@ -14371,14 +14285,14 @@ async function updateCommand() {
14371
14285
  const { stdout } = await execAsync2("npm show cascade-ai version", { timeout: 1e4 });
14372
14286
  const latest = stdout.trim().split("\n").filter(Boolean).pop() ?? "";
14373
14287
  if (latest === CASCADE_VERSION) {
14374
- spin.succeed(chalk11.green(`Already up to date (v${CASCADE_VERSION})`));
14288
+ spin.succeed(chalk9.green(`Already up to date (v${CASCADE_VERSION})`));
14375
14289
  return;
14376
14290
  }
14377
14291
  spin.text = `Updating cascade-ai ${CASCADE_VERSION} \u2192 ${latest}\u2026`;
14378
14292
  await execAsync2("npm install -g cascade-ai@latest", { timeout: 6e4 });
14379
- spin.succeed(chalk11.green(`Updated to v${latest}! Restart your terminal.`));
14293
+ spin.succeed(chalk9.green(`Updated to v${latest}! Restart your terminal.`));
14380
14294
  } catch (err) {
14381
- spin.fail(chalk11.red(`Update failed: ${err instanceof Error ? err.message : String(err)}`));
14295
+ spin.fail(chalk9.red(`Update failed: ${err instanceof Error ? err.message : String(err)}`));
14382
14296
  }
14383
14297
  }
14384
14298
  init_constants();
@@ -14543,6 +14457,11 @@ var DashboardSocket = class {
14543
14457
  emitToSocket(socketId, event, data) {
14544
14458
  this.io.sockets.sockets.get(socketId)?.emit(event, data);
14545
14459
  }
14460
+ onConfigGet(callback) {
14461
+ this.io.on("connection", (socket) => {
14462
+ socket.on("config:get", () => callback(socket.id));
14463
+ });
14464
+ }
14546
14465
  onCascadeRun(callback) {
14547
14466
  this.io.on("connection", (socket) => {
14548
14467
  socket.on("cascade:run", (payload) => {
@@ -14592,6 +14511,16 @@ var DashboardServer = class {
14592
14511
  this.socket.onSessionRate((sessionId, rating) => {
14593
14512
  this.activeSessions.get(sessionId)?.rateLastRun(rating);
14594
14513
  });
14514
+ this.socket.onConfigGet((socketId) => {
14515
+ this.socket.emitToSocket(socketId, "config:current", {
14516
+ models: this.config.models ?? {},
14517
+ budget: {
14518
+ maxCostPerRun: this.config.budget?.maxCostPerRunUsd,
14519
+ autoBias: this.config.autoBias
14520
+ },
14521
+ providersWithKey: (this.config.providers ?? []).filter((p) => typeof p.apiKey === "string" && p.apiKey.length > 0).map((p) => p.type)
14522
+ });
14523
+ });
14595
14524
  this.socket.onConfigUpdate((data) => {
14596
14525
  if (data.keys) {
14597
14526
  for (const [type, apiKey] of Object.entries(data.keys)) {
@@ -14602,7 +14531,11 @@ var DashboardServer = class {
14602
14531
  }
14603
14532
  }
14604
14533
  if (data.models) {
14605
- this.config.models = { ...this.config.models, ...data.models };
14534
+ const models = this.config.models;
14535
+ for (const [tier, val] of Object.entries(data.models)) {
14536
+ if (val && val !== "auto") models[tier] = val;
14537
+ else delete models[tier];
14538
+ }
14606
14539
  }
14607
14540
  if (data.budget) {
14608
14541
  if (typeof data.budget.maxCostPerRun === "number") {
@@ -14612,6 +14545,7 @@ var DashboardServer = class {
14612
14545
  this.config.autoBias = data.budget.autoBias;
14613
14546
  }
14614
14547
  }
14548
+ this.persistConfig();
14615
14549
  });
14616
14550
  this.socket.onCascadeRun(async (prompt, model, socketId) => {
14617
14551
  const sessionId = randomUUID();
@@ -14688,6 +14622,20 @@ var DashboardServer = class {
14688
14622
  getSocket() {
14689
14623
  return this.socket;
14690
14624
  }
14625
+ /**
14626
+ * Write the in-memory config back to the workspace config file so mutations
14627
+ * made over the socket (Settings → Save) persist across restarts. Best-effort:
14628
+ * a write failure is logged but never crashes the running dashboard.
14629
+ */
14630
+ persistConfig() {
14631
+ try {
14632
+ const configPath = path23.join(this.workspacePath, CASCADE_CONFIG_FILE);
14633
+ fs21.mkdirSync(path23.dirname(configPath), { recursive: true });
14634
+ fs21.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
14635
+ } catch (err) {
14636
+ console.warn(`[dashboard] Failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
14637
+ }
14638
+ }
14691
14639
  /**
14692
14640
  * Produce a stable dashboard JWT signing secret.
14693
14641
  *
@@ -15187,10 +15135,10 @@ async function dashboardCommand(config, workspacePath = process.cwd()) {
15187
15135
  process.once("exit", onExit);
15188
15136
  try {
15189
15137
  await server.start();
15190
- spin.succeed(chalk11.green(`Dashboard running at http://localhost:${port}`));
15138
+ spin.succeed(chalk9.green(`Dashboard running at http://localhost:${port}`));
15191
15139
  server.refreshRuntime("workspace");
15192
15140
  server.refreshRuntime("global");
15193
- console.log(chalk11.gray(` Press Ctrl+C to stop
15141
+ console.log(chalk9.gray(` Press Ctrl+C to stop
15194
15142
  `));
15195
15143
  await new Promise(() => {
15196
15144
  });
@@ -15199,7 +15147,7 @@ async function dashboardCommand(config, workspacePath = process.cwd()) {
15199
15147
  await server.stop().catch(() => {
15200
15148
  });
15201
15149
  onExit();
15202
- spin.fail(chalk11.red(`Dashboard failed: ${err instanceof Error ? err.message : String(err)}`));
15150
+ spin.fail(chalk9.red(`Dashboard failed: ${err instanceof Error ? err.message : String(err)}`));
15203
15151
  process.exit(1);
15204
15152
  }
15205
15153
  }
@@ -15210,15 +15158,15 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
15210
15158
  const store = new MemoryStore(path23.join(workspacePath, CASCADE_DB_FILE));
15211
15159
  try {
15212
15160
  const identities = store.listIdentities();
15213
- console.log(chalk11.bold("\n Identities:"));
15161
+ console.log(chalk9.bold("\n Identities:"));
15214
15162
  if (identities.length === 0) {
15215
- console.log(chalk11.gray(" No identities found."));
15163
+ console.log(chalk9.gray(" No identities found."));
15216
15164
  } else {
15217
15165
  identities.forEach((id) => {
15218
- const defaultLabel = id.isDefault ? chalk11.green(" [Default]") : "";
15219
- console.log(` - ${chalk11.cyan(id.name)}${defaultLabel}`);
15220
- console.log(chalk11.gray(` ID: ${id.id}`));
15221
- if (id.description) console.log(chalk11.gray(` ${id.description}`));
15166
+ const defaultLabel = id.isDefault ? chalk9.green(" [Default]") : "";
15167
+ console.log(` - ${chalk9.cyan(id.name)}${defaultLabel}`);
15168
+ console.log(chalk9.gray(` ID: ${id.id}`));
15169
+ if (id.description) console.log(chalk9.gray(` ${id.description}`));
15222
15170
  });
15223
15171
  }
15224
15172
  console.log();
@@ -15244,9 +15192,9 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
15244
15192
  isDefault: !!options.default,
15245
15193
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
15246
15194
  });
15247
- console.log(chalk11.green(`
15195
+ console.log(chalk9.green(`
15248
15196
  Successfully created identity: ${name} (${id})`));
15249
- if (options.default) console.log(chalk11.green(" Set as default."));
15197
+ if (options.default) console.log(chalk9.green(" Set as default."));
15250
15198
  console.log();
15251
15199
  } finally {
15252
15200
  store.close();
@@ -15258,7 +15206,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
15258
15206
  const identities = store.listIdentities();
15259
15207
  const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
15260
15208
  if (!match) {
15261
- console.error(chalk11.red(`
15209
+ console.error(chalk9.red(`
15262
15210
  Identity '${query}' not found.
15263
15211
  `));
15264
15212
  process.exit(1);
@@ -15268,7 +15216,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
15268
15216
  store.updateIdentity(existingDefault.id, { isDefault: false });
15269
15217
  }
15270
15218
  store.updateIdentity(match.id, { isDefault: true });
15271
- console.log(chalk11.green(`
15219
+ console.log(chalk9.green(`
15272
15220
  Identity ${match.name} is now the default.
15273
15221
  `));
15274
15222
  } finally {
@@ -15278,7 +15226,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
15278
15226
  return identity;
15279
15227
  }
15280
15228
  async function modelsCommand(options = {}) {
15281
- console.log(chalk11.magenta("\n \u25C8 Cascade Models\n"));
15229
+ console.log(chalk9.magenta("\n \u25C8 Cascade Models\n"));
15282
15230
  const cm = new ConfigManager(process.cwd());
15283
15231
  await cm.load();
15284
15232
  const config = cm.getConfig();
@@ -15286,16 +15234,16 @@ async function modelsCommand(options = {}) {
15286
15234
  try {
15287
15235
  await router.init(config);
15288
15236
  } catch (err) {
15289
- console.error(chalk11.red(` Failed to initialize router: ${err instanceof Error ? err.message : String(err)}`));
15237
+ console.error(chalk9.red(` Failed to initialize router: ${err instanceof Error ? err.message : String(err)}`));
15290
15238
  process.exit(1);
15291
15239
  }
15292
15240
  await withTimeout(router.refreshLiveData(), 6e3, "live data timeout").catch(() => {
15293
15241
  });
15294
15242
  const liveData = router.getLiveData();
15295
15243
  const tiers = [
15296
- { tier: "T1", label: "T1 Administrator", color: chalk11.hex("#7C6AF7") },
15297
- { tier: "T2", label: "T2 Manager", color: chalk11.hex("#5AB4E8") },
15298
- { tier: "T3", label: "T3 Worker", color: chalk11.hex("#5AE8A4") }
15244
+ { tier: "T1", label: "T1 Administrator", color: chalk9.hex("#7C6AF7") },
15245
+ { tier: "T2", label: "T2 Manager", color: chalk9.hex("#5AB4E8") },
15246
+ { tier: "T3", label: "T3 Worker", color: chalk9.hex("#5AE8A4") }
15299
15247
  ];
15300
15248
  let anyMissing = false;
15301
15249
  for (const { tier, label, color } of tiers) {
@@ -15304,48 +15252,72 @@ async function modelsCommand(options = {}) {
15304
15252
  const costIn = model.inputCostPer1kTokens === 0 ? "free" : `$${model.inputCostPer1kTokens.toFixed(4)}/1K in`;
15305
15253
  const costOut = model.outputCostPer1kTokens === 0 ? "free" : `$${model.outputCostPer1kTokens.toFixed(4)}/1K out`;
15306
15254
  const ctx = model.contextWindow >= 1e6 ? `${(model.contextWindow / 1e6).toFixed(1)}M ctx` : `${(model.contextWindow / 1e3).toFixed(0)}K ctx`;
15307
- const local = model.isLocal ? chalk11.gray(" [local]") : "";
15308
- const vision = model.isVisionCapable ? chalk11.gray(" \u{1F441}") : "";
15255
+ const local = model.isLocal ? chalk9.gray(" [local]") : "";
15256
+ const vision = model.isVisionCapable ? chalk9.gray(" \u{1F441}") : "";
15309
15257
  const bench = Math.round(benchmarkScore01(model, "mixed") * 100);
15310
15258
  console.log(
15311
- ` ${color.bold(tier)} ${chalk11.white(col(model.name, 24))}${chalk11.gray(col(model.provider, 16))}` + (options.verbose ? `${chalk11.gray(col(ctx, 12))}${chalk11.gray(col(`bench ${bench}/100`, 14))}${chalk11.gray(`${costIn}, ${costOut}`)}` : `${chalk11.gray(col(ctx, 10))}${chalk11.gray(`bench ${bench}/100`)}`) + local + vision
15259
+ ` ${color.bold(tier)} ${chalk9.white(col(model.name, 24))}${chalk9.gray(col(model.provider, 16))}` + (options.verbose ? `${chalk9.gray(col(ctx, 12))}${chalk9.gray(col(`bench ${bench}/100`, 14))}${chalk9.gray(`${costIn}, ${costOut}`)}` : `${chalk9.gray(col(ctx, 10))}${chalk9.gray(`bench ${bench}/100`)}`) + local + vision
15312
15260
  );
15313
15261
  } else {
15314
- console.log(` ${color.bold(tier)} ${chalk11.red("No model available")} ${chalk11.gray(`(check provider config for ${label})`)}`);
15262
+ console.log(` ${color.bold(tier)} ${chalk9.red("No model available")} ${chalk9.gray(`(check provider config for ${label})`)}`);
15315
15263
  anyMissing = true;
15316
15264
  }
15317
15265
  }
15318
15266
  console.log();
15319
15267
  const providers = config.providers.map((p) => p.type).join(", ") || "(none)";
15320
- console.log(chalk11.gray(` Configured providers: ${providers}`));
15268
+ console.log(chalk9.gray(` Configured providers: ${providers}`));
15321
15269
  if (liveData) {
15322
15270
  const src = liveData.getDataSource();
15323
15271
  const gen = liveData.getGeneratedAt();
15324
15272
  const srcLabel = src === "live" ? "live (just fetched)" : src === "cache" ? "cached" : "bundled";
15325
- console.log(chalk11.gray(
15273
+ console.log(chalk9.gray(
15326
15274
  ` Benchmark data: ${srcLabel}` + (gen ? ` \xB7 updated ${gen.slice(0, 10)}` : "") + ` \xB7 pricing: ${liveData.hasLivePricing() ? "live (OpenRouter)" : "catalog"}`
15327
15275
  ));
15328
15276
  }
15329
15277
  if (options.verbose) {
15330
15278
  console.log();
15331
- console.log(chalk11.white(" Available models by provider:\n"));
15279
+ console.log(chalk9.white(" Available models by provider:\n"));
15332
15280
  const allProviderTypes = [...new Set(config.providers.map((p) => p.type))];
15333
15281
  for (const providerType of allProviderTypes) {
15334
15282
  const available = router.getModelsForProvider(providerType);
15335
15283
  if (available.length === 0) continue;
15336
- console.log(chalk11.gray(` ${providerType}:`));
15284
+ console.log(chalk9.gray(` ${providerType}:`));
15337
15285
  for (const m of available) {
15338
15286
  const override = config.models.t1 === m.id ? " \u2190 T1" : config.models.t2 === m.id ? " \u2190 T2" : config.models.t3 === m.id ? " \u2190 T3" : "";
15339
- console.log(` ${chalk11.white(col(m.name, 28))}${chalk11.gray(m.id)}${chalk11.yellow(override)}`);
15287
+ console.log(` ${chalk9.white(col(m.name, 28))}${chalk9.gray(m.id)}${chalk9.yellow(override)}`);
15340
15288
  }
15341
15289
  console.log();
15342
15290
  }
15343
15291
  }
15344
15292
  if (anyMissing) {
15345
- console.log(chalk11.yellow(" Some tiers have no available model. Run `cascade doctor` for details.\n"));
15293
+ console.log(chalk9.yellow(" Some tiers have no available model. Run `cascade doctor` for details.\n"));
15346
15294
  } else {
15347
- console.log(chalk11.green(" All tiers are configured.\n"));
15295
+ console.log(chalk9.green(" All tiers are configured.\n"));
15296
+ }
15297
+ }
15298
+ async function setModelCommand(action, tierArg, value) {
15299
+ const tier = (tierArg ?? "").toLowerCase();
15300
+ if (!["t1", "t2", "t3"].includes(tier)) {
15301
+ console.error(chalk9.red(` Invalid tier "${tierArg ?? ""}". Use t1, t2, or t3.`));
15302
+ console.log(chalk9.gray(" e.g. cascade models set t1 anthropic:claude-opus-4-8"));
15303
+ process.exit(1);
15304
+ }
15305
+ const override = action === "unset" ? "auto" : (value ?? "").trim();
15306
+ if (action === "set" && !override) {
15307
+ console.error(chalk9.red(' Missing value. e.g. cascade models set t3 openai:gpt-4o-mini (or "auto")'));
15308
+ process.exit(1);
15348
15309
  }
15310
+ const cm = new ConfigManager(process.cwd());
15311
+ await cm.load();
15312
+ const config = cm.getConfig();
15313
+ const models = { ...config.models };
15314
+ if (override === "auto") delete models[tier];
15315
+ else models[tier] = override;
15316
+ await cm.updateConfig({ models });
15317
+ const label = override === "auto" ? chalk9.gray("auto (routing decides)") : chalk9.white(override);
15318
+ console.log(chalk9.green(`
15319
+ \u2713 ${tier.toUpperCase()} model set to ${label}
15320
+ `));
15349
15321
  }
15350
15322
  function col(s, width) {
15351
15323
  return s.length < width ? s.padEnd(width) : `${s} `;
@@ -15367,7 +15339,7 @@ async function exportCommand(options = {}) {
15367
15339
  }
15368
15340
  store = new MemoryStore(dbPath);
15369
15341
  } catch (err) {
15370
- spin.fail(chalk11.red(`Cannot open memory store: ${err instanceof Error ? err.message : String(err)}`));
15342
+ spin.fail(chalk9.red(`Cannot open memory store: ${err instanceof Error ? err.message : String(err)}`));
15371
15343
  process.exit(1);
15372
15344
  }
15373
15345
  try {
@@ -15375,7 +15347,7 @@ async function exportCommand(options = {}) {
15375
15347
  if (options.sessionId) {
15376
15348
  const session2 = store.getSession(options.sessionId);
15377
15349
  if (!session2) {
15378
- spin.fail(chalk11.red(`Session "${options.sessionId}" not found.`));
15350
+ spin.fail(chalk9.red(`Session "${options.sessionId}" not found.`));
15379
15351
  process.exit(1);
15380
15352
  }
15381
15353
  sessions = [session2];
@@ -15383,14 +15355,14 @@ async function exportCommand(options = {}) {
15383
15355
  const limit = options.last ?? 10;
15384
15356
  sessions = store.listSessions(void 0, limit);
15385
15357
  if (sessions.length === 0) {
15386
- spin.warn(chalk11.yellow("No sessions found."));
15358
+ spin.warn(chalk9.yellow("No sessions found."));
15387
15359
  return;
15388
15360
  }
15389
15361
  const latest = sessions[0];
15390
15362
  const full = store.getSession(latest.id);
15391
15363
  sessions = full ? [full] : [];
15392
15364
  if (sessions.length === 0) {
15393
- spin.fail(chalk11.red("Could not load latest session."));
15365
+ spin.fail(chalk9.red("Could not load latest session."));
15394
15366
  process.exit(1);
15395
15367
  }
15396
15368
  }
@@ -15402,15 +15374,15 @@ async function exportCommand(options = {}) {
15402
15374
  const defaultFile = `cascade-export-${safeName}${ext}`;
15403
15375
  const outPath = options.output ? path23.resolve(options.output) : path23.join(process.cwd(), defaultFile);
15404
15376
  await fs9.writeFile(outPath, content, "utf-8");
15405
- spin.succeed(chalk11.green(`Exported to ${chalk11.white(outPath)}`));
15377
+ spin.succeed(chalk9.green(`Exported to ${chalk9.white(outPath)}`));
15406
15378
  const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
15407
15379
  console.log();
15408
- console.log(chalk11.gray(` Session: ${session.title}`));
15409
- console.log(chalk11.gray(` Messages: ${messageCount}`));
15410
- console.log(chalk11.gray(` Format: ${format}`));
15380
+ console.log(chalk9.gray(` Session: ${session.title}`));
15381
+ console.log(chalk9.gray(` Messages: ${messageCount}`));
15382
+ console.log(chalk9.gray(` Format: ${format}`));
15411
15383
  console.log();
15412
15384
  } catch (err) {
15413
- spin.fail(chalk11.red(`Export failed: ${err instanceof Error ? err.message : String(err)}`));
15385
+ spin.fail(chalk9.red(`Export failed: ${err instanceof Error ? err.message : String(err)}`));
15414
15386
  process.exit(1);
15415
15387
  }
15416
15388
  }
@@ -15467,9 +15439,9 @@ function buildJsonExport(session) {
15467
15439
  async function linkCommand(target, options = {}) {
15468
15440
  const found = await discoverCredentials();
15469
15441
  if (found.length === 0) {
15470
- console.log(chalk11.yellow("\n No reusable credentials found.\n"));
15471
- console.log(chalk11.gray(" Cascade looks for Claude Code, Codex, Gemini CLI, and GitHub Copilot logins,"));
15472
- console.log(chalk11.gray(" plus ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY in your environment.\n"));
15442
+ console.log(chalk9.yellow("\n No reusable credentials found.\n"));
15443
+ console.log(chalk9.gray(" Cascade looks for Claude Code, Codex, Gemini CLI, and GitHub Copilot logins,"));
15444
+ console.log(chalk9.gray(" plus ANTHROPIC_API_KEY / OPENAI_API_KEY / GEMINI_API_KEY in your environment.\n"));
15473
15445
  return;
15474
15446
  }
15475
15447
  if (!target) {
@@ -15478,7 +15450,7 @@ async function linkCommand(target, options = {}) {
15478
15450
  }
15479
15451
  const provider = normalizeProvider(target);
15480
15452
  if (!provider) {
15481
- console.log(chalk11.red(`
15453
+ console.log(chalk9.red(`
15482
15454
  Unknown provider "${target}". Use one of: anthropic, openai, gemini.
15483
15455
  `));
15484
15456
  return;
@@ -15486,46 +15458,46 @@ async function linkCommand(target, options = {}) {
15486
15458
  const candidates2 = found.filter((c) => c.provider === provider);
15487
15459
  const chosen = candidates2.find((c) => c.directlyUsable) ?? candidates2[0];
15488
15460
  if (!chosen) {
15489
- console.log(chalk11.yellow(`
15461
+ console.log(chalk9.yellow(`
15490
15462
  No detected credential maps to "${provider}".
15491
15463
  `));
15492
15464
  return;
15493
15465
  }
15494
15466
  if (!chosen.directlyUsable) {
15495
- console.log(chalk11.yellow(`
15467
+ console.log(chalk9.yellow(`
15496
15468
  Found a ${chosen.sourceTool} credential, but it can't be used against the standard ${provider} API.`));
15497
- if (chosen.warning) console.log(chalk11.gray(` ${chosen.warning}`));
15498
- console.log(chalk11.gray(" Cascade won't adopt it because it would create a non-working provider.\n"));
15469
+ if (chosen.warning) console.log(chalk9.gray(` ${chosen.warning}`));
15470
+ console.log(chalk9.gray(" Cascade won't adopt it because it would create a non-working provider.\n"));
15499
15471
  return;
15500
15472
  }
15501
15473
  if (chosen.kind === "oauth" && !options.acceptRisk) {
15502
- console.log(chalk11.yellow(`
15474
+ console.log(chalk9.yellow(`
15503
15475
  ${chosen.sourceTool} provides a subscription OAuth token, not an API key.`));
15504
- if (chosen.warning) console.log(chalk11.gray(` ${chosen.warning}`));
15505
- console.log(chalk11.gray(" Re-run with --accept-risk to adopt it anyway:\n"));
15506
- console.log(chalk11.cyan(` cascade link ${provider} --accept-risk
15476
+ if (chosen.warning) console.log(chalk9.gray(` ${chosen.warning}`));
15477
+ console.log(chalk9.gray(" Re-run with --accept-risk to adopt it anyway:\n"));
15478
+ console.log(chalk9.cyan(` cascade link ${provider} --accept-risk
15507
15479
  `));
15508
15480
  return;
15509
15481
  }
15510
15482
  await adoptCredential(chosen, options.workspace ?? process.cwd());
15511
- console.log(chalk11.green(`
15483
+ console.log(chalk9.green(`
15512
15484
  \u2713 Linked ${provider} using your ${chosen.sourceTool} credential (${maskSecret(chosen.secret)}).`));
15513
15485
  if (chosen.kind === "oauth") {
15514
- console.log(chalk11.gray(" Adopted as an OAuth bearer token \u2014 revoke it in the source tool to disable."));
15486
+ console.log(chalk9.gray(" Adopted as an OAuth bearer token \u2014 revoke it in the source tool to disable."));
15515
15487
  }
15516
- console.log(chalk11.gray(" Run `cascade doctor` to verify, or `cascade` to start.\n"));
15488
+ console.log(chalk9.gray(" Run `cascade doctor` to verify, or `cascade` to start.\n"));
15517
15489
  }
15518
15490
  function printDiscovered(found) {
15519
- console.log(chalk11.magenta("\n \u25C8 Detected credentials\n"));
15491
+ console.log(chalk9.magenta("\n \u25C8 Detected credentials\n"));
15520
15492
  for (const c of found) {
15521
- const usable = c.directlyUsable ? chalk11.green("usable") : chalk11.yellow("needs vendor backend");
15522
- const kind = c.kind === "oauth" ? chalk11.yellow("oauth") : chalk11.gray("api-key");
15523
- console.log(` ${chalk11.white(c.provider.padEnd(18))} ${chalk11.gray(maskSecret(c.secret).padEnd(12))} ${kind} ${usable}`);
15524
- console.log(chalk11.gray(` from ${c.sourceTool}`));
15525
- if (c.warning) console.log(chalk11.yellow(` \u26A0 ${c.warning}`));
15493
+ const usable = c.directlyUsable ? chalk9.green("usable") : chalk9.yellow("needs vendor backend");
15494
+ const kind = c.kind === "oauth" ? chalk9.yellow("oauth") : chalk9.gray("api-key");
15495
+ console.log(` ${chalk9.white(c.provider.padEnd(18))} ${chalk9.gray(maskSecret(c.secret).padEnd(12))} ${kind} ${usable}`);
15496
+ console.log(chalk9.gray(` from ${c.sourceTool}`));
15497
+ if (c.warning) console.log(chalk9.yellow(` \u26A0 ${c.warning}`));
15526
15498
  }
15527
- console.log(chalk11.gray("\n Adopt one with: ") + chalk11.cyan("cascade link <provider> [--accept-risk]"));
15528
- console.log(chalk11.gray(" --accept-risk is required for subscription OAuth tokens.\n"));
15499
+ console.log(chalk9.gray("\n Adopt one with: ") + chalk9.cyan("cascade link <provider> [--accept-risk]"));
15500
+ console.log(chalk9.gray(" --accept-risk is required for subscription OAuth tokens.\n"));
15529
15501
  }
15530
15502
  function normalizeProvider(target) {
15531
15503
  const t = target.toLowerCase();
@@ -15558,12 +15530,12 @@ async function telemetryCommand(action) {
15558
15530
  if (action === "status") {
15559
15531
  const state = config.telemetry?.enabled ? "ON" : "OFF";
15560
15532
  console.log();
15561
- console.log(chalk11.magenta(" \u25C8 Cascade Telemetry"));
15533
+ console.log(chalk9.magenta(" \u25C8 Cascade Telemetry"));
15562
15534
  console.log();
15563
- console.log(` Status: ${config.telemetry?.enabled ? chalk11.green(state) : chalk11.gray(state)}`);
15564
- console.log(chalk11.gray(" Scope: anonymous session metadata only (no prompts/outputs)"));
15535
+ console.log(` Status: ${config.telemetry?.enabled ? chalk9.green(state) : chalk9.gray(state)}`);
15536
+ console.log(chalk9.gray(" Scope: anonymous session metadata only (no prompts/outputs)"));
15565
15537
  console.log();
15566
- console.log(chalk11.gray(" Toggle with: cascade telemetry on | cascade telemetry off"));
15538
+ console.log(chalk9.gray(" Toggle with: cascade telemetry on | cascade telemetry off"));
15567
15539
  console.log();
15568
15540
  return;
15569
15541
  }
@@ -15577,11 +15549,11 @@ async function telemetryCommand(action) {
15577
15549
  });
15578
15550
  console.log();
15579
15551
  if (enabled) {
15580
- console.log(chalk11.green(` \u2713 Telemetry enabled.`));
15581
- console.log(chalk11.gray(" Anonymous session metadata (no prompts, no outputs) will be sent."));
15552
+ console.log(chalk9.green(` \u2713 Telemetry enabled.`));
15553
+ console.log(chalk9.gray(" Anonymous session metadata (no prompts, no outputs) will be sent."));
15582
15554
  } else {
15583
- console.log(chalk11.yellow(` \u2713 Telemetry disabled.`));
15584
- console.log(chalk11.gray(" No events will be transmitted from this workspace."));
15555
+ console.log(chalk9.yellow(` \u2713 Telemetry disabled.`));
15556
+ console.log(chalk9.gray(" No events will be transmitted from this workspace."));
15585
15557
  }
15586
15558
  console.log();
15587
15559
  }
@@ -15591,11 +15563,11 @@ async function statsCommand() {
15591
15563
  await tracker.load();
15592
15564
  const all = tracker.getAll();
15593
15565
  if (all.size === 0) {
15594
- console.log(chalk11.dim("\n No routing history yet \u2014 run some tasks first.\n"));
15566
+ console.log(chalk9.dim("\n No routing history yet \u2014 run some tasks first.\n"));
15595
15567
  return;
15596
15568
  }
15597
- console.log(chalk11.magenta("\n \u25C8 Auto-Routing History\n"));
15598
- console.log(chalk11.dim(" Per-task-type model performance learned from past runs.\n"));
15569
+ console.log(chalk9.magenta("\n \u25C8 Auto-Routing History\n"));
15570
+ console.log(chalk9.dim(" Per-task-type model performance learned from past runs.\n"));
15599
15571
  for (const taskType of TASK_TYPES) {
15600
15572
  const entries = [];
15601
15573
  for (const [key, stat] of all) {
@@ -15607,21 +15579,21 @@ async function statsCommand() {
15607
15579
  }
15608
15580
  if (entries.length === 0) continue;
15609
15581
  entries.sort((a, b) => b.successRate - a.successRate || b.samples - a.samples);
15610
- console.log(chalk11.bold(` ${taskType.toUpperCase()}`));
15582
+ console.log(chalk9.bold(` ${taskType.toUpperCase()}`));
15611
15583
  const header = ` ${"Model".padEnd(36)} ${"Success".padEnd(9)} ${"Samples".padEnd(9)} Avg cost`;
15612
- console.log(chalk11.dim(header));
15613
- console.log(chalk11.dim(" " + "\u2500".repeat(62)));
15584
+ console.log(chalk9.dim(header));
15585
+ console.log(chalk9.dim(" " + "\u2500".repeat(62)));
15614
15586
  for (const e of entries) {
15615
15587
  const pct = `${Math.round(e.successRate * 100)}%`;
15616
15588
  const cost = e.avgCostUsd < 1e-4 ? "<$0.0001" : `$${e.avgCostUsd.toFixed(4)}`;
15617
- const color = e.successRate >= 0.8 ? chalk11.green : e.successRate >= 0.5 ? chalk11.yellow : chalk11.red;
15589
+ const color = e.successRate >= 0.8 ? chalk9.green : e.successRate >= 0.5 ? chalk9.yellow : chalk9.red;
15618
15590
  console.log(
15619
- ` ${e.modelId.padEnd(36)} ${color(pct.padEnd(9))} ${String(e.samples).padEnd(9)} ${chalk11.dim(cost)}`
15591
+ ` ${e.modelId.padEnd(36)} ${color(pct.padEnd(9))} ${String(e.samples).padEnd(9)} ${chalk9.dim(cost)}`
15620
15592
  );
15621
15593
  }
15622
15594
  console.log();
15623
15595
  }
15624
- console.log(chalk11.dim(" tip: use /rate good | bad after a task to improve these scores.\n"));
15596
+ console.log(chalk9.dim(" tip: use /rate good | bad after a task to improve these scores.\n"));
15625
15597
  }
15626
15598
 
15627
15599
  // src/cli/index.ts
@@ -15636,7 +15608,7 @@ function warnIfBuildIsStale() {
15636
15608
  if (pkg.name !== "cascade-ai") continue;
15637
15609
  if (pkg.version && pkg.version !== CASCADE_VERSION) {
15638
15610
  console.error(
15639
- chalk11.yellow(
15611
+ chalk9.yellow(
15640
15612
  `\u26A0 Stale build: compiled output is v${CASCADE_VERSION} but the source tree is v${pkg.version}.
15641
15613
  Run: npm install && npm run build`
15642
15614
  )
@@ -15704,13 +15676,17 @@ program.command("dashboard").description("Launch the web dashboard").option("-p,
15704
15676
  program.command("run <prompt>").description("Run a single prompt and exit").option("-t, --theme <name>", "Color theme", DEFAULT_THEME).option("-i, --identity <name>", "Identity name or ID").action(async (prompt, opts) => {
15705
15677
  await runHeadless(prompt, { theme: opts.theme, workspace: process.cwd(), identity: opts.identity });
15706
15678
  });
15707
- program.command("models").description("List available AI models for each tier").option("-v, --verbose", "Show all models per provider with pricing").action(async (opts) => {
15708
- await modelsCommand({ verbose: opts.verbose });
15679
+ program.command("models [action] [tier] [value]").description("List AI models per tier, or set/unset a tier provider+model (e.g. models set t1 anthropic:claude-opus-4-8)").option("-v, --verbose", "Show all models per provider with pricing").action(async (action, tier, value, opts) => {
15680
+ if (action === "set" || action === "unset") {
15681
+ await setModelCommand(action, tier, value);
15682
+ } else {
15683
+ await modelsCommand({ verbose: opts.verbose });
15684
+ }
15709
15685
  });
15710
15686
  program.command("telemetry [action]").description("Toggle anonymous usage telemetry (on | off | status). Default: status").action(async (action) => {
15711
15687
  const normalized = (action ?? "status").toLowerCase();
15712
15688
  if (normalized !== "on" && normalized !== "off" && normalized !== "status") {
15713
- console.error(chalk11.red(`Unknown action: ${action}. Use: on | off | status`));
15689
+ console.error(chalk9.red(`Unknown action: ${action}. Use: on | off | status`));
15714
15690
  process.exit(1);
15715
15691
  }
15716
15692
  await telemetryCommand(normalized);
@@ -15732,14 +15708,14 @@ async function startRepl(options) {
15732
15708
  try {
15733
15709
  await cm.load();
15734
15710
  } catch (err) {
15735
- console.error(chalk11.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
15736
- console.error(chalk11.gray("Run `cascade init` to set up this directory."));
15711
+ console.error(chalk9.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
15712
+ console.error(chalk9.gray("Run `cascade init` to set up this directory."));
15737
15713
  process.exit(1);
15738
15714
  }
15739
15715
  let config = cm.getConfig();
15740
15716
  const needsSetup = !config.providers?.length || config.providers.every((p) => p.type !== "ollama" && !p.apiKey);
15741
15717
  if (needsSetup) {
15742
- console.log(chalk11.magenta(" \u25C8 No providers configured \u2014 launching setup wizard\u2026"));
15718
+ console.log(chalk9.magenta(" \u25C8 No providers configured \u2014 launching setup wizard\u2026"));
15743
15719
  console.log();
15744
15720
  config = await runSetupWizard(workspacePath);
15745
15721
  await cm.updateConfig(config);
@@ -15773,24 +15749,24 @@ async function runHeadless(prompt, options) {
15773
15749
  try {
15774
15750
  await cm.load();
15775
15751
  } catch (err) {
15776
- console.error(chalk11.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
15777
- console.error(chalk11.gray("Run `cascade init` to set up this directory."));
15752
+ console.error(chalk9.red(`Config error: ${err instanceof Error ? err.message : String(err)}`));
15753
+ console.error(chalk9.gray("Run `cascade init` to set up this directory."));
15778
15754
  process.exit(1);
15779
15755
  }
15780
15756
  const config = cm.getConfig();
15781
15757
  const needsSetup = !config.providers?.length || config.providers.every((p) => p.type !== "ollama" && !p.apiKey);
15782
15758
  if (needsSetup) {
15783
- console.error(chalk11.red("No providers configured. Run `cascade init` first."));
15759
+ console.error(chalk9.red("No providers configured. Run `cascade init` first."));
15784
15760
  process.exit(1);
15785
15761
  }
15786
15762
  const cascade = new Cascade(config, workspacePath);
15787
15763
  try {
15788
15764
  await cascade.init();
15789
15765
  } catch (err) {
15790
- console.error(chalk11.red(`Initialization failed: ${err instanceof Error ? err.message : String(err)}`));
15766
+ console.error(chalk9.red(`Initialization failed: ${err instanceof Error ? err.message : String(err)}`));
15791
15767
  process.exit(1);
15792
15768
  }
15793
- console.error(chalk11.gray(" \u25C8 Running headlessly \u2014 tool approvals are auto-granted."));
15769
+ console.error(chalk9.gray(" \u25C8 Running headlessly \u2014 tool approvals are auto-granted."));
15794
15770
  let lastProgress = "";
15795
15771
  cascade.on("tier:status", (ev) => {
15796
15772
  const action = ev?.currentAction?.trim();
@@ -15798,7 +15774,7 @@ async function runHeadless(prompt, options) {
15798
15774
  const line = ` \xB7 ${ev.role ?? ""} ${action}`.trimEnd();
15799
15775
  if (line === lastProgress) return;
15800
15776
  lastProgress = line;
15801
- console.error(chalk11.gray(line));
15777
+ console.error(chalk9.gray(line));
15802
15778
  });
15803
15779
  try {
15804
15780
  const result = await cascade.run({
@@ -15809,7 +15785,7 @@ async function runHeadless(prompt, options) {
15809
15785
  });
15810
15786
  process.stdout.write(result.output.trimEnd() + "\n");
15811
15787
  } catch (err) {
15812
- console.error(chalk11.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
15788
+ console.error(chalk9.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
15813
15789
  await cascade.close().catch(() => {
15814
15790
  });
15815
15791
  process.exit(1);
@@ -15821,10 +15797,10 @@ async function runHeadless(prompt, options) {
15821
15797
  function printBanner() {
15822
15798
  if (process.stdout.columns < 60) return;
15823
15799
  console.log();
15824
- console.log(chalk11.hex("#7C6AF7").bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
15825
- console.log(chalk11.hex("#7C6AF7").bold(" \u2551") + chalk11.white.bold(" \u25C8 CASCADE AI") + chalk11.gray(" v" + CASCADE_VERSION + " ") + chalk11.hex("#7C6AF7").bold("\u2551"));
15826
- console.log(chalk11.hex("#7C6AF7").bold(" \u2551") + chalk11.gray(" Multi-Tier Orchestration ") + chalk11.hex("#7C6AF7").bold("\u2551"));
15827
- console.log(chalk11.hex("#7C6AF7").bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
15800
+ console.log(chalk9.hex("#7C6AF7").bold(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
15801
+ console.log(chalk9.hex("#7C6AF7").bold(" \u2551") + chalk9.white.bold(" \u25C8 CASCADE AI") + chalk9.gray(" v" + CASCADE_VERSION + " ") + chalk9.hex("#7C6AF7").bold("\u2551"));
15802
+ console.log(chalk9.hex("#7C6AF7").bold(" \u2551") + chalk9.gray(" Multi-Tier Orchestration ") + chalk9.hex("#7C6AF7").bold("\u2551"));
15803
+ console.log(chalk9.hex("#7C6AF7").bold(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
15828
15804
  console.log();
15829
15805
  }
15830
15806
  program.parse(process.argv);