ccbot-cli 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +236 -580
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,63 +5,53 @@ import { createRequire } from "module";
|
|
|
5
5
|
|
|
6
6
|
// src/utils/logger.ts
|
|
7
7
|
import pc from "picocolors";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
function step(n, total, title) {
|
|
9
|
+
console.log(pc.cyan(`
|
|
10
|
+
[${n}/${total}] `) + pc.bold(title));
|
|
11
|
+
}
|
|
12
|
+
function hint(msg) {
|
|
13
|
+
console.log(pc.dim(` ${msg}`));
|
|
14
|
+
}
|
|
15
|
+
function ok(msg) {
|
|
16
|
+
console.log(pc.green(" \u2713 ") + msg);
|
|
17
|
+
}
|
|
18
|
+
function fail(msg) {
|
|
19
|
+
console.log(pc.red(" \u2717 ") + msg);
|
|
20
|
+
}
|
|
21
|
+
function warn(msg) {
|
|
22
|
+
console.log(pc.yellow(" \u26A0 ") + msg);
|
|
23
|
+
}
|
|
24
|
+
function info(msg) {
|
|
25
|
+
console.log(pc.cyan(" \u2139 ") + msg);
|
|
26
|
+
}
|
|
27
|
+
function banner(version2) {
|
|
17
28
|
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"));
|
|
29
|
+
console.log(pc.cyan(" \u{1F916} ccbot") + pc.dim(` v${version2}`) + pc.cyan(" \u2014 Claude Code \u73AF\u5883\u4E00\u952E\u914D\u7F6E"));
|
|
27
30
|
console.log();
|
|
28
31
|
}
|
|
29
|
-
function
|
|
32
|
+
function done(items) {
|
|
30
33
|
const okCount = items.filter((i) => i.ok).length;
|
|
31
34
|
const failCount = items.filter((i) => !i.ok).length;
|
|
32
35
|
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
36
|
if (failCount === 0) {
|
|
36
|
-
console.log(pc.
|
|
37
|
+
console.log(pc.green(pc.bold(" \u2713 \u5168\u90E8\u5B8C\u6210!")));
|
|
37
38
|
} else {
|
|
38
|
-
console.log(pc.
|
|
39
|
+
console.log(pc.yellow(` \u26A0 \u5B8C\u6210 ${okCount} \u9879, ${failCount} \u9879\u9700\u6CE8\u610F`));
|
|
39
40
|
}
|
|
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
41
|
console.log();
|
|
43
42
|
for (const item of items) {
|
|
44
|
-
if (item.ok)
|
|
45
|
-
|
|
46
|
-
} else {
|
|
47
|
-
log.error(item.label);
|
|
48
|
-
}
|
|
43
|
+
if (item.ok) ok(item.label);
|
|
44
|
+
else fail(item.label);
|
|
49
45
|
}
|
|
50
46
|
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"));
|
|
47
|
+
console.log(pc.dim(" \u4E0B\u4E00\u6B65:"));
|
|
48
|
+
console.log(pc.dim(" 1. ") + pc.green("claude") + pc.dim(" \u542F\u52A8 Claude Code"));
|
|
49
|
+
console.log(pc.dim(" 2. \u7F16\u8F91 ") + pc.green("CLAUDE.md") + pc.dim(" \u81EA\u5B9A\u4E49\u6307\u4EE4"));
|
|
56
50
|
console.log();
|
|
57
51
|
}
|
|
58
52
|
|
|
59
53
|
// src/cli.ts
|
|
60
|
-
import
|
|
61
|
-
import pc8 from "picocolors";
|
|
62
|
-
|
|
63
|
-
// src/steps/detect.ts
|
|
64
|
-
import * as p from "@clack/prompts";
|
|
54
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
65
55
|
import pc3 from "picocolors";
|
|
66
56
|
|
|
67
57
|
// src/utils/exec.ts
|
|
@@ -90,40 +80,11 @@ async function npmInstallGlobal(pkg) {
|
|
|
90
80
|
return run("npm", ["install", "-g", pkg]);
|
|
91
81
|
}
|
|
92
82
|
|
|
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
83
|
// src/steps/detect.ts
|
|
122
84
|
var SENSITIVE_PATTERNS = [
|
|
123
85
|
/^ANTHROPIC_API_KEY$/i,
|
|
124
86
|
/^CLAUDE_API_KEY$/i,
|
|
125
87
|
/^OPENAI_API_KEY$/i,
|
|
126
|
-
/^OPENAI_ORG_ID$/i,
|
|
127
88
|
/^GOOGLE_API_KEY$/i,
|
|
128
89
|
/^AZURE_OPENAI_API_KEY$/i,
|
|
129
90
|
/^HUGGINGFACE_TOKEN$/i,
|
|
@@ -136,156 +97,97 @@ var SENSITIVE_PATTERNS = [
|
|
|
136
97
|
/_TOKEN$/i,
|
|
137
98
|
/_API_KEY$/i
|
|
138
99
|
];
|
|
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...");
|
|
100
|
+
async function detect(total) {
|
|
101
|
+
step(1, total, "\u73AF\u5883\u68C0\u6D4B");
|
|
160
102
|
const nodeVersion = process.version;
|
|
161
103
|
const npmResult = await run("npm", ["--version"]);
|
|
162
104
|
const npmVersion = npmResult.stdout.trim();
|
|
163
|
-
const osMap = {
|
|
164
|
-
win32: "Windows",
|
|
165
|
-
darwin: "macOS",
|
|
166
|
-
linux: "Linux"
|
|
167
|
-
};
|
|
105
|
+
const osMap = { win32: "Windows", darwin: "macOS", linux: "Linux" };
|
|
168
106
|
const os = osMap[process.platform] ?? process.platform;
|
|
169
107
|
const claudeInstalled = await commandExists("claude");
|
|
170
108
|
let claudeVersion = null;
|
|
171
109
|
if (claudeInstalled) {
|
|
172
|
-
const
|
|
173
|
-
claudeVersion =
|
|
110
|
+
const r = await run("claude", ["--version"]);
|
|
111
|
+
claudeVersion = r.stdout.trim();
|
|
174
112
|
}
|
|
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)`);
|
|
113
|
+
const sensitiveEnvVars = [];
|
|
114
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
115
|
+
if (!value) continue;
|
|
116
|
+
if (SENSITIVE_PATTERNS.some((p) => p.test(key))) {
|
|
117
|
+
sensitiveEnvVars.push({ key, value });
|
|
118
|
+
}
|
|
185
119
|
}
|
|
120
|
+
ok(`Node ${nodeVersion} \xB7 npm v${npmVersion} \xB7 ${os}`);
|
|
121
|
+
if (claudeInstalled) ok(`Claude CLI ${claudeVersion}`);
|
|
122
|
+
else warn("Claude CLI \u672A\u5B89\u88C5");
|
|
186
123
|
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
|
-
}
|
|
124
|
+
warn(`\u53D1\u73B0 ${sensitiveEnvVars.length} \u4E2A\u654F\u611F\u73AF\u5883\u53D8\u91CF`);
|
|
192
125
|
}
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
nodeVersion,
|
|
196
|
-
npmVersion,
|
|
197
|
-
os,
|
|
198
|
-
platform: process.platform,
|
|
199
|
-
claudeInstalled,
|
|
200
|
-
claudeVersion,
|
|
201
|
-
sensitiveEnvVars
|
|
202
|
-
};
|
|
126
|
+
return { nodeVersion, npmVersion, os, platform: process.platform, claudeInstalled, claudeVersion, sensitiveEnvVars };
|
|
203
127
|
}
|
|
204
128
|
|
|
205
129
|
// src/steps/select.ts
|
|
206
|
-
import
|
|
207
|
-
async function selectComponents(env,
|
|
208
|
-
|
|
209
|
-
|
|
130
|
+
import { checkbox } from "@inquirer/prompts";
|
|
131
|
+
async function selectComponents(env, total) {
|
|
132
|
+
step(2, total, "\u9009\u62E9\u7EC4\u4EF6");
|
|
133
|
+
hint("\u7A7A\u683C=\u5207\u6362 a=\u5168\u9009 \u56DE\u8F66=\u786E\u8BA4");
|
|
134
|
+
const choices = [
|
|
210
135
|
{
|
|
211
136
|
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"
|
|
137
|
+
name: `Claude Code CLI ${env.claudeInstalled ? "(\u5DF2\u5B89\u88C5, \u8DF3\u8FC7)" : "(\u672A\u5B89\u88C5)"}`,
|
|
138
|
+
checked: true
|
|
219
139
|
},
|
|
220
|
-
{
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
hint: "\u914D\u7F6E AI \u5DE5\u5177\u670D\u52A1 (\u6D4F\u89C8\u5668/\u641C\u7D22/\u6587\u6863\u7B49)"
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
value: "installSkills",
|
|
227
|
-
label: "Skills / Plugins",
|
|
228
|
-
hint: "\u589E\u5F3A Claude Code \u5DE5\u4F5C\u6D41\u80FD\u529B"
|
|
229
|
-
}
|
|
140
|
+
{ value: "scaffold", name: "\u9879\u76EE\u914D\u7F6E\u6587\u4EF6 (CLAUDE.md + .claude/)", checked: true },
|
|
141
|
+
{ value: "installMcp", name: "MCP Servers (AI\u5DE5\u5177\u670D\u52A1)", checked: true },
|
|
142
|
+
{ value: "installSkills", name: "Skills / Plugins (\u5DE5\u4F5C\u6D41\u589E\u5F3A)", checked: true }
|
|
230
143
|
];
|
|
231
144
|
if (env.sensitiveEnvVars.length > 0) {
|
|
232
|
-
|
|
145
|
+
choices.push({
|
|
233
146
|
value: "cleanEnv",
|
|
234
|
-
|
|
235
|
-
|
|
147
|
+
name: `\u73AF\u5883\u53D8\u91CF\u6E05\u7406 (${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u53D8\u91CF)`,
|
|
148
|
+
checked: true
|
|
236
149
|
});
|
|
237
150
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
options,
|
|
242
|
-
initialValues: options.map((o) => o.value),
|
|
151
|
+
const selected = await checkbox({
|
|
152
|
+
message: "\u5B89\u88C5\u7EC4\u4EF6",
|
|
153
|
+
choices,
|
|
243
154
|
required: true
|
|
244
155
|
});
|
|
245
|
-
if (p2.isCancel(selected)) {
|
|
246
|
-
p2.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
247
|
-
process.exit(0);
|
|
248
|
-
}
|
|
249
|
-
const values = selected;
|
|
250
156
|
return {
|
|
251
|
-
installCli:
|
|
252
|
-
scaffold:
|
|
253
|
-
installMcp:
|
|
254
|
-
installSkills:
|
|
255
|
-
cleanEnv:
|
|
157
|
+
installCli: selected.includes("installCli"),
|
|
158
|
+
scaffold: selected.includes("scaffold"),
|
|
159
|
+
installMcp: selected.includes("installMcp"),
|
|
160
|
+
installSkills: selected.includes("installSkills"),
|
|
161
|
+
cleanEnv: selected.includes("cleanEnv")
|
|
256
162
|
};
|
|
257
163
|
}
|
|
258
164
|
|
|
259
165
|
// src/steps/install-cli.ts
|
|
260
|
-
import * as p3 from "@clack/prompts";
|
|
261
166
|
async function installCli(alreadyInstalled) {
|
|
262
167
|
if (alreadyInstalled) {
|
|
263
|
-
|
|
168
|
+
info("Claude CLI \u5DF2\u5B89\u88C5, \u8DF3\u8FC7");
|
|
264
169
|
return { success: true, version: null, skipped: true };
|
|
265
170
|
}
|
|
266
|
-
|
|
267
|
-
s.start("\u6B63\u5728\u5B89\u88C5 Claude Code CLI...");
|
|
171
|
+
info("\u6B63\u5728\u5B89\u88C5 Claude Code CLI...");
|
|
268
172
|
const result = await npmInstallGlobal("@anthropic-ai/claude-code");
|
|
269
173
|
if (result.exitCode !== 0) {
|
|
270
|
-
|
|
271
|
-
|
|
174
|
+
fail("Claude CLI \u5B89\u88C5\u5931\u8D25");
|
|
175
|
+
warn("\u624B\u52A8: npm install -g @anthropic-ai/claude-code");
|
|
272
176
|
return { success: false, version: null, skipped: false };
|
|
273
177
|
}
|
|
274
178
|
const exists = await commandExists("claude");
|
|
275
179
|
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");
|
|
180
|
+
warn("\u5B89\u88C5\u5B8C\u6210\u4F46 claude \u547D\u4EE4\u4E0D\u53EF\u7528, \u53EF\u80FD\u9700\u91CD\u542F\u7EC8\u7AEF");
|
|
278
181
|
return { success: false, version: null, skipped: false };
|
|
279
182
|
}
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
return { success: true, version, skipped: false };
|
|
183
|
+
const v = await run("claude", ["--version"]);
|
|
184
|
+
const version2 = v.stdout.trim();
|
|
185
|
+
ok(`Claude CLI ${version2}`);
|
|
186
|
+
return { success: true, version: version2, skipped: false };
|
|
284
187
|
}
|
|
285
188
|
|
|
286
189
|
// src/steps/scaffold.ts
|
|
287
|
-
import
|
|
288
|
-
import pc4 from "picocolors";
|
|
190
|
+
import { select } from "@inquirer/prompts";
|
|
289
191
|
import { join as join2 } from "path";
|
|
290
192
|
|
|
291
193
|
// src/utils/fs.ts
|
|
@@ -333,71 +235,57 @@ function deepMerge(target, source) {
|
|
|
333
235
|
}
|
|
334
236
|
|
|
335
237
|
// src/steps/scaffold.ts
|
|
336
|
-
var
|
|
238
|
+
var TEMPLATES = {
|
|
239
|
+
minimal: `# Project Instructions
|
|
337
240
|
|
|
338
241
|
- Follow existing code style
|
|
339
242
|
- Read before write
|
|
340
243
|
- Keep changes minimal
|
|
341
|
-
|
|
342
|
-
|
|
244
|
+
`,
|
|
245
|
+
standard: `# Project Instructions
|
|
343
246
|
|
|
344
247
|
## Overview
|
|
345
|
-
<!--
|
|
248
|
+
<!-- Project purpose and tech stack -->
|
|
346
249
|
|
|
347
250
|
## Coding Standards
|
|
348
|
-
- Follow existing
|
|
349
|
-
- Read
|
|
350
|
-
- Keep changes focused
|
|
251
|
+
- Follow existing patterns
|
|
252
|
+
- Read context before modifying
|
|
253
|
+
- Keep changes focused
|
|
351
254
|
|
|
352
255
|
## Key Files
|
|
353
|
-
<!--
|
|
256
|
+
<!-- Important file paths -->
|
|
354
257
|
|
|
355
258
|
## Notes
|
|
356
259
|
<!-- Special considerations -->
|
|
357
|
-
|
|
358
|
-
|
|
260
|
+
`,
|
|
261
|
+
detailed: `# Project Instructions
|
|
359
262
|
|
|
360
|
-
##
|
|
361
|
-
<!--
|
|
263
|
+
## Overview
|
|
264
|
+
<!-- Project purpose, goals, tech stack -->
|
|
362
265
|
|
|
363
266
|
## Architecture
|
|
364
|
-
<!--
|
|
267
|
+
<!-- High-level design decisions -->
|
|
365
268
|
|
|
366
269
|
## Coding Standards
|
|
367
|
-
- Follow existing
|
|
368
|
-
- Read
|
|
369
|
-
- Keep changes focused and minimal
|
|
270
|
+
- Follow existing patterns
|
|
271
|
+
- Read context before modifying
|
|
370
272
|
- Write clear commit messages
|
|
371
273
|
- Add comments for complex logic
|
|
372
274
|
|
|
373
275
|
## Key Files
|
|
374
|
-
<!--
|
|
276
|
+
<!-- Important paths and purposes -->
|
|
375
277
|
|
|
376
278
|
## Dependencies
|
|
377
|
-
<!--
|
|
279
|
+
<!-- Key deps and roles -->
|
|
378
280
|
|
|
379
281
|
## Testing
|
|
380
|
-
<!--
|
|
282
|
+
<!-- How to run tests -->
|
|
381
283
|
|
|
382
284
|
## 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
|
-
}
|
|
285
|
+
<!-- Gotchas and context -->
|
|
286
|
+
`
|
|
398
287
|
};
|
|
399
|
-
var CLAUDEIGNORE =
|
|
400
|
-
node_modules/
|
|
288
|
+
var CLAUDEIGNORE = `node_modules/
|
|
401
289
|
dist/
|
|
402
290
|
build/
|
|
403
291
|
.env
|
|
@@ -407,105 +295,38 @@ build/
|
|
|
407
295
|
coverage/
|
|
408
296
|
.git/
|
|
409
297
|
`;
|
|
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
|
-
}
|
|
298
|
+
async function scaffoldConfig(total) {
|
|
299
|
+
step(3, total, "CLAUDE.md \u6A21\u677F");
|
|
300
|
+
const style = await select({
|
|
301
|
+
message: "\u9009\u62E9\u6A21\u677F\u98CE\u683C",
|
|
302
|
+
choices: [
|
|
303
|
+
{ value: "minimal", name: "\u6781\u7B80 \u2014 \u51E0\u884C\u6838\u5FC3\u89C4\u5219" },
|
|
304
|
+
{ value: "standard", name: "\u6807\u51C6 \u2014 \u5E38\u7528\u5206\u533A (\u63A8\u8350)" },
|
|
305
|
+
{ value: "detailed", name: "\u8BE6\u7EC6 \u2014 \u542B\u67B6\u6784/\u6D4B\u8BD5/\u4F9D\u8D56" }
|
|
431
306
|
],
|
|
432
|
-
|
|
307
|
+
default: "standard"
|
|
433
308
|
});
|
|
434
|
-
|
|
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
|
-
}
|
|
457
|
-
],
|
|
458
|
-
initialValue: "normal"
|
|
459
|
-
});
|
|
460
|
-
if (p4.isCancel(permission)) {
|
|
461
|
-
p4.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
462
|
-
process.exit(0);
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
style,
|
|
466
|
-
permission
|
|
467
|
-
};
|
|
309
|
+
return { style };
|
|
468
310
|
}
|
|
469
|
-
async function scaffold(targetDir,
|
|
470
|
-
const s = p4.spinner();
|
|
471
|
-
s.start("\u6B63\u5728\u751F\u6210\u914D\u7F6E\u6587\u4EF6...");
|
|
311
|
+
async function scaffold(targetDir, opts) {
|
|
472
312
|
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]);
|
|
313
|
+
const r1 = writeFileSafe(join2(targetDir, "CLAUDE.md"), TEMPLATES[opts.style]);
|
|
480
314
|
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");
|
|
315
|
+
ensureDir(join2(targetDir, ".claude"));
|
|
316
|
+
const settings = { permissions: { allow: ["Read", "Glob", "Grep", "WebFetch", "WebSearch"], deny: [] }, mcpServers: {} };
|
|
317
|
+
const r2 = writeFileSafe(join2(targetDir, ".claude", "settings.json"), JSON.stringify(settings, null, 2) + "\n");
|
|
489
318
|
files.push({ path: ".claude/settings.json", written: r2.written });
|
|
490
|
-
const
|
|
491
|
-
const r3 = writeFileSafe(ignorePath, CLAUDEIGNORE);
|
|
319
|
+
const r3 = writeFileSafe(join2(targetDir, ".claudeignore"), CLAUDEIGNORE);
|
|
492
320
|
files.push({ path: ".claudeignore", written: r3.written });
|
|
493
|
-
s.stop("\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5B8C\u6210");
|
|
494
|
-
const lines = [];
|
|
495
321
|
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
|
-
}
|
|
322
|
+
if (f.written) ok(f.path);
|
|
323
|
+
else info(`${f.path} (\u5DF2\u5B58\u5728, \u8DF3\u8FC7)`);
|
|
501
324
|
}
|
|
502
|
-
p4.note(lines.join("\n"), "\u751F\u6210\u7684\u6587\u4EF6");
|
|
503
325
|
return { files };
|
|
504
326
|
}
|
|
505
327
|
|
|
506
328
|
// src/steps/install-mcp.ts
|
|
507
|
-
import
|
|
508
|
-
import pc5 from "picocolors";
|
|
329
|
+
import { checkbox as checkbox2, confirm, input } from "@inquirer/prompts";
|
|
509
330
|
import { join as join3 } from "path";
|
|
510
331
|
import { homedir } from "os";
|
|
511
332
|
|
|
@@ -570,127 +391,73 @@ var MCP_SERVERS = [
|
|
|
570
391
|
];
|
|
571
392
|
|
|
572
393
|
// 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) => ({
|
|
394
|
+
async function selectMcpServers(total) {
|
|
395
|
+
step(4, total, "MCP Servers");
|
|
396
|
+
hint("\u7A7A\u683C=\u5207\u6362 a=\u5168\u9009 \u56DE\u8F66=\u786E\u8BA4");
|
|
397
|
+
const selected = await checkbox2({
|
|
398
|
+
message: "\u9009\u62E9 MCP Servers",
|
|
399
|
+
choices: MCP_SERVERS.map((s) => ({
|
|
588
400
|
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
|
|
401
|
+
name: `${s.name} \u2014 ${s.description}`,
|
|
402
|
+
checked: true
|
|
403
|
+
}))
|
|
603
404
|
});
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (!val.trim()) return "\u8BF7\u8F93\u5165\u81F3\u5C11\u4E00\u4E2A\u5305\u540D";
|
|
610
|
-
}
|
|
405
|
+
const servers = MCP_SERVERS.filter((s) => selected.includes(s.name));
|
|
406
|
+
const addCustom = await confirm({ message: "\u6DFB\u52A0\u81EA\u5B9A\u4E49 MCP (npm\u5305)?", default: false });
|
|
407
|
+
if (addCustom) {
|
|
408
|
+
const raw = await input({
|
|
409
|
+
message: "npm \u5305\u540D (\u9017\u53F7\u5206\u9694)"
|
|
611
410
|
});
|
|
612
|
-
if (
|
|
613
|
-
const
|
|
614
|
-
for (const pkg of customPkgs) {
|
|
411
|
+
if (raw.trim()) {
|
|
412
|
+
for (const pkg of raw.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
615
413
|
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
|
-
});
|
|
414
|
+
servers.push({ name, package: pkg, description: `\u81EA\u5B9A\u4E49: ${pkg}`, scope: "user", command: "npx", args: ["-y", pkg] });
|
|
624
415
|
}
|
|
625
416
|
}
|
|
626
417
|
}
|
|
627
418
|
return servers;
|
|
628
419
|
}
|
|
629
420
|
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...");
|
|
421
|
+
if (servers.length === 0) return { installed: [], failed: [] };
|
|
635
422
|
const installed = [];
|
|
636
423
|
const failed = [];
|
|
637
|
-
const userServers = servers.filter((
|
|
638
|
-
const projectServers = servers.filter((
|
|
424
|
+
const userServers = servers.filter((s) => s.scope === "user");
|
|
425
|
+
const projectServers = servers.filter((s) => s.scope === "project");
|
|
639
426
|
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
|
-
};
|
|
427
|
+
const dir = join3(homedir(), ".claude");
|
|
428
|
+
ensureDir(dir);
|
|
429
|
+
const cfg = {};
|
|
430
|
+
for (const s of userServers) {
|
|
431
|
+
cfg[s.name] = { command: s.command, args: s.args, ...s.env ? { env: s.env } : {} };
|
|
650
432
|
}
|
|
651
433
|
try {
|
|
652
|
-
mergeJsonFile(
|
|
653
|
-
|
|
434
|
+
mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
|
|
435
|
+
userServers.forEach((s) => installed.push(s.name));
|
|
654
436
|
} catch {
|
|
655
|
-
|
|
437
|
+
userServers.forEach((s) => failed.push(s.name));
|
|
656
438
|
}
|
|
657
439
|
}
|
|
658
440
|
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
|
-
};
|
|
441
|
+
const dir = join3(process.cwd(), ".claude");
|
|
442
|
+
ensureDir(dir);
|
|
443
|
+
const cfg = {};
|
|
444
|
+
for (const s of projectServers) {
|
|
445
|
+
cfg[s.name] = { command: s.command, args: s.args, ...s.env ? { env: s.env } : {} };
|
|
669
446
|
}
|
|
670
447
|
try {
|
|
671
|
-
mergeJsonFile(
|
|
672
|
-
|
|
448
|
+
mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
|
|
449
|
+
projectServers.forEach((s) => installed.push(s.name));
|
|
673
450
|
} catch {
|
|
674
|
-
|
|
451
|
+
projectServers.forEach((s) => failed.push(s.name));
|
|
675
452
|
}
|
|
676
453
|
}
|
|
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
|
-
}
|
|
454
|
+
for (const n of installed) ok(`${n} MCP`);
|
|
455
|
+
for (const n of failed) fail(`${n} MCP`);
|
|
688
456
|
return { installed, failed };
|
|
689
457
|
}
|
|
690
458
|
|
|
691
459
|
// src/steps/install-skills.ts
|
|
692
|
-
import
|
|
693
|
-
import pc6 from "picocolors";
|
|
460
|
+
import { checkbox as checkbox3 } from "@inquirer/prompts";
|
|
694
461
|
|
|
695
462
|
// src/registry/skills.ts
|
|
696
463
|
var SKILLS = [
|
|
@@ -715,260 +482,149 @@ var SKILLS = [
|
|
|
715
482
|
];
|
|
716
483
|
|
|
717
484
|
// 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) => ({
|
|
485
|
+
async function selectSkills(total) {
|
|
486
|
+
step(5, total, "Skills / Plugins");
|
|
487
|
+
hint("\u7A7A\u683C=\u5207\u6362 a=\u5168\u9009 \u56DE\u8F66=\u786E\u8BA4");
|
|
488
|
+
const selected = await checkbox3({
|
|
489
|
+
message: "\u9009\u62E9 Skills",
|
|
490
|
+
choices: SKILLS.map((s) => ({
|
|
733
491
|
value: s.name,
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}))
|
|
737
|
-
initialValues: SKILLS.map((s) => s.name),
|
|
738
|
-
required: false
|
|
492
|
+
name: `${s.name} \u2014 ${s.description}`,
|
|
493
|
+
checked: true
|
|
494
|
+
}))
|
|
739
495
|
});
|
|
740
|
-
if (p6.isCancel(selected)) {
|
|
741
|
-
return [];
|
|
742
|
-
}
|
|
743
496
|
return SKILLS.filter((s) => selected.includes(s.name));
|
|
744
497
|
}
|
|
745
498
|
async function installSkills(skills) {
|
|
746
|
-
if (skills.length === 0) {
|
|
747
|
-
return { installed: [], failed: [] };
|
|
748
|
-
}
|
|
499
|
+
if (skills.length === 0) return { installed: [], failed: [] };
|
|
749
500
|
const installed = [];
|
|
750
501
|
const failed = [];
|
|
751
502
|
for (const skill of skills) {
|
|
752
|
-
const s = p6.spinner();
|
|
753
|
-
s.start(`\u6B63\u5728\u5B89\u88C5 ${skill.name}...`);
|
|
754
503
|
const [cmd, ...args] = skill.installCmd;
|
|
755
504
|
const result = await run(cmd, args);
|
|
756
505
|
if (result.exitCode === 0) {
|
|
757
506
|
installed.push(skill.name);
|
|
758
|
-
|
|
507
|
+
ok(skill.name);
|
|
759
508
|
} else {
|
|
760
509
|
failed.push(skill.name);
|
|
761
|
-
|
|
762
|
-
|
|
510
|
+
fail(skill.name);
|
|
511
|
+
warn(` \u624B\u52A8: ${skill.installCmd.join(" ")}`);
|
|
763
512
|
}
|
|
764
513
|
}
|
|
765
514
|
return { installed, failed };
|
|
766
515
|
}
|
|
767
516
|
|
|
768
517
|
// 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) => ({
|
|
518
|
+
import { checkbox as checkbox4 } from "@inquirer/prompts";
|
|
519
|
+
import pc2 from "picocolors";
|
|
520
|
+
function mask(val) {
|
|
521
|
+
if (val.length <= 8) return "****";
|
|
522
|
+
return val.slice(0, 4) + "..." + val.slice(-4);
|
|
523
|
+
}
|
|
524
|
+
async function cleanEnv(vars, total) {
|
|
525
|
+
if (vars.length === 0) return { removed: [], skipped: [] };
|
|
526
|
+
step(6, total, "\u73AF\u5883\u53D8\u91CF\u6E05\u7406");
|
|
527
|
+
hint("\u7A7A\u683C=\u5207\u6362 \u56DE\u8F66=\u786E\u8BA4 (\u4E0D\u9009\u5219\u5168\u90E8\u4FDD\u7559)");
|
|
528
|
+
const selected = await checkbox4({
|
|
529
|
+
message: "\u9009\u62E9\u8981\u79FB\u9664\u7684\u53D8\u91CF",
|
|
530
|
+
choices: vars.map((v) => ({
|
|
789
531
|
value: v.key,
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
}))
|
|
793
|
-
initialValues: [],
|
|
794
|
-
required: false
|
|
532
|
+
name: `${v.key} = ${pc2.dim(mask(v.value))}`,
|
|
533
|
+
checked: false
|
|
534
|
+
}))
|
|
795
535
|
});
|
|
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;
|
|
536
|
+
if (selected.length === 0) return { removed: [], skipped: vars.map((v) => v.key) };
|
|
801
537
|
const removed = [];
|
|
802
538
|
const skipped = [];
|
|
803
|
-
const
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
539
|
+
for (const key of selected) {
|
|
540
|
+
if (process.platform === "win32") {
|
|
541
|
+
const r = await run("powershell", ["-Command", `[Environment]::SetEnvironmentVariable('${key}', $null, 'User')`]);
|
|
542
|
+
if (r.exitCode === 0) {
|
|
543
|
+
removed.push(key);
|
|
544
|
+
ok(`${key} \u5DF2\u79FB\u9664`);
|
|
545
|
+
} else {
|
|
546
|
+
skipped.push(key);
|
|
547
|
+
warn(`${key} \u79FB\u9664\u5931\u8D25`);
|
|
548
|
+
}
|
|
809
549
|
} else {
|
|
550
|
+
delete process.env[key];
|
|
810
551
|
skipped.push(key);
|
|
552
|
+
warn(`${key} \u9700\u624B\u52A8\u4ECE ~/.bashrc \u6216 ~/.zshrc \u79FB\u9664`);
|
|
811
553
|
}
|
|
812
554
|
}
|
|
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
555
|
return { removed, skipped };
|
|
832
556
|
}
|
|
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
557
|
|
|
850
558
|
// src/cli.ts
|
|
851
|
-
var
|
|
559
|
+
var TOTAL = 7;
|
|
852
560
|
async function runCli() {
|
|
853
561
|
const results = [];
|
|
854
|
-
const env = await detect(
|
|
855
|
-
const components = await selectComponents(env,
|
|
562
|
+
const env = await detect(TOTAL);
|
|
563
|
+
const components = await selectComponents(env, TOTAL);
|
|
856
564
|
let scaffoldOpts = null;
|
|
857
|
-
if (components.scaffold)
|
|
858
|
-
scaffoldOpts = await scaffoldConfig(TOTAL_STEPS);
|
|
859
|
-
}
|
|
565
|
+
if (components.scaffold) scaffoldOpts = await scaffoldConfig(TOTAL);
|
|
860
566
|
let mcpServers = [];
|
|
861
|
-
if (components.installMcp)
|
|
862
|
-
mcpServers = await selectMcpServers(TOTAL_STEPS);
|
|
863
|
-
}
|
|
567
|
+
if (components.installMcp) mcpServers = await selectMcpServers(TOTAL);
|
|
864
568
|
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");
|
|
569
|
+
if (components.installSkills) skills = await selectSkills(TOTAL);
|
|
570
|
+
step(6, TOTAL, "\u786E\u8BA4");
|
|
571
|
+
const actions = [];
|
|
572
|
+
if (components.installCli && !env.claudeInstalled) actions.push("\u5B89\u88C5 Claude CLI");
|
|
573
|
+
if (scaffoldOpts) actions.push(`\u751F\u6210\u914D\u7F6E (${scaffoldOpts.style})`);
|
|
574
|
+
if (mcpServers.length > 0) actions.push(`${mcpServers.length} \u4E2A MCP`);
|
|
575
|
+
if (skills.length > 0) actions.push(`${skills.length} \u4E2A Skills`);
|
|
576
|
+
if (components.cleanEnv) actions.push(`\u6E05\u7406 ${env.sensitiveEnvVars.length} \u4E2A\u53D8\u91CF`);
|
|
577
|
+
if (actions.length === 0) {
|
|
578
|
+
info("\u65E0\u64CD\u4F5C");
|
|
898
579
|
return;
|
|
899
580
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
initialValue: true
|
|
905
|
-
});
|
|
906
|
-
if (p8.isCancel(confirmed) || !confirmed) {
|
|
907
|
-
p8.cancel("\u5DF2\u53D6\u6D88\u64CD\u4F5C");
|
|
581
|
+
console.log(pc3.dim(` \u2192 ${actions.join(" \xB7 ")}`));
|
|
582
|
+
const go = await confirm2({ message: "\u5F00\u59CB\u6267\u884C?", default: true });
|
|
583
|
+
if (!go) {
|
|
584
|
+
info("\u5DF2\u53D6\u6D88");
|
|
908
585
|
process.exit(0);
|
|
909
586
|
}
|
|
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();
|
|
587
|
+
step(7, TOTAL, "\u6267\u884C\u5B89\u88C5");
|
|
915
588
|
if (components.installCli) {
|
|
916
589
|
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
|
-
});
|
|
590
|
+
results.push({ label: r.skipped ? "Claude CLI (\u5DF2\u5B89\u88C5)" : "Claude CLI", ok: r.success });
|
|
921
591
|
}
|
|
922
|
-
if (
|
|
592
|
+
if (scaffoldOpts) {
|
|
923
593
|
await scaffold(process.cwd(), scaffoldOpts);
|
|
924
|
-
results.push({ label: "\u914D\u7F6E\u6587\u4EF6
|
|
594
|
+
results.push({ label: "\u914D\u7F6E\u6587\u4EF6", ok: true });
|
|
925
595
|
}
|
|
926
596
|
if (mcpServers.length > 0) {
|
|
927
597
|
const r = await installMcp(mcpServers);
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
}
|
|
931
|
-
for (const name of r.failed) {
|
|
932
|
-
results.push({ label: `${name} MCP`, ok: false });
|
|
933
|
-
}
|
|
598
|
+
r.installed.forEach((n) => results.push({ label: `${n} MCP`, ok: true }));
|
|
599
|
+
r.failed.forEach((n) => results.push({ label: `${n} MCP`, ok: false }));
|
|
934
600
|
}
|
|
935
601
|
if (skills.length > 0) {
|
|
936
602
|
const r = await installSkills(skills);
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
}
|
|
940
|
-
for (const name of r.failed) {
|
|
941
|
-
results.push({ label: `${name} Skill`, ok: false });
|
|
942
|
-
}
|
|
603
|
+
r.installed.forEach((n) => results.push({ label: `${n} Skill`, ok: true }));
|
|
604
|
+
r.failed.forEach((n) => results.push({ label: `${n} Skill`, ok: false }));
|
|
943
605
|
}
|
|
944
606
|
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
|
-
}
|
|
607
|
+
const r = await cleanEnv(env.sensitiveEnvVars, TOTAL);
|
|
608
|
+
r.removed.forEach((k) => results.push({ label: `${k} \u5DF2\u79FB\u9664`, ok: true }));
|
|
609
|
+
r.skipped.forEach((k) => results.push({ label: `${k} \u9700\u624B\u52A8`, ok: false }));
|
|
952
610
|
}
|
|
953
|
-
|
|
611
|
+
done(results);
|
|
954
612
|
}
|
|
955
613
|
|
|
956
614
|
// src/index.ts
|
|
957
|
-
import * as p9 from "@clack/prompts";
|
|
958
615
|
var require2 = createRequire(import.meta.url);
|
|
959
|
-
var { version
|
|
616
|
+
var { version } = require2("../package.json");
|
|
960
617
|
async function main() {
|
|
961
|
-
banner(
|
|
962
|
-
p9.intro("\u5F00\u59CB\u914D\u7F6E Claude Code \u73AF\u5883");
|
|
618
|
+
banner(version);
|
|
963
619
|
try {
|
|
964
620
|
await runCli();
|
|
965
621
|
} catch (err) {
|
|
966
|
-
if (err instanceof Error) {
|
|
967
|
-
|
|
622
|
+
if (err instanceof Error && err.message.includes("User force closed")) {
|
|
623
|
+
console.log("\n \u5DF2\u9000\u51FA");
|
|
624
|
+
process.exit(0);
|
|
968
625
|
}
|
|
969
|
-
|
|
626
|
+
console.error(err);
|
|
970
627
|
process.exit(1);
|
|
971
628
|
}
|
|
972
|
-
p9.outro("\u611F\u8C22\u4F7F\u7528 ccbot\uFF01");
|
|
973
629
|
}
|
|
974
630
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccbot-cli",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
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
|
},
|