@supercollab/cli 0.4.5 → 0.4.6
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 +12 -0
- package/bin/supercollab.js +154 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -149,6 +149,18 @@ 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 installer first runs the MCP smoke check, then calls Claude Code's own
|
|
160
|
+
`claude mcp add` with absolute Node and CLI paths plus explicit `HOME`, `PATH`,
|
|
161
|
+
and `SUPERCOLLAB_WORKDIR` values. This avoids Homebrew, nvm, shell startup, and
|
|
162
|
+
GUI path differences.
|
|
163
|
+
|
|
152
164
|
Default server: `https://hyper.polynode.dev`.
|
|
153
165
|
|
|
154
166
|
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.6';
|
|
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
|
+
'--transport', 'stdio',
|
|
1834
|
+
'--env', `HOME=${env.HOME}`,
|
|
1835
|
+
'--env', `PATH=${env.PATH}`,
|
|
1836
|
+
'--env', `SUPERCOLLAB_WORKDIR=${env.SUPERCOLLAB_WORKDIR}`,
|
|
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');
|
|
@@ -2100,6 +2212,7 @@ async function promptMcpConfig(config, file, prompts) {
|
|
|
2100
2212
|
const client = await prompts.select({
|
|
2101
2213
|
message: 'MCP client config',
|
|
2102
2214
|
options: [
|
|
2215
|
+
{ value: 'claude-code', label: 'Claude Code', hint: 'one-command installer' },
|
|
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' },
|
|
@@ -2110,6 +2223,42 @@ async function promptMcpConfig(config, file, prompts) {
|
|
|
2110
2223
|
prompts.note(mcpConfigText(client, file), `${client} MCP config`);
|
|
2111
2224
|
}
|
|
2112
2225
|
|
|
2226
|
+
async function promptInstallClaudeCode(config, file, prompts) {
|
|
2227
|
+
const cwd = await prompts.text({
|
|
2228
|
+
message: 'Claude Code project directory',
|
|
2229
|
+
defaultValue: process.cwd(),
|
|
2230
|
+
placeholder: process.cwd(),
|
|
2231
|
+
validate: (value) => fs.existsSync(path.resolve(String(value || ''))) ? undefined : 'Directory does not exist',
|
|
2232
|
+
});
|
|
2233
|
+
if (prompts.isCancel(cwd)) throw new Error('cancelled');
|
|
2234
|
+
const scope = await prompts.select({
|
|
2235
|
+
message: 'Claude Code MCP scope',
|
|
2236
|
+
options: [
|
|
2237
|
+
{ value: 'local', label: 'Local project', hint: 'recommended; private to this project' },
|
|
2238
|
+
{ value: 'user', label: 'User', hint: 'available across your projects' },
|
|
2239
|
+
],
|
|
2240
|
+
});
|
|
2241
|
+
if (prompts.isCancel(scope)) throw new Error('cancelled');
|
|
2242
|
+
const spin = prompts.spinner();
|
|
2243
|
+
spin.start('Installing SuperCollab into Claude Code');
|
|
2244
|
+
try {
|
|
2245
|
+
const result = await installClaudeCodeMcp(config, file, { client: 'claude-code', cwd: String(cwd), scope });
|
|
2246
|
+
spin.stop('Claude Code MCP installed');
|
|
2247
|
+
prompts.note([
|
|
2248
|
+
`Scope: ${result.scope}`,
|
|
2249
|
+
`Project: ${result.cwd}`,
|
|
2250
|
+
`Smoke: ${result.smoke?.ok ? 'ok' : 'failed'}`,
|
|
2251
|
+
result.claude_list?.stdout || '',
|
|
2252
|
+
'',
|
|
2253
|
+
result.next,
|
|
2254
|
+
].filter(Boolean).join('\n'), 'Claude Code');
|
|
2255
|
+
return result;
|
|
2256
|
+
} catch (err) {
|
|
2257
|
+
spin.stop('Claude Code install failed');
|
|
2258
|
+
throw err;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2113
2262
|
function sessionsFromResponse(data) {
|
|
2114
2263
|
if (Array.isArray(data)) return data;
|
|
2115
2264
|
if (Array.isArray(data?.sessions)) return data.sessions;
|
|
@@ -2258,6 +2407,7 @@ async function runSettingsMenu(config, file, prompts) {
|
|
|
2258
2407
|
options: [
|
|
2259
2408
|
{ value: 'doctor', label: 'System check / install BGE model' },
|
|
2260
2409
|
{ value: 'account', label: 'Account and config status' },
|
|
2410
|
+
{ value: 'install_claude_code', label: 'Install Claude Code MCP' },
|
|
2261
2411
|
{ value: 'mcp', label: 'Show MCP config' },
|
|
2262
2412
|
{ value: 'sessions', label: 'Manage sessions' },
|
|
2263
2413
|
{ value: 'server', label: 'Set server URL' },
|
|
@@ -2268,6 +2418,7 @@ async function runSettingsMenu(config, file, prompts) {
|
|
|
2268
2418
|
try {
|
|
2269
2419
|
if (action === 'doctor') await promptSystemCheck(config, file, prompts);
|
|
2270
2420
|
if (action === 'account') await promptAccountStatus(config, file, prompts);
|
|
2421
|
+
if (action === 'install_claude_code') await promptInstallClaudeCode(config, file, prompts);
|
|
2271
2422
|
if (action === 'mcp') await promptMcpConfig(config, file, prompts);
|
|
2272
2423
|
if (action === 'sessions') await promptManageSessions(config, prompts);
|
|
2273
2424
|
if (action === 'server') await promptSetServerUrl(config, file, prompts);
|
|
@@ -2344,6 +2495,7 @@ async function runSetupWizard(config, file, opts = {}) {
|
|
|
2344
2495
|
const client = await prompts.select({
|
|
2345
2496
|
message: 'MCP client config',
|
|
2346
2497
|
options: [
|
|
2498
|
+
{ value: 'claude-code', label: 'Claude Code', hint: 'one-command installer' },
|
|
2347
2499
|
{ value: 'codex', label: 'Codex', hint: 'TOML config snippet' },
|
|
2348
2500
|
{ value: 'claude', label: 'Claude', hint: 'JSON config snippet' },
|
|
2349
2501
|
{ value: 'manual', label: 'Manual', hint: 'stdio command' },
|
|
@@ -2432,6 +2584,7 @@ async function main() {
|
|
|
2432
2584
|
if (sub === 'warmup') return console.log(JSON.stringify(await embeddingWarmup(), null, 2));
|
|
2433
2585
|
}
|
|
2434
2586
|
if (cmd === 'mcp' && sub === 'stdio') return runMcp(opts);
|
|
2587
|
+
if (cmd === 'mcp' && sub === 'install') return console.log(JSON.stringify(await installClaudeCodeMcp(config, file, opts), null, 2));
|
|
2435
2588
|
if (cmd === 'mcp' && sub === 'smoke') return console.log(JSON.stringify(await runMcpSmoke(opts), null, 2));
|
|
2436
2589
|
if (cmd === 'mcp' && sub === 'print-config') return printCodexConfig(opts);
|
|
2437
2590
|
throw new Error(`unknown command: ${positionals.join(' ')}`);
|