subconscious-cli 6.1.0 → 6.2.0
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 +52 -47
- package/bin/agent-data.js +37 -1
- package/bin/agent-help.js +12 -4
- package/bin/agent-install.js +19 -135
- package/bin/agents.js +6 -5
- package/bin/cli.js +2 -1
- package/bin/models.js +12 -0
- 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/runbook/README.md +13 -2
- package/bin/runbook/claude-code/agent.json +6 -6
- package/bin/runbook/codex/agent.json +1 -0
- package/bin/runbook/copilot/agent.json +1 -0
- package/bin/runbook/cursor/agent.json +3 -2
- package/bin/runbook/deepseek-harness/agent.json +1 -0
- package/bin/runbook/opencode/agent.json +1 -0
- package/bin/runbook/pi/agent.json +1 -0
- package/bin/runbook/shared.json +1 -2
- package/bin/sessions.js +1 -26
- package/bin/update-check.js +30 -0
- package/bin/upgrade.js +3 -3
- package/bin/windows/agents.js +14 -47
- package/bin/windows/launch.js +1 -1
- package/package.json +2 -3
- package/bin/runbook/subconscious-code/agent.json +0 -156
- package/bin/runbook/subconscious-code/install.sh +0 -154
- package/bin/windows/install.js +0 -182
package/README.md
CHANGED
|
@@ -4,25 +4,59 @@ Log in to Subconscious, then run coding agents against the Subconscious gateway.
|
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
|
+
Install with npm:
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
10
|
npm install -g subconscious-cli
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install on macOS or Linux without npm:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
curl -fsSL https://raw.githubusercontent.com/subconscious-systems/subconscious/main/scripts/install.sh | bash
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then log in and launch an agent:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
9
22
|
subc login
|
|
10
|
-
subc
|
|
23
|
+
subc opencode
|
|
11
24
|
subc claude
|
|
12
25
|
```
|
|
13
26
|
|
|
27
|
+
The curl installer downloads the `subconscious-cli` tarball from the npm
|
|
28
|
+
registry, checks it against the registry's integrity hash, and installs it in
|
|
29
|
+
`~/.local/share/subconscious-cli` with a `subc` link in `~/.local/bin`. It does
|
|
30
|
+
not need npm, so a registry policy that blocks `npm install -g` does not stop
|
|
31
|
+
it. Node.js 18 or newer must already be installed, because `subc` runs on it.
|
|
32
|
+
If `~/.local/bin` is not on your `PATH`, the installer prints the line to add.
|
|
33
|
+
Run it again to reinstall or update. These variables change its defaults:
|
|
34
|
+
|
|
35
|
+
| Variable | Default |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `SUBC_VERSION` | `latest` (a version such as `6.1.0`, or a dist-tag) |
|
|
38
|
+
| `SUBC_INSTALL_DIR` | `~/.local/share/subconscious-cli` |
|
|
39
|
+
| `SUBC_BIN_DIR` | `~/.local/bin` |
|
|
40
|
+
| `SUBC_REGISTRY` | `https://registry.npmjs.org` |
|
|
41
|
+
|
|
42
|
+
On Windows, use npm (see [Native Windows support](#native-windows-support)).
|
|
43
|
+
|
|
44
|
+
subc does not install the coding agents. Install each agent you use from its
|
|
45
|
+
vendor. If an agent is missing, `subc <agent>` prints its install command and
|
|
46
|
+
official install page, then exits with status 127.
|
|
47
|
+
|
|
14
48
|
Every interactive `subc` command checks npm for a newer CLI release. When an
|
|
15
49
|
update is available, a notice shows the installed and latest versions and lets
|
|
16
50
|
you select **Update now** or **Skip for now** with the arrow keys and Enter.
|
|
17
51
|
Each option describes what it will do, and the active option is highlighted.
|
|
18
|
-
Update runs `npm install -g subconscious-cli@latest
|
|
19
|
-
requested command.
|
|
52
|
+
Update runs `npm install -g subconscious-cli@latest`, or the curl installer
|
|
53
|
+
again if that is how subc was installed; Skip continues the requested command.
|
|
20
54
|
Non-interactive commands automatically skip, and registry errors and timeouts
|
|
21
55
|
never block the requested command. Set `SUBC_DISABLE_UPDATE_CHECK=1` to suppress
|
|
22
56
|
the check in offline automation.
|
|
23
57
|
|
|
24
58
|
Login creates both the saved credential and a ready-to-use `default` profile,
|
|
25
|
-
so
|
|
59
|
+
so OpenCode, Claude Code, Codex, and DeepSeek Harness can launch immediately.
|
|
26
60
|
Persistent editor and Pi integrations are installed per agent:
|
|
27
61
|
|
|
28
62
|
```bash
|
|
@@ -53,8 +87,8 @@ Every command accepts `help` as a subcommand. These only read the selected
|
|
|
53
87
|
profile; they do not authenticate, install, configure, or launch anything:
|
|
54
88
|
|
|
55
89
|
```bash
|
|
90
|
+
subc opencode help
|
|
56
91
|
subc claude help
|
|
57
|
-
subc marathon help
|
|
58
92
|
subc codex help
|
|
59
93
|
subc cursor help
|
|
60
94
|
subc config help
|
|
@@ -69,7 +103,6 @@ gateway environment applied for that process. Arguments pass through as usual:
|
|
|
69
103
|
|
|
70
104
|
```bash
|
|
71
105
|
subc claude --continue
|
|
72
|
-
subc marathon -- -p "fix the tests"
|
|
73
106
|
subc codex exec "write a test"
|
|
74
107
|
subc opencode
|
|
75
108
|
subc pi
|
|
@@ -105,8 +138,9 @@ Update with `npm.cmd install -g subconscious-cli@latest` or `subc.cmd upgrade --
|
|
|
105
138
|
|
|
106
139
|
If PowerShell's execution policy blocks npm-generated `.ps1` entry points, use
|
|
107
140
|
`npm.cmd` and `subc.cmd` or run the same commands in Command Prompt. No policy
|
|
108
|
-
change is required.
|
|
109
|
-
|
|
141
|
+
change is required. subc does not install the agents on Windows either. If an
|
|
142
|
+
agent is missing, subc prints its install command and official install page,
|
|
143
|
+
then exits with status 127.
|
|
110
144
|
|
|
111
145
|
Native executables and standard npm `.cmd` shims are supported. The launcher
|
|
112
146
|
invokes the underlying executable or Node entry point directly, preserving
|
|
@@ -129,19 +163,6 @@ VS Code's secret store and prompts for its key. Its configuration goes under
|
|
|
129
163
|
`%APPDATA%\Code\User` (or Code - Insiders/VSCodium). Rerun install after moving
|
|
130
164
|
your Node installation, because hooks record the absolute Node executable path.
|
|
131
165
|
|
|
132
|
-
To install the native x64 `marathon.exe` (stable releases starting with 0.1.4), clear
|
|
133
|
-
any preview version pin from the current PowerShell session:
|
|
134
|
-
|
|
135
|
-
```powershell
|
|
136
|
-
Remove-Item Env:SC_CODE_VERSION -ErrorAction SilentlyContinue
|
|
137
|
-
subc.cmd marathon install
|
|
138
|
-
subc.cmd marathon
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
The installer requires the selected release's Windows asset and SHA-256
|
|
142
|
-
checksum; it reports a clear error if unavailable and never downloads a Unix
|
|
143
|
-
binary as a fallback. Windows ARM64 binaries are not currently published.
|
|
144
|
-
|
|
145
166
|
Run `npm run test:windows` from the repository root for the separate Windows suite. The
|
|
146
167
|
Windows CI job also builds/tests the Go TUI. Existing Unix tests and runbooks
|
|
147
168
|
remain separate and unchanged.
|
|
@@ -152,18 +173,17 @@ The packaged integrations live in `bin/runbook`.
|
|
|
152
173
|
|
|
153
174
|
| Command | Behavior |
|
|
154
175
|
| --- | --- |
|
|
155
|
-
| `subc
|
|
176
|
+
| `subc opencode` | Launch OpenCode with the runbook provider, client header, and context/output limits |
|
|
156
177
|
| `subc claude` | Launch Claude Code with the runbook environment, context limits, subagent limits, and OTEL usage reporting |
|
|
157
178
|
| `subc codex` | Launch Codex with the runbook provider, temporary model catalog, and surgically merged compaction hooks |
|
|
158
|
-
| `subc
|
|
159
|
-
| `subc cursor install` | Install/update Cursor conversation and compaction hooks |
|
|
179
|
+
| `subc cursor install` | Install/update Cursor conversation and compaction hooks (Cursor desktop app and IDE only — the Cursor CLI is not supported) |
|
|
160
180
|
| `subc copilot install` | Install/update the VS Code custom endpoint and Copilot hooks |
|
|
161
181
|
| `subc pi` | Refresh the Pi provider from the live catalog, then launch |
|
|
162
182
|
| `subc dsh` | Launch the DeepSeek Harness Web UI with a temporary provider populated from the live catalog |
|
|
163
183
|
|
|
164
184
|
## Headless runs
|
|
165
185
|
|
|
166
|
-
`subc <agent> headless PROMPT [args...]` runs one task without a terminal UI and exits with the agent's status. It works for `claude`, `codex`, `opencode`, `pi`,
|
|
186
|
+
`subc <agent> headless PROMPT [args...]` runs one task without a terminal UI and exits with the agent's status. It works for `claude`, `codex`, `opencode`, `pi`, and `dsh`, and uses the same setup as an interactive launch. It never prompts: there is no update check, a missing agent fails with its install command, stdin is not read, and stdout carries only the agent's own output. Remaining arguments go to the agent, for example `subc claude headless "fix the failing test" --output-format stream-json --verbose`. subc reads `-p`/`--profile` and `--model` itself; put agent options with those names after `--`.
|
|
167
187
|
|
|
168
188
|
The agents keep their own permission defaults, so a run that should edit files usually needs the agent's permission flags in `[args...]`. For example, `codex exec` uses a read-only sandbox and requires a git repository unless given `--skip-git-repo-check`.
|
|
169
189
|
|
|
@@ -188,7 +208,6 @@ The endpoint must speak the same APIs as the Subconscious gateway: Anthropic Mes
|
|
|
188
208
|
| `codex` | `codex exec -- PROMPT` |
|
|
189
209
|
| `opencode` | `opencode run`, with the prompt on stdin |
|
|
190
210
|
| `pi` | `pi --print`, with the prompt on stdin |
|
|
191
|
-
| `marathon` | `marathon --print=PROMPT` |
|
|
192
211
|
| `dsh` | `dsh --profile headless`, with the prompt on stdin |
|
|
193
212
|
|
|
194
213
|
The prompt is always the argument after `headless`, including for `dsh`, which no longer reads a task piped into subc. Where the table says stdin, subc passes the prompt that way so it reaches the agent unchanged even when it starts with `-` or `@`. On Windows only `dsh` supports headless runs.
|
|
@@ -222,7 +241,7 @@ Each `harnesses.<id>` entry has:
|
|
|
222
241
|
|
|
223
242
|
| Field | Meaning |
|
|
224
243
|
| --- | --- |
|
|
225
|
-
| `install` | `method`, `package` or `repository`, `version` or `channel`, `minimum_version`, release `targets`,
|
|
244
|
+
| `install` | `method`, `package` or `repository`, `version` or `channel`, `minimum_version`, release `targets`, the per-OS `commands`, and `url`, the vendor's official install page. subc shows `commands` and `url` and never runs them. |
|
|
226
245
|
| `prerequisites` | Commands the runbook needs, such as `jq`, with what needs them and whether they are required. `a\|b` means either one. |
|
|
227
246
|
| `binary` | Executable name, or `null` for IDE integrations. |
|
|
228
247
|
| `launch` | `argv` template, `headless_argv` for `subc <agent> headless` (absent when unsupported), `headless_stdin` when the prompt is passed on stdin instead, `headless_platforms` (`darwin`, `linux`, `win32`) where it works, `resume` and `handoff` argv for `subc sessions`, Codex's `config_flag`, and whether a launch also writes persistent files. |
|
|
@@ -240,7 +259,7 @@ Enumerated values:
|
|
|
240
259
|
|
|
241
260
|
| Field | Values |
|
|
242
261
|
| --- | --- |
|
|
243
|
-
| `install.method` | `npm`, `
|
|
262
|
+
| `install.method` | `npm`, `user`, `null` |
|
|
244
263
|
| `compaction.default` | `on`, `unknown` |
|
|
245
264
|
| knob `kind` | `env`, `cli-config`, `config-field`, `model-catalog-field`, `null` |
|
|
246
265
|
| `threshold.semantics` | `window`, `trigger` |
|
|
@@ -293,7 +312,7 @@ subc sessions resume codex:SESSION_ID --harness opencode
|
|
|
293
312
|
```
|
|
294
313
|
|
|
295
314
|
The catalog discovers recent sessions written locally by Claude Code, Codex,
|
|
296
|
-
OpenCode,
|
|
315
|
+
OpenCode, and Pi. It shows each session's originating
|
|
297
316
|
harness, title, last activity, project directory, and model when the harness
|
|
298
317
|
records one. Selecting the original harness uses its native resume mechanism.
|
|
299
318
|
|
|
@@ -309,26 +328,12 @@ conversation stores do not expose a stable local resume interface. A session
|
|
|
309
328
|
whose local transcript is missing remains available for native resume but does
|
|
310
329
|
not offer cross-harness destinations.
|
|
311
330
|
|
|
312
|
-
|
|
313
|
-
|
|
331
|
+
subc never installs OpenCode, Claude Code, Codex, DeepSeek Harness, or Pi. If
|
|
332
|
+
one is missing, subc prints its install command and official install page, then
|
|
333
|
+
exits with status 127, in interactive and headless runs alike. Pi refreshes its Subconscious provider on every
|
|
314
334
|
`subc pi` launch while preserving all other providers in `models.json`; its
|
|
315
335
|
executable must already be installed.
|
|
316
336
|
|
|
317
|
-
`subc marathon install` detects the operating system and architecture, then downloads
|
|
318
|
-
and checksum-verifies the matching precompiled release from
|
|
319
|
-
`subconscious-systems/subconscious-code`. Apple Silicon and Intel macOS plus
|
|
320
|
-
x86_64 and ARM64 Linux and Windows x64 are supported; Cargo is not required.
|
|
321
|
-
|
|
322
|
-
Starting with CLI 4.1.1, `subc marathon` is the native agent command. On Windows,
|
|
323
|
-
the installer writes `marathon.exe`, avoiding the system `sc.exe` command.
|
|
324
|
-
`subc sc` remains a compatibility alias but never launches Windows service
|
|
325
|
-
control. Older pinned release binaries are also installed as `marathon.exe`.
|
|
326
|
-
Starting with CLI 4.1.2 and native agent 0.1.6, macOS and Linux also install
|
|
327
|
-
and launch `marathon`. Existing `.sc` settings, sessions, and `SC_*` variables
|
|
328
|
-
are preserved on every platform. Shell behavior is unchanged. Older Unix
|
|
329
|
-
release archives are installed under the new name; existing `sc` files are
|
|
330
|
-
left untouched. `subc sc` remains a compatibility alias on every platform.
|
|
331
|
-
|
|
332
337
|
`subc codex` disables Codex apps and plugin tools for that launch by default so
|
|
333
338
|
requests remain below the gateway's 128-tool limit. Core coding tools remain
|
|
334
339
|
available. Use `subc codex --external-tools` to opt back in when targeting a
|
|
@@ -383,7 +388,7 @@ rewritten. Run a one-shot task with `subc dsh headless "PROMPT"`.
|
|
|
383
388
|
```
|
|
384
389
|
|
|
385
390
|
The file is mode `600` and contains the shared gateway URL, API key, model,
|
|
386
|
-
optional per-agent key overrides, and all
|
|
391
|
+
optional per-agent key overrides, and all Claude/Codex/OpenCode/Pi/Copilot/
|
|
387
392
|
DeepSeek Harness context and output settings used by the packaged runbook scripts.
|
|
388
393
|
|
|
389
394
|
```bash
|
package/bin/agent-data.js
CHANGED
|
@@ -77,6 +77,30 @@ function validateLaunch(launch, where) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function validateInstall(install, where) {
|
|
81
|
+
check(isObject(install), where, 'install block is required');
|
|
82
|
+
check(
|
|
83
|
+
typeof install.url === 'string' && install.url.startsWith('https://'),
|
|
84
|
+
where,
|
|
85
|
+
'install.url must be an https:// link to the official install page',
|
|
86
|
+
);
|
|
87
|
+
const { commands } = install;
|
|
88
|
+
if (commands === null) return;
|
|
89
|
+
check(
|
|
90
|
+
isObject(commands),
|
|
91
|
+
where,
|
|
92
|
+
'install.commands must be an object or null',
|
|
93
|
+
);
|
|
94
|
+
for (const [os, command] of Object.entries(commands)) {
|
|
95
|
+
check(PLATFORMS.has(os), where, `install.commands.${os} is not an OS`);
|
|
96
|
+
check(
|
|
97
|
+
typeof command === 'string' && command,
|
|
98
|
+
where,
|
|
99
|
+
`install.commands.${os}`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
80
104
|
function validateRunbook(runbook, where) {
|
|
81
105
|
check(isObject(runbook), where, 'runbook block is required');
|
|
82
106
|
check(MODES.has(runbook.mode), where, `bad runbook.mode ${runbook.mode}`);
|
|
@@ -101,7 +125,7 @@ export function validateAgent(agent, id) {
|
|
|
101
125
|
for (const key of ['command', 'name', 'description', 'protocol'])
|
|
102
126
|
check(typeof agent[key] === 'string' && agent[key], where, `${key}`);
|
|
103
127
|
check(isStringList(agent.aliases), where, 'aliases must be a list');
|
|
104
|
-
|
|
128
|
+
validateInstall(agent.install, where);
|
|
105
129
|
check(isObject(agent.help), where, 'help block is required');
|
|
106
130
|
check(Array.isArray(agent.help.options), where, 'help.options');
|
|
107
131
|
validateRunbook(agent.runbook, where);
|
|
@@ -176,6 +200,18 @@ export function installCommands(agent) {
|
|
|
176
200
|
);
|
|
177
201
|
}
|
|
178
202
|
|
|
203
|
+
/**
|
|
204
|
+
* The install command and official install page to show when the agent's
|
|
205
|
+
* binary is missing. subc prints these and never runs them. Windows never
|
|
206
|
+
* borrows the Linux command.
|
|
207
|
+
*/
|
|
208
|
+
export function installAdvice(agent, platform = process.platform) {
|
|
209
|
+
const commands = installCommands(agent);
|
|
210
|
+
const command =
|
|
211
|
+
commands?.[platform] ?? (platform === 'win32' ? null : commands?.linux);
|
|
212
|
+
return { command: command ?? null, url: agent.install.url };
|
|
213
|
+
}
|
|
214
|
+
|
|
179
215
|
/** The API key input that overrides API_KEY for this agent, if any. */
|
|
180
216
|
export function agentKeyName(agent) {
|
|
181
217
|
return (
|
package/bin/agent-help.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
agentBinary,
|
|
2
3
|
agentCommandName,
|
|
3
4
|
agentInputs,
|
|
4
5
|
agentSetupActions,
|
|
6
|
+
installAdvice,
|
|
5
7
|
} from './agent-data.js';
|
|
6
8
|
import { c } from './colors.js';
|
|
7
9
|
import { profileSettingsForAgent, resolvedProfileValues } from './profiles.js';
|
|
@@ -72,12 +74,18 @@ function printSetupHints(agent, profile) {
|
|
|
72
74
|
console.log(
|
|
73
75
|
`\n Edit the env file with ${c.cyan}subc -p ${profile?.name || 'default'} config edit${c.reset}.`,
|
|
74
76
|
);
|
|
77
|
+
if (agentBinary(agent)) {
|
|
78
|
+
const { command: install, url } = installAdvice(agent);
|
|
79
|
+
const how = install
|
|
80
|
+
? `with ${c.cyan}${install}${c.reset}, or see ${c.cyan}${url}${c.reset}`
|
|
81
|
+
: `from ${c.cyan}${url}${c.reset}`;
|
|
82
|
+
console.log(` subc doesn't install ${agent.name}. Install it ${how}.`);
|
|
83
|
+
}
|
|
75
84
|
const actions = agentSetupActions(agent);
|
|
76
85
|
if (actions.includes('install')) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
console.log(` ${what} ${c.cyan}subc ${command} install${c.reset}.`);
|
|
86
|
+
console.log(
|
|
87
|
+
` Install the persistent integration with ${c.cyan}subc ${command} install${c.reset}.`,
|
|
88
|
+
);
|
|
81
89
|
}
|
|
82
90
|
if (actions.includes('uninstall')) {
|
|
83
91
|
console.log(
|
package/bin/agent-install.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFileSync
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { constants as fsConstants } from 'node:fs';
|
|
3
3
|
import fs from 'node:fs/promises';
|
|
4
4
|
import os from 'node:os';
|
|
@@ -9,26 +9,11 @@ import {
|
|
|
9
9
|
agentById,
|
|
10
10
|
agentCommandName,
|
|
11
11
|
agentInstallDirs,
|
|
12
|
-
|
|
12
|
+
installAdvice,
|
|
13
13
|
} from './agent-data.js';
|
|
14
|
-
import { runbookScriptPath } from './agent-spawn.js';
|
|
15
14
|
import { c } from './colors.js';
|
|
16
15
|
import { compareVersions } from './update-check.js';
|
|
17
16
|
|
|
18
|
-
/**
|
|
19
|
-
* The install command for this OS, falling back to the Linux one, then any
|
|
20
|
-
* command present; plus the optional fallback command.
|
|
21
|
-
*/
|
|
22
|
-
export function resolveInstall(agent) {
|
|
23
|
-
const commands = installCommands(agent);
|
|
24
|
-
if (!commands) return { command: undefined, fallback: undefined };
|
|
25
|
-
const command =
|
|
26
|
-
commands[process.platform] ||
|
|
27
|
-
commands.linux ||
|
|
28
|
-
Object.entries(commands).find(([os]) => os !== 'fallback')?.[1];
|
|
29
|
-
return { command, fallback: commands.fallback };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
17
|
/**
|
|
33
18
|
* Common locations a freshly-installed coding-agent binary lands in but which
|
|
34
19
|
* are often NOT on the current process's PATH (e.g. aider/claude install into
|
|
@@ -139,21 +124,6 @@ export function augmentPath(
|
|
|
139
124
|
].join(path.delimiter);
|
|
140
125
|
}
|
|
141
126
|
|
|
142
|
-
/** Ask a yes/no question on the TTY. Empty answer counts as yes. */
|
|
143
|
-
function askYesNo(question) {
|
|
144
|
-
return new Promise((resolve) => {
|
|
145
|
-
const rl = readline.createInterface({
|
|
146
|
-
input: process.stdin,
|
|
147
|
-
output: process.stdout,
|
|
148
|
-
});
|
|
149
|
-
rl.question(question, (answer) => {
|
|
150
|
-
rl.close();
|
|
151
|
-
const a = answer.trim().toLowerCase();
|
|
152
|
-
resolve(a === '' || a === 'y' || a === 'yes');
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
127
|
function waitForEnter(prompt) {
|
|
158
128
|
return new Promise((resolve) => {
|
|
159
129
|
const rl = readline.createInterface({
|
|
@@ -167,113 +137,33 @@ function waitForEnter(prompt) {
|
|
|
167
137
|
});
|
|
168
138
|
}
|
|
169
139
|
|
|
170
|
-
/**
|
|
171
|
-
function
|
|
172
|
-
agent
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const installScript =
|
|
178
|
-
usePackagedScript && agent.runbook.binary_install_script;
|
|
179
|
-
const child = installScript
|
|
180
|
-
? spawn('bash', [runbookScriptPath(agent, installScript), 'install'], {
|
|
181
|
-
stdio: 'inherit',
|
|
182
|
-
})
|
|
183
|
-
: spawn(install, { shell: true, stdio: 'inherit' });
|
|
184
|
-
child.on('error', () => resolve(false));
|
|
185
|
-
child.on('exit', (code) => resolve(code === 0));
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** Print the resolved install command (plus any fallback) for an agent. */
|
|
190
|
-
function printInstallCommands(agent) {
|
|
191
|
-
const { command, fallback } = resolveInstall(agent);
|
|
192
|
-
console.error(` ${c.cyan}${command}${c.reset}`);
|
|
193
|
-
if (fallback) {
|
|
194
|
-
console.error(` ${c.dim}or, as a fallback:${c.reset}`);
|
|
195
|
-
console.error(` ${c.cyan}${fallback}${c.reset}`);
|
|
196
|
-
}
|
|
197
|
-
console.error('');
|
|
140
|
+
/** Print the install command and official install page; subc runs neither. */
|
|
141
|
+
function printInstallAdvice(agent) {
|
|
142
|
+
const { command, url } = installAdvice(agent);
|
|
143
|
+
if (command) console.error(` ${c.cyan}${command}${c.reset}`);
|
|
144
|
+
console.error(
|
|
145
|
+
` ${c.dim}Official install page:${c.reset} ${c.cyan}${url}${c.reset}\n`,
|
|
146
|
+
);
|
|
198
147
|
}
|
|
199
148
|
|
|
200
149
|
/**
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* - non-interactive: print the resolved install command (+ fallback) and
|
|
205
|
-
* exit 127 without running anything.
|
|
206
|
-
*
|
|
207
|
-
* Returns the directory containing the bin (to prepend to the child's PATH) on
|
|
208
|
-
* success. May exit the process on failure or when manual action is needed.
|
|
150
|
+
* Return the directory that holds the agent's binary. subc never installs a
|
|
151
|
+
* third-party agent: when the binary is missing it prints how to install it
|
|
152
|
+
* and exits 127, in every shell.
|
|
209
153
|
*/
|
|
210
|
-
export async function ensureInstalled(agent
|
|
154
|
+
export async function ensureInstalled(agent) {
|
|
211
155
|
const bin = agentBinary(agent);
|
|
212
|
-
const
|
|
213
|
-
const preferredDirs = preferredBinDirsForAgent(agent);
|
|
214
|
-
const existing = await resolveBinPath(bin, preferredDirs);
|
|
156
|
+
const existing = await resolveBinPath(bin, preferredBinDirsForAgent(agent));
|
|
215
157
|
if (existing) return existing;
|
|
216
158
|
|
|
217
|
-
// Agents without an installer are launch-only. Their setup integration may
|
|
218
|
-
// configure the provider, but `subc <agent>` must never install the binary.
|
|
219
|
-
if (!install) {
|
|
220
|
-
console.error(
|
|
221
|
-
`\n ${c.red}${agent.name} isn't installed${c.reset} ${c.dim}(\`${bin}\` not found on PATH).${c.reset}`,
|
|
222
|
-
);
|
|
223
|
-
console.error(
|
|
224
|
-
` Install ${agent.name} separately, then re-run ${c.cyan}subc ${agentCommandName(agent)}${c.reset}.\n`,
|
|
225
|
-
);
|
|
226
|
-
process.exit(127);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const interactive = mayPrompt && process.stdin.isTTY && process.stdout.isTTY;
|
|
230
|
-
|
|
231
|
-
if (!interactive) {
|
|
232
|
-
console.error(
|
|
233
|
-
`\n ${c.red}${agent.name} isn't installed${c.reset} ${c.dim}(\`${bin}\` not found on PATH).${c.reset}`,
|
|
234
|
-
);
|
|
235
|
-
console.error(` Install it with:\n`);
|
|
236
|
-
printInstallCommands(agent);
|
|
237
|
-
process.exit(127);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
console.error(`\n ${c.bold}${agent.name}${c.reset} isn't installed.`);
|
|
241
|
-
const ok = await askYesNo(` Install it now? ${c.dim}[Y/n]${c.reset} `);
|
|
242
|
-
if (!ok) {
|
|
243
|
-
console.error(`\n No problem. Install it yourself with:\n`);
|
|
244
|
-
printInstallCommands(agent);
|
|
245
|
-
process.exit(127);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
159
|
console.error(
|
|
249
|
-
`\n ${c.
|
|
160
|
+
`\n ${c.red}${agent.name} isn't installed${c.reset} ${c.dim}(\`${bin}\` not found on PATH).${c.reset}`,
|
|
250
161
|
);
|
|
251
|
-
let installed = await runInstaller(agent);
|
|
252
|
-
|
|
253
|
-
// Primary failed and a fallback exists — try it once.
|
|
254
|
-
if (!installed && fallback) {
|
|
255
|
-
console.error(
|
|
256
|
-
`\n ${c.dim}That didn't work. Trying the fallback: ${c.reset}${c.cyan}${fallback}${c.reset}\n`,
|
|
257
|
-
);
|
|
258
|
-
installed = await runInstaller(agent, fallback, false);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (!installed) {
|
|
262
|
-
console.error(`\n ${c.red}Install failed.${c.reset} Try it manually:\n`);
|
|
263
|
-
printInstallCommands(agent);
|
|
264
|
-
process.exit(127);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// PATH hardening: the freshly-installed binary is often not on the current
|
|
268
|
-
// process's PATH. Re-resolve against PATH + candidate dirs.
|
|
269
|
-
const found = await resolveBinPath(bin, preferredDirs);
|
|
270
|
-
if (found) return found;
|
|
271
|
-
|
|
272
162
|
console.error(
|
|
273
|
-
|
|
274
|
-
`Open a new terminal (or add a bin dir to PATH) and re-run \`subc ${agentCommandName(agent)}\`.${c.reset}\n`,
|
|
163
|
+
` subc doesn't install ${agent.name}. Install it yourself, then re-run ${c.cyan}subc ${agentCommandName(agent)}${c.reset}.\n`,
|
|
275
164
|
);
|
|
276
|
-
|
|
165
|
+
printInstallAdvice(agent);
|
|
166
|
+
process.exit(127);
|
|
277
167
|
}
|
|
278
168
|
|
|
279
169
|
export function parseClaudeVersion(text) {
|
|
@@ -335,14 +225,8 @@ export async function ensureMinimumVersion(
|
|
|
335
225
|
console.error(
|
|
336
226
|
`\n Minimum supported ${agent.name} version is ${minimum}. Your version is ${version}. Upgrade to get the best experience.\n`,
|
|
337
227
|
);
|
|
338
|
-
const { command, fallback } = resolveInstall(agent);
|
|
339
228
|
console.error(` Upgrade it with:`);
|
|
340
|
-
|
|
341
|
-
if (fallback) {
|
|
342
|
-
console.error(` or`);
|
|
343
|
-
console.error(` ${c.cyan}${fallback}${c.reset}`);
|
|
344
|
-
}
|
|
345
|
-
console.error('');
|
|
229
|
+
printInstallAdvice(agent);
|
|
346
230
|
if (
|
|
347
231
|
mayPrompt &&
|
|
348
232
|
process.stdin.isTTY === true &&
|
package/bin/agents.js
CHANGED
|
@@ -283,7 +283,8 @@ export const RUNBOOK_ENV_DESCRIPTION = Object.freeze({
|
|
|
283
283
|
{
|
|
284
284
|
name: 'SUBCONSCIOUS_MODELS',
|
|
285
285
|
value: '{catalog}',
|
|
286
|
-
description:
|
|
286
|
+
description:
|
|
287
|
+
'Newline-separated model catalog. Set by the caller, it pins the catalog and skips live discovery; otherwise it is the live catalog.',
|
|
287
288
|
},
|
|
288
289
|
{
|
|
289
290
|
name: 'PATH',
|
|
@@ -362,6 +363,7 @@ async function resolvedModelsForLaunch(profile, apiKey, selectedModel) {
|
|
|
362
363
|
apiKey,
|
|
363
364
|
selectedModel,
|
|
364
365
|
fallbackModels: PACKAGED_MODELS,
|
|
366
|
+
pinnedModels: process.env.SUBCONSCIOUS_MODELS,
|
|
365
367
|
});
|
|
366
368
|
if (catalog.source === 'public' && apiKey) {
|
|
367
369
|
console.error(` ${c.dim}${PUBLIC_CATALOG_FALLBACK_MESSAGE}${c.reset}\n`);
|
|
@@ -390,7 +392,7 @@ export function selectLaunchModel(requestedModel, modelSource, catalog) {
|
|
|
390
392
|
async function runRunbookSetup(agent, argv, profile) {
|
|
391
393
|
const script = agent.runbook.setup_script;
|
|
392
394
|
if (isSetupWithoutAuth(argv) || agent.runbook.setup_needs_auth === false) {
|
|
393
|
-
// Status
|
|
395
|
+
// Status and uninstall read no model or
|
|
394
396
|
// agent setting, so an invalid saved one must not block them.
|
|
395
397
|
const lenient = { strict: false };
|
|
396
398
|
const base = { ...(profile?.values || {}), ...process.env };
|
|
@@ -521,9 +523,8 @@ async function launchAgent(agent, argv, profile, headless) {
|
|
|
521
523
|
const apiKey = await requireApiKey(profile, agent);
|
|
522
524
|
if (!apiKey) return 1;
|
|
523
525
|
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
await ensureMinimumVersion(agent, binDir, { mayPrompt });
|
|
526
|
+
const binDir = await ensureInstalled(agent);
|
|
527
|
+
await ensureMinimumVersion(agent, binDir, { mayPrompt: !headless });
|
|
527
528
|
const catalog = await resolvedModelsForLaunch(
|
|
528
529
|
profile,
|
|
529
530
|
apiKey,
|
package/bin/cli.js
CHANGED
|
@@ -159,7 +159,8 @@ Usage:
|
|
|
159
159
|
subc upgrade --latest
|
|
160
160
|
subc upgrade help
|
|
161
161
|
|
|
162
|
-
Upgrade subconscious-cli to the latest published version
|
|
162
|
+
Upgrade subconscious-cli to the latest published version, the same way it
|
|
163
|
+
was installed: npm (or pnpm, yarn, bun), or the curl installer.
|
|
163
164
|
|
|
164
165
|
subc upgrade Prompt "Do you want to upgrade?" then install @latest
|
|
165
166
|
subc upgrade --latest Skip the prompt and install @latest
|
package/bin/models.js
CHANGED
|
@@ -175,10 +175,22 @@ export async function resolveModelCatalog({
|
|
|
175
175
|
apiKey,
|
|
176
176
|
selectedModel,
|
|
177
177
|
fallbackModels = [],
|
|
178
|
+
pinnedModels,
|
|
178
179
|
fetchImpl = globalThis.fetch,
|
|
179
180
|
timeoutMs = DEFAULT_MODEL_FETCH_TIMEOUT_MS,
|
|
180
181
|
signal,
|
|
181
182
|
}) {
|
|
183
|
+
// A caller that names its catalog (a benchmark run, a CI job) needs the same
|
|
184
|
+
// models on every launch, whatever the gateway lists that day.
|
|
185
|
+
const pinned = pinnedModels?.split(/\r?\n/).filter((id) => id.trim());
|
|
186
|
+
if (pinned?.length) {
|
|
187
|
+
return {
|
|
188
|
+
models: normalizeModelIds(pinned, selectedModel),
|
|
189
|
+
source: 'pinned',
|
|
190
|
+
error: null,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
const key = apiKey?.trim() || '';
|
|
183
195
|
|
|
184
196
|
if (key) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/bin/runbook/README.md
CHANGED
|
@@ -8,7 +8,6 @@ the default gateway, model capabilities, and the settings every agent uses
|
|
|
8
8
|
|
|
9
9
|
| Agent | Launch | Setup |
|
|
10
10
|
| --- | --- | --- |
|
|
11
|
-
| `subconscious-code` (Marathon) | binary | `install.sh` (binary installer) |
|
|
12
11
|
| `claude-code` | binary | `install.sh` (leftover status/uninstall) |
|
|
13
12
|
| `codex` | `run.sh` | `install.sh` (hooks) |
|
|
14
13
|
| `opencode` | binary | `install.sh` (leftover status/uninstall) |
|
|
@@ -22,7 +21,7 @@ the default gateway, model capabilities, and the settings every agent uses
|
|
|
22
21
|
| Field | Read by |
|
|
23
22
|
| --- | --- |
|
|
24
23
|
| `id`, `command`, `aliases`, `name`, `description` | routing, `subc help`, the TUI menu |
|
|
25
|
-
| `install` | the
|
|
24
|
+
| `install` | the missing-agent message, agent help, and the upgrade warning (`minimum_version`); `{package}` fills in `commands` |
|
|
26
25
|
| `runbook` | which script launches the agent (none means the binary is run directly) and which setup actions exist |
|
|
27
26
|
| `help` | `subc <agent> help`; flags with a `help` text are added from `inputs` |
|
|
28
27
|
| `sessions`, `launch.resume`, `launch.handoff` | `subc sessions resume` |
|
|
@@ -32,6 +31,18 @@ the default gateway, model capabilities, and the settings every agent uses
|
|
|
32
31
|
| `config` | `cli-config` entries become `config_flag NAME=VALUE` (Codex `-c`) when `when` holds; other entries describe files the runbooks write |
|
|
33
32
|
| `capabilities`, `prerequisites` | the harness manifest only |
|
|
34
33
|
|
|
34
|
+
## Install data is display-only
|
|
35
|
+
|
|
36
|
+
subc never installs an agent. When an agent's binary is missing, subc prints
|
|
37
|
+
`install.commands` for the current OS and `install.url`, then exits with
|
|
38
|
+
status 127. Windows never shows the Linux command. Rules for `install`, checked
|
|
39
|
+
by `bin/agent-data.js` and `test/agent-install-policy.test.js`:
|
|
40
|
+
|
|
41
|
+
- `url` is required: an `https://` link to the vendor's official install page.
|
|
42
|
+
- `commands` is `null`, or an object keyed by `darwin`, `linux`, and `win32`.
|
|
43
|
+
- A command is one the user can paste, such as `npm i -g {package}`. It never
|
|
44
|
+
pipes a downloaded script into a shell.
|
|
45
|
+
|
|
35
46
|
Values may use `{baseUrl}`, `{baseUrlV1}`, `{apiKey}`, `{model}`, `{prompt}`,
|
|
36
47
|
`{args}`, `{config}`, and `${NAME}` for another input or env entry.
|
|
37
48
|
`subc harness-manifest --json` prints every agent file with `${NAME}` replaced
|
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
"homepage": "https://docs.anthropic.com/en/docs/claude-code",
|
|
9
9
|
"install": {
|
|
10
10
|
"commands": {
|
|
11
|
-
"darwin": "
|
|
12
|
-
"linux": "
|
|
13
|
-
"win32": "
|
|
14
|
-
"fallback": "npm i -g {package}"
|
|
11
|
+
"darwin": "npm i -g {package}",
|
|
12
|
+
"linux": "npm i -g {package}",
|
|
13
|
+
"win32": "npm i -g {package}"
|
|
15
14
|
},
|
|
16
|
-
"
|
|
15
|
+
"url": "https://code.claude.com/docs/en/setup",
|
|
16
|
+
"method": "npm",
|
|
17
17
|
"package": "@anthropic-ai/claude-code",
|
|
18
18
|
"version": null,
|
|
19
|
-
"channel": "
|
|
19
|
+
"channel": "latest (unpinned)",
|
|
20
20
|
"minimum_version": "2.1.242",
|
|
21
21
|
"verified": true,
|
|
22
22
|
"note": "subc warns, and does not block, below minimum_version."
|