aico-cli 2.0.24 → 2.0.25

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.
@@ -13,7 +13,7 @@ import { join as join$1 } from 'node:path';
13
13
  import { join, dirname, basename } from 'pathe';
14
14
  import { fileURLToPath } from 'node:url';
15
15
 
16
- const version = "2.0.24";
16
+ const version = "2.0.25";
17
17
 
18
18
  function displayBanner(subtitle) {
19
19
  const defaultSubtitle = "\u4E00\u952E\u914D\u7F6E\u4F60\u7684\u5F00\u53D1\u73AF\u5883";
@@ -5284,7 +5284,8 @@ class InstallationComposer {
5284
5284
  }
5285
5285
  /**
5286
5286
  * 公司配置安装
5287
- * 包含:Claude Code + 固定API配置 + CCometixLine + MCP + Workflow
5287
+ * 包含:固定API配置 + CCometixLine + MCP + Workflow
5288
+ * Claude Code 由 startClaudeCodeEditor 函数自动安装
5288
5289
  * 公司配置不需要安装 CCR
5289
5290
  */
5290
5291
  async installCompanySetup(options = {}) {
@@ -5310,7 +5311,6 @@ class InstallationComposer {
5310
5311
  const configType = configCheckResult?.data?.configType;
5311
5312
  if (configType === "none" || options.force) {
5312
5313
  const companySteps = [
5313
- INSTALLER_NAMES.CLAUDE_CODE,
5314
5314
  INSTALLER_NAMES.CONFIG,
5315
5315
  INSTALLER_NAMES.CCOMETIX_LINE,
5316
5316
  INSTALLER_NAMES.MCP
@@ -5323,7 +5323,6 @@ class InstallationComposer {
5323
5323
  } else {
5324
5324
  console.log(ansis.yellow(`\u26A0 \u68C0\u6D4B\u5230${configType === "company" ? "\u516C\u53F8" : "\u4E2A\u4EBA"}\u914D\u7F6E\uFF0C\u5C06\u5F3A\u5236\u6267\u884C\u5B8C\u6574\u516C\u53F8\u914D\u7F6E`));
5325
5325
  const companySteps = [
5326
- INSTALLER_NAMES.CLAUDE_CODE,
5327
5326
  INSTALLER_NAMES.CONFIG,
5328
5327
  INSTALLER_NAMES.CCOMETIX_LINE,
5329
5328
  INSTALLER_NAMES.MCP
@@ -5337,12 +5336,12 @@ class InstallationComposer {
5337
5336
  }
5338
5337
  /**
5339
5338
  * 个人配置安装
5340
- * 包含:Claude Code + CCR + 配置备份应用 + CCometixLine + MCP + Workflow
5339
+ * 包含:CCR + 配置备份应用 + CCometixLine + MCP + Workflow
5340
+ * Claude Code 由 startClaudeCodeEditor 函数自动安装
5341
5341
  */
5342
5342
  async installPersonalSetup(options = {}) {
5343
5343
  console.log(ansis.cyan("\n\u{1F464} \u5F00\u59CB\u4E2A\u4EBA\u914D\u7F6E\u5B89\u88C5...\n"));
5344
5344
  const installationSteps = [
5345
- INSTALLER_NAMES.CLAUDE_CODE,
5346
5345
  INSTALLER_NAMES.CCR,
5347
5346
  INSTALLER_NAMES.CONFIG,
5348
5347
  INSTALLER_NAMES.CCOMETIX_LINE,
package/dist/cli.mjs CHANGED
@@ -176,10 +176,7 @@ class ProcessManager extends EventEmitter {
176
176
  unregisterProcess(pid) {
177
177
  const info = this.processes.get(pid);
178
178
  if (info) {
179
- this.log(`\u6CE8\u9500\u8FDB\u7A0B: ${info.name} (PID: ${pid})`, "info");
180
179
  this.processes.delete(pid);
181
- } else {
182
- this.log(`\u5C1D\u8BD5\u6CE8\u9500\u4E0D\u5B58\u5728\u7684\u8FDB\u7A0B (PID: ${pid})`, "warning");
183
180
  }
184
181
  }
185
182
  /**
@@ -202,60 +199,83 @@ class ProcessManager extends EventEmitter {
202
199
  this.log("\u7528\u6237\u8BF7\u6C42\u5F3A\u5236\u5173\u95ED\uFF0C\u7ACB\u5373\u7EC8\u6B62\u6240\u6709\u8FDB\u7A0B...", "warning");
203
200
  };
204
201
  process.once("SIGINT", forceShutdownHandler);
205
- let runningProcesses = Array.from(this.processes.values()).filter((p) => p.status === "running");
206
- if (runningProcesses.length > 0) {
207
- this.log(`\u5411 ${runningProcesses.length} \u4E2A\u8FD0\u884C\u4E2D\u7684\u8FDB\u7A0B\u53D1\u9001SIGTERM\u4FE1\u53F7...`, "info");
208
- for (const info of runningProcesses) {
209
- try {
210
- info.process.kill("SIGTERM");
211
- info.status = "killed";
212
- } catch (error) {
213
- this.log(`\u65E0\u6CD5\u7EC8\u6B62\u8FDB\u7A0B ${info.name} (PID: ${info.pid}): ${error}`, "error");
202
+ try {
203
+ let runningProcesses = Array.from(this.processes.values()).filter((p) => p.status === "running");
204
+ if (runningProcesses.length > 0) {
205
+ for (const info of runningProcesses) {
206
+ try {
207
+ info.process.kill("SIGTERM");
208
+ info.status = "killed";
209
+ } catch (error) {
210
+ this.log(`\u65E0\u6CD5\u7EC8\u6B62\u8FDB\u7A0B ${info.name} (PID: ${info.pid}): ${error}`, "error");
211
+ }
214
212
  }
215
- }
216
- const maxWaitTime = 1e3;
217
- const checkInterval = 100;
218
- let waitedTime = 0;
219
- while (waitedTime < maxWaitTime && !forceShutdownRequested) {
220
- await new Promise((resolve) => setTimeout(resolve, checkInterval));
221
- waitedTime += checkInterval;
222
- runningProcesses = Array.from(this.processes.values()).filter((p) => p.status === "running");
223
- if (runningProcesses.length === 0) {
224
- break;
213
+ const maxWaitTime = 1e3;
214
+ const checkInterval = 100;
215
+ let waitedTime = 0;
216
+ while (waitedTime < maxWaitTime && !forceShutdownRequested) {
217
+ await new Promise((resolve) => setTimeout(resolve, checkInterval));
218
+ waitedTime += checkInterval;
219
+ runningProcesses = Array.from(this.processes.values()).filter((p) => p.status === "running");
220
+ if (runningProcesses.length === 0) {
221
+ break;
222
+ }
225
223
  }
226
224
  }
227
- }
228
- process.removeListener("SIGINT", forceShutdownHandler);
229
- runningProcesses = Array.from(this.processes.values()).filter((p) => p.status === "running");
230
- if (runningProcesses.length > 0) {
231
- this.log(`\u5F3A\u5236\u7EC8\u6B62 ${runningProcesses.length} \u4E2A\u4ECD\u5728\u8FD0\u884C\u7684\u8FDB\u7A0B...`, "warning");
232
- for (const info of runningProcesses) {
233
- try {
234
- info.process.kill("SIGKILL");
235
- this.log(`\u5F3A\u5236\u7EC8\u6B62\u8FDB\u7A0B ${info.name} (PID: ${info.pid})`, "warning");
236
- } catch (error) {
237
- this.log(`\u65E0\u6CD5\u5F3A\u5236\u7EC8\u6B62\u8FDB\u7A0B ${info.name} (PID: ${info.pid}): ${error}`, "error");
225
+ process.removeListener("SIGINT", forceShutdownHandler);
226
+ runningProcesses = Array.from(this.processes.values()).filter((p) => p.status === "running");
227
+ if (runningProcesses.length > 0) {
228
+ this.log(`\u5F3A\u5236\u7EC8\u6B62 ${runningProcesses.length} \u4E2A\u4ECD\u5728\u8FD0\u884C\u7684\u8FDB\u7A0B...`, "warning");
229
+ for (const info of runningProcesses) {
230
+ try {
231
+ info.process.kill("SIGKILL");
232
+ this.log(`\u5F3A\u5236\u7EC8\u6B62\u8FDB\u7A0B ${info.name} (PID: ${info.pid})`, "warning");
233
+ } catch (error) {
234
+ this.log(`\u65E0\u6CD5\u5F3A\u5236\u7EC8\u6B62\u8FDB\u7A0B ${info.name} (PID: ${info.pid}): ${error}`, "error");
235
+ }
238
236
  }
239
237
  }
238
+ const totalTime = Date.now() - firstSignalTime;
239
+ this.log(`\u5173\u95ED\u5B8C\u6210\uFF0C\u8017\u65F6 ${totalTime}ms`, "success");
240
+ } catch (error) {
241
+ this.log(`\u5173\u95ED\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF: ${error}`, "error");
242
+ } finally {
243
+ this.cleanup();
244
+ setTimeout(() => {
245
+ process.exit(0);
246
+ }, 100);
240
247
  }
241
- const totalTime = Date.now() - firstSignalTime;
242
- this.log(`\u5173\u95ED\u5B8C\u6210\uFF0C\u8017\u65F6 ${totalTime}ms`, "success");
243
- this.cleanup();
244
- process.exit(0);
245
248
  }
246
249
  /**
247
250
  * 清理所有进程
248
251
  */
249
252
  cleanup() {
250
- for (const [pid, info] of this.processes) {
251
- if (info.status === "running") {
252
- try {
253
- info.process.kill("SIGKILL");
254
- } catch {
253
+ try {
254
+ let cleanedCount = 0;
255
+ for (const [pid, info] of this.processes) {
256
+ if (info.status === "running") {
257
+ try {
258
+ if (info.process.kill("SIGTERM")) {
259
+ this.log(`\u5DF2\u53D1\u9001 SIGTERM \u4FE1\u53F7\u7ED9\u8FDB\u7A0B ${info.name} (PID: ${pid})`, "info");
260
+ cleanedCount++;
261
+ }
262
+ } catch (error) {
263
+ this.log(`\u53D1\u9001 SIGTERM \u4FE1\u53F7\u7ED9\u8FDB\u7A0B ${info.name} (PID: ${pid}) \u5931\u8D25: ${error}`, "warning");
264
+ try {
265
+ if (info.process.kill("SIGKILL")) {
266
+ this.log(`\u5DF2\u53D1\u9001 SIGKILL \u4FE1\u53F7\u7ED9\u8FDB\u7A0B ${info.name} (PID: ${pid})`, "warning");
267
+ cleanedCount++;
268
+ }
269
+ } catch (killError) {
270
+ this.log(`\u53D1\u9001 SIGKILL \u4FE1\u53F7\u7ED9\u8FDB\u7A0B ${info.name} (PID: ${pid}) \u5931\u8D25: ${killError}`, "error");
271
+ }
272
+ }
255
273
  }
256
274
  }
275
+ this.processes.clear();
276
+ } catch (error) {
277
+ this.log(`\u8FDB\u7A0B\u6E05\u7406\u8FC7\u7A0B\u4E2D\u53D1\u751F\u9519\u8BEF: ${error}`, "error");
257
278
  }
258
- this.processes.clear();
259
279
  }
260
280
  /**
261
281
  * 获取当前运行的进程数量
@@ -532,6 +552,81 @@ promisify$1(exec$1);
532
552
 
533
553
  promisify$1(exec$1);
534
554
 
555
+ async function checkLocalClaudeCode() {
556
+ try {
557
+ const fs = await import('fs');
558
+ const localClaudePath = path.resolve(process.cwd(), "bin/cli/cli.js");
559
+ if (fs.existsSync(localClaudePath)) {
560
+ const stats = fs.statSync(localClaudePath);
561
+ return {
562
+ exists: true,
563
+ path: localClaudePath,
564
+ size: stats.size
565
+ };
566
+ }
567
+ return {
568
+ exists: false,
569
+ path: localClaudePath
570
+ };
571
+ } catch (error) {
572
+ return {
573
+ exists: false,
574
+ path: path.resolve(process.cwd(), "bin/cli/cli.js"),
575
+ error: error instanceof Error ? error.message : String(error)
576
+ };
577
+ }
578
+ }
579
+ async function checkGlobalClaudeCode() {
580
+ try {
581
+ const isWindows = process.platform === "win32";
582
+ const command = isWindows ? "claude.cmd" : "claude";
583
+ const { spawnSync } = await import('child_process');
584
+ const whichCommand = isWindows ? "where" : "which";
585
+ const result = spawnSync(whichCommand, [command], {
586
+ stdio: "pipe",
587
+ timeout: 5e3
588
+ // 5秒超时
589
+ });
590
+ if (result.status === 0 && result.stdout) {
591
+ const output = result.stdout.toString().trim();
592
+ return {
593
+ installed: true,
594
+ command: output.split("\n")[0]
595
+ // 返回第一个匹配的路径
596
+ };
597
+ }
598
+ return {
599
+ installed: false,
600
+ error: result.stderr?.toString() || "\u547D\u4EE4\u672A\u627E\u5230"
601
+ };
602
+ } catch (error) {
603
+ return {
604
+ installed: false,
605
+ error: error instanceof Error ? error.message : String(error)
606
+ };
607
+ }
608
+ }
609
+ async function performHealthCheck() {
610
+ const localCheck = await checkLocalClaudeCode();
611
+ if (localCheck.exists) {
612
+ console.log(ansis.green(`\u2705 \u68C0\u6D4B\u5230\u7F16\u8F91\u5668`));
613
+ } else {
614
+ console.log(ansis.yellow(`\u26A0\uFE0F \u6CA1\u6709\u68C0\u6D4B\u5230\u7F16\u8F91\u5668: ${localCheck.path}`));
615
+ if (localCheck.error) {
616
+ console.log(ansis.gray(` \u9519\u8BEF\u8BE6\u60C5: ${localCheck.error}`));
617
+ }
618
+ }
619
+ const globalCheck = await checkGlobalClaudeCode();
620
+ if (globalCheck.installed) {
621
+ console.log(ansis.green(`\u{1F680} \u542F\u52A8\u7F16\u8F91\u5668`));
622
+ } else {
623
+ console.log(ansis.yellow("\u26A0\uFE0F \u5168\u5C40 Claude Code \u672A\u5B89\u88C5"));
624
+ if (globalCheck.error) {
625
+ console.log(ansis.gray(` \u9519\u8BEF\u8BE6\u60C5: ${globalCheck.error}`));
626
+ }
627
+ }
628
+ }
629
+
535
630
  async function tryStartClaude(command, args, options) {
536
631
  process.platform === "win32";
537
632
  const spawnOptions = {
@@ -546,6 +641,7 @@ async function tryStartClaude(command, args, options) {
546
641
  async function startClaudeCodeEditor(_lang) {
547
642
  try {
548
643
  console.log(ansis.cyan("\u{1F680} \u6B63\u5728\u542F\u52A8\u4EE3\u7801\u7F16\u8F91\u5668..."));
644
+ await performHealthCheck();
549
645
  const isWindows = process.platform === "win32";
550
646
  const spawnOptions = {
551
647
  stdio: "inherit",
@@ -556,14 +652,16 @@ async function startClaudeCodeEditor(_lang) {
556
652
  // 禁用shell模式,避免进程泄漏(Windows和Unix都适用)
557
653
  };
558
654
  try {
559
- const localClaudePath = path.resolve(__dirname, "../../bin/cli/cli.js");
560
- console.log(ansis.gray(`\u{1F4C1} \u5C1D\u8BD5\u4F7F\u7528\u672C\u5730 Claude Code: ${localClaudePath}`));
655
+ const localCheck = await checkLocalClaudeCode();
656
+ if (!localCheck.exists) {
657
+ throw new Error(`\u672C\u5730 aico\u8FD0\u884C\u547D\u4EE4 \u6587\u4EF6\u4E0D\u5B58\u5728: ${localCheck.path}${localCheck.error ? ` (${localCheck.error})` : ""}`);
658
+ }
561
659
  const command = isWindows ? "node.exe" : "node";
562
- const args = [localClaudePath];
660
+ const args = [localCheck.path];
563
661
  await tryStartClaude(command, args, spawnOptions);
564
662
  return;
565
663
  } catch (localError) {
566
- console.log(ansis.yellow("\u26A0\uFE0F \u672C\u5730 Claude Code \u8C03\u7528\u5931\u8D25\uFF0C\u5C1D\u8BD5\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8..."));
664
+ console.log(ansis.yellow("\u{1F4A1} \u5C1D\u8BD5\u81EA\u52A8\u5B89\u88C5\u5E76\u542F\u52A8..."));
567
665
  try {
568
666
  const installed = await isClaudeCodeInstalled();
569
667
  if (!installed) {
@@ -573,6 +671,7 @@ async function startClaudeCodeEditor(_lang) {
573
671
  }
574
672
  const command = isWindows ? "claude.cmd" : "claude";
575
673
  const args = [];
674
+ console.log(ansis.gray(`\u{1F527} \u4F7F\u7528\u5168\u5C40\u547D\u4EE4: ${command}`));
576
675
  if (isWindows) {
577
676
  await tryStartClaude(command, args, spawnOptions);
578
677
  } else {
@@ -580,9 +679,11 @@ async function startClaudeCodeEditor(_lang) {
580
679
  }
581
680
  return;
582
681
  } catch (installError) {
583
- console.log(ansis.yellow("\u26A0\uFE0F \u5B89\u88C5\u540E\u542F\u52A8\u5931\u8D25\uFF0C\u5C1D\u8BD5\u4F7F\u7528 npx..."));
682
+ console.log(ansis.yellow("\u26A0\uFE0F \u5B89\u88C5\u540E\u542F\u52A8\u5931\u8D25: "), ansis.gray(installError.message));
683
+ console.log(ansis.yellow("\u{1F4A1} \u5C1D\u8BD5\u4F7F\u7528 npx \u542F\u52A8..."));
584
684
  try {
585
685
  const npxArgs = ["@anthropic-ai/claude-code"];
686
+ console.log(ansis.gray(`\u{1F527} \u4F7F\u7528 npx \u547D\u4EE4: npx ${npxArgs.join(" ")}`));
586
687
  if (isWindows) {
587
688
  await tryStartClaude("npx.cmd", npxArgs, spawnOptions);
588
689
  } else {
@@ -590,40 +691,58 @@ async function startClaudeCodeEditor(_lang) {
590
691
  }
591
692
  return;
592
693
  } catch (npxError) {
593
- throw installError;
694
+ console.log(ansis.yellow("\u26A0\uFE0F npx \u542F\u52A8\u4E5F\u5931\u8D25: "), ansis.gray(npxError.message));
695
+ throw new Error(`\u6240\u6709\u542F\u52A8\u65B9\u5F0F\u5747\u5931\u8D25:
696
+ - \u672C\u5730\u542F\u52A8: ${localError.message}
697
+ - \u5168\u5C40\u542F\u52A8: ${installError.message || "\u8DF3\u8FC7"}
698
+ - npx\u542F\u52A8: ${npxError.message}`);
594
699
  }
595
700
  }
596
701
  }
597
702
  } catch (error) {
598
703
  console.error(ansis.red("\u274C \u542F\u52A8\u4EE3\u7801\u7F16\u8F91\u5668\u5931\u8D25:"));
599
704
  const isWindows = process.platform === "win32";
705
+ console.error(ansis.yellow("\u{1F4CB} \u8BE6\u7EC6\u8BCA\u65AD\u4FE1\u606F:"));
706
+ console.error(ansis.gray(` \u9519\u8BEF\u7C7B\u578B: ${error.name || "Unknown"}`));
707
+ console.error(ansis.gray(` \u9519\u8BEF\u4EE3\u7801: ${error.code || "\u65E0"}`));
708
+ console.error(ansis.gray(` \u9519\u8BEF\u6D88\u606F: ${error.message || error}`));
709
+ console.error(ansis.gray(` \u5F53\u524D\u76EE\u5F55: ${process.cwd()}`));
710
+ console.error(ansis.gray(` \u64CD\u4F5C\u7CFB\u7EDF: ${process.platform}`));
711
+ console.error(ansis.gray(` Node.js \u7248\u672C: ${process.version}`));
712
+ console.error(ansis.gray(` \u5185\u5B58\u4F7F\u7528: ${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`));
600
713
  if (error.code === "ENOENT" || error.message?.includes("command not found") || error.message?.includes("ENOENT")) {
601
714
  console.error(ansis.yellow("\u{1F4A1} \u53EF\u80FD\u7684\u539F\u56E0:"));
602
715
  console.error(ansis.gray(" 1. \u672C\u5730 Claude Code \u6587\u4EF6\u4E0D\u5B58\u5728\u6216\u8DEF\u5F84\u9519\u8BEF"));
603
716
  console.error(ansis.gray(" 2. \u81EA\u52A8\u5B89\u88C5 Claude Code \u5931\u8D25"));
604
717
  console.error(ansis.gray(" 3. Node.js \u73AF\u5883\u95EE\u9898"));
718
+ console.error(ansis.gray(" 4. PATH \u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u95EE\u9898"));
605
719
  console.error(ansis.yellow("\u{1F4A1} \u89E3\u51B3\u65B9\u6848:"));
606
720
  console.error(ansis.gray(" - \u786E\u4FDD\u9879\u76EE\u5DF2\u6B63\u786E\u6784\u5EFA\uFF0Cbin/cli/cli.js \u6587\u4EF6\u5B58\u5728"));
607
721
  console.error(ansis.gray(" - \u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\uFF0C\u786E\u4FDD\u53EF\u4EE5\u8BBF\u95EE npm \u4ED3\u5E93"));
608
722
  console.error(ansis.gray(" - \u624B\u52A8\u5B89\u88C5: npm install -g @anthropic-ai/claude-code"));
723
+ console.error(ansis.gray(" - \u68C0\u67E5 Node.js \u548C npm \u7248\u672C\u517C\u5BB9\u6027"));
609
724
  if (isWindows) {
610
725
  console.error(ansis.yellow("\u{1F4A1} Windows \u7528\u6237\u989D\u5916\u63D0\u793A:"));
611
726
  console.error(ansis.gray(" 1. \u91CD\u542F\u547D\u4EE4\u884C\u7A97\u53E3\u4EE5\u5237\u65B0 PATH \u73AF\u5883\u53D8\u91CF"));
612
727
  console.error(ansis.gray(" 2. \u786E\u8BA4 npm \u5168\u5C40\u5B89\u88C5\u8DEF\u5F84\u5728 PATH \u4E2D"));
613
728
  console.error(ansis.gray(" 3. \u5C1D\u8BD5\u4F7F\u7528 npx.cmd @anthropic-ai/claude-code --dangerously-skip-permissions"));
614
729
  console.error(ansis.gray(" 4. \u68C0\u67E5\u662F\u5426\u5B89\u88C5\u4E86 Node.js \u548C npm"));
730
+ console.error(ansis.gray(" 5. \u5C1D\u8BD5\u4EE5\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C"));
615
731
  }
616
732
  } else if (error.message?.includes("spawn") || error.message?.includes("\u8FDB\u7A0B")) {
617
733
  console.error(ansis.yellow("\u{1F4A1} \u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF0C\u53EF\u80FD\u662F\u8FDB\u7A0B\u7BA1\u7406\u95EE\u9898:"));
618
734
  console.error(ansis.gray(" 1. \u68C0\u67E5\u7CFB\u7EDF\u662F\u5426\u6709\u8DB3\u591F\u7684\u8D44\u6E90"));
619
735
  console.error(ansis.gray(" 2. \u5C1D\u8BD5\u5173\u95ED\u5176\u4ED6\u5E94\u7528\u7A0B\u5E8F\u91CA\u653E\u5185\u5B58"));
736
+ console.error(ansis.gray(" 3. \u68C0\u67E5\u8FDB\u7A0B\u7BA1\u7406\u5668\u72B6\u6001"));
620
737
  if (isWindows) {
621
- console.error(ansis.gray(" 3. Windows\u7528\u6237: \u5C1D\u8BD5\u4EE5\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C"));
622
- console.error(ansis.gray(" 4. Windows\u7528\u6237: \u68C0\u67E5\u9632\u75C5\u6BD2\u8F6F\u4EF6\u662F\u5426\u963B\u6B62\u4E86\u8FDB\u7A0B\u542F\u52A8"));
738
+ console.error(ansis.gray(" 4. Windows\u7528\u6237: \u5C1D\u8BD5\u4EE5\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C"));
739
+ console.error(ansis.gray(" 5. Windows\u7528\u6237: \u68C0\u67E5\u9632\u75C5\u6BD2\u8F6F\u4EF6\u662F\u5426\u963B\u6B62\u4E86\u8FDB\u7A0B\u542F\u52A8"));
623
740
  }
624
741
  } else {
625
- console.error(ansis.gray(` \u9519\u8BEF\u4FE1\u606F: ${error.message || error}`));
626
- console.error(ansis.gray(` \u9519\u8BEF\u4EE3\u7801: ${error.code || "\u65E0"}`));
742
+ console.error(ansis.yellow("\u{1F4A1} \u672A\u77E5\u9519\u8BEF\u7C7B\u578B\uFF0C\u8BF7\u68C0\u67E5\u4EE5\u4E0B\u5185\u5BB9:"));
743
+ console.error(ansis.gray(" 1. \u67E5\u770B\u5B8C\u6574\u7684\u9519\u8BEF\u5806\u6808\u4FE1\u606F"));
744
+ console.error(ansis.gray(" 2. \u68C0\u67E5\u7CFB\u7EDF\u65E5\u5FD7"));
745
+ console.error(ansis.gray(" 3. \u5C1D\u8BD5\u91CD\u542F\u7CFB\u7EDF"));
627
746
  }
628
747
  try {
629
748
  const processCount = processManager.getProcessCount();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aico-cli",
3
- "version": "2.0.24",
3
+ "version": "2.0.25",
4
4
  "packageManager": "pnpm@9.15.9",
5
5
  "description": "AI CLI",
6
6
  "repository": {
@@ -2,15 +2,6 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
3
  "hooks": {
4
4
  "PreToolUse": [
5
- {
6
- "matcher": "Bash",
7
- "hooks": [
8
- {
9
- "type": "command",
10
- "command": "if [ \"$OS\" = \"Windows_NT\" ]; then powershell -File \"$env:USERPROFILE/.claude/hooks/scripts/PreToolUse/powershell/command-logger.ps1\"; else ~/.claude/hooks/scripts/PreToolUse/bash/command-logger.sh; fi"
11
- }
12
- ]
13
- },
14
5
  {
15
6
  "matcher": "Edit|Write",
16
7
  "hooks": [
@@ -1,95 +1,125 @@
1
- # Stop Hook - 会话摘要
2
- # Claude Code停止响应时记录会话摘要
3
-
4
- # 加载跨平台工具模块
5
- . "$PSScriptRoot\..\..\..\utils\crossplatform-detector.ps1"
6
-
7
- # 获取当前时间信息
8
- $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
9
- $sessionFile = "$(Get-HooksDirectory)\session-summary.txt"
10
-
11
- # 确保日志目录存在
12
- $logDir = Split-Path -Parent $sessionFile
13
- if (-not (Test-Path $logDir)) {
14
- New-Item -ItemType Directory -Path $logDir -Force | Out-Null
1
+ # Stop Hook - Session Summary
2
+ # Record session summary when Claude Code stops
3
+
4
+ # Set error handling preference
5
+ $ErrorActionPreference = "Continue"
6
+
7
+ # Simple OS detection
8
+ function Get-OperatingSystem {
9
+ if ($env:OS -eq "Windows_NT") {
10
+ return "windows"
11
+ } elseif ($IsLinux) {
12
+ return "linux"
13
+ } elseif ($IsMacOS) {
14
+ return "macos"
15
+ } else {
16
+ return "unknown"
17
+ }
15
18
  }
16
19
 
17
- # 创建会话摘要的函数
18
- function New-SessionSummary {
19
- $summary = @"
20
- === Claude Code 会话摘要 ===
21
- 结束时间: $timestamp
22
- 工作目录: $(Get-ProjectDirectory)
23
- 操作系统: $(Get-OperatingSystem)
20
+ # Simple log function
21
+ function Write-HookLog {
22
+ param(
23
+ [string]$Level,
24
+ [string]$Message
25
+ )
26
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
27
+ Write-Host "[$timestamp] [$Level] $Message"
28
+ }
24
29
 
25
- "@
30
+ # Send stop notification function
31
+ function Send-StopNotification {
32
+ $osType = Get-OperatingSystem
33
+ $message = "Claude Code session ended"
34
+ $title = "Claude Code"
26
35
 
27
- # 统计最近的命令执行情况
28
- $commandLog = "$(Get-HooksDirectory)\bash-command-log.txt"
29
- if (Test-Path $commandLog) {
30
- $today = Get-Date -Format "yyyy-MM-dd"
31
- $todayCommands = (Get-Content $commandLog | Where-Object { $_ -match $today }).Count
32
- $summary += "今日执行命令数: $todayCommands`r`n"
33
-
34
- # 显示最近的几个命令
35
- $summary += "最近执行的命令:`r`n"
36
- $recentCommands = Get-Content $commandLog | Select-Object -Last 5
37
- $summary += ($recentCommands -join "`r`n") + "`r`n"
38
- } else {
39
- $summary += "今日执行命令数: 0`r`n"
36
+ Write-HookLog "INFO" "Detected OS: $osType"
37
+
38
+ try {
39
+ # Simplified notification logic
40
+ switch ($osType) {
41
+ "windows" {
42
+ # Windows simplified notification
43
+ Write-Host "=== $title ===" -ForegroundColor Green
44
+ Write-Host $message -ForegroundColor White
45
+
46
+ # Play simple system sound
47
+ try {
48
+ [System.Media.SystemSounds]::Asterisk.Play()
49
+ Write-HookLog "INFO" "Played system sound successfully"
50
+ } catch {
51
+ Write-HookLog "WARN" "Failed to play system sound: $($_.Exception.Message)"
52
+ }
53
+ }
54
+ default {
55
+ Write-HookLog "INFO" "Using console output for notification"
56
+ Write-Host "=== $title ===" -ForegroundColor Green
57
+ Write-Host $message -ForegroundColor White
58
+ }
59
+ }
60
+ } catch {
61
+ Write-HookLog "ERROR" "Error during notification: $($_.Exception.Message)"
62
+ Write-Host "[$Title] $message" -ForegroundColor Cyan
40
63
  }
41
64
 
42
- $summary += "`r`n---`r`n`r`n"
65
+ Write-HookLog "INFO" "Stop notification sent"
66
+ }
43
67
 
44
- # 写入文件
45
- Add-Content -Path $sessionFile -Value $summary
68
+ # Simple session summary function
69
+ function Write-SimpleSessionSummary {
70
+ try {
71
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
72
+ $summary = @"
73
+ === Claude Code Session Summary ===
74
+ End Time: $timestamp
75
+ Working Directory: $(Get-Location)
76
+ OS: $(Get-OperatingSystem)
46
77
 
47
- # 记录到主日志
48
- Write-HookLog "INFO" "Claude Code会话结束,已生成摘要"
49
- }
78
+ Session Summary: Claude Code session ended normally
50
79
 
51
- # 创建会话摘要
52
- New-SessionSummary
80
+ ---
53
81
 
54
- # 发送完成通知的函数
55
- function Send-CompletionNotification {
56
- $osType = Get-OperatingSystem
57
- $message = "Claude Code 会话已结束"
58
- $title = "Claude Code"
82
+ "@
59
83
 
60
- switch ($osType) {
61
- "windows" {
62
- # Windows 使用系统通知
63
- try {
64
- Add-Type -AssemblyName System.Windows.Forms
65
- $result = [System.Windows.Forms.MessageBox]::Show($Message, $Title, [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
66
-
67
- # 播放完成声音
68
- $soundFile = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::Windows) + "\Media\tada.wav"
69
- if (Test-Path $soundFile) {
70
- $player = New-Object Media.SoundPlayer $soundFile
71
- $player.Play()
72
- }
73
- } catch {
74
- Write-Host "通知发送失败: $_"
75
- }
84
+ # Write to file
85
+ try {
86
+ $userProfile = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::UserProfile)
87
+ $sessionFile = "$userProfile\.claude\hooks\session-summary.txt"
88
+ Write-HookLog "INFO" "Session file path: $sessionFile"
89
+ } catch {
90
+ Write-HookLog "ERROR" "Failed to get user profile path: $($_.Exception.Message)"
91
+ # Fallback to a simpler path
92
+ $sessionFile = ".\session-summary.txt"
93
+ Write-HookLog "WARN" "Using fallback path: $sessionFile"
76
94
  }
77
- default {
78
- # Unix/Linux/macOS 系统通知
79
- if (Test-CommandExists "notify-send") {
80
- notify-send $Title $Message
81
- }
82
95
 
83
- # 播放完成声音
84
- if (Test-CommandExists "paplay") {
85
- paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>$null
86
- }
96
+ try {
97
+ Add-Content -Path $sessionFile -Value $summary
98
+ Write-HookLog "INFO" "Session summary written to file"
99
+ } catch {
100
+ Write-HookLog "WARN" "Failed to write session summary: $($_.Exception.Message)"
87
101
  }
102
+ } catch {
103
+ Write-HookLog "ERROR" "Error creating session summary: $($_.Exception.Message)"
88
104
  }
89
105
  }
90
106
 
91
- # 发送完成通知
92
- Send-CompletionNotification
93
-
94
- # 继续执行
95
- exit 0
107
+ # Main execution
108
+ try {
109
+ Write-HookLog "INFO" "Starting Stop hook"
110
+
111
+ # Create session summary
112
+ Write-SimpleSessionSummary
113
+
114
+ # Send completion notification
115
+ Send-StopNotification
116
+
117
+ Write-HookLog "INFO" "Stop hook completed successfully"
118
+ exit 0
119
+ } catch {
120
+ Write-HookLog "ERROR" "Stop hook failed: $($_.Exception.Message)"
121
+ Write-HookLog "ERROR" "Exception type: $($_.Exception.GetType().FullName)"
122
+ Write-HookLog "ERROR" "Stack trace: $($_.Exception.StackTrace)"
123
+ # Even if there's an error, exit with success code to avoid disrupting the main process
124
+ exit 0
125
+ }
@@ -1,61 +1,33 @@
1
- # UserPromptSubmit Hook - 输入通知器
2
- # 当用户提交输入时发送通知
1
+ # UserPromptSubmit Hook - Input Notifier
2
+ # Send notification when user submits input
3
3
 
4
- # 设置错误处理策略
4
+ # Set error handling preference
5
5
  $ErrorActionPreference = "Continue"
6
6
 
7
- # 尝试加载跨平台工具模块
8
- $utilsPath = Join-Path $PSScriptRoot "../../utils/crossplatform-detector.ps1"
9
- if (Test-Path $utilsPath) {
10
- try {
11
- . $utilsPath
12
- Write-Host "[INFO] 成功加载跨平台工具模块"
13
- } catch {
14
- Write-Host "[WARN] 加载跨平台工具模块失败: $($_.Exception.Message)"
15
- }
16
- } else {
17
- Write-Host "[WARN] 跨平台工具模块不存在: $utilsPath"
18
- }
19
-
20
- # 如果模块加载失败,提供备用函数
21
- if (-not (Get-Command "Write-HookLog" -ErrorAction SilentlyContinue)) {
22
- function Write-HookLog {
23
- param(
24
- [string]$Level,
25
- [string]$Message
26
- )
27
- $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
28
- Write-Host "[$timestamp] [$Level] $Message"
29
- }
30
- }
31
-
32
- if (-not (Get-Command "Test-CommandExists" -ErrorAction SilentlyContinue)) {
33
- function Test-CommandExists {
34
- param([string]$Command)
35
- try {
36
- Get-Command $Command -ErrorAction Stop > $null
37
- return $true
38
- } catch {
39
- return $false
40
- }
7
+ # Simple OS detection
8
+ function Get-OperatingSystem {
9
+ if ($env:OS -eq "Windows_NT") {
10
+ return "windows"
11
+ } elseif ($IsLinux) {
12
+ return "linux"
13
+ } elseif ($IsMacOS) {
14
+ return "macos"
15
+ } else {
16
+ return "unknown"
41
17
  }
42
18
  }
43
19
 
44
- if (-not (Get-Command "Get-OperatingSystem" -ErrorAction SilentlyContinue)) {
45
- function Get-OperatingSystem {
46
- if ($env:OS -eq "Windows_NT") {
47
- return "windows"
48
- } elseif ($IsLinux) {
49
- return "linux"
50
- } elseif ($IsMacOS) {
51
- return "macos"
52
- } else {
53
- return "unknown"
54
- }
55
- }
20
+ # Simple log function
21
+ function Write-HookLog {
22
+ param(
23
+ [string]$Level,
24
+ [string]$Message
25
+ )
26
+ $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
27
+ Write-Host "[$timestamp] [$Level] $Message"
56
28
  }
57
29
 
58
- # 发送通知的函数
30
+ # Send notification function
59
31
  function Send-Notification {
60
32
  param(
61
33
  [string]$Title,
@@ -63,84 +35,51 @@ function Send-Notification {
63
35
  )
64
36
 
65
37
  $osType = Get-OperatingSystem
66
- Write-HookLog "INFO" "检测到操作系统: $osType"
38
+ Write-HookLog "INFO" "Detected OS: $osType"
67
39
 
68
40
  try {
69
- # 简化通知逻辑 - 使用控制台输出作为主要方式
41
+ # Simplified notification logic
70
42
  switch ($osType) {
71
43
  "windows" {
72
- # Windows 简化通知 - 只使用控制台输出
44
+ # Windows simplified notification
73
45
  Write-Host "=== $Title ===" -ForegroundColor Yellow
74
46
  Write-Host $Message -ForegroundColor White
75
47
 
76
- # 播放简单的系统声音
48
+ # Play simple system sound
77
49
  try {
78
50
  [System.Media.SystemSounds]::Beep.Play()
79
- Write-HookLog "INFO" "播放系统蜂鸣声成功"
51
+ Write-HookLog "INFO" "Played system beep successfully"
80
52
  } catch {
81
- Write-HookLog "WARN" "系统声音播放失败: $($_.Exception.Message)"
82
- }
83
- }
84
- "linux" {
85
- # Linux 简化通知
86
- Write-Host "=== $Title ===" -ForegroundColor Yellow
87
- Write-Host $Message -ForegroundColor White
88
-
89
- # 如果可用,使用系统通知
90
- if (Test-CommandExists "notify-send") {
91
- try {
92
- Start-Process -FilePath "notify-send" -ArgumentList $Title, $Message -NoNewWindow -Wait
93
- Write-HookLog "INFO" "发送 Linux 桌面通知"
94
- } catch {
95
- Write-HookLog "WARN" "notify-send 失败,使用控制台输出"
96
- }
97
- }
98
- }
99
- "macos" {
100
- # macOS 简化通知
101
- Write-Host "=== $Title ===" -ForegroundColor Yellow
102
- Write-Host $Message -ForegroundColor White
103
-
104
- # 如果可用,使用系统通知
105
- if (Test-CommandExists "osascript") {
106
- try {
107
- $script = "display notification \"$Message\" with title \"$Title\""
108
- Start-Process -FilePath "osascript" -ArgumentList "-e", $script -NoNewWindow -Wait
109
- Write-HookLog "INFO" "发送 macOS 桌面通知"
110
- } catch {
111
- Write-HookLog "WARN" "osascript 失败,使用控制台输出"
112
- }
53
+ Write-HookLog "WARN" "Failed to play system sound: $($_.Exception.Message)"
113
54
  }
114
55
  }
115
56
  default {
116
- Write-HookLog "WARN" "未知操作系统,使用控制台输出"
57
+ Write-HookLog "INFO" "Using console output for notification"
117
58
  Write-Host "=== $Title ===" -ForegroundColor Yellow
118
59
  Write-Host $Message -ForegroundColor White
119
60
  }
120
61
  }
121
62
  } catch {
122
- Write-HookLog "ERROR" "通知发送过程中发生错误: $($_.Exception.Message)"
123
- # 确保总是有某种形式的输出
63
+ Write-HookLog "ERROR" "Error during notification: $($_.Exception.Message)"
124
64
  Write-Host "[$Title] $Message" -ForegroundColor Cyan
125
65
  }
126
66
 
127
- Write-HookLog "INFO" "通知发送完成: $Message"
67
+ Write-HookLog "INFO" "Notification sent: $Message"
128
68
  }
129
69
 
130
- # 主执行逻辑
70
+ # Main execution
131
71
  try {
132
- Write-HookLog "INFO" "开始执行 UserPromptSubmit hook"
72
+ Write-HookLog "INFO" "Starting UserPromptSubmit hook"
133
73
 
134
- $message = "Claude Code 等待您的输入"
74
+ $message = "Claude Code is waiting for your input"
135
75
  $title = "Claude Code"
136
76
 
137
- # 发送通知
77
+ # Send notification
138
78
  Send-Notification -Title $title -Message $message
139
79
 
140
- Write-HookLog "INFO" "UserPromptSubmit hook 执行成功"
80
+ Write-HookLog "INFO" "UserPromptSubmit hook completed successfully"
141
81
  exit 0
142
82
  } catch {
143
- Write-HookLog "ERROR" "UserPromptSubmit hook 执行失败: $($_.Exception.Message)"
144
- # 即使失败也返回成功状态码,避免影响主流程
83
+ Write-HookLog "ERROR" "UserPromptSubmit hook failed: $($_.Exception.Message)"
145
84
  exit 0
146
85
  }