@contextos/setup 0.1.0 → 0.2.1

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 (3) hide show
  1. package/README.md +202 -0
  2. package/dist/index.js +586 -150
  3. package/package.json +11 -3
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # @contextos/setup
2
+
3
+ **Universal setup for ContextOS - one command to configure all AI coding tools**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@contextos/setup?style=flat-square)](https://www.npmjs.com/package/@contextos/setup)
6
+
7
+ ```bash
8
+ npx @contextos/setup
9
+ ```
10
+
11
+ ## 🎯 What It Does
12
+
13
+ This package automatically detects and configures ContextOS for **all your AI coding tools**:
14
+
15
+ ### ✅ Supported Tools
16
+
17
+ | Tool | Type | MCP Support | Auto-Configure |
18
+ |------|------|-------------|----------------|
19
+ | **Claude Desktop** | IDE | ✅ Native | ✅ |
20
+ | **Claude Code CLI** | CLI | ✅ Native | ✅ |
21
+ | **Cursor** | IDE | ✅ Native | ✅ |
22
+ | **Windsurf** | IDE | ✅ Native | ✅ |
23
+ | **VS Code** | IDE | 🔌 Extension | ✅ |
24
+ | **Kilo Code** | IDE | ✅ Native | ✅ |
25
+ | **Codex CLI** | CLI | 📦 Wrapper | ✅ |
26
+ | **Gemini CLI** | CLI | 📦 Wrapper | ✅ |
27
+ | **OpenCode CLI** | CLI | 📦 Wrapper | ✅ |
28
+ | **Warp Terminal** | Terminal | 📦 Wrapper | ✅ |
29
+
30
+ - ✅ **Native MCP**: Direct config injection, works immediately
31
+ - 🔌 **Extension**: Needs VS Code extension (Continue.dev)
32
+ - 📦 **Wrapper**: Creates wrapper script for context injection
33
+
34
+ ## 🚀 Quick Start
35
+
36
+ ### One-Command Setup
37
+
38
+ ```bash
39
+ # Detect and configure ALL AI tools
40
+ npx @contextos/setup
41
+
42
+ # Example output:
43
+ # 🚀 ContextOS Universal Setup
44
+ #
45
+ # Found 4 AI tool(s)
46
+ #
47
+ # 🖥️ IDEs:
48
+ # Claude Desktop MCP
49
+ # Cursor MCP
50
+ #
51
+ # ⌨️ CLI Tools:
52
+ # Claude Code CLI MCP
53
+ # Codex CLI Wrapper
54
+ #
55
+ # ✓ Claude Desktop: MCP configuration added
56
+ # ✓ Cursor: MCP configuration added
57
+ # ✓ Claude Code CLI: MCP configuration added
58
+ # ✓ Codex CLI: Wrapper script created
59
+ # → Add ~/.local/bin to your PATH
60
+ # → Use 'codex-ctx' instead of 'codex'
61
+ #
62
+ # ✅ Setup complete: 4/4 tools configured
63
+ ```
64
+
65
+ ## 📋 Commands
66
+
67
+ ### `npx @contextos/setup` (or `npx @contextos/setup auto`)
68
+
69
+ Automatically detect and configure all tools.
70
+
71
+ **Options:**
72
+ - `--dry-run` - Preview without making changes
73
+ - `--force` - Overwrite existing configurations
74
+ - `--only-mcp` - Only configure tools with native MCP support
75
+ - `--only-cli` - Only configure CLI tools
76
+ - `--only-ide` - Only configure IDE apps
77
+
78
+ ### `npx @contextos/setup list`
79
+
80
+ List all detected AI tools and their status.
81
+
82
+ ```bash
83
+ npx @contextos/setup list
84
+
85
+ # Output:
86
+ # Tool Type MCP Support Status
87
+ # ────────────────────────────────────────────────────────────
88
+ # 🖥️ Claude Desktop IDE MCP Ready
89
+ # 🖥️ Cursor IDE MCP Configured
90
+ # ⌨️ Claude Code CLI CLI MCP Ready
91
+ # ⌨️ Codex CLI CLI Wrapper Ready
92
+ ```
93
+
94
+ ### `npx @contextos/setup configure <tool>`
95
+
96
+ Configure a specific tool.
97
+
98
+ ```bash
99
+ npx @contextos/setup configure cursor
100
+ npx @contextos/setup configure codex --force
101
+ ```
102
+
103
+ ### `npx @contextos/setup hook`
104
+
105
+ Generate shell hook for automatic context updates.
106
+
107
+ ```bash
108
+ npx @contextos/setup hook
109
+
110
+ # Output shell script to add to ~/.bashrc or ~/.zshrc
111
+ ```
112
+
113
+ ### `npx @contextos/setup status`
114
+
115
+ Show integration status overview.
116
+
117
+ ## 🔧 How It Works
118
+
119
+ ### For Native MCP Tools (Claude, Cursor, Windsurf)
120
+
121
+ We inject configuration directly into the tool's config file:
122
+
123
+ **Claude Desktop** (`~/.config/claude/claude_desktop_config.json`):
124
+ ```json
125
+ {
126
+ "mcpServers": {
127
+ "contextos": {
128
+ "command": "npx",
129
+ "args": ["-y", "@contextos/mcp"]
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ **Cursor** (`~/.config/Cursor/User/settings.json`):
136
+ ```json
137
+ {
138
+ "mcp.servers": {
139
+ "contextos": {
140
+ "command": "npx @contextos/mcp",
141
+ "cwd": "${workspaceFolder}"
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### For Wrapper Tools (Codex, Gemini CLI)
148
+
149
+ We create wrapper scripts in `~/.local/bin/`:
150
+
151
+ ```bash
152
+ # ~/.local/bin/codex-ctx
153
+ #!/bin/bash
154
+ # ContextOS wrapper for Codex CLI
155
+
156
+ # Build context if in a ContextOS project
157
+ if [ -d ".contextos" ]; then
158
+ npx @contextos/mcp --build 2>/dev/null
159
+ fi
160
+
161
+ # Run original command with context
162
+ codex "$@" --system-prompt "$(cat .contextos/cache/last-context.md)"
163
+ ```
164
+
165
+ ## 📦 After Setup
166
+
167
+ 1. **Restart your IDE(s)** - Configuration changes require restart
168
+ 2. **Navigate to a project** - `cd your-project`
169
+ 3. **Initialize ContextOS** - `npx @contextos/cli init`
170
+ 4. **Use AI with context!** - Your AI tools now have optimized context
171
+
172
+ ## 🔍 Troubleshooting
173
+
174
+ ### Tool not detected?
175
+
176
+ Make sure the tool is installed and has created its config directory:
177
+
178
+ ```bash
179
+ # Check if config directory exists
180
+ ls ~/.config/Cursor/ # Linux/macOS
181
+ dir %APPDATA%\Cursor\ # Windows
182
+ ```
183
+
184
+ ### MCP not working?
185
+
186
+ 1. Check tool's MCP settings
187
+ 2. Verify `npx @contextos/mcp` runs correctly
188
+ 3. Check logs in tool's developer console
189
+
190
+ ### Need to reconfigure?
191
+
192
+ ```bash
193
+ npx @contextos/setup configure cursor --force
194
+ ```
195
+
196
+ ## 🤝 Contributing
197
+
198
+ Found a tool we should support? Open an issue or PR!
199
+
200
+ ## 📄 License
201
+
202
+ MIT © ContextOS Team
package/dist/index.js CHANGED
@@ -10,38 +10,48 @@ import inquirer from "inquirer";
10
10
  import { existsSync } from "fs";
11
11
  import { homedir, platform } from "os";
12
12
  import { join } from "path";
13
- function detectClaude() {
13
+ import { execSync } from "child_process";
14
+ function commandExists(cmd) {
15
+ try {
16
+ const isWin = platform() === "win32";
17
+ const checkCmd = isWin ? `where ${cmd}` : `which ${cmd}`;
18
+ execSync(checkCmd, { stdio: "ignore" });
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+ function getConfigDir(appName, subPath = "") {
14
25
  const os = platform();
15
- let configPath;
26
+ let base;
16
27
  if (os === "win32") {
17
- configPath = join(homedir(), "AppData", "Roaming", "Claude", "claude_desktop_config.json");
28
+ base = join(homedir(), "AppData", "Roaming", appName);
18
29
  } else if (os === "darwin") {
19
- configPath = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
30
+ base = join(homedir(), "Library", "Application Support", appName);
20
31
  } else {
21
- configPath = join(homedir(), ".config", "claude", "claude_desktop_config.json");
32
+ base = join(homedir(), ".config", appName.toLowerCase());
22
33
  }
34
+ return subPath ? join(base, subPath) : base;
35
+ }
36
+ function detectClaudeDesktop() {
37
+ const configPath = getConfigDir("Claude", "claude_desktop_config.json");
23
38
  const configDir = join(configPath, "..");
24
39
  const isInstalled = existsSync(configDir) || existsSync(configPath);
25
40
  if (!isInstalled) return null;
26
41
  return {
27
- id: "claude",
42
+ id: "claude-desktop",
28
43
  name: "Claude Desktop",
29
44
  configPath,
30
45
  configType: "json",
31
46
  hasExistingConfig: existsSync(configPath),
32
- mcpKey: "mcpServers"
47
+ mcpKey: "mcpServers",
48
+ toolType: "ide",
49
+ mcpSupport: "native",
50
+ installMethod: "app"
33
51
  };
34
52
  }
35
53
  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
- }
54
+ const configPath = getConfigDir("Cursor", join("User", "settings.json"));
45
55
  const configDir = join(configPath, "..");
46
56
  const isInstalled = existsSync(configDir);
47
57
  if (!isInstalled) return null;
@@ -51,19 +61,14 @@ function detectCursor() {
51
61
  configPath,
52
62
  configType: "json",
53
63
  hasExistingConfig: existsSync(configPath),
54
- mcpKey: "mcp.servers"
64
+ mcpKey: "mcp.servers",
65
+ toolType: "ide",
66
+ mcpSupport: "native",
67
+ installMethod: "app"
55
68
  };
56
69
  }
57
70
  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
- }
71
+ const configPath = getConfigDir("Windsurf", join("User", "settings.json"));
67
72
  const configDir = join(configPath, "..");
68
73
  const isInstalled = existsSync(configDir);
69
74
  if (!isInstalled) return null;
@@ -73,19 +78,14 @@ function detectWindsurf() {
73
78
  configPath,
74
79
  configType: "json",
75
80
  hasExistingConfig: existsSync(configPath),
76
- mcpKey: "mcp.servers"
81
+ mcpKey: "mcp.servers",
82
+ toolType: "ide",
83
+ mcpSupport: "native",
84
+ installMethod: "app"
77
85
  };
78
86
  }
79
87
  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
- }
88
+ const configPath = getConfigDir("Code", join("User", "settings.json"));
89
89
  const configDir = join(configPath, "..");
90
90
  const isInstalled = existsSync(configDir);
91
91
  if (!isInstalled) return null;
@@ -95,64 +95,186 @@ function detectVSCode() {
95
95
  configPath,
96
96
  configType: "json",
97
97
  hasExistingConfig: existsSync(configPath),
98
- mcpKey: "mcp.servers"
98
+ mcpKey: "mcp.servers",
99
+ toolType: "ide",
100
+ mcpSupport: "extension",
101
+ // VS Code uses Continue.dev extension for MCP
102
+ installMethod: "app"
99
103
  };
100
104
  }
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
- }
105
+ function detectKiloCode() {
106
+ const configPath = getConfigDir("KiloCode", join("User", "settings.json"));
109
107
  const configDir = join(configPath, "..");
110
108
  const isInstalled = existsSync(configDir);
111
109
  if (!isInstalled) return null;
110
+ return {
111
+ id: "kilocode",
112
+ name: "Kilo Code",
113
+ configPath,
114
+ configType: "json",
115
+ hasExistingConfig: existsSync(configPath),
116
+ mcpKey: "mcp.servers",
117
+ toolType: "ide",
118
+ mcpSupport: "native",
119
+ installMethod: "app"
120
+ };
121
+ }
122
+ function detectClaudeCodeCLI() {
123
+ const hasCommand = commandExists("claude");
124
+ const configPath = join(homedir(), ".claude", "mcp.json");
125
+ const configDir = join(configPath, "..");
126
+ const hasConfig = existsSync(configDir);
127
+ if (!hasCommand && !hasConfig) return null;
128
+ return {
129
+ id: "claude-code",
130
+ name: "Claude Code CLI",
131
+ configPath,
132
+ configType: "json",
133
+ hasExistingConfig: existsSync(configPath),
134
+ mcpKey: "mcpServers",
135
+ toolType: "cli",
136
+ mcpSupport: "native",
137
+ installMethod: hasCommand ? "binary" : "npm"
138
+ };
139
+ }
140
+ function detectCodexCLI() {
141
+ const hasCommand = commandExists("codex");
142
+ const configPath = join(homedir(), ".codex", "config.json");
143
+ const configDir = join(configPath, "..");
144
+ const hasConfig = existsSync(configDir);
145
+ if (!hasCommand && !hasConfig) return null;
112
146
  return {
113
147
  id: "codex",
114
148
  name: "OpenAI Codex CLI",
115
149
  configPath,
116
150
  configType: "json",
117
151
  hasExistingConfig: existsSync(configPath),
118
- mcpKey: "mcpServers"
152
+ mcpKey: "mcpServers",
153
+ toolType: "cli",
154
+ mcpSupport: "wrapper",
155
+ // Need to wrap with context injection
156
+ installMethod: hasCommand ? "npm" : void 0
157
+ };
158
+ }
159
+ function detectGeminiCLI() {
160
+ const hasCommand = commandExists("gemini");
161
+ const configPath = join(homedir(), ".gemini", "config.json");
162
+ const configDir = join(configPath, "..");
163
+ const hasConfig = existsSync(configDir);
164
+ if (!hasCommand && !hasConfig) return null;
165
+ return {
166
+ id: "gemini-cli",
167
+ name: "Gemini CLI",
168
+ configPath,
169
+ configType: "json",
170
+ hasExistingConfig: existsSync(configPath),
171
+ mcpKey: "mcpServers",
172
+ toolType: "cli",
173
+ mcpSupport: "wrapper",
174
+ // Need to wrap with context injection
175
+ installMethod: hasCommand ? "npm" : void 0
176
+ };
177
+ }
178
+ function detectOpenCodeCLI() {
179
+ const hasCommand = commandExists("opencode");
180
+ const configPath = join(homedir(), ".opencode", "config.json");
181
+ const configDir = join(configPath, "..");
182
+ const hasConfig = existsSync(configDir);
183
+ if (!hasCommand && !hasConfig) return null;
184
+ return {
185
+ id: "opencode",
186
+ name: "OpenCode CLI",
187
+ configPath,
188
+ configType: "json",
189
+ hasExistingConfig: existsSync(configPath),
190
+ mcpKey: "mcpServers",
191
+ toolType: "cli",
192
+ mcpSupport: "wrapper",
193
+ installMethod: hasCommand ? "binary" : void 0
194
+ };
195
+ }
196
+ function detectWarp() {
197
+ const os = platform();
198
+ let configPath;
199
+ if (os === "darwin") {
200
+ configPath = join(homedir(), ".warp", "config.yaml");
201
+ } else {
202
+ configPath = join(homedir(), ".warp", "config.yaml");
203
+ }
204
+ const configDir = join(configPath, "..");
205
+ const hasWarp = existsSync(configDir) || commandExists("warp");
206
+ if (!hasWarp) return null;
207
+ return {
208
+ id: "warp",
209
+ name: "Warp Terminal",
210
+ configPath,
211
+ configType: "yaml",
212
+ hasExistingConfig: existsSync(configPath),
213
+ mcpKey: "ai.context_providers",
214
+ toolType: "terminal",
215
+ mcpSupport: "wrapper",
216
+ // Needs shell hook integration
217
+ installMethod: "app"
119
218
  };
120
219
  }
121
220
  async function detectIDEs() {
122
221
  const detectors = [
123
- detectClaude,
222
+ // Desktop Apps / IDEs
223
+ detectClaudeDesktop,
124
224
  detectCursor,
125
225
  detectWindsurf,
126
226
  detectVSCode,
127
- detectCodex
227
+ detectKiloCode,
228
+ // CLI Tools
229
+ detectClaudeCodeCLI,
230
+ detectCodexCLI,
231
+ detectGeminiCLI,
232
+ detectOpenCodeCLI,
233
+ // Terminals
234
+ detectWarp
128
235
  ];
129
236
  const results = [];
130
237
  for (const detect of detectors) {
131
- const ide = detect();
132
- if (ide) {
133
- results.push(ide);
238
+ try {
239
+ const ide = detect();
240
+ if (ide) {
241
+ results.push(ide);
242
+ }
243
+ } catch {
134
244
  }
135
245
  }
136
246
  return results;
137
247
  }
248
+ async function detectCLITools() {
249
+ const all = await detectIDEs();
250
+ return all.filter((t) => t.toolType === "cli");
251
+ }
252
+ async function detectIDEApps() {
253
+ const all = await detectIDEs();
254
+ return all.filter((t) => t.toolType === "ide");
255
+ }
256
+ async function detectMCPNativeTools() {
257
+ const all = await detectIDEs();
258
+ return all.filter((t) => t.mcpSupport === "native");
259
+ }
138
260
 
139
261
  // 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 = {
262
+ import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
263
+ import { dirname, join as join2 } from "path";
264
+ import { homedir as homedir2, platform as platform2 } from "os";
265
+ function generateClaudeMCPConfig(_projectPath) {
266
+ return {
144
267
  command: "npx",
145
- args: ["@contextos/mcp"]
268
+ args: ["-y", "@contextos/mcp"]
146
269
  };
147
- if (projectPath) {
148
- return {
149
- ...baseConfig,
150
- cwd: projectPath
151
- };
152
- }
153
- return baseConfig;
154
270
  }
155
- async function injectClaude(ide, options) {
271
+ function generateVSCodeMCPConfig(projectPath) {
272
+ return {
273
+ command: "npx @contextos/mcp",
274
+ cwd: projectPath || "${workspaceFolder}"
275
+ };
276
+ }
277
+ async function injectClaudeStyle(ide, options) {
156
278
  let config = {};
157
279
  if (existsSync2(ide.configPath)) {
158
280
  try {
@@ -171,7 +293,7 @@ async function injectClaude(ide, options) {
171
293
  }
172
294
  config.mcpServers = {
173
295
  ...mcpServers,
174
- contextos: generateMCPConfig(options.projectPath)
296
+ contextos: generateClaudeMCPConfig(options.projectPath)
175
297
  };
176
298
  const configDir = dirname(ide.configPath);
177
299
  if (!existsSync2(configDir)) {
@@ -181,7 +303,7 @@ async function injectClaude(ide, options) {
181
303
  return {
182
304
  ide: ide.name,
183
305
  success: true,
184
- message: "Configuration added successfully",
306
+ message: "MCP configuration added",
185
307
  configPath: ide.configPath
186
308
  };
187
309
  }
@@ -214,70 +336,343 @@ async function injectVSCodeStyle(ide, options) {
214
336
  }
215
337
  current[lastKey] = {
216
338
  ...servers,
217
- contextos: {
218
- command: "npx @contextos/mcp",
219
- cwd: options.projectPath || "${workspaceFolder}"
220
- }
339
+ contextos: generateVSCodeMCPConfig(options.projectPath)
221
340
  };
222
341
  const configDir = dirname(ide.configPath);
223
342
  if (!existsSync2(configDir)) {
224
343
  mkdirSync(configDir, { recursive: true });
225
344
  }
226
345
  writeFileSync(ide.configPath, JSON.stringify(config, null, 2));
346
+ const nextSteps = [];
347
+ if (ide.id === "vscode" && ide.mcpSupport === "extension") {
348
+ nextSteps.push("Install Continue.dev extension: code --install-extension continue.continue");
349
+ }
227
350
  return {
228
351
  ide: ide.name,
229
352
  success: true,
230
- message: "Configuration added successfully",
231
- configPath: ide.configPath
353
+ message: "MCP configuration added",
354
+ configPath: ide.configPath,
355
+ nextSteps: nextSteps.length > 0 ? nextSteps : void 0
232
356
  };
233
357
  }
234
- async function injectConfig(ide, options = {}) {
358
+ function getWrapperDir() {
359
+ const os = platform2();
360
+ if (os === "win32") {
361
+ return join2(homedir2(), ".contextos", "bin");
362
+ }
363
+ return join2(homedir2(), ".local", "bin");
364
+ }
365
+ function generateWrapperScript(toolName, originalCommand) {
366
+ const os = platform2();
367
+ if (os === "win32") {
368
+ return `#!/usr/bin/env pwsh
369
+ # ContextOS wrapper for ${toolName}
370
+ # This script injects optimized context before running the tool
371
+
372
+ $ErrorActionPreference = "SilentlyContinue"
373
+
374
+ # Build context if in a ContextOS project
375
+ $contextFile = ".contextos/cache/last-context.md"
376
+ if (Test-Path ".contextos") {
377
+ # Refresh context
378
+ npx @contextos/mcp --build 2>$null | Out-Null
379
+ }
380
+
381
+ # Build system prompt with context
382
+ $systemPrompt = ""
383
+ if (Test-Path $contextFile) {
384
+ $systemPrompt = Get-Content $contextFile -Raw
385
+ }
386
+
387
+ # Run original command with context
388
+ if ($systemPrompt) {
389
+ & ${originalCommand} @args --system-prompt $systemPrompt
390
+ } else {
391
+ & ${originalCommand} @args
392
+ }
393
+ `;
394
+ } else {
395
+ return `#!/bin/bash
396
+ # ContextOS wrapper for ${toolName}
397
+ # This script injects optimized context before running the tool
398
+
399
+ set -e
400
+
401
+ # Build context if in a ContextOS project
402
+ CONTEXT_FILE=".contextos/cache/last-context.md"
403
+ if [ -d ".contextos" ]; then
404
+ # Refresh context silently
405
+ npx @contextos/mcp --build 2>/dev/null || true
406
+ fi
407
+
408
+ # Build system prompt with context
409
+ SYSTEM_PROMPT=""
410
+ if [ -f "$CONTEXT_FILE" ]; then
411
+ SYSTEM_PROMPT=$(cat "$CONTEXT_FILE")
412
+ fi
413
+
414
+ # Run original command with context
415
+ if [ -n "$SYSTEM_PROMPT" ]; then
416
+ ${originalCommand} "$@" --system-prompt "$SYSTEM_PROMPT"
417
+ else
418
+ ${originalCommand} "$@"
419
+ fi
420
+ `;
421
+ }
422
+ }
423
+ async function injectWrapper(ide, options) {
424
+ const wrapperDir = getWrapperDir();
425
+ const os = platform2();
426
+ const ext = os === "win32" ? ".ps1" : "";
427
+ let wrapperName = "";
428
+ let originalCommand = "";
235
429
  switch (ide.id) {
236
- case "claude":
237
- return injectClaude(ide, options);
238
- case "cursor":
239
- case "windsurf":
240
- case "vscode":
241
430
  case "codex":
431
+ wrapperName = `codex-ctx${ext}`;
432
+ originalCommand = "codex";
433
+ break;
434
+ case "gemini-cli":
435
+ wrapperName = `gemini-ctx${ext}`;
436
+ originalCommand = "gemini";
437
+ break;
438
+ case "opencode":
439
+ wrapperName = `opencode-ctx${ext}`;
440
+ originalCommand = "opencode";
441
+ break;
442
+ default:
443
+ return {
444
+ ide: ide.name,
445
+ success: false,
446
+ message: `Wrapper not implemented for ${ide.id}`
447
+ };
448
+ }
449
+ const wrapperPath = join2(wrapperDir, wrapperName);
450
+ if (existsSync2(wrapperPath) && !options.force) {
451
+ return {
452
+ ide: ide.name,
453
+ success: false,
454
+ message: "Wrapper already exists (use --force to overwrite)"
455
+ };
456
+ }
457
+ if (!existsSync2(wrapperDir)) {
458
+ mkdirSync(wrapperDir, { recursive: true });
459
+ }
460
+ const script = generateWrapperScript(ide.name, originalCommand);
461
+ writeFileSync(wrapperPath, script);
462
+ if (os !== "win32") {
463
+ chmodSync(wrapperPath, "755");
464
+ }
465
+ const shellConfigFile = os === "win32" ? "PowerShell Profile" : "~/.bashrc or ~/.zshrc";
466
+ return {
467
+ ide: ide.name,
468
+ success: true,
469
+ message: `Wrapper script created: ${wrapperName}`,
470
+ configPath: wrapperPath,
471
+ nextSteps: [
472
+ `Add ${wrapperDir} to your PATH`,
473
+ `Update your ${shellConfigFile}`,
474
+ `Use '${wrapperName.replace(ext, "")}' instead of '${originalCommand}'`
475
+ ]
476
+ };
477
+ }
478
+ async function injectWarp(ide, _options) {
479
+ return {
480
+ ide: ide.name,
481
+ success: true,
482
+ message: "Warp integration guidance provided",
483
+ nextSteps: [
484
+ "Warp AI integration is experimental",
485
+ 'Use shell hook: eval "$(npx @contextos/mcp --hook)"',
486
+ "Or run: ctx build before Warp AI commands"
487
+ ]
488
+ };
489
+ }
490
+ async function injectConfig(ide, options = {}) {
491
+ switch (ide.mcpSupport) {
492
+ case "native":
493
+ if (ide.mcpKey === "mcpServers") {
494
+ return injectClaudeStyle(ide, options);
495
+ } else {
496
+ return injectVSCodeStyle(ide, options);
497
+ }
498
+ case "wrapper":
499
+ if (ide.toolType === "terminal") {
500
+ return injectWarp(ide, options);
501
+ }
502
+ return injectWrapper(ide, options);
503
+ case "extension":
242
504
  return injectVSCodeStyle(ide, options);
243
505
  default:
244
506
  return {
245
507
  ide: ide.name,
246
508
  success: false,
247
- message: `Unknown IDE: ${ide.id}`
509
+ message: `Unknown MCP support type: ${ide.mcpSupport}`
248
510
  };
249
511
  }
250
512
  }
513
+ async function injectAll(ides, options = {}) {
514
+ const results = [];
515
+ for (const ide of ides) {
516
+ try {
517
+ const result = await injectConfig(ide, options);
518
+ results.push(result);
519
+ } catch (error) {
520
+ results.push({
521
+ ide: ide.name,
522
+ success: false,
523
+ message: error instanceof Error ? error.message : "Unknown error"
524
+ });
525
+ }
526
+ }
527
+ return results;
528
+ }
529
+ function generateShellHook() {
530
+ const os = platform2();
531
+ if (os === "win32") {
532
+ return `
533
+ # ContextOS PowerShell Hook
534
+ # Add to your $PROFILE
535
+
536
+ function ctx-hook {
537
+ if (Test-Path ".contextos") {
538
+ npx @contextos/mcp --build 2>$null | Out-Null
539
+ }
540
+ }
541
+
542
+ # Auto-run on directory change
543
+ $ExecutionContext.SessionState.InvokeCommand.PostCommandLookupAction = {
544
+ param($command)
545
+ if ($command -eq "cd" -or $command -eq "Set-Location") {
546
+ ctx-hook
547
+ }
548
+ }
549
+ `;
550
+ } else {
551
+ return `
552
+ # ContextOS Shell Hook
553
+ # Add to your ~/.bashrc or ~/.zshrc
554
+
555
+ ctx_hook() {
556
+ if [ -d ".contextos" ]; then
557
+ npx @contextos/mcp --build 2>/dev/null &
558
+ fi
559
+ }
560
+
561
+ # For bash
562
+ if [ -n "$BASH_VERSION" ]; then
563
+ PROMPT_COMMAND="ctx_hook;$PROMPT_COMMAND"
564
+ fi
565
+
566
+ # For zsh
567
+ if [ -n "$ZSH_VERSION" ]; then
568
+ autoload -Uz add-zsh-hook
569
+ add-zsh-hook chpwd ctx_hook
570
+ fi
571
+ `;
572
+ }
573
+ }
251
574
 
252
575
  // src/index.ts
253
576
  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();
577
+ function getToolTypeIcon(tool) {
578
+ switch (tool.toolType) {
579
+ case "ide":
580
+ return "\u{1F5A5}\uFE0F";
581
+ case "cli":
582
+ return "\u2328\uFE0F";
583
+ case "terminal":
584
+ return "\u{1F4BB}";
585
+ default:
586
+ return "\u{1F4E6}";
587
+ }
588
+ }
589
+ function getMCPSupportBadge(tool) {
590
+ switch (tool.mcpSupport) {
591
+ case "native":
592
+ return chalk.green("MCP");
593
+ case "wrapper":
594
+ return chalk.yellow("Wrapper");
595
+ case "extension":
596
+ return chalk.blue("Extension");
597
+ default:
598
+ return chalk.gray("Unknown");
599
+ }
600
+ }
601
+ function printToolList(tools) {
602
+ console.log();
603
+ console.log(chalk.gray(" Tool Type MCP Support Status"));
604
+ console.log(chalk.gray(" " + "\u2500".repeat(68)));
605
+ tools.forEach((tool) => {
606
+ const icon = getToolTypeIcon(tool);
607
+ const status = tool.hasExistingConfig ? chalk.yellow("Configured") : chalk.green("Ready");
608
+ const mcpBadge = getMCPSupportBadge(tool);
609
+ const typeLabel = tool.toolType.toUpperCase().padEnd(10);
610
+ console.log(` ${icon} ${tool.name.padEnd(22)} ${typeLabel} ${mcpBadge.padEnd(20)} ${status}`);
611
+ });
612
+ console.log();
613
+ }
614
+ program.name("contextos-setup").description("One-click ContextOS setup for all your AI coding tools").version("0.2.0");
615
+ program.command("auto").description("Automatically detect and configure all AI tools").option("--dry-run", "Show what would be configured without making changes").option("--force", "Overwrite existing configurations").option("--only-mcp", "Only configure tools with native MCP support").option("--only-cli", "Only configure CLI tools").option("--only-ide", "Only configure IDE apps").action(async (options) => {
616
+ console.log(chalk.cyan.bold("\n\u{1F680} ContextOS Universal Setup\n"));
617
+ const spinner = ora("Detecting AI coding tools...").start();
258
618
  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"));
619
+ let tools;
620
+ if (options.onlyMcp) {
621
+ tools = await detectMCPNativeTools();
622
+ } else if (options.onlyCli) {
623
+ tools = await detectCLITools();
624
+ } else if (options.onlyIde) {
625
+ tools = await detectIDEApps();
626
+ } else {
627
+ tools = await detectIDEs();
628
+ }
629
+ if (tools.length === 0) {
630
+ spinner.warn("No supported AI tools detected");
631
+ console.log(chalk.gray("\nSupported tools:"));
632
+ console.log(chalk.gray(" IDEs: Claude Desktop, Cursor, Windsurf, VS Code, Kilo Code"));
633
+ console.log(chalk.gray(" CLI: Claude Code, Codex, Gemini CLI, OpenCode"));
634
+ console.log(chalk.gray(" Terminal: Warp\n"));
263
635
  return;
264
636
  }
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
- });
637
+ spinner.succeed(`Found ${tools.length} AI tool(s)`);
638
+ const ides = tools.filter((t) => t.toolType === "ide");
639
+ const clis = tools.filter((t) => t.toolType === "cli");
640
+ const terminals = tools.filter((t) => t.toolType === "terminal");
641
+ if (ides.length > 0) {
642
+ console.log(chalk.cyan("\n\u{1F5A5}\uFE0F IDEs:"));
643
+ ides.forEach((t) => {
644
+ const badge = getMCPSupportBadge(t);
645
+ const status = t.hasExistingConfig ? chalk.yellow("(configured)") : "";
646
+ console.log(` ${t.name} ${badge} ${status}`);
647
+ });
648
+ }
649
+ if (clis.length > 0) {
650
+ console.log(chalk.cyan("\n\u2328\uFE0F CLI Tools:"));
651
+ clis.forEach((t) => {
652
+ const badge = getMCPSupportBadge(t);
653
+ const status = t.hasExistingConfig ? chalk.yellow("(configured)") : "";
654
+ console.log(` ${t.name} ${badge} ${status}`);
655
+ });
656
+ }
657
+ if (terminals.length > 0) {
658
+ console.log(chalk.cyan("\n\u{1F4BB} Terminals:"));
659
+ terminals.forEach((t) => {
660
+ const badge = getMCPSupportBadge(t);
661
+ const status = t.hasExistingConfig ? chalk.yellow("(configured)") : "";
662
+ console.log(` ${t.name} ${badge} ${status}`);
663
+ });
664
+ }
271
665
  if (options.dryRun) {
272
666
  console.log(chalk.yellow("\n[Dry Run] Would configure:"));
273
- ides.forEach((ide) => console.log(` - ${ide.name}`));
667
+ tools.forEach((t) => console.log(` - ${t.name} (${t.mcpSupport})`));
274
668
  return;
275
669
  }
670
+ console.log();
276
671
  const { proceed } = await inquirer.prompt([
277
672
  {
278
673
  type: "confirm",
279
674
  name: "proceed",
280
- message: "Configure ContextOS for these IDEs?",
675
+ message: "Configure ContextOS for these tools?",
281
676
  default: true
282
677
  }
283
678
  ]);
@@ -286,75 +681,84 @@ program.command("auto").description("Automatically detect and configure all IDEs
286
681
  return;
287
682
  }
288
683
  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}`);
684
+ const results = await injectAll(tools, { force: options.force });
685
+ for (const result of results) {
686
+ if (result.success) {
687
+ console.log(chalk.green(` \u2713 ${result.ide}: ${result.message}`));
688
+ if (result.nextSteps && result.nextSteps.length > 0) {
689
+ result.nextSteps.forEach((step) => {
690
+ console.log(chalk.gray(` \u2192 ${step}`));
691
+ });
299
692
  }
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
- });
693
+ } else {
694
+ console.log(chalk.yellow(` \u26A0 ${result.ide}: ${result.message}`));
307
695
  }
308
696
  }
309
697
  const successful = results.filter((r) => r.success).length;
698
+ const native = results.filter((r) => r.success && !r.nextSteps).length;
699
+ const needsSteps = results.filter((r) => r.success && r.nextSteps).length;
310
700
  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");
701
+ \u2705 Setup complete: ${successful}/${tools.length} tools configured`));
702
+ if (native > 0) {
703
+ console.log(chalk.cyan(` ${native} tools are ready to use`));
704
+ }
705
+ if (needsSteps > 0) {
706
+ console.log(chalk.yellow(` ${needsSteps} tools need additional steps (see above)`));
318
707
  }
708
+ console.log(chalk.cyan("\n\u{1F4CB} Next steps:"));
709
+ console.log(" 1. Restart your IDE(s) and terminal(s)");
710
+ console.log(" 2. Navigate to a project: cd your-project");
711
+ console.log(" 3. Initialize ContextOS: npx @contextos/cli init");
712
+ console.log(" 4. Start using AI with optimized context!\n");
319
713
  } catch (error) {
320
714
  spinner.fail("Setup failed");
321
715
  console.error(chalk.red(error instanceof Error ? error.message : "Unknown error"));
322
716
  process.exit(1);
323
717
  }
324
718
  });
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();
719
+ program.command("list").description("List all detected AI tools and their status").option("--json", "Output as JSON").action(async (options) => {
720
+ const spinner = ora("Scanning for AI tools...").start();
721
+ const tools = await detectIDEs();
329
722
  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"));
723
+ if (options.json) {
724
+ console.log(JSON.stringify(tools, null, 2));
333
725
  return;
334
726
  }
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();
727
+ console.log(chalk.cyan.bold("\n\u{1F50D} Detected AI Tools\n"));
728
+ if (tools.length === 0) {
729
+ console.log(chalk.yellow("No AI tools found.\n"));
730
+ console.log(chalk.gray("Supported tools:"));
731
+ console.log(chalk.gray(" IDEs: Claude Desktop, Cursor, Windsurf, VS Code, Kilo Code"));
732
+ console.log(chalk.gray(" CLI: Claude Code, Codex, Gemini CLI, OpenCode"));
733
+ console.log(chalk.gray(" Terminal: Warp\n"));
734
+ return;
735
+ }
736
+ printToolList(tools);
343
737
  });
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) {
738
+ program.command("configure <tool>").description("Configure a specific tool").option("--force", "Overwrite existing configuration").action(async (toolName, options) => {
739
+ const tools = await detectIDEs();
740
+ const tool = tools.find(
741
+ (t) => t.id === toolName.toLowerCase() || t.name.toLowerCase().includes(toolName.toLowerCase())
742
+ );
743
+ if (!tool) {
348
744
  console.log(chalk.red(`
349
- IDE not found: ${ideName}`));
350
- console.log(chalk.gray("Available: claude, cursor, windsurf, vscode\n"));
745
+ \u274C Tool not found: ${toolName}`));
746
+ console.log(chalk.gray("\nAvailable tools:"));
747
+ tools.forEach((t) => console.log(` - ${t.id} (${t.name})`));
748
+ console.log();
351
749
  process.exit(1);
352
750
  }
353
- const spinner = ora(`Configuring ${ide.name}...`).start();
751
+ const spinner = ora(`Configuring ${tool.name}...`).start();
354
752
  try {
355
- const result = await injectConfig(ide, { force: options.force });
753
+ const result = await injectConfig(tool, { force: options.force });
356
754
  if (result.success) {
357
- spinner.succeed(result.message);
755
+ spinner.succeed(`${tool.name}: ${result.message}`);
756
+ if (result.nextSteps) {
757
+ console.log(chalk.cyan("\nNext steps:"));
758
+ result.nextSteps.forEach((step, i) => {
759
+ console.log(` ${i + 1}. ${step}`);
760
+ });
761
+ }
358
762
  } else {
359
763
  spinner.warn(result.message);
360
764
  }
@@ -362,13 +766,25 @@ IDE not found: ${ideName}`));
362
766
  spinner.fail(error instanceof Error ? error.message : "Failed");
363
767
  process.exit(1);
364
768
  }
769
+ console.log();
770
+ });
771
+ program.command("hook").description("Generate shell hook for automatic context updates").option("--install", "Automatically install hook to shell config").action(async (options) => {
772
+ const hook = generateShellHook();
773
+ if (options.install) {
774
+ console.log(chalk.yellow("Auto-install not yet implemented.\n"));
775
+ console.log(chalk.cyan("Add this to your shell config:\n"));
776
+ } else {
777
+ console.log(chalk.cyan.bold("\n\u{1FA9D} ContextOS Shell Hook\n"));
778
+ console.log(chalk.gray("Add this to your ~/.bashrc, ~/.zshrc, or PowerShell $PROFILE:\n"));
779
+ }
780
+ console.log(hook);
365
781
  });
366
- program.command("uninstall").description("Remove ContextOS from all configured IDEs").action(async () => {
782
+ program.command("uninstall").description("Remove ContextOS from all configured tools").action(async () => {
367
783
  const { confirm } = await inquirer.prompt([
368
784
  {
369
785
  type: "confirm",
370
786
  name: "confirm",
371
- message: "Remove ContextOS configuration from all IDEs?",
787
+ message: "Remove ContextOS configuration from all tools?",
372
788
  default: false
373
789
  }
374
790
  ]);
@@ -376,12 +792,32 @@ program.command("uninstall").description("Remove ContextOS from all configured I
376
792
  console.log(chalk.gray("Cancelled.\n"));
377
793
  return;
378
794
  }
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
- }
795
+ const tools = await detectIDEs();
796
+ const configured = tools.filter((t) => t.hasExistingConfig);
797
+ if (configured.length === 0) {
798
+ console.log(chalk.gray("No configured tools found.\n"));
799
+ return;
800
+ }
801
+ console.log(chalk.yellow("\nRemoving ContextOS configuration...\n"));
802
+ for (const tool of configured) {
803
+ console.log(chalk.gray(` Would remove from ${tool.name}`));
804
+ }
805
+ console.log(chalk.yellow("\nNote: Removal not yet fully implemented.\n"));
806
+ });
807
+ program.command("status").description("Show ContextOS integration status").action(async () => {
808
+ console.log(chalk.cyan.bold("\n\u{1F4CA} ContextOS Integration Status\n"));
809
+ const tools = await detectIDEs();
810
+ const configured = tools.filter((t) => t.hasExistingConfig);
811
+ const mcpNative = tools.filter((t) => t.mcpSupport === "native");
812
+ const needsWrapper = tools.filter((t) => t.mcpSupport === "wrapper");
813
+ console.log(` Total tools detected: ${chalk.bold(tools.length)}`);
814
+ console.log(` Already configured: ${chalk.green(configured.length)}`);
815
+ console.log(` MCP Native support: ${chalk.cyan(mcpNative.length)}`);
816
+ console.log(` Need wrapper scripts: ${chalk.yellow(needsWrapper.length)}`);
817
+ if (tools.length > 0) {
818
+ printToolList(tools);
384
819
  }
820
+ console.log(chalk.gray('Run "npx @contextos/setup" to configure all tools.\n'));
385
821
  });
386
822
  program.action(async () => {
387
823
  await program.commands.find((c) => c.name() === "auto")?.parseAsync(process.argv);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contextos/setup",
3
- "version": "0.1.0",
4
- "description": "One-click setup for ContextOS - automatically configure all your IDEs",
3
+ "version": "0.2.1",
4
+ "description": "Universal setup for ContextOS - one command to configure all AI coding tools (Claude, Cursor, Codex, Gemini, Windsurf, VS Code, Kilo Code, Warp)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "contextos-setup": "./dist/index.js"
@@ -30,10 +30,18 @@
30
30
  "setup",
31
31
  "mcp",
32
32
  "claude",
33
+ "claude-code",
33
34
  "cursor",
34
35
  "windsurf",
36
+ "vscode",
37
+ "codex",
38
+ "gemini",
39
+ "opencode",
40
+ "warp",
41
+ "kilocode",
35
42
  "ai",
36
- "coding"
43
+ "coding",
44
+ "context"
37
45
  ],
38
46
  "scripts": {
39
47
  "build": "tsup src/index.ts --format esm --clean",