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.
Files changed (2) hide show
  1. package/dist/index.js +237 -580
  2. 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
- var log = {
9
- info: (msg) => console.log(pc.cyan(" \u2139 ") + msg),
10
- success: (msg) => console.log(pc.green(" \u2713 ") + msg),
11
- warn: (msg) => console.log(pc.yellow(" \u26A0 ") + msg),
12
- error: (msg) => console.log(pc.red(" \u2717 ") + msg),
13
- step: (msg) => console.log(pc.blue(" \u2192 ") + msg),
14
- dim: (msg) => console.log(pc.dim(" " + msg))
15
- };
16
- function banner(version) {
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(" \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"));
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 summary(items) {
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.cyan(" \u2502") + pc.green(pc.bold(" \u2713 \u5168\u90E8\u5B89\u88C5\u6210\u529F\uFF01")) + " " + pc.cyan("\u2502"));
38
+ console.log(pc.green(pc.bold(" \u2713 \u5168\u90E8\u5B8C\u6210!")));
37
39
  } else {
38
- console.log(pc.cyan(" \u2502") + pc.yellow(pc.bold(` \u26A0 \u5B8C\u6210 ${okCount} \u9879\uFF0C${failCount} \u9879\u9700\u6CE8\u610F`)) + " " + pc.cyan("\u2502"));
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
- log.success(item.label);
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.cyan(" \u250C ") + pc.bold("\u4E0B\u4E00\u6B65"));
52
- console.log(pc.cyan(" \u2502 ") + "1. \u8FD0\u884C " + pc.green("claude") + " \u542F\u52A8 Claude Code");
53
- console.log(pc.cyan(" \u2502 ") + "2. \u7F16\u8F91 " + pc.green("CLAUDE.md") + " \u81EA\u5B9A\u4E49\u9879\u76EE\u6307\u4EE4");
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 * as p8 from "@clack/prompts";
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 maskValue(val) {
140
- if (val.length <= 8) return "****";
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 result = await run("claude", ["--version"]);
173
- claudeVersion = result.stdout.trim();
111
+ const r = await run("claude", ["--version"]);
112
+ claudeVersion = r.stdout.trim();
174
113
  }
175
- const sensitiveEnvVars = scanSensitiveEnvVars();
176
- s.stop("\u73AF\u5883\u626B\u63CF\u5B8C\u6210");
177
- const lines = [];
178
- lines.push(`${pc3.green("\u2713")} Node.js ${nodeVersion}`);
179
- lines.push(`${pc3.green("\u2713")} npm v${npmVersion}`);
180
- lines.push(`${pc3.green("\u2713")} \u7CFB\u7EDF ${os}`);
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
- lines.push("");
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
- p.note(lines.join("\n"), "\u73AF\u5883\u4FE1\u606F");
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 * as p2 from "@clack/prompts";
207
- async function selectComponents(env, totalSteps) {
208
- stepHeader(2, totalSteps, "\u9009\u62E9\u5B89\u88C5\u7EC4\u4EF6");
209
- const options = [
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
- label: "Claude Code CLI",
213
- hint: env.claudeInstalled ? `\u5DF2\u5B89\u88C5 ${env.claudeVersion ?? ""}, \u5C06\u8DF3\u8FC7` : "\u5168\u5C40\u5B89\u88C5 @anthropic-ai/claude-code"
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
- value: "installSkills",
227
- label: "Skills / Plugins",
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
- options.push({
146
+ choices.push({
233
147
  value: "cleanEnv",
234
- label: "\u73AF\u5883\u53D8\u91CF\u6E05\u7406",
235
- hint: `\u53D1\u73B0 ${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u53D8\u91CF, \u5EFA\u8BAE\u79FB\u9664`
148
+ name: `\u73AF\u5883\u53D8\u91CF\u6E05\u7406 (${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u53D8\u91CF)`,
149
+ checked: true
236
150
  });
237
151
  }
238
- hintMultiselect();
239
- const selected = await p2.multiselect({
240
- message: "\u9009\u62E9\u8981\u5B89\u88C5\u7684\u7EC4\u4EF6",
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: values.includes("installCli"),
252
- scaffold: values.includes("scaffold"),
253
- installMcp: values.includes("installMcp"),
254
- installSkills: values.includes("installSkills"),
255
- cleanEnv: values.includes("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
- p3.log.info("Claude Code CLI \u5DF2\u5B89\u88C5\uFF0C\u8DF3\u8FC7");
169
+ info("Claude CLI \u5DF2\u5B89\u88C5, \u8DF3\u8FC7");
264
170
  return { success: true, version: null, skipped: true };
265
171
  }
266
- const s = p3.spinner();
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
- s.stop("Claude Code CLI \u5B89\u88C5\u5931\u8D25");
271
- p3.log.error(result.stderr || "\u5B89\u88C5\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u8FD0\u884C: npm install -g @anthropic-ai/claude-code");
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
- s.stop("Claude Code CLI \u5B89\u88C5\u5F02\u5E38");
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 versionResult = await run("claude", ["--version"]);
281
- const version = versionResult.stdout.trim();
282
- s.stop(`Claude Code CLI \u5B89\u88C5\u5B8C\u6210 (${version})`);
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 * as p4 from "@clack/prompts";
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 CLAUDE_MD_MINIMAL = `# Project Instructions
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
- var CLAUDE_MD_STANDARD = `# Project Instructions
245
+ `,
246
+ standard: `# Project Instructions
343
247
 
344
248
  ## Overview
345
- <!-- Describe your project purpose and tech stack -->
249
+ <!-- Project purpose and tech stack -->
346
250
 
347
251
  ## Coding Standards
348
- - Follow existing code patterns and style
349
- - Read and understand context before modifying
350
- - Keep changes focused and minimal
252
+ - Follow existing patterns
253
+ - Read context before modifying
254
+ - Keep changes focused
351
255
 
352
256
  ## Key Files
353
- <!-- List important file paths here -->
257
+ <!-- Important file paths -->
354
258
 
355
259
  ## Notes
356
260
  <!-- Special considerations -->
357
- `;
358
- var CLAUDE_MD_DETAILED = `# Project Instructions
261
+ `,
262
+ detailed: `# Project Instructions
359
263
 
360
- ## Project Overview
361
- <!-- Describe the project purpose, goals, and tech stack in detail -->
264
+ ## Overview
265
+ <!-- Project purpose, goals, tech stack -->
362
266
 
363
267
  ## Architecture
364
- <!-- Describe the high-level architecture and key design decisions -->
268
+ <!-- High-level design decisions -->
365
269
 
366
270
  ## Coding Standards
367
- - Follow existing code patterns and style
368
- - Read and understand context before modifying files
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
- <!-- List important file paths and their purposes -->
277
+ <!-- Important paths and purposes -->
375
278
 
376
279
  ## Dependencies
377
- <!-- List key dependencies and their roles -->
280
+ <!-- Key deps and roles -->
378
281
 
379
282
  ## Testing
380
- <!-- Describe testing approach and how to run tests -->
283
+ <!-- How to run tests -->
381
284
 
382
285
  ## Notes
383
- <!-- Special considerations, gotchas, or important context -->
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 = `# Ignore patterns for Claude Code
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(totalSteps) {
411
- stepHeader(3, totalSteps, "\u914D\u7F6E\u811A\u624B\u67B6");
412
- hintSelect();
413
- const style = await p4.select({
414
- message: "CLAUDE.md \u6A21\u677F\u98CE\u683C",
415
- options: [
416
- {
417
- value: "minimal",
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
- initialValue: "normal"
308
+ default: "standard"
459
309
  });
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
- };
310
+ return { style };
468
311
  }
469
- async function scaffold(targetDir, options) {
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 templates = {
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
- const claudeDir = join2(targetDir, ".claude");
482
- ensureDir(claudeDir);
483
- const settingsPath = join2(claudeDir, "settings.json");
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 ignorePath = join2(targetDir, ".claudeignore");
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
- lines.push(`${pc4.green("\u2713")} ${f.path}`);
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 * as p5 from "@clack/prompts";
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(totalSteps) {
574
- stepHeader(4, totalSteps, "MCP Servers \u914D\u7F6E");
575
- p5.note(
576
- [
577
- "MCP (Model Context Protocol) \u8BA9 Claude Code \u80FD\u8C03\u7528\u5916\u90E8\u5DE5\u5177",
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
- label: `${s.name}`,
590
- hint: s.description
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
- if (!p5.isCancel(addCustom) && addCustom) {
605
- const customInput = await p5.text({
606
- message: "\u8F93\u5165 npm \u5305\u540D (\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694)",
607
- placeholder: "\u4F8B\u5982: @anthropic-ai/mcp-xxx, my-mcp-server",
608
- validate: (val) => {
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 (!p5.isCancel(customInput) && customInput) {
613
- const customPkgs = customInput.split(",").map((s) => s.trim()).filter(Boolean);
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((sv) => sv.scope === "user");
638
- const projectServers = servers.filter((sv) => sv.scope === "project");
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 userClaudeDir = join3(homedir(), ".claude");
641
- ensureDir(userClaudeDir);
642
- const userSettingsPath = join3(userClaudeDir, "settings.json");
643
- const mcpConfig = {};
644
- for (const sv of userServers) {
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(userSettingsPath, { mcpServers: mcpConfig });
653
- for (const sv of userServers) installed.push(sv.name);
435
+ mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
436
+ userServers.forEach((s) => installed.push(s.name));
654
437
  } catch {
655
- for (const sv of userServers) failed.push(sv.name);
438
+ userServers.forEach((s) => failed.push(s.name));
656
439
  }
657
440
  }
658
441
  if (projectServers.length > 0) {
659
- const projectClaudeDir = join3(process.cwd(), ".claude");
660
- ensureDir(projectClaudeDir);
661
- const projectSettingsPath = join3(projectClaudeDir, "settings.json");
662
- const mcpConfig = {};
663
- for (const sv of projectServers) {
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(projectSettingsPath, { mcpServers: mcpConfig });
672
- for (const sv of projectServers) installed.push(sv.name);
449
+ mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
450
+ projectServers.forEach((s) => installed.push(s.name));
673
451
  } catch {
674
- for (const sv of projectServers) failed.push(sv.name);
452
+ projectServers.forEach((s) => failed.push(s.name));
675
453
  }
676
454
  }
677
- s.stop("MCP Servers \u914D\u7F6E\u5B8C\u6210");
678
- const lines = [];
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 * as p6 from "@clack/prompts";
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(totalSteps) {
719
- stepHeader(5, totalSteps, "Skills / Plugins \u914D\u7F6E");
720
- p6.note(
721
- [
722
- "Skills \u4E3A Claude Code \u6DFB\u52A0\u4E13\u4E1A\u5DE5\u4F5C\u6D41\u80FD\u529B",
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
- label: s.name,
735
- hint: s.description
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
- s.stop(`${pc6.green("\u2713")} ${skill.name} \u5B89\u88C5\u5B8C\u6210`);
508
+ ok(skill.name);
759
509
  } else {
760
510
  failed.push(skill.name);
761
- s.stop(`${pc6.red("\u2717")} ${skill.name} \u5B89\u88C5\u5931\u8D25`);
762
- p6.log.warning(` \u624B\u52A8\u5B89\u88C5: ${skill.installCmd.join(" ")}`);
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 * as p7 from "@clack/prompts";
770
- import pc7 from "picocolors";
771
- async function cleanEnv(sensitiveVars, totalSteps) {
772
- if (sensitiveVars.length === 0) {
773
- return { removed: [], skipped: [] };
774
- }
775
- stepHeader(6, totalSteps, "\u73AF\u5883\u53D8\u91CF\u6E05\u7406");
776
- p7.note(
777
- [
778
- "\u4EE5\u4E0B\u73AF\u5883\u53D8\u91CF\u5305\u542B\u654F\u611F\u4FE1\u606F (API\u5BC6\u94A5/Token)",
779
- "\u5C06\u5B83\u4EEC\u5B58\u50A8\u5728\u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u4E2D\u5B58\u5728\u6CC4\u9732\u98CE\u9669",
780
- "",
781
- "\u5EFA\u8BAE: \u79FB\u9664\u540E\u6539\u7528 Claude Code \u5185\u7F6E\u7684\u5BC6\u94A5\u7BA1\u7406"
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
- label: v.key,
791
- hint: maskValue2(v.value)
792
- })),
793
- initialValues: [],
794
- required: false
533
+ name: `${v.key} = ${pc2.dim(mask(v.value))}`,
534
+ checked: false
535
+ }))
795
536
  });
796
- if (p7.isCancel(selected) || selected.length === 0) {
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 s = p7.spinner();
804
- s.start("\u6B63\u5728\u6E05\u7406\u73AF\u5883\u53D8\u91CF...");
805
- for (const key of toRemove) {
806
- const success = await removeEnvVar(key);
807
- if (success) {
808
- removed.push(key);
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 TOTAL_STEPS = 7;
560
+ var TOTAL = 7;
852
561
  async function runCli() {
853
562
  const results = [];
854
- const env = await detect(TOTAL_STEPS);
855
- const components = await selectComponents(env, TOTAL_STEPS);
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
- skills = await selectSkills(TOTAL_STEPS);
867
- }
868
- console.log();
869
- console.log(
870
- pc8.cyan(" \u2501\u2501\u2501 ") + pc8.bold(`\u6B65\u9AA4 6/${TOTAL_STEPS}`) + pc8.cyan(" \u2501 ") + pc8.white("\u786E\u8BA4\u6267\u884C") + pc8.cyan(" \u2501\u2501\u2501")
871
- );
872
- console.log();
873
- const actionLines = [];
874
- let actionNum = 1;
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
- p8.note(actionLines.join("\n"), "\u5373\u5C06\u6267\u884C\u4EE5\u4E0B\u64CD\u4F5C");
901
- hintConfirm();
902
- const confirmed = await p8.confirm({
903
- message: "\u786E\u8BA4\u6267\u884C\u4EE5\u4E0A\u6240\u6709\u64CD\u4F5C?",
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
- console.log();
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 (components.scaffold && scaffoldOpts) {
593
+ if (scaffoldOpts) {
923
594
  await scaffold(process.cwd(), scaffoldOpts);
924
- results.push({ label: "\u914D\u7F6E\u6587\u4EF6\u751F\u6210", ok: true });
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
- for (const name of r.installed) {
929
- results.push({ label: `${name} MCP`, ok: true });
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
- for (const name of r.installed) {
938
- results.push({ label: `${name} Skill`, ok: true });
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, TOTAL_STEPS);
946
- for (const key of r.removed) {
947
- results.push({ label: `${key} \u5DF2\u79FB\u9664`, ok: true });
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
- summary(results);
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: VERSION } = require2("../package.json");
617
+ var { version } = require2("../package.json");
960
618
  async function main() {
961
- banner(VERSION);
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
- p9.log.error(err.message);
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
- p9.cancel("\u53D1\u751F\u9519\u8BEF\uFF0C\u5DF2\u9000\u51FA");
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.1.0",
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
- "@clack/prompts": "^0.9.1",
31
+ "@inquirer/prompts": "^8.3.0",
32
32
  "execa": "^9.5.2",
33
33
  "picocolors": "^1.1.1"
34
34
  },