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.
- package/dist/chunks/simple-config.mjs +5 -6
- package/dist/cli.mjs +172 -53
- package/package.json +1 -1
- package/templates/hooks/claude-code-hooks.json +0 -9
- package/templates/hooks/scripts/Stop/powershell/session-summary.ps1 +108 -78
- package/templates/hooks/scripts/UserPromptSubmit/powershell/input-notifier.ps1 +37 -98
|
@@ -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.
|
|
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
|
-
*
|
|
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
|
-
* 包含:
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
560
|
-
|
|
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 = [
|
|
660
|
+
const args = [localCheck.path];
|
|
563
661
|
await tryStartClaude(command, args, spawnOptions);
|
|
564
662
|
return;
|
|
565
663
|
} catch (localError) {
|
|
566
|
-
console.log(ansis.yellow("\
|
|
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
|
|
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
|
-
|
|
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("
|
|
622
|
-
console.error(ansis.gray("
|
|
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.
|
|
626
|
-
console.error(ansis.gray(
|
|
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
|
@@ -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
|
-
#
|
|
3
|
-
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
$
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
65
|
+
Write-HookLog "INFO" "Stop notification sent"
|
|
66
|
+
}
|
|
43
67
|
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
9
|
-
if (
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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" "
|
|
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" "
|
|
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 "
|
|
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" "
|
|
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" "
|
|
67
|
+
Write-HookLog "INFO" "Notification sent: $Message"
|
|
128
68
|
}
|
|
129
69
|
|
|
130
|
-
#
|
|
70
|
+
# Main execution
|
|
131
71
|
try {
|
|
132
|
-
Write-HookLog "INFO" "
|
|
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
|
|
144
|
-
# 即使失败也返回成功状态码,避免影响主流程
|
|
83
|
+
Write-HookLog "ERROR" "UserPromptSubmit hook failed: $($_.Exception.Message)"
|
|
145
84
|
exit 0
|
|
146
85
|
}
|