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.
Files changed (76) hide show
  1. package/README.md +135 -2
  2. package/bin/agent-data.js +242 -0
  3. package/bin/agent-help.js +106 -0
  4. package/bin/agent-install.js +354 -0
  5. package/bin/agent-launch.js +199 -0
  6. package/bin/agent-spawn.js +137 -0
  7. package/bin/agents.js +284 -840
  8. package/bin/auth.js +89 -33
  9. package/bin/branding.js +3 -1
  10. package/bin/claude-picker.js +143 -0
  11. package/bin/cli.js +87 -20
  12. package/bin/clipboard-image.js +24 -5
  13. package/bin/colors.js +1 -1
  14. package/bin/feedback.js +71 -21
  15. package/bin/harness-manifest.js +286 -0
  16. package/bin/headless-args.js +35 -0
  17. package/bin/launch-values.js +25 -0
  18. package/bin/model-capabilities.js +12 -8
  19. package/bin/models.js +35 -19
  20. package/bin/native/subc-tui-darwin-amd64 +0 -0
  21. package/bin/native/subc-tui-darwin-arm64 +0 -0
  22. package/bin/native/subc-tui-linux-amd64 +0 -0
  23. package/bin/native/subc-tui-linux-arm64 +0 -0
  24. package/bin/native/subc-tui-windows-amd64.exe +0 -0
  25. package/bin/native/subc-tui-windows-arm64.exe +0 -0
  26. package/bin/opencode-provider.js +48 -5
  27. package/bin/profile-settings.js +129 -0
  28. package/bin/profiles.js +136 -449
  29. package/bin/runbook/README.md +66 -23
  30. package/bin/runbook/claude-code/agent.json +300 -0
  31. package/bin/runbook/claude-code/install.sh +9 -252
  32. package/bin/runbook/codex/agent.json +461 -0
  33. package/bin/runbook/codex/hooks-lib.sh +17 -4
  34. package/bin/runbook/codex/install.sh +0 -4
  35. package/bin/runbook/codex/run.sh +16 -235
  36. package/bin/runbook/copilot/agent.json +185 -0
  37. package/bin/runbook/copilot/install.sh +45 -63
  38. package/bin/runbook/cursor/agent.json +137 -0
  39. package/bin/runbook/cursor/install.sh +8 -35
  40. package/bin/runbook/deepseek-harness/agent.json +163 -0
  41. package/bin/runbook/deepseek-harness/run.sh +20 -59
  42. package/bin/runbook/image-window/index.ts +14 -0
  43. package/bin/runbook/image-window/window.d.ts +19 -0
  44. package/bin/runbook/image-window/window.js +140 -0
  45. package/bin/runbook/lib.sh +41 -0
  46. package/bin/runbook/opencode/agent.json +158 -0
  47. package/bin/runbook/opencode/install.sh +9 -183
  48. package/bin/runbook/opencode/opencode-plugin.d.ts +16 -0
  49. package/bin/runbook/opencode/subconscious-image-window.ts +24 -0
  50. package/bin/runbook/pi/agent.json +216 -0
  51. package/bin/runbook/pi/install.sh +18 -44
  52. package/bin/runbook/pi/run.sh +6 -18
  53. package/bin/runbook/pi/subconscious-compaction.ts +71 -49
  54. package/bin/runbook/shared.json +58 -0
  55. package/bin/runbook/subconscious-code/agent.json +156 -0
  56. package/bin/sessions.js +173 -58
  57. package/bin/tui.js +104 -32
  58. package/bin/update-check.js +54 -21
  59. package/bin/upgrade.js +67 -43
  60. package/bin/usage.js +47 -24
  61. package/bin/windows/agents.js +141 -37
  62. package/bin/windows/common.js +36 -25
  63. package/bin/windows/install.js +116 -34
  64. package/bin/windows/launch.js +260 -77
  65. package/bin/windows/pi-compaction.ts +68 -13
  66. package/bin/windows/process.js +210 -57
  67. package/bin/windows/setup.js +251 -56
  68. package/package.json +9 -4
  69. package/bin/registry.generated.json +0 -406
  70. package/bin/runbook/claude-code/run.sh +0 -254
  71. package/bin/runbook/model-capabilities.generated.sh +0 -9
  72. package/bin/runbook/opencode/provider.sh +0 -93
  73. package/bin/runbook/opencode/run.sh +0 -99
  74. package/bin/runbook/opencode/subconscious-compaction.ts +0 -100
  75. package/bin/runbook/subconscious-code/run.sh +0 -21
  76. package/bin/windows/claude.js +0 -71
@@ -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) result[key.toUpperCase()] = String(value);
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 [...new Set([
20
- env.APPDATA && path.win32.join(env.APPDATA, 'npm'),
21
- env.USERPROFILE && path.win32.join(env.USERPROFILE, '.local', 'bin'),
22
- home && path.win32.join(home, '.local', 'bin'),
23
- env.LOCALAPPDATA && path.win32.join(env.LOCALAPPDATA, 'Microsoft', 'WinGet', 'Links'),
24
- env.SYSTEMROOT && path.win32.join(env.SYSTEMROOT, 'System32', 'config', 'systemprofile', '.local', 'bin'),
25
- env.SYSTEMROOT && path.win32.join(env.SYSTEMROOT, 'System32'),
26
- env.SYSTEMROOT && path.win32.join(env.SYSTEMROOT, 'System32', 'WindowsPowerShell', 'v1.0'),
27
- path.dirname(process.execPath),
28
- ].filter(Boolean))];
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(';')].filter(dir => {
34
- const key = dir.replace(/^"|"$/g, '').replace(/[\\/]+$/, '').toLowerCase();
35
- if (!key || seen.has(key)) return false;
36
- seen.add(key);
37
- return true;
38
- }).map(dir => dir.replace(/^"|"$/g, '')).join(';');
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 = options.isFile || (file => { try { return fs.statSync(file).isFile(); } catch { return false; } });
45
- const extensions = [...new Set(['.exe', '.com', ...(env.PATHEXT || '.CMD;.BAT').split(';').map(x => x.toLowerCase()), ''])];
46
- const hasExtension = extensions.some(ext => ext && command.toLowerCase().endsWith(ext));
47
- const dirs = /[\\/]/.test(command) ? [''] : [
48
- ...(options.preferredDirs || []),
49
- ...(env.PATH || '').split(';').filter(Boolean),
50
- ...windowsBinDirs(env, options.home),
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 ? pathApi.join(dir.replace(/^"|"$/g, ''), command + ext) : command + ext;
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(command, args = [], environment = process.env, options = {}) {
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) throw new Error(`Could not find ${command} on the Windows PATH.`);
69
- if (!/\.(cmd|bat)$/i.test(executable)) return { command: executable, args, env };
70
- const readFile = options.readFile || (file => fs.readFileSync(file, 'utf8'));
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 = [...contents.matchAll(/%(?:dp0%|~dp0)[\\/]?([^"\r\n]+)"/gi)];
74
- const name = pathApi.basename(executable).replace(/\.(cmd|bat)$/i, '').toLowerCase();
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.map(match => pathApi.resolve(pathApi.dirname(executable), match[1])).find(target => /\.exe$/i.test(target) && !/node\.exe$/i.test(target) && fs.existsSync(target));
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)) return { command: target, args, env };
151
+ if (!electron && !/node\.exe$/i.test(target) && fs.existsSync(target))
152
+ return { command: target, args, env };
84
153
  continue;
85
154
  }
86
- if (['npm', 'npx'].includes(name) && pathApi.basename(target) !== `${name}-cli.js`) continue;
155
+ if (
156
+ ['npm', 'npx'].includes(name) &&
157
+ pathApi.basename(target) !== `${name}-cli.js`
158
+ )
159
+ continue;
87
160
  let source;
88
- try { source = readFile(target); } catch { continue; }
89
- if (!/\.[cm]?js$/i.test(target) && !/^#![^\r\n]*\bnode\b/.test(source)) continue;
90
- return { command: electron || process.execPath, args: [target, ...args], env: electron ? { ...env, ELECTRON_RUN_AS_NODE: '1' } : env };
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(`Cannot safely launch the custom batch wrapper ${executable}. Install the native executable or standard npm package for ${command}.`);
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(command, args, options.env || process.env);
97
- return spawn(invocation.command, invocation.args, { ...options, env: invocation.env, shell: false });
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(command, args, options.env || process.env);
103
- return spawnSync(invocation.command, invocation.args, { ...options, env: invocation.env, shell: false });
104
- } catch (error) { return { error, status: null, stdout: '', stderr: '' }; }
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, { stdio: 'inherit', ...options });
110
- const forward = signal => { try { child.kill(signal); } catch { /* already exited */ } };
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 => { cleanup(); reject(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, encoding: 'utf8', timeout: 3000, stdio: ['ignore', 'pipe', 'ignore'],
253
+ env: invocation.env,
254
+ encoding: 'utf8',
255
+ timeout: 3000,
256
+ stdio: ['ignore', 'pipe', 'ignore'],
132
257
  });
133
- } catch { return ''; }
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 { command: 'powershell.exe', args: ['-NoLogo', '-NoProfile', '-NonInteractive', '-EncodedCommand', Buffer.from(source, 'utf16le').toString('base64')], env: windowsEnv(env) };
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('Start-Process -FilePath $env:SUBC_BROWSER_URL', { ...process.env, SUBC_BROWSER_URL: url });
143
- return runWindows(invocation.command, invocation.args, { env: invocation.env, stdio: 'ignore' });
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 = requested || env.VISUAL?.trim() || env.EDITOR?.trim() || 'notepad.exe';
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) ? [text] : text.match(/"[^"]*"|[^\s"]+/g) || [];
152
- if ((text.match(/"/g) || []).length % 2) throw new Error('Unmatched quote in VISUAL / EDITOR');
153
- const [command, ...args] = parts.map(part => part.replace(/^"|"$/g, ''));
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 (/^(code|code-insiders)(\.cmd|\.exe)?$/i.test(path.win32.basename(command)) && !args.includes('--wait')) args.unshift('--wait');
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
  }