claude-code-pack 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -48
- package/bin/claude-pack.mjs +2 -0
- package/claude-pack.config.json +67 -6
- package/package.json +2 -2
- package/src/install.mjs +197 -9
package/README.md
CHANGED
|
@@ -2,19 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
Portable Claude Code setup — installs your plugins, skills, MCP servers, and settings on any machine with a single command.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### From npm
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npx claude-pack install
|
|
10
|
+
npx claude-code-pack install
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
Or install globally:
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
npm install -g claude-pack
|
|
16
|
+
npm install -g claude-code-pack
|
|
17
|
+
claude-pack install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### From GitHub
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone git@github.com:lvtan71/claude-pack.git
|
|
24
|
+
cd claude-pack
|
|
25
|
+
npm link
|
|
15
26
|
claude-pack install
|
|
16
27
|
```
|
|
17
28
|
|
|
29
|
+
Or run directly without installing:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx github:lvtan71/claude-pack install
|
|
33
|
+
```
|
|
34
|
+
|
|
18
35
|
## What It Does
|
|
19
36
|
|
|
20
37
|
`claude-pack` reads `claude-pack.config.json` and sets up your Claude Code environment:
|
|
@@ -28,43 +45,63 @@ claude-pack install
|
|
|
28
45
|
|
|
29
46
|
Everything is fetched fresh from GitHub, so you always get the latest version.
|
|
30
47
|
|
|
31
|
-
##
|
|
48
|
+
## What's Included
|
|
32
49
|
|
|
33
|
-
|
|
50
|
+
### Plugins
|
|
34
51
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"repo": "thedotmack/claude-mem",
|
|
42
|
-
"enabled": true
|
|
43
|
-
}
|
|
44
|
-
],
|
|
45
|
-
"marketplaces": [
|
|
46
|
-
{ "name": "thedotmack", "repo": "thedotmack/claude-mem" }
|
|
47
|
-
],
|
|
48
|
-
"skills": [
|
|
49
|
-
{ "name": "my-skill", "source": "bundled" },
|
|
50
|
-
{ "name": "remote-skill", "source": "github", "repo": "user/repo" }
|
|
51
|
-
],
|
|
52
|
-
"mcpServers": {
|
|
53
|
-
"my-server": {
|
|
54
|
-
"command": "npx",
|
|
55
|
-
"args": ["-y", "@some/mcp-server"]
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"settings": {
|
|
59
|
-
"model": "opus"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
52
|
+
| Plugin | Source | Description |
|
|
53
|
+
|--------|--------|-------------|
|
|
54
|
+
| **claude-mem** | [thedotmack/claude-mem](https://github.com/thedotmack/claude-mem) | Persistent cross-session memory, smart code exploration (AST-based), phased implementation plans, and timeline reports |
|
|
55
|
+
| **understand-anything** | [Lum1104/Understand-Anything](https://github.com/Lum1104/Understand-Anything) | Codebase analysis, interactive knowledge graphs, architecture visualization, onboarding guides, and diff analysis |
|
|
56
|
+
|
|
57
|
+
### Marketplaces
|
|
63
58
|
|
|
64
|
-
|
|
59
|
+
| Marketplace | Source | Description |
|
|
60
|
+
|-------------|--------|-------------|
|
|
61
|
+
| **thedotmack** | [thedotmack/claude-mem](https://github.com/thedotmack/claude-mem) | Plugin marketplace for claude-mem and related tools |
|
|
62
|
+
| **understand-anything** | [Lum1104/Understand-Anything](https://github.com/Lum1104/Understand-Anything) | Codebase understanding, onboarding, and architecture analysis tools |
|
|
65
63
|
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
### Skills (Bundled)
|
|
65
|
+
|
|
66
|
+
| Skill | Description |
|
|
67
|
+
|-------|-------------|
|
|
68
|
+
| **cloud-devops** | Cloud infrastructure and DevOps workflows — AWS, Azure, GCP, Kubernetes, Terraform, CI/CD, monitoring |
|
|
69
|
+
| **fastapi** | FastAPI best practices, Pydantic models, dependency injection, streaming, and latest patterns |
|
|
70
|
+
| **senior-ml-engineer** | MLOps pipelines, model deployment, drift monitoring, RAG systems, LLM integration, cost optimization |
|
|
71
|
+
| **technical-writer** | Documentation, API references, guides, tutorials, and technical content creation |
|
|
72
|
+
|
|
73
|
+
### MCP Servers
|
|
74
|
+
|
|
75
|
+
| Server | Type | Source | Description |
|
|
76
|
+
|--------|------|--------|-------------|
|
|
77
|
+
| **linear** | HTTP | [mcp.linear.app](https://mcp.linear.app/mcp) | Linear project management — issues, projects, comments, milestones |
|
|
78
|
+
| **context7** | HTTP | [mcp.context7.com](https://mcp.context7.com/mcp) | Up-to-date library documentation and code examples |
|
|
79
|
+
| **fetch** | stdio | `mcp-server-fetch` (uvx) | Fetch web content for processing |
|
|
80
|
+
| **filesystem** | stdio | `@modelcontextprotocol/server-filesystem` (npx) | File system access (defaults to `$HOME`, configurable at install) |
|
|
81
|
+
| **sequential-thinking** | stdio | `@modelcontextprotocol/server-sequential-thinking` (npx) | Step-by-step reasoning and problem decomposition |
|
|
82
|
+
| **time** | stdio | `mcp-server-time` (uvx) | Current time and timezone operations |
|
|
83
|
+
|
|
84
|
+
> `$HOME` in MCP server args is automatically resolved to the user's home directory at install time.
|
|
85
|
+
> The **filesystem** server prompts you during install to confirm or customize the path. Works on Linux/WSL, macOS, and Windows.
|
|
86
|
+
|
|
87
|
+
### Settings
|
|
88
|
+
|
|
89
|
+
| Setting | Value |
|
|
90
|
+
|---------|-------|
|
|
91
|
+
| **Model (Claude Code)** | `opus` |
|
|
92
|
+
| **Model (Codex)** | `gpt-5.4` (reasoning effort: `xhigh`) |
|
|
93
|
+
| **Statusline** | Custom bash script with context window, 5h/7d rate limit bars, and color-coded usage indicators |
|
|
94
|
+
|
|
95
|
+
### Codex Integration
|
|
96
|
+
|
|
97
|
+
The installer also generates a `~/.codex/config.toml` that **reuses the same MCP servers and skills** as Claude Code — no duplicate configuration needed.
|
|
98
|
+
|
|
99
|
+
| What | How it's shared |
|
|
100
|
+
|------|----------------|
|
|
101
|
+
| **MCP servers** | Generated into `config.toml` from the same `mcpServers` in `claude-pack.config.json` |
|
|
102
|
+
| **Skills** | Codex `[[skills.config]]` points to `~/.claude/skills/` (the same directory Claude Code uses) |
|
|
103
|
+
|
|
104
|
+
This means when you add a new MCP server or skill to the pack, both Claude Code and Codex pick it up.
|
|
68
105
|
|
|
69
106
|
## Commands
|
|
70
107
|
|
|
@@ -78,32 +115,66 @@ claude-pack install --force # Overwrite existing installations
|
|
|
78
115
|
claude-pack install --skip-plugins # Skip plugin installation
|
|
79
116
|
claude-pack install --skip-skills # Skip skill installation
|
|
80
117
|
claude-pack install --skip-settings # Skip settings merge
|
|
118
|
+
claude-pack install --skip-codex # Skip Codex config generation
|
|
81
119
|
claude-pack install --dry-run # Preview without changes
|
|
82
120
|
```
|
|
83
121
|
|
|
84
|
-
##
|
|
122
|
+
## Contributing
|
|
85
123
|
|
|
86
|
-
###
|
|
124
|
+
### Setup
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git clone git@github.com:lvtan71/claude-pack.git
|
|
128
|
+
cd claude-pack
|
|
129
|
+
npm link # makes `claude-pack` command available locally
|
|
130
|
+
```
|
|
87
131
|
|
|
88
|
-
|
|
89
|
-
2. Run `claude-pack install`
|
|
132
|
+
### Adding a Plugin
|
|
90
133
|
|
|
91
|
-
|
|
134
|
+
1. Edit `claude-pack.config.json` — add entries to both `plugins` and `marketplaces`:
|
|
92
135
|
|
|
93
136
|
```json
|
|
137
|
+
// In "plugins" array:
|
|
138
|
+
{
|
|
139
|
+
"name": "my-plugin",
|
|
140
|
+
"marketplace": "marketplace-name",
|
|
141
|
+
"repo": "github-user/repo-name",
|
|
142
|
+
"enabled": true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// In "marketplaces" array:
|
|
94
146
|
{
|
|
95
|
-
"name": "
|
|
96
|
-
"
|
|
97
|
-
"repo": "username/my-skill-repo"
|
|
147
|
+
"name": "marketplace-name",
|
|
148
|
+
"repo": "github-user/repo-name"
|
|
98
149
|
}
|
|
99
150
|
```
|
|
100
151
|
|
|
101
|
-
|
|
152
|
+
2. Update the **Plugins** and **Marketplaces** tables in this README.
|
|
102
153
|
|
|
103
|
-
|
|
104
|
-
2. Add to config: `{ "name": "my-skill", "source": "bundled" }`
|
|
154
|
+
### Adding a Skill
|
|
105
155
|
|
|
106
|
-
|
|
156
|
+
**Option A — Bundled skill** (skill files live in this repo):
|
|
157
|
+
|
|
158
|
+
1. Create `skills/my-skill/SKILL.md` (and optional `references/` directory).
|
|
159
|
+
2. Add to `claude-pack.config.json`:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{ "name": "my-skill", "source": "bundled" }
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Option B — GitHub-hosted skill** (cloned at install time):
|
|
166
|
+
|
|
167
|
+
1. Add to `claude-pack.config.json`:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{ "name": "my-skill", "source": "github", "repo": "github-user/my-skill-repo" }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
2. Update the **Skills** table in this README.
|
|
174
|
+
|
|
175
|
+
### Adding an MCP Server
|
|
176
|
+
|
|
177
|
+
1. Add to the `mcpServers` object in `claude-pack.config.json`:
|
|
107
178
|
|
|
108
179
|
```json
|
|
109
180
|
"mcpServers": {
|
|
@@ -114,7 +185,46 @@ claude-pack install --dry-run # Preview without changes
|
|
|
114
185
|
}
|
|
115
186
|
```
|
|
116
187
|
|
|
188
|
+
2. Update the **MCP Servers** section in this README.
|
|
189
|
+
|
|
190
|
+
### Publishing a New Version
|
|
191
|
+
|
|
192
|
+
1. Make your changes and commit.
|
|
193
|
+
2. Bump the version:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm version patch # 1.0.0 → 1.0.1 (bug fixes, small updates)
|
|
197
|
+
npm version minor # 1.0.0 → 1.1.0 (new plugins/skills added)
|
|
198
|
+
npm version major # 1.0.0 → 2.0.0 (breaking config changes)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
3. Push with tags:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
git push origin main --tags
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
4. Publish to npm:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
npm publish
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
> `npm version` automatically creates a git commit and tag (e.g., `v1.1.0`).
|
|
214
|
+
> Make sure all changes are committed before running it.
|
|
215
|
+
|
|
216
|
+
### Pull Request Guidelines
|
|
217
|
+
|
|
218
|
+
- One plugin/skill/MCP server per PR when possible.
|
|
219
|
+
- Update the README tables to reflect your additions.
|
|
220
|
+
- Test with `claude-pack install --dry-run` before submitting.
|
|
221
|
+
- Test a full install with `claude-pack install --force` on a clean environment if possible.
|
|
222
|
+
|
|
117
223
|
## Requirements
|
|
118
224
|
|
|
119
225
|
- Node.js >= 18
|
|
120
226
|
- git
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
package/bin/claude-pack.mjs
CHANGED
|
@@ -11,6 +11,7 @@ const flags = {
|
|
|
11
11
|
skipPlugins: args.includes('--skip-plugins'),
|
|
12
12
|
skipSkills: args.includes('--skip-skills'),
|
|
13
13
|
skipSettings: args.includes('--skip-settings'),
|
|
14
|
+
skipCodex: args.includes('--skip-codex'),
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
switch (command) {
|
|
@@ -44,5 +45,6 @@ Flags:
|
|
|
44
45
|
--skip-plugins Skip plugin installation
|
|
45
46
|
--skip-skills Skip skill installation
|
|
46
47
|
--skip-settings Skip settings merge
|
|
48
|
+
--skip-codex Skip Codex config generation
|
|
47
49
|
`);
|
|
48
50
|
}
|
package/claude-pack.config.json
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
"marketplace": "thedotmack",
|
|
6
6
|
"repo": "thedotmack/claude-mem",
|
|
7
7
|
"enabled": true
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "understand-anything",
|
|
11
|
+
"marketplace": "understand-anything",
|
|
12
|
+
"repo": "Lum1104/Understand-Anything",
|
|
13
|
+
"enabled": true
|
|
8
14
|
}
|
|
9
15
|
],
|
|
10
16
|
"marketplaces": [
|
|
@@ -18,12 +24,60 @@
|
|
|
18
24
|
}
|
|
19
25
|
],
|
|
20
26
|
"skills": [
|
|
21
|
-
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
{
|
|
28
|
+
"name": "cloud-devops",
|
|
29
|
+
"source": "bundled"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "fastapi",
|
|
33
|
+
"source": "bundled"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "senior-ml-engineer",
|
|
37
|
+
"source": "bundled"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "technical-writer",
|
|
41
|
+
"source": "bundled"
|
|
42
|
+
}
|
|
25
43
|
],
|
|
26
|
-
"mcpServers": {
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"linear": {
|
|
46
|
+
"type": "http",
|
|
47
|
+
"url": "https://mcp.linear.app/mcp"
|
|
48
|
+
},
|
|
49
|
+
"context7": {
|
|
50
|
+
"type": "http",
|
|
51
|
+
"url": "https://mcp.context7.com/mcp"
|
|
52
|
+
},
|
|
53
|
+
"fetch": {
|
|
54
|
+
"command": "uvx",
|
|
55
|
+
"args": [
|
|
56
|
+
"mcp-server-fetch"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"filesystem": {
|
|
60
|
+
"command": "npx",
|
|
61
|
+
"args": [
|
|
62
|
+
"-y",
|
|
63
|
+
"@modelcontextprotocol/server-filesystem",
|
|
64
|
+
"$HOME"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
"sequential-thinking": {
|
|
68
|
+
"command": "npx",
|
|
69
|
+
"args": [
|
|
70
|
+
"-y",
|
|
71
|
+
"@modelcontextprotocol/server-sequential-thinking"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"time": {
|
|
75
|
+
"command": "uvx",
|
|
76
|
+
"args": [
|
|
77
|
+
"mcp-server-time"
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
},
|
|
27
81
|
"settings": {
|
|
28
82
|
"model": "opus",
|
|
29
83
|
"statusLine": {
|
|
@@ -33,5 +87,12 @@
|
|
|
33
87
|
},
|
|
34
88
|
"assets": {
|
|
35
89
|
"statusline": "assets/statusline-command.sh"
|
|
90
|
+
},
|
|
91
|
+
"codex": {
|
|
92
|
+
"enabled": true,
|
|
93
|
+
"model": "gpt-5.4",
|
|
94
|
+
"model_reasoning_effort": "xhigh",
|
|
95
|
+
"configPath": "$HOME/.codex/config.toml",
|
|
96
|
+
"skillsPath": "$HOME/.claude/skills"
|
|
36
97
|
}
|
|
37
|
-
}
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-pack",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Portable Claude Code setup — installs plugins, skills, MCP servers, and statusline on any machine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/
|
|
26
|
+
"url": "git+https://github.com/lvtan71/claude-pack.git"
|
|
27
27
|
},
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">=18"
|
package/src/install.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, cpSync } from 'node:fs';
|
|
3
3
|
import { join, dirname, resolve } from 'node:path';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
4
|
+
import { homedir, platform } from 'node:os';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { createInterface } from 'node:readline';
|
|
6
7
|
|
|
7
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
9
|
const PACK_ROOT = resolve(__dirname, '..');
|
|
@@ -37,6 +38,37 @@ function writeJSON(path, data) {
|
|
|
37
38
|
writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
function prompt(question) {
|
|
42
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
rl.question(question, (answer) => {
|
|
45
|
+
rl.close();
|
|
46
|
+
resolve(answer.trim());
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getDefaultHome() {
|
|
52
|
+
return homedir();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function resolveHomeInConfig(obj) {
|
|
56
|
+
if (typeof obj === 'string') {
|
|
57
|
+
return obj.replace(/\$HOME/g, homedir());
|
|
58
|
+
}
|
|
59
|
+
if (Array.isArray(obj)) {
|
|
60
|
+
return obj.map(resolveHomeInConfig);
|
|
61
|
+
}
|
|
62
|
+
if (obj && typeof obj === 'object') {
|
|
63
|
+
const resolved = {};
|
|
64
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
65
|
+
resolved[k] = resolveHomeInConfig(v);
|
|
66
|
+
}
|
|
67
|
+
return resolved;
|
|
68
|
+
}
|
|
69
|
+
return obj;
|
|
70
|
+
}
|
|
71
|
+
|
|
40
72
|
function loadConfig() {
|
|
41
73
|
const configPath = join(PACK_ROOT, 'claude-pack.config.json');
|
|
42
74
|
return JSON.parse(readFileSync(configPath, 'utf8'));
|
|
@@ -59,7 +91,7 @@ function gitClone(repo, dest) {
|
|
|
59
91
|
return 'cloned';
|
|
60
92
|
}
|
|
61
93
|
|
|
62
|
-
function getPluginVersion(marketplaceDir
|
|
94
|
+
function getPluginVersion(marketplaceDir) {
|
|
63
95
|
// Try to read version from plugin's package.json or manifest
|
|
64
96
|
const candidates = [
|
|
65
97
|
join(marketplaceDir, 'plugin', 'package.json'),
|
|
@@ -82,7 +114,7 @@ function getGitCommitSha(dir) {
|
|
|
82
114
|
}
|
|
83
115
|
}
|
|
84
116
|
|
|
85
|
-
function findPluginInstallPath(marketplaceDir
|
|
117
|
+
function findPluginInstallPath(marketplaceDir) {
|
|
86
118
|
// Look for a plugin/ subdirectory or the root
|
|
87
119
|
const pluginSubdir = join(marketplaceDir, 'plugin');
|
|
88
120
|
if (existsSync(pluginSubdir)) return pluginSubdir;
|
|
@@ -288,7 +320,7 @@ function installSkills(config, flags) {
|
|
|
288
320
|
|
|
289
321
|
// ─── Settings Merge ────────────────────────────────────────────────────
|
|
290
322
|
|
|
291
|
-
function installSettings(config, flags) {
|
|
323
|
+
async function installSettings(config, flags) {
|
|
292
324
|
console.log('\n⚙ Settings');
|
|
293
325
|
|
|
294
326
|
const settings = readJSON(SETTINGS_PATH);
|
|
@@ -329,23 +361,173 @@ function installSettings(config, flags) {
|
|
|
329
361
|
}
|
|
330
362
|
|
|
331
363
|
// Merge MCP servers
|
|
364
|
+
let resolvedFsPath = null;
|
|
332
365
|
if (config.mcpServers && Object.keys(config.mcpServers).length > 0) {
|
|
366
|
+
console.log('\n🔌 MCP Servers');
|
|
333
367
|
if (!settings.mcpServers) settings.mcpServers = {};
|
|
368
|
+
|
|
369
|
+
// Resolve filesystem server path interactively
|
|
370
|
+
const home = getDefaultHome();
|
|
371
|
+
const os = platform();
|
|
372
|
+
const osLabel = os === 'win32' ? 'Windows' : os === 'darwin' ? 'macOS' : 'Linux/WSL';
|
|
373
|
+
|
|
334
374
|
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
|
|
375
|
+
if (name === 'filesystem' && !flags.dryRun) {
|
|
376
|
+
console.log(`\n Detected OS: ${osLabel}`);
|
|
377
|
+
console.log(` Default filesystem path: ${home}`);
|
|
378
|
+
const answer = await prompt(` Use "${home}" as filesystem root? [Y/n/custom path] `);
|
|
379
|
+
|
|
380
|
+
if (answer === '' || answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
381
|
+
resolvedFsPath = home;
|
|
382
|
+
const resolved = JSON.parse(JSON.stringify(serverConfig));
|
|
383
|
+
resolved.args = resolved.args.map((a) => a === '$HOME' ? home : a);
|
|
384
|
+
settings.mcpServers[name] = resolved;
|
|
385
|
+
log('✓', `MCP server "${name}" configured (${home})`);
|
|
386
|
+
} else if (answer.toLowerCase() === 'n' || answer.toLowerCase() === 'no') {
|
|
387
|
+
log('⊘', `MCP server "${name}" skipped — configure manually in ~/.claude/settings.json:`);
|
|
388
|
+
console.log(` "mcpServers": {`);
|
|
389
|
+
console.log(` "filesystem": {`);
|
|
390
|
+
console.log(` "command": "npx",`);
|
|
391
|
+
console.log(` "args": ["-y", "@modelcontextprotocol/server-filesystem", "/your/path"]`);
|
|
392
|
+
console.log(` }`);
|
|
393
|
+
console.log(` }`);
|
|
394
|
+
} else {
|
|
395
|
+
// User provided a custom path
|
|
396
|
+
const customPath = answer.replace(/^~/, home);
|
|
397
|
+
resolvedFsPath = customPath;
|
|
398
|
+
const resolved = JSON.parse(JSON.stringify(serverConfig));
|
|
399
|
+
resolved.args = resolved.args.map((a) => a === '$HOME' ? customPath : a);
|
|
400
|
+
settings.mcpServers[name] = resolved;
|
|
401
|
+
log('✓', `MCP server "${name}" configured (${customPath})`);
|
|
402
|
+
}
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const resolved = resolveHomeInConfig(serverConfig);
|
|
335
407
|
if (flags.dryRun) {
|
|
336
408
|
log('○', `Would configure MCP server "${name}"`);
|
|
337
409
|
} else {
|
|
338
|
-
settings.mcpServers[name] =
|
|
410
|
+
settings.mcpServers[name] = resolved;
|
|
339
411
|
log('✓', `MCP server "${name}" configured`);
|
|
340
412
|
}
|
|
341
413
|
}
|
|
342
|
-
} else {
|
|
343
|
-
log('●', 'No MCP servers to configure (they come from plugins)');
|
|
344
414
|
}
|
|
345
415
|
|
|
346
416
|
if (!flags.dryRun) {
|
|
347
417
|
writeJSON(SETTINGS_PATH, settings);
|
|
348
418
|
}
|
|
419
|
+
|
|
420
|
+
return resolvedFsPath;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ─── Codex Config Generation ───────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
function tomlValue(val) {
|
|
426
|
+
if (typeof val === 'string') return `"${val}"`;
|
|
427
|
+
if (typeof val === 'boolean') return val ? 'true' : 'false';
|
|
428
|
+
if (typeof val === 'number') return String(val);
|
|
429
|
+
if (Array.isArray(val)) {
|
|
430
|
+
const items = val.map(tomlValue);
|
|
431
|
+
// Use multiline for long arrays
|
|
432
|
+
if (items.join(', ').length > 60) {
|
|
433
|
+
return '[\n ' + items.join(',\n ') + '\n]';
|
|
434
|
+
}
|
|
435
|
+
return '[' + items.join(', ') + ']';
|
|
436
|
+
}
|
|
437
|
+
return `"${val}"`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function generateCodexToml(config, resolvedFsPath) {
|
|
441
|
+
const codex = config.codex;
|
|
442
|
+
const home = homedir();
|
|
443
|
+
const lines = [];
|
|
444
|
+
|
|
445
|
+
lines.push(`model = "${codex.model}"`);
|
|
446
|
+
if (codex.model_reasoning_effort) {
|
|
447
|
+
lines.push(`model_reasoning_effort = "${codex.model_reasoning_effort}"`);
|
|
448
|
+
}
|
|
449
|
+
lines.push('');
|
|
450
|
+
|
|
451
|
+
// Generate MCP server sections from the shared config
|
|
452
|
+
for (const [name, serverConfig] of Object.entries(config.mcpServers || {})) {
|
|
453
|
+
lines.push(`[mcp_servers.${name}]`);
|
|
454
|
+
lines.push('enabled = true');
|
|
455
|
+
|
|
456
|
+
if (serverConfig.type === 'http' || serverConfig.url) {
|
|
457
|
+
lines.push(`url = "${serverConfig.url}"`);
|
|
458
|
+
} else if (serverConfig.command) {
|
|
459
|
+
let command = serverConfig.command;
|
|
460
|
+
const args = serverConfig.args || [];
|
|
461
|
+
|
|
462
|
+
// For npx commands, resolve to full node/npx path for Codex compatibility
|
|
463
|
+
let resolvedArgs = args.map((a) => {
|
|
464
|
+
if (a === '$HOME') return resolvedFsPath || home;
|
|
465
|
+
return a;
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
lines.push(`command = "${command}"`);
|
|
469
|
+
if (resolvedArgs.length > 0) {
|
|
470
|
+
lines.push(`args = ${tomlValue(resolvedArgs)}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Environment variables
|
|
475
|
+
if (serverConfig.env && Object.keys(serverConfig.env).length > 0) {
|
|
476
|
+
lines.push('');
|
|
477
|
+
lines.push(`[mcp_servers.${name}.env]`);
|
|
478
|
+
for (const [k, v] of Object.entries(serverConfig.env)) {
|
|
479
|
+
lines.push(`${k} = "${v}"`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
lines.push('');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Skills — point to the shared ~/.claude/skills directory
|
|
487
|
+
const skillsPath = (codex.skillsPath || '$HOME/.claude/skills').replace(/\$HOME/g, home);
|
|
488
|
+
lines.push('[[skills.config]]');
|
|
489
|
+
lines.push(`path = "${skillsPath}"`);
|
|
490
|
+
lines.push('enabled = true');
|
|
491
|
+
lines.push('');
|
|
492
|
+
|
|
493
|
+
return lines.join('\n');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async function installCodex(config, flags, resolvedFsPath) {
|
|
497
|
+
if (!config.codex?.enabled) return;
|
|
498
|
+
|
|
499
|
+
console.log('\n🤖 Codex');
|
|
500
|
+
|
|
501
|
+
const home = homedir();
|
|
502
|
+
const configPath = (config.codex.configPath || '$HOME/.codex/config.toml').replace(/\$HOME/g, home);
|
|
503
|
+
const configDir = dirname(configPath);
|
|
504
|
+
|
|
505
|
+
if (flags.dryRun) {
|
|
506
|
+
log('○', `Would generate Codex config at ${configPath}`);
|
|
507
|
+
log('○', `Model: ${config.codex.model}`);
|
|
508
|
+
log('○', `Skills path: ~/.claude/skills (shared with Claude Code)`);
|
|
509
|
+
const mcpCount = Object.keys(config.mcpServers || {}).length;
|
|
510
|
+
log('○', `MCP servers: ${mcpCount} (same as Claude Code)`);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const toml = generateCodexToml(config, resolvedFsPath);
|
|
515
|
+
ensureDir(configDir);
|
|
516
|
+
|
|
517
|
+
const exists = existsSync(configPath);
|
|
518
|
+
if (exists && !flags.force) {
|
|
519
|
+
const answer = await prompt(` Codex config exists at ${configPath}. Overwrite? [y/N] `);
|
|
520
|
+
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
521
|
+
log('⊘', 'Codex config skipped (use --force to overwrite)');
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
writeFileSync(configPath, toml);
|
|
527
|
+
log('✓', `Codex config written to ${configPath}`);
|
|
528
|
+
log('✓', `Model: ${config.codex.model}`);
|
|
529
|
+
log('✓', `Skills: ~/.claude/skills (shared with Claude Code)`);
|
|
530
|
+
log('✓', `MCP servers: ${Object.keys(config.mcpServers || {}).length} configured`);
|
|
349
531
|
}
|
|
350
532
|
|
|
351
533
|
// ─── Main Install ──────────────────────────────────────────────────────
|
|
@@ -379,13 +561,19 @@ export async function install(flags = {}) {
|
|
|
379
561
|
|
|
380
562
|
if (!flags.skipPlugins) installPlugins(config, flags);
|
|
381
563
|
if (!flags.skipSkills) installSkills(config, flags);
|
|
382
|
-
|
|
564
|
+
let resolvedFsPath = null;
|
|
565
|
+
if (!flags.skipSettings) {
|
|
566
|
+
resolvedFsPath = await installSettings(config, flags);
|
|
567
|
+
}
|
|
568
|
+
if (!flags.skipCodex) {
|
|
569
|
+
await installCodex(config, flags, resolvedFsPath);
|
|
570
|
+
}
|
|
383
571
|
|
|
384
572
|
console.log('\n────────────────────────────────────────────');
|
|
385
573
|
if (flags.dryRun) {
|
|
386
574
|
console.log(' Dry run complete. Run without --dry-run to apply.');
|
|
387
575
|
} else {
|
|
388
|
-
console.log(' ✓ All done! Restart Claude Code to pick up changes.');
|
|
576
|
+
console.log(' ✓ All done! Restart Claude Code / Codex to pick up changes.');
|
|
389
577
|
}
|
|
390
578
|
console.log('');
|
|
391
579
|
}
|