subconscious-cli 6.2.0 → 6.3.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.
@@ -0,0 +1,75 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { spawnWindows } from './windows/process.js';
3
+
4
+ export const COMMAND_TIMEOUT_MS = 5_000;
5
+ const MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
6
+
7
+ /**
8
+ * Run a command and collect its stdout without blocking the event loop.
9
+ *
10
+ * A synchronous spawn with a timeout only sends SIGTERM and then waits for the
11
+ * child to exit, so a child that ignores SIGTERM blocks the process for as long
12
+ * as it runs. This sends SIGKILL at the deadline and stops reading at once,
13
+ * even when a grandchild still holds the pipe open.
14
+ */
15
+ export function runCommand(command, args, options = {}) {
16
+ const spawnImpl =
17
+ options.spawn || (process.platform === 'win32' ? spawnWindows : spawn);
18
+ const timeoutMs = options.timeout ?? COMMAND_TIMEOUT_MS;
19
+ const signal = options.signal;
20
+
21
+ return new Promise((resolve) => {
22
+ if (signal?.aborted) {
23
+ resolve({ status: null, stdout: '', error: new Error('cancelled') });
24
+ return;
25
+ }
26
+
27
+ let child;
28
+ try {
29
+ child = spawnImpl(command, args, {
30
+ env: options.env || process.env,
31
+ stdio: ['ignore', 'pipe', 'ignore'],
32
+ windowsHide: true,
33
+ });
34
+ } catch (error) {
35
+ resolve({ status: null, stdout: '', error });
36
+ return;
37
+ }
38
+
39
+ let stdout = '';
40
+ let settled = false;
41
+ let timer;
42
+ const finish = (result) => {
43
+ if (settled) return;
44
+ settled = true;
45
+ clearTimeout(timer);
46
+ signal?.removeEventListener('abort', onAbort);
47
+ resolve(result);
48
+ };
49
+ const stop = (message) => {
50
+ try {
51
+ child.kill('SIGKILL');
52
+ } catch {
53
+ // The child already exited.
54
+ }
55
+ child.stdout?.destroy();
56
+ finish({ status: null, stdout: '', error: new Error(message) });
57
+ };
58
+ const onAbort = () => stop('cancelled');
59
+
60
+ timer = setTimeout(
61
+ () => stop(`${command} timed out after ${timeoutMs}ms`),
62
+ timeoutMs,
63
+ );
64
+ signal?.addEventListener('abort', onAbort, { once: true });
65
+
66
+ child.stdout?.setEncoding('utf8');
67
+ child.stdout?.on('data', (chunk) => {
68
+ stdout += chunk;
69
+ if (stdout.length > MAX_OUTPUT_BYTES) stop(`${command} output too large`);
70
+ });
71
+ child.stdout?.on('error', () => {});
72
+ child.once('error', (error) => finish({ status: null, stdout: '', error }));
73
+ child.once('close', (status) => finish({ status, stdout }));
74
+ });
75
+ }
Binary file
Binary file
Binary file
Binary file
package/bin/profiles.js CHANGED
@@ -36,6 +36,10 @@ const PREVIOUS_DEFAULT_GATEWAYS = new Set([
36
36
  const CONFIG_OVERRIDE = process.env.SUBC_CONFIG_DIR?.trim();
37
37
  const CONFIG_DIR = CONFIG_OVERRIDE || path.join(os.homedir(), '.subconscious');
38
38
  export const PROFILES_DIR = path.join(CONFIG_DIR, 'profiles');
39
+ /** Absolute path of the subc config dir (~/.subconscious or $SUBC_CONFIG_DIR). */
40
+ export function configDir() {
41
+ return CONFIG_DIR;
42
+ }
39
43
  const LEGACY_PROFILES_DIR = CONFIG_OVERRIDE
40
44
  ? null
41
45
  : path.join(os.homedir(), '.subcon', 'profiles');
@@ -8,12 +8,11 @@ the default gateway, model capabilities, and the settings every agent uses
8
8
 
9
9
  | Agent | Launch | Setup |
10
10
  | --- | --- | --- |
11
+ | `opencode` | binary | `install.sh` (leftover status/uninstall) |
11
12
  | `claude-code` | binary | `install.sh` (leftover status/uninstall) |
12
13
  | `codex` | `run.sh` | `install.sh` (hooks) |
13
- | `opencode` | binary | `install.sh` (leftover status/uninstall) |
14
- | `cursor` | none | `install.sh` (hooks) |
15
- | `copilot` | none | `install.sh` (provider and hooks) |
16
14
  | `pi` | `run.sh` | `install.sh` (provider and extensions) |
15
+ | `copilot` | none | `install.sh` (provider and hooks) |
17
16
  | `deepseek-harness` | `run.sh` | none |
18
17
 
19
18
  ## What `agent.json` holds and who reads it
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "help": {
30
30
  "usage": "subc [-p NAME] claude [help|status|uninstall] [Claude arguments...]",
31
- "behavior": "Launches Claude Code with the active Subconscious profile and model picker. Persistent files are leftover-only: subc claude uninstall.",
31
+ "behavior": "Launches Claude Code with the active Subconscious profile and model picker. Sessions run with CLAUDE_CONFIG_DIR=~/.subconscious/claude-code, which shares the setup your plain claude uses (~/.claude, or your CLAUDE_CONFIG_DIR): CLAUDE.md, skills, plugins, hooks, MCP servers and session history. Only the model pick is kept apart, so a /model choice in subc does not change plain Claude. Persistent files are leftover-only: subc claude uninstall.",
32
32
  "options": [
33
33
  ["help", "Show this help"],
34
34
  ["status", "Inspect leftover ~/.claude/subconscious-gateway.env"],
@@ -113,6 +113,18 @@
113
113
  "type": "integer",
114
114
  "min": 1
115
115
  },
116
+ {
117
+ "name": "CLAUDE_CODE_ISOLATE",
118
+ "flag": "--no-isolate",
119
+ "flag_value": "off",
120
+ "default": "on",
121
+ "strict": true,
122
+ "choices": ["on", "off"],
123
+ "help": "Use ~/.claude directly, so a /model pick in subc also changes plain Claude Code",
124
+ "label": "Isolated Claude config",
125
+ "description": "When on, subc claude sessions use ~/.subconscious/claude-code as the Claude config dir. It links back to ~/.claude for CLAUDE.md, skills, hooks and session history, copies the plugin index, rebuilds settings.json from ~/.claude/settings.json on each launch, and keeps only the subc model pick apart.",
126
+ "comment": "CLAUDE_CODE_ISOLATE=off shares ~/.claude with plain Claude Code."
127
+ },
116
128
  { "name": "MAX_CONCURRENT_SUBAGENTS" },
117
129
  {
118
130
  "name": "MAX_SUBAGENT_SPAWN_DEPTH",
@@ -147,6 +159,12 @@
147
159
  "override": ["ANTHROPIC_SMALL_FAST_MODEL"],
148
160
  "description": "Small/fast background model."
149
161
  },
162
+ {
163
+ "name": "CLAUDE_CONFIG_DIR",
164
+ "value": "{claudeConfigDir}",
165
+ "optional": true,
166
+ "description": "Claude Code config dir for subc sessions, prepared before each launch to share the user's own Claude config (~/.claude, or a CLAUDE_CONFIG_DIR they set, which becomes the source rather than the launch dir). When CLAUDE_CODE_ISOLATE=off, the user's own CLAUDE_CONFIG_DIR is kept, or none is set, so sessions use it directly."
167
+ },
150
168
  {
151
169
  "name": "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY",
152
170
  "value": "0",
@@ -233,6 +251,12 @@
233
251
  },
234
252
  "description": "Replaces the built-in model picker with the live catalog and allowlists only those models."
235
253
  },
254
+ {
255
+ "kind": "file",
256
+ "path": "~/.subconscious/claude-code",
257
+ "lifetime": "persistent",
258
+ "description": "Claude Code config dir for subc sessions (~/.subconscious/claude-code, honoring $SUBC_CONFIG_DIR), set via CLAUDE_CONFIG_DIR. Each launch links CLAUDE.md, history.jsonl and keybindings.json (when present) and the rules, agents, commands, skills, output-styles, hooks, projects, file-history, todos, plans and ide folders (created in ~/.claude when missing) back to ~/.claude, copies the plugin index files (plugin changes made inside subc stay in subc and reset on the next launch), rebuilds settings.json from ~/.claude/settings.json (keeping the subc model pick, dropping apiKeyHelper and env names subc sets), and copies MCP servers, trust and onboarding state from ~/.claude.json (never login state). Settings or MCP servers changed inside a subc session are replaced on the next launch; that launch names them and saves them in settings.discarded.json or mcp.discarded.json in this dir. subc never deletes it and never writes ~/.claude settings; it refuses to launch when this dir resolves to your own config. To remove it, delete the links first: rm -r with a trailing slash, or PowerShell Remove-Item -Recurse, follows them into ~/.claude. Opt out with CLAUDE_CODE_ISOLATE=off or subc claude --no-isolate."
259
+ },
236
260
  {
237
261
  "kind": "file",
238
262
  "path": "~/.claude/subconscious-gateway.env",
@@ -3,9 +3,8 @@
3
3
  "opencode",
4
4
  "claude-code",
5
5
  "codex",
6
- "cursor",
7
- "copilot",
8
6
  "pi",
7
+ "copilot",
9
8
  "deepseek-harness"
10
9
  ],
11
10
  "defaults": {
package/bin/sessions.js CHANGED
@@ -1,12 +1,11 @@
1
- import { spawnSync } from 'node:child_process';
2
1
  import fs from 'node:fs/promises';
3
2
  import os from 'node:os';
4
3
  import path from 'node:path';
5
4
  import { AGENTS, agentById, agentCommandName } from './agent-data.js';
6
5
  import { resolveAgent, runAgent } from './agents.js';
7
-
6
+ import { claudeSourceDir } from './claude-config.js';
8
7
  import { c } from './colors.js';
9
- import { spawnWindowsSync } from './windows/process.js';
8
+ import { runCommand } from './command-output.js';
10
9
 
11
10
  const MAX_CANDIDATE_FILES = 160;
12
11
  const MAX_READ_BYTES = 2 * 1024 * 1024;
@@ -399,12 +398,11 @@ async function discoverCodexIndex(root, indexFile, max) {
399
398
  );
400
399
  }
401
400
 
402
- function commandResult(execute, command, args, options = {}) {
403
- const result = execute(command, args, {
404
- encoding: 'utf8',
405
- timeout: options.timeout ?? 5_000,
401
+ async function commandResult(execute, command, args, options = {}) {
402
+ const result = await execute(command, args, {
403
+ timeout: options.timeout,
404
+ signal: options.signal,
406
405
  env: { ...process.env, NO_COLOR: '1' },
407
- maxBuffer: 4 * 1024 * 1024,
408
406
  });
409
407
  if (result.error || result.status !== 0) return '';
410
408
  return String(result.stdout || '').trim();
@@ -422,18 +420,18 @@ function sqlString(value) {
422
420
  return `'${String(value).replaceAll("'", "''")}'`;
423
421
  }
424
422
 
425
- function discoverOpenCodeSessions(execute, max) {
423
+ async function discoverOpenCodeSessions(execute, max, options = {}) {
426
424
  const query = [
427
425
  'select id, title, directory, time_created, time_updated, model',
428
426
  'from session where time_archived is null',
429
427
  `order by time_updated desc limit ${Math.max(1, Math.min(500, max))}`,
430
428
  ].join(' ');
431
- const output = commandResult(execute, 'opencode', [
432
- 'db',
433
- '--format',
434
- 'json',
435
- query,
436
- ]);
429
+ const output = await commandResult(
430
+ execute,
431
+ 'opencode',
432
+ ['db', '--format', 'json', query],
433
+ options,
434
+ );
437
435
  if (!output) return [];
438
436
  try {
439
437
  return JSON.parse(output).map((record) =>
@@ -452,11 +450,11 @@ function discoverOpenCodeSessions(execute, max) {
452
450
  export async function discoverSessions(options = {}) {
453
451
  const home = options.home || os.homedir();
454
452
  const max = options.max || 500;
455
- const execute =
456
- options.execute ||
457
- (process.platform === 'win32' ? spawnWindowsSync : spawnSync);
453
+ const execute = options.execute || runCommand;
454
+ // Plain claude's folder: ~/.claude, or the user's CLAUDE_CONFIG_DIR.
455
+ const claudeDir = claudeSourceDir({ env: options.env, home });
458
456
  const roots = options.roots || {
459
- claude: path.join(home, '.claude', 'projects'),
457
+ claude: path.join(claudeDir, 'projects'),
460
458
  codex: path.join(home, '.codex', 'sessions'),
461
459
  pi: path.join(home, '.pi', 'agent', 'sessions'),
462
460
  };
@@ -468,7 +466,7 @@ export async function discoverSessions(options = {}) {
468
466
  codex: path.join(roots.codex, '.session-index.jsonl'),
469
467
  }
470
468
  : {
471
- claude: path.join(home, '.claude', 'history.jsonl'),
469
+ claude: path.join(claudeDir, 'history.jsonl'),
472
470
  codex: path.join(home, '.codex', 'session_index.jsonl'),
473
471
  });
474
472
  const claude = await discoverClaudeIndex(roots.claude, indexes.claude, max);
@@ -476,7 +474,10 @@ export async function discoverSessions(options = {}) {
476
474
  const groups = await Promise.all([
477
475
  claude || discoverFileSessions(roots.claude, 'claude', max),
478
476
  codex || discoverFileSessions(roots.codex, 'codex', max),
479
- Promise.resolve(discoverOpenCodeSessions(execute, max)),
477
+ discoverOpenCodeSessions(execute, max, {
478
+ timeout: options.commandTimeoutMs,
479
+ signal: options.signal,
480
+ }),
480
481
  discoverFileSessions(roots.pi, 'pi', max),
481
482
  ]);
482
483
  const seen = new Set();
@@ -500,7 +501,7 @@ async function openCodeMessages(session, execute) {
500
501
  "and json_extract(part.data, '$.type') = 'text'",
501
502
  'order by message.time_created asc, part.time_created asc',
502
503
  ].join(' ');
503
- const output = commandResult(execute, 'opencode', [
504
+ const output = await commandResult(execute, 'opencode', [
504
505
  'db',
505
506
  '--format',
506
507
  'json',
@@ -521,11 +522,7 @@ async function openCodeMessages(session, execute) {
521
522
 
522
523
  export async function readSessionMessages(session, options = {}) {
523
524
  if (session.harness === 'opencode') {
524
- return openCodeMessages(
525
- session,
526
- options.execute ||
527
- (process.platform === 'win32' ? spawnWindowsSync : spawnSync),
528
- );
525
+ return openCodeMessages(session, options.execute || runCommand);
529
526
  }
530
527
  if (!session.sourcePath) return [];
531
528
  const records = parseLines(await readBounded(session.sourcePath));
package/bin/tui.js CHANGED
@@ -411,6 +411,7 @@ function spawnAndWait(command, args, options = {}) {
411
411
  stdio: options.stdio ?? 'inherit',
412
412
  });
413
413
  child.once('error', reject);
414
+ options.onSpawn?.();
414
415
  child.once('exit', (code, signal) => {
415
416
  if (signal) {
416
417
  reject(new Error(`Subconscious TUI exited with signal ${signal}`));
@@ -458,13 +459,17 @@ async function runTuiOnce(options) {
458
459
  }
459
460
  };
460
461
 
461
- const remote = providedState
462
- ? Promise.resolve()
463
- : loadRemoteTuiUpdates(state, {
464
- ...options,
465
- signal: controller.signal,
466
- writePatch,
467
- });
462
+ // Remote work starts only once the TUI process exists, so nothing it does
463
+ // (a slow gateway, a stuck session probe) can delay the first frame.
464
+ let remote = Promise.resolve();
465
+ const startRemote = () => {
466
+ if (providedState) return;
467
+ remote = loadRemoteTuiUpdates(state, {
468
+ ...options,
469
+ signal: controller.signal,
470
+ writePatch,
471
+ });
472
+ };
468
473
 
469
474
  try {
470
475
  // This state intentionally contains only display data. API keys are used
@@ -483,7 +488,12 @@ async function runTuiOnce(options) {
483
488
  '--updates',
484
489
  updatesPath,
485
490
  ],
486
- { cwd: executable.cwd, spawn: options.spawn, stdio: options.stdio },
491
+ {
492
+ cwd: executable.cwd,
493
+ spawn: options.spawn,
494
+ stdio: options.stdio,
495
+ onSpawn: startRemote,
496
+ },
487
497
  );
488
498
  controller.abort();
489
499
  await remote.catch(() => {});
@@ -18,12 +18,10 @@ async function main() {
18
18
  });
19
19
  try { payload = JSON.parse(input); } catch { /* fail open */ }
20
20
  const event = payload?.hook_event_name || '';
21
- const response = client === 'cursor'
22
- ? (event === 'preCompact' ? {} : { continue: true, permission: 'allow' })
23
- : client === 'copilot' ? { continue: true } : {};
21
+ const response = client === 'copilot' ? { continue: true } : {};
24
22
  try {
25
23
  const config = JSON.parse(await fs.readFile(configPath, 'utf8'));
26
- const cid = payload?.conversation_id || payload?.session_id;
24
+ const cid = payload?.session_id;
27
25
  if (!cid || typeof cid !== 'string' || !config.apiKey || !config.gatewayUrl) return;
28
26
  const post = async body => {
29
27
  const controller = new AbortController();
@@ -36,19 +34,15 @@ async function main() {
36
34
  await res.body?.cancel();
37
35
  } catch { /* fail open */ } finally { clearTimeout(timer); }
38
36
  };
39
- if ((client === 'cursor' && event === 'beforeSubmitPrompt') || (client === 'copilot' && event === 'UserPromptSubmit')) {
37
+ if (client === 'copilot' && event === 'UserPromptSubmit') {
40
38
  if (typeof payload.prompt !== 'string' || !payload.prompt) return;
41
- const root = payload.workspace_roots?.[0] || payload.cwd;
39
+ const root = payload.cwd;
42
40
  await post({ event: 'conversation_ensure', prompt: payload.prompt, workspace: typeof root === 'string' ? path.win32.basename(root) : undefined });
43
41
  }
44
42
  if (client === 'codex' && ['PreCompact', 'PostCompact'].includes(event)) {
45
43
  const phase = event === 'PreCompact' ? 'start' : 'end';
46
44
  await post({ event: 'conversation_compaction', phase, dedupe_key: `${cid}:${phase}:${payload.turn_id || randomUUID()}`, metadata: { trigger: payload.trigger, turn_id: payload.turn_id } });
47
45
  }
48
- if (client === 'cursor' && event === 'preCompact') {
49
- const metadata = Object.fromEntries(['trigger', 'context_tokens', 'context_window_size', 'context_usage_percent', 'message_count', 'messages_to_compact', 'is_first_compaction'].filter(k => payload[k] != null).map(k => [k, payload[k]]));
50
- await post({ event: 'conversation_compaction', phase: 'point', dedupe_key: `${cid}:${payload.generation_id ?? 'gen'}:${payload.is_first_compaction ?? false}`, metadata });
51
- }
52
46
  if (client === 'copilot' && ['PreCompact', 'UserPromptSubmit'].includes(event)) {
53
47
  const pendingDir = path.join(path.dirname(configPath), 'subconscious-compact-pending');
54
48
  const pending = path.join(pendingDir, createHash('sha256').update(cid).digest('hex') + '.json');
@@ -9,6 +9,7 @@ import {
9
9
  resolveInputs,
10
10
  takeFlags,
11
11
  } from '../agent-launch.js';
12
+ import { prepareClaudeLaunch } from '../claude-config.js';
12
13
  import { claudePickerSettings } from '../claude-picker.js';
13
14
  import { modelIds, positiveInteger } from '../launch-values.js';
14
15
  import { modelSupportsVision } from '../model-capabilities.js';
@@ -56,11 +57,18 @@ function claudeLaunch(agent, argv, env) {
56
57
  }
57
58
 
58
59
  function plannedLaunch(agent, argv, env) {
59
- const plan =
60
- agent.id === 'claude-code'
61
- ? claudeLaunch(agent, argv, env)
62
- : launchPlan(agent, { env, args: argv });
63
- return { command: plan.argv[0], args: plan.argv.slice(1), env: plan.env };
60
+ if (agent.id !== 'claude-code') {
61
+ const plan = launchPlan(agent, { env, args: argv });
62
+ return { command: plan.argv[0], args: plan.argv.slice(1), env: plan.env };
63
+ }
64
+ const plan = claudeLaunch(agent, argv, env);
65
+ return {
66
+ command: plan.argv[0],
67
+ args: plan.argv.slice(1),
68
+ env: plan.env,
69
+ // Runs only when the launch executes, so planning never touches disk.
70
+ prepare: (options) => prepareClaudeLaunch(agent, plan.env, env, options),
71
+ };
64
72
  }
65
73
 
66
74
  function codexCatalog(env, context, maxContext, compact) {
@@ -266,12 +274,17 @@ export async function windowsLaunch(
266
274
  throw new Error(`No native Windows launcher is available for ${id}`);
267
275
  }
268
276
 
269
- export async function executeWindowsLaunch(spec, run) {
277
+ export async function executeWindowsLaunch(spec, run, prepareOptions) {
270
278
  try {
279
+ const prepared = spec.prepare?.(prepareOptions);
280
+ for (const warning of prepared?.warnings ?? []) {
281
+ console.error(` ${warning}`);
282
+ }
283
+ // Claude Code would write into the user's own config there.
284
+ if (prepared?.refused) return 1;
285
+ const env = { ...spec.env, ...prepared?.env };
271
286
  const options =
272
- spec.input === undefined
273
- ? { env: spec.env }
274
- : { env: spec.env, input: spec.input };
287
+ spec.input === undefined ? { env } : { env, input: spec.input };
275
288
  return await run(spec.command, spec.args, options);
276
289
  } finally {
277
290
  await spec.cleanup?.();
@@ -1,6 +1,6 @@
1
1
  // Windows process handling. Unix callers keep using their existing launch paths.
2
2
 
3
- import { execFileSync, spawn, spawnSync } from 'node:child_process';
3
+ import { execFileSync, spawn } from 'node:child_process';
4
4
  import fs from 'node:fs';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
@@ -189,23 +189,6 @@ export function spawnWindows(command, args, options = {}) {
189
189
  });
190
190
  }
191
191
 
192
- export function spawnWindowsSync(command, args, options = {}) {
193
- try {
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
- }
207
- }
208
-
209
192
  export function runWindows(command, args, options = {}) {
210
193
  const { input, ...spawnOptions } = options;
211
194
  return new Promise((resolve, reject) => {
@@ -78,7 +78,6 @@ export function mergeHooks(document, client, command) {
78
78
  if (command) {
79
79
  const events = {
80
80
  codex: ['PreCompact', 'PostCompact'],
81
- cursor: ['beforeSubmitPrompt', 'preCompact'],
82
81
  copilot: ['UserPromptSubmit', 'PreCompact'],
83
82
  }[client];
84
83
  for (const event of events) {
@@ -99,7 +98,6 @@ export function mergeHooks(document, client, command) {
99
98
  }
100
99
  }
101
100
  result.hooks = hooks;
102
- if (client === 'cursor' && command) result.version ??= 1;
103
101
  return result;
104
102
  }
105
103
 
@@ -136,11 +134,11 @@ export async function windowsSetup(
136
134
  const apiKey = options['--api-key'] || value(env, 'API_KEY');
137
135
  const installing = action === 'install';
138
136
  const removing = action === 'uninstall';
139
- const needsKey = ['codex', 'cursor', 'copilot', 'pi'].includes(id);
137
+ const needsKey = ['codex', 'copilot', 'pi'].includes(id);
140
138
  if (installing && needsKey && !apiKey)
141
139
  throw new Error('An API key is required for setup. Run subc login first.');
142
140
 
143
- if (['codex', 'cursor', 'copilot'].includes(id)) {
141
+ if (['codex', 'copilot'].includes(id)) {
144
142
  const dir =
145
143
  id === 'codex'
146
144
  ? env.CODEX_DIR || env.CODEX_HOME || path.join(home, '.codex')
@@ -243,10 +241,6 @@ export async function windowsSetup(
243
241
  await fs.rm(script, { force: true });
244
242
  await fs.rm(config, { force: true });
245
243
  }
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
244
  if (installing && id === 'copilot')
251
245
  log(
252
246
  'Restart VS Code, select a Subconscious model, and enter your API key when prompted.',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "subconscious-cli",
3
- "version": "6.2.0",
4
- "description": "CLI for Subconscious — run Subconscious Code, Claude Code, Codex, OpenCode, DeepSeek Harness, Cursor, Copilot, and Pi",
3
+ "version": "6.3.0",
4
+ "description": "CLI for Subconscious — run Subconscious Code, Claude Code, Codex, OpenCode, DeepSeek Harness, Copilot, and Pi",
5
5
  "bin": {
6
6
  "subc": "bin/cli.js"
7
7
  },
@@ -9,7 +9,7 @@
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 test/github-govuln-issue.test.js test/harness-manifest.test.js test/agent-files.test.js test/agent-launch.test.js test/agent-install-policy.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/command-output.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 test/agent-install-policy.test.js",
13
13
  "test:tui": "go -C tui test ./...",
14
14
  "lint": "biome ci .",
15
15
  "format": "biome check --write .",
@@ -40,7 +40,6 @@
40
40
  "claude-code",
41
41
  "opencode",
42
42
  "codex",
43
- "cursor",
44
43
  "copilot",
45
44
  "pi",
46
45
  "deepseek-harness",