@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.
Files changed (2) hide show
  1. package/dist/index.js +389 -0
  2. 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
+ }