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,22 +1,53 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
+ import { agentById } from '../agent-data.js';
5
+ import { resolveInputs } from '../agent-launch.js';
4
6
  import { modelSupportsVision } from '../model-capabilities.js';
5
- import { defaults, origin, value, modelIds, positiveInteger, parseOptions, object, readJson, writeJson } from './common.js';
7
+ import {
8
+ defaults,
9
+ modelIds,
10
+ object,
11
+ origin,
12
+ parseOptions,
13
+ positiveInteger,
14
+ readJson,
15
+ value,
16
+ writeJson,
17
+ } from './common.js';
6
18
 
7
- const owned = command => {
19
+ const owned = (command) => {
8
20
  if (typeof command !== 'string') return false;
9
21
  if (/subconscious-hook\.(?:sh|cjs)/.test(command)) return true;
10
22
  const encoded = /-EncodedCommand ([A-Za-z0-9+/=]+)$/i.exec(command)?.[1];
11
- return Boolean(encoded && Buffer.from(encoded, 'base64').toString('utf16le').startsWith('# SUBC_WINDOWS_HOOK\n'));
23
+ return Boolean(
24
+ encoded &&
25
+ Buffer.from(encoded, 'base64')
26
+ .toString('utf16le')
27
+ .startsWith('# SUBC_WINDOWS_HOOK\n'),
28
+ );
12
29
  };
13
30
  const providerName = 'Subconscious Gateway';
14
- const exists = async file => { try { await fs.access(file); return true; } catch { return false; } };
31
+ const exists = async (file) => {
32
+ try {
33
+ await fs.access(file);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ };
15
39
 
16
40
  // Hook hosts expect command strings. Keep paths and credentials out of shell
17
41
  // syntax: PowerShell decodes data, then invokes Node with an argv array.
18
- export function windowsHookCommand(client, script, config, node = process.execPath) {
19
- const data = Buffer.from(JSON.stringify({ client, script, config, node })).toString('base64');
42
+ export function windowsHookCommand(
43
+ client,
44
+ script,
45
+ config,
46
+ node = process.execPath,
47
+ ) {
48
+ const data = Buffer.from(
49
+ JSON.stringify({ client, script, config, node }),
50
+ ).toString('base64');
20
51
  const source = `# SUBC_WINDOWS_HOOK\n$d = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${data}')) | ConvertFrom-Json\n& $d.node $d.script $d.client $d.config\nexit 0`;
21
52
  return `powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ${Buffer.from(source, 'utf16le').toString('base64')}`;
22
53
  }
@@ -25,26 +56,45 @@ export function mergeHooks(document, client, command) {
25
56
  const result = structuredClone(object(document, 'hooks configuration'));
26
57
  const hooks = object(result.hooks ?? {}, 'hooks');
27
58
  for (const [event, entries] of Object.entries(hooks)) {
28
- if (!Array.isArray(entries)) throw new Error(`hooks.${event} must be an array; leaving it unchanged`);
59
+ if (!Array.isArray(entries))
60
+ throw new Error(`hooks.${event} must be an array; leaving it unchanged`);
29
61
  const kept = [];
30
62
  for (const entry of entries) {
31
63
  object(entry, `hooks.${event} entry`);
32
64
  if (Array.isArray(entry.hooks)) {
33
65
  for (const hook of entry.hooks) object(hook, `hooks.${event} command`);
34
- const filtered = entry.hooks.filter(hook => !owned(hook.command));
35
- if (filtered.length || entry.hooks.length === 0) kept.push({ ...entry, hooks: filtered });
66
+ const filtered = entry.hooks.filter((hook) => !owned(hook.command));
67
+ if (filtered.length || entry.hooks.length === 0)
68
+ kept.push({ ...entry, hooks: filtered });
36
69
  } else if (entry.hooks !== undefined) {
37
- throw new Error(`hooks.${event}.hooks must be an array; leaving it unchanged`);
70
+ throw new Error(
71
+ `hooks.${event}.hooks must be an array; leaving it unchanged`,
72
+ );
38
73
  } else if (!owned(entry.command)) kept.push(entry);
39
74
  }
40
75
  if (kept.length) hooks[event] = kept;
41
76
  else delete hooks[event];
42
77
  }
43
78
  if (command) {
44
- const events = { codex: ['PreCompact', 'PostCompact'], cursor: ['beforeSubmitPrompt', 'preCompact'], copilot: ['UserPromptSubmit', 'PreCompact'] }[client];
79
+ const events = {
80
+ codex: ['PreCompact', 'PostCompact'],
81
+ cursor: ['beforeSubmitPrompt', 'preCompact'],
82
+ copilot: ['UserPromptSubmit', 'PreCompact'],
83
+ }[client];
45
84
  for (const event of events) {
46
- const hook = { type: 'command', command, timeout: client === 'copilot' ? 5 : 3 };
47
- const entry = client === 'codex' ? { hooks: [{ ...hook, statusMessage: 'Reporting Subconscious compaction' }] } : hook;
85
+ const hook = {
86
+ type: 'command',
87
+ command,
88
+ timeout: client === 'copilot' ? 5 : 3,
89
+ };
90
+ const entry =
91
+ client === 'codex'
92
+ ? {
93
+ hooks: [
94
+ { ...hook, statusMessage: 'Reporting Subconscious compaction' },
95
+ ],
96
+ }
97
+ : hook;
48
98
  hooks[event] = [...(hooks[event] || []), entry];
49
99
  }
50
100
  }
@@ -53,55 +103,130 @@ export function mergeHooks(document, client, command) {
53
103
  return result;
54
104
  }
55
105
 
56
- export async function windowsSetup(id, action, argv, environment, { home = os.homedir(), log = console.log } = {}) {
106
+ export async function windowsSetup(
107
+ id,
108
+ action,
109
+ argv,
110
+ environment,
111
+ { home = os.homedir(), log = console.log } = {},
112
+ ) {
57
113
  const schema = { '--gateway-url': 1, '--api-key': 1, '--model': 1 };
58
- if (id === 'copilot') Object.assign(schema, { '--vscode-app': 1, '--max-input-tokens': 1, '--max-output-tokens': 1 });
59
- if (id === 'pi') Object.assign(schema, { '--context-window': 1, '--max-tokens': 1 });
114
+ if (id === 'copilot')
115
+ Object.assign(schema, {
116
+ '--vscode-app': 1,
117
+ '--max-input-tokens': 1,
118
+ '--max-output-tokens': 1,
119
+ });
120
+ if (id === 'pi')
121
+ Object.assign(schema, { '--context-window': 1, '--max-tokens': 1 });
60
122
  const { options } = parseOptions(argv, schema, { strict: true });
61
- const env = { ...environment, ...(options['--model'] ? { MODEL: options['--model'] } : {}) };
62
- const gatewayUrl = origin(options['--gateway-url'] || value(env, 'GATEWAY_URL', defaults.baseUrl));
123
+ const agent = agentById(id);
124
+ const env = resolveInputs(
125
+ agent,
126
+ {
127
+ ...environment,
128
+ ...(options['--model'] ? { MODEL: options['--model'] } : {}),
129
+ },
130
+ {},
131
+ { strict: false },
132
+ );
133
+ const gatewayUrl = origin(
134
+ options['--gateway-url'] || value(env, 'GATEWAY_URL', defaults.baseUrl),
135
+ );
63
136
  const apiKey = options['--api-key'] || value(env, 'API_KEY');
64
137
  const installing = action === 'install';
65
138
  const removing = action === 'uninstall';
66
139
  const needsKey = ['codex', 'cursor', 'copilot', 'pi'].includes(id);
67
- if (installing && needsKey && !apiKey) throw new Error('An API key is required for setup. Run subc login first.');
140
+ if (installing && needsKey && !apiKey)
141
+ throw new Error('An API key is required for setup. Run subc login first.');
68
142
 
69
143
  if (['codex', 'cursor', 'copilot'].includes(id)) {
70
- const dir = id === 'codex' ? (env.CODEX_DIR || env.CODEX_HOME || path.join(home, '.codex')) : path.join(home, `.${id}`);
71
- const hooksFile = id === 'copilot' ? path.join(dir, 'hooks', 'subconscious-hooks.json') : path.join(dir, 'hooks.json');
144
+ const dir =
145
+ id === 'codex'
146
+ ? env.CODEX_DIR || env.CODEX_HOME || path.join(home, '.codex')
147
+ : path.join(home, `.${id}`);
148
+ const hooksFile =
149
+ id === 'copilot'
150
+ ? path.join(dir, 'hooks', 'subconscious-hooks.json')
151
+ : path.join(dir, 'hooks.json');
72
152
  const script = path.join(dir, 'hooks', 'subconscious-hook.cjs');
73
153
  const config = path.join(dir, 'subconscious-windows.json');
74
154
  const document = await readJson(hooksFile, {});
75
155
  // Validate all existing user configuration before writing any files.
76
- const merged = mergeHooks(document, id, installing ? windowsHookCommand(id, script, config) : null);
156
+ const merged = mergeHooks(
157
+ document,
158
+ id,
159
+ installing ? windowsHookCommand(id, script, config) : null,
160
+ );
77
161
  let providerFile, providers;
78
162
  if (id === 'copilot') {
79
163
  const apps = ['Code', 'Code - Insiders', 'VSCodium'];
80
164
  const appData = env.APPDATA || path.join(home, 'AppData', 'Roaming');
81
165
  let app = options['--vscode-app'] || env.VSCODE_APP;
82
- if (app && !apps.includes(app)) throw new Error('--vscode-app must be Code, Code - Insiders, or VSCodium');
166
+ if (app && !apps.includes(app))
167
+ throw new Error(
168
+ '--vscode-app must be Code, Code - Insiders, or VSCodium',
169
+ );
83
170
  if (!app) {
84
- for (const candidate of apps) if (await exists(path.join(appData, candidate, 'User'))) { app = candidate; break; }
171
+ for (const candidate of apps)
172
+ if (await exists(path.join(appData, candidate, 'User'))) {
173
+ app = candidate;
174
+ break;
175
+ }
85
176
  app ||= 'Code';
86
177
  }
87
178
  providerFile = path.join(appData, app, 'User', 'chatLanguageModels.json');
88
179
  const current = await readJson(providerFile, []);
89
- if (!Array.isArray(current)) throw new Error(`${providerFile} must be a JSON array; leaving it unchanged`);
90
- for (const provider of current) object(provider, 'language model provider');
91
- providers = current.filter(provider => provider.name !== providerName);
180
+ if (!Array.isArray(current))
181
+ throw new Error(
182
+ `${providerFile} must be a JSON array; leaving it unchanged`,
183
+ );
184
+ for (const provider of current)
185
+ object(provider, 'language model provider');
186
+ providers = current.filter((provider) => provider.name !== providerName);
92
187
  if (installing) {
93
- const context = positiveInteger(options['--max-input-tokens'] || value(env, 'COPILOT_MAX_INPUT_TOKENS', '5000000'), 'max-input-tokens');
94
- const maxTokens = positiveInteger(options['--max-output-tokens'] || value(env, 'COPILOT_MAX_OUTPUT_TOKENS', '65536'), 'max-output-tokens');
95
- providers.push({ name: providerName, vendor: 'customendpoint', apiKey: '${input:chat.lm.secret.subconscious-gateway}', apiType: 'messages', models: modelIds(env).map(id => ({
96
- id, name: `Subconscious ${id}`, url: `${gatewayUrl}/v1/messages`, toolCalling: true, vision: modelSupportsVision(id),
97
- maxInputTokens: context, maxOutputTokens: maxTokens, thinking: true, streaming: true,
98
- requestHeaders: { 'x-subconscious-client': 'copilot' },
99
- })) });
188
+ const context = positiveInteger(
189
+ options['--max-input-tokens'] ||
190
+ value(env, 'COPILOT_MAX_INPUT_TOKENS'),
191
+ 'max-input-tokens',
192
+ );
193
+ const maxTokens = positiveInteger(
194
+ options['--max-output-tokens'] ||
195
+ value(env, 'COPILOT_MAX_OUTPUT_TOKENS'),
196
+ 'max-output-tokens',
197
+ );
198
+ providers.push({
199
+ name: providerName,
200
+ vendor: 'customendpoint',
201
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: VS Code secret input, not a JS template
202
+ apiKey: '${input:chat.lm.secret.subconscious-gateway}',
203
+ apiType: 'messages',
204
+ models: modelIds(env).map((id) => ({
205
+ id,
206
+ name: `Subconscious ${id}`,
207
+ url: `${gatewayUrl}/v1/messages`,
208
+ toolCalling: true,
209
+ vision: modelSupportsVision(id),
210
+ maxInputTokens: context,
211
+ maxOutputTokens: maxTokens,
212
+ thinking: true,
213
+ streaming: true,
214
+ requestHeaders: { 'x-subconscious-client': 'copilot' },
215
+ })),
216
+ });
100
217
  }
101
218
  }
102
219
  if (action === 'status') {
103
- const hasHook = Object.values(document.hooks || {}).some(entries => entries.some(entry => owned(entry.command) || entry.hooks?.some(hook => owned(hook.command))));
104
- log(`${id}: ${hasHook ? 'Subconscious hooks installed' : 'no Subconscious hooks'} (${hooksFile})`);
220
+ const hasHook = Object.values(document.hooks || {}).some((entries) =>
221
+ entries.some(
222
+ (entry) =>
223
+ owned(entry.command) ||
224
+ entry.hooks?.some((hook) => owned(hook.command)),
225
+ ),
226
+ );
227
+ log(
228
+ `${id}: ${hasHook ? 'Subconscious hooks installed' : 'no Subconscious hooks'} (${hooksFile})`,
229
+ );
105
230
  if (providerFile) log(`Model configuration: ${providerFile}`);
106
231
  return 0;
107
232
  }
@@ -110,14 +235,22 @@ export async function windowsSetup(id, action, argv, environment, { home = os.ho
110
235
  await fs.copyFile(new URL('./hook.cjs', import.meta.url), script);
111
236
  await writeJson(config, { gatewayUrl, apiKey });
112
237
  }
113
- if (installing || await exists(hooksFile)) await writeJson(hooksFile, merged);
114
- if (providerFile && (installing || await exists(providerFile))) await writeJson(providerFile, providers);
238
+ if (installing || (await exists(hooksFile)))
239
+ await writeJson(hooksFile, merged);
240
+ if (providerFile && (installing || (await exists(providerFile))))
241
+ await writeJson(providerFile, providers);
115
242
  if (removing) {
116
243
  await fs.rm(script, { force: true });
117
244
  await fs.rm(config, { force: true });
118
245
  }
119
- if (installing && id === 'cursor') log(`In Cursor Settings, use ${gatewayUrl}/v1, your API key, and model ${value(env, 'MODEL', defaults.model)}. Restart Cursor to load hooks.`);
120
- if (installing && id === 'copilot') log('Restart VS Code, select a Subconscious model, and enter your API key when prompted.');
246
+ if (installing && id === 'cursor')
247
+ log(
248
+ `In Cursor Settings, use ${gatewayUrl}/v1, your API key, and model ${value(env, 'MODEL', defaults.model)}. Restart Cursor to load hooks.`,
249
+ );
250
+ if (installing && id === 'copilot')
251
+ log(
252
+ 'Restart VS Code, select a Subconscious model, and enter your API key when prompted.',
253
+ );
121
254
  log(`${id}: Windows integration ${installing ? 'installed' : 'removed'}.`);
122
255
  return 0;
123
256
  }
@@ -126,46 +259,108 @@ export async function windowsSetup(id, action, argv, environment, { home = os.ho
126
259
  const modelsFile = path.join(dir, 'models.json');
127
260
  const document = object(await readJson(modelsFile, {}), 'Pi models');
128
261
  const providers = object(document.providers ?? {}, 'Pi providers');
129
- if (action === 'status') { log(`Pi: ${providers.subconscious ? 'Subconscious provider installed' : 'not installed'} (${modelsFile})`); return 0; }
130
- const extension = path.join(dir, 'extensions', 'subconscious-compaction.ts');
262
+ if (action === 'status') {
263
+ log(
264
+ `Pi: ${providers.subconscious ? 'Subconscious provider installed' : 'not installed'} (${modelsFile})`,
265
+ );
266
+ return 0;
267
+ }
268
+ const extension = path.join(
269
+ dir,
270
+ 'extensions',
271
+ 'subconscious-compaction.ts',
272
+ );
273
+ // Keeps requests at the newest 30 images; same files as the shell runbook.
274
+ const imageWindow = path.join(
275
+ dir,
276
+ 'extensions',
277
+ 'subconscious-image-window',
278
+ );
131
279
  const config = path.join(dir, 'subconscious-windows.json');
132
280
  if (installing) {
133
- const context = positiveInteger(options['--context-window'] || value(env, 'PI_CONTEXT_WINDOW', '5000000'), 'context-window');
134
- const maxTokens = positiveInteger(options['--max-tokens'] || value(env, 'PI_MAX_TOKENS', '65536'), 'max-tokens');
135
- providers.subconscious = { baseUrl: `${gatewayUrl}/v1`, api: 'openai-completions', apiKey, headers: { 'x-subconscious-client': 'pi' }, models: modelIds(env).map(id => ({
136
- id, contextWindow: context, maxTokens, compat: { sendSessionAffinityHeaders: true, sessionAffinityFormat: 'openai-nosession' },
137
- ...(modelSupportsVision(id) ? { input: ['text', 'image'] } : {}),
138
- })) };
281
+ const context = positiveInteger(
282
+ options['--context-window'] || value(env, 'PI_CONTEXT_WINDOW'),
283
+ 'context-window',
284
+ );
285
+ const maxTokens = positiveInteger(
286
+ options['--max-tokens'] || value(env, 'PI_MAX_TOKENS'),
287
+ 'max-tokens',
288
+ );
289
+ providers.subconscious = {
290
+ baseUrl: `${gatewayUrl}/v1`,
291
+ api: 'openai-completions',
292
+ apiKey,
293
+ headers: { 'x-subconscious-client': 'pi' },
294
+ models: modelIds(env).map((id) => ({
295
+ id,
296
+ contextWindow: context,
297
+ maxTokens,
298
+ compat: {
299
+ sendSessionAffinityHeaders: true,
300
+ sessionAffinityFormat: 'openai-nosession',
301
+ },
302
+ ...(modelSupportsVision(id) ? { input: ['text', 'image'] } : {}),
303
+ })),
304
+ };
139
305
  await fs.mkdir(path.dirname(extension), { recursive: true });
140
- await fs.copyFile(new URL('./pi-compaction.ts', import.meta.url), extension);
306
+ await fs.copyFile(
307
+ new URL('./pi-compaction.ts', import.meta.url),
308
+ extension,
309
+ );
310
+ await fs.rm(imageWindow, { recursive: true, force: true });
311
+ await fs.cp(
312
+ new URL('../runbook/image-window', import.meta.url),
313
+ imageWindow,
314
+ { recursive: true },
315
+ );
141
316
  await writeJson(config, { gatewayUrl, apiKey });
142
317
  } else {
143
318
  delete providers.subconscious;
144
319
  await fs.rm(extension, { force: true });
320
+ await fs.rm(imageWindow, { recursive: true, force: true });
145
321
  await fs.rm(config, { force: true });
146
322
  }
147
- if (installing || await exists(modelsFile)) await writeJson(modelsFile, { ...document, providers });
323
+ if (installing || (await exists(modelsFile)))
324
+ await writeJson(modelsFile, { ...document, providers });
148
325
  log(`Pi: Windows integration ${installing ? 'installed' : 'removed'}.`);
149
326
  return 0;
150
327
  }
151
328
  if (id === 'claude-code' || id === 'opencode') {
152
329
  const dir = path.join(home, id === 'claude-code' ? '.claude' : '.opencode');
153
- const legacy = path.join(dir, id === 'claude-code' ? 'subconscious-gateway.env' : 'subconscious.env');
330
+ const legacy = path.join(
331
+ dir,
332
+ id === 'claude-code' ? 'subconscious-gateway.env' : 'subconscious.env',
333
+ );
154
334
  let present = await exists(legacy);
155
335
  if (id === 'opencode') {
156
336
  const file = path.join(dir, 'opencode.json');
157
- const document = object(await readJson(file, {}), 'OpenCode configuration');
158
- if (document.provider !== undefined) object(document.provider, 'OpenCode providers');
337
+ const document = object(
338
+ await readJson(file, {}),
339
+ 'OpenCode configuration',
340
+ );
341
+ if (document.provider !== undefined)
342
+ object(document.provider, 'OpenCode providers');
159
343
  present ||= Boolean(document.provider?.subconscious);
160
- if (removing && await exists(file)) {
344
+ if (removing && (await exists(file))) {
161
345
  if (document.provider) delete document.provider.subconscious;
162
346
  if (document.model?.startsWith('subconscious/')) delete document.model;
163
347
  await writeJson(file, document);
164
348
  }
165
- if (removing) await fs.rm(path.join(env.XDG_CONFIG_HOME || path.join(home, '.config'), 'opencode', 'plugins', 'subconscious-compaction.ts'), { force: true });
349
+ if (removing)
350
+ await fs.rm(
351
+ path.join(
352
+ env.XDG_CONFIG_HOME || path.join(home, '.config'),
353
+ 'opencode',
354
+ 'plugins',
355
+ 'subconscious-compaction.ts',
356
+ ),
357
+ { force: true },
358
+ );
166
359
  }
167
360
  if (removing) await fs.rm(legacy, { force: true });
168
- log(`${id}: ${removing ? 'legacy integration removed' : present ? 'legacy integration exists' : 'launch-only; no legacy integration'}.`);
361
+ log(
362
+ `${id}: ${removing ? 'legacy integration removed' : present ? 'legacy integration exists' : 'launch-only; no legacy integration'}.`,
363
+ );
169
364
  return 0;
170
365
  }
171
366
  throw new Error(`No native Windows setup is available for ${id}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subconscious-cli",
3
- "version": "6.0.0",
3
+ "version": "6.1.0",
4
4
  "description": "CLI for Subconscious — run Subconscious Code, Claude Code, Codex, OpenCode, DeepSeek Harness, Cursor, Copilot, and Pi",
5
5
  "bin": {
6
6
  "subc": "bin/cli.js"
@@ -9,9 +9,11 @@
9
9
  "build:tui": "node scripts/build-tui.js",
10
10
  "build:tui:host": "node scripts/build-tui.js --host",
11
11
  "test": "node --test",
12
- "test:windows": "node --test test/windows.test.js test/models.test.js test/model-capabilities.test.js test/auth.test.js test/tui.test.js test/update-check.test.js test/sessions.test.js test/branding.test.js test/claude-version.test.js test/usage.test.js test/feedback.test.js test/release.test.js",
12
+ "test:windows": "node --test test/windows.test.js test/models.test.js test/model-capabilities.test.js test/auth.test.js test/tui.test.js test/update-check.test.js test/sessions.test.js test/branding.test.js test/claude-version.test.js test/usage.test.js test/feedback.test.js test/release.test.js test/github-govuln-issue.test.js test/harness-manifest.test.js test/agent-files.test.js test/agent-launch.test.js",
13
13
  "test:tui": "go -C tui test ./...",
14
- "generate": "node scripts/generate-agents.js",
14
+ "lint": "biome ci .",
15
+ "format": "biome check --write .",
16
+ "typecheck": "tsc --noEmit",
15
17
  "prepare": "node scripts/install-git-hooks.js",
16
18
  "prepack": "npm run build:tui",
17
19
  "prepublishOnly": "node scripts/require-publish-script.js"
@@ -51,8 +53,11 @@
51
53
  "commit-msg": "npx --no-install commitlint --edit $1"
52
54
  },
53
55
  "devDependencies": {
56
+ "@biomejs/biome": "^2.5.14",
54
57
  "@commitlint/cli": "^21.2.3",
55
58
  "@commitlint/config-conventional": "^21.2.3",
56
- "simple-git-hooks": "^2.14.0"
59
+ "@types/node": "^26.6.2",
60
+ "simple-git-hooks": "^2.14.0",
61
+ "typescript": "^7.0.2"
57
62
  }
58
63
  }