@supercollab/cli 0.4.5 → 0.4.7
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 +21 -0
- package/bin/supercollab.js +163 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,6 +149,27 @@ opening Claude, verify the local MCP handshake:
|
|
|
149
149
|
supercollab mcp smoke
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
+
For Claude Code, install the MCP server from the project directory:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
cd /path/to/project
|
|
156
|
+
supercollab mcp install --client claude-code
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The same installer is available without typing the command:
|
|
160
|
+
|
|
161
|
+
```text
|
|
162
|
+
supercollab -> Settings -> Install Claude Code MCP
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
During first-run setup, choose `Install Claude Code MCP` at the MCP setup step
|
|
166
|
+
to run the installer immediately.
|
|
167
|
+
|
|
168
|
+
The installer first runs the MCP smoke check, then calls Claude Code's own
|
|
169
|
+
`claude mcp add` with absolute Node and CLI paths plus explicit `HOME`, `PATH`,
|
|
170
|
+
and `SUPERCOLLAB_WORKDIR` values. This avoids Homebrew, nvm, shell startup, and
|
|
171
|
+
GUI path differences.
|
|
172
|
+
|
|
152
173
|
Default server: `https://hyper.polynode.dev`.
|
|
153
174
|
|
|
154
175
|
Local config is stored at `~/.supercollab/config.json` with mode `0600`; the
|
package/bin/supercollab.js
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
import * as readlineCore from 'node:readline';
|
|
9
9
|
import { stdin as input, stdout as output } from 'node:process';
|
|
10
10
|
|
|
11
|
-
const VERSION = '0.4.
|
|
11
|
+
const VERSION = '0.4.7';
|
|
12
12
|
const CLI_ENTRY = fileURLToPath(import.meta.url);
|
|
13
13
|
const DEFAULT_SERVER = process.env.SUPERCOLLAB_URL || 'https://hyper.polynode.dev';
|
|
14
14
|
const DEFAULT_CONFIG = process.env.SUPERCOLLAB_CONFIG || path.join(os.homedir(), '.supercollab', 'config.json');
|
|
@@ -61,6 +61,7 @@ Usage:
|
|
|
61
61
|
supercollab embeddings status
|
|
62
62
|
supercollab embeddings warmup
|
|
63
63
|
supercollab mcp stdio
|
|
64
|
+
supercollab mcp install --client claude-code [--scope local] [--cwd PATH]
|
|
64
65
|
supercollab mcp print-config --client codex
|
|
65
66
|
supercollab mcp smoke [--timeout 5000]
|
|
66
67
|
supercollab config path
|
|
@@ -1796,9 +1797,120 @@ function mcpConfigText(client, file, opts = {}) {
|
|
|
1796
1797
|
if (client === 'codex') {
|
|
1797
1798
|
return `[mcp_servers.supercollab]\ncommand = "supercollab"\nargs = ["mcp", "stdio", "--config", "${escaped}"]`;
|
|
1798
1799
|
}
|
|
1800
|
+
if (client === 'claude-code') {
|
|
1801
|
+
const install = claudeCodeInstallPlan(file, opts);
|
|
1802
|
+
return shellCommand(install.command, install.args);
|
|
1803
|
+
}
|
|
1799
1804
|
return `supercollab mcp stdio --config "${escaped}"`;
|
|
1800
1805
|
}
|
|
1801
1806
|
|
|
1807
|
+
function shellQuote(value) {
|
|
1808
|
+
const text = String(value);
|
|
1809
|
+
if (/^[A-Za-z0-9_./:=@%+-]+$/.test(text)) return text;
|
|
1810
|
+
return `'${text.replaceAll("'", "'\\''")}'`;
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
function shellCommand(command, args = []) {
|
|
1814
|
+
return [command, ...args].map(shellQuote).join(' ');
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
function claudeCodeMcpEnv(opts = {}) {
|
|
1818
|
+
const cwd = path.resolve(String(opts.cwd || process.env.SUPERCOLLAB_WORKDIR || process.cwd()));
|
|
1819
|
+
return {
|
|
1820
|
+
HOME: os.homedir(),
|
|
1821
|
+
PATH: defaultPathEnv(),
|
|
1822
|
+
SUPERCOLLAB_WORKDIR: cwd,
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
function claudeCodeInstallPlan(file, opts = {}) {
|
|
1827
|
+
const scope = String(opts.scope || 'local');
|
|
1828
|
+
const claude = String(opts.claude || 'claude');
|
|
1829
|
+
const env = claudeCodeMcpEnv(opts);
|
|
1830
|
+
const args = [
|
|
1831
|
+
'mcp', 'add',
|
|
1832
|
+
'--scope', scope,
|
|
1833
|
+
'--env', `HOME=${env.HOME}`,
|
|
1834
|
+
'--env', `PATH=${env.PATH}`,
|
|
1835
|
+
'--env', `SUPERCOLLAB_WORKDIR=${env.SUPERCOLLAB_WORKDIR}`,
|
|
1836
|
+
'--transport', 'stdio',
|
|
1837
|
+
'supercollab',
|
|
1838
|
+
'--',
|
|
1839
|
+
process.execPath,
|
|
1840
|
+
CLI_ENTRY,
|
|
1841
|
+
'mcp',
|
|
1842
|
+
'stdio',
|
|
1843
|
+
'--config',
|
|
1844
|
+
path.resolve(file),
|
|
1845
|
+
];
|
|
1846
|
+
return { command: claude, args, env, scope, cwd: env.SUPERCOLLAB_WORKDIR };
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
function runProcess(command, args, options = {}) {
|
|
1850
|
+
const result = spawnSync(command, args, {
|
|
1851
|
+
encoding: 'utf8',
|
|
1852
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
1853
|
+
...options,
|
|
1854
|
+
});
|
|
1855
|
+
return {
|
|
1856
|
+
ok: result.status === 0,
|
|
1857
|
+
status: result.status,
|
|
1858
|
+
signal: result.signal,
|
|
1859
|
+
stdout: String(result.stdout || '').trim(),
|
|
1860
|
+
stderr: String(result.stderr || '').trim(),
|
|
1861
|
+
error: result.error?.message || null,
|
|
1862
|
+
};
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
async function installClaudeCodeMcp(config, file, opts = {}) {
|
|
1866
|
+
const client = String(opts.client || 'claude-code');
|
|
1867
|
+
if (!['claude-code', 'claude'].includes(client)) throw new Error(`unsupported mcp install client: ${client}`);
|
|
1868
|
+
const plan = claudeCodeInstallPlan(file, opts);
|
|
1869
|
+
const dryRun = Boolean(opts['dry-run'] || opts.dryRun);
|
|
1870
|
+
const smoke = await runMcpSmoke({ ...opts, config: file, timeout: opts.timeout || 5000 });
|
|
1871
|
+
const removeArgs = ['mcp', 'remove', 'supercollab', '--scope', plan.scope];
|
|
1872
|
+
|
|
1873
|
+
if (dryRun) {
|
|
1874
|
+
return {
|
|
1875
|
+
ok: true,
|
|
1876
|
+
dry_run: true,
|
|
1877
|
+
client: 'claude-code',
|
|
1878
|
+
smoke,
|
|
1879
|
+
remove_command: shellCommand(plan.command, removeArgs),
|
|
1880
|
+
install_command: shellCommand(plan.command, plan.args),
|
|
1881
|
+
scope: plan.scope,
|
|
1882
|
+
cwd: plan.cwd,
|
|
1883
|
+
config: path.resolve(file),
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
const probe = runProcess(plan.command, ['--version'], { cwd: plan.cwd });
|
|
1888
|
+
if (!probe.ok) {
|
|
1889
|
+
throw new Error(`Claude Code CLI not found or not runnable as ${plan.command}. Install Claude Code first, or pass --claude /absolute/path/to/claude. ${probe.stderr || probe.error || ''}`.trim());
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
runProcess(plan.command, removeArgs, { cwd: plan.cwd });
|
|
1893
|
+
const add = runProcess(plan.command, plan.args, { cwd: plan.cwd });
|
|
1894
|
+
if (!add.ok) {
|
|
1895
|
+
throw new Error(`claude mcp add failed: ${add.stderr || add.stdout || add.error || `exit ${add.status}`}`);
|
|
1896
|
+
}
|
|
1897
|
+
const list = runProcess(plan.command, ['mcp', 'list'], { cwd: plan.cwd });
|
|
1898
|
+
const get = runProcess(plan.command, ['mcp', 'get', 'supercollab'], { cwd: plan.cwd });
|
|
1899
|
+
return {
|
|
1900
|
+
ok: true,
|
|
1901
|
+
client: 'claude-code',
|
|
1902
|
+
scope: plan.scope,
|
|
1903
|
+
cwd: plan.cwd,
|
|
1904
|
+
config: path.resolve(file),
|
|
1905
|
+
smoke,
|
|
1906
|
+
install_command: shellCommand(plan.command, plan.args),
|
|
1907
|
+
claude_add: { stdout: add.stdout, stderr: add.stderr },
|
|
1908
|
+
claude_list: { ok: list.ok, stdout: list.stdout, stderr: list.stderr },
|
|
1909
|
+
claude_get: { ok: get.ok, stdout: get.stdout, stderr: get.stderr },
|
|
1910
|
+
next: 'Start Claude Code in this project, or run /mcp inside Claude Code and confirm supercollab is connected.',
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1802
1914
|
async function promptSystemCheck(config, file, prompts) {
|
|
1803
1915
|
const spin = prompts.spinner();
|
|
1804
1916
|
spin.start('Checking this machine and warming the local BGE model');
|
|
@@ -2098,8 +2210,9 @@ async function promptAccountStatus(config, file, prompts) {
|
|
|
2098
2210
|
|
|
2099
2211
|
async function promptMcpConfig(config, file, prompts) {
|
|
2100
2212
|
const client = await prompts.select({
|
|
2101
|
-
message: 'MCP
|
|
2213
|
+
message: 'MCP setup',
|
|
2102
2214
|
options: [
|
|
2215
|
+
{ value: 'claude-code', label: 'Install Claude Code MCP', hint: 'runs claude mcp add now' },
|
|
2103
2216
|
{ value: 'codex', label: 'Codex', hint: 'TOML config snippet' },
|
|
2104
2217
|
{ value: 'claude', label: 'Claude', hint: 'JSON config snippet' },
|
|
2105
2218
|
{ value: 'manual', label: 'Manual', hint: 'stdio command' },
|
|
@@ -2107,9 +2220,48 @@ async function promptMcpConfig(config, file, prompts) {
|
|
|
2107
2220
|
],
|
|
2108
2221
|
});
|
|
2109
2222
|
if (prompts.isCancel(client) || client === 'back') return;
|
|
2223
|
+
if (client === 'claude-code') return promptInstallClaudeCode(config, file, prompts);
|
|
2110
2224
|
prompts.note(mcpConfigText(client, file), `${client} MCP config`);
|
|
2111
2225
|
}
|
|
2112
2226
|
|
|
2227
|
+
async function promptInstallClaudeCode(config, file, prompts, options = {}) {
|
|
2228
|
+
const defaultCwd = path.resolve(String(options.cwd || process.cwd()));
|
|
2229
|
+
const cwd = await prompts.text({
|
|
2230
|
+
message: 'Claude Code project directory',
|
|
2231
|
+
defaultValue: defaultCwd,
|
|
2232
|
+
placeholder: defaultCwd,
|
|
2233
|
+
validate: (value) => fs.existsSync(path.resolve(String(value || ''))) ? undefined : 'Directory does not exist',
|
|
2234
|
+
});
|
|
2235
|
+
if (prompts.isCancel(cwd)) throw new Error('cancelled');
|
|
2236
|
+
let scope = options.scope || null;
|
|
2237
|
+
if (!scope) scope = await prompts.select({
|
|
2238
|
+
message: 'Claude Code MCP scope',
|
|
2239
|
+
options: [
|
|
2240
|
+
{ value: 'local', label: 'Local project', hint: 'recommended; private to this project' },
|
|
2241
|
+
{ value: 'user', label: 'User', hint: 'available across your projects' },
|
|
2242
|
+
],
|
|
2243
|
+
});
|
|
2244
|
+
if (prompts.isCancel(scope)) throw new Error('cancelled');
|
|
2245
|
+
const spin = prompts.spinner();
|
|
2246
|
+
spin.start('Installing SuperCollab into Claude Code');
|
|
2247
|
+
try {
|
|
2248
|
+
const result = await installClaudeCodeMcp(config, file, { client: 'claude-code', cwd: String(cwd), scope });
|
|
2249
|
+
spin.stop('Claude Code MCP installed');
|
|
2250
|
+
prompts.note([
|
|
2251
|
+
`Scope: ${result.scope}`,
|
|
2252
|
+
`Project: ${result.cwd}`,
|
|
2253
|
+
`Smoke: ${result.smoke?.ok ? 'ok' : 'failed'}`,
|
|
2254
|
+
result.claude_list?.stdout || '',
|
|
2255
|
+
'',
|
|
2256
|
+
result.next,
|
|
2257
|
+
].filter(Boolean).join('\n'), 'Claude Code');
|
|
2258
|
+
return result;
|
|
2259
|
+
} catch (err) {
|
|
2260
|
+
spin.stop('Claude Code install failed');
|
|
2261
|
+
throw err;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2113
2265
|
function sessionsFromResponse(data) {
|
|
2114
2266
|
if (Array.isArray(data)) return data;
|
|
2115
2267
|
if (Array.isArray(data?.sessions)) return data.sessions;
|
|
@@ -2258,7 +2410,8 @@ async function runSettingsMenu(config, file, prompts) {
|
|
|
2258
2410
|
options: [
|
|
2259
2411
|
{ value: 'doctor', label: 'System check / install BGE model' },
|
|
2260
2412
|
{ value: 'account', label: 'Account and config status' },
|
|
2261
|
-
{ value: '
|
|
2413
|
+
{ value: 'install_claude_code', label: 'Install Claude Code MCP' },
|
|
2414
|
+
{ value: 'mcp', label: 'MCP setup/config' },
|
|
2262
2415
|
{ value: 'sessions', label: 'Manage sessions' },
|
|
2263
2416
|
{ value: 'server', label: 'Set server URL' },
|
|
2264
2417
|
{ value: 'back', label: 'Back' },
|
|
@@ -2268,6 +2421,7 @@ async function runSettingsMenu(config, file, prompts) {
|
|
|
2268
2421
|
try {
|
|
2269
2422
|
if (action === 'doctor') await promptSystemCheck(config, file, prompts);
|
|
2270
2423
|
if (action === 'account') await promptAccountStatus(config, file, prompts);
|
|
2424
|
+
if (action === 'install_claude_code') await promptInstallClaudeCode(config, file, prompts);
|
|
2271
2425
|
if (action === 'mcp') await promptMcpConfig(config, file, prompts);
|
|
2272
2426
|
if (action === 'sessions') await promptManageSessions(config, prompts);
|
|
2273
2427
|
if (action === 'server') await promptSetServerUrl(config, file, prompts);
|
|
@@ -2342,8 +2496,9 @@ async function runSetupWizard(config, file, opts = {}) {
|
|
|
2342
2496
|
const smoke = await runSetupSmoke(config, file, roomId, prompts);
|
|
2343
2497
|
|
|
2344
2498
|
const client = await prompts.select({
|
|
2345
|
-
message: 'MCP
|
|
2499
|
+
message: 'MCP setup',
|
|
2346
2500
|
options: [
|
|
2501
|
+
{ value: 'claude-code', label: 'Install Claude Code MCP', hint: 'runs claude mcp add now' },
|
|
2347
2502
|
{ value: 'codex', label: 'Codex', hint: 'TOML config snippet' },
|
|
2348
2503
|
{ value: 'claude', label: 'Claude', hint: 'JSON config snippet' },
|
|
2349
2504
|
{ value: 'manual', label: 'Manual', hint: 'stdio command' },
|
|
@@ -2361,7 +2516,9 @@ async function runSetupWizard(config, file, opts = {}) {
|
|
|
2361
2516
|
};
|
|
2362
2517
|
saveConfig(config, file);
|
|
2363
2518
|
|
|
2364
|
-
if (client
|
|
2519
|
+
if (client === 'claude-code') {
|
|
2520
|
+
await promptInstallClaudeCode(config, file, prompts, { cwd: activation?.cwd || process.cwd(), scope: 'local' });
|
|
2521
|
+
} else if (client !== 'skip') {
|
|
2365
2522
|
prompts.note(mcpConfigText(client, file), `${client} MCP config`);
|
|
2366
2523
|
}
|
|
2367
2524
|
prompts.outro(`Ready. Config saved at ${file}`);
|
|
@@ -2432,6 +2589,7 @@ async function main() {
|
|
|
2432
2589
|
if (sub === 'warmup') return console.log(JSON.stringify(await embeddingWarmup(), null, 2));
|
|
2433
2590
|
}
|
|
2434
2591
|
if (cmd === 'mcp' && sub === 'stdio') return runMcp(opts);
|
|
2592
|
+
if (cmd === 'mcp' && sub === 'install') return console.log(JSON.stringify(await installClaudeCodeMcp(config, file, opts), null, 2));
|
|
2435
2593
|
if (cmd === 'mcp' && sub === 'smoke') return console.log(JSON.stringify(await runMcpSmoke(opts), null, 2));
|
|
2436
2594
|
if (cmd === 'mcp' && sub === 'print-config') return printCodexConfig(opts);
|
|
2437
2595
|
throw new Error(`unknown command: ${positionals.join(' ')}`);
|