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 +16 -2
- package/bin/agents.js +210 -70
- package/bin/auth.js +80 -32
- package/bin/branding.js +3 -1
- package/bin/cli.js +28 -20
- package/bin/clipboard-image.js +22 -5
- package/bin/colors.js +1 -1
- package/bin/feedback.js +57 -18
- package/bin/model-capabilities.js +4 -1
- package/bin/models.js +22 -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/opencode-provider.js +27 -5
- package/bin/profiles.js +126 -44
- package/bin/registry.generated.json +1 -1
- package/bin/runbook/opencode/opencode-plugin.d.ts +9 -0
- package/bin/runbook/opencode/subconscious-compaction.ts +26 -26
- package/bin/runbook/pi/subconscious-compaction.ts +69 -49
- package/bin/sessions.js +126 -41
- package/bin/tui.js +91 -32
- package/bin/update-check.js +49 -20
- package/bin/upgrade.js +49 -18
- package/bin/usage.js +26 -14
- package/bin/windows/agents.js +135 -32
- package/bin/windows/claude.js +43 -14
- package/bin/windows/common.js +56 -19
- package/bin/windows/install.js +120 -32
- package/bin/windows/launch.js +211 -49
- package/bin/windows/pi-compaction.ts +68 -13
- package/bin/windows/process.js +198 -56
- package/bin/windows/setup.js +231 -56
- package/package.json +9 -3
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 `
|
|
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 {
|
|
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)
|
|
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')
|
|
91
|
-
|
|
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 {
|
|
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)
|
|
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
|
-
[
|
|
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:
|
|
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:
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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
|
-
[
|
|
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:
|
|
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
|
-
[
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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(
|
|
310
|
-
|
|
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(
|
|
365
|
+
console.log(
|
|
366
|
+
` ${c.cyan}${option.padEnd(optionWidth)}${c.reset} ${description}`,
|
|
367
|
+
);
|
|
319
368
|
}
|
|
320
369
|
console.log();
|
|
321
370
|
}
|
|
322
|
-
console.log(
|
|
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(
|
|
326
|
-
|
|
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(
|
|
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
|
|
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(
|
|
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 || '')
|
|
474
|
-
|
|
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 [
|
|
510
|
-
|
|
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({
|
|
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({
|
|
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(
|
|
612
|
+
function runInstaller(
|
|
613
|
+
agent,
|
|
614
|
+
install = agent.install,
|
|
615
|
+
usePackagedScript = true,
|
|
616
|
+
) {
|
|
538
617
|
return new Promise((resolve) => {
|
|
539
|
-
const installScript =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|
|
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)
|
|
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`] =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
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 {
|
|
962
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
1006
|
-
|
|
1007
|
-
|
|
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(
|
|
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
|
|
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 {
|
|
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(
|
|
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(
|