@zmice/zc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -0
- package/dist/adapters/codex.d.ts +13 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +61 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +8 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/qwen-code.d.ts +13 -0
- package/dist/adapters/qwen-code.d.ts.map +1 -0
- package/dist/adapters/qwen-code.js +60 -0
- package/dist/adapters/qwen-code.js.map +1 -0
- package/dist/adapters/types.d.ts +29 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +11 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +98 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/msg.d.ts +3 -0
- package/dist/cli/msg.d.ts.map +1 -0
- package/dist/cli/msg.js +20 -0
- package/dist/cli/msg.js.map +1 -0
- package/dist/cli/run.d.ts +17 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +70 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +41 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/task.d.ts +3 -0
- package/dist/cli/task.d.ts.map +1 -0
- package/dist/cli/task.js +20 -0
- package/dist/cli/task.js.map +1 -0
- package/dist/cli/team.d.ts +3 -0
- package/dist/cli/team.d.ts.map +1 -0
- package/dist/cli/team.js +169 -0
- package/dist/cli/team.js.map +1 -0
- package/dist/runtime/logger.d.ts +2 -0
- package/dist/runtime/logger.d.ts.map +1 -0
- package/dist/runtime/logger.js +2 -0
- package/dist/runtime/logger.js.map +1 -0
- package/dist/runtime/session-manager.d.ts +39 -0
- package/dist/runtime/session-manager.d.ts.map +1 -0
- package/dist/runtime/session-manager.js +107 -0
- package/dist/runtime/session-manager.js.map +1 -0
- package/dist/runtime/state.d.ts +9 -0
- package/dist/runtime/state.d.ts.map +1 -0
- package/dist/runtime/state.js +22 -0
- package/dist/runtime/state.js.map +1 -0
- package/dist/runtime/worktree-manager.d.ts +39 -0
- package/dist/runtime/worktree-manager.d.ts.map +1 -0
- package/dist/runtime/worktree-manager.js +123 -0
- package/dist/runtime/worktree-manager.js.map +1 -0
- package/dist/team/mailbox.d.ts +22 -0
- package/dist/team/mailbox.d.ts.map +1 -0
- package/dist/team/mailbox.js +52 -0
- package/dist/team/mailbox.js.map +1 -0
- package/dist/team/orchestrator.d.ts +68 -0
- package/dist/team/orchestrator.d.ts.map +1 -0
- package/dist/team/orchestrator.js +248 -0
- package/dist/team/orchestrator.js.map +1 -0
- package/dist/team/task-queue.d.ts +34 -0
- package/dist/team/task-queue.d.ts.map +1 -0
- package/dist/team/task-queue.js +97 -0
- package/dist/team/task-queue.js.map +1 -0
- package/dist/team/worker-manager.d.ts +54 -0
- package/dist/team/worker-manager.d.ts.map +1 -0
- package/dist/team/worker-manager.js +159 -0
- package/dist/team/worker-manager.js.map +1 -0
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +2 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/platform.d.ts +17 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +46 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/skill-loader.d.ts +46 -0
- package/dist/utils/skill-loader.d.ts.map +1 -0
- package/dist/utils/skill-loader.js +210 -0
- package/dist/utils/skill-loader.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { discoverSkills, discoverCommands, discoverAgents, } from "../utils/skill-loader.js";
|
|
2
|
+
export function registerSetupCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("setup")
|
|
5
|
+
.description("扫描并验证项目资产(skills/commands/agents)")
|
|
6
|
+
.option("-r, --root <dir>", "项目根目录")
|
|
7
|
+
.action(async (opts) => {
|
|
8
|
+
console.log("\n🔍 扫描项目资产...\n");
|
|
9
|
+
const [skills, commands, agents] = await Promise.all([
|
|
10
|
+
discoverSkills(opts.root),
|
|
11
|
+
discoverCommands(opts.root),
|
|
12
|
+
discoverAgents(opts.root),
|
|
13
|
+
]);
|
|
14
|
+
// Skills
|
|
15
|
+
console.log(`📦 Skills: ${skills.length} 个`);
|
|
16
|
+
for (const s of skills) {
|
|
17
|
+
console.log(` ✅ ${s.name}`);
|
|
18
|
+
}
|
|
19
|
+
// Commands
|
|
20
|
+
console.log(`\n📋 Commands: ${commands.length} 个`);
|
|
21
|
+
for (const c of commands) {
|
|
22
|
+
console.log(` ✅ ${c.name}`);
|
|
23
|
+
}
|
|
24
|
+
// Agents
|
|
25
|
+
console.log(`\n🤖 Agents: ${agents.length} 个`);
|
|
26
|
+
for (const a of agents) {
|
|
27
|
+
console.log(` ✅ ${a.name}`);
|
|
28
|
+
}
|
|
29
|
+
// Summary
|
|
30
|
+
const total = skills.length + commands.length + agents.length;
|
|
31
|
+
console.log(`\n📊 总计: ${total} 个资产`);
|
|
32
|
+
if (total === 0) {
|
|
33
|
+
console.log("\n⚠️ 未发现任何资产,请检查目录结构是否正确");
|
|
34
|
+
console.log(" 期望结构: skills/<name>/SKILL.md, commands/*.md, agents/*.md");
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
console.log("\n✅ 目录结构验证通过");
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../../src/cli/setup.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,cAAc,GACf,MAAM,0BAA0B,CAAC;AAElC,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,mCAAmC,CAAC;SAChD,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC;SACnC,MAAM,CAAC,KAAK,EAAE,IAAuB,EAAE,EAAE;QACxC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAEhC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACnD,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;YACzB,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;YAC3B,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC;SAC1B,CAAC,CAAC;QAEH,SAAS;QACT,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAChC,CAAC;QAED,WAAW;QACX,OAAO,CAAC,GAAG,CAAC,kBAAkB,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC;QACnD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAChC,CAAC;QAED,SAAS;QACT,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QAC/C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAChC,CAAC;QAED,UAAU;QACV,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,MAAM,CAAC,CAAC;QAErC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;QAC7E,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/cli/task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsB1D"}
|
package/dist/cli/task.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function registerTaskCommand(program) {
|
|
2
|
+
const task = program
|
|
3
|
+
.command("task")
|
|
4
|
+
.description("任务操作命令");
|
|
5
|
+
task.command("list").description("列出任务").action(async () => {
|
|
6
|
+
console.log("[zc task list] TODO");
|
|
7
|
+
});
|
|
8
|
+
task.command("claim").description("认领任务").argument("<id>", "任务 ID").action(async (id) => {
|
|
9
|
+
console.log(`[zc task claim] ID: ${id}`);
|
|
10
|
+
});
|
|
11
|
+
task.command("done").description("标记完成").argument("<id>", "任务 ID").action(async (id) => {
|
|
12
|
+
console.log(`[zc task done] ID: ${id}`);
|
|
13
|
+
});
|
|
14
|
+
task.command("fail").description("标记失败").argument("<id>", "任务 ID")
|
|
15
|
+
.option("-r, --reason <reason>", "失败原因")
|
|
16
|
+
.action(async (id, opts) => {
|
|
17
|
+
console.log(`[zc task fail] ID: ${id}, Reason: ${opts.reason}`);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/cli/task.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEzB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE;QACzD,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,EAAE;QAC9F,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,EAAE;QAC7F,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;SAC/D,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC;SACvC,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,IAAyB,EAAE,EAAE;QACtD,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,aAAa,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.d.ts","sourceRoot":"","sources":["../../src/cli/team.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6BzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6K1D"}
|
package/dist/cli/team.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { Orchestrator } from "../team/orchestrator.js";
|
|
2
|
+
import { SessionManager } from "../runtime/session-manager.js";
|
|
3
|
+
import { WorktreeManager } from "../runtime/worktree-manager.js";
|
|
4
|
+
import { readJson } from "../runtime/state.js";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
function getStateDir() {
|
|
7
|
+
return resolve(process.cwd(), ".zc");
|
|
8
|
+
}
|
|
9
|
+
function parseWorkers(raw) {
|
|
10
|
+
return raw.split(",").map((pair) => {
|
|
11
|
+
const [id, cli] = pair.split(":");
|
|
12
|
+
if (!id || !cli) {
|
|
13
|
+
throw new Error(`Invalid worker format "${pair}". Expected "id:cli" (e.g. "w1:codex").`);
|
|
14
|
+
}
|
|
15
|
+
return { id: id.trim(), cli: cli.trim() };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function defaultTeamName() {
|
|
19
|
+
const now = new Date();
|
|
20
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
21
|
+
return `team-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
22
|
+
}
|
|
23
|
+
export function registerTeamCommand(program) {
|
|
24
|
+
const team = program
|
|
25
|
+
.command("team")
|
|
26
|
+
.description("团队编排命令");
|
|
27
|
+
// --- start ---
|
|
28
|
+
team
|
|
29
|
+
.command("start")
|
|
30
|
+
.description("启动团队")
|
|
31
|
+
.requiredOption("-w, --workers <spec>", '工人规格 "id:cli,id:cli,..." (e.g. "w1:codex,w2:qwen-code")')
|
|
32
|
+
.option("-t, --tasks <task...>", "任务描述(可重复)", [])
|
|
33
|
+
.option("-n, --name <name>", "团队名称", defaultTeamName())
|
|
34
|
+
.option("-m, --model <model>", "模型名称")
|
|
35
|
+
.option("-s, --skills <skill...>", "手动指定 skills(全局应用到所有任务)", [])
|
|
36
|
+
.option("--skill-match <mode>", "技能匹配模式 (keyword|ai)", "keyword")
|
|
37
|
+
.action(async (opts) => {
|
|
38
|
+
if (opts.tasks.length === 0) {
|
|
39
|
+
console.error("Error: at least one --tasks is required.");
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const workers = parseWorkers(opts.workers);
|
|
44
|
+
const spec = {
|
|
45
|
+
name: opts.name,
|
|
46
|
+
workers,
|
|
47
|
+
tasks: opts.tasks,
|
|
48
|
+
model: opts.model,
|
|
49
|
+
skills: opts.skills.length > 0 ? opts.skills : undefined,
|
|
50
|
+
skillMatchMode: opts.skillMatch,
|
|
51
|
+
};
|
|
52
|
+
console.log(`Starting team "${spec.name}" with ${workers.length} worker(s) and ${spec.tasks.length} task(s)...`);
|
|
53
|
+
const session = new SessionManager();
|
|
54
|
+
const worktree = new WorktreeManager(process.cwd());
|
|
55
|
+
const orch = new Orchestrator(getStateDir(), session, worktree);
|
|
56
|
+
try {
|
|
57
|
+
await orch.startTeam(spec);
|
|
58
|
+
console.log(`Team "${spec.name}" started. Entering dispatch loop...`);
|
|
59
|
+
await orch.runDispatchLoop();
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.error("Failed to start team:", err instanceof Error ? err.message : err);
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// --- status ---
|
|
67
|
+
team
|
|
68
|
+
.command("status")
|
|
69
|
+
.description("查询团队状态")
|
|
70
|
+
.argument("[name]", "团队名称")
|
|
71
|
+
.action(async (name) => {
|
|
72
|
+
const stateDir = getStateDir();
|
|
73
|
+
if (!name) {
|
|
74
|
+
console.log("Usage: zc team status <name>");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const statePath = resolve(stateDir, name, "state.json");
|
|
79
|
+
const status = await readJson(statePath, null);
|
|
80
|
+
if (!status) {
|
|
81
|
+
console.error(`No state found for team "${name}".`);
|
|
82
|
+
process.exitCode = 1;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const s = status;
|
|
86
|
+
console.log(`\nTeam: ${s.name}`);
|
|
87
|
+
console.log("─".repeat(40));
|
|
88
|
+
// Workers table
|
|
89
|
+
console.log("\nWorkers:");
|
|
90
|
+
console.log(" ID CLI Status Task");
|
|
91
|
+
console.log(" " + "─".repeat(52));
|
|
92
|
+
for (const w of s.workers) {
|
|
93
|
+
console.log(` ${w.id.padEnd(14)}${w.cli.padEnd(13)}${w.status.padEnd(10)}${w.currentTask ?? "-"}`);
|
|
94
|
+
}
|
|
95
|
+
// Task summary
|
|
96
|
+
console.log("\nTasks:");
|
|
97
|
+
console.log(` Pending: ${s.tasks.pending} Running: ${s.tasks.running} Done: ${s.tasks.done} Failed: ${s.tasks.failed}`);
|
|
98
|
+
// Messages
|
|
99
|
+
console.log(`\nMessages: ${s.messages}`);
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.error("Failed to read team status:", err instanceof Error ? err.message : err);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// --- shutdown ---
|
|
107
|
+
team
|
|
108
|
+
.command("shutdown")
|
|
109
|
+
.description("关闭团队")
|
|
110
|
+
.argument("<name>", "团队名称")
|
|
111
|
+
.action(async (name) => {
|
|
112
|
+
console.log(`Shutting down team "${name}"...`);
|
|
113
|
+
const session = new SessionManager();
|
|
114
|
+
const worktree = new WorktreeManager(process.cwd());
|
|
115
|
+
const orch = new Orchestrator(getStateDir(), session, worktree);
|
|
116
|
+
try {
|
|
117
|
+
// Kill the tmux session directly as a fallback
|
|
118
|
+
await session.killSession(`zc-${name}`);
|
|
119
|
+
await worktree.cleanup(name);
|
|
120
|
+
console.log(`Team "${name}" shut down.`);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
console.error("Failed to shutdown team:", err instanceof Error ? err.message : err);
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
// --- log ---
|
|
128
|
+
team
|
|
129
|
+
.command("log")
|
|
130
|
+
.description("查看团队日志")
|
|
131
|
+
.argument("<name>", "团队名称")
|
|
132
|
+
.option("-w, --worker <id>", "指定 worker ID")
|
|
133
|
+
.action(async (name, opts) => {
|
|
134
|
+
const session = new SessionManager();
|
|
135
|
+
try {
|
|
136
|
+
const statePath = resolve(getStateDir(), name, "state.json");
|
|
137
|
+
const status = await readJson(statePath, null);
|
|
138
|
+
if (!status) {
|
|
139
|
+
console.error(`No state found for team "${name}".`);
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const s = status;
|
|
144
|
+
const targetWorkers = opts.worker
|
|
145
|
+
? s.workers.filter((w) => w.id === opts.worker)
|
|
146
|
+
: s.workers;
|
|
147
|
+
if (targetWorkers.length === 0) {
|
|
148
|
+
console.error(opts.worker ? `Worker "${opts.worker}" not found.` : "No workers found.");
|
|
149
|
+
process.exitCode = 1;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
for (const w of targetWorkers) {
|
|
153
|
+
console.log(`\n=== Worker: ${w.id} (pane: ${w.paneId}) ===`);
|
|
154
|
+
try {
|
|
155
|
+
const output = await session.captureOutput(w.paneId, 50);
|
|
156
|
+
console.log(output);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
console.log(" (unable to capture output — pane may be dead)");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.error("Failed to read logs:", err instanceof Error ? err.message : err);
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=team.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../src/cli/team.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/runtime/logger.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/runtime/logger.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare class SessionManager {
|
|
2
|
+
/**
|
|
3
|
+
* 创建一个新的 tmux session
|
|
4
|
+
* @returns session name
|
|
5
|
+
*/
|
|
6
|
+
createSession(name: string): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* 在 session 中创建新的 window/pane 并执行命令
|
|
9
|
+
* @returns pane identifier (session:window.pane)
|
|
10
|
+
*/
|
|
11
|
+
createPane(sessionName: string, command: string): Promise<string>;
|
|
12
|
+
/**
|
|
13
|
+
* 向指定 pane 发送按键/命令
|
|
14
|
+
*/
|
|
15
|
+
sendKeys(paneId: string, keys: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* 捕获指定 pane 的当前输出
|
|
18
|
+
*/
|
|
19
|
+
captureOutput(paneId: string, lines?: number): Promise<string>;
|
|
20
|
+
/**
|
|
21
|
+
* 关闭指定 pane/window
|
|
22
|
+
*/
|
|
23
|
+
killPane(paneId: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* 关闭整个 session
|
|
26
|
+
*/
|
|
27
|
+
killSession(sessionName: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* 列出所有活跃的 zc sessions
|
|
30
|
+
*/
|
|
31
|
+
listSessions(): Promise<string[]>;
|
|
32
|
+
/**
|
|
33
|
+
* 检查 tmux 是否可用
|
|
34
|
+
*/
|
|
35
|
+
isAvailable(): Promise<boolean>;
|
|
36
|
+
private getWindowCount;
|
|
37
|
+
private sanitize;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/runtime/session-manager.ts"],"names":[],"mappings":"AAKA,qBAAa,cAAc;IACzB;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD;;;OAGG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAYvE;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM3D;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOjE;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C;;OAEG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAYvC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;YASvB,cAAc;IAY5B,OAAO,CAAC,QAAQ;CAIjB"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
export class SessionManager {
|
|
5
|
+
/**
|
|
6
|
+
* 创建一个新的 tmux session
|
|
7
|
+
* @returns session name
|
|
8
|
+
*/
|
|
9
|
+
async createSession(name) {
|
|
10
|
+
await execAsync(`tmux new-session -d -s ${this.sanitize(name)}`);
|
|
11
|
+
return name;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 在 session 中创建新的 window/pane 并执行命令
|
|
15
|
+
* @returns pane identifier (session:window.pane)
|
|
16
|
+
*/
|
|
17
|
+
async createPane(sessionName, command) {
|
|
18
|
+
const safe = this.sanitize(sessionName);
|
|
19
|
+
// Create a new window in the session
|
|
20
|
+
const windowIndex = await this.getWindowCount(safe);
|
|
21
|
+
await execAsync(`tmux new-window -t ${safe} -n worker-${windowIndex}`);
|
|
22
|
+
const paneId = `${safe}:${windowIndex}`;
|
|
23
|
+
if (command) {
|
|
24
|
+
await this.sendKeys(paneId, command);
|
|
25
|
+
}
|
|
26
|
+
return paneId;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 向指定 pane 发送按键/命令
|
|
30
|
+
*/
|
|
31
|
+
async sendKeys(paneId, keys) {
|
|
32
|
+
// Escape single quotes in keys
|
|
33
|
+
const escaped = keys.replace(/'/g, "'\\''");
|
|
34
|
+
await execAsync(`tmux send-keys -t '${paneId}' '${escaped}' Enter`);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 捕获指定 pane 的当前输出
|
|
38
|
+
*/
|
|
39
|
+
async captureOutput(paneId, lines = 100) {
|
|
40
|
+
const { stdout } = await execAsync(`tmux capture-pane -t '${paneId}' -p -S -${lines}`);
|
|
41
|
+
return stdout;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 关闭指定 pane/window
|
|
45
|
+
*/
|
|
46
|
+
async killPane(paneId) {
|
|
47
|
+
try {
|
|
48
|
+
await execAsync(`tmux kill-window -t '${paneId}'`);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// pane may already be dead
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 关闭整个 session
|
|
56
|
+
*/
|
|
57
|
+
async killSession(sessionName) {
|
|
58
|
+
try {
|
|
59
|
+
await execAsync(`tmux kill-session -t ${this.sanitize(sessionName)}`);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// session may already be dead
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 列出所有活跃的 zc sessions
|
|
67
|
+
*/
|
|
68
|
+
async listSessions() {
|
|
69
|
+
try {
|
|
70
|
+
const { stdout } = await execAsync("tmux list-sessions -F '#{session_name}'");
|
|
71
|
+
return stdout
|
|
72
|
+
.trim()
|
|
73
|
+
.split("\n")
|
|
74
|
+
.filter((s) => s.startsWith("zc-"));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 检查 tmux 是否可用
|
|
82
|
+
*/
|
|
83
|
+
async isAvailable() {
|
|
84
|
+
try {
|
|
85
|
+
await execAsync("tmux -V");
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async getWindowCount(sessionName) {
|
|
93
|
+
try {
|
|
94
|
+
const { stdout } = await execAsync(`tmux list-windows -t ${sessionName} -F '#{window_index}'`);
|
|
95
|
+
const windows = stdout.trim().split("\n").filter(Boolean);
|
|
96
|
+
return windows.length;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
sanitize(name) {
|
|
103
|
+
// tmux session names: alphanumeric, dash, underscore
|
|
104
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/runtime/session-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,OAAO,cAAc;IACzB;;;OAGG;IACH,KAAK,CAAC,aAAa,CAAC,IAAY;QAC9B,MAAM,SAAS,CAAC,0BAA0B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,WAAmB,EAAE,OAAe;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxC,qCAAqC;QACrC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,SAAS,CAAC,sBAAsB,IAAI,cAAc,WAAW,EAAE,CAAC,CAAC;QACvE,MAAM,MAAM,GAAG,GAAG,IAAI,IAAI,WAAW,EAAE,CAAC;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,IAAY;QACzC,+BAA+B;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,SAAS,CAAC,sBAAsB,MAAM,MAAM,OAAO,SAAS,CAAC,CAAC;IACtE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,KAAK,GAAG,GAAG;QAC7C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,yBAAyB,MAAM,YAAY,KAAK,EAAE,CACnD,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CAAC,MAAc;QAC3B,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,wBAAwB,MAAM,GAAG,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,WAAmB;QACnC,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,wBAAwB,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,yCAAyC,CAAC,CAAC;YAC9E,OAAO,MAAM;iBACV,IAAI,EAAE;iBACN,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,WAAmB;QAC9C,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,wBAAwB,WAAW,uBAAuB,CAC3D,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1D,OAAO,OAAO,CAAC,MAAM,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IAEO,QAAQ,CAAC,IAAY;QAC3B,qDAAqD;QACrD,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;IAC9C,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 读取 JSON 文件,不存在时返回默认值
|
|
3
|
+
*/
|
|
4
|
+
export declare function readJson<T>(filePath: string, defaultValue: T): Promise<T>;
|
|
5
|
+
/**
|
|
6
|
+
* 写入 JSON 文件,自动创建目录
|
|
7
|
+
*/
|
|
8
|
+
export declare function writeJson<T>(filePath: string, data: T): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/runtime/state.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO/E;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAG3E"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* 读取 JSON 文件,不存在时返回默认值
|
|
5
|
+
*/
|
|
6
|
+
export async function readJson(filePath, defaultValue) {
|
|
7
|
+
try {
|
|
8
|
+
const content = await readFile(filePath, "utf-8");
|
|
9
|
+
return JSON.parse(content);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return defaultValue;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 写入 JSON 文件,自动创建目录
|
|
17
|
+
*/
|
|
18
|
+
export async function writeJson(filePath, data) {
|
|
19
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
20
|
+
await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/runtime/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAI,QAAgB,EAAE,YAAe;IACjE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAM,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,YAAY,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAI,QAAgB,EAAE,IAAO;IAC1D,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface WorktreeInfo {
|
|
2
|
+
path: string;
|
|
3
|
+
branch: string;
|
|
4
|
+
head: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class WorktreeManager {
|
|
7
|
+
private readonly repoRoot;
|
|
8
|
+
constructor(repoRoot: string);
|
|
9
|
+
/**
|
|
10
|
+
* 创建一个新的 git worktree + 分支
|
|
11
|
+
* @returns worktree 绝对路径
|
|
12
|
+
*/
|
|
13
|
+
create(workerName: string, teamName: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* 移除一个 worktree
|
|
16
|
+
*/
|
|
17
|
+
remove(worktreePath: string): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* 列出所有 worktree
|
|
20
|
+
*/
|
|
21
|
+
list(): Promise<WorktreeInfo[]>;
|
|
22
|
+
/**
|
|
23
|
+
* 将 worktree 分支合并到目标分支
|
|
24
|
+
*/
|
|
25
|
+
mergeTo(worktreePath: string, targetBranch: string): Promise<{
|
|
26
|
+
success: boolean;
|
|
27
|
+
message: string;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* 清理团队的所有 worktree 和分支
|
|
31
|
+
*/
|
|
32
|
+
cleanup(teamName: string): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* 剪枝已失效的 worktree 引用
|
|
35
|
+
*/
|
|
36
|
+
prune(): Promise<void>;
|
|
37
|
+
private getRepoName;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=worktree-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worktree-manager.d.ts","sourceRoot":"","sources":["../../src/runtime/worktree-manager.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,MAAM;IAE7C;;;OAGG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBnE;;OAEG;IACG,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAuBrC;;OAEG;IACG,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAmBzG;;OAEG;IACG,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B9C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,CAAC,WAAW;CAGpB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { resolve, dirname } from "node:path";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
/**
|
|
6
|
+
* Validate that a name contains only safe characters to prevent injection.
|
|
7
|
+
*/
|
|
8
|
+
function validateName(name, label) {
|
|
9
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
|
|
10
|
+
throw new Error(`Invalid ${label}: "${name}". Only alphanumeric, underscore and hyphen allowed.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class WorktreeManager {
|
|
14
|
+
repoRoot;
|
|
15
|
+
constructor(repoRoot) {
|
|
16
|
+
this.repoRoot = repoRoot;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 创建一个新的 git worktree + 分支
|
|
20
|
+
* @returns worktree 绝对路径
|
|
21
|
+
*/
|
|
22
|
+
async create(workerName, teamName) {
|
|
23
|
+
validateName(teamName, "team name");
|
|
24
|
+
validateName(workerName, "worker name");
|
|
25
|
+
const branch = `zc/${teamName}/${workerName}`;
|
|
26
|
+
const parentDir = dirname(this.repoRoot);
|
|
27
|
+
const worktreePath = resolve(parentDir, `${this.getRepoName()}-zc-${workerName}`);
|
|
28
|
+
// Get current branch as base
|
|
29
|
+
const { stdout: currentBranch } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: this.repoRoot });
|
|
30
|
+
// Create worktree with new branch
|
|
31
|
+
await execFileAsync("git", ["worktree", "add", "-b", branch, worktreePath, currentBranch.trim()], { cwd: this.repoRoot });
|
|
32
|
+
return worktreePath;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 移除一个 worktree
|
|
36
|
+
*/
|
|
37
|
+
async remove(worktreePath) {
|
|
38
|
+
await execFileAsync("git", ["worktree", "remove", worktreePath, "--force"], {
|
|
39
|
+
cwd: this.repoRoot,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 列出所有 worktree
|
|
44
|
+
*/
|
|
45
|
+
async list() {
|
|
46
|
+
const { stdout } = await execFileAsync("git", ["worktree", "list", "--porcelain"], {
|
|
47
|
+
cwd: this.repoRoot,
|
|
48
|
+
});
|
|
49
|
+
const worktrees = [];
|
|
50
|
+
let current = {};
|
|
51
|
+
for (const line of stdout.split("\n")) {
|
|
52
|
+
if (line.startsWith("worktree ")) {
|
|
53
|
+
if (current.path)
|
|
54
|
+
worktrees.push(current);
|
|
55
|
+
current = { path: line.slice(9) };
|
|
56
|
+
}
|
|
57
|
+
else if (line.startsWith("HEAD ")) {
|
|
58
|
+
current.head = line.slice(5);
|
|
59
|
+
}
|
|
60
|
+
else if (line.startsWith("branch ")) {
|
|
61
|
+
current.branch = line.slice(7).replace("refs/heads/", "");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (current.path)
|
|
65
|
+
worktrees.push(current);
|
|
66
|
+
return worktrees;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 将 worktree 分支合并到目标分支
|
|
70
|
+
*/
|
|
71
|
+
async mergeTo(worktreePath, targetBranch) {
|
|
72
|
+
try {
|
|
73
|
+
// Get the branch name of the worktree
|
|
74
|
+
const { stdout: branch } = await execFileAsync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd: worktreePath });
|
|
75
|
+
// Switch to target branch in main repo and merge
|
|
76
|
+
await execFileAsync("git", ["checkout", targetBranch], { cwd: this.repoRoot });
|
|
77
|
+
await execFileAsync("git", ["merge", branch.trim(), "--no-edit"], { cwd: this.repoRoot });
|
|
78
|
+
return { success: true, message: `Merged ${branch.trim()} into ${targetBranch}` };
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
82
|
+
return { success: false, message };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 清理团队的所有 worktree 和分支
|
|
87
|
+
*/
|
|
88
|
+
async cleanup(teamName) {
|
|
89
|
+
validateName(teamName, "team name");
|
|
90
|
+
const worktrees = await this.list();
|
|
91
|
+
const teamWorktrees = worktrees.filter((wt) => wt.branch?.includes(`zc/${teamName}/`));
|
|
92
|
+
for (const wt of teamWorktrees) {
|
|
93
|
+
try {
|
|
94
|
+
await this.remove(wt.path);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// continue cleanup even if one fails
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Clean up branches
|
|
101
|
+
try {
|
|
102
|
+
const { stdout } = await execFileAsync("git", ["branch", "--list", `zc/${teamName}/*`], { cwd: this.repoRoot });
|
|
103
|
+
const branches = stdout.trim().split("\n").filter(Boolean).map((b) => b.trim());
|
|
104
|
+
for (const branch of branches) {
|
|
105
|
+
try {
|
|
106
|
+
await execFileAsync("git", ["branch", "-D", branch], { cwd: this.repoRoot });
|
|
107
|
+
}
|
|
108
|
+
catch { /* ignore */ }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch { /* ignore */ }
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 剪枝已失效的 worktree 引用
|
|
115
|
+
*/
|
|
116
|
+
async prune() {
|
|
117
|
+
await execFileAsync("git", ["worktree", "prune"], { cwd: this.repoRoot });
|
|
118
|
+
}
|
|
119
|
+
getRepoName() {
|
|
120
|
+
return this.repoRoot.split(/[/\\]/).pop() ?? "repo";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=worktree-manager.js.map
|