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.
Files changed (2) hide show
  1. package/dist/index.js +236 -580
  2. 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
- 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) {
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(" \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"));
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 summary(items) {
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.cyan(" \u2502") + pc.green(pc.bold(" \u2713 \u5168\u90E8\u5B89\u88C5\u6210\u529F\uFF01")) + " " + pc.cyan("\u2502"));
37
+ console.log(pc.green(pc.bold(" \u2713 \u5168\u90E8\u5B8C\u6210!")));
37
38
  } 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"));
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
- log.success(item.label);
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.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"));
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 * as p8 from "@clack/prompts";
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 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...");
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 result = await run("claude", ["--version"]);
173
- claudeVersion = result.stdout.trim();
110
+ const r = await run("claude", ["--version"]);
111
+ claudeVersion = r.stdout.trim();
174
112
  }
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)`);
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
- 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
- }
124
+ warn(`\u53D1\u73B0 ${sensitiveEnvVars.length} \u4E2A\u654F\u611F\u73AF\u5883\u53D8\u91CF`);
192
125
  }
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
- };
126
+ return { nodeVersion, npmVersion, os, platform: process.platform, claudeInstalled, claudeVersion, sensitiveEnvVars };
203
127
  }
204
128
 
205
129
  // 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 = [
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
- 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"
137
+ name: `Claude Code CLI ${env.claudeInstalled ? "(\u5DF2\u5B89\u88C5, \u8DF3\u8FC7)" : "(\u672A\u5B89\u88C5)"}`,
138
+ checked: true
219
139
  },
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)"
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
- options.push({
145
+ choices.push({
233
146
  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`
147
+ name: `\u73AF\u5883\u53D8\u91CF\u6E05\u7406 (${env.sensitiveEnvVars.length} \u4E2A\u654F\u611F\u53D8\u91CF)`,
148
+ checked: true
236
149
  });
237
150
  }
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),
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: values.includes("installCli"),
252
- scaffold: values.includes("scaffold"),
253
- installMcp: values.includes("installMcp"),
254
- installSkills: values.includes("installSkills"),
255
- cleanEnv: values.includes("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
- p3.log.info("Claude Code CLI \u5DF2\u5B89\u88C5\uFF0C\u8DF3\u8FC7");
168
+ info("Claude CLI \u5DF2\u5B89\u88C5, \u8DF3\u8FC7");
264
169
  return { success: true, version: null, skipped: true };
265
170
  }
266
- const s = p3.spinner();
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
- 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");
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
- 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");
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 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 };
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 * as p4 from "@clack/prompts";
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 CLAUDE_MD_MINIMAL = `# Project Instructions
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
- var CLAUDE_MD_STANDARD = `# Project Instructions
244
+ `,
245
+ standard: `# Project Instructions
343
246
 
344
247
  ## Overview
345
- <!-- Describe your project purpose and tech stack -->
248
+ <!-- Project purpose and tech stack -->
346
249
 
347
250
  ## Coding Standards
348
- - Follow existing code patterns and style
349
- - Read and understand context before modifying
350
- - Keep changes focused and minimal
251
+ - Follow existing patterns
252
+ - Read context before modifying
253
+ - Keep changes focused
351
254
 
352
255
  ## Key Files
353
- <!-- List important file paths here -->
256
+ <!-- Important file paths -->
354
257
 
355
258
  ## Notes
356
259
  <!-- Special considerations -->
357
- `;
358
- var CLAUDE_MD_DETAILED = `# Project Instructions
260
+ `,
261
+ detailed: `# Project Instructions
359
262
 
360
- ## Project Overview
361
- <!-- Describe the project purpose, goals, and tech stack in detail -->
263
+ ## Overview
264
+ <!-- Project purpose, goals, tech stack -->
362
265
 
363
266
  ## Architecture
364
- <!-- Describe the high-level architecture and key design decisions -->
267
+ <!-- High-level design decisions -->
365
268
 
366
269
  ## Coding Standards
367
- - Follow existing code patterns and style
368
- - Read and understand context before modifying files
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
- <!-- List important file paths and their purposes -->
276
+ <!-- Important paths and purposes -->
375
277
 
376
278
  ## Dependencies
377
- <!-- List key dependencies and their roles -->
279
+ <!-- Key deps and roles -->
378
280
 
379
281
  ## Testing
380
- <!-- Describe testing approach and how to run tests -->
282
+ <!-- How to run tests -->
381
283
 
382
284
  ## 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
- }
285
+ <!-- Gotchas and context -->
286
+ `
398
287
  };
399
- var CLAUDEIGNORE = `# Ignore patterns for Claude Code
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(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
- }
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
- initialValue: "standard"
307
+ default: "standard"
433
308
  });
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
- }
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, options) {
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 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]);
313
+ const r1 = writeFileSafe(join2(targetDir, "CLAUDE.md"), TEMPLATES[opts.style]);
480
314
  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");
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 ignorePath = join2(targetDir, ".claudeignore");
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
- lines.push(`${pc4.green("\u2713")} ${f.path}`);
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 * as p5 from "@clack/prompts";
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(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) => ({
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
- 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
401
+ name: `${s.name} \u2014 ${s.description}`,
402
+ checked: true
403
+ }))
603
404
  });
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
- }
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 (!p5.isCancel(customInput) && customInput) {
613
- const customPkgs = customInput.split(",").map((s) => s.trim()).filter(Boolean);
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((sv) => sv.scope === "user");
638
- const projectServers = servers.filter((sv) => sv.scope === "project");
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 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
- };
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(userSettingsPath, { mcpServers: mcpConfig });
653
- for (const sv of userServers) installed.push(sv.name);
434
+ mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
435
+ userServers.forEach((s) => installed.push(s.name));
654
436
  } catch {
655
- for (const sv of userServers) failed.push(sv.name);
437
+ userServers.forEach((s) => failed.push(s.name));
656
438
  }
657
439
  }
658
440
  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
- };
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(projectSettingsPath, { mcpServers: mcpConfig });
672
- for (const sv of projectServers) installed.push(sv.name);
448
+ mergeJsonFile(join3(dir, "settings.json"), { mcpServers: cfg });
449
+ projectServers.forEach((s) => installed.push(s.name));
673
450
  } catch {
674
- for (const sv of projectServers) failed.push(sv.name);
451
+ projectServers.forEach((s) => failed.push(s.name));
675
452
  }
676
453
  }
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
- }
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 * as p6 from "@clack/prompts";
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(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) => ({
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
- label: s.name,
735
- hint: s.description
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
- s.stop(`${pc6.green("\u2713")} ${skill.name} \u5B89\u88C5\u5B8C\u6210`);
507
+ ok(skill.name);
759
508
  } else {
760
509
  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(" ")}`);
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 * 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) => ({
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
- label: v.key,
791
- hint: maskValue2(v.value)
792
- })),
793
- initialValues: [],
794
- required: false
532
+ name: `${v.key} = ${pc2.dim(mask(v.value))}`,
533
+ checked: false
534
+ }))
795
535
  });
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;
536
+ if (selected.length === 0) return { removed: [], skipped: vars.map((v) => v.key) };
801
537
  const removed = [];
802
538
  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);
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 TOTAL_STEPS = 7;
559
+ var TOTAL = 7;
852
560
  async function runCli() {
853
561
  const results = [];
854
- const env = await detect(TOTAL_STEPS);
855
- const components = await selectComponents(env, TOTAL_STEPS);
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
- 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");
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
- 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");
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
- 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();
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 (components.scaffold && scaffoldOpts) {
592
+ if (scaffoldOpts) {
923
593
  await scaffold(process.cwd(), scaffoldOpts);
924
- results.push({ label: "\u914D\u7F6E\u6587\u4EF6\u751F\u6210", ok: true });
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
- 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
- }
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
- 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
- }
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, 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
- }
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
- summary(results);
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: VERSION } = require2("../package.json");
616
+ var { version } = require2("../package.json");
960
617
  async function main() {
961
- banner(VERSION);
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
- p9.log.error(err.message);
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
- p9.cancel("\u53D1\u751F\u9519\u8BEF\uFF0C\u5DF2\u9000\u51FA");
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.0",
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
- "@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
  },