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/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,113 @@ 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 = {}) {
|
|
108
210
|
return new Promise((resolve, reject) => {
|
|
109
211
|
const child = spawnWindows(command, args, { stdio: 'inherit', ...options });
|
|
110
|
-
const forward =
|
|
212
|
+
const forward = (signal) => {
|
|
213
|
+
try {
|
|
214
|
+
child.kill(signal);
|
|
215
|
+
} catch {
|
|
216
|
+
/* already exited */
|
|
217
|
+
}
|
|
218
|
+
};
|
|
111
219
|
const interrupt = () => forward('SIGINT');
|
|
112
220
|
const terminate = () => forward('SIGTERM');
|
|
113
221
|
process.on('SIGINT', interrupt);
|
|
@@ -116,7 +224,10 @@ export function runWindows(command, args, options = {}) {
|
|
|
116
224
|
process.off('SIGINT', interrupt);
|
|
117
225
|
process.off('SIGTERM', terminate);
|
|
118
226
|
};
|
|
119
|
-
child.once('error', error => {
|
|
227
|
+
child.once('error', (error) => {
|
|
228
|
+
cleanup();
|
|
229
|
+
reject(error);
|
|
230
|
+
});
|
|
120
231
|
child.once('close', (code, signal) => {
|
|
121
232
|
cleanup();
|
|
122
233
|
resolve(code ?? (signal === 'SIGINT' ? 130 : 143));
|
|
@@ -128,30 +239,61 @@ export function readWindowsVersion(command, env) {
|
|
|
128
239
|
try {
|
|
129
240
|
const invocation = windowsInvocation(command, ['--version'], env);
|
|
130
241
|
return execFileSync(invocation.command, invocation.args, {
|
|
131
|
-
env: invocation.env,
|
|
242
|
+
env: invocation.env,
|
|
243
|
+
encoding: 'utf8',
|
|
244
|
+
timeout: 3000,
|
|
245
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
132
246
|
});
|
|
133
|
-
} catch {
|
|
247
|
+
} catch {
|
|
248
|
+
return '';
|
|
249
|
+
}
|
|
134
250
|
}
|
|
135
251
|
|
|
136
252
|
export function powershellCommand(source, env = process.env) {
|
|
137
253
|
// Only fixed application source is encoded here. Dynamic data uses env vars.
|
|
138
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
command: 'powershell.exe',
|
|
256
|
+
args: [
|
|
257
|
+
'-NoLogo',
|
|
258
|
+
'-NoProfile',
|
|
259
|
+
'-NonInteractive',
|
|
260
|
+
'-EncodedCommand',
|
|
261
|
+
Buffer.from(source, 'utf16le').toString('base64'),
|
|
262
|
+
],
|
|
263
|
+
env: windowsEnv(env),
|
|
264
|
+
};
|
|
139
265
|
}
|
|
140
266
|
|
|
141
267
|
export function openWindowsBrowser(url) {
|
|
142
|
-
const invocation = powershellCommand(
|
|
143
|
-
|
|
268
|
+
const invocation = powershellCommand(
|
|
269
|
+
'Start-Process -FilePath $env:SUBC_BROWSER_URL',
|
|
270
|
+
{ ...process.env, SUBC_BROWSER_URL: url },
|
|
271
|
+
);
|
|
272
|
+
return runWindows(invocation.command, invocation.args, {
|
|
273
|
+
env: invocation.env,
|
|
274
|
+
stdio: 'ignore',
|
|
275
|
+
});
|
|
144
276
|
}
|
|
145
277
|
|
|
146
278
|
export function windowsEditor(requested, environment = process.env) {
|
|
147
279
|
const env = windowsEnv(environment);
|
|
148
|
-
const text =
|
|
280
|
+
const text =
|
|
281
|
+
requested || env.VISUAL?.trim() || env.EDITOR?.trim() || 'notepad.exe';
|
|
149
282
|
// An existing full path may contain spaces without quotes. Otherwise accept
|
|
150
283
|
// double-quoted argv, preserving Windows backslashes and rejecting shell use.
|
|
151
|
-
const parts = fs.existsSync(text)
|
|
152
|
-
|
|
153
|
-
|
|
284
|
+
const parts = fs.existsSync(text)
|
|
285
|
+
? [text]
|
|
286
|
+
: text.match(/"[^"]*"|[^\s"]+/g) || [];
|
|
287
|
+
if ((text.match(/"/g) || []).length % 2)
|
|
288
|
+
throw new Error('Unmatched quote in VISUAL / EDITOR');
|
|
289
|
+
const [command, ...args] = parts.map((part) => part.replace(/^"|"$/g, ''));
|
|
154
290
|
if (!command) throw new Error('Editor is empty');
|
|
155
|
-
if (
|
|
291
|
+
if (
|
|
292
|
+
/^(code|code-insiders)(\.cmd|\.exe)?$/i.test(
|
|
293
|
+
path.win32.basename(command),
|
|
294
|
+
) &&
|
|
295
|
+
!args.includes('--wait')
|
|
296
|
+
)
|
|
297
|
+
args.unshift('--wait');
|
|
156
298
|
return { command, args };
|
|
157
299
|
}
|