ccbot-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +747 -0
- package/package.json +40 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/logger.ts
|
|
4
|
+
import pc from "picocolors";
|
|
5
|
+
var log = {
|
|
6
|
+
info: (msg) => console.log(pc.cyan(" \u2139 ") + msg),
|
|
7
|
+
success: (msg) => console.log(pc.green(" \u2713 ") + msg),
|
|
8
|
+
warn: (msg) => console.log(pc.yellow(" \u26A0 ") + msg),
|
|
9
|
+
error: (msg) => console.log(pc.red(" \u2717 ") + msg),
|
|
10
|
+
step: (msg) => console.log(pc.blue(" \u2192 ") + msg),
|
|
11
|
+
dim: (msg) => console.log(pc.dim(" " + msg))
|
|
12
|
+
};
|
|
13
|
+
function banner(version) {
|
|
14
|
+
console.log();
|
|
15
|
+
console.log(pc.cyan(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
16
|
+
console.log(pc.cyan(" \u2502 \u2502"));
|
|
17
|
+
console.log(pc.cyan(" \u2502") + " \u{1F916} ccbot - Claude Code \u73AF\u5883\u914D\u7F6E " + pc.cyan("\u2502"));
|
|
18
|
+
console.log(pc.cyan(" \u2502") + pc.dim(` \u4E00\u952E\u914D\u7F6E\u5DE5\u5177 v${version}`) + " " + pc.cyan("\u2502"));
|
|
19
|
+
console.log(pc.cyan(" \u2502 \u2502"));
|
|
20
|
+
console.log(pc.cyan(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
21
|
+
console.log();
|
|
22
|
+
}
|
|
23
|
+
function summary(items) {
|
|
24
|
+
console.log();
|
|
25
|
+
console.log(pc.cyan(" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
26
|
+
console.log(pc.cyan(" \u2502") + pc.bold(" \u5B89\u88C5\u5B8C\u6210\uFF01") + " " + pc.cyan("\u2502"));
|
|
27
|
+
console.log(pc.cyan(" \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
28
|
+
console.log();
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
if (item.ok) {
|
|
31
|
+
log.success(item.label);
|
|
32
|
+
} else {
|
|
33
|
+
log.error(item.label);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.log();
|
|
37
|
+
log.info("\u4E0B\u4E00\u6B65\uFF1A");
|
|
38
|
+
log.dim("1. \u8FD0\u884C claude \u542F\u52A8 Claude Code");
|
|
39
|
+
log.dim("2. \u7F16\u8F91 CLAUDE.md \u81EA\u5B9A\u4E49\u9879\u76EE\u6307\u4EE4");
|
|
40
|
+
console.log();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/cli.ts
|
|
44
|
+
import * as p8 from "@clack/prompts";
|
|
45
|
+
import pc7 from "picocolors";
|
|
46
|
+
|
|
47
|
+
// src/steps/detect.ts
|
|
48
|
+
import * as p from "@clack/prompts";
|
|
49
|
+
import pc2 from "picocolors";
|
|
50
|
+
|
|
51
|
+
// src/utils/exec.ts
|
|
52
|
+
import { execa } from "execa";
|
|
53
|
+
async function run(command, args = [], options) {
|
|
54
|
+
try {
|
|
55
|
+
const result = await execa(command, args, {
|
|
56
|
+
...options,
|
|
57
|
+
reject: false
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
stdout: result.stdout?.toString() ?? "",
|
|
61
|
+
stderr: result.stderr?.toString() ?? "",
|
|
62
|
+
exitCode: result.exitCode ?? 0
|
|
63
|
+
};
|
|
64
|
+
} catch {
|
|
65
|
+
return { stdout: "", stderr: "Command not found or failed to execute", exitCode: 127 };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function commandExists(command) {
|
|
69
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
70
|
+
const result = await run(cmd, [command]);
|
|
71
|
+
return result.exitCode === 0;
|
|
72
|
+
}
|
|
73
|
+
async function npmInstallGlobal(pkg) {
|
|
74
|
+
return run("npm", ["install", "-g", pkg]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/steps/detect.ts
|
|
78
|
+
var SENSITIVE_PATTERNS = [
|
|
79
|
+
/^ANTHROPIC_API_KEY$/i,
|
|
80
|
+
/^CLAUDE_API_KEY$/i,
|
|
81
|
+
/^OPENAI_API_KEY$/i,
|
|
82
|
+
/^OPENAI_ORG_ID$/i,
|
|
83
|
+
/^GOOGLE_API_KEY$/i,
|
|
84
|
+
/^AZURE_OPENAI_API_KEY$/i,
|
|
85
|
+
/^HUGGINGFACE_TOKEN$/i,
|
|
86
|
+
/^HF_TOKEN$/i,
|
|
87
|
+
/^COHERE_API_KEY$/i,
|
|
88
|
+
/^MISTRAL_API_KEY$/i,
|
|
89
|
+
/^DEEPSEEK_API_KEY$/i,
|
|
90
|
+
/^GROQ_API_KEY$/i,
|
|
91
|
+
/_SECRET$/i,
|
|
92
|
+
/_TOKEN$/i,
|
|
93
|
+
/_API_KEY$/i
|
|
94
|
+
];
|
|
95
|
+
function maskValue(val) {
|
|
96
|
+
if (val.length <= 8) return "****";
|
|
97
|
+
return val.slice(0, 4) + "..." + val.slice(-4);
|
|
98
|
+
}
|
|
99
|
+
function scanSensitiveEnvVars() {
|
|
100
|
+
const found = [];
|
|
101
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
102
|
+
if (!value) continue;
|
|
103
|
+
for (const pattern of SENSITIVE_PATTERNS) {
|
|
104
|
+
if (pattern.test(key)) {
|
|
105
|
+
found.push({ key, value });
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return found;
|
|
111
|
+
}
|
|
112
|
+
async function detect() {
|
|
113
|
+
const s = p.spinner();
|
|
114
|
+
s.start("\u6B63\u5728\u68C0\u6D4B\u73AF\u5883...");
|
|
115
|
+
const nodeVersion = process.version;
|
|
116
|
+
const npmResult = await run("npm", ["--version"]);
|
|
117
|
+
const npmVersion = npmResult.stdout.trim();
|
|
118
|
+
const osMap = {
|
|
119
|
+
win32: "Windows",
|
|
120
|
+
darwin: "macOS",
|
|
121
|
+
linux: "Linux"
|
|
122
|
+
};
|
|
123
|
+
const os = osMap[process.platform] ?? process.platform;
|
|
124
|
+
const claudeInstalled = await commandExists("claude");
|
|
125
|
+
let claudeVersion = null;
|
|
126
|
+
if (claudeInstalled) {
|
|
127
|
+
const result = await run("claude", ["--version"]);
|
|
128
|
+
claudeVersion = result.stdout.trim();
|
|
129
|
+
}
|
|
130
|
+
const sensitiveEnvVars = scanSensitiveEnvVars();
|
|
131
|
+
s.stop("\u73AF\u5883\u68C0\u6D4B\u5B8C\u6210");
|
|
132
|
+
p.log.success(`Node.js ${nodeVersion}`);
|
|
133
|
+
p.log.success(`npm v${npmVersion}`);
|
|
134
|
+
p.log.success(os);
|
|
135
|
+
if (claudeInstalled) {
|
|
136
|
+
p.log.success(`Claude Code CLI ${claudeVersion}`);
|
|
137
|
+
} else {
|
|
138
|
+
p.log.warning("Claude Code CLI \u672A\u5B89\u88C5");
|
|
139
|
+
}
|
|
140
|
+
if (sensitiveEnvVars.length > 0) {
|
|
141
|
+
p.log.warning("\u68C0\u6D4B\u5230\u654F\u611F\u73AF\u5883\u53D8\u91CF:");
|
|
142
|
+
for (const { key, value } of sensitiveEnvVars) {
|
|
143
|
+
console.log(pc2.yellow(` ${key}=${maskValue(value)}`));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
nodeVersion,
|
|
148
|
+
npmVersion,
|
|
149
|
+
os,
|
|
150
|
+
platform: process.platform,
|
|
151
|
+
claudeInstalled,
|
|
152
|
+
claudeVersion,
|
|
153
|
+
sensitiveEnvVars
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/steps/select.ts
|
|
158
|
+
import * as p2 from "@clack/prompts";
|
|
159
|
+
import pc3 from "picocolors";
|
|
160
|
+
function printMultiselectHint() {
|
|
161
|
+
console.log();
|
|
162
|
+
console.log(pc3.dim(" \u64CD\u4F5C\u6307\u5357:"));
|
|
163
|
+
console.log(pc3.dim(" \u2191/\u2193 \u4E0A\u4E0B\u79FB\u52A8\u5149\u6807"));
|
|
164
|
+
console.log(pc3.dim(" \u7A7A\u683C \u9009\u4E2D/\u53D6\u6D88\u9009\u4E2D\u5F53\u524D\u9879"));
|
|
165
|
+
console.log(pc3.dim(" a \u5168\u9009/\u5168\u4E0D\u9009"));
|
|
166
|
+
console.log(pc3.dim(" \u56DE\u8F66 \u786E\u8BA4\u9009\u62E9"));
|
|
167
|
+
console.log();
|
|
168
|
+
}
|
|
169
|
+
async function selectComponents(env) {
|
|
170
|
+
const options = [
|
|
171
|
+
{
|
|
172
|
+
value: "installCli",
|
|
173
|
+
label: "Claude Code CLI \u5B89\u88C5",
|
|
174
|
+
hint: env.claudeInstalled ? "\u5DF2\u5B89\u88C5\uFF0C\u5C06\u8DF3\u8FC7" : "\u672A\u5B89\u88C5"
|
|
175
|
+
},
|
|
176
|
+
{ value: "scaffold", label: "\u9879\u76EE\u914D\u7F6E\u811A\u624B\u67B6", hint: "CLAUDE.md + .claude/ \u914D\u7F6E" },
|
|
177
|
+
{ value: "installMcp", label: "MCP Servers", hint: "\u4ECE\u9884\u8BBE\u5217\u8868\u9009\u62E9\u5B89\u88C5" },
|
|
178
|
+
{ value: "installSkills", label: "Skills / Plugins", hint: "\u589E\u5F3A Claude Code \u80FD\u529B" }
|
|
179
|
+
];
|
|
180
|
+
if (env.sensitiveEnvVars.length > 0) {
|
|
181
|
+
options.push({
|
|
182
|
+
value: "cleanEnv",
|
|
183
|
+
label: "\u73AF\u5883\u53D8\u91CF\u6E05\u7406",
|
|
184
|
+
hint: `\u68C0\u6D4B\u5230 ${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u53D8\u91CF`
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
printMultiselectHint();
|
|
188
|
+
const selected = await p2.multiselect({
|
|
189
|
+
message: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u7EC4\u4EF6 (\u7A7A\u683C\u5207\u6362, \u56DE\u8F66\u786E\u8BA4)",
|
|
190
|
+
options,
|
|
191
|
+
initialValues: options.map((o) => o.value),
|
|
192
|
+
required: true
|
|
193
|
+
});
|
|
194
|
+
if (p2.isCancel(selected)) {
|
|
195
|
+
p2.cancel("\u5DF2\u53D6\u6D88");
|
|
196
|
+
process.exit(0);
|
|
197
|
+
}
|
|
198
|
+
const values = selected;
|
|
199
|
+
return {
|
|
200
|
+
installCli: values.includes("installCli"),
|
|
201
|
+
scaffold: values.includes("scaffold"),
|
|
202
|
+
installMcp: values.includes("installMcp"),
|
|
203
|
+
installSkills: values.includes("installSkills"),
|
|
204
|
+
cleanEnv: values.includes("cleanEnv")
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/steps/install-cli.ts
|
|
209
|
+
import * as p3 from "@clack/prompts";
|
|
210
|
+
async function installCli(alreadyInstalled) {
|
|
211
|
+
if (alreadyInstalled) {
|
|
212
|
+
p3.log.info("Claude Code CLI \u5DF2\u5B89\u88C5\uFF0C\u8DF3\u8FC7");
|
|
213
|
+
return { success: true, version: null, skipped: true };
|
|
214
|
+
}
|
|
215
|
+
const s = p3.spinner();
|
|
216
|
+
s.start("\u6B63\u5728\u5B89\u88C5 Claude Code CLI...");
|
|
217
|
+
const result = await npmInstallGlobal("@anthropic-ai/claude-code");
|
|
218
|
+
if (result.exitCode !== 0) {
|
|
219
|
+
s.stop("Claude Code CLI \u5B89\u88C5\u5931\u8D25");
|
|
220
|
+
p3.log.error(result.stderr || "\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C: npm install -g @anthropic-ai/claude-code");
|
|
221
|
+
return { success: false, version: null, skipped: false };
|
|
222
|
+
}
|
|
223
|
+
const exists = await commandExists("claude");
|
|
224
|
+
if (!exists) {
|
|
225
|
+
s.stop("Claude Code CLI \u5B89\u88C5\u5F02\u5E38");
|
|
226
|
+
p3.log.warning("\u5B89\u88C5\u5B8C\u6210\u4F46 claude \u547D\u4EE4\u4E0D\u53EF\u7528\uFF0C\u53EF\u80FD\u9700\u8981\u91CD\u542F\u7EC8\u7AEF");
|
|
227
|
+
return { success: false, version: null, skipped: false };
|
|
228
|
+
}
|
|
229
|
+
const versionResult = await run("claude", ["--version"]);
|
|
230
|
+
const version = versionResult.stdout.trim();
|
|
231
|
+
s.stop(`Claude Code CLI \u5B89\u88C5\u5B8C\u6210 (${version})`);
|
|
232
|
+
return { success: true, version, skipped: false };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/steps/scaffold.ts
|
|
236
|
+
import * as p4 from "@clack/prompts";
|
|
237
|
+
import { join as join2 } from "path";
|
|
238
|
+
|
|
239
|
+
// src/utils/fs.ts
|
|
240
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
241
|
+
import { dirname } from "path";
|
|
242
|
+
function ensureDir(dir) {
|
|
243
|
+
if (!existsSync(dir)) {
|
|
244
|
+
mkdirSync(dir, { recursive: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function writeFileSafe(filePath, content, overwrite = false) {
|
|
248
|
+
ensureDir(dirname(filePath));
|
|
249
|
+
if (existsSync(filePath) && !overwrite) {
|
|
250
|
+
return { written: false, skipped: true };
|
|
251
|
+
}
|
|
252
|
+
writeFileSync(filePath, content, "utf-8");
|
|
253
|
+
return { written: true, skipped: false };
|
|
254
|
+
}
|
|
255
|
+
function readJsonFile(filePath) {
|
|
256
|
+
try {
|
|
257
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
258
|
+
return JSON.parse(raw);
|
|
259
|
+
} catch {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function mergeJsonFile(filePath, patch) {
|
|
264
|
+
const existing = readJsonFile(filePath) ?? {};
|
|
265
|
+
const merged = deepMerge(existing, patch);
|
|
266
|
+
writeFileSync(filePath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
267
|
+
}
|
|
268
|
+
function deepMerge(target, source) {
|
|
269
|
+
const result = { ...target };
|
|
270
|
+
for (const key of Object.keys(source)) {
|
|
271
|
+
if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key]) && target[key] && typeof target[key] === "object" && !Array.isArray(target[key])) {
|
|
272
|
+
result[key] = deepMerge(
|
|
273
|
+
target[key],
|
|
274
|
+
source[key]
|
|
275
|
+
);
|
|
276
|
+
} else {
|
|
277
|
+
result[key] = source[key];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/steps/scaffold.ts
|
|
284
|
+
var CLAUDE_MD_TEMPLATE = `# Project Instructions
|
|
285
|
+
|
|
286
|
+
<!-- \u5728\u6B64\u7F16\u5199\u9879\u76EE\u7EA7 Claude Code \u6307\u4EE4 -->
|
|
287
|
+
|
|
288
|
+
## \u9879\u76EE\u6982\u8FF0
|
|
289
|
+
|
|
290
|
+
<!-- \u63CF\u8FF0\u9879\u76EE\u7684\u76EE\u7684\u548C\u6280\u672F\u6808 -->
|
|
291
|
+
|
|
292
|
+
## \u7F16\u7801\u89C4\u8303
|
|
293
|
+
|
|
294
|
+
- \u4F7F\u7528 TypeScript
|
|
295
|
+
- \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u4EE3\u7801\u98CE\u683C
|
|
296
|
+
- \u5148\u8BFB\u540E\u5199\uFF0C\u7406\u89E3\u4E0A\u4E0B\u6587\u518D\u4FEE\u6539
|
|
297
|
+
|
|
298
|
+
## \u91CD\u8981\u6587\u4EF6
|
|
299
|
+
|
|
300
|
+
<!-- \u5217\u51FA\u5173\u952E\u6587\u4EF6\u8DEF\u5F84 -->
|
|
301
|
+
|
|
302
|
+
## \u6CE8\u610F\u4E8B\u9879
|
|
303
|
+
|
|
304
|
+
<!-- \u5217\u51FA\u9700\u8981\u7279\u522B\u6CE8\u610F\u7684\u4E8B\u9879 -->
|
|
305
|
+
`;
|
|
306
|
+
var SETTINGS_TEMPLATE = {
|
|
307
|
+
permissions: {
|
|
308
|
+
allow: [
|
|
309
|
+
"Read",
|
|
310
|
+
"Glob",
|
|
311
|
+
"Grep",
|
|
312
|
+
"WebFetch",
|
|
313
|
+
"WebSearch"
|
|
314
|
+
],
|
|
315
|
+
deny: []
|
|
316
|
+
},
|
|
317
|
+
mcpServers: {}
|
|
318
|
+
};
|
|
319
|
+
var CLAUDEIGNORE_TEMPLATE = `# \u5FFD\u7565\u6587\u4EF6
|
|
320
|
+
node_modules/
|
|
321
|
+
dist/
|
|
322
|
+
.env
|
|
323
|
+
.env.*
|
|
324
|
+
*.log
|
|
325
|
+
.DS_Store
|
|
326
|
+
coverage/
|
|
327
|
+
`;
|
|
328
|
+
async function scaffold(targetDir) {
|
|
329
|
+
const s = p4.spinner();
|
|
330
|
+
s.start("\u6B63\u5728\u751F\u6210\u914D\u7F6E\u6587\u4EF6...");
|
|
331
|
+
const files = [];
|
|
332
|
+
const claudeMdPath = join2(targetDir, "CLAUDE.md");
|
|
333
|
+
const r1 = writeFileSafe(claudeMdPath, CLAUDE_MD_TEMPLATE);
|
|
334
|
+
files.push({ path: "CLAUDE.md", written: r1.written });
|
|
335
|
+
const claudeDir = join2(targetDir, ".claude");
|
|
336
|
+
ensureDir(claudeDir);
|
|
337
|
+
const settingsPath = join2(claudeDir, "settings.json");
|
|
338
|
+
const r2 = writeFileSafe(settingsPath, JSON.stringify(SETTINGS_TEMPLATE, null, 2) + "\n");
|
|
339
|
+
files.push({ path: ".claude/settings.json", written: r2.written });
|
|
340
|
+
const ignorePath = join2(targetDir, ".claudeignore");
|
|
341
|
+
const r3 = writeFileSafe(ignorePath, CLAUDEIGNORE_TEMPLATE);
|
|
342
|
+
files.push({ path: ".claudeignore", written: r3.written });
|
|
343
|
+
s.stop("\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5B8C\u6210");
|
|
344
|
+
for (const f of files) {
|
|
345
|
+
if (f.written) {
|
|
346
|
+
p4.log.success(`${f.path} \u5DF2\u751F\u6210`);
|
|
347
|
+
} else {
|
|
348
|
+
p4.log.info(`${f.path} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return { files };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/steps/install-mcp.ts
|
|
355
|
+
import * as p5 from "@clack/prompts";
|
|
356
|
+
import pc4 from "picocolors";
|
|
357
|
+
import { join as join3 } from "path";
|
|
358
|
+
import { homedir } from "os";
|
|
359
|
+
|
|
360
|
+
// src/registry/mcp-servers.ts
|
|
361
|
+
var MCP_SERVERS = [
|
|
362
|
+
{
|
|
363
|
+
name: "playwright",
|
|
364
|
+
package: "@anthropic-ai/mcp-playwright",
|
|
365
|
+
description: "\u6D4F\u89C8\u5668\u81EA\u52A8\u5316 (\u622A\u56FE\u3001\u70B9\u51FB\u3001\u8868\u5355\u586B\u5199)",
|
|
366
|
+
scope: "user",
|
|
367
|
+
command: "npx",
|
|
368
|
+
args: ["-y", "@anthropic-ai/mcp-playwright"]
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: "filesystem",
|
|
372
|
+
package: "@modelcontextprotocol/server-filesystem",
|
|
373
|
+
description: "\u6587\u4EF6\u7CFB\u7EDF\u8BFB\u5199\u8BBF\u95EE",
|
|
374
|
+
scope: "project",
|
|
375
|
+
command: "npx",
|
|
376
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "."]
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: "context7",
|
|
380
|
+
package: "@anthropic-ai/mcp-context7",
|
|
381
|
+
description: "\u7F16\u7A0B\u6587\u6863\u5B9E\u65F6\u67E5\u8BE2",
|
|
382
|
+
scope: "user",
|
|
383
|
+
command: "npx",
|
|
384
|
+
args: ["-y", "@upstash/context7-mcp"]
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: "deepwiki",
|
|
388
|
+
package: "@anthropic-ai/mcp-deepwiki",
|
|
389
|
+
description: "GitHub \u4ED3\u5E93 Wiki \u77E5\u8BC6\u5E93",
|
|
390
|
+
scope: "user",
|
|
391
|
+
command: "npx",
|
|
392
|
+
args: ["-y", "@anthropic-ai/mcp-deepwiki"]
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "open-websearch",
|
|
396
|
+
package: "open-websearch-mcp",
|
|
397
|
+
description: "\u591A\u5F15\u64CE\u7F51\u9875\u641C\u7D22 (DuckDuckGo/Bing/Brave)",
|
|
398
|
+
scope: "user",
|
|
399
|
+
command: "npx",
|
|
400
|
+
args: ["-y", "open-websearch-mcp"]
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
name: "memory",
|
|
404
|
+
package: "@modelcontextprotocol/server-memory",
|
|
405
|
+
description: "\u6301\u4E45\u5316\u8BB0\u5FC6\u5B58\u50A8",
|
|
406
|
+
scope: "user",
|
|
407
|
+
command: "npx",
|
|
408
|
+
args: ["-y", "@modelcontextprotocol/server-memory"]
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "sequential-thinking",
|
|
412
|
+
package: "@modelcontextprotocol/server-sequential-thinking",
|
|
413
|
+
description: "\u94FE\u5F0F\u601D\u8003\u63A8\u7406",
|
|
414
|
+
scope: "user",
|
|
415
|
+
command: "npx",
|
|
416
|
+
args: ["-y", "@modelcontextprotocol/server-sequential-thinking"]
|
|
417
|
+
}
|
|
418
|
+
];
|
|
419
|
+
|
|
420
|
+
// src/steps/install-mcp.ts
|
|
421
|
+
async function selectMcpServers() {
|
|
422
|
+
console.log();
|
|
423
|
+
console.log(pc4.dim(" \u64CD\u4F5C\u6307\u5357:"));
|
|
424
|
+
console.log(pc4.dim(" \u2191/\u2193 \u4E0A\u4E0B\u79FB\u52A8\u5149\u6807"));
|
|
425
|
+
console.log(pc4.dim(" \u7A7A\u683C \u9009\u4E2D/\u53D6\u6D88\u9009\u4E2D\u5F53\u524D\u9879"));
|
|
426
|
+
console.log(pc4.dim(" a \u5168\u9009/\u5168\u4E0D\u9009"));
|
|
427
|
+
console.log(pc4.dim(" \u56DE\u8F66 \u786E\u8BA4\u9009\u62E9"));
|
|
428
|
+
console.log();
|
|
429
|
+
const selected = await p5.multiselect({
|
|
430
|
+
message: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 MCP Servers (\u7A7A\u683C\u5207\u6362, \u56DE\u8F66\u786E\u8BA4)",
|
|
431
|
+
options: MCP_SERVERS.map((s) => ({
|
|
432
|
+
value: s.name,
|
|
433
|
+
label: s.name,
|
|
434
|
+
hint: s.description
|
|
435
|
+
})),
|
|
436
|
+
initialValues: MCP_SERVERS.map((s) => s.name),
|
|
437
|
+
required: false
|
|
438
|
+
});
|
|
439
|
+
if (p5.isCancel(selected)) {
|
|
440
|
+
return [];
|
|
441
|
+
}
|
|
442
|
+
const names = selected;
|
|
443
|
+
return MCP_SERVERS.filter((s) => names.includes(s.name));
|
|
444
|
+
}
|
|
445
|
+
async function installMcp(servers) {
|
|
446
|
+
if (servers.length === 0) {
|
|
447
|
+
return { installed: [], failed: [] };
|
|
448
|
+
}
|
|
449
|
+
const s = p5.spinner();
|
|
450
|
+
s.start("\u6B63\u5728\u914D\u7F6E MCP Servers...");
|
|
451
|
+
const installed = [];
|
|
452
|
+
const failed = [];
|
|
453
|
+
const userServers = servers.filter((sv) => sv.scope === "user");
|
|
454
|
+
const projectServers = servers.filter((sv) => sv.scope === "project");
|
|
455
|
+
if (userServers.length > 0) {
|
|
456
|
+
const userClaudeDir = join3(homedir(), ".claude");
|
|
457
|
+
ensureDir(userClaudeDir);
|
|
458
|
+
const userSettingsPath = join3(userClaudeDir, "settings.json");
|
|
459
|
+
const mcpConfig = {};
|
|
460
|
+
for (const sv of userServers) {
|
|
461
|
+
mcpConfig[sv.name] = {
|
|
462
|
+
command: sv.command,
|
|
463
|
+
args: sv.args,
|
|
464
|
+
...sv.env ? { env: sv.env } : {}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
mergeJsonFile(userSettingsPath, { mcpServers: mcpConfig });
|
|
469
|
+
for (const sv of userServers) {
|
|
470
|
+
installed.push(sv.name);
|
|
471
|
+
}
|
|
472
|
+
} catch {
|
|
473
|
+
for (const sv of userServers) {
|
|
474
|
+
failed.push(sv.name);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (projectServers.length > 0) {
|
|
479
|
+
const projectClaudeDir = join3(process.cwd(), ".claude");
|
|
480
|
+
ensureDir(projectClaudeDir);
|
|
481
|
+
const projectSettingsPath = join3(projectClaudeDir, "settings.json");
|
|
482
|
+
const mcpConfig = {};
|
|
483
|
+
for (const sv of projectServers) {
|
|
484
|
+
mcpConfig[sv.name] = {
|
|
485
|
+
command: sv.command,
|
|
486
|
+
args: sv.args,
|
|
487
|
+
...sv.env ? { env: sv.env } : {}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
mergeJsonFile(projectSettingsPath, { mcpServers: mcpConfig });
|
|
492
|
+
for (const sv of projectServers) {
|
|
493
|
+
installed.push(sv.name);
|
|
494
|
+
}
|
|
495
|
+
} catch {
|
|
496
|
+
for (const sv of projectServers) {
|
|
497
|
+
failed.push(sv.name);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
s.stop("MCP Servers \u914D\u7F6E\u5B8C\u6210");
|
|
502
|
+
for (const name of installed) {
|
|
503
|
+
p5.log.success(`${name} MCP \u5DF2\u914D\u7F6E`);
|
|
504
|
+
}
|
|
505
|
+
for (const name of failed) {
|
|
506
|
+
p5.log.error(`${name} MCP \u914D\u7F6E\u5931\u8D25`);
|
|
507
|
+
}
|
|
508
|
+
return { installed, failed };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/steps/install-skills.ts
|
|
512
|
+
import * as p6 from "@clack/prompts";
|
|
513
|
+
import pc5 from "picocolors";
|
|
514
|
+
|
|
515
|
+
// src/registry/skills.ts
|
|
516
|
+
var SKILLS = [
|
|
517
|
+
{
|
|
518
|
+
name: "superpowers",
|
|
519
|
+
description: "\u589E\u5F3A\u5DE5\u4F5C\u6D41 (\u5934\u8111\u98CE\u66B4\u3001TDD\u3001\u4EE3\u7801\u5BA1\u67E5\u3001\u8BA1\u5212)",
|
|
520
|
+
source: "claude-plugins-official/superpowers",
|
|
521
|
+
installCmd: ["claude", "plugins", "install", "superpowers"]
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: "spec-workflow",
|
|
525
|
+
description: "\u89C4\u8303\u5316\u5F00\u53D1\u6D41\u7A0B (\u9700\u6C42\u2192\u8BBE\u8BA1\u2192\u4EFB\u52A1\u2192\u5B9E\u73B0)",
|
|
526
|
+
source: "spec-workflow",
|
|
527
|
+
installCmd: ["claude", "plugins", "install", "spec-workflow"]
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
name: "skill-creator",
|
|
531
|
+
description: "Skill \u521B\u5EFA\u4E0E\u7BA1\u7406\u5DE5\u5177",
|
|
532
|
+
source: "skill-creator",
|
|
533
|
+
installCmd: ["claude", "plugins", "install", "skill-creator"]
|
|
534
|
+
}
|
|
535
|
+
];
|
|
536
|
+
|
|
537
|
+
// src/steps/install-skills.ts
|
|
538
|
+
async function selectSkills() {
|
|
539
|
+
console.log();
|
|
540
|
+
console.log(pc5.dim(" \u64CD\u4F5C\u6307\u5357:"));
|
|
541
|
+
console.log(pc5.dim(" \u2191/\u2193 \u4E0A\u4E0B\u79FB\u52A8\u5149\u6807"));
|
|
542
|
+
console.log(pc5.dim(" \u7A7A\u683C \u9009\u4E2D/\u53D6\u6D88\u9009\u4E2D\u5F53\u524D\u9879"));
|
|
543
|
+
console.log(pc5.dim(" a \u5168\u9009/\u5168\u4E0D\u9009"));
|
|
544
|
+
console.log(pc5.dim(" \u56DE\u8F66 \u786E\u8BA4\u9009\u62E9"));
|
|
545
|
+
console.log();
|
|
546
|
+
const selected = await p6.multiselect({
|
|
547
|
+
message: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills / Plugins (\u7A7A\u683C\u5207\u6362, \u56DE\u8F66\u786E\u8BA4)",
|
|
548
|
+
options: SKILLS.map((s) => ({
|
|
549
|
+
value: s.name,
|
|
550
|
+
label: s.name,
|
|
551
|
+
hint: s.description
|
|
552
|
+
})),
|
|
553
|
+
initialValues: SKILLS.map((s) => s.name),
|
|
554
|
+
required: false
|
|
555
|
+
});
|
|
556
|
+
if (p6.isCancel(selected)) {
|
|
557
|
+
return [];
|
|
558
|
+
}
|
|
559
|
+
const names = selected;
|
|
560
|
+
return SKILLS.filter((s) => names.includes(s.name));
|
|
561
|
+
}
|
|
562
|
+
async function installSkills(skills) {
|
|
563
|
+
if (skills.length === 0) {
|
|
564
|
+
return { installed: [], failed: [] };
|
|
565
|
+
}
|
|
566
|
+
const s = p6.spinner();
|
|
567
|
+
const installed = [];
|
|
568
|
+
const failed = [];
|
|
569
|
+
for (const skill of skills) {
|
|
570
|
+
s.start(`\u6B63\u5728\u5B89\u88C5 ${skill.name}...`);
|
|
571
|
+
const [cmd, ...args] = skill.installCmd;
|
|
572
|
+
const result = await run(cmd, args);
|
|
573
|
+
if (result.exitCode === 0) {
|
|
574
|
+
installed.push(skill.name);
|
|
575
|
+
s.stop(`${skill.name} \u5B89\u88C5\u5B8C\u6210`);
|
|
576
|
+
} else {
|
|
577
|
+
failed.push(skill.name);
|
|
578
|
+
s.stop(`${skill.name} \u5B89\u88C5\u5931\u8D25`);
|
|
579
|
+
p6.log.warning(` ${result.stderr || "\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u624B\u52A8\u5B89\u88C5"}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return { installed, failed };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/steps/clean-env.ts
|
|
586
|
+
import * as p7 from "@clack/prompts";
|
|
587
|
+
import pc6 from "picocolors";
|
|
588
|
+
async function cleanEnv(sensitiveVars) {
|
|
589
|
+
if (sensitiveVars.length === 0) {
|
|
590
|
+
return { removed: [], skipped: [] };
|
|
591
|
+
}
|
|
592
|
+
console.log();
|
|
593
|
+
console.log(pc6.dim(" \u64CD\u4F5C\u6307\u5357:"));
|
|
594
|
+
console.log(pc6.dim(" \u2191/\u2193 \u4E0A\u4E0B\u79FB\u52A8\u5149\u6807"));
|
|
595
|
+
console.log(pc6.dim(" \u7A7A\u683C \u9009\u4E2D/\u53D6\u6D88\u9009\u4E2D\u5F53\u524D\u9879"));
|
|
596
|
+
console.log(pc6.dim(" a \u5168\u9009/\u5168\u4E0D\u9009"));
|
|
597
|
+
console.log(pc6.dim(" \u56DE\u8F66 \u786E\u8BA4\u9009\u62E9"));
|
|
598
|
+
console.log();
|
|
599
|
+
const selected = await p7.multiselect({
|
|
600
|
+
message: "\u9009\u62E9\u8981\u79FB\u9664\u7684\u654F\u611F\u73AF\u5883\u53D8\u91CF (\u7A7A\u683C\u5207\u6362, \u56DE\u8F66\u786E\u8BA4)",
|
|
601
|
+
options: sensitiveVars.map((v) => ({
|
|
602
|
+
value: v.key,
|
|
603
|
+
label: v.key,
|
|
604
|
+
hint: maskValue2(v.value)
|
|
605
|
+
})),
|
|
606
|
+
initialValues: [],
|
|
607
|
+
required: false
|
|
608
|
+
});
|
|
609
|
+
if (p7.isCancel(selected) || selected.length === 0) {
|
|
610
|
+
return { removed: [], skipped: sensitiveVars.map((v) => v.key) };
|
|
611
|
+
}
|
|
612
|
+
const toRemove = selected;
|
|
613
|
+
const removed = [];
|
|
614
|
+
const skipped = [];
|
|
615
|
+
const s = p7.spinner();
|
|
616
|
+
s.start("\u6B63\u5728\u6E05\u7406\u73AF\u5883\u53D8\u91CF...");
|
|
617
|
+
for (const key of toRemove) {
|
|
618
|
+
const success = await removeEnvVar(key);
|
|
619
|
+
if (success) {
|
|
620
|
+
removed.push(key);
|
|
621
|
+
} else {
|
|
622
|
+
skipped.push(key);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
s.stop("\u73AF\u5883\u53D8\u91CF\u6E05\u7406\u5B8C\u6210");
|
|
626
|
+
for (const key of removed) {
|
|
627
|
+
p7.log.success(`${key} \u5DF2\u79FB\u9664`);
|
|
628
|
+
}
|
|
629
|
+
for (const key of skipped) {
|
|
630
|
+
p7.log.warning(`${key} \u79FB\u9664\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u5904\u7406`);
|
|
631
|
+
}
|
|
632
|
+
if (skipped.length > 0 && process.platform !== "win32") {
|
|
633
|
+
p7.log.info("\u624B\u52A8\u79FB\u9664\u65B9\u6CD5:");
|
|
634
|
+
for (const key of skipped) {
|
|
635
|
+
console.log(pc6.dim(` \u4ECE ~/.bashrc \u6216 ~/.zshrc \u4E2D\u5220\u9664: export ${key}=...`));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return { removed, skipped };
|
|
639
|
+
}
|
|
640
|
+
async function removeEnvVar(key) {
|
|
641
|
+
if (process.platform === "win32") {
|
|
642
|
+
const result = await run("powershell", [
|
|
643
|
+
"-Command",
|
|
644
|
+
`[Environment]::SetEnvironmentVariable('${key}', $null, 'User')`
|
|
645
|
+
]);
|
|
646
|
+
return result.exitCode === 0;
|
|
647
|
+
} else {
|
|
648
|
+
delete process.env[key];
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function maskValue2(val) {
|
|
653
|
+
if (val.length <= 8) return "****";
|
|
654
|
+
return val.slice(0, 4) + "..." + val.slice(-4);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/cli.ts
|
|
658
|
+
async function runCli() {
|
|
659
|
+
const results = [];
|
|
660
|
+
const env = await detect();
|
|
661
|
+
const components = await selectComponents(env);
|
|
662
|
+
const mcpServers = components.installMcp ? await selectMcpServers() : [];
|
|
663
|
+
const skills = components.installSkills ? await selectSkills() : [];
|
|
664
|
+
const actions = [];
|
|
665
|
+
if (components.installCli && !env.claudeInstalled) actions.push("\u5B89\u88C5 Claude Code CLI");
|
|
666
|
+
if (components.scaffold) actions.push("\u751F\u6210 CLAUDE.md + .claude/ \u914D\u7F6E");
|
|
667
|
+
if (mcpServers.length > 0) actions.push(`\u5B89\u88C5 ${mcpServers.length} \u4E2A MCP Servers`);
|
|
668
|
+
if (skills.length > 0) actions.push(`\u5B89\u88C5 ${skills.length} \u4E2A Skills`);
|
|
669
|
+
if (components.cleanEnv) actions.push(`\u6E05\u7406 ${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u73AF\u5883\u53D8\u91CF`);
|
|
670
|
+
if (actions.length === 0) {
|
|
671
|
+
p8.log.info("\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u64CD\u4F5C\uFF0C\u9000\u51FA");
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
p8.log.info("\u5373\u5C06\u6267\u884C\u4EE5\u4E0B\u64CD\u4F5C:");
|
|
675
|
+
for (let i = 0; i < actions.length; i++) {
|
|
676
|
+
console.log(pc7.dim(` ${i + 1}. ${actions[i]}`));
|
|
677
|
+
}
|
|
678
|
+
console.log();
|
|
679
|
+
console.log(pc7.dim(" Y/\u56DE\u8F66 \u786E\u8BA4 | N \u53D6\u6D88"));
|
|
680
|
+
console.log();
|
|
681
|
+
const confirmed = await p8.confirm({
|
|
682
|
+
message: "\u786E\u8BA4\u6267\u884C\uFF1F",
|
|
683
|
+
initialValue: true
|
|
684
|
+
});
|
|
685
|
+
if (p8.isCancel(confirmed) || !confirmed) {
|
|
686
|
+
p8.cancel("\u5DF2\u53D6\u6D88");
|
|
687
|
+
process.exit(0);
|
|
688
|
+
}
|
|
689
|
+
if (components.installCli) {
|
|
690
|
+
const r = await installCli(env.claudeInstalled);
|
|
691
|
+
results.push({
|
|
692
|
+
label: r.skipped ? "Claude Code CLI (\u5DF2\u5B89\u88C5)" : "Claude Code CLI \u5B89\u88C5",
|
|
693
|
+
ok: r.success
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if (components.scaffold) {
|
|
697
|
+
const r = await scaffold(process.cwd());
|
|
698
|
+
results.push({ label: "\u914D\u7F6E\u6587\u4EF6\u751F\u6210", ok: true });
|
|
699
|
+
}
|
|
700
|
+
if (mcpServers.length > 0) {
|
|
701
|
+
const r = await installMcp(mcpServers);
|
|
702
|
+
for (const name of r.installed) {
|
|
703
|
+
results.push({ label: `${name} MCP`, ok: true });
|
|
704
|
+
}
|
|
705
|
+
for (const name of r.failed) {
|
|
706
|
+
results.push({ label: `${name} MCP`, ok: false });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
if (skills.length > 0) {
|
|
710
|
+
const r = await installSkills(skills);
|
|
711
|
+
for (const name of r.installed) {
|
|
712
|
+
results.push({ label: `${name} Skill`, ok: true });
|
|
713
|
+
}
|
|
714
|
+
for (const name of r.failed) {
|
|
715
|
+
results.push({ label: `${name} Skill`, ok: false });
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (components.cleanEnv) {
|
|
719
|
+
const r = await cleanEnv(env.sensitiveEnvVars);
|
|
720
|
+
for (const key of r.removed) {
|
|
721
|
+
results.push({ label: `${key} \u5DF2\u79FB\u9664`, ok: true });
|
|
722
|
+
}
|
|
723
|
+
for (const key of r.skipped) {
|
|
724
|
+
results.push({ label: `${key} \u9700\u624B\u52A8\u79FB\u9664`, ok: false });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
summary(results);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/index.ts
|
|
731
|
+
import * as p9 from "@clack/prompts";
|
|
732
|
+
var VERSION = "1.0.0";
|
|
733
|
+
async function main() {
|
|
734
|
+
banner(VERSION);
|
|
735
|
+
p9.intro("\u5F00\u59CB\u914D\u7F6E Claude Code \u73AF\u5883");
|
|
736
|
+
try {
|
|
737
|
+
await runCli();
|
|
738
|
+
} catch (err) {
|
|
739
|
+
if (err instanceof Error) {
|
|
740
|
+
p9.log.error(err.message);
|
|
741
|
+
}
|
|
742
|
+
p9.cancel("\u53D1\u751F\u9519\u8BEF\uFF0C\u5DF2\u9000\u51FA");
|
|
743
|
+
process.exit(1);
|
|
744
|
+
}
|
|
745
|
+
p9.outro("\u611F\u8C22\u4F7F\u7528 ccbot\uFF01");
|
|
746
|
+
}
|
|
747
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ccbot-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code 环境一键配置工具",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ccbot-cli": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup",
|
|
14
|
+
"dev": "tsup --watch",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"claude",
|
|
23
|
+
"claude-code",
|
|
24
|
+
"cli",
|
|
25
|
+
"setup",
|
|
26
|
+
"mcp",
|
|
27
|
+
"ai"
|
|
28
|
+
],
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@clack/prompts": "^0.9.1",
|
|
32
|
+
"execa": "^9.5.2",
|
|
33
|
+
"picocolors": "^1.1.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.10.0",
|
|
37
|
+
"tsup": "^8.3.5",
|
|
38
|
+
"typescript": "^5.7.2"
|
|
39
|
+
}
|
|
40
|
+
}
|