@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.
- package/README.md +202 -0
- package/dist/index.js +586 -150
- 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
|
+
[](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
|
-
|
|
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
|
|
26
|
+
let base;
|
|
16
27
|
if (os === "win32") {
|
|
17
|
-
|
|
28
|
+
base = join(homedir(), "AppData", "Roaming", appName);
|
|
18
29
|
} else if (os === "darwin") {
|
|
19
|
-
|
|
30
|
+
base = join(homedir(), "Library", "Application Support", appName);
|
|
20
31
|
} else {
|
|
21
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
102
|
-
const
|
|
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
|
-
|
|
222
|
+
// Desktop Apps / IDEs
|
|
223
|
+
detectClaudeDesktop,
|
|
124
224
|
detectCursor,
|
|
125
225
|
detectWindsurf,
|
|
126
226
|
detectVSCode,
|
|
127
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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
|
-
|
|
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:
|
|
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: "
|
|
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: "
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
-
|
|
260
|
-
if (
|
|
261
|
-
|
|
262
|
-
|
|
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 ${
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
}
|
|
301
|
-
|
|
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}/${
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
console.log(
|
|
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
|
|
326
|
-
|
|
327
|
-
const
|
|
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 (
|
|
331
|
-
console.log(
|
|
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.
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
console.log(
|
|
341
|
-
|
|
342
|
-
|
|
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 <
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
|
|
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
|
-
|
|
350
|
-
console.log(chalk.gray("
|
|
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 ${
|
|
751
|
+
const spinner = ora(`Configuring ${tool.name}...`).start();
|
|
354
752
|
try {
|
|
355
|
-
const result = await injectConfig(
|
|
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
|
|
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
|
|
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
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
|
4
|
-
"description": "
|
|
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",
|