subconscious-cli 6.0.0 → 6.0.1

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
@@ -82,7 +82,7 @@ Windows has its own implementation in `bin/windows/`, selected only when
82
82
  Node runs on Windows. macOS/Linux continue to use the existing, unchanged shell
83
83
  runbooks. WSL continues to use the Linux implementation.
84
84
 
85
- Use Node.js 22 or 24 and Windows PowerShell 5.1+ (included with Windows), from either
85
+ Use Node.js 20, 22, or 24 and Windows PowerShell 5.1+ (included with Windows), from either
86
86
  PowerShell or Command Prompt. The Windows CLI integrations do not require Bash,
87
87
  `jq`, `curl`, or WSL. The agents themselves must support your Windows version;
88
88
  their own dependencies still apply.
@@ -129,7 +129,7 @@ VS Code's secret store and prompts for its key. Its configuration goes under
129
129
  `%APPDATA%\Code\User` (or Code - Insiders/VSCodium). Rerun install after moving
130
130
  your Node installation, because hooks record the absolute Node executable path.
131
131
 
132
- To install the native x64 `sc.exe` (stable releases starting with 0.1.4), clear
132
+ To install the native x64 `marathon.exe` (stable releases starting with 0.1.4), clear
133
133
  any preview version pin from the current PowerShell session:
134
134
 
135
135
  ```powershell
@@ -330,6 +330,7 @@ subconscious/glm-5.3-marathon (default)
330
330
  subconscious/glm-5.2
331
331
  subconscious/tim-qwen3.6-27b
332
332
  subconscious/deepseek-v4-flash-marathon
333
+ subconscious/deepseek-v4.1-flash-marathon
333
334
  ```
334
335
 
335
336
  Select a model per run, save it in the current profile, or override it through
@@ -443,6 +444,19 @@ Existing credentials and profiles under `~/.subcon` are copied into
443
444
  `~/.subconscious` automatically on first use. The legacy files are left in
444
445
  place so migration is recoverable.
445
446
 
447
+ Set `SUBC_CONFIG_DIR` to keep the saved key and profiles in another directory
448
+ instead of `~/.subconscious`. Legacy `~/.subcon` files are not migrated when it
449
+ is set.
450
+
451
+ `subc feedback` sends a message to the Subconscious support team from the
452
+ terminal, using the active profile's API key. Pass `-s`/`--subject`,
453
+ `-m`/`--message`, and `--image <path>` (JPEG or PNG, repeatable), or run it
454
+ without `--message` in a terminal to be prompted:
455
+
456
+ ```bash
457
+ subc feedback -s "Bug" -m "Playground won't load"
458
+ ```
459
+
446
460
  ## Contributing
447
461
 
448
462
  Setup, commit message rules, and local checks are in [CONTRIBUTING.md](CONTRIBUTING.md). `npm install` installs the commit-message hook. Version bumps and the changelog are created by Release Please when a release pull request merges to `main`.
package/bin/agents.js CHANGED
@@ -10,25 +10,28 @@
10
10
  * single source of truth `agents/registry.json`. Run `node scripts/generate-agents.js` to update.
11
11
  */
12
12
 
13
- import { spawn, execFileSync } from 'node:child_process';
14
- import { readFileSync } from 'node:fs';
13
+ import { execFileSync, spawn } from 'node:child_process';
14
+ import { constants as fsConstants, readFileSync } from 'node:fs';
15
15
  import fs from 'node:fs/promises';
16
- import { constants as fsConstants } from 'node:fs';
17
16
  import os from 'node:os';
18
17
  import path from 'node:path';
19
18
  import readline from 'node:readline';
20
19
  import { fileURLToPath } from 'node:url';
21
- import { c } from './colors.js';
22
- import { runWindowsAgent } from './windows/agents.js';
23
20
  import { getApiKey } from './auth.js';
21
+ import { c } from './colors.js';
22
+ import {
23
+ isLiveModelSource,
24
+ PUBLIC_CATALOG_FALLBACK_MESSAGE,
25
+ resolveModelCatalog,
26
+ } from './models.js';
24
27
  import {
25
28
  isUnsetSetting,
26
29
  profileSettingsForAgent,
27
30
  resolvedModelSetting,
28
31
  resolvedProfileValues,
29
32
  } from './profiles.js';
30
- import { isLiveModelSource, PUBLIC_CATALOG_FALLBACK_MESSAGE, resolveModelCatalog } from './models.js';
31
33
  import { compareVersions } from './update-check.js';
34
+ import { runWindowsAgent } from './windows/agents.js';
32
35
 
33
36
  export const MIN_CLAUDE_CODE_VERSION = '2.1.242';
34
37
 
@@ -74,7 +77,8 @@ function substitute(value, ctx) {
74
77
  return JSON.stringify(substitute(value.$json, ctx));
75
78
  }
76
79
  const out = {};
77
- for (const key of keys) out[substituteString(key, ctx)] = substitute(value[key], ctx);
80
+ for (const key of keys)
81
+ out[substituteString(key, ctx)] = substitute(value[key], ctx);
78
82
  return out;
79
83
  }
80
84
  return value;
@@ -87,8 +91,10 @@ function substitute(value, ctx) {
87
91
  * Returns `{ command, fallback }` where `fallback` may be undefined.
88
92
  */
89
93
  function resolveInstall(install) {
90
- if (typeof install === 'string') return { command: install, fallback: undefined };
91
- if (!install || typeof install !== 'object') return { command: undefined, fallback: undefined };
94
+ if (typeof install === 'string')
95
+ return { command: install, fallback: undefined };
96
+ if (!install || typeof install !== 'object')
97
+ return { command: undefined, fallback: undefined };
92
98
  const command =
93
99
  install[process.platform] ||
94
100
  install.linux ||
@@ -103,7 +109,12 @@ const AGENTS = registry.agents
103
109
  .filter((agent) => agent.cli !== false)
104
110
  .map((agent) => {
105
111
  const { command, fallback } = resolveInstall(agent.install);
106
- return { ...agent, bin: agent.binByPlatform?.[process.platform] || agent.bin, install: command, installFallback: fallback };
112
+ return {
113
+ ...agent,
114
+ bin: agent.binByPlatform?.[process.platform] || agent.bin,
115
+ install: command,
116
+ installFallback: fallback,
117
+ };
107
118
  });
108
119
  const BY_ALIAS = new Map();
109
120
  for (const agent of AGENTS) {
@@ -131,7 +142,9 @@ const SETUP_ACTIONS = new Set(['install', 'status', 'uninstall']);
131
142
  const DROPPED_SETUP_HELPERS = new Set(['use', 'env', 'unset']);
132
143
 
133
144
  export function agentSetupActions(agent) {
134
- return Array.isArray(agent.runbook?.setupActions) ? agent.runbook.setupActions : [];
145
+ return Array.isArray(agent.runbook?.setupActions)
146
+ ? agent.runbook.setupActions
147
+ : [];
135
148
  }
136
149
 
137
150
  export function agentCommandName(agent) {
@@ -180,13 +193,19 @@ const AGENT_HELP = {
180
193
  'Launches the native Subconscious coding agent with the selected profile gateway, credential, and model.',
181
194
  options: [
182
195
  ['help', 'Show this help'],
183
- ['install', process.platform === 'win32' ? 'Install a verified Windows release, when published upstream' : 'Install the latest precompiled Linux or macOS release'],
196
+ [
197
+ 'install',
198
+ process.platform === 'win32'
199
+ ? 'Install a verified Windows release, when published upstream'
200
+ : 'Install the latest precompiled Linux or macOS release',
201
+ ],
184
202
  ['--model MODEL', 'Override the profile model for this launch'],
185
203
  ['ARGS...', 'Pass arguments directly to Marathon'],
186
204
  ],
187
205
  },
188
206
  'claude-code': {
189
- usage: 'subc [-p NAME] claude [help|status|uninstall] [Claude arguments...]',
207
+ usage:
208
+ 'subc [-p NAME] claude [help|status|uninstall] [Claude arguments...]',
190
209
  behavior:
191
210
  'Launches Claude Code with the active Subconscious profile and model picker. Persistent files are leftover-only: subc claude uninstall.',
192
211
  options: [
@@ -200,34 +219,51 @@ const AGENT_HELP = {
200
219
  ],
201
220
  },
202
221
  codex: {
203
- usage: 'subc [-p NAME] codex [help|install|status|uninstall] [Codex arguments...]',
222
+ usage:
223
+ 'subc [-p NAME] codex [help|install|status|uninstall] [Codex arguments...]',
204
224
  behavior:
205
225
  'Launches Codex with a temporary Subconscious provider catalog. Compaction hooks are merged into ~/.codex/hooks.json on first launch and removed with subc codex uninstall; Codex will not run them until you trust them with /hooks once.',
206
226
  options: [
207
227
  ['help', 'Show this help'],
208
- ['install', 'Install the Subconscious compaction hooks (then trust with /hooks)'],
228
+ [
229
+ 'install',
230
+ 'Install the Subconscious compaction hooks (then trust with /hooks)',
231
+ ],
209
232
  ['status', 'Inspect the installed compaction hooks'],
210
233
  ['uninstall', 'Remove only the Subconscious Codex hooks'],
211
234
  ['--model MODEL', 'Override the profile model for this launch'],
212
235
  ['--context-window N', 'Override catalog context_window'],
213
236
  ['--max-context-window N', 'Override catalog max_context_window'],
214
- ['--auto-compact-token-limit N', 'Override the automatic compaction threshold'],
237
+ [
238
+ '--auto-compact-token-limit N',
239
+ 'Override the automatic compaction threshold',
240
+ ],
215
241
  ['--reasoning-effort LEVEL', 'Use none, low, medium, high, or max'],
216
242
  ['--external-tools', 'Enable Codex apps/plugins for this launch'],
217
- ['--stream-idle-timeout MS', 'Allow a longer silent think before timing out'],
243
+ [
244
+ '--stream-idle-timeout MS',
245
+ 'Allow a longer silent think before timing out',
246
+ ],
218
247
  ['--max-subagents N', 'Subagents allowed to run at once (default 4)'],
219
- ['--subagent-effort LEVEL', 'Effort for subagents, separate from the parent'],
248
+ [
249
+ '--subagent-effort LEVEL',
250
+ 'Effort for subagents, separate from the parent',
251
+ ],
220
252
  ['-- ARGS...', 'Pass remaining arguments to Codex'],
221
253
  ],
222
254
  },
223
255
  opencode: {
224
- usage: 'subc [-p NAME] opencode [help|status|uninstall] [OpenCode arguments...]',
256
+ usage:
257
+ 'subc [-p NAME] opencode [help|status|uninstall] [OpenCode arguments...]',
225
258
  behavior:
226
259
  'Launches OpenCode with an ephemeral provider containing every Subconscious model. Persistent files are leftover-only: subc opencode uninstall.',
227
260
  options: [
228
261
  ['help', 'Show this help'],
229
262
  ['status', 'Inspect leftover OpenCode Subconscious config'],
230
- ['uninstall', 'Remove only the Subconscious OpenCode provider and plugin'],
263
+ [
264
+ 'uninstall',
265
+ 'Remove only the Subconscious OpenCode provider and plugin',
266
+ ],
231
267
  ['--model MODEL', 'Override the profile model for this launch'],
232
268
  ['ARGS...', 'Pass arguments directly to OpenCode'],
233
269
  ],
@@ -245,7 +281,8 @@ const AGENT_HELP = {
245
281
  },
246
282
  copilot: {
247
283
  usage: 'subc [-p NAME] copilot [help|install|status|uninstall]',
248
- behavior: 'Manages the VS Code model provider and Copilot correlation hooks.',
284
+ behavior:
285
+ 'Manages the VS Code model provider and Copilot correlation hooks.',
249
286
  options: [
250
287
  ['help', 'Show this help'],
251
288
  ['install', 'Install or update the provider and hooks (default action)'],
@@ -254,7 +291,8 @@ const AGENT_HELP = {
254
291
  ],
255
292
  },
256
293
  pi: {
257
- usage: 'subc [-p NAME] pi [help|install|status|uninstall] [Pi arguments...]',
294
+ usage:
295
+ 'subc [-p NAME] pi [help|install|status|uninstall] [Pi arguments...]',
258
296
  behavior:
259
297
  'Refreshes the persistent Subconscious provider from the live catalog, then launches Pi.',
260
298
  options: [
@@ -267,7 +305,8 @@ const AGENT_HELP = {
267
305
  ],
268
306
  },
269
307
  'deepseek-harness': {
270
- usage: 'subc [-p NAME] dsh [web|headless] [--model MODEL] [Harness arguments...]',
308
+ usage:
309
+ 'subc [-p NAME] dsh [web|headless] [--model MODEL] [Harness arguments...]',
271
310
  behavior:
272
311
  'Launches DeepSeek Harness with a temporary Subconscious provider containing every live gateway model. Web mode is the default.',
273
312
  options: [
@@ -287,7 +326,9 @@ export function isAgentHelpRequest(argv = []) {
287
326
  function displayProfileValue(setting, value, values) {
288
327
  if (setting.type === 'secret') {
289
328
  if (value) return '(set)';
290
- return setting.key !== 'API_KEY' && values.API_KEY ? '(shared key)' : '(not set)';
329
+ return setting.key !== 'API_KEY' && values.API_KEY
330
+ ? '(shared key)'
331
+ : '(not set)';
291
332
  }
292
333
  if (
293
334
  !value &&
@@ -306,8 +347,14 @@ export function printAgentHelp(agent, profile) {
306
347
  };
307
348
  const settings = profileSettingsForAgent(agent.id);
308
349
  const values = resolvedProfileValues(profile);
309
- const optionWidth = Math.max(0, ...details.options.map(([option]) => option.length));
310
- const settingWidth = Math.max(0, ...settings.map((setting) => setting.key.length));
350
+ const optionWidth = Math.max(
351
+ 0,
352
+ ...details.options.map(([option]) => option.length),
353
+ );
354
+ const settingWidth = Math.max(
355
+ 0,
356
+ ...settings.map((setting) => setting.key.length),
357
+ );
311
358
 
312
359
  console.log(`\n ${c.bold}${agent.name} + Subconscious${c.reset}\n`);
313
360
  console.log(` ${details.behavior}\n`);
@@ -315,15 +362,23 @@ export function printAgentHelp(agent, profile) {
315
362
  if (details.options.length) {
316
363
  console.log(` ${c.bold}Commands and options${c.reset}`);
317
364
  for (const [option, description] of details.options) {
318
- console.log(` ${c.cyan}${option.padEnd(optionWidth)}${c.reset} ${description}`);
365
+ console.log(
366
+ ` ${c.cyan}${option.padEnd(optionWidth)}${c.reset} ${description}`,
367
+ );
319
368
  }
320
369
  console.log();
321
370
  }
322
- console.log(` ${c.bold}Profile settings${c.reset} ${c.dim}(${profile?.name || 'default'})${c.reset}`);
371
+ console.log(
372
+ ` ${c.bold}Profile settings${c.reset} ${c.dim}(${profile?.name || 'default'})${c.reset}`,
373
+ );
323
374
  for (const setting of settings) {
324
375
  const value = displayProfileValue(setting, values[setting.key], values);
325
- console.log(` ${c.cyan}${setting.key.padEnd(settingWidth)}${c.reset} ${value}`);
326
- console.log(` ${' '.repeat(settingWidth)} ${c.dim}${setting.description}${c.reset}`);
376
+ console.log(
377
+ ` ${c.cyan}${setting.key.padEnd(settingWidth)}${c.reset} ${value}`,
378
+ );
379
+ console.log(
380
+ ` ${' '.repeat(settingWidth)} ${c.dim}${setting.description}${c.reset}`,
381
+ );
327
382
  }
328
383
  const command = agentCommandName(agent);
329
384
  const profileFlag = profile?.name || 'default';
@@ -335,7 +390,9 @@ export function printAgentHelp(agent, profile) {
335
390
  const installDescription = agent.runbook?.binaryInstallScript
336
391
  ? 'Install the agent binary with'
337
392
  : 'Install the persistent integration with';
338
- console.log(` ${installDescription} ${c.cyan}subc ${command} install${c.reset}.`);
393
+ console.log(
394
+ ` ${installDescription} ${c.cyan}subc ${command} install${c.reset}.`,
395
+ );
339
396
  }
340
397
  if (actions.includes('uninstall')) {
341
398
  console.log(
@@ -370,7 +427,11 @@ export function extractModel(argv, profile) {
370
427
  const environmentModel = resolvedModelSetting(process.env.SUBCONSCIOUS_MODEL);
371
428
  const profileModel = resolvedModelSetting(profile?.values?.MODEL);
372
429
  let model = environmentModel || profileModel || '';
373
- let modelSource = environmentModel ? 'environment' : profileModel ? 'profile' : 'catalog';
430
+ let modelSource = environmentModel
431
+ ? 'environment'
432
+ : profileModel
433
+ ? 'profile'
434
+ : 'catalog';
374
435
  const rest = [];
375
436
  for (let i = 0; i < argv.length; i++) {
376
437
  const a = argv[i];
@@ -433,7 +494,9 @@ function candidateBinDirs() {
433
494
  stdio: ['ignore', 'pipe', 'ignore'],
434
495
  }).trim();
435
496
  if (prefix) {
436
- dirs.push(process.platform === 'win32' ? prefix : path.join(prefix, 'bin'));
497
+ dirs.push(
498
+ process.platform === 'win32' ? prefix : path.join(prefix, 'bin'),
499
+ );
437
500
  }
438
501
  } catch {
439
502
  // npm not available — skip.
@@ -470,8 +533,12 @@ function binExts() {
470
533
  * dirs lets us find binaries installed this session that aren't on PATH yet.
471
534
  */
472
535
  async function resolveBinPath(bin, preferredDirs = []) {
473
- const pathDirs = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
474
- const dirs = [...new Set([...preferredDirs, ...pathDirs, ...candidateBinDirs()])];
536
+ const pathDirs = (process.env.PATH || '')
537
+ .split(path.delimiter)
538
+ .filter(Boolean);
539
+ const dirs = [
540
+ ...new Set([...preferredDirs, ...pathDirs, ...candidateBinDirs()]),
541
+ ];
475
542
  const exts = binExts();
476
543
  for (const dir of dirs) {
477
544
  for (const ext of exts) {
@@ -506,15 +573,20 @@ export function augmentPath(
506
573
  extraDirs.indexOf(dir) === index,
507
574
  );
508
575
  const remaining = current.filter((dir) => dir !== preferredDir);
509
- return [...(preferredDir ? [preferredDir] : []), ...prepend, ...remaining].join(
510
- path.delimiter,
511
- );
576
+ return [
577
+ ...(preferredDir ? [preferredDir] : []),
578
+ ...prepend,
579
+ ...remaining,
580
+ ].join(path.delimiter);
512
581
  }
513
582
 
514
583
  /** Ask a yes/no question on the TTY. Empty answer counts as yes. */
515
584
  function askYesNo(question) {
516
585
  return new Promise((resolve) => {
517
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
586
+ const rl = readline.createInterface({
587
+ input: process.stdin,
588
+ output: process.stdout,
589
+ });
518
590
  rl.question(question, (answer) => {
519
591
  rl.close();
520
592
  const a = answer.trim().toLowerCase();
@@ -525,7 +597,10 @@ function askYesNo(question) {
525
597
 
526
598
  function waitForEnter(prompt) {
527
599
  return new Promise((resolve) => {
528
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
600
+ const rl = readline.createInterface({
601
+ input: process.stdin,
602
+ output: process.stdout,
603
+ });
529
604
  rl.question(prompt, () => {
530
605
  rl.close();
531
606
  resolve();
@@ -534,9 +609,14 @@ function waitForEnter(prompt) {
534
609
  }
535
610
 
536
611
  /** Run the agent's packaged binary installer, or its shell install command. */
537
- function runInstaller(agent, install = agent.install, usePackagedScript = true) {
612
+ function runInstaller(
613
+ agent,
614
+ install = agent.install,
615
+ usePackagedScript = true,
616
+ ) {
538
617
  return new Promise((resolve) => {
539
- const installScript = usePackagedScript && agent.runbook?.binaryInstallScript;
618
+ const installScript =
619
+ usePackagedScript && agent.runbook?.binaryInstallScript;
540
620
  const child = installScript
541
621
  ? spawn('bash', [runbookScriptPath(agent, installScript), 'install'], {
542
622
  stdio: 'inherit',
@@ -578,7 +658,9 @@ async function ensureInstalled(agent) {
578
658
  console.error(
579
659
  `\n ${c.red}${agent.name} isn't installed${c.reset} ${c.dim}(\`${agent.bin}\` not found on PATH).${c.reset}`,
580
660
  );
581
- console.error(` Install ${agent.name} separately, then re-run ${c.cyan}subc ${agent.command || agent.id}${c.reset}.\n`);
661
+ console.error(
662
+ ` Install ${agent.name} separately, then re-run ${c.cyan}subc ${agent.command || agent.id}${c.reset}.\n`,
663
+ );
582
664
  process.exit(127);
583
665
  }
584
666
 
@@ -601,7 +683,9 @@ async function ensureInstalled(agent) {
601
683
  process.exit(127);
602
684
  }
603
685
 
604
- console.error(`\n ${c.dim}Running ${c.reset}${c.cyan}${agent.install}${c.reset}\n`);
686
+ console.error(
687
+ `\n ${c.dim}Running ${c.reset}${c.cyan}${agent.install}${c.reset}\n`,
688
+ );
605
689
  let installed = await runInstaller(agent);
606
690
 
607
691
  // Primary failed and a fallback exists — try it once.
@@ -635,12 +719,17 @@ export function parseClaudeVersion(text) {
635
719
  return match?.[1] ?? null;
636
720
  }
637
721
 
638
- export function claudeVersionNeedsUpgrade(installed, minimum = MIN_CLAUDE_CODE_VERSION) {
722
+ export function claudeVersionNeedsUpgrade(
723
+ installed,
724
+ minimum = MIN_CLAUDE_CODE_VERSION,
725
+ ) {
639
726
  return versionNeedsUpgrade(installed, minimum);
640
727
  }
641
728
 
642
729
  export function versionNeedsUpgrade(installed, minimum) {
643
- return Boolean(installed && minimum && compareVersions(installed, minimum) < 0);
730
+ return Boolean(
731
+ installed && minimum && compareVersions(installed, minimum) < 0,
732
+ );
644
733
  }
645
734
 
646
735
  export function readClaudeVersion(bin, binDir, options = {}) {
@@ -658,7 +747,9 @@ export function readClaudeVersion(bin, binDir, options = {}) {
658
747
  },
659
748
  timeout: options.timeoutMs ?? 3000,
660
749
  });
661
- return parseClaudeVersion(typeof stdout === 'string' ? stdout : stdout?.toString?.());
750
+ return parseClaudeVersion(
751
+ typeof stdout === 'string' ? stdout : stdout?.toString?.(),
752
+ );
662
753
  } catch {
663
754
  return null;
664
755
  }
@@ -711,7 +802,9 @@ function spawnRunbook(agent, args, env, relativeScript) {
711
802
  child.on('error', (error) => {
712
803
  if (error.code === 'ENOENT') {
713
804
  reject(
714
- new Error('These coding-agent integrations require `bash`, but it was not found on PATH.'),
805
+ new Error(
806
+ 'These coding-agent integrations require `bash`, but it was not found on PATH.',
807
+ ),
715
808
  );
716
809
  return;
717
810
  }
@@ -748,7 +841,8 @@ export async function getAgentApiKey(profile, agent) {
748
841
  const specificSetting = agentApiKeySetting(agent);
749
842
  const specificKey = specificSetting?.key;
750
843
  const specificEnvKey = specificKey && process.env[specificKey]?.trim();
751
- if (specificEnvKey) return { key: specificEnvKey, source: `${specificKey} env var` };
844
+ if (specificEnvKey)
845
+ return { key: specificEnvKey, source: `${specificKey} env var` };
752
846
 
753
847
  const sharedEnvKey = process.env.SUBCONSCIOUS_API_KEY?.trim();
754
848
  if (sharedEnvKey) {
@@ -836,7 +930,8 @@ function claudeModelPickerEnv(agent, ctx, models) {
836
930
  // Pad through Fable so the built-in `fable` alias cannot resolve to Anthropic
837
931
  // Fable 5. The /model menu itself is replaced via claudePickerSettings.
838
932
  const pickerModels = uniqueCatalogModels(models, ctx.model);
839
- while (pickerModels.length < 4) pickerModels.push(pickerModels.at(-1) || ctx.model);
933
+ while (pickerModels.length < 4)
934
+ pickerModels.push(pickerModels.at(-1) || ctx.model);
840
935
 
841
936
  const roles = ['OPUS', 'SONNET', 'HAIKU', 'FABLE'];
842
937
  const env = {};
@@ -846,7 +941,8 @@ function claudeModelPickerEnv(agent, ctx, models) {
846
941
  if (!model) continue;
847
942
  env[`ANTHROPIC_DEFAULT_${role}_MODEL`] = model;
848
943
  env[`ANTHROPIC_DEFAULT_${role}_MODEL_NAME`] = model;
849
- env[`ANTHROPIC_DEFAULT_${role}_MODEL_DESCRIPTION`] = `Subconscious model ${model}`;
944
+ env[`ANTHROPIC_DEFAULT_${role}_MODEL_DESCRIPTION`] =
945
+ `Subconscious model ${model}`;
850
946
  }
851
947
  const customModel = pickerModels[4];
852
948
  if (customModel) {
@@ -855,7 +951,9 @@ function claudeModelPickerEnv(agent, ctx, models) {
855
951
  env.ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION = `Subconscious model ${customModel}`;
856
952
  }
857
953
  return Object.fromEntries(
858
- CLAUDE_MODEL_PICKER_KEYS.map((key) => [key, env[key]]).filter(([, value]) => value),
954
+ CLAUDE_MODEL_PICKER_KEYS.map((key) => [key, env[key]]).filter(
955
+ ([, value]) => value,
956
+ ),
859
957
  );
860
958
  }
861
959
 
@@ -904,7 +1002,9 @@ export function runbookEnv(
904
1002
  SUBCONSCIOUS_MODELS: models.join('\n'),
905
1003
  ...(agent.id === 'claude-code'
906
1004
  ? {
907
- SUBC_CLAUDE_SETTINGS: JSON.stringify(claudePickerSettings(models, model)),
1005
+ SUBC_CLAUDE_SETTINGS: JSON.stringify(
1006
+ claudePickerSettings(models, model),
1007
+ ),
908
1008
  CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '0',
909
1009
  CLAUDE_CODE_AUTO_MODE_SERVER: '0',
910
1010
  }
@@ -949,19 +1049,38 @@ export function selectLaunchModel(requestedModel, modelSource, catalog) {
949
1049
  return useLiveDefault ? first : requestedModel;
950
1050
  }
951
1051
 
952
- async function runRunbookSetup(agent, argv, profile, relativeScript = agent.runbook.script) {
1052
+ async function runRunbookSetup(
1053
+ agent,
1054
+ argv,
1055
+ profile,
1056
+ relativeScript = agent.runbook.script,
1057
+ ) {
953
1058
  if (isSetupWithoutAuth(argv) || agent.runbook?.setupNeedsAuth === false) {
954
- return spawnRunbook(agent, argv, {
955
- ...(profile?.values || {}),
956
- ...process.env,
957
- SUBC_ENV_FILE: os.devNull,
958
- }, relativeScript);
1059
+ return spawnRunbook(
1060
+ agent,
1061
+ argv,
1062
+ {
1063
+ ...(profile?.values || {}),
1064
+ ...process.env,
1065
+ SUBC_ENV_FILE: os.devNull,
1066
+ },
1067
+ relativeScript,
1068
+ );
959
1069
  }
960
1070
 
961
- const { model: requestedModel, modelSource, rest } = extractModel(argv, profile);
962
- const apiKey = optionValue(rest, '--api-key') || (await requireApiKey(profile, agent));
1071
+ const {
1072
+ model: requestedModel,
1073
+ modelSource,
1074
+ rest,
1075
+ } = extractModel(argv, profile);
1076
+ const apiKey =
1077
+ optionValue(rest, '--api-key') || (await requireApiKey(profile, agent));
963
1078
  if (!apiKey) return 1;
964
- const catalog = await resolvedModelsForLaunch(profile, apiKey, requestedModel);
1079
+ const catalog = await resolvedModelsForLaunch(
1080
+ profile,
1081
+ apiKey,
1082
+ requestedModel,
1083
+ );
965
1084
  const model = selectLaunchModel(requestedModel, modelSource, catalog);
966
1085
  if (requestedModel && model !== requestedModel) {
967
1086
  console.error(
@@ -984,7 +1103,9 @@ async function runRunbookSetup(agent, argv, profile, relativeScript = agent.runb
984
1103
  if (code !== 0 || !installed) return code;
985
1104
 
986
1105
  if (agent.id === 'pi') {
987
- console.log(`\n ${c.dim}Start a fresh session with ${c.reset}${c.cyan}subc pi${c.reset}${c.dim}.${c.reset}\n`);
1106
+ console.log(
1107
+ `\n ${c.dim}Start a fresh session with ${c.reset}${c.cyan}subc pi${c.reset}${c.dim}.${c.reset}\n`,
1108
+ );
988
1109
  }
989
1110
  return code;
990
1111
  }
@@ -1002,19 +1123,30 @@ export async function runAgent(agent, argv, options = {}) {
1002
1123
 
1003
1124
  if (process.platform === 'win32') {
1004
1125
  return runWindowsAgent(agent, argv, {
1005
- profile, parseAgentAction, extractModel, requireApiKey,
1006
- resolvedModelsForLaunch, selectLaunchModel, runbookEnv,
1007
- minimumClaudeVersion: MIN_CLAUDE_CODE_VERSION, parseClaudeVersion, claudeVersionNeedsUpgrade,
1126
+ profile,
1127
+ parseAgentAction,
1128
+ extractModel,
1129
+ requireApiKey,
1130
+ resolvedModelsForLaunch,
1131
+ selectLaunchModel,
1132
+ runbookEnv,
1133
+ minimumClaudeVersion: MIN_CLAUDE_CODE_VERSION,
1134
+ parseClaudeVersion,
1135
+ claudeVersionNeedsUpgrade,
1008
1136
  });
1009
1137
  }
1010
1138
 
1011
1139
  const parsed = parseAgentAction(agent, argv);
1012
1140
  if (parsed.action !== 'launch') {
1013
1141
  if (!agent.runbook?.setupScript) {
1014
- throw new Error(`No persistent integration is available for ${agent.name}`);
1142
+ throw new Error(
1143
+ `No persistent integration is available for ${agent.name}`,
1144
+ );
1015
1145
  }
1016
1146
  const setupArgs =
1017
- parsed.args[0] === parsed.action ? parsed.args : [parsed.action, ...parsed.args];
1147
+ parsed.args[0] === parsed.action
1148
+ ? parsed.args
1149
+ : [parsed.action, ...parsed.args];
1018
1150
  const code = await runRunbookSetup(
1019
1151
  agent,
1020
1152
  setupArgs,
@@ -1033,13 +1165,21 @@ export async function runAgent(agent, argv, options = {}) {
1033
1165
  return code;
1034
1166
  }
1035
1167
 
1036
- const { model: requestedModel, modelSource, rest } = extractModel(argv, profile);
1168
+ const {
1169
+ model: requestedModel,
1170
+ modelSource,
1171
+ rest,
1172
+ } = extractModel(argv, profile);
1037
1173
  const apiKey = await requireApiKey(profile, agent);
1038
1174
  if (!apiKey) return 1;
1039
1175
 
1040
1176
  const binDir = await ensureInstalled(agent);
1041
1177
  await ensureClaudeCompatible(agent, binDir);
1042
- const catalog = await resolvedModelsForLaunch(profile, apiKey, requestedModel);
1178
+ const catalog = await resolvedModelsForLaunch(
1179
+ profile,
1180
+ apiKey,
1181
+ requestedModel,
1182
+ );
1043
1183
  const model = selectLaunchModel(requestedModel, modelSource, catalog);
1044
1184
  if (requestedModel && model !== requestedModel) {
1045
1185
  console.error(