subconscious-cli 6.0.0 → 6.1.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 +135 -2
- package/bin/agent-data.js +242 -0
- package/bin/agent-help.js +106 -0
- package/bin/agent-install.js +354 -0
- package/bin/agent-launch.js +199 -0
- package/bin/agent-spawn.js +137 -0
- package/bin/agents.js +284 -840
- package/bin/auth.js +89 -33
- package/bin/branding.js +3 -1
- package/bin/claude-picker.js +143 -0
- package/bin/cli.js +87 -20
- package/bin/clipboard-image.js +24 -5
- package/bin/colors.js +1 -1
- package/bin/feedback.js +71 -21
- package/bin/harness-manifest.js +286 -0
- package/bin/headless-args.js +35 -0
- package/bin/launch-values.js +25 -0
- package/bin/model-capabilities.js +12 -8
- package/bin/models.js +35 -19
- 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 +48 -5
- package/bin/profile-settings.js +129 -0
- package/bin/profiles.js +136 -449
- package/bin/runbook/README.md +66 -23
- package/bin/runbook/claude-code/agent.json +300 -0
- package/bin/runbook/claude-code/install.sh +9 -252
- package/bin/runbook/codex/agent.json +461 -0
- package/bin/runbook/codex/hooks-lib.sh +17 -4
- package/bin/runbook/codex/install.sh +0 -4
- package/bin/runbook/codex/run.sh +16 -235
- package/bin/runbook/copilot/agent.json +185 -0
- package/bin/runbook/copilot/install.sh +45 -63
- package/bin/runbook/cursor/agent.json +137 -0
- package/bin/runbook/cursor/install.sh +8 -35
- package/bin/runbook/deepseek-harness/agent.json +163 -0
- package/bin/runbook/deepseek-harness/run.sh +20 -59
- package/bin/runbook/image-window/index.ts +14 -0
- package/bin/runbook/image-window/window.d.ts +19 -0
- package/bin/runbook/image-window/window.js +140 -0
- package/bin/runbook/lib.sh +41 -0
- package/bin/runbook/opencode/agent.json +158 -0
- package/bin/runbook/opencode/install.sh +9 -183
- package/bin/runbook/opencode/opencode-plugin.d.ts +16 -0
- package/bin/runbook/opencode/subconscious-image-window.ts +24 -0
- package/bin/runbook/pi/agent.json +216 -0
- package/bin/runbook/pi/install.sh +18 -44
- package/bin/runbook/pi/run.sh +6 -18
- package/bin/runbook/pi/subconscious-compaction.ts +71 -49
- package/bin/runbook/shared.json +58 -0
- package/bin/runbook/subconscious-code/agent.json +156 -0
- package/bin/sessions.js +173 -58
- package/bin/tui.js +104 -32
- package/bin/update-check.js +54 -21
- package/bin/upgrade.js +67 -43
- package/bin/usage.js +47 -24
- package/bin/windows/agents.js +141 -37
- package/bin/windows/common.js +36 -25
- package/bin/windows/install.js +116 -34
- package/bin/windows/launch.js +260 -77
- package/bin/windows/pi-compaction.ts +68 -13
- package/bin/windows/process.js +210 -57
- package/bin/windows/setup.js +251 -56
- package/package.json +9 -4
- package/bin/registry.generated.json +0 -406
- package/bin/runbook/claude-code/run.sh +0 -254
- package/bin/runbook/model-capabilities.generated.sh +0 -9
- package/bin/runbook/opencode/provider.sh +0 -93
- package/bin/runbook/opencode/run.sh +0 -99
- package/bin/runbook/opencode/subconscious-compaction.ts +0 -100
- package/bin/runbook/subconscious-code/run.sh +0 -21
- package/bin/windows/claude.js +0 -71
package/bin/windows/process.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// Windows process handling. Unix callers keep using their existing launch paths.
|
|
2
|
+
|
|
3
|
+
import { execFileSync, spawn, spawnSync } from 'node:child_process';
|
|
2
4
|
import fs from 'node:fs';
|
|
3
5
|
import os from 'node:os';
|
|
4
6
|
import path from 'node:path';
|
|
5
|
-
import { spawn, spawnSync, execFileSync } from 'node:child_process';
|
|
6
7
|
|
|
7
8
|
export function windowsEnv(...sources) {
|
|
8
9
|
const result = {};
|
|
9
10
|
for (const source of sources) {
|
|
10
11
|
for (const [key, value] of Object.entries(source || {})) {
|
|
11
|
-
if (value !== undefined && value !== null)
|
|
12
|
+
if (value !== undefined && value !== null)
|
|
13
|
+
result[key.toUpperCase()] = String(value);
|
|
12
14
|
}
|
|
13
15
|
}
|
|
14
16
|
return result;
|
|
@@ -16,42 +18,88 @@ export function windowsEnv(...sources) {
|
|
|
16
18
|
|
|
17
19
|
export function windowsBinDirs(environment = process.env, home = os.homedir()) {
|
|
18
20
|
const env = windowsEnv(environment);
|
|
19
|
-
return [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
return [
|
|
22
|
+
...new Set(
|
|
23
|
+
[
|
|
24
|
+
env.APPDATA && path.win32.join(env.APPDATA, 'npm'),
|
|
25
|
+
env.USERPROFILE && path.win32.join(env.USERPROFILE, '.local', 'bin'),
|
|
26
|
+
home && path.win32.join(home, '.local', 'bin'),
|
|
27
|
+
env.LOCALAPPDATA &&
|
|
28
|
+
path.win32.join(env.LOCALAPPDATA, 'Microsoft', 'WinGet', 'Links'),
|
|
29
|
+
env.SYSTEMROOT &&
|
|
30
|
+
path.win32.join(
|
|
31
|
+
env.SYSTEMROOT,
|
|
32
|
+
'System32',
|
|
33
|
+
'config',
|
|
34
|
+
'systemprofile',
|
|
35
|
+
'.local',
|
|
36
|
+
'bin',
|
|
37
|
+
),
|
|
38
|
+
env.SYSTEMROOT && path.win32.join(env.SYSTEMROOT, 'System32'),
|
|
39
|
+
env.SYSTEMROOT &&
|
|
40
|
+
path.win32.join(
|
|
41
|
+
env.SYSTEMROOT,
|
|
42
|
+
'System32',
|
|
43
|
+
'WindowsPowerShell',
|
|
44
|
+
'v1.0',
|
|
45
|
+
),
|
|
46
|
+
path.dirname(process.execPath),
|
|
47
|
+
].filter(Boolean),
|
|
48
|
+
),
|
|
49
|
+
];
|
|
29
50
|
}
|
|
30
51
|
|
|
31
52
|
export function windowsPath(extraDirs, currentPath = '') {
|
|
32
53
|
const seen = new Set();
|
|
33
|
-
return [...extraDirs, ...currentPath.split(';')]
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
return [...extraDirs, ...currentPath.split(';')]
|
|
55
|
+
.filter((dir) => {
|
|
56
|
+
const key = dir
|
|
57
|
+
.replace(/^"|"$/g, '')
|
|
58
|
+
.replace(/[\\/]+$/, '')
|
|
59
|
+
.toLowerCase();
|
|
60
|
+
if (!key || seen.has(key)) return false;
|
|
61
|
+
seen.add(key);
|
|
62
|
+
return true;
|
|
63
|
+
})
|
|
64
|
+
.map((dir) => dir.replace(/^"|"$/g, ''))
|
|
65
|
+
.join(';');
|
|
39
66
|
}
|
|
40
67
|
|
|
41
68
|
export function resolveWindowsExecutable(command, options = {}) {
|
|
42
69
|
const env = windowsEnv(options.env || process.env);
|
|
43
70
|
const pathApi = options.pathApi || path.win32;
|
|
44
|
-
const isFile =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
const isFile =
|
|
72
|
+
options.isFile ||
|
|
73
|
+
((file) => {
|
|
74
|
+
try {
|
|
75
|
+
return fs.statSync(file).isFile();
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const extensions = [
|
|
81
|
+
...new Set([
|
|
82
|
+
'.exe',
|
|
83
|
+
'.com',
|
|
84
|
+
...(env.PATHEXT || '.CMD;.BAT').split(';').map((x) => x.toLowerCase()),
|
|
85
|
+
'',
|
|
86
|
+
]),
|
|
51
87
|
];
|
|
88
|
+
const hasExtension = extensions.some(
|
|
89
|
+
(ext) => ext && command.toLowerCase().endsWith(ext),
|
|
90
|
+
);
|
|
91
|
+
const dirs = /[\\/]/.test(command)
|
|
92
|
+
? ['']
|
|
93
|
+
: [
|
|
94
|
+
...(options.preferredDirs || []),
|
|
95
|
+
...(env.PATH || '').split(';').filter(Boolean),
|
|
96
|
+
...windowsBinDirs(env, options.home),
|
|
97
|
+
];
|
|
52
98
|
for (const dir of dirs) {
|
|
53
99
|
for (const ext of hasExtension ? [''] : extensions) {
|
|
54
|
-
const candidate = dir
|
|
100
|
+
const candidate = dir
|
|
101
|
+
? pathApi.join(dir.replace(/^"|"$/g, ''), command + ext)
|
|
102
|
+
: command + ext;
|
|
55
103
|
if (isFile(candidate)) return candidate;
|
|
56
104
|
}
|
|
57
105
|
}
|
|
@@ -61,53 +109,124 @@ export function resolveWindowsExecutable(command, options = {}) {
|
|
|
61
109
|
// npm shims pass %* through cmd.exe, which reinterprets quotes, JSON, %, &,
|
|
62
110
|
// newlines, etc. Resolve their quoted entry point and run it with Node directly.
|
|
63
111
|
// This also supports npm/npx and VS Code's code.cmd without invoking a shell.
|
|
64
|
-
export function windowsInvocation(
|
|
112
|
+
export function windowsInvocation(
|
|
113
|
+
command,
|
|
114
|
+
args = [],
|
|
115
|
+
environment = process.env,
|
|
116
|
+
options = {},
|
|
117
|
+
) {
|
|
65
118
|
const env = windowsEnv(environment);
|
|
66
119
|
const resolve = options.resolve || resolveWindowsExecutable;
|
|
67
120
|
const executable = resolve(command, { env, ...options });
|
|
68
|
-
if (!executable)
|
|
69
|
-
|
|
70
|
-
|
|
121
|
+
if (!executable)
|
|
122
|
+
throw new Error(`Could not find ${command} on the Windows PATH.`);
|
|
123
|
+
if (!/\.(cmd|bat)$/i.test(executable))
|
|
124
|
+
return { command: executable, args, env };
|
|
125
|
+
const readFile =
|
|
126
|
+
options.readFile || ((file) => fs.readFileSync(file, 'utf8'));
|
|
71
127
|
const pathApi = options.pathApi || path.win32;
|
|
72
128
|
const contents = readFile(executable);
|
|
73
|
-
const candidates = [
|
|
74
|
-
|
|
129
|
+
const candidates = [
|
|
130
|
+
...contents.matchAll(/%(?:dp0%|~dp0)[\\/]?([^"\r\n]+)"/gi),
|
|
131
|
+
];
|
|
132
|
+
const name = pathApi
|
|
133
|
+
.basename(executable)
|
|
134
|
+
.replace(/\.(cmd|bat)$/i, '')
|
|
135
|
+
.toLowerCase();
|
|
75
136
|
let electron;
|
|
76
137
|
if (/ELECTRON_RUN_AS_NODE\s*=\s*1/i.test(contents)) {
|
|
77
|
-
electron = candidates
|
|
138
|
+
electron = candidates
|
|
139
|
+
.map((match) => pathApi.resolve(pathApi.dirname(executable), match[1]))
|
|
140
|
+
.find(
|
|
141
|
+
(target) =>
|
|
142
|
+
/\.exe$/i.test(target) &&
|
|
143
|
+
!/node\.exe$/i.test(target) &&
|
|
144
|
+
fs.existsSync(target),
|
|
145
|
+
);
|
|
78
146
|
}
|
|
79
147
|
for (const match of candidates) {
|
|
80
148
|
const target = pathApi.resolve(pathApi.dirname(executable), match[1]);
|
|
81
149
|
// A shim's first executable is often node.exe, not the agent entry point.
|
|
82
150
|
if (/\.(exe|com)$/i.test(target)) {
|
|
83
|
-
if (!electron && !/node\.exe$/i.test(target) && fs.existsSync(target))
|
|
151
|
+
if (!electron && !/node\.exe$/i.test(target) && fs.existsSync(target))
|
|
152
|
+
return { command: target, args, env };
|
|
84
153
|
continue;
|
|
85
154
|
}
|
|
86
|
-
if (
|
|
155
|
+
if (
|
|
156
|
+
['npm', 'npx'].includes(name) &&
|
|
157
|
+
pathApi.basename(target) !== `${name}-cli.js`
|
|
158
|
+
)
|
|
159
|
+
continue;
|
|
87
160
|
let source;
|
|
88
|
-
try {
|
|
89
|
-
|
|
90
|
-
|
|
161
|
+
try {
|
|
162
|
+
source = readFile(target);
|
|
163
|
+
} catch {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!/\.[cm]?js$/i.test(target) && !/^#![^\r\n]*\bnode\b/.test(source))
|
|
167
|
+
continue;
|
|
168
|
+
return {
|
|
169
|
+
command: electron || process.execPath,
|
|
170
|
+
args: [target, ...args],
|
|
171
|
+
env: electron ? { ...env, ELECTRON_RUN_AS_NODE: '1' } : env,
|
|
172
|
+
};
|
|
91
173
|
}
|
|
92
|
-
throw new Error(
|
|
174
|
+
throw new Error(
|
|
175
|
+
`Cannot safely launch the custom batch wrapper ${executable}. Install the native executable or standard npm package for ${command}.`,
|
|
176
|
+
);
|
|
93
177
|
}
|
|
94
178
|
|
|
95
179
|
export function spawnWindows(command, args, options = {}) {
|
|
96
|
-
const invocation = windowsInvocation(
|
|
97
|
-
|
|
180
|
+
const invocation = windowsInvocation(
|
|
181
|
+
command,
|
|
182
|
+
args,
|
|
183
|
+
options.env || process.env,
|
|
184
|
+
);
|
|
185
|
+
return spawn(invocation.command, invocation.args, {
|
|
186
|
+
...options,
|
|
187
|
+
env: invocation.env,
|
|
188
|
+
shell: false,
|
|
189
|
+
});
|
|
98
190
|
}
|
|
99
191
|
|
|
100
192
|
export function spawnWindowsSync(command, args, options = {}) {
|
|
101
193
|
try {
|
|
102
|
-
const invocation = windowsInvocation(
|
|
103
|
-
|
|
104
|
-
|
|
194
|
+
const invocation = windowsInvocation(
|
|
195
|
+
command,
|
|
196
|
+
args,
|
|
197
|
+
options.env || process.env,
|
|
198
|
+
);
|
|
199
|
+
return spawnSync(invocation.command, invocation.args, {
|
|
200
|
+
...options,
|
|
201
|
+
env: invocation.env,
|
|
202
|
+
shell: false,
|
|
203
|
+
});
|
|
204
|
+
} catch (error) {
|
|
205
|
+
return { error, status: null, stdout: '', stderr: '' };
|
|
206
|
+
}
|
|
105
207
|
}
|
|
106
208
|
|
|
107
209
|
export function runWindows(command, args, options = {}) {
|
|
210
|
+
const { input, ...spawnOptions } = options;
|
|
108
211
|
return new Promise((resolve, reject) => {
|
|
109
|
-
const child = spawnWindows(command, args, {
|
|
110
|
-
|
|
212
|
+
const child = spawnWindows(command, args, {
|
|
213
|
+
stdio: input === undefined ? 'inherit' : ['pipe', 'inherit', 'inherit'],
|
|
214
|
+
...spawnOptions,
|
|
215
|
+
});
|
|
216
|
+
if (input !== undefined) {
|
|
217
|
+
// EPIPE means the agent exited without reading; its exit code reports why.
|
|
218
|
+
child.stdin.on('error', (error) => {
|
|
219
|
+
if (error.code !== 'EPIPE') reject(error);
|
|
220
|
+
});
|
|
221
|
+
child.stdin.end(input);
|
|
222
|
+
}
|
|
223
|
+
const forward = (signal) => {
|
|
224
|
+
try {
|
|
225
|
+
child.kill(signal);
|
|
226
|
+
} catch {
|
|
227
|
+
/* already exited */
|
|
228
|
+
}
|
|
229
|
+
};
|
|
111
230
|
const interrupt = () => forward('SIGINT');
|
|
112
231
|
const terminate = () => forward('SIGTERM');
|
|
113
232
|
process.on('SIGINT', interrupt);
|
|
@@ -116,7 +235,10 @@ export function runWindows(command, args, options = {}) {
|
|
|
116
235
|
process.off('SIGINT', interrupt);
|
|
117
236
|
process.off('SIGTERM', terminate);
|
|
118
237
|
};
|
|
119
|
-
child.once('error', error => {
|
|
238
|
+
child.once('error', (error) => {
|
|
239
|
+
cleanup();
|
|
240
|
+
reject(error);
|
|
241
|
+
});
|
|
120
242
|
child.once('close', (code, signal) => {
|
|
121
243
|
cleanup();
|
|
122
244
|
resolve(code ?? (signal === 'SIGINT' ? 130 : 143));
|
|
@@ -128,30 +250,61 @@ export function readWindowsVersion(command, env) {
|
|
|
128
250
|
try {
|
|
129
251
|
const invocation = windowsInvocation(command, ['--version'], env);
|
|
130
252
|
return execFileSync(invocation.command, invocation.args, {
|
|
131
|
-
env: invocation.env,
|
|
253
|
+
env: invocation.env,
|
|
254
|
+
encoding: 'utf8',
|
|
255
|
+
timeout: 3000,
|
|
256
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
132
257
|
});
|
|
133
|
-
} catch {
|
|
258
|
+
} catch {
|
|
259
|
+
return '';
|
|
260
|
+
}
|
|
134
261
|
}
|
|
135
262
|
|
|
136
263
|
export function powershellCommand(source, env = process.env) {
|
|
137
264
|
// Only fixed application source is encoded here. Dynamic data uses env vars.
|
|
138
|
-
return {
|
|
265
|
+
return {
|
|
266
|
+
command: 'powershell.exe',
|
|
267
|
+
args: [
|
|
268
|
+
'-NoLogo',
|
|
269
|
+
'-NoProfile',
|
|
270
|
+
'-NonInteractive',
|
|
271
|
+
'-EncodedCommand',
|
|
272
|
+
Buffer.from(source, 'utf16le').toString('base64'),
|
|
273
|
+
],
|
|
274
|
+
env: windowsEnv(env),
|
|
275
|
+
};
|
|
139
276
|
}
|
|
140
277
|
|
|
141
278
|
export function openWindowsBrowser(url) {
|
|
142
|
-
const invocation = powershellCommand(
|
|
143
|
-
|
|
279
|
+
const invocation = powershellCommand(
|
|
280
|
+
'Start-Process -FilePath $env:SUBC_BROWSER_URL',
|
|
281
|
+
{ ...process.env, SUBC_BROWSER_URL: url },
|
|
282
|
+
);
|
|
283
|
+
return runWindows(invocation.command, invocation.args, {
|
|
284
|
+
env: invocation.env,
|
|
285
|
+
stdio: 'ignore',
|
|
286
|
+
});
|
|
144
287
|
}
|
|
145
288
|
|
|
146
289
|
export function windowsEditor(requested, environment = process.env) {
|
|
147
290
|
const env = windowsEnv(environment);
|
|
148
|
-
const text =
|
|
291
|
+
const text =
|
|
292
|
+
requested || env.VISUAL?.trim() || env.EDITOR?.trim() || 'notepad.exe';
|
|
149
293
|
// An existing full path may contain spaces without quotes. Otherwise accept
|
|
150
294
|
// double-quoted argv, preserving Windows backslashes and rejecting shell use.
|
|
151
|
-
const parts = fs.existsSync(text)
|
|
152
|
-
|
|
153
|
-
|
|
295
|
+
const parts = fs.existsSync(text)
|
|
296
|
+
? [text]
|
|
297
|
+
: text.match(/"[^"]*"|[^\s"]+/g) || [];
|
|
298
|
+
if ((text.match(/"/g) || []).length % 2)
|
|
299
|
+
throw new Error('Unmatched quote in VISUAL / EDITOR');
|
|
300
|
+
const [command, ...args] = parts.map((part) => part.replace(/^"|"$/g, ''));
|
|
154
301
|
if (!command) throw new Error('Editor is empty');
|
|
155
|
-
if (
|
|
302
|
+
if (
|
|
303
|
+
/^(code|code-insiders)(\.cmd|\.exe)?$/i.test(
|
|
304
|
+
path.win32.basename(command),
|
|
305
|
+
) &&
|
|
306
|
+
!args.includes('--wait')
|
|
307
|
+
)
|
|
308
|
+
args.unshift('--wait');
|
|
156
309
|
return { command, args };
|
|
157
310
|
}
|