@contextos/setup 0.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.
- package/dist/index.js +389 -0
- package/package.json +44 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import inquirer from "inquirer";
|
|
8
|
+
|
|
9
|
+
// src/detectors/index.ts
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { homedir, platform } from "os";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
function detectClaude() {
|
|
14
|
+
const os = platform();
|
|
15
|
+
let configPath;
|
|
16
|
+
if (os === "win32") {
|
|
17
|
+
configPath = join(homedir(), "AppData", "Roaming", "Claude", "claude_desktop_config.json");
|
|
18
|
+
} else if (os === "darwin") {
|
|
19
|
+
configPath = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
20
|
+
} else {
|
|
21
|
+
configPath = join(homedir(), ".config", "claude", "claude_desktop_config.json");
|
|
22
|
+
}
|
|
23
|
+
const configDir = join(configPath, "..");
|
|
24
|
+
const isInstalled = existsSync(configDir) || existsSync(configPath);
|
|
25
|
+
if (!isInstalled) return null;
|
|
26
|
+
return {
|
|
27
|
+
id: "claude",
|
|
28
|
+
name: "Claude Desktop",
|
|
29
|
+
configPath,
|
|
30
|
+
configType: "json",
|
|
31
|
+
hasExistingConfig: existsSync(configPath),
|
|
32
|
+
mcpKey: "mcpServers"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function detectCursor() {
|
|
36
|
+
const os = platform();
|
|
37
|
+
let configPath;
|
|
38
|
+
if (os === "win32") {
|
|
39
|
+
configPath = join(homedir(), "AppData", "Roaming", "Cursor", "User", "settings.json");
|
|
40
|
+
} else if (os === "darwin") {
|
|
41
|
+
configPath = join(homedir(), "Library", "Application Support", "Cursor", "User", "settings.json");
|
|
42
|
+
} else {
|
|
43
|
+
configPath = join(homedir(), ".config", "Cursor", "User", "settings.json");
|
|
44
|
+
}
|
|
45
|
+
const configDir = join(configPath, "..");
|
|
46
|
+
const isInstalled = existsSync(configDir);
|
|
47
|
+
if (!isInstalled) return null;
|
|
48
|
+
return {
|
|
49
|
+
id: "cursor",
|
|
50
|
+
name: "Cursor",
|
|
51
|
+
configPath,
|
|
52
|
+
configType: "json",
|
|
53
|
+
hasExistingConfig: existsSync(configPath),
|
|
54
|
+
mcpKey: "mcp.servers"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function detectWindsurf() {
|
|
58
|
+
const os = platform();
|
|
59
|
+
let configPath;
|
|
60
|
+
if (os === "win32") {
|
|
61
|
+
configPath = join(homedir(), "AppData", "Roaming", "Windsurf", "User", "settings.json");
|
|
62
|
+
} else if (os === "darwin") {
|
|
63
|
+
configPath = join(homedir(), "Library", "Application Support", "Windsurf", "User", "settings.json");
|
|
64
|
+
} else {
|
|
65
|
+
configPath = join(homedir(), ".config", "Windsurf", "User", "settings.json");
|
|
66
|
+
}
|
|
67
|
+
const configDir = join(configPath, "..");
|
|
68
|
+
const isInstalled = existsSync(configDir);
|
|
69
|
+
if (!isInstalled) return null;
|
|
70
|
+
return {
|
|
71
|
+
id: "windsurf",
|
|
72
|
+
name: "Windsurf",
|
|
73
|
+
configPath,
|
|
74
|
+
configType: "json",
|
|
75
|
+
hasExistingConfig: existsSync(configPath),
|
|
76
|
+
mcpKey: "mcp.servers"
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function detectVSCode() {
|
|
80
|
+
const os = platform();
|
|
81
|
+
let configPath;
|
|
82
|
+
if (os === "win32") {
|
|
83
|
+
configPath = join(homedir(), "AppData", "Roaming", "Code", "User", "settings.json");
|
|
84
|
+
} else if (os === "darwin") {
|
|
85
|
+
configPath = join(homedir(), "Library", "Application Support", "Code", "User", "settings.json");
|
|
86
|
+
} else {
|
|
87
|
+
configPath = join(homedir(), ".config", "Code", "User", "settings.json");
|
|
88
|
+
}
|
|
89
|
+
const configDir = join(configPath, "..");
|
|
90
|
+
const isInstalled = existsSync(configDir);
|
|
91
|
+
if (!isInstalled) return null;
|
|
92
|
+
return {
|
|
93
|
+
id: "vscode",
|
|
94
|
+
name: "VS Code",
|
|
95
|
+
configPath,
|
|
96
|
+
configType: "json",
|
|
97
|
+
hasExistingConfig: existsSync(configPath),
|
|
98
|
+
mcpKey: "mcp.servers"
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function detectCodex() {
|
|
102
|
+
const os = platform();
|
|
103
|
+
let configPath;
|
|
104
|
+
if (os === "win32") {
|
|
105
|
+
configPath = join(homedir(), ".codex", "config.json");
|
|
106
|
+
} else {
|
|
107
|
+
configPath = join(homedir(), ".codex", "config.json");
|
|
108
|
+
}
|
|
109
|
+
const configDir = join(configPath, "..");
|
|
110
|
+
const isInstalled = existsSync(configDir);
|
|
111
|
+
if (!isInstalled) return null;
|
|
112
|
+
return {
|
|
113
|
+
id: "codex",
|
|
114
|
+
name: "OpenAI Codex CLI",
|
|
115
|
+
configPath,
|
|
116
|
+
configType: "json",
|
|
117
|
+
hasExistingConfig: existsSync(configPath),
|
|
118
|
+
mcpKey: "mcpServers"
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async function detectIDEs() {
|
|
122
|
+
const detectors = [
|
|
123
|
+
detectClaude,
|
|
124
|
+
detectCursor,
|
|
125
|
+
detectWindsurf,
|
|
126
|
+
detectVSCode,
|
|
127
|
+
detectCodex
|
|
128
|
+
];
|
|
129
|
+
const results = [];
|
|
130
|
+
for (const detect of detectors) {
|
|
131
|
+
const ide = detect();
|
|
132
|
+
if (ide) {
|
|
133
|
+
results.push(ide);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/injectors/index.ts
|
|
140
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
141
|
+
import { dirname } from "path";
|
|
142
|
+
function generateMCPConfig(projectPath) {
|
|
143
|
+
const baseConfig = {
|
|
144
|
+
command: "npx",
|
|
145
|
+
args: ["@contextos/mcp"]
|
|
146
|
+
};
|
|
147
|
+
if (projectPath) {
|
|
148
|
+
return {
|
|
149
|
+
...baseConfig,
|
|
150
|
+
cwd: projectPath
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return baseConfig;
|
|
154
|
+
}
|
|
155
|
+
async function injectClaude(ide, options) {
|
|
156
|
+
let config = {};
|
|
157
|
+
if (existsSync2(ide.configPath)) {
|
|
158
|
+
try {
|
|
159
|
+
config = JSON.parse(readFileSync(ide.configPath, "utf-8"));
|
|
160
|
+
} catch {
|
|
161
|
+
config = {};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const mcpServers = config.mcpServers || {};
|
|
165
|
+
if (mcpServers.contextos && !options.force) {
|
|
166
|
+
return {
|
|
167
|
+
ide: ide.name,
|
|
168
|
+
success: false,
|
|
169
|
+
message: "Already configured (use --force to overwrite)"
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
config.mcpServers = {
|
|
173
|
+
...mcpServers,
|
|
174
|
+
contextos: generateMCPConfig(options.projectPath)
|
|
175
|
+
};
|
|
176
|
+
const configDir = dirname(ide.configPath);
|
|
177
|
+
if (!existsSync2(configDir)) {
|
|
178
|
+
mkdirSync(configDir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
writeFileSync(ide.configPath, JSON.stringify(config, null, 2));
|
|
181
|
+
return {
|
|
182
|
+
ide: ide.name,
|
|
183
|
+
success: true,
|
|
184
|
+
message: "Configuration added successfully",
|
|
185
|
+
configPath: ide.configPath
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async function injectVSCodeStyle(ide, options) {
|
|
189
|
+
let config = {};
|
|
190
|
+
if (existsSync2(ide.configPath)) {
|
|
191
|
+
try {
|
|
192
|
+
config = JSON.parse(readFileSync(ide.configPath, "utf-8"));
|
|
193
|
+
} catch {
|
|
194
|
+
config = {};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const keys = ide.mcpKey.split(".");
|
|
198
|
+
let current = config;
|
|
199
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
200
|
+
const key = keys[i];
|
|
201
|
+
if (!current[key]) {
|
|
202
|
+
current[key] = {};
|
|
203
|
+
}
|
|
204
|
+
current = current[key];
|
|
205
|
+
}
|
|
206
|
+
const lastKey = keys[keys.length - 1];
|
|
207
|
+
const servers = current[lastKey] || {};
|
|
208
|
+
if (servers.contextos && !options.force) {
|
|
209
|
+
return {
|
|
210
|
+
ide: ide.name,
|
|
211
|
+
success: false,
|
|
212
|
+
message: "Already configured (use --force to overwrite)"
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
current[lastKey] = {
|
|
216
|
+
...servers,
|
|
217
|
+
contextos: {
|
|
218
|
+
command: "npx @contextos/mcp",
|
|
219
|
+
cwd: options.projectPath || "${workspaceFolder}"
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const configDir = dirname(ide.configPath);
|
|
223
|
+
if (!existsSync2(configDir)) {
|
|
224
|
+
mkdirSync(configDir, { recursive: true });
|
|
225
|
+
}
|
|
226
|
+
writeFileSync(ide.configPath, JSON.stringify(config, null, 2));
|
|
227
|
+
return {
|
|
228
|
+
ide: ide.name,
|
|
229
|
+
success: true,
|
|
230
|
+
message: "Configuration added successfully",
|
|
231
|
+
configPath: ide.configPath
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async function injectConfig(ide, options = {}) {
|
|
235
|
+
switch (ide.id) {
|
|
236
|
+
case "claude":
|
|
237
|
+
return injectClaude(ide, options);
|
|
238
|
+
case "cursor":
|
|
239
|
+
case "windsurf":
|
|
240
|
+
case "vscode":
|
|
241
|
+
case "codex":
|
|
242
|
+
return injectVSCodeStyle(ide, options);
|
|
243
|
+
default:
|
|
244
|
+
return {
|
|
245
|
+
ide: ide.name,
|
|
246
|
+
success: false,
|
|
247
|
+
message: `Unknown IDE: ${ide.id}`
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/index.ts
|
|
253
|
+
var program = new Command();
|
|
254
|
+
program.name("contextos-setup").description("One-click ContextOS setup for all your IDEs").version("0.1.0");
|
|
255
|
+
program.command("auto").description("Automatically detect and configure all IDEs").option("--dry-run", "Show what would be configured without making changes").option("--force", "Overwrite existing configurations").action(async (options) => {
|
|
256
|
+
console.log(chalk.cyan.bold("\n\u{1F680} ContextOS Auto Setup\n"));
|
|
257
|
+
const spinner = ora("Detecting installed IDEs...").start();
|
|
258
|
+
try {
|
|
259
|
+
const ides = await detectIDEs();
|
|
260
|
+
if (ides.length === 0) {
|
|
261
|
+
spinner.warn("No supported IDEs detected");
|
|
262
|
+
console.log(chalk.gray("\nSupported IDEs: Claude Desktop, Cursor, Windsurf, VS Code\n"));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
spinner.succeed(`Found ${ides.length} IDE(s)`);
|
|
266
|
+
console.log(chalk.gray("\nDetected IDEs:"));
|
|
267
|
+
ides.forEach((ide) => {
|
|
268
|
+
const status = ide.hasExistingConfig ? chalk.yellow("\u26A0 has config") : chalk.green("\u2713 ready");
|
|
269
|
+
console.log(` ${ide.name} ${status}`);
|
|
270
|
+
});
|
|
271
|
+
if (options.dryRun) {
|
|
272
|
+
console.log(chalk.yellow("\n[Dry Run] Would configure:"));
|
|
273
|
+
ides.forEach((ide) => console.log(` - ${ide.name}`));
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const { proceed } = await inquirer.prompt([
|
|
277
|
+
{
|
|
278
|
+
type: "confirm",
|
|
279
|
+
name: "proceed",
|
|
280
|
+
message: "Configure ContextOS for these IDEs?",
|
|
281
|
+
default: true
|
|
282
|
+
}
|
|
283
|
+
]);
|
|
284
|
+
if (!proceed) {
|
|
285
|
+
console.log(chalk.gray("Setup cancelled.\n"));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
console.log();
|
|
289
|
+
const results = [];
|
|
290
|
+
for (const ide of ides) {
|
|
291
|
+
const configSpinner = ora(`Configuring ${ide.name}...`).start();
|
|
292
|
+
try {
|
|
293
|
+
const result = await injectConfig(ide, { force: options.force });
|
|
294
|
+
results.push(result);
|
|
295
|
+
if (result.success) {
|
|
296
|
+
configSpinner.succeed(`${ide.name} configured`);
|
|
297
|
+
} else {
|
|
298
|
+
configSpinner.warn(`${ide.name}: ${result.message}`);
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
configSpinner.fail(`${ide.name} failed`);
|
|
302
|
+
results.push({
|
|
303
|
+
ide: ide.name,
|
|
304
|
+
success: false,
|
|
305
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const successful = results.filter((r) => r.success).length;
|
|
310
|
+
console.log(chalk.green(`
|
|
311
|
+
\u2705 Setup complete: ${successful}/${ides.length} IDEs configured
|
|
312
|
+
`));
|
|
313
|
+
if (successful > 0) {
|
|
314
|
+
console.log(chalk.cyan("Next steps:"));
|
|
315
|
+
console.log(" 1. Restart your IDE(s)");
|
|
316
|
+
console.log(" 2. Open a project folder");
|
|
317
|
+
console.log(" 3. ContextOS will be available in your AI assistant!\n");
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
spinner.fail("Setup failed");
|
|
321
|
+
console.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
program.command("list").description("List detected IDEs and their status").action(async () => {
|
|
326
|
+
console.log(chalk.cyan.bold("\n\u{1F50D} Detected IDEs\n"));
|
|
327
|
+
const spinner = ora("Scanning...").start();
|
|
328
|
+
const ides = await detectIDEs();
|
|
329
|
+
spinner.stop();
|
|
330
|
+
if (ides.length === 0) {
|
|
331
|
+
console.log(chalk.yellow("No supported IDEs found.\n"));
|
|
332
|
+
console.log(chalk.gray("Supported: Claude Desktop, Cursor, Windsurf, VS Code\n"));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
console.log(chalk.gray("IDE Status Config Path"));
|
|
336
|
+
console.log(chalk.gray("\u2500".repeat(70)));
|
|
337
|
+
ides.forEach((ide) => {
|
|
338
|
+
const status = ide.hasExistingConfig ? chalk.yellow("Configured") : chalk.green("Ready");
|
|
339
|
+
const configPath = ide.configPath.length > 35 ? "..." + ide.configPath.slice(-32) : ide.configPath;
|
|
340
|
+
console.log(`${ide.name.padEnd(24)}${status.padEnd(20)}${chalk.gray(configPath)}`);
|
|
341
|
+
});
|
|
342
|
+
console.log();
|
|
343
|
+
});
|
|
344
|
+
program.command("configure <ide>").description("Configure a specific IDE (claude, cursor, windsurf, vscode)").option("--force", "Overwrite existing configuration").action(async (ideName, options) => {
|
|
345
|
+
const ides = await detectIDEs();
|
|
346
|
+
const ide = ides.find((i) => i.id === ideName.toLowerCase());
|
|
347
|
+
if (!ide) {
|
|
348
|
+
console.log(chalk.red(`
|
|
349
|
+
IDE not found: ${ideName}`));
|
|
350
|
+
console.log(chalk.gray("Available: claude, cursor, windsurf, vscode\n"));
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
const spinner = ora(`Configuring ${ide.name}...`).start();
|
|
354
|
+
try {
|
|
355
|
+
const result = await injectConfig(ide, { force: options.force });
|
|
356
|
+
if (result.success) {
|
|
357
|
+
spinner.succeed(result.message);
|
|
358
|
+
} else {
|
|
359
|
+
spinner.warn(result.message);
|
|
360
|
+
}
|
|
361
|
+
} catch (error) {
|
|
362
|
+
spinner.fail(error instanceof Error ? error.message : "Failed");
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
program.command("uninstall").description("Remove ContextOS from all configured IDEs").action(async () => {
|
|
367
|
+
const { confirm } = await inquirer.prompt([
|
|
368
|
+
{
|
|
369
|
+
type: "confirm",
|
|
370
|
+
name: "confirm",
|
|
371
|
+
message: "Remove ContextOS configuration from all IDEs?",
|
|
372
|
+
default: false
|
|
373
|
+
}
|
|
374
|
+
]);
|
|
375
|
+
if (!confirm) {
|
|
376
|
+
console.log(chalk.gray("Cancelled.\n"));
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const ides = await detectIDEs();
|
|
380
|
+
for (const ide of ides) {
|
|
381
|
+
if (ide.hasExistingConfig) {
|
|
382
|
+
console.log(chalk.yellow(`Would remove from ${ide.name}`));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
program.action(async () => {
|
|
387
|
+
await program.commands.find((c) => c.name() === "auto")?.parseAsync(process.argv);
|
|
388
|
+
});
|
|
389
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contextos/setup",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One-click setup for ContextOS - automatically configure all your IDEs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"contextos-setup": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"chalk": "^5.3.0",
|
|
13
|
+
"commander": "^12.0.0",
|
|
14
|
+
"inquirer": "^9.2.0",
|
|
15
|
+
"ora": "^8.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/inquirer": "^9.0.7",
|
|
19
|
+
"@types/node": "^20.11.0",
|
|
20
|
+
"eslint": "^8.57.0",
|
|
21
|
+
"tsup": "^8.0.2",
|
|
22
|
+
"typescript": "^5.4.0"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"keywords": [
|
|
29
|
+
"contextos",
|
|
30
|
+
"setup",
|
|
31
|
+
"mcp",
|
|
32
|
+
"claude",
|
|
33
|
+
"cursor",
|
|
34
|
+
"windsurf",
|
|
35
|
+
"ai",
|
|
36
|
+
"coding"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup src/index.ts --format esm --clean",
|
|
40
|
+
"dev": "tsup src/index.ts --format esm --watch",
|
|
41
|
+
"start": "node dist/index.js",
|
|
42
|
+
"lint": "eslint src/"
|
|
43
|
+
}
|
|
44
|
+
}
|