subconscious-cli 4.0.12 → 4.0.14
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 +17 -7
- package/bin/agents.js +184 -39
- package/bin/cli.js +7 -6
- package/bin/native/subc-tui-darwin-amd64 +0 -0
- package/bin/native/subc-tui-darwin-arm64 +0 -0
- package/bin/native/subc-tui-linux-amd64 +0 -0
- package/bin/native/subc-tui-linux-arm64 +0 -0
- package/bin/native/subc-tui-windows-amd64.exe +0 -0
- package/bin/native/subc-tui-windows-arm64.exe +0 -0
- package/bin/profiles.js +24 -8
- package/bin/runbook/claude-code/install.sh +43 -6
- package/bin/runbook/claude-code/run.sh +89 -7
- package/bin/runbook/copilot/install.sh +10 -8
- package/bin/tui.js +6 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -122,6 +122,8 @@ saved to the selected profile.
|
|
|
122
122
|
Cursor still requires enabling its OpenAI API Key Override in Cursor Settings.
|
|
123
123
|
Copilot requires entering the custom endpoint key once through VS Code's
|
|
124
124
|
Manage Language Models UI. The install scripts print the relevant next steps.
|
|
125
|
+
Its Custom Endpoint provider uses the Messages API at `/v1/messages` by
|
|
126
|
+
default; rerun `subc copilot install` to migrate an older Chat Completions entry.
|
|
125
127
|
|
|
126
128
|
The runbook scripts require Bash. Cursor, Copilot, Pi, and Codex hook merge
|
|
127
129
|
also require `jq`; Cursor and Copilot also require `curl`.
|
|
@@ -151,8 +153,9 @@ subc config # list every profile and its file path
|
|
|
151
153
|
subc -p staging config create # create a profile with default settings
|
|
152
154
|
subc -p staging config # print that path and env file
|
|
153
155
|
subc -p staging config --model subconscious/glm-5.2
|
|
156
|
+
subc -p staging config --model UNSET
|
|
154
157
|
subc -p staging config --subagent-model subconscious/deepseek-v4-flash-marathon
|
|
155
|
-
subc -p staging config --subagent-model
|
|
158
|
+
subc -p staging config --subagent-model UNSET
|
|
156
159
|
subc config --gateway-url https://gateway.example
|
|
157
160
|
subc config path
|
|
158
161
|
subc config edit # open the selected profile in $VISUAL, $EDITOR, vim, or nano
|
|
@@ -168,9 +171,12 @@ Subconscious-injected defaults; resolved login identity (`GATEWAY_URL`,
|
|
|
168
171
|
In scripts and CI, keep using `subc config --gateway-url`, `--api-key`,
|
|
169
172
|
`--model`, and `--subagent-model`.
|
|
170
173
|
|
|
174
|
+
Use `UNSET` to clear the profile default so launches use the first model in
|
|
175
|
+
the live catalog (gateway priority order).
|
|
176
|
+
|
|
171
177
|
The subagent setting controls `CLAUDE_CODE_SUBAGENT_MODEL` for Claude Code.
|
|
172
|
-
Use `
|
|
173
|
-
|
|
178
|
+
Use `UNSET` to clear the override so subagents automatically follow the
|
|
179
|
+
profile's default model.
|
|
174
180
|
|
|
175
181
|
Each agent section can hold its own API key. An agent-specific key takes
|
|
176
182
|
precedence over the shared profile key and can be used on its own, so profiles
|
|
@@ -222,11 +228,15 @@ list. The selected profile model is listed first only while the gateway still
|
|
|
222
228
|
advertises it.
|
|
223
229
|
OpenCode rebuilds its isolated runtime provider on every `subc opencode` launch,
|
|
224
230
|
so models left in older OpenCode configuration files do not leak into the list.
|
|
225
|
-
If a saved profile default has been removed, launches use the first
|
|
226
|
-
an explicit `--model` or `SUBCONSCIOUS_MODEL` override is always
|
|
231
|
+
If a saved profile default is UNSET or has been removed, launches use the first
|
|
232
|
+
live model; an explicit `--model` or `SUBCONSCIOUS_MODEL` override is always
|
|
233
|
+
preserved.
|
|
227
234
|
Claude Code exposes four native Opus/Sonnet/Haiku/Fable picker slots plus one
|
|
228
|
-
custom model option
|
|
229
|
-
|
|
235
|
+
custom model option. The CLI remaps those aliases to the live catalog, then
|
|
236
|
+
replaces the built-in `/model` lineup (`modelPicker.replaceBuiltInOptions`) and
|
|
237
|
+
allowlists only those models (`availableModels`) so Anthropic Fable and
|
|
238
|
+
ungranted slugs do not appear. Any other model can still be selected with
|
|
239
|
+
`subc claude --model MODEL`. Cursor requires adding
|
|
230
240
|
the printed model IDs in its OpenAI API Key Override settings. Use the printed
|
|
231
241
|
`/v1` Base URL in Cursor Settings; the profile itself stores the gateway origin
|
|
232
242
|
so correlation hooks can post to `/v1/agent-hooks`.
|
package/bin/agents.js
CHANGED
|
@@ -20,8 +20,16 @@ import readline from 'node:readline';
|
|
|
20
20
|
import { fileURLToPath } from 'node:url';
|
|
21
21
|
import { c } from './colors.js';
|
|
22
22
|
import { getApiKey } from './auth.js';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
isUnsetSetting,
|
|
25
|
+
profileSettingsForAgent,
|
|
26
|
+
resolvedModelSetting,
|
|
27
|
+
resolvedProfileValues,
|
|
28
|
+
} from './profiles.js';
|
|
24
29
|
import { isLiveModelSource, PUBLIC_CATALOG_FALLBACK_MESSAGE, resolveModelCatalog } from './models.js';
|
|
30
|
+
import { compareVersions } from './update-check.js';
|
|
31
|
+
|
|
32
|
+
export const MIN_CLAUDE_CODE_VERSION = '2.1.242';
|
|
25
33
|
|
|
26
34
|
// --- Registry (single source of truth, generated copy shipped in the package).
|
|
27
35
|
const registry = JSON.parse(
|
|
@@ -258,6 +266,12 @@ function displayProfileValue(setting, value, values) {
|
|
|
258
266
|
if (value) return '(set)';
|
|
259
267
|
return setting.key !== 'API_KEY' && values.API_KEY ? '(shared key)' : '(not set)';
|
|
260
268
|
}
|
|
269
|
+
if (
|
|
270
|
+
!value &&
|
|
271
|
+
(setting.key === 'MODEL' || setting.key === 'CLAUDE_CODE_SUBAGENT_MODEL')
|
|
272
|
+
) {
|
|
273
|
+
return 'UNSET';
|
|
274
|
+
}
|
|
261
275
|
return value || '(auto)';
|
|
262
276
|
}
|
|
263
277
|
|
|
@@ -325,28 +339,40 @@ function buildContext(apiKey, model, profile) {
|
|
|
325
339
|
/**
|
|
326
340
|
* Pull a `--model <value>` / `--model=<value>` flag out of the passthrough
|
|
327
341
|
* args (so it sets the Subconscious model rather than reaching the agent).
|
|
328
|
-
* Falls back to SUBCONSCIOUS_MODEL, then the
|
|
342
|
+
* Falls back to SUBCONSCIOUS_MODEL, then the profile MODEL. UNSET or a blank
|
|
343
|
+
* profile model means "use the first live catalog entry" (gateway priority).
|
|
329
344
|
*/
|
|
330
|
-
function extractModel(argv, profile) {
|
|
331
|
-
const environmentModel = process.env.SUBCONSCIOUS_MODEL
|
|
332
|
-
const profileModel = profile?.values?.MODEL
|
|
333
|
-
let model = environmentModel || profileModel ||
|
|
334
|
-
let modelSource = environmentModel ? 'environment' : profileModel ? 'profile' : '
|
|
345
|
+
export function extractModel(argv, profile) {
|
|
346
|
+
const environmentModel = resolvedModelSetting(process.env.SUBCONSCIOUS_MODEL);
|
|
347
|
+
const profileModel = resolvedModelSetting(profile?.values?.MODEL);
|
|
348
|
+
let model = environmentModel || profileModel || '';
|
|
349
|
+
let modelSource = environmentModel ? 'environment' : profileModel ? 'profile' : 'catalog';
|
|
335
350
|
const rest = [];
|
|
336
351
|
for (let i = 0; i < argv.length; i++) {
|
|
337
352
|
const a = argv[i];
|
|
338
353
|
if (a === '--model') {
|
|
339
354
|
const v = argv[i + 1];
|
|
340
355
|
if (v && !v.startsWith('-')) {
|
|
341
|
-
|
|
342
|
-
|
|
356
|
+
if (isUnsetSetting(v)) {
|
|
357
|
+
model = '';
|
|
358
|
+
modelSource = 'catalog';
|
|
359
|
+
} else {
|
|
360
|
+
model = v;
|
|
361
|
+
modelSource = 'command';
|
|
362
|
+
}
|
|
343
363
|
i++;
|
|
344
364
|
}
|
|
345
365
|
continue;
|
|
346
366
|
}
|
|
347
367
|
if (a.startsWith('--model=')) {
|
|
348
|
-
|
|
349
|
-
|
|
368
|
+
const v = a.slice('--model='.length);
|
|
369
|
+
if (isUnsetSetting(v)) {
|
|
370
|
+
model = '';
|
|
371
|
+
modelSource = 'catalog';
|
|
372
|
+
} else {
|
|
373
|
+
model = v;
|
|
374
|
+
modelSource = 'command';
|
|
375
|
+
}
|
|
350
376
|
continue;
|
|
351
377
|
}
|
|
352
378
|
rest.push(a);
|
|
@@ -445,6 +471,16 @@ function askYesNo(question) {
|
|
|
445
471
|
});
|
|
446
472
|
}
|
|
447
473
|
|
|
474
|
+
function waitForEnter(prompt) {
|
|
475
|
+
return new Promise((resolve) => {
|
|
476
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
477
|
+
rl.question(prompt, () => {
|
|
478
|
+
rl.close();
|
|
479
|
+
resolve();
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
448
484
|
/** Run the agent's install command (may contain `&&`, so shell:true). */
|
|
449
485
|
function runInstaller(install) {
|
|
450
486
|
return new Promise((resolve) => {
|
|
@@ -536,6 +572,51 @@ async function ensureInstalled(agent) {
|
|
|
536
572
|
process.exit(0);
|
|
537
573
|
}
|
|
538
574
|
|
|
575
|
+
export function parseClaudeVersion(text) {
|
|
576
|
+
const match = String(text ?? '').match(/v?(\d+\.\d+\.\d+)/);
|
|
577
|
+
return match?.[1] ?? null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export function claudeVersionNeedsUpgrade(installed, minimum = MIN_CLAUDE_CODE_VERSION) {
|
|
581
|
+
return Boolean(installed && minimum && compareVersions(installed, minimum) < 0);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function readClaudeVersion(bin, binDir, options = {}) {
|
|
585
|
+
const execFile = options.execFileSync || execFileSync;
|
|
586
|
+
try {
|
|
587
|
+
const stdout = execFile(bin, ['--version'], {
|
|
588
|
+
encoding: 'utf-8',
|
|
589
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
590
|
+
env: { ...process.env, PATH: augmentPath([binDir, ...candidateBinDirs()].filter(Boolean)) },
|
|
591
|
+
timeout: options.timeoutMs ?? 3000,
|
|
592
|
+
});
|
|
593
|
+
return parseClaudeVersion(typeof stdout === 'string' ? stdout : stdout?.toString?.());
|
|
594
|
+
} catch {
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
async function ensureClaudeCompatible(agent, binDir) {
|
|
600
|
+
if (agent.id !== 'claude-code') return;
|
|
601
|
+
const version = readClaudeVersion(agent.bin, binDir);
|
|
602
|
+
if (!claudeVersionNeedsUpgrade(version)) return;
|
|
603
|
+
|
|
604
|
+
console.error(
|
|
605
|
+
`\n Minimum supported Claude Code version is ${MIN_CLAUDE_CODE_VERSION}. Your version is ${version}. Upgrade to get the best experience.\n`,
|
|
606
|
+
);
|
|
607
|
+
console.error(` Upgrade it with:`);
|
|
608
|
+
console.error(` ${c.cyan}${agent.install}${c.reset}`);
|
|
609
|
+
if (agent.installFallback) {
|
|
610
|
+
console.error(` or`);
|
|
611
|
+
console.error(` ${c.cyan}${agent.installFallback}${c.reset}`);
|
|
612
|
+
}
|
|
613
|
+
console.error('');
|
|
614
|
+
if (process.stdin.isTTY === true && process.stdout.isTTY === true) {
|
|
615
|
+
await waitForEnter(` ${c.dim}Press Enter to continue.${c.reset} `);
|
|
616
|
+
console.error('');
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
539
620
|
/** Resolve and validate a script inside the packaged runbook directory. */
|
|
540
621
|
function runbookScriptPath(agent, relativeScript = agent.runbook.script) {
|
|
541
622
|
const script = path.resolve(RUNBOOK_DIR, relativeScript);
|
|
@@ -639,21 +720,48 @@ const CLAUDE_MODEL_PICKER_KEYS = [
|
|
|
639
720
|
'ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION',
|
|
640
721
|
];
|
|
641
722
|
|
|
723
|
+
const CLAUDE_MODEL_PICKER_ID_KEYS = [
|
|
724
|
+
'ANTHROPIC_DEFAULT_OPUS_MODEL',
|
|
725
|
+
'ANTHROPIC_DEFAULT_SONNET_MODEL',
|
|
726
|
+
'ANTHROPIC_DEFAULT_HAIKU_MODEL',
|
|
727
|
+
'ANTHROPIC_DEFAULT_FABLE_MODEL',
|
|
728
|
+
'ANTHROPIC_CUSTOM_MODEL_OPTION',
|
|
729
|
+
];
|
|
730
|
+
|
|
731
|
+
function uniqueCatalogModels(models, fallbackModel) {
|
|
732
|
+
const unique = [];
|
|
733
|
+
for (const model of models) {
|
|
734
|
+
if (model && !unique.includes(model)) unique.push(model);
|
|
735
|
+
}
|
|
736
|
+
if (unique.length === 0 && fallbackModel) unique.push(fallbackModel);
|
|
737
|
+
return unique;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
export function claudePickerSettings(models, fallbackModel) {
|
|
741
|
+
const unique = uniqueCatalogModels(models, fallbackModel);
|
|
742
|
+
return {
|
|
743
|
+
availableModels: unique,
|
|
744
|
+
modelPicker: {
|
|
745
|
+
replaceBuiltInOptions: true,
|
|
746
|
+
options: unique.map((model) => ({
|
|
747
|
+
model,
|
|
748
|
+
label: model,
|
|
749
|
+
description: `Subconscious model ${model}`,
|
|
750
|
+
})),
|
|
751
|
+
},
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
|
|
642
755
|
function claudeModelPickerEnv(agent, ctx, models) {
|
|
643
756
|
if (agent.id !== 'claude-code') return {};
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const pickerModels = [];
|
|
653
|
-
for (const model of [...models, ...configuredModels]) {
|
|
654
|
-
if (model && !pickerModels.includes(model)) pickerModels.push(model);
|
|
655
|
-
}
|
|
656
|
-
while (pickerModels.length < 3) pickerModels.push(pickerModels.at(-1) || ctx.model);
|
|
757
|
+
// Only the live catalog belongs in Claude's picker. Packaged registry slots
|
|
758
|
+
// (Haiku → DeepSeek, etc.) must not be unioned back in — that advertised
|
|
759
|
+
// models the key cannot call.
|
|
760
|
+
//
|
|
761
|
+
// Pad through Fable so the built-in `fable` alias cannot resolve to Anthropic
|
|
762
|
+
// Fable 5. The /model menu itself is replaced via claudePickerSettings.
|
|
763
|
+
const pickerModels = uniqueCatalogModels(models, ctx.model);
|
|
764
|
+
while (pickerModels.length < 4) pickerModels.push(pickerModels.at(-1) || ctx.model);
|
|
657
765
|
|
|
658
766
|
const roles = ['OPUS', 'SONNET', 'HAIKU', 'FABLE'];
|
|
659
767
|
const env = {};
|
|
@@ -676,6 +784,27 @@ function claudeModelPickerEnv(agent, ctx, models) {
|
|
|
676
784
|
);
|
|
677
785
|
}
|
|
678
786
|
|
|
787
|
+
function applyClaudePickerCatalog(env, picker, models) {
|
|
788
|
+
if (Object.keys(picker).length === 0) return env;
|
|
789
|
+
const allowed = new Set(models);
|
|
790
|
+
|
|
791
|
+
for (const key of CLAUDE_MODEL_PICKER_KEYS) {
|
|
792
|
+
if (!picker[key]) {
|
|
793
|
+
delete env[key];
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
for (const key of CLAUDE_MODEL_PICKER_ID_KEYS) {
|
|
798
|
+
if (!picker[key] || !env[key] || allowed.has(env[key])) continue;
|
|
799
|
+
env[key] = picker[key];
|
|
800
|
+
const nameKey = `${key}_NAME`;
|
|
801
|
+
const descriptionKey = `${key}_DESCRIPTION`;
|
|
802
|
+
if (picker[nameKey]) env[nameKey] = picker[nameKey];
|
|
803
|
+
if (picker[descriptionKey]) env[descriptionKey] = picker[descriptionKey];
|
|
804
|
+
}
|
|
805
|
+
return env;
|
|
806
|
+
}
|
|
807
|
+
|
|
679
808
|
export function runbookEnv(
|
|
680
809
|
apiKey,
|
|
681
810
|
model,
|
|
@@ -687,18 +816,29 @@ export function runbookEnv(
|
|
|
687
816
|
const ctx = buildContext(apiKey, model, profile);
|
|
688
817
|
const extraDirs = [binDir, ...candidateBinDirs()].filter(Boolean);
|
|
689
818
|
const specificApiKey = agentApiKeySetting(agent)?.key;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
819
|
+
const picker = claudeModelPickerEnv(agent, ctx, models);
|
|
820
|
+
return applyClaudePickerCatalog(
|
|
821
|
+
{
|
|
822
|
+
...picker,
|
|
823
|
+
...(profile?.values || {}),
|
|
824
|
+
...process.env,
|
|
825
|
+
GATEWAY_URL: ctx.baseUrl,
|
|
826
|
+
API_KEY: apiKey,
|
|
827
|
+
...(specificApiKey ? { [specificApiKey]: apiKey } : {}),
|
|
828
|
+
MODEL: model,
|
|
829
|
+
SUBCONSCIOUS_MODELS: models.join('\n'),
|
|
830
|
+
...(agent.id === 'claude-code'
|
|
831
|
+
? {
|
|
832
|
+
SUBC_CLAUDE_SETTINGS: JSON.stringify(claudePickerSettings(models, model)),
|
|
833
|
+
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: '0',
|
|
834
|
+
}
|
|
835
|
+
: {}),
|
|
836
|
+
SUBC_ENV_FILE: os.devNull,
|
|
837
|
+
PATH: augmentPath(extraDirs),
|
|
838
|
+
},
|
|
839
|
+
picker,
|
|
840
|
+
models,
|
|
841
|
+
);
|
|
702
842
|
}
|
|
703
843
|
|
|
704
844
|
async function resolvedModelsForLaunch(profile, apiKey, selectedModel) {
|
|
@@ -721,12 +861,16 @@ async function resolvedModelsForLaunch(profile, apiKey, selectedModel) {
|
|
|
721
861
|
}
|
|
722
862
|
|
|
723
863
|
export function selectLaunchModel(requestedModel, modelSource, catalog) {
|
|
864
|
+
const first = catalog.models[0] || requestedModel || DEFAULTS.model;
|
|
865
|
+
if (modelSource === 'catalog' || !requestedModel) {
|
|
866
|
+
return first;
|
|
867
|
+
}
|
|
724
868
|
const useLiveDefault =
|
|
725
869
|
isLiveModelSource(catalog.source) &&
|
|
726
870
|
catalog.models.length > 0 &&
|
|
727
871
|
!catalog.models.includes(requestedModel) &&
|
|
728
872
|
(modelSource === 'profile' || modelSource === 'default');
|
|
729
|
-
return useLiveDefault ?
|
|
873
|
+
return useLiveDefault ? first : requestedModel;
|
|
730
874
|
}
|
|
731
875
|
|
|
732
876
|
async function runRunbookSetup(agent, argv, profile, relativeScript = agent.runbook.script) {
|
|
@@ -743,7 +887,7 @@ async function runRunbookSetup(agent, argv, profile, relativeScript = agent.runb
|
|
|
743
887
|
if (!apiKey) return 1;
|
|
744
888
|
const catalog = await resolvedModelsForLaunch(profile, apiKey, requestedModel);
|
|
745
889
|
const model = selectLaunchModel(requestedModel, modelSource, catalog);
|
|
746
|
-
if (model !== requestedModel) {
|
|
890
|
+
if (requestedModel && model !== requestedModel) {
|
|
747
891
|
console.error(
|
|
748
892
|
` ${c.yellow}Configured model ${requestedModel} is not in the live catalog; using ${model}.${c.reset}\n`,
|
|
749
893
|
);
|
|
@@ -810,9 +954,10 @@ export async function runAgent(agent, argv, options = {}) {
|
|
|
810
954
|
if (!apiKey) return 1;
|
|
811
955
|
|
|
812
956
|
const binDir = await ensureInstalled(agent);
|
|
957
|
+
await ensureClaudeCompatible(agent, binDir);
|
|
813
958
|
const catalog = await resolvedModelsForLaunch(profile, apiKey, requestedModel);
|
|
814
959
|
const model = selectLaunchModel(requestedModel, modelSource, catalog);
|
|
815
|
-
if (model !== requestedModel) {
|
|
960
|
+
if (requestedModel && model !== requestedModel) {
|
|
816
961
|
console.error(
|
|
817
962
|
` ${c.yellow}Configured model ${requestedModel} is not in the live catalog; using ${model}.${c.reset}\n`,
|
|
818
963
|
);
|
package/bin/cli.js
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
loadProfile,
|
|
35
35
|
modelsCommand,
|
|
36
36
|
printConfigHelp,
|
|
37
|
+
resolvedModelSetting,
|
|
37
38
|
RUNBOOK_DEFAULTS,
|
|
38
39
|
SUPPORTED_MODELS as PACKAGED_MODELS,
|
|
39
40
|
updateUrlCommand,
|
|
@@ -74,7 +75,7 @@ function printHelp() {
|
|
|
74
75
|
${agents}
|
|
75
76
|
|
|
76
77
|
${c.bold}Options${c.reset}
|
|
77
|
-
${c.dim}--model <id>${c.reset} Model to use (
|
|
78
|
+
${c.dim}--model <id>${c.reset} Model to use (profile MODEL, or first live catalog model if UNSET)
|
|
78
79
|
${c.dim}-p, --profile${c.reset} Select a profile (default: default)
|
|
79
80
|
${c.dim}-h, --help${c.reset} Show this help
|
|
80
81
|
${c.dim}-v, --version${c.reset} Show version
|
|
@@ -154,7 +155,8 @@ Usage:
|
|
|
154
155
|
subc models
|
|
155
156
|
subc models help
|
|
156
157
|
|
|
157
|
-
List available Subconscious models.
|
|
158
|
+
List available Subconscious models. The default is the profile MODEL,
|
|
159
|
+
or the first live catalog model if MODEL is UNSET.
|
|
158
160
|
`,
|
|
159
161
|
};
|
|
160
162
|
|
|
@@ -309,10 +311,9 @@ async function main() {
|
|
|
309
311
|
}
|
|
310
312
|
const profile = await loadProfile(profileName);
|
|
311
313
|
const auth = await getApiKey(profile);
|
|
312
|
-
const selectedModel =
|
|
313
|
-
process.env.SUBCONSCIOUS_MODEL
|
|
314
|
-
|
|
315
|
-
RUNBOOK_DEFAULTS.MODEL;
|
|
314
|
+
const selectedModel = resolvedModelSetting(
|
|
315
|
+
process.env.SUBCONSCIOUS_MODEL || profile.values.MODEL,
|
|
316
|
+
);
|
|
316
317
|
const baseUrl =
|
|
317
318
|
process.env.SUBCONSCIOUS_BASE_URL?.trim() ||
|
|
318
319
|
profile.values.GATEWAY_URL?.trim() ||
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/profiles.js
CHANGED
|
@@ -84,14 +84,15 @@ const SETTINGS = {
|
|
|
84
84
|
MODEL: {
|
|
85
85
|
key: 'MODEL',
|
|
86
86
|
label: 'Default model',
|
|
87
|
-
description:
|
|
87
|
+
description:
|
|
88
|
+
'Initial model used by coding-agent launches and setup. UNSET uses the first live catalog model.',
|
|
88
89
|
type: 'choice',
|
|
89
90
|
choices: SUPPORTED_MODELS,
|
|
90
91
|
},
|
|
91
92
|
CLAUDE_CODE_SUBAGENT_MODEL: {
|
|
92
93
|
key: 'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
93
94
|
label: 'Subagent model',
|
|
94
|
-
description: 'Model used by Claude Code subagents
|
|
95
|
+
description: 'Model used by Claude Code subagents. UNSET follows the default model.',
|
|
95
96
|
type: 'choice',
|
|
96
97
|
choices: SUPPORTED_MODELS,
|
|
97
98
|
},
|
|
@@ -340,7 +341,7 @@ function profileTemplate() {
|
|
|
340
341
|
|
|
341
342
|
GATEWAY_URL={GATEWAY_URL}
|
|
342
343
|
API_KEY={API_KEY}
|
|
343
|
-
# Available models:
|
|
344
|
+
# Available models (MODEL=UNSET uses the first live catalog model):
|
|
344
345
|
${modelComments}
|
|
345
346
|
MODEL={MODEL}
|
|
346
347
|
|
|
@@ -356,7 +357,7 @@ DEEPSEEK_HARNESS_API_KEY={DEEPSEEK_HARNESS_API_KEY}
|
|
|
356
357
|
# Claude Code
|
|
357
358
|
# Leave CLAUDE_GATEWAY_URL blank to use GATEWAY_URL above.
|
|
358
359
|
CLAUDE_GATEWAY_URL={CLAUDE_GATEWAY_URL}
|
|
359
|
-
#
|
|
360
|
+
# CLAUDE_CODE_SUBAGENT_MODEL=UNSET follows MODEL above.
|
|
360
361
|
CLAUDE_CODE_SUBAGENT_MODEL={CLAUDE_CODE_SUBAGENT_MODEL}
|
|
361
362
|
CLAUDE_CODE_AUTO_COMPACT_WINDOW={CLAUDE_CODE_AUTO_COMPACT_WINDOW}
|
|
362
363
|
CLAUDE_CODE_MAX_CONTEXT_TOKENS={CLAUDE_CODE_MAX_CONTEXT_TOKENS}
|
|
@@ -600,6 +601,16 @@ async function printProfile(profile) {
|
|
|
600
601
|
console.log();
|
|
601
602
|
}
|
|
602
603
|
|
|
604
|
+
export function isUnsetSetting(value) {
|
|
605
|
+
const normalized = String(value ?? '').trim().toUpperCase();
|
|
606
|
+
return normalized === 'UNSET' || normalized === 'FOLLOW-GATEWAY' || normalized === 'FOLLOW-DEFAULT';
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
export function resolvedModelSetting(value) {
|
|
610
|
+
const trimmed = String(value ?? '').trim();
|
|
611
|
+
return isUnsetSetting(trimmed) ? '' : trimmed;
|
|
612
|
+
}
|
|
613
|
+
|
|
603
614
|
export function validateSettingValue(setting, value) {
|
|
604
615
|
if (!value) {
|
|
605
616
|
if (setting.required) return `${setting.label} cannot be blank`;
|
|
@@ -691,8 +702,9 @@ Usage:
|
|
|
691
702
|
subc config edit [vim|nano]
|
|
692
703
|
subc -p NAME config edit [vim|nano]
|
|
693
704
|
subc config [show|path|list|create|delete]
|
|
694
|
-
[--gateway-url URL] [--api-key KEY]
|
|
695
|
-
[--
|
|
705
|
+
[--gateway-url URL] [--api-key KEY]
|
|
706
|
+
[--model MODEL|UNSET]
|
|
707
|
+
[--subagent-model MODEL|UNSET]
|
|
696
708
|
|
|
697
709
|
subc config List every profile and its file path
|
|
698
710
|
subc -p NAME config Print that profile's path and env file
|
|
@@ -702,6 +714,9 @@ Usage:
|
|
|
702
714
|
subc config path Print the selected profile path
|
|
703
715
|
subc -p NAME config create Create a new profile with default settings
|
|
704
716
|
subc -p NAME config delete Delete a non-default profile
|
|
717
|
+
|
|
718
|
+
--model UNSET Clear MODEL so launches use the first live catalog model
|
|
719
|
+
--subagent-model UNSET Clear the Claude subagent override so it follows MODEL
|
|
705
720
|
`);
|
|
706
721
|
}
|
|
707
722
|
|
|
@@ -745,7 +760,8 @@ export async function configCommand(argv, profileName = DEFAULT_PROFILE, options
|
|
|
745
760
|
'--model': 'MODEL',
|
|
746
761
|
'--subagent-model': 'CLAUDE_CODE_SUBAGENT_MODEL',
|
|
747
762
|
}[arg];
|
|
748
|
-
updates[key] =
|
|
763
|
+
updates[key] =
|
|
764
|
+
(arg === '--model' || arg === '--subagent-model') && isUnsetSetting(value) ? '' : value;
|
|
749
765
|
} else if (action === 'edit' && ALLOWED_EDITORS.has(arg)) {
|
|
750
766
|
if (editor) throw new Error('Specify only one editor (vim or nano)');
|
|
751
767
|
editor = arg;
|
|
@@ -813,7 +829,7 @@ export async function configCommand(argv, profileName = DEFAULT_PROFILE, options
|
|
|
813
829
|
}
|
|
814
830
|
|
|
815
831
|
export function modelsCommand(models = SUPPORTED_MODELS, options = {}) {
|
|
816
|
-
const selectedModel = options.selectedModel || registry.defaults.model;
|
|
832
|
+
const selectedModel = options.selectedModel || models[0] || registry.defaults.model;
|
|
817
833
|
console.log(`\n ${c.bold}Available models${c.reset}\n`);
|
|
818
834
|
for (const model of models) {
|
|
819
835
|
const suffix = model === selectedModel ? ` ${c.dim}(default)${c.reset}` : '';
|
|
@@ -62,9 +62,6 @@ DEFAULT_COMPACT_WINDOW="1000000"
|
|
|
62
62
|
DEFAULT_MAX_CONTEXT_TOKENS="3000000"
|
|
63
63
|
DEFAULT_MAX_CONCURRENT_SUBAGENTS="4"
|
|
64
64
|
DEFAULT_MAX_SUBAGENT_SPAWN_DEPTH="1"
|
|
65
|
-
DEFAULT_OPUS_MODEL="subconscious/glm-5.2"
|
|
66
|
-
DEFAULT_SONNET_MODEL="subconscious/tim-qwen3.6-27b"
|
|
67
|
-
DEFAULT_HAIKU_MODEL="subconscious/deepseek-v4-flash-marathon"
|
|
68
65
|
|
|
69
66
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
70
67
|
API_KEY="${CLAUDE_CODE_API_KEY:-${API_KEY:-}}"
|
|
@@ -73,15 +70,55 @@ COMPACT_WINDOW="${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-${COMPACT_WINDOW:-$DEFAULT_CO
|
|
|
73
70
|
MAX_CONTEXT_TOKENS="${CLAUDE_CODE_MAX_CONTEXT_TOKENS:-${MAX_CONTEXT_TOKENS:-$DEFAULT_MAX_CONTEXT_TOKENS}}"
|
|
74
71
|
MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS:-$DEFAULT_MAX_CONCURRENT_SUBAGENTS}"
|
|
75
72
|
MAX_SUBAGENT_SPAWN_DEPTH="${MAX_SUBAGENT_SPAWN_DEPTH:-$DEFAULT_MAX_SUBAGENT_SPAWN_DEPTH}"
|
|
73
|
+
|
|
74
|
+
PICKER_MODELS=()
|
|
75
|
+
if [[ -n "${SUBCONSCIOUS_MODELS:-}" ]]; then
|
|
76
|
+
while IFS= read -r picker_model; do
|
|
77
|
+
[[ -n "$picker_model" ]] && PICKER_MODELS+=("$picker_model")
|
|
78
|
+
done <<< "$SUBCONSCIOUS_MODELS"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
picker_model_at() {
|
|
82
|
+
local index="$1"
|
|
83
|
+
local count="${#PICKER_MODELS[@]}"
|
|
84
|
+
if [[ "$count" -eq 0 ]]; then
|
|
85
|
+
printf '%s' "$MODEL"
|
|
86
|
+
return
|
|
87
|
+
fi
|
|
88
|
+
if [[ "$index" -ge "$count" ]]; then
|
|
89
|
+
index=$((count - 1))
|
|
90
|
+
fi
|
|
91
|
+
printf '%s' "${PICKER_MODELS[$index]}"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
in_picker_catalog() {
|
|
95
|
+
local candidate="$1" existing
|
|
96
|
+
[[ -z "$candidate" ]] && return 1
|
|
97
|
+
[[ "${#PICKER_MODELS[@]}" -eq 0 ]] && return 0
|
|
98
|
+
for existing in "${PICKER_MODELS[@]}"; do
|
|
99
|
+
[[ "$existing" == "$candidate" ]] && return 0
|
|
100
|
+
done
|
|
101
|
+
return 1
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if [[ "${#PICKER_MODELS[@]}" -gt 0 ]]; then
|
|
105
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_OPUS_MODEL:-}" || ANTHROPIC_DEFAULT_OPUS_MODEL=""
|
|
106
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_SONNET_MODEL:-}" || ANTHROPIC_DEFAULT_SONNET_MODEL=""
|
|
107
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_HAIKU_MODEL:-}" || ANTHROPIC_DEFAULT_HAIKU_MODEL=""
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
DEFAULT_OPUS_MODEL="$(picker_model_at 0)"
|
|
111
|
+
DEFAULT_SONNET_MODEL="$(picker_model_at 1)"
|
|
112
|
+
DEFAULT_HAIKU_MODEL="$(picker_model_at 2)"
|
|
76
113
|
OPUS_MODEL="${ANTHROPIC_DEFAULT_OPUS_MODEL:-$DEFAULT_OPUS_MODEL}"
|
|
77
114
|
OPUS_MODEL_NAME="${ANTHROPIC_DEFAULT_OPUS_MODEL_NAME:-$OPUS_MODEL}"
|
|
78
|
-
OPUS_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION:-Subconscious
|
|
115
|
+
OPUS_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION:-Subconscious model ${OPUS_MODEL}}"
|
|
79
116
|
SONNET_MODEL="${ANTHROPIC_DEFAULT_SONNET_MODEL:-$DEFAULT_SONNET_MODEL}"
|
|
80
117
|
SONNET_MODEL_NAME="${ANTHROPIC_DEFAULT_SONNET_MODEL_NAME:-$SONNET_MODEL}"
|
|
81
|
-
SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION:-Subconscious
|
|
118
|
+
SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION:-Subconscious model ${SONNET_MODEL}}"
|
|
82
119
|
HAIKU_MODEL="${ANTHROPIC_DEFAULT_HAIKU_MODEL:-$DEFAULT_HAIKU_MODEL}"
|
|
83
120
|
HAIKU_MODEL_NAME="${ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME:-$HAIKU_MODEL}"
|
|
84
|
-
HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-Subconscious
|
|
121
|
+
HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-Subconscious model ${HAIKU_MODEL}}"
|
|
85
122
|
COMMAND="install"
|
|
86
123
|
|
|
87
124
|
usage() {
|
|
@@ -30,9 +30,6 @@ DEFAULT_COMPACT_WINDOW="1000000"
|
|
|
30
30
|
DEFAULT_MAX_CONTEXT_TOKENS="3000000"
|
|
31
31
|
DEFAULT_MAX_CONCURRENT_SUBAGENTS="4"
|
|
32
32
|
DEFAULT_MAX_SUBAGENT_SPAWN_DEPTH="1"
|
|
33
|
-
DEFAULT_OPUS_MODEL="subconscious/glm-5.2"
|
|
34
|
-
DEFAULT_SONNET_MODEL="subconscious/tim-qwen3.6-27b"
|
|
35
|
-
DEFAULT_HAIKU_MODEL="subconscious/deepseek-v4-flash-marathon"
|
|
36
33
|
|
|
37
34
|
GATEWAY_URL="${GATEWAY_URL:-}"
|
|
38
35
|
API_KEY="${CLAUDE_CODE_API_KEY:-${API_KEY:-}}"
|
|
@@ -41,15 +38,94 @@ COMPACT_WINDOW="${CLAUDE_CODE_AUTO_COMPACT_WINDOW:-${COMPACT_WINDOW:-$DEFAULT_CO
|
|
|
41
38
|
MAX_CONTEXT_TOKENS="${CLAUDE_CODE_MAX_CONTEXT_TOKENS:-${MAX_CONTEXT_TOKENS:-$DEFAULT_MAX_CONTEXT_TOKENS}}"
|
|
42
39
|
MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS:-$DEFAULT_MAX_CONCURRENT_SUBAGENTS}"
|
|
43
40
|
MAX_SUBAGENT_SPAWN_DEPTH="${MAX_SUBAGENT_SPAWN_DEPTH:-$DEFAULT_MAX_SUBAGENT_SPAWN_DEPTH}"
|
|
41
|
+
|
|
42
|
+
# Claude's Opus/Sonnet/Haiku/Fable/custom slots must follow the live catalog
|
|
43
|
+
# (SUBCONSCIOUS_MODELS). Packaged DeepSeek/GLM defaults are not a capability.
|
|
44
|
+
PICKER_MODELS=()
|
|
45
|
+
if [[ -n "${SUBCONSCIOUS_MODELS:-}" ]]; then
|
|
46
|
+
while IFS= read -r picker_model; do
|
|
47
|
+
[[ -n "$picker_model" ]] && PICKER_MODELS+=("$picker_model")
|
|
48
|
+
done <<< "$SUBCONSCIOUS_MODELS"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
picker_model_at() {
|
|
52
|
+
local index="$1"
|
|
53
|
+
local count="${#PICKER_MODELS[@]}"
|
|
54
|
+
if [[ "$count" -eq 0 ]]; then
|
|
55
|
+
printf '%s' "$MODEL"
|
|
56
|
+
return
|
|
57
|
+
fi
|
|
58
|
+
if [[ "$index" -ge "$count" ]]; then
|
|
59
|
+
index=$((count - 1))
|
|
60
|
+
fi
|
|
61
|
+
printf '%s' "${PICKER_MODELS[$index]}"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
in_picker_catalog() {
|
|
65
|
+
local candidate="$1" existing
|
|
66
|
+
[[ -z "$candidate" ]] && return 1
|
|
67
|
+
[[ "${#PICKER_MODELS[@]}" -eq 0 ]] && return 0
|
|
68
|
+
for existing in "${PICKER_MODELS[@]}"; do
|
|
69
|
+
[[ "$existing" == "$candidate" ]] && return 0
|
|
70
|
+
done
|
|
71
|
+
return 1
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if [[ "${#PICKER_MODELS[@]}" -gt 0 ]]; then
|
|
75
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_OPUS_MODEL:-}" || ANTHROPIC_DEFAULT_OPUS_MODEL=""
|
|
76
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_SONNET_MODEL:-}" || ANTHROPIC_DEFAULT_SONNET_MODEL=""
|
|
77
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_HAIKU_MODEL:-}" || ANTHROPIC_DEFAULT_HAIKU_MODEL=""
|
|
78
|
+
in_picker_catalog "${ANTHROPIC_DEFAULT_FABLE_MODEL:-}" || unset ANTHROPIC_DEFAULT_FABLE_MODEL ANTHROPIC_DEFAULT_FABLE_MODEL_NAME ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION
|
|
79
|
+
if [[ "${#PICKER_MODELS[@]}" -lt 5 ]] || ! in_picker_catalog "${ANTHROPIC_CUSTOM_MODEL_OPTION:-}"; then
|
|
80
|
+
unset ANTHROPIC_CUSTOM_MODEL_OPTION ANTHROPIC_CUSTOM_MODEL_OPTION_NAME ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
DEFAULT_OPUS_MODEL="$(picker_model_at 0)"
|
|
85
|
+
DEFAULT_SONNET_MODEL="$(picker_model_at 1)"
|
|
86
|
+
DEFAULT_HAIKU_MODEL="$(picker_model_at 2)"
|
|
87
|
+
DEFAULT_FABLE_MODEL="$(picker_model_at 3)"
|
|
44
88
|
OPUS_MODEL="${ANTHROPIC_DEFAULT_OPUS_MODEL:-$DEFAULT_OPUS_MODEL}"
|
|
45
89
|
OPUS_MODEL_NAME="${ANTHROPIC_DEFAULT_OPUS_MODEL_NAME:-$OPUS_MODEL}"
|
|
46
|
-
OPUS_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION:-Subconscious
|
|
90
|
+
OPUS_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_OPUS_MODEL_DESCRIPTION:-Subconscious model ${OPUS_MODEL}}"
|
|
47
91
|
SONNET_MODEL="${ANTHROPIC_DEFAULT_SONNET_MODEL:-$DEFAULT_SONNET_MODEL}"
|
|
48
92
|
SONNET_MODEL_NAME="${ANTHROPIC_DEFAULT_SONNET_MODEL_NAME:-$SONNET_MODEL}"
|
|
49
|
-
SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION:-Subconscious
|
|
93
|
+
SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION:-Subconscious model ${SONNET_MODEL}}"
|
|
50
94
|
HAIKU_MODEL="${ANTHROPIC_DEFAULT_HAIKU_MODEL:-$DEFAULT_HAIKU_MODEL}"
|
|
51
95
|
HAIKU_MODEL_NAME="${ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME:-$HAIKU_MODEL}"
|
|
52
|
-
HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-Subconscious
|
|
96
|
+
HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-Subconscious model ${HAIKU_MODEL}}"
|
|
97
|
+
FABLE_MODEL="${ANTHROPIC_DEFAULT_FABLE_MODEL:-$DEFAULT_FABLE_MODEL}"
|
|
98
|
+
FABLE_MODEL_NAME="${ANTHROPIC_DEFAULT_FABLE_MODEL_NAME:-$FABLE_MODEL}"
|
|
99
|
+
FABLE_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION:-Subconscious model ${FABLE_MODEL}}"
|
|
100
|
+
|
|
101
|
+
# Model IDs are already restricted to [-A-Za-z0-9._:/+], so they are JSON-safe.
|
|
102
|
+
build_claude_picker_settings() {
|
|
103
|
+
local ids=() model_id first options allowlist
|
|
104
|
+
if [[ "${#PICKER_MODELS[@]}" -gt 0 ]]; then
|
|
105
|
+
ids=("${PICKER_MODELS[@]}")
|
|
106
|
+
else
|
|
107
|
+
ids=("$MODEL")
|
|
108
|
+
fi
|
|
109
|
+
options=""
|
|
110
|
+
allowlist=""
|
|
111
|
+
first=1
|
|
112
|
+
for model_id in "${ids[@]}"; do
|
|
113
|
+
[[ -n "$model_id" ]] || continue
|
|
114
|
+
if [[ "$first" -eq 1 ]]; then
|
|
115
|
+
first=0
|
|
116
|
+
else
|
|
117
|
+
options="${options},"
|
|
118
|
+
allowlist="${allowlist},"
|
|
119
|
+
fi
|
|
120
|
+
options="${options}{\"model\":\"${model_id}\",\"label\":\"${model_id}\",\"description\":\"Subconscious model ${model_id}\"}"
|
|
121
|
+
allowlist="${allowlist}\"${model_id}\""
|
|
122
|
+
done
|
|
123
|
+
printf '{"availableModels":[%s],"modelPicker":{"replaceBuiltInOptions":true,"options":[%s]}}' "$allowlist" "$options"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if [[ -z "${SUBC_CLAUDE_SETTINGS:-}" ]]; then
|
|
127
|
+
SUBC_CLAUDE_SETTINGS="$(build_claude_picker_settings)"
|
|
128
|
+
fi
|
|
53
129
|
|
|
54
130
|
usage() {
|
|
55
131
|
cat <<'EOF'
|
|
@@ -136,6 +212,10 @@ export ANTHROPIC_DEFAULT_SONNET_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_SONNET_MO
|
|
|
136
212
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL="${ANTHROPIC_DEFAULT_HAIKU_MODEL:-$HAIKU_MODEL}"
|
|
137
213
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME="${ANTHROPIC_DEFAULT_HAIKU_MODEL_NAME:-$HAIKU_MODEL_NAME}"
|
|
138
214
|
export ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_HAIKU_MODEL_DESCRIPTION:-$HAIKU_MODEL_DESCRIPTION}"
|
|
215
|
+
export ANTHROPIC_DEFAULT_FABLE_MODEL="${ANTHROPIC_DEFAULT_FABLE_MODEL:-$FABLE_MODEL}"
|
|
216
|
+
export ANTHROPIC_DEFAULT_FABLE_MODEL_NAME="${ANTHROPIC_DEFAULT_FABLE_MODEL_NAME:-$FABLE_MODEL_NAME}"
|
|
217
|
+
export ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION="${ANTHROPIC_DEFAULT_FABLE_MODEL_DESCRIPTION:-$FABLE_MODEL_DESCRIPTION}"
|
|
218
|
+
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY="${CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY:-0}"
|
|
139
219
|
export CLAUDE_CODE_SUBAGENT_MODEL="${CLAUDE_CODE_SUBAGENT_MODEL:-$MODEL}"
|
|
140
220
|
export CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS="${MAX_CONCURRENT_SUBAGENTS}"
|
|
141
221
|
export CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH="${MAX_SUBAGENT_SPAWN_DEPTH}"
|
|
@@ -164,4 +244,6 @@ if [[ "${BASH_SOURCE[0]:-$0}" != "${0}" ]]; then
|
|
|
164
244
|
fi
|
|
165
245
|
|
|
166
246
|
# If executed, launch claude with any passed-through args.
|
|
167
|
-
|
|
247
|
+
# --settings is additional JSON: it replaces the built-in Fable/Opus lineup
|
|
248
|
+
# with the live catalog and allowlists only those models.
|
|
249
|
+
exec claude --settings "${SUBC_CLAUDE_SETTINGS}" ${PASSTHRU[@]+"${PASSTHRU[@]}"}
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
# "name": "Subconscious Gateway",
|
|
31
31
|
# "vendor": "customendpoint",
|
|
32
32
|
# "apiKey": "${input:chat.lm.secret.subconscious-gateway}",
|
|
33
|
-
# "apiType": "
|
|
33
|
+
# "apiType": "messages",
|
|
34
34
|
# "models": [
|
|
35
35
|
# {
|
|
36
36
|
# "id": "subconscious/glm-5.2",
|
|
37
37
|
# "name": "Subconscious GLM 5.2",
|
|
38
|
-
# "url": "https://your-gateway.example/v1/
|
|
38
|
+
# "url": "https://your-gateway.example/v1/messages",
|
|
39
39
|
# "toolCalling": true,
|
|
40
40
|
# "vision": false,
|
|
41
41
|
# "maxInputTokens": 5000000,
|
|
@@ -280,11 +280,13 @@ write_config() {
|
|
|
280
280
|
local user_dir="$1"
|
|
281
281
|
local models_json="${user_dir}/chatLanguageModels.json"
|
|
282
282
|
local base_url="${GATEWAY_URL%/}"
|
|
283
|
-
local
|
|
283
|
+
local messages_url
|
|
284
284
|
case "$base_url" in
|
|
285
|
-
*/v1/
|
|
286
|
-
*/v1)
|
|
287
|
-
|
|
285
|
+
*/v1/messages) messages_url="$base_url" ;;
|
|
286
|
+
*/v1/chat/completions) messages_url="${base_url%/chat/completions}/messages" ;;
|
|
287
|
+
*/v1/responses) messages_url="${base_url%/responses}/messages" ;;
|
|
288
|
+
*/v1) messages_url="${base_url}/messages" ;;
|
|
289
|
+
*) messages_url="${base_url}/v1/messages" ;;
|
|
288
290
|
esac
|
|
289
291
|
|
|
290
292
|
mkdir -p "$user_dir"
|
|
@@ -297,7 +299,7 @@ write_config() {
|
|
|
297
299
|
--argjson models "$provider_models" \
|
|
298
300
|
--arg modelId "$model_id" \
|
|
299
301
|
--arg modelName "Subconscious ${model_id}" \
|
|
300
|
-
--arg url "$
|
|
302
|
+
--arg url "$messages_url" \
|
|
301
303
|
--argjson maxIn "$MAX_INPUT_TOKENS" \
|
|
302
304
|
--argjson maxOut "$MAX_OUTPUT_TOKENS" \
|
|
303
305
|
'$models + [{
|
|
@@ -323,7 +325,7 @@ write_config() {
|
|
|
323
325
|
name: $name,
|
|
324
326
|
vendor: "customendpoint",
|
|
325
327
|
apiKey: $apiKeyRef,
|
|
326
|
-
apiType: "
|
|
328
|
+
apiType: "messages",
|
|
327
329
|
models: $models
|
|
328
330
|
}')
|
|
329
331
|
|
package/bin/tui.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
DEFAULT_PROFILE,
|
|
12
12
|
listProfiles,
|
|
13
13
|
loadProfile,
|
|
14
|
+
resolvedModelSetting,
|
|
14
15
|
RUNBOOK_DEFAULTS,
|
|
15
16
|
SUPPORTED_MODELS as PACKAGED_MODELS,
|
|
16
17
|
} from './profiles.js';
|
|
@@ -70,15 +71,11 @@ export async function resolveTuiExecutable(options = {}) {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
function selectedModelFor(profile) {
|
|
73
|
-
return (
|
|
74
|
-
process.env.SUBCONSCIOUS_MODEL?.trim() ||
|
|
75
|
-
profile.values.MODEL?.trim() ||
|
|
76
|
-
RUNBOOK_DEFAULTS.MODEL
|
|
77
|
-
);
|
|
74
|
+
return resolvedModelSetting(process.env.SUBCONSCIOUS_MODEL || profile.values.MODEL);
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
function subagentModelFor(profile) {
|
|
81
|
-
return profile.values.CLAUDE_CODE_SUBAGENT_MODEL
|
|
78
|
+
return resolvedModelSetting(profile.values.CLAUDE_CODE_SUBAGENT_MODEL);
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
function gatewayFor(profile) {
|
|
@@ -117,14 +114,15 @@ export async function createTuiState(profileName = DEFAULT_PROFILE, options = {}
|
|
|
117
114
|
);
|
|
118
115
|
|
|
119
116
|
const auth = await getApiKey(activeProfile);
|
|
120
|
-
const
|
|
117
|
+
const requestedModel = selectedModelFor(activeProfile);
|
|
121
118
|
const gatewayUrl = gatewayFor(activeProfile);
|
|
122
119
|
const catalog = await resolveModelCatalog({
|
|
123
120
|
baseUrl: gatewayUrl,
|
|
124
121
|
apiKey: auth?.key,
|
|
125
|
-
selectedModel,
|
|
122
|
+
selectedModel: requestedModel,
|
|
126
123
|
fallbackModels: PACKAGED_MODELS,
|
|
127
124
|
});
|
|
125
|
+
const selectedModel = requestedModel;
|
|
128
126
|
|
|
129
127
|
return {
|
|
130
128
|
version: await packageVersion(),
|