aico-cli 0.4.4 → 0.4.6
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 +1 -1
- package/dist/cli.mjs +161 -16
- package/package.json +1 -1
- package/templates/commands/git/worktree.md +11 -11
- package/templates/personality.md +9 -1
|
@@ -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 = "0.4.
|
|
16
|
+
const version = "0.4.6";
|
|
17
17
|
|
|
18
18
|
function displayBanner(subtitle) {
|
|
19
19
|
const defaultSubtitle = "\u4E00\u952E\u914D\u7F6E\u4F60\u7684\u5F00\u53D1\u73AF\u5883";
|
package/dist/cli.mjs
CHANGED
|
@@ -53,13 +53,26 @@ class ProcessManager extends EventEmitter {
|
|
|
53
53
|
async spawn(command, args = [], options = {}) {
|
|
54
54
|
const startTime = Date.now();
|
|
55
55
|
const processName = options.name || command;
|
|
56
|
+
const isWindows = process.platform === "win32";
|
|
56
57
|
this.log(`\u542F\u52A8\u8FDB\u7A0B: ${processName}`, "info");
|
|
57
58
|
try {
|
|
58
|
-
|
|
59
|
+
let finalCommand = command;
|
|
60
|
+
let finalArgs = args;
|
|
61
|
+
if (isWindows) {
|
|
62
|
+
if (command === "npx") {
|
|
63
|
+
finalCommand = "npx.cmd";
|
|
64
|
+
} else if (command === "claude") {
|
|
65
|
+
finalCommand = "claude.cmd";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const child = spawn(finalCommand, finalArgs, {
|
|
59
69
|
cwd: options.cwd || process.cwd(),
|
|
60
70
|
env: { ...process.env, ...options.env },
|
|
61
71
|
stdio: options.stdio || "pipe",
|
|
62
|
-
shell:
|
|
72
|
+
shell: false,
|
|
73
|
+
// 禁用shell模式,避免进程泄漏
|
|
74
|
+
windowsHide: isWindows
|
|
75
|
+
// Windows上隐藏子进程窗口
|
|
63
76
|
});
|
|
64
77
|
this.registerProcess(child, processName);
|
|
65
78
|
let timeoutId;
|
|
@@ -246,14 +259,20 @@ class ProcessManager extends EventEmitter {
|
|
|
246
259
|
* 清理所有进程
|
|
247
260
|
*/
|
|
248
261
|
cleanup() {
|
|
262
|
+
const isWindows = process.platform === "win32";
|
|
263
|
+
if (isWindows) {
|
|
264
|
+
this.cleanupWindowsProcesses();
|
|
265
|
+
}
|
|
249
266
|
for (const [pid, info] of this.processes) {
|
|
250
267
|
if (info.status === "running") {
|
|
251
268
|
try {
|
|
252
|
-
|
|
269
|
+
const signal = isWindows ? "SIGTERM" : "SIGKILL";
|
|
270
|
+
info.process.kill(signal);
|
|
253
271
|
} catch {
|
|
254
272
|
}
|
|
255
273
|
}
|
|
256
274
|
}
|
|
275
|
+
this.cleanupZombieProcesses();
|
|
257
276
|
this.processes.clear();
|
|
258
277
|
}
|
|
259
278
|
/**
|
|
@@ -335,9 +354,18 @@ class ProcessManager extends EventEmitter {
|
|
|
335
354
|
cleanupZombieProcesses() {
|
|
336
355
|
let cleanedCount = 0;
|
|
337
356
|
const runningProcesses = this.getRunningProcessList();
|
|
357
|
+
const isWindows = process.platform === "win32";
|
|
358
|
+
this.log(`\u5F00\u59CB\u6E05\u7406\u50F5\u5C38\u8FDB\u7A0B\uFF0C\u5F53\u524D\u8FD0\u884C\u4E2D\u8FDB\u7A0B\u6570: ${runningProcesses.length}`, "info");
|
|
338
359
|
for (const process2 of runningProcesses) {
|
|
339
360
|
try {
|
|
340
361
|
process2.process.kill(0);
|
|
362
|
+
if (isWindows) {
|
|
363
|
+
if (process2.process.exitCode !== null) {
|
|
364
|
+
this.log(`\u6E05\u7406Windows\u50F5\u5C38\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
365
|
+
this.unregisterProcess(process2.pid);
|
|
366
|
+
cleanedCount++;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
341
369
|
} catch (error) {
|
|
342
370
|
this.log(`\u6E05\u7406\u50F5\u5C38\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
343
371
|
this.unregisterProcess(process2.pid);
|
|
@@ -349,6 +377,56 @@ class ProcessManager extends EventEmitter {
|
|
|
349
377
|
}
|
|
350
378
|
return cleanedCount;
|
|
351
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* 深度清理进程,包括可能的残留进程
|
|
382
|
+
*/
|
|
383
|
+
deepCleanup() {
|
|
384
|
+
const isWindows = process.platform === "win32";
|
|
385
|
+
let totalCleaned = 0;
|
|
386
|
+
this.log("\u5F00\u59CB\u6DF1\u5EA6\u6E05\u7406\u8FDB\u7A0B...", "info");
|
|
387
|
+
totalCleaned += this.cleanupZombieProcesses();
|
|
388
|
+
if (isWindows) {
|
|
389
|
+
totalCleaned += this.cleanupWindowsProcesses();
|
|
390
|
+
}
|
|
391
|
+
const remainingProcesses = this.getProcessCount();
|
|
392
|
+
if (remainingProcesses > 0) {
|
|
393
|
+
this.log(`\u5F3A\u5236\u6E05\u7406\u5269\u4F59\u7684 ${remainingProcesses} \u4E2A\u8FDB\u7A0B`, "warning");
|
|
394
|
+
this.cleanup();
|
|
395
|
+
totalCleaned += remainingProcesses;
|
|
396
|
+
}
|
|
397
|
+
this.log(`\u6DF1\u5EA6\u6E05\u7406\u5B8C\u6210\uFF0C\u603B\u5171\u6E05\u7406\u4E86 ${totalCleaned} \u4E2A\u8FDB\u7A0B`, "info");
|
|
398
|
+
return totalCleaned;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Windows平台特定的进程清理
|
|
402
|
+
* 针对Windows的进程管理特点进行优化
|
|
403
|
+
*/
|
|
404
|
+
cleanupWindowsProcesses() {
|
|
405
|
+
const isWindows = process.platform === "win32";
|
|
406
|
+
if (!isWindows) {
|
|
407
|
+
return 0;
|
|
408
|
+
}
|
|
409
|
+
let cleanedCount = 0;
|
|
410
|
+
const runningProcesses = this.getRunningProcessList();
|
|
411
|
+
this.log("\u6267\u884CWindows\u5E73\u53F0\u7279\u5B9A\u8FDB\u7A0B\u6E05\u7406...", "info");
|
|
412
|
+
for (const process2 of runningProcesses) {
|
|
413
|
+
try {
|
|
414
|
+
if (process2.process.exitCode !== null) {
|
|
415
|
+
this.log(`\u6E05\u7406Windows\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "warning");
|
|
416
|
+
this.unregisterProcess(process2.pid);
|
|
417
|
+
cleanedCount++;
|
|
418
|
+
}
|
|
419
|
+
} catch (error) {
|
|
420
|
+
this.log(`\u5F3A\u5236\u6E05\u7406Windows\u8FDB\u7A0B: ${process2.name} (PID: ${process2.pid})`, "error");
|
|
421
|
+
this.unregisterProcess(process2.pid);
|
|
422
|
+
cleanedCount++;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (cleanedCount > 0) {
|
|
426
|
+
this.log(`Windows\u5E73\u53F0\u6E05\u7406\u4E86 ${cleanedCount} \u4E2A\u8FDB\u7A0B`, "info");
|
|
427
|
+
}
|
|
428
|
+
return cleanedCount;
|
|
429
|
+
}
|
|
352
430
|
/**
|
|
353
431
|
* 添加进程到列表(用于测试)
|
|
354
432
|
*/
|
|
@@ -361,33 +439,59 @@ class ProcessManager extends EventEmitter {
|
|
|
361
439
|
*/
|
|
362
440
|
async replaceProcess(command, args = [], options = {}) {
|
|
363
441
|
const processName = options.name || command;
|
|
442
|
+
const isWindows = process.platform === "win32";
|
|
364
443
|
try {
|
|
365
|
-
this.log(`\u6E05\u7406 ${this.processes.size} \u4E2A\u7BA1\u7406\u8FDB\u7A0B...`, "info");
|
|
366
444
|
this.cleanup();
|
|
367
445
|
const { spawn: spawn2 } = await import('node:child_process');
|
|
368
|
-
|
|
446
|
+
let finalCommand = command;
|
|
447
|
+
let finalArgs = args;
|
|
448
|
+
if (isWindows) {
|
|
449
|
+
if (command === "npx") {
|
|
450
|
+
finalCommand = "npx.cmd";
|
|
451
|
+
} else if (command === "claude") {
|
|
452
|
+
finalCommand = "claude.cmd";
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
const child = spawn2(finalCommand, finalArgs, {
|
|
369
456
|
stdio: "inherit",
|
|
370
457
|
// 继承所有 stdio
|
|
371
458
|
cwd: options.cwd || process.cwd(),
|
|
372
459
|
env: { ...process.env, ...options.env },
|
|
373
|
-
shell:
|
|
374
|
-
//
|
|
375
|
-
//
|
|
460
|
+
shell: false,
|
|
461
|
+
// 禁用shell模式,避免进程泄漏
|
|
462
|
+
// Windows上使用更严格的进程控制
|
|
463
|
+
windowsHide: isWindows
|
|
464
|
+
// Windows上隐藏子进程窗口
|
|
376
465
|
});
|
|
377
466
|
child.on("error", (error) => {
|
|
378
467
|
this.log(`\u8FDB\u7A0B\u66FF\u6362\u5931\u8D25: ${error}`, "error");
|
|
468
|
+
this.cleanup();
|
|
379
469
|
process.exit(1);
|
|
380
470
|
});
|
|
381
471
|
child.on("exit", (code) => {
|
|
382
472
|
this.log(`\u66FF\u6362\u8FDB\u7A0B\u5DF2\u9000\u51FA\uFF0C\u9000\u51FA\u7801: ${code}`, "info");
|
|
473
|
+
this.cleanup();
|
|
383
474
|
process.exit(code || 0);
|
|
384
475
|
});
|
|
385
476
|
this.registerProcess(child, processName);
|
|
477
|
+
if (isWindows) {
|
|
478
|
+
const checkInterval = setInterval(() => {
|
|
479
|
+
if (child.exitCode !== null) {
|
|
480
|
+
clearInterval(checkInterval);
|
|
481
|
+
this.cleanup();
|
|
482
|
+
process.exit(child.exitCode || 0);
|
|
483
|
+
}
|
|
484
|
+
}, 1e3);
|
|
485
|
+
child.on("exit", () => {
|
|
486
|
+
clearInterval(checkInterval);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
386
489
|
await new Promise(() => {
|
|
387
490
|
});
|
|
388
491
|
process.exit(0);
|
|
389
492
|
} catch (error) {
|
|
390
493
|
this.log(`\u8FDB\u7A0B\u66FF\u6362\u5931\u8D25: ${error}`, "error");
|
|
494
|
+
this.cleanup();
|
|
391
495
|
process.exit(1);
|
|
392
496
|
}
|
|
393
497
|
}
|
|
@@ -502,12 +606,14 @@ promisify$1(exec$1);
|
|
|
502
606
|
promisify$1(exec$1);
|
|
503
607
|
|
|
504
608
|
async function tryStartClaude(command, args, options) {
|
|
609
|
+
const isWindows = process.platform === "win32";
|
|
505
610
|
await processManager.replaceProcess(command, args, {
|
|
506
611
|
name: "Claude Code",
|
|
507
612
|
cwd: options.cwd,
|
|
508
613
|
stdio: "inherit",
|
|
509
614
|
// 必须继承 stdio 才能正确交互
|
|
510
|
-
shell: options.shell
|
|
615
|
+
shell: isWindows ? false : options.shell
|
|
616
|
+
// Windows上禁用shell避免进程泄漏
|
|
511
617
|
});
|
|
512
618
|
}
|
|
513
619
|
async function startClaudeCodeEditor(_lang) {
|
|
@@ -519,18 +625,28 @@ async function startClaudeCodeEditor(_lang) {
|
|
|
519
625
|
// 继承父进程的 stdio,这样可以在当前终端中交互
|
|
520
626
|
cwd: process.cwd(),
|
|
521
627
|
// 使用当前工作目录
|
|
522
|
-
shell:
|
|
523
|
-
// Windows
|
|
628
|
+
shell: false
|
|
629
|
+
// 禁用shell模式,避免进程泄漏(Windows和Unix都适用)
|
|
524
630
|
};
|
|
525
631
|
try {
|
|
526
632
|
const command = isWindows ? "claude.cmd" : "claude";
|
|
527
|
-
|
|
633
|
+
const args = ["--dangerously-skip-permissions"];
|
|
634
|
+
if (isWindows) {
|
|
635
|
+
await tryStartClaude(command, args, spawnOptions);
|
|
636
|
+
} else {
|
|
637
|
+
await tryStartClaude(command, args, spawnOptions);
|
|
638
|
+
}
|
|
528
639
|
return;
|
|
529
640
|
} catch (firstError) {
|
|
530
641
|
if (firstError.code === "ENOENT") {
|
|
531
642
|
console.log(ansis.yellow("\u26A0\uFE0F \u76F4\u63A5\u8C03\u7528 claude \u5931\u8D25\uFF0C\u5C1D\u8BD5\u4F7F\u7528 npx..."));
|
|
532
643
|
try {
|
|
533
|
-
|
|
644
|
+
const npxArgs = ["@anthropic-ai/claude-code", "--dangerously-skip-permissions"];
|
|
645
|
+
if (isWindows) {
|
|
646
|
+
await tryStartClaude("npx.cmd", npxArgs, spawnOptions);
|
|
647
|
+
} else {
|
|
648
|
+
await tryStartClaude("npx", npxArgs, spawnOptions);
|
|
649
|
+
}
|
|
534
650
|
return;
|
|
535
651
|
} catch (secondError) {
|
|
536
652
|
throw firstError;
|
|
@@ -541,21 +657,50 @@ async function startClaudeCodeEditor(_lang) {
|
|
|
541
657
|
}
|
|
542
658
|
} catch (error) {
|
|
543
659
|
console.error(ansis.red("\u274C \u542F\u52A8\u4EE3\u7801\u7F16\u8F91\u5668\u5931\u8D25:"));
|
|
660
|
+
const isWindows = process.platform === "win32";
|
|
544
661
|
if (error.code === "ENOENT" || error.message?.includes("command not found") || error.message?.includes("ENOENT")) {
|
|
545
662
|
console.error(ansis.yellow("\u{1F4A1} \u8BF7\u786E\u4FDD\u5DF2\u5B89\u88C5 Claude Code:"));
|
|
546
663
|
console.error(ansis.gray(" npm install -g @anthropic-ai/claude-code"));
|
|
547
|
-
if (
|
|
664
|
+
if (isWindows) {
|
|
548
665
|
console.error(ansis.yellow("\u{1F4A1} Windows \u7528\u6237\u989D\u5916\u63D0\u793A:"));
|
|
549
666
|
console.error(ansis.gray(" 1. \u91CD\u542F\u547D\u4EE4\u884C\u7A97\u53E3\u4EE5\u5237\u65B0 PATH \u73AF\u5883\u53D8\u91CF"));
|
|
550
667
|
console.error(ansis.gray(" 2. \u786E\u8BA4 npm \u5168\u5C40\u5B89\u88C5\u8DEF\u5F84\u5728 PATH \u4E2D"));
|
|
551
|
-
console.error(ansis.gray(" 3. \u5C1D\u8BD5\u4F7F\u7528 npx @anthropic-ai/claude-code --dangerously-skip-permissions"));
|
|
668
|
+
console.error(ansis.gray(" 3. \u5C1D\u8BD5\u4F7F\u7528 npx.cmd @anthropic-ai/claude-code --dangerously-skip-permissions"));
|
|
669
|
+
console.error(ansis.gray(" 4. \u68C0\u67E5\u662F\u5426\u5B89\u88C5\u4E86 Node.js \u548C npm"));
|
|
670
|
+
}
|
|
671
|
+
} else if (error.message?.includes("spawn") || error.message?.includes("\u8FDB\u7A0B")) {
|
|
672
|
+
console.error(ansis.yellow("\u{1F4A1} \u8FDB\u7A0B\u542F\u52A8\u5931\u8D25\uFF0C\u53EF\u80FD\u662F\u8FDB\u7A0B\u7BA1\u7406\u95EE\u9898:"));
|
|
673
|
+
console.error(ansis.gray(" 1. \u68C0\u67E5\u7CFB\u7EDF\u662F\u5426\u6709\u8DB3\u591F\u7684\u8D44\u6E90"));
|
|
674
|
+
console.error(ansis.gray(" 2. \u5C1D\u8BD5\u5173\u95ED\u5176\u4ED6\u5E94\u7528\u7A0B\u5E8F\u91CA\u653E\u5185\u5B58"));
|
|
675
|
+
if (isWindows) {
|
|
676
|
+
console.error(ansis.gray(" 3. Windows\u7528\u6237: \u5C1D\u8BD5\u4EE5\u7BA1\u7406\u5458\u6743\u9650\u8FD0\u884C"));
|
|
677
|
+
console.error(ansis.gray(" 4. Windows\u7528\u6237: \u68C0\u67E5\u9632\u75C5\u6BD2\u8F6F\u4EF6\u662F\u5426\u963B\u6B62\u4E86\u8FDB\u7A0B\u542F\u52A8"));
|
|
552
678
|
}
|
|
553
679
|
} else {
|
|
554
680
|
console.error(ansis.gray(` \u9519\u8BEF\u4FE1\u606F: ${error.message || error}`));
|
|
681
|
+
console.error(ansis.gray(` \u9519\u8BEF\u4EE3\u7801: ${error.code || "\u65E0"}`));
|
|
682
|
+
}
|
|
683
|
+
try {
|
|
684
|
+
const processCount = processManager.getProcessCount();
|
|
685
|
+
const runningCount = processManager.getRunningProcessCount();
|
|
686
|
+
console.error(ansis.yellow(`\u{1F4CA} \u5F53\u524D\u8FDB\u7A0B\u72B6\u6001: \u603B\u8FDB\u7A0B\u6570 ${processCount}, \u8FD0\u884C\u4E2D ${runningCount}`));
|
|
687
|
+
if (runningCount > 0) {
|
|
688
|
+
console.error(ansis.yellow("\u{1F4A1} \u68C0\u6D4B\u5230\u8FD0\u884C\u4E2D\u7684\u8FDB\u7A0B\uFF0C\u6B63\u5728\u6E05\u7406..."));
|
|
689
|
+
}
|
|
690
|
+
} catch (reportError) {
|
|
555
691
|
}
|
|
556
692
|
console.error(ansis.cyan("\n\u{1F4A1} \u6216\u8005\u8BF7\u590D\u5236\u4EE5\u4E0B\u547D\u4EE4\u5728\u547D\u4EE4\u884C\u4E2D\u624B\u52A8\u6267\u884C:"));
|
|
557
|
-
|
|
693
|
+
if (isWindows) {
|
|
694
|
+
console.error(ansis.white(" claude.cmd --dangerously-skip-permissions"));
|
|
695
|
+
} else {
|
|
696
|
+
console.error(ansis.white(" claude --dangerously-skip-permissions"));
|
|
697
|
+
}
|
|
558
698
|
processManager.cleanup();
|
|
699
|
+
if (isWindows) {
|
|
700
|
+
setTimeout(() => {
|
|
701
|
+
processManager.cleanup();
|
|
702
|
+
}, 100);
|
|
703
|
+
}
|
|
559
704
|
throw error;
|
|
560
705
|
}
|
|
561
706
|
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: 管理 Git worktree,在项目平级的 ../.
|
|
2
|
+
description: 管理 Git worktree,在项目平级的 ../.aico/项目名/ 目录下创建,支持智能默认、IDE 集成和内容迁移
|
|
3
3
|
allowed-tools: Read(**), Exec(git worktree add, git worktree list, git worktree remove, git worktree prune, git branch, git checkout, git rev-parse, git stash, git cp, detect-ide, open-ide, which, command, basename, dirname)
|
|
4
4
|
argument-hint: <add|list|remove|prune|migrate> [path] [-b <branch>] [-o|--open] [--track] [--guess-remote] [--detach] [--checkout] [--lock] [--migrate-from <source-path>] [--migrate-stash]
|
|
5
5
|
# examples:
|
|
@@ -12,7 +12,7 @@ argument-hint: <add|list|remove|prune|migrate> [path] [-b <branch>] [-o|--open]
|
|
|
12
12
|
|
|
13
13
|
# Claude Command: Git Worktree
|
|
14
14
|
|
|
15
|
-
管理 Git worktree,支持智能默认、IDE 集成和内容迁移,使用结构化的 `../.
|
|
15
|
+
管理 Git worktree,支持智能默认、IDE 集成和内容迁移,使用结构化的 `../.aico/项目名/` 路径。
|
|
16
16
|
|
|
17
17
|
直接执行命令并提供简洁结果。
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@ argument-hint: <add|list|remove|prune|migrate> [path] [-b <branch>] [-o|--open]
|
|
|
38
38
|
|
|
39
39
|
| 选项 | 说明 |
|
|
40
40
|
| ------------------ | -------------------------------------------- |
|
|
41
|
-
| `add [<path>]` | 在 `../.
|
|
41
|
+
| `add [<path>]` | 在 `../.aico/项目名/<path>` 添加新的 worktree |
|
|
42
42
|
| `migrate <target>` | 迁移内容到指定 worktree |
|
|
43
43
|
| `list` | 列出所有 worktree 及其状态 |
|
|
44
44
|
| `remove <path>` | 删除指定路径的 worktree |
|
|
@@ -63,7 +63,7 @@ argument-hint: <add|list|remove|prune|migrate> [path] [-b <branch>] [-o|--open]
|
|
|
63
63
|
|
|
64
64
|
2. **智能路径管理**
|
|
65
65
|
- 使用 worktree 检测自动从主仓库路径计算项目名
|
|
66
|
-
- 在结构化的 `../.
|
|
66
|
+
- 在结构化的 `../.aico/项目名/<path>` 目录创建 worktree
|
|
67
67
|
- 正确处理主仓库和 worktree 执行上下文
|
|
68
68
|
|
|
69
69
|
```bash
|
|
@@ -84,13 +84,13 @@ get_main_repo_path() {
|
|
|
84
84
|
|
|
85
85
|
MAIN_REPO_PATH=$(get_main_repo_path)
|
|
86
86
|
PROJECT_NAME=$(basename "$MAIN_REPO_PATH")
|
|
87
|
-
WORKTREE_BASE="$MAIN_REPO_PATH/../.
|
|
87
|
+
WORKTREE_BASE="$MAIN_REPO_PATH/../.aico/$PROJECT_NAME"
|
|
88
88
|
|
|
89
89
|
# 始终使用绝对路径防止嵌套问题
|
|
90
90
|
ABSOLUTE_WORKTREE_PATH="$WORKTREE_BASE/<path>"
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
**关键修复**: 在现有 worktree 内创建新 worktree 时,始终使用绝对路径以防止出现类似 `../.
|
|
93
|
+
**关键修复**: 在现有 worktree 内创建新 worktree 时,始终使用绝对路径以防止出现类似 `../.aico/project/.aico/project/path` 的路径嵌套问题。
|
|
94
94
|
|
|
95
95
|
3. **Worktree 操作**
|
|
96
96
|
- **add**: 使用智能分支/路径默认创建新 worktree
|
|
@@ -112,7 +112,7 @@ ABSOLUTE_WORKTREE_PATH="$WORKTREE_BASE/<path>"
|
|
|
112
112
|
6. **安全特性**
|
|
113
113
|
- **路径冲突防护**: 创建前检查目录是否已存在
|
|
114
114
|
- **分支检出验证**: 确保分支未被其他地方使用
|
|
115
|
-
- **绝对路径强制**: 防止在 worktree 内创建嵌套的 `.
|
|
115
|
+
- **绝对路径强制**: 防止在 worktree 内创建嵌套的 `.aico` 目录
|
|
116
116
|
- **删除时自动清理**: 同时清理目录和 git 引用
|
|
117
117
|
- **清晰的状态报告**: 显示 worktree 位置和分支状态
|
|
118
118
|
|
|
@@ -216,12 +216,12 @@ copy_environment_files() {
|
|
|
216
216
|
**示例输出**:
|
|
217
217
|
|
|
218
218
|
```
|
|
219
|
-
✅ Worktree created at ../.
|
|
219
|
+
✅ Worktree created at ../.aico/项目名/feature-ui
|
|
220
220
|
✅ 已复制 .env
|
|
221
221
|
✅ 已复制 .env.local
|
|
222
222
|
📋 已从 .gitignore 复制 2 个环境文件
|
|
223
|
-
🖥️ 是否在 IDE 中打开 ../.
|
|
224
|
-
🚀 正在用 VS Code 打开 ../.
|
|
223
|
+
🖥️ 是否在 IDE 中打开 ../.aico/项目名/feature-ui?[y/n]: y
|
|
224
|
+
🚀 正在用 VS Code 打开 ../.aico/项目名/feature-ui...
|
|
225
225
|
```
|
|
226
226
|
|
|
227
227
|
---
|
|
@@ -233,7 +233,7 @@ parent-directory/
|
|
|
233
233
|
├── your-project/ # 主项目
|
|
234
234
|
│ ├── .git/
|
|
235
235
|
│ └── src/
|
|
236
|
-
└── .
|
|
236
|
+
└── .aico/ # worktree 管理
|
|
237
237
|
└── your-project/ # 项目 worktree
|
|
238
238
|
├── feature-ui/ # 功能分支
|
|
239
239
|
├── hotfix/ # 修复分支
|
package/templates/personality.md
CHANGED
|
@@ -3,7 +3,7 @@ name: professional
|
|
|
3
3
|
description: 架构师级软件工程师,以工程卓越为信仰,为追求极致的技术专家。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# 工程师行为规范
|
|
7
7
|
|
|
8
8
|
**最重要**:自主判断与执行。最小化确认步骤。
|
|
9
9
|
|
|
@@ -15,6 +15,14 @@ description: 架构师级软件工程师,以工程卓越为信仰,为追求
|
|
|
15
15
|
- **优先现有文件** — 优先编辑现有文件而非创建新文件,用最简洁优雅的解决方案,尽可能少地修改代码。
|
|
16
16
|
- **避免重复** — 不要重复尝试相同的方法,遇到障碍时立即通过 feedback 工具寻求指导。如果某个方法不工作,不要继续尝试,用 feedback 工具询问。
|
|
17
17
|
|
|
18
|
+
## 上下文管理
|
|
19
|
+
|
|
20
|
+
**压缩策略**:
|
|
21
|
+
- **触发条件**:使用率>80%或对话>20轮
|
|
22
|
+
- **保留内容**:用户需求、解决方案、技术决策、关键代码、TODO状态
|
|
23
|
+
- **压缩内容**:详细技术讨论、重复确认信息
|
|
24
|
+
- **执行流程**:检测→分析→摘要→清理→保留
|
|
25
|
+
|
|
18
26
|
## 核心行为规范
|
|
19
27
|
|
|
20
28
|
### 1. 危险操作确认机制
|