ccbot-cli 1.1.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/dist/index.js +237 -580
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,67 +1,58 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
2
3
|
|
|
3
4
|
// src/index.ts
|
|
4
5
|
import { createRequire } from "module";
|
|
5
6
|
|
|
6
7
|
// src/utils/logger.ts
|
|
7
8
|
import pc from "picocolors";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
function step(n, total, title) {
|
|
10
|
+
console.log(pc.cyan(`
|
|
11
|
+
[${n}/${total}] `) + pc.bold(title));
|
|
12
|
+
}
|
|
13
|
+
function hint(msg) {
|
|
14
|
+
console.log(pc.dim(` ${msg}`));
|
|
15
|
+
}
|
|
16
|
+
function ok(msg) {
|
|
17
|
+
console.log(pc.green(" \u2713 ") + msg);
|
|
18
|
+
}
|
|
19
|
+
function fail(msg) {
|
|
20
|
+
console.log(pc.red(" \u2717 ") + msg);
|
|
21
|
+
}
|
|
22
|
+
function warn(msg) {
|
|
23
|
+
console.log(pc.yellow(" \u26A0 ") + msg);
|
|
24
|
+
}
|
|
25
|
+
function info(msg) {
|
|
26
|
+
console.log(pc.cyan(" \u2139 ") + msg);
|
|
27
|
+
}
|
|
28
|
+
function banner(version2) {
|
|
17
29
|
console.log();
|
|
18
|
-
console.log(pc.cyan(" \
|
|
19
|
-
console.log(pc.cyan(" \u2502 \u2502"));
|
|
20
|
-
console.log(pc.cyan(" \u2502") + " \u{1F916} ccbot - Claude Code \u73AF\u5883\u4E00\u952E\u914D\u7F6E " + pc.cyan("\u2502"));
|
|
21
|
-
console.log(pc.cyan(" \u2502") + pc.dim(` v${version}`) + " " + pc.cyan("\u2502"));
|
|
22
|
-
console.log(pc.cyan(" \u2502 \u2502"));
|
|
23
|
-
console.log(pc.cyan(" \u2502") + pc.dim(" \u81EA\u52A8\u68C0\u6D4B\u73AF\u5883 \xB7 \u5B89\u88C5CLI \xB7 \u914D\u7F6EMCP ") + pc.cyan("\u2502"));
|
|
24
|
-
console.log(pc.cyan(" \u2502") + pc.dim(" \u5B89\u88C5Skills \xB7 \u751F\u6210\u914D\u7F6E \xB7 \u6E05\u7406\u53D8\u91CF ") + pc.cyan("\u2502"));
|
|
25
|
-
console.log(pc.cyan(" \u2502 \u2502"));
|
|
26
|
-
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\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
30
|
+
console.log(pc.cyan(" \u{1F916} ccbot") + pc.dim(` v${version2}`) + pc.cyan(" \u2014 Claude Code \u73AF\u5883\u4E00\u952E\u914D\u7F6E"));
|
|
27
31
|
console.log();
|
|
28
32
|
}
|
|
29
|
-
function
|
|
33
|
+
function done(items) {
|
|
30
34
|
const okCount = items.filter((i) => i.ok).length;
|
|
31
35
|
const failCount = items.filter((i) => !i.ok).length;
|
|
32
36
|
console.log();
|
|
33
|
-
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\u2500\u2500\u2500\u2500\u2500\u256E"));
|
|
34
|
-
console.log(pc.cyan(" \u2502 \u2502"));
|
|
35
37
|
if (failCount === 0) {
|
|
36
|
-
console.log(pc.
|
|
38
|
+
console.log(pc.green(pc.bold(" \u2713 \u5168\u90E8\u5B8C\u6210!")));
|
|
37
39
|
} else {
|
|
38
|
-
console.log(pc.
|
|
40
|
+
console.log(pc.yellow(` \u26A0 \u5B8C\u6210 ${okCount} \u9879, ${failCount} \u9879\u9700\u6CE8\u610F`));
|
|
39
41
|
}
|
|
40
|
-
console.log(pc.cyan(" \u2502 \u2502"));
|
|
41
|
-
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\u2500\u2500\u2500\u2500\u2500\u256F"));
|
|
42
42
|
console.log();
|
|
43
43
|
for (const item of items) {
|
|
44
|
-
if (item.ok)
|
|
45
|
-
|
|
46
|
-
} else {
|
|
47
|
-
log.error(item.label);
|
|
48
|
-
}
|
|
44
|
+
if (item.ok) ok(item.label);
|
|
45
|
+
else fail(item.label);
|
|
49
46
|
}
|
|
50
47
|
console.log();
|
|
51
|
-
console.log(pc.
|
|
52
|
-
console.log(pc.
|
|
53
|
-
console.log(pc.
|
|
54
|
-
console.log(pc.cyan(" \u2502 ") + "3. \u67E5\u770B " + pc.green(".claude/settings.json") + " \u786E\u8BA4 MCP \u914D\u7F6E");
|
|
55
|
-
console.log(pc.cyan(" \u2514"));
|
|
48
|
+
console.log(pc.dim(" \u4E0B\u4E00\u6B65:"));
|
|
49
|
+
console.log(pc.dim(" 1. ") + pc.green("claude") + pc.dim(" \u542F\u52A8 Claude Code"));
|
|
50
|
+
console.log(pc.dim(" 2. \u7F16\u8F91 ") + pc.green("CLAUDE.md") + pc.dim(" \u81EA\u5B9A\u4E49\u6307\u4EE4"));
|
|
56
51
|
console.log();
|
|
57
52
|
}
|
|
58
53
|
|
|
59
54
|
// src/cli.ts
|
|
60
|
-
import
|
|
61
|
-
import pc8 from "picocolors";
|
|
62
|
-
|
|
63
|
-
// src/steps/detect.ts
|
|
64
|
-
import * as p from "@clack/prompts";
|
|
55
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
65
56
|
import pc3 from "picocolors";
|
|
66
57
|
|
|
67
58
|
// src/utils/exec.ts
|
|
@@ -90,40 +81,11 @@ async function npmInstallGlobal(pkg) {
|
|
|
90
81
|
return run("npm", ["install", "-g", pkg]);
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
// src/utils/hints.ts
|
|
94
|
-
import pc2 from "picocolors";
|
|
95
|
-
function hintMultiselect() {
|
|
96
|
-
console.log();
|
|
97
|
-
console.log(pc2.cyan(" \u250C ") + pc2.bold("\u64CD\u4F5C\u6307\u5357"));
|
|
98
|
-
console.log(pc2.cyan(" \u2502 ") + pc2.dim("\u2191\u2193") + " \u79FB\u52A8\u5149\u6807 " + pc2.dim("\u7A7A\u683C") + " \u9009\u4E2D/\u53D6\u6D88");
|
|
99
|
-
console.log(pc2.cyan(" \u2502 ") + pc2.dim("a") + " \u5168\u9009/\u5168\u4E0D\u9009 " + pc2.dim("\u56DE\u8F66") + " \u786E\u8BA4\u63D0\u4EA4");
|
|
100
|
-
console.log(pc2.cyan(" \u2514"));
|
|
101
|
-
console.log();
|
|
102
|
-
}
|
|
103
|
-
function hintSelect() {
|
|
104
|
-
console.log();
|
|
105
|
-
console.log(pc2.cyan(" \u250C ") + pc2.bold("\u64CD\u4F5C\u6307\u5357"));
|
|
106
|
-
console.log(pc2.cyan(" \u2502 ") + pc2.dim("\u2191\u2193") + " \u79FB\u52A8\u5149\u6807 " + pc2.dim("\u56DE\u8F66") + " \u786E\u8BA4\u9009\u62E9");
|
|
107
|
-
console.log(pc2.cyan(" \u2514"));
|
|
108
|
-
console.log();
|
|
109
|
-
}
|
|
110
|
-
function hintConfirm() {
|
|
111
|
-
console.log(pc2.dim(" Y/\u56DE\u8F66 \u786E\u8BA4 \u2502 N \u53D6\u6D88"));
|
|
112
|
-
}
|
|
113
|
-
function stepHeader(current, total, title) {
|
|
114
|
-
console.log();
|
|
115
|
-
console.log(
|
|
116
|
-
pc2.cyan(" \u2501\u2501\u2501 ") + pc2.bold(`\u6B65\u9AA4 ${current}/${total}`) + pc2.cyan(" \u2501 ") + pc2.white(title) + pc2.cyan(" \u2501\u2501\u2501")
|
|
117
|
-
);
|
|
118
|
-
console.log();
|
|
119
|
-
}
|
|
120
|
-
|
|
121
84
|
// src/steps/detect.ts
|
|
122
85
|
var SENSITIVE_PATTERNS = [
|
|
123
86
|
/^ANTHROPIC_API_KEY$/i,
|
|
124
87
|
/^CLAUDE_API_KEY$/i,
|
|
125
88
|
/^OPENAI_API_KEY$/i,
|
|
126
|
-
/^OPENAI_ORG_ID$/i,
|
|
127
89
|
/^GOOGLE_API_KEY$/i,
|
|
128
90
|
/^AZURE_OPENAI_API_KEY$/i,
|
|
129
91
|
/^HUGGINGFACE_TOKEN$/i,
|
|
@@ -136,156 +98,97 @@ var SENSITIVE_PATTERNS = [
|
|
|
136
98
|
/_TOKEN$/i,
|
|
137
99
|
/_API_KEY$/i
|
|
138
100
|
];
|
|
139
|
-
function
|
|
140
|
-
|
|
141
|
-
return val.slice(0, 4) + "..." + val.slice(-4);
|
|
142
|
-
}
|
|
143
|
-
function scanSensitiveEnvVars() {
|
|
144
|
-
const found = [];
|
|
145
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
146
|
-
if (!value) continue;
|
|
147
|
-
for (const pattern of SENSITIVE_PATTERNS) {
|
|
148
|
-
if (pattern.test(key)) {
|
|
149
|
-
found.push({ key, value });
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return found;
|
|
155
|
-
}
|
|
156
|
-
async function detect(totalSteps) {
|
|
157
|
-
stepHeader(1, totalSteps, "\u73AF\u5883\u68C0\u6D4B");
|
|
158
|
-
const s = p.spinner();
|
|
159
|
-
s.start("\u6B63\u5728\u626B\u63CF\u7CFB\u7EDF\u73AF\u5883...");
|
|
101
|
+
async function detect(total) {
|
|
102
|
+
step(1, total, "\u73AF\u5883\u68C0\u6D4B");
|
|
160
103
|
const nodeVersion = process.version;
|
|
161
104
|
const npmResult = await run("npm", ["--version"]);
|
|
162
105
|
const npmVersion = npmResult.stdout.trim();
|
|
163
|
-
const osMap = {
|
|
164
|
-
win32: "Windows",
|
|
165
|
-
darwin: "macOS",
|
|
166
|
-
linux: "Linux"
|
|
167
|
-
};
|
|
106
|
+
const osMap = { win32: "Windows", darwin: "macOS", linux: "Linux" };
|
|
168
107
|
const os = osMap[process.platform] ?? process.platform;
|
|
169
108
|
const claudeInstalled = await commandExists("claude");
|
|
170
109
|
let claudeVersion = null;
|
|
171
110
|
if (claudeInstalled) {
|
|
172
|
-
const
|
|
173
|
-
claudeVersion =
|
|
111
|
+
const r = await run("claude", ["--version"]);
|
|
112
|
+
claudeVersion = r.stdout.trim();
|
|
174
113
|
}
|
|
175
|
-
const sensitiveEnvVars =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (claudeInstalled) {
|
|
182
|
-
lines.push(`${pc3.green("\u2713")} Claude CLI ${claudeVersion}`);
|
|
183
|
-
} else {
|
|
184
|
-
lines.push(`${pc3.yellow("\u2717")} Claude CLI \u672A\u5B89\u88C5 (\u5C06\u5728\u540E\u7EED\u6B65\u9AA4\u5B89\u88C5)`);
|
|
114
|
+
const sensitiveEnvVars = [];
|
|
115
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
116
|
+
if (!value) continue;
|
|
117
|
+
if (SENSITIVE_PATTERNS.some((p) => p.test(key))) {
|
|
118
|
+
sensitiveEnvVars.push({ key, value });
|
|
119
|
+
}
|
|
185
120
|
}
|
|
121
|
+
ok(`Node ${nodeVersion} \xB7 npm v${npmVersion} \xB7 ${os}`);
|
|
122
|
+
if (claudeInstalled) ok(`Claude CLI ${claudeVersion}`);
|
|
123
|
+
else warn("Claude CLI \u672A\u5B89\u88C5");
|
|
186
124
|
if (sensitiveEnvVars.length > 0) {
|
|
187
|
-
|
|
188
|
-
lines.push(`${pc3.yellow("\u26A0")} \u68C0\u6D4B\u5230 ${sensitiveEnvVars.length} \u4E2A\u654F\u611F\u73AF\u5883\u53D8\u91CF:`);
|
|
189
|
-
for (const { key, value } of sensitiveEnvVars) {
|
|
190
|
-
lines.push(` ${pc3.yellow("\xB7")} ${key} = ${pc3.dim(maskValue(value))}`);
|
|
191
|
-
}
|
|
125
|
+
warn(`\u53D1\u73B0 ${sensitiveEnvVars.length} \u4E2A\u654F\u611F\u73AF\u5883\u53D8\u91CF`);
|
|
192
126
|
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
nodeVersion,
|
|
196
|
-
npmVersion,
|
|
197
|
-
os,
|
|
198
|
-
platform: process.platform,
|
|
199
|
-
claudeInstalled,
|
|
200
|
-
claudeVersion,
|
|
201
|
-
sensitiveEnvVars
|
|
202
|
-
};
|
|
127
|
+
return { nodeVersion, npmVersion, os, platform: process.platform, claudeInstalled, claudeVersion, sensitiveEnvVars };
|
|
203
128
|
}
|
|
204
129
|
|
|
205
130
|
// src/steps/select.ts
|
|
206
|
-
import
|
|
207
|
-
async function selectComponents(env,
|
|
208
|
-
|
|
209
|
-
|
|
131
|
+
import { checkbox } from "@inquirer/prompts";
|
|
132
|
+
async function selectComponents(env, total) {
|
|
133
|
+
step(2, total, "\u9009\u62E9\u7EC4\u4EF6");
|
|
134
|
+
hint("\u7A7A\u683C=\u5207\u6362 a=\u5168\u9009 \u56DE\u8F66=\u786E\u8BA4");
|
|
135
|
+
const choices = [
|
|
210
136
|
{
|
|
211
137
|
value: "installCli",
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
value: "scaffold",
|
|
217
|
-
label: "\u9879\u76EE\u914D\u7F6E\u6587\u4EF6",
|
|
218
|
-
hint: "\u751F\u6210 CLAUDE.md / .claude/ / .claudeignore"
|
|
219
|
-
},
|
|
220
|
-
{
|
|
221
|
-
value: "installMcp",
|
|
222
|
-
label: "MCP Servers",
|
|
223
|
-
hint: "\u914D\u7F6E AI \u5DE5\u5177\u670D\u52A1 (\u6D4F\u89C8\u5668/\u641C\u7D22/\u6587\u6863\u7B49)"
|
|
138
|
+
name: `Claude Code CLI ${env.claudeInstalled ? "(\u5DF2\u5B89\u88C5, \u8DF3\u8FC7)" : "(\u672A\u5B89\u88C5)"}`,
|
|
139
|
+
checked: true
|
|
224
140
|
},
|
|
225
|
-
{
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
hint: "\u589E\u5F3A Claude Code \u5DE5\u4F5C\u6D41\u80FD\u529B"
|
|
229
|
-
}
|
|
141
|
+
{ value: "scaffold", name: "\u9879\u76EE\u914D\u7F6E\u6587\u4EF6 (CLAUDE.md + .claude/)", checked: true },
|
|
142
|
+
{ value: "installMcp", name: "MCP Servers (AI\u5DE5\u5177\u670D\u52A1)", checked: true },
|
|
143
|
+
{ value: "installSkills", name: "Skills / Plugins (\u5DE5\u4F5C\u6D41\u589E\u5F3A)", checked: true }
|
|
230
144
|
];
|
|
231
145
|
if (env.sensitiveEnvVars.length > 0) {
|
|
232
|
-
|
|
146
|
+
choices.push({
|
|
233
147
|
value: "cleanEnv",
|
|
234
|
-
|
|
235
|
-
|
|
148
|
+
name: `\u73AF\u5883\u53D8\u91CF\u6E05\u7406 (${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u53D8\u91CF)`,
|
|
149
|
+
checked: true
|
|
236
150
|
});
|
|
237
151
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
options,
|
|
242
|
-
initialValues: options.map((o) => o.value),
|
|
152
|
+
const selected = await checkbox({
|
|
153
|
+
message: "\u5B89\u88C5\u7EC4\u4EF6",
|
|
154
|
+
choices,
|
|
243
155
|
required: true
|
|
244
156
|
});
|
|
245
|
-
if (p2.isCancel(selected)) {
|
|
246
|
-
p2.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
247
|
-
process.exit(0);
|
|
248
|
-
}
|
|
249
|
-
const values = selected;
|
|
250
157
|
return {
|
|
251
|
-
installCli:
|
|
252
|
-
scaffold:
|
|
253
|
-
installMcp:
|
|
254
|
-
installSkills:
|
|
255
|
-
cleanEnv:
|
|
158
|
+
installCli: selected.includes("installCli"),
|
|
159
|
+
scaffold: selected.includes("scaffold"),
|
|
160
|
+
installMcp: selected.includes("installMcp"),
|
|
161
|
+
installSkills: selected.includes("installSkills"),
|
|
162
|
+
cleanEnv: selected.includes("cleanEnv")
|
|
256
163
|
};
|
|
257
164
|
}
|
|
258
165
|
|
|
259
166
|
// src/steps/install-cli.ts
|
|
260
|
-
import * as p3 from "@clack/prompts";
|
|
261
167
|
async function installCli(alreadyInstalled) {
|
|
262
168
|
if (alreadyInstalled) {
|
|
263
|
-
|
|
169
|
+
info("Claude CLI \u5DF2\u5B89\u88C5, \u8DF3\u8FC7");
|
|
264
170
|
return { success: true, version: null, skipped: true };
|
|
265
171
|
}
|
|
266
|
-
|
|
267
|
-
s.start("\u6B63\u5728\u5B89\u88C5 Claude Code CLI...");
|
|
172
|
+
info("\u6B63\u5728\u5B89\u88C5 Claude Code CLI...");
|
|
268
173
|
const result = await npmInstallGlobal("@anthropic-ai/claude-code");
|
|
269
174
|
if (result.exitCode !== 0) {
|
|
270
|
-
|
|
271
|
-
|
|
175
|
+
fail("Claude CLI \u5B89\u88C5\u5931\u8D25");
|
|
176
|
+
warn("\u624B\u52A8: npm install -g @anthropic-ai/claude-code");
|
|
272
177
|
return { success: false, version: null, skipped: false };
|
|
273
178
|
}
|
|
274
179
|
const exists = await commandExists("claude");
|
|
275
180
|
if (!exists) {
|
|
276
|
-
|
|
277
|
-
p3.log.warning("\u5B89\u88C5\u5B8C\u6210\u4F46 claude \u547D\u4EE4\u4E0D\u53EF\u7528\uFF0C\u53EF\u80FD\u9700\u8981\u91CD\u542F\u7EC8\u7AEF");
|
|
181
|
+
warn("\u5B89\u88C5\u5B8C\u6210\u4F46 claude \u547D\u4EE4\u4E0D\u53EF\u7528, \u53EF\u80FD\u9700\u91CD\u542F\u7EC8\u7AEF");
|
|
278
182
|
return { success: false, version: null, skipped: false };
|
|
279
183
|
}
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
return { success: true, version, skipped: false };
|
|
184
|
+
const v = await run("claude", ["--version"]);
|
|
185
|
+
const version2 = v.stdout.trim();
|
|
186
|
+
ok(`Claude CLI ${version2}`);
|
|
187
|
+
return { success: true, version: version2, skipped: false };
|
|
284
188
|
}
|
|
285
189
|
|
|
286
190
|
// src/steps/scaffold.ts
|
|
287
|
-
import
|
|
288
|
-
import pc4 from "picocolors";
|
|
191
|
+
import { select } from "@inquirer/prompts";
|
|
289
192
|
import { join as join2 } from "path";
|
|
290
193
|
|
|
291
194
|
// src/utils/fs.ts
|
|
@@ -333,71 +236,57 @@ function deepMerge(target, source) {
|
|
|
333
236
|
}
|
|
334
237
|
|
|
335
238
|
// src/steps/scaffold.ts
|
|
336
|
-
var
|
|
239
|
+
var TEMPLATES = {
|
|
240
|
+
minimal: `# Project Instructions
|
|
337
241
|
|
|
338
242
|
- Follow existing code style
|
|
339
243
|
- Read before write
|
|
340
244
|
- Keep changes minimal
|
|
341
|
-
|
|
342
|
-
|
|
245
|
+
`,
|
|
246
|
+
standard: `# Project Instructions
|
|
343
247
|
|
|
344
248
|
## Overview
|
|
345
|
-
<!--
|
|
249
|
+
<!-- Project purpose and tech stack -->
|
|
346
250
|
|
|
347
251
|
## Coding Standards
|
|
348
|
-
- Follow existing
|
|
349
|
-
- Read
|
|
350
|
-
- Keep changes focused
|
|
252
|
+
- Follow existing patterns
|
|
253
|
+
- Read context before modifying
|
|
254
|
+
- Keep changes focused
|
|
351
255
|
|
|
352
256
|
## Key Files
|
|
353
|
-
<!--
|
|
257
|
+
<!-- Important file paths -->
|
|
354
258
|
|
|
355
259
|
## Notes
|
|
356
260
|
<!-- Special considerations -->
|
|
357
|
-
|
|
358
|
-
|
|
261
|
+
`,
|
|
262
|
+
detailed: `# Project Instructions
|
|
359
263
|
|
|
360
|
-
##
|
|
361
|
-
<!--
|
|
264
|
+
## Overview
|
|
265
|
+
<!-- Project purpose, goals, tech stack -->
|
|
362
266
|
|
|
363
267
|
## Architecture
|
|
364
|
-
<!--
|
|
268
|
+
<!-- High-level design decisions -->
|
|
365
269
|
|
|
366
270
|
## Coding Standards
|
|
367
|
-
- Follow existing
|
|
368
|
-
- Read
|
|
369
|
-
- Keep changes focused and minimal
|
|
271
|
+
- Follow existing patterns
|
|
272
|
+
- Read context before modifying
|
|
370
273
|
- Write clear commit messages
|
|
371
274
|
- Add comments for complex logic
|
|
372
275
|
|
|
373
276
|
## Key Files
|
|
374
|
-
<!--
|
|
277
|
+
<!-- Important paths and purposes -->
|
|
375
278
|
|
|
376
279
|
## Dependencies
|
|
377
|
-
<!--
|
|
280
|
+
<!-- Key deps and roles -->
|
|
378
281
|
|
|
379
282
|
## Testing
|
|
380
|
-
<!--
|
|
283
|
+
<!-- How to run tests -->
|
|
381
284
|
|
|
382
285
|
## Notes
|
|
383
|
-
<!--
|
|
384
|
-
|
|
385
|
-
var PERMISSIONS = {
|
|
386
|
-
strict: {
|
|
387
|
-
allow: ["Read", "Glob", "Grep"],
|
|
388
|
-
deny: ["Bash", "Write", "Edit"]
|
|
389
|
-
},
|
|
390
|
-
normal: {
|
|
391
|
-
allow: ["Read", "Glob", "Grep", "WebFetch", "WebSearch"],
|
|
392
|
-
deny: []
|
|
393
|
-
},
|
|
394
|
-
permissive: {
|
|
395
|
-
allow: ["Read", "Glob", "Grep", "Write", "Edit", "Bash", "WebFetch", "WebSearch"],
|
|
396
|
-
deny: []
|
|
397
|
-
}
|
|
286
|
+
<!-- Gotchas and context -->
|
|
287
|
+
`
|
|
398
288
|
};
|
|
399
|
-
var CLAUDEIGNORE =
|
|
400
|
-
node_modules/
|
|
289
|
+
var CLAUDEIGNORE = `node_modules/
|
|
401
290
|
dist/
|
|
402
291
|
build/
|
|
403
292
|
.env
|
|
@@ -407,105 +296,38 @@ build/
|
|
|
407
296
|
coverage/
|
|
408
297
|
.git/
|
|
409
298
|
`;
|
|
410
|
-
async function scaffoldConfig(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
{
|
|
417
|
-
|
|
418
|
-
label: "\u6781\u7B80",
|
|
419
|
-
hint: "\u51E0\u884C\u6838\u5FC3\u89C4\u5219, \u9002\u5408\u5C0F\u9879\u76EE"
|
|
420
|
-
},
|
|
421
|
-
{
|
|
422
|
-
value: "standard",
|
|
423
|
-
label: "\u6807\u51C6 (\u63A8\u8350)",
|
|
424
|
-
hint: "\u5E38\u7528\u5206\u533A\u6A21\u677F, \u9002\u5408\u5927\u591A\u6570\u9879\u76EE"
|
|
425
|
-
},
|
|
426
|
-
{
|
|
427
|
-
value: "detailed",
|
|
428
|
-
label: "\u8BE6\u7EC6",
|
|
429
|
-
hint: "\u5B8C\u6574\u5206\u533A\u542B\u67B6\u6784/\u6D4B\u8BD5/\u4F9D\u8D56, \u9002\u5408\u5927\u578B\u9879\u76EE"
|
|
430
|
-
}
|
|
431
|
-
],
|
|
432
|
-
initialValue: "standard"
|
|
433
|
-
});
|
|
434
|
-
if (p4.isCancel(style)) {
|
|
435
|
-
p4.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
436
|
-
process.exit(0);
|
|
437
|
-
}
|
|
438
|
-
hintSelect();
|
|
439
|
-
const permission = await p4.select({
|
|
440
|
-
message: "Claude Code \u6743\u9650\u7EA7\u522B",
|
|
441
|
-
options: [
|
|
442
|
-
{
|
|
443
|
-
value: "strict",
|
|
444
|
-
label: "\u4E25\u683C",
|
|
445
|
-
hint: "\u53EA\u8BFB, \u7981\u6B62\u5199\u5165\u548C\u6267\u884C\u547D\u4EE4"
|
|
446
|
-
},
|
|
447
|
-
{
|
|
448
|
-
value: "normal",
|
|
449
|
-
label: "\u6807\u51C6 (\u63A8\u8350)",
|
|
450
|
-
hint: "\u8BFB\u53D6+\u641C\u7D22+\u7F51\u7EDC, \u5199\u5165\u9700\u786E\u8BA4"
|
|
451
|
-
},
|
|
452
|
-
{
|
|
453
|
-
value: "permissive",
|
|
454
|
-
label: "\u5BBD\u677E",
|
|
455
|
-
hint: "\u8BFB\u5199+\u6267\u884C+\u7F51\u7EDC, \u5168\u90E8\u81EA\u52A8\u5141\u8BB8"
|
|
456
|
-
}
|
|
299
|
+
async function scaffoldConfig(total) {
|
|
300
|
+
step(3, total, "CLAUDE.md \u6A21\u677F");
|
|
301
|
+
const style = await select({
|
|
302
|
+
message: "\u9009\u62E9\u6A21\u677F\u98CE\u683C",
|
|
303
|
+
choices: [
|
|
304
|
+
{ value: "minimal", name: "\u6781\u7B80 \u2014 \u51E0\u884C\u6838\u5FC3\u89C4\u5219" },
|
|
305
|
+
{ value: "standard", name: "\u6807\u51C6 \u2014 \u5E38\u7528\u5206\u533A (\u63A8\u8350)" },
|
|
306
|
+
{ value: "detailed", name: "\u8BE6\u7EC6 \u2014 \u542B\u67B6\u6784/\u6D4B\u8BD5/\u4F9D\u8D56" }
|
|
457
307
|
],
|
|
458
|
-
|
|
308
|
+
default: "standard"
|
|
459
309
|
});
|
|
460
|
-
|
|
461
|
-
p4.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
462
|
-
process.exit(0);
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
style,
|
|
466
|
-
permission
|
|
467
|
-
};
|
|
310
|
+
return { style };
|
|
468
311
|
}
|
|
469
|
-
async function scaffold(targetDir,
|
|
470
|
-
const s = p4.spinner();
|
|
471
|
-
s.start("\u6B63\u5728\u751F\u6210\u914D\u7F6E\u6587\u4EF6...");
|
|
312
|
+
async function scaffold(targetDir, opts) {
|
|
472
313
|
const files = [];
|
|
473
|
-
const
|
|
474
|
-
minimal: CLAUDE_MD_MINIMAL,
|
|
475
|
-
standard: CLAUDE_MD_STANDARD,
|
|
476
|
-
detailed: CLAUDE_MD_DETAILED
|
|
477
|
-
};
|
|
478
|
-
const claudeMdPath = join2(targetDir, "CLAUDE.md");
|
|
479
|
-
const r1 = writeFileSafe(claudeMdPath, templates[options.style]);
|
|
314
|
+
const r1 = writeFileSafe(join2(targetDir, "CLAUDE.md"), TEMPLATES[opts.style]);
|
|
480
315
|
files.push({ path: "CLAUDE.md", written: r1.written });
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
const
|
|
484
|
-
const settingsData = {
|
|
485
|
-
permissions: PERMISSIONS[options.permission],
|
|
486
|
-
mcpServers: {}
|
|
487
|
-
};
|
|
488
|
-
const r2 = writeFileSafe(settingsPath, JSON.stringify(settingsData, null, 2) + "\n");
|
|
316
|
+
ensureDir(join2(targetDir, ".claude"));
|
|
317
|
+
const settings = { permissions: { allow: ["Read", "Glob", "Grep", "WebFetch", "WebSearch"], deny: [] }, mcpServers: {} };
|
|
318
|
+
const r2 = writeFileSafe(join2(targetDir, ".claude", "settings.json"), JSON.stringify(settings, null, 2) + "\n");
|
|
489
319
|
files.push({ path: ".claude/settings.json", written: r2.written });
|
|
490
|
-
const
|
|
491
|
-
const r3 = writeFileSafe(ignorePath, CLAUDEIGNORE);
|
|
320
|
+
const r3 = writeFileSafe(join2(targetDir, ".claudeignore"), CLAUDEIGNORE);
|
|
492
321
|
files.push({ path: ".claudeignore", written: r3.written });
|
|
493
|
-
s.stop("\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5B8C\u6210");
|
|
494
|
-
const lines = [];
|
|
495
322
|
for (const f of files) {
|
|
496
|
-
if (f.written)
|
|
497
|
-
|
|
498
|
-
} else {
|
|
499
|
-
lines.push(`${pc4.yellow("\u2192")} ${f.path} (\u5DF2\u5B58\u5728, \u8DF3\u8FC7)`);
|
|
500
|
-
}
|
|
323
|
+
if (f.written) ok(f.path);
|
|
324
|
+
else info(`${f.path} (\u5DF2\u5B58\u5728, \u8DF3\u8FC7)`);
|
|
501
325
|
}
|
|
502
|
-
p4.note(lines.join("\n"), "\u751F\u6210\u7684\u6587\u4EF6");
|
|
503
326
|
return { files };
|
|
504
327
|
}
|
|
505
328
|
|
|
506
329
|
// src/steps/install-mcp.ts
|
|
507
|
-
import
|
|
508
|
-
import pc5 from "picocolors";
|
|
330
|
+
import { checkbox as checkbox2, confirm, input } from "@inquirer/prompts";
|
|
509
331
|
import { join as join3 } from "path";
|
|
510
332
|
import { homedir } from "os";
|
|
511
333
|
|
|
@@ -570,127 +392,73 @@ var MCP_SERVERS = [
|
|
|
570
392
|
];
|
|
571
393
|
|
|
572
394
|
// src/steps/install-mcp.ts
|
|
573
|
-
async function selectMcpServers(
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
"\u4F8B\u5982: \u6D4F\u89C8\u5668\u64CD\u4F5C\u3001\u6587\u4EF6\u641C\u7D22\u3001\u7F51\u9875\u641C\u7D22\u3001\u6587\u6863\u67E5\u8BE2\u7B49",
|
|
580
|
-
"\u5B89\u88C5\u540E\u4F1A\u5199\u5165 .claude/settings.json, \u65E0\u9700\u5168\u5C40\u5B89\u88C5"
|
|
581
|
-
].join("\n"),
|
|
582
|
-
"\u4EC0\u4E48\u662F MCP Servers?"
|
|
583
|
-
);
|
|
584
|
-
hintMultiselect();
|
|
585
|
-
const selected = await p5.multiselect({
|
|
586
|
-
message: "\u9009\u62E9\u8981\u914D\u7F6E\u7684 MCP Servers",
|
|
587
|
-
options: MCP_SERVERS.map((s) => ({
|
|
395
|
+
async function selectMcpServers(total) {
|
|
396
|
+
step(4, total, "MCP Servers");
|
|
397
|
+
hint("\u7A7A\u683C=\u5207\u6362 a=\u5168\u9009 \u56DE\u8F66=\u786E\u8BA4");
|
|
398
|
+
const selected = await checkbox2({
|
|
399
|
+
message: "\u9009\u62E9 MCP Servers",
|
|
400
|
+
choices: MCP_SERVERS.map((s) => ({
|
|
588
401
|
value: s.name,
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}))
|
|
592
|
-
initialValues: MCP_SERVERS.map((s) => s.name),
|
|
593
|
-
required: false
|
|
594
|
-
});
|
|
595
|
-
if (p5.isCancel(selected)) {
|
|
596
|
-
return [];
|
|
597
|
-
}
|
|
598
|
-
const names = selected;
|
|
599
|
-
const servers = MCP_SERVERS.filter((s) => names.includes(s.name));
|
|
600
|
-
const addCustom = await p5.confirm({
|
|
601
|
-
message: "\u662F\u5426\u6DFB\u52A0\u81EA\u5B9A\u4E49 MCP Server (npm \u5305)?",
|
|
602
|
-
initialValue: false
|
|
402
|
+
name: `${s.name} \u2014 ${s.description}`,
|
|
403
|
+
checked: true
|
|
404
|
+
}))
|
|
603
405
|
});
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (!val.trim()) return "\u8BF7\u8F93\u5165\u81F3\u5C11\u4E00\u4E2A\u5305\u540D";
|
|
610
|
-
}
|
|
406
|
+
const servers = MCP_SERVERS.filter((s) => selected.includes(s.name));
|
|
407
|
+
const addCustom = await confirm({ message: "\u6DFB\u52A0\u81EA\u5B9A\u4E49 MCP (npm\u5305)?", default: false });
|
|
408
|
+
if (addCustom) {
|
|
409
|
+
const raw = await input({
|
|
410
|
+
message: "npm \u5305\u540D (\u9017\u53F7\u5206\u9694)"
|
|
611
411
|
});
|
|
612
|
-
if (
|
|
613
|
-
const
|
|
614
|
-
for (const pkg of customPkgs) {
|
|
412
|
+
if (raw.trim()) {
|
|
413
|
+
for (const pkg of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
615
414
|
const name = pkg.split("/").pop()?.replace(/^mcp-/, "") ?? pkg;
|
|
616
|
-
servers.push({
|
|
617
|
-
name,
|
|
618
|
-
package: pkg,
|
|
619
|
-
description: `\u81EA\u5B9A\u4E49: ${pkg}`,
|
|
620
|
-
scope: "user",
|
|
621
|
-
command: "npx",
|
|
622
|
-
args: ["-y", pkg]
|
|
623
|
-
});
|
|
415
|
+
servers.push({ name, package: pkg, description: `\u81EA\u5B9A\u4E49: ${pkg}`, scope: "user", command: "npx", args: ["-y", pkg] });
|
|
624
416
|
}
|
|
625
417
|
}
|
|
626
418
|
}
|
|
627
419
|
return servers;
|
|
628
420
|
}
|
|
629
421
|
async function installMcp(servers) {
|
|
630
|
-
if (servers.length === 0) {
|
|
631
|
-
return { installed: [], failed: [] };
|
|
632
|
-
}
|
|
633
|
-
const s = p5.spinner();
|
|
634
|
-
s.start("\u6B63\u5728\u914D\u7F6E MCP Servers...");
|
|
422
|
+
if (servers.length === 0) return { installed: [], failed: [] };
|
|
635
423
|
const installed = [];
|
|
636
424
|
const failed = [];
|
|
637
|
-
const userServers = servers.filter((
|
|
638
|
-
const projectServers = servers.filter((
|
|
425
|
+
const userServers = servers.filter((s) => s.scope === "user");
|
|
426
|
+
const projectServers = servers.filter((s) => s.scope === "project");
|
|
639
427
|
if (userServers.length > 0) {
|
|
640
|
-
const
|
|
641
|
-
ensureDir(
|
|
642
|
-
const
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
mcpConfig[sv.name] = {
|
|
646
|
-
command: sv.command,
|
|
647
|
-
args: sv.args,
|
|
648
|
-
...sv.env ? { env: sv.env } : {}
|
|
649
|
-
};
|
|
428
|
+
const dir = join3(homedir(), ".claude");
|
|
429
|
+
ensureDir(dir);
|
|
430
|
+
const cfg = {};
|
|
431
|
+
for (const s of userServers) {
|
|
432
|
+
cfg[s.name] = { command: s.command, args: s.args, ...s.env ? { env: s.env } : {} };
|
|
650
433
|
}
|
|
651
434
|
try {
|
|
652
|
-
mergeJsonFile(
|
|
653
|
-
|
|
435
|
+
mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
|
|
436
|
+
userServers.forEach((s) => installed.push(s.name));
|
|
654
437
|
} catch {
|
|
655
|
-
|
|
438
|
+
userServers.forEach((s) => failed.push(s.name));
|
|
656
439
|
}
|
|
657
440
|
}
|
|
658
441
|
if (projectServers.length > 0) {
|
|
659
|
-
const
|
|
660
|
-
ensureDir(
|
|
661
|
-
const
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
mcpConfig[sv.name] = {
|
|
665
|
-
command: sv.command,
|
|
666
|
-
args: sv.args,
|
|
667
|
-
...sv.env ? { env: sv.env } : {}
|
|
668
|
-
};
|
|
442
|
+
const dir = join3(process.cwd(), ".claude");
|
|
443
|
+
ensureDir(dir);
|
|
444
|
+
const cfg = {};
|
|
445
|
+
for (const s of projectServers) {
|
|
446
|
+
cfg[s.name] = { command: s.command, args: s.args, ...s.env ? { env: s.env } : {} };
|
|
669
447
|
}
|
|
670
448
|
try {
|
|
671
|
-
mergeJsonFile(
|
|
672
|
-
|
|
449
|
+
mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
|
|
450
|
+
projectServers.forEach((s) => installed.push(s.name));
|
|
673
451
|
} catch {
|
|
674
|
-
|
|
452
|
+
projectServers.forEach((s) => failed.push(s.name));
|
|
675
453
|
}
|
|
676
454
|
}
|
|
677
|
-
|
|
678
|
-
const
|
|
679
|
-
for (const name of installed) {
|
|
680
|
-
lines.push(`${pc5.green("\u2713")} ${name}`);
|
|
681
|
-
}
|
|
682
|
-
for (const name of failed) {
|
|
683
|
-
lines.push(`${pc5.red("\u2717")} ${name} (\u914D\u7F6E\u5931\u8D25)`);
|
|
684
|
-
}
|
|
685
|
-
if (lines.length > 0) {
|
|
686
|
-
p5.note(lines.join("\n"), "MCP \u914D\u7F6E\u7ED3\u679C");
|
|
687
|
-
}
|
|
455
|
+
for (const n of installed) ok(`${n} MCP`);
|
|
456
|
+
for (const n of failed) fail(`${n} MCP`);
|
|
688
457
|
return { installed, failed };
|
|
689
458
|
}
|
|
690
459
|
|
|
691
460
|
// src/steps/install-skills.ts
|
|
692
|
-
import
|
|
693
|
-
import pc6 from "picocolors";
|
|
461
|
+
import { checkbox as checkbox3 } from "@inquirer/prompts";
|
|
694
462
|
|
|
695
463
|
// src/registry/skills.ts
|
|
696
464
|
var SKILLS = [
|
|
@@ -715,260 +483,149 @@ var SKILLS = [
|
|
|
715
483
|
];
|
|
716
484
|
|
|
717
485
|
// src/steps/install-skills.ts
|
|
718
|
-
async function selectSkills(
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
"\u4F8B\u5982: TDD\u5F00\u53D1\u6D41\u7A0B\u3001\u4EE3\u7801\u5BA1\u67E5\u3001\u5934\u8111\u98CE\u66B4\u3001\u89C4\u8303\u5316\u5F00\u53D1\u7B49",
|
|
725
|
-
"\u5B89\u88C5\u540E\u901A\u8FC7 /skill-name \u547D\u4EE4\u8C03\u7528"
|
|
726
|
-
].join("\n"),
|
|
727
|
-
"\u4EC0\u4E48\u662F Skills?"
|
|
728
|
-
);
|
|
729
|
-
hintMultiselect();
|
|
730
|
-
const selected = await p6.multiselect({
|
|
731
|
-
message: "\u9009\u62E9\u8981\u5B89\u88C5\u7684 Skills",
|
|
732
|
-
options: SKILLS.map((s) => ({
|
|
486
|
+
async function selectSkills(total) {
|
|
487
|
+
step(5, total, "Skills / Plugins");
|
|
488
|
+
hint("\u7A7A\u683C=\u5207\u6362 a=\u5168\u9009 \u56DE\u8F66=\u786E\u8BA4");
|
|
489
|
+
const selected = await checkbox3({
|
|
490
|
+
message: "\u9009\u62E9 Skills",
|
|
491
|
+
choices: SKILLS.map((s) => ({
|
|
733
492
|
value: s.name,
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}))
|
|
737
|
-
initialValues: SKILLS.map((s) => s.name),
|
|
738
|
-
required: false
|
|
493
|
+
name: `${s.name} \u2014 ${s.description}`,
|
|
494
|
+
checked: true
|
|
495
|
+
}))
|
|
739
496
|
});
|
|
740
|
-
if (p6.isCancel(selected)) {
|
|
741
|
-
return [];
|
|
742
|
-
}
|
|
743
497
|
return SKILLS.filter((s) => selected.includes(s.name));
|
|
744
498
|
}
|
|
745
499
|
async function installSkills(skills) {
|
|
746
|
-
if (skills.length === 0) {
|
|
747
|
-
return { installed: [], failed: [] };
|
|
748
|
-
}
|
|
500
|
+
if (skills.length === 0) return { installed: [], failed: [] };
|
|
749
501
|
const installed = [];
|
|
750
502
|
const failed = [];
|
|
751
503
|
for (const skill of skills) {
|
|
752
|
-
const s = p6.spinner();
|
|
753
|
-
s.start(`\u6B63\u5728\u5B89\u88C5 ${skill.name}...`);
|
|
754
504
|
const [cmd, ...args] = skill.installCmd;
|
|
755
505
|
const result = await run(cmd, args);
|
|
756
506
|
if (result.exitCode === 0) {
|
|
757
507
|
installed.push(skill.name);
|
|
758
|
-
|
|
508
|
+
ok(skill.name);
|
|
759
509
|
} else {
|
|
760
510
|
failed.push(skill.name);
|
|
761
|
-
|
|
762
|
-
|
|
511
|
+
fail(skill.name);
|
|
512
|
+
warn(` \u624B\u52A8: ${skill.installCmd.join(" ")}`);
|
|
763
513
|
}
|
|
764
514
|
}
|
|
765
515
|
return { installed, failed };
|
|
766
516
|
}
|
|
767
517
|
|
|
768
518
|
// src/steps/clean-env.ts
|
|
769
|
-
import
|
|
770
|
-
import
|
|
771
|
-
|
|
772
|
-
if (
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
].join("\n"),
|
|
783
|
-
"\u4E3A\u4EC0\u4E48\u8981\u6E05\u7406?"
|
|
784
|
-
);
|
|
785
|
-
hintMultiselect();
|
|
786
|
-
const selected = await p7.multiselect({
|
|
787
|
-
message: "\u9009\u62E9\u8981\u79FB\u9664\u7684\u73AF\u5883\u53D8\u91CF (\u4E0D\u9009\u5219\u5168\u90E8\u4FDD\u7559)",
|
|
788
|
-
options: sensitiveVars.map((v) => ({
|
|
519
|
+
import { checkbox as checkbox4 } from "@inquirer/prompts";
|
|
520
|
+
import pc2 from "picocolors";
|
|
521
|
+
function mask(val) {
|
|
522
|
+
if (val.length <= 8) return "****";
|
|
523
|
+
return val.slice(0, 4) + "..." + val.slice(-4);
|
|
524
|
+
}
|
|
525
|
+
async function cleanEnv(vars, total) {
|
|
526
|
+
if (vars.length === 0) return { removed: [], skipped: [] };
|
|
527
|
+
step(6, total, "\u73AF\u5883\u53D8\u91CF\u6E05\u7406");
|
|
528
|
+
hint("\u7A7A\u683C=\u5207\u6362 \u56DE\u8F66=\u786E\u8BA4 (\u4E0D\u9009\u5219\u5168\u90E8\u4FDD\u7559)");
|
|
529
|
+
const selected = await checkbox4({
|
|
530
|
+
message: "\u9009\u62E9\u8981\u79FB\u9664\u7684\u53D8\u91CF",
|
|
531
|
+
choices: vars.map((v) => ({
|
|
789
532
|
value: v.key,
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
}))
|
|
793
|
-
initialValues: [],
|
|
794
|
-
required: false
|
|
533
|
+
name: `${v.key} = ${pc2.dim(mask(v.value))}`,
|
|
534
|
+
checked: false
|
|
535
|
+
}))
|
|
795
536
|
});
|
|
796
|
-
if (
|
|
797
|
-
p7.log.info("\u8DF3\u8FC7\u73AF\u5883\u53D8\u91CF\u6E05\u7406");
|
|
798
|
-
return { removed: [], skipped: sensitiveVars.map((v) => v.key) };
|
|
799
|
-
}
|
|
800
|
-
const toRemove = selected;
|
|
537
|
+
if (selected.length === 0) return { removed: [], skipped: vars.map((v) => v.key) };
|
|
801
538
|
const removed = [];
|
|
802
539
|
const skipped = [];
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
540
|
+
for (const key of selected) {
|
|
541
|
+
if (process.platform === "win32") {
|
|
542
|
+
const r = await run("powershell", ["-Command", `[Environment]::SetEnvironmentVariable('${key}', $null, 'User')`]);
|
|
543
|
+
if (r.exitCode === 0) {
|
|
544
|
+
removed.push(key);
|
|
545
|
+
ok(`${key} \u5DF2\u79FB\u9664`);
|
|
546
|
+
} else {
|
|
547
|
+
skipped.push(key);
|
|
548
|
+
warn(`${key} \u79FB\u9664\u5931\u8D25`);
|
|
549
|
+
}
|
|
809
550
|
} else {
|
|
551
|
+
delete process.env[key];
|
|
810
552
|
skipped.push(key);
|
|
553
|
+
warn(`${key} \u9700\u624B\u52A8\u4ECE ~/.bashrc \u6216 ~/.zshrc \u79FB\u9664`);
|
|
811
554
|
}
|
|
812
555
|
}
|
|
813
|
-
s.stop("\u73AF\u5883\u53D8\u91CF\u6E05\u7406\u5B8C\u6210");
|
|
814
|
-
const lines = [];
|
|
815
|
-
for (const key of removed) {
|
|
816
|
-
lines.push(`${pc7.green("\u2713")} ${key} \u5DF2\u79FB\u9664`);
|
|
817
|
-
}
|
|
818
|
-
for (const key of skipped) {
|
|
819
|
-
lines.push(`${pc7.yellow("!")} ${key} \u9700\u624B\u52A8\u79FB\u9664`);
|
|
820
|
-
}
|
|
821
|
-
if (skipped.length > 0 && process.platform !== "win32") {
|
|
822
|
-
lines.push("");
|
|
823
|
-
lines.push(pc7.dim("\u624B\u52A8\u79FB\u9664\u65B9\u6CD5:"));
|
|
824
|
-
for (const key of skipped) {
|
|
825
|
-
lines.push(pc7.dim(` \u4ECE ~/.bashrc \u6216 ~/.zshrc \u5220\u9664: export ${key}=...`));
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
if (lines.length > 0) {
|
|
829
|
-
p7.note(lines.join("\n"), "\u6E05\u7406\u7ED3\u679C");
|
|
830
|
-
}
|
|
831
556
|
return { removed, skipped };
|
|
832
557
|
}
|
|
833
|
-
async function removeEnvVar(key) {
|
|
834
|
-
if (process.platform === "win32") {
|
|
835
|
-
const result = await run("powershell", [
|
|
836
|
-
"-Command",
|
|
837
|
-
`[Environment]::SetEnvironmentVariable('${key}', $null, 'User')`
|
|
838
|
-
]);
|
|
839
|
-
return result.exitCode === 0;
|
|
840
|
-
} else {
|
|
841
|
-
delete process.env[key];
|
|
842
|
-
return false;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
function maskValue2(val) {
|
|
846
|
-
if (val.length <= 8) return "****";
|
|
847
|
-
return val.slice(0, 4) + "..." + val.slice(-4);
|
|
848
|
-
}
|
|
849
558
|
|
|
850
559
|
// src/cli.ts
|
|
851
|
-
var
|
|
560
|
+
var TOTAL = 7;
|
|
852
561
|
async function runCli() {
|
|
853
562
|
const results = [];
|
|
854
|
-
const env = await detect(
|
|
855
|
-
const components = await selectComponents(env,
|
|
563
|
+
const env = await detect(TOTAL);
|
|
564
|
+
const components = await selectComponents(env, TOTAL);
|
|
856
565
|
let scaffoldOpts = null;
|
|
857
|
-
if (components.scaffold)
|
|
858
|
-
scaffoldOpts = await scaffoldConfig(TOTAL_STEPS);
|
|
859
|
-
}
|
|
566
|
+
if (components.scaffold) scaffoldOpts = await scaffoldConfig(TOTAL);
|
|
860
567
|
let mcpServers = [];
|
|
861
|
-
if (components.installMcp)
|
|
862
|
-
mcpServers = await selectMcpServers(TOTAL_STEPS);
|
|
863
|
-
}
|
|
568
|
+
if (components.installMcp) mcpServers = await selectMcpServers(TOTAL);
|
|
864
569
|
let skills = [];
|
|
865
|
-
if (components.installSkills)
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
);
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
if (components.installCli && !env.claudeInstalled) {
|
|
876
|
-
actionLines.push(`${actionNum++}. \u5B89\u88C5 Claude Code CLI`);
|
|
877
|
-
}
|
|
878
|
-
if (components.scaffold && scaffoldOpts) {
|
|
879
|
-
actionLines.push(`${actionNum++}. \u751F\u6210\u914D\u7F6E\u6587\u4EF6 (${scaffoldOpts.style}\u6A21\u677F, ${scaffoldOpts.permission}\u6743\u9650)`);
|
|
880
|
-
}
|
|
881
|
-
if (mcpServers.length > 0) {
|
|
882
|
-
actionLines.push(`${actionNum++}. \u914D\u7F6E ${mcpServers.length} \u4E2A MCP Servers`);
|
|
883
|
-
for (const s of mcpServers) {
|
|
884
|
-
actionLines.push(` \xB7 ${s.name} - ${s.description}`);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
if (skills.length > 0) {
|
|
888
|
-
actionLines.push(`${actionNum++}. \u5B89\u88C5 ${skills.length} \u4E2A Skills`);
|
|
889
|
-
for (const s of skills) {
|
|
890
|
-
actionLines.push(` \xB7 ${s.name}`);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
if (components.cleanEnv && env.sensitiveEnvVars.length > 0) {
|
|
894
|
-
actionLines.push(`${actionNum++}. \u6E05\u7406 ${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u73AF\u5883\u53D8\u91CF`);
|
|
895
|
-
}
|
|
896
|
-
if (actionLines.length === 0) {
|
|
897
|
-
p8.log.info("\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u64CD\u4F5C");
|
|
570
|
+
if (components.installSkills) skills = await selectSkills(TOTAL);
|
|
571
|
+
step(6, TOTAL, "\u786E\u8BA4");
|
|
572
|
+
const actions = [];
|
|
573
|
+
if (components.installCli && !env.claudeInstalled) actions.push("\u5B89\u88C5 Claude CLI");
|
|
574
|
+
if (scaffoldOpts) actions.push(`\u751F\u6210\u914D\u7F6E (${scaffoldOpts.style})`);
|
|
575
|
+
if (mcpServers.length > 0) actions.push(`${mcpServers.length} \u4E2A MCP`);
|
|
576
|
+
if (skills.length > 0) actions.push(`${skills.length} \u4E2A Skills`);
|
|
577
|
+
if (components.cleanEnv) actions.push(`\u6E05\u7406 ${env.sensitiveEnvVars.length} \u4E2A\u53D8\u91CF`);
|
|
578
|
+
if (actions.length === 0) {
|
|
579
|
+
info("\u65E0\u64CD\u4F5C");
|
|
898
580
|
return;
|
|
899
581
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
initialValue: true
|
|
905
|
-
});
|
|
906
|
-
if (p8.isCancel(confirmed) || !confirmed) {
|
|
907
|
-
p8.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
582
|
+
console.log(pc3.dim(` \u2192 ${actions.join(" \xB7 ")}`));
|
|
583
|
+
const go = await confirm2({ message: "\u5F00\u59CB\u6267\u884C?", default: true });
|
|
584
|
+
if (!go) {
|
|
585
|
+
info("\u5DF2\u53D6\u6D88");
|
|
908
586
|
process.exit(0);
|
|
909
587
|
}
|
|
910
|
-
|
|
911
|
-
console.log(
|
|
912
|
-
pc8.cyan(" \u2501\u2501\u2501 ") + pc8.bold(`\u6B65\u9AA4 7/${TOTAL_STEPS}`) + pc8.cyan(" \u2501 ") + pc8.white("\u6267\u884C\u5B89\u88C5") + pc8.cyan(" \u2501\u2501\u2501")
|
|
913
|
-
);
|
|
914
|
-
console.log();
|
|
588
|
+
step(7, TOTAL, "\u6267\u884C\u5B89\u88C5");
|
|
915
589
|
if (components.installCli) {
|
|
916
590
|
const r = await installCli(env.claudeInstalled);
|
|
917
|
-
results.push({
|
|
918
|
-
label: r.skipped ? "Claude Code CLI (\u5DF2\u5B89\u88C5)" : "Claude Code CLI",
|
|
919
|
-
ok: r.success
|
|
920
|
-
});
|
|
591
|
+
results.push({ label: r.skipped ? "Claude CLI (\u5DF2\u5B89\u88C5)" : "Claude CLI", ok: r.success });
|
|
921
592
|
}
|
|
922
|
-
if (
|
|
593
|
+
if (scaffoldOpts) {
|
|
923
594
|
await scaffold(process.cwd(), scaffoldOpts);
|
|
924
|
-
results.push({ label: "\u914D\u7F6E\u6587\u4EF6
|
|
595
|
+
results.push({ label: "\u914D\u7F6E\u6587\u4EF6", ok: true });
|
|
925
596
|
}
|
|
926
597
|
if (mcpServers.length > 0) {
|
|
927
598
|
const r = await installMcp(mcpServers);
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
}
|
|
931
|
-
for (const name of r.failed) {
|
|
932
|
-
results.push({ label: `${name} MCP`, ok: false });
|
|
933
|
-
}
|
|
599
|
+
r.installed.forEach((n) => results.push({ label: `${n} MCP`, ok: true }));
|
|
600
|
+
r.failed.forEach((n) => results.push({ label: `${n} MCP`, ok: false }));
|
|
934
601
|
}
|
|
935
602
|
if (skills.length > 0) {
|
|
936
603
|
const r = await installSkills(skills);
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
for (const name of r.failed) {
|
|
941
|
-
results.push({ label: `${name} Skill`, ok: false });
|
|
942
|
-
}
|
|
604
|
+
r.installed.forEach((n) => results.push({ label: `${n} Skill`, ok: true }));
|
|
605
|
+
r.failed.forEach((n) => results.push({ label: `${n} Skill`, ok: false }));
|
|
943
606
|
}
|
|
944
607
|
if (components.cleanEnv) {
|
|
945
|
-
const r = await cleanEnv(env.sensitiveEnvVars,
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
}
|
|
949
|
-
for (const key of r.skipped) {
|
|
950
|
-
results.push({ label: `${key} \u9700\u624B\u52A8\u79FB\u9664`, ok: false });
|
|
951
|
-
}
|
|
608
|
+
const r = await cleanEnv(env.sensitiveEnvVars, TOTAL);
|
|
609
|
+
r.removed.forEach((k) => results.push({ label: `${k} \u5DF2\u79FB\u9664`, ok: true }));
|
|
610
|
+
r.skipped.forEach((k) => results.push({ label: `${k} \u9700\u624B\u52A8`, ok: false }));
|
|
952
611
|
}
|
|
953
|
-
|
|
612
|
+
done(results);
|
|
954
613
|
}
|
|
955
614
|
|
|
956
615
|
// src/index.ts
|
|
957
|
-
import * as p9 from "@clack/prompts";
|
|
958
616
|
var require2 = createRequire(import.meta.url);
|
|
959
|
-
var { version
|
|
617
|
+
var { version } = require2("../package.json");
|
|
960
618
|
async function main() {
|
|
961
|
-
banner(
|
|
962
|
-
p9.intro("\u5F00\u59CB\u914D\u7F6E Claude Code \u73AF\u5883");
|
|
619
|
+
banner(version);
|
|
963
620
|
try {
|
|
964
621
|
await runCli();
|
|
965
622
|
} catch (err) {
|
|
966
|
-
if (err instanceof Error) {
|
|
967
|
-
|
|
623
|
+
if (err instanceof Error && err.message.includes("User force closed")) {
|
|
624
|
+
console.log("\n \u5DF2\u9000\u51FA");
|
|
625
|
+
process.exit(0);
|
|
968
626
|
}
|
|
969
|
-
|
|
627
|
+
console.error(err);
|
|
970
628
|
process.exit(1);
|
|
971
629
|
}
|
|
972
|
-
p9.outro("\u611F\u8C22\u4F7F\u7528 ccbot\uFF01");
|
|
973
630
|
}
|
|
974
631
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccbot-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Claude Code 环境一键配置工具",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
],
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@
|
|
31
|
+
"@inquirer/prompts": "^8.3.0",
|
|
32
32
|
"execa": "^9.5.2",
|
|
33
33
|
"picocolors": "^1.1.1"
|
|
34
34
|
},
|