@yeaft/webchat-agent 0.0.164 → 0.0.166
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/connection.js +5 -1
- package/crew.js +86 -14
- package/history.js +11 -0
- package/package.json +1 -1
package/connection.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
createCrewSession, handleCrewHumanInput, handleCrewControl,
|
|
25
25
|
addRoleToSession, removeRoleFromSession,
|
|
26
|
-
handleListCrewSessions, resumeCrewSession, removeFromCrewIndex
|
|
26
|
+
handleListCrewSessions, handleCheckCrewExists, resumeCrewSession, removeFromCrewIndex
|
|
27
27
|
} from './crew.js';
|
|
28
28
|
|
|
29
29
|
// 需要在断连期间缓冲的消息类型(Claude 输出相关的关键消息)
|
|
@@ -299,6 +299,10 @@ async function handleMessage(msg) {
|
|
|
299
299
|
await handleListCrewSessions(msg);
|
|
300
300
|
break;
|
|
301
301
|
|
|
302
|
+
case 'check_crew_exists':
|
|
303
|
+
await handleCheckCrewExists(msg);
|
|
304
|
+
break;
|
|
305
|
+
|
|
302
306
|
case 'resume_crew_session':
|
|
303
307
|
await resumeCrewSession(msg);
|
|
304
308
|
break;
|
package/crew.js
CHANGED
|
@@ -383,6 +383,63 @@ export async function handleListCrewSessions(msg) {
|
|
|
383
383
|
});
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
+
/**
|
|
387
|
+
* 检查工作目录下是否存在 .crew 目录
|
|
388
|
+
*/
|
|
389
|
+
export async function handleCheckCrewExists(msg) {
|
|
390
|
+
const { projectDir, requestId, _requestClientId } = msg;
|
|
391
|
+
if (!projectDir) {
|
|
392
|
+
ctx.sendToServer({
|
|
393
|
+
type: 'crew_exists_result',
|
|
394
|
+
requestId,
|
|
395
|
+
_requestClientId,
|
|
396
|
+
exists: false,
|
|
397
|
+
error: 'projectDir is required'
|
|
398
|
+
});
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const crewDir = join(projectDir, '.crew');
|
|
403
|
+
try {
|
|
404
|
+
const stat = await fs.stat(crewDir);
|
|
405
|
+
if (stat.isDirectory()) {
|
|
406
|
+
// 尝试读取 crew-session.json 获取 session 信息
|
|
407
|
+
let sessionInfo = null;
|
|
408
|
+
try {
|
|
409
|
+
const sessionPath = join(crewDir, 'crew-session.json');
|
|
410
|
+
const data = await fs.readFile(sessionPath, 'utf-8');
|
|
411
|
+
sessionInfo = JSON.parse(data);
|
|
412
|
+
} catch {
|
|
413
|
+
// crew-session.json 可能不存在,不影响
|
|
414
|
+
}
|
|
415
|
+
ctx.sendToServer({
|
|
416
|
+
type: 'crew_exists_result',
|
|
417
|
+
requestId,
|
|
418
|
+
_requestClientId,
|
|
419
|
+
exists: true,
|
|
420
|
+
projectDir,
|
|
421
|
+
sessionInfo
|
|
422
|
+
});
|
|
423
|
+
} else {
|
|
424
|
+
ctx.sendToServer({
|
|
425
|
+
type: 'crew_exists_result',
|
|
426
|
+
requestId,
|
|
427
|
+
_requestClientId,
|
|
428
|
+
exists: false,
|
|
429
|
+
projectDir
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
} catch {
|
|
433
|
+
ctx.sendToServer({
|
|
434
|
+
type: 'crew_exists_result',
|
|
435
|
+
requestId,
|
|
436
|
+
_requestClientId,
|
|
437
|
+
exists: false,
|
|
438
|
+
projectDir
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
386
443
|
/**
|
|
387
444
|
* 恢复已停止的 crew session
|
|
388
445
|
*/
|
|
@@ -585,6 +642,20 @@ export async function createCrewSession(msg) {
|
|
|
585
642
|
}
|
|
586
643
|
}
|
|
587
644
|
|
|
645
|
+
// 生成组名
|
|
646
|
+
const groupNames = {};
|
|
647
|
+
const maxGroup = Math.max(0, ...roles.map(r => r.groupIndex || 0));
|
|
648
|
+
for (let g = 1; g <= maxGroup; g++) {
|
|
649
|
+
const members = roles.filter(r => r.groupIndex === g);
|
|
650
|
+
const hasRev = members.some(r => r.roleType === 'reviewer');
|
|
651
|
+
const hasTest = members.some(r => r.roleType === 'tester');
|
|
652
|
+
if (hasRev && hasTest) {
|
|
653
|
+
groupNames[g] = maxGroup > 1 ? `全栈开发组 ${g}` : '全栈开发组';
|
|
654
|
+
} else {
|
|
655
|
+
groupNames[g] = maxGroup > 1 ? `开发组 ${g}` : '开发组';
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
588
659
|
// 找到决策者
|
|
589
660
|
const decisionMaker = roles.find(r => r.isDecisionMaker)?.name || roles[0]?.name || null;
|
|
590
661
|
|
|
@@ -610,6 +681,7 @@ export async function createCrewSession(msg) {
|
|
|
610
681
|
waitingHumanContext: null, // { fromRole, reason, message }
|
|
611
682
|
pendingRoutes: [], // [{ fromRole, route }] — 暂停时未完成的路由
|
|
612
683
|
features: new Map(), // taskId → { taskId, taskTitle, createdAt } — 持久化 feature 列表
|
|
684
|
+
groupNames, // groupIndex → 组名
|
|
613
685
|
userId,
|
|
614
686
|
username,
|
|
615
687
|
agentId: ctx.CONFIG?.agentName || null,
|
|
@@ -639,6 +711,7 @@ export async function createCrewSession(msg) {
|
|
|
639
711
|
})),
|
|
640
712
|
decisionMaker,
|
|
641
713
|
maxRounds,
|
|
714
|
+
groupNames,
|
|
642
715
|
userId,
|
|
643
716
|
username
|
|
644
717
|
});
|
|
@@ -889,18 +962,17 @@ ${roles.length > 0 ? roles.map(r => `- ${roleLabel(r)}(${r.name}): ${r.descripti
|
|
|
889
962
|
2. 等待其他角色的产出但迟迟没有收到
|
|
890
963
|
3. 任务描述不清楚或有歧义,无法判断正确做法
|
|
891
964
|
4. 遇到超出自己职责范围的问题
|
|
892
|
-
5. 连续尝试
|
|
893
|
-
上报时请说明:你在做什么任务、卡在哪里、你认为需要谁来协助。
|
|
965
|
+
5. 连续尝试 2 次相同操作仍然失败
|
|
966
|
+
上报时请说明:你在做什么任务、卡在哪里、你认为需要谁来协助。PM 会统筹全局,判断是分配给合适的人还是调整任务顺序。
|
|
894
967
|
|
|
895
968
|
# Worktree 隔离规则
|
|
896
|
-
-
|
|
897
|
-
-
|
|
898
|
-
-
|
|
969
|
+
- dev/reviewer/tester 角色必须在各自分配的 worktree 中工作,绝对禁止在项目主目录或 main 分支上修改代码
|
|
970
|
+
- 每个角色的 CLAUDE.md 会标明「代码工作目录」,该路径就是你的 worktree,所有文件操作必须使用该路径
|
|
971
|
+
- PM 和 designer 不使用 worktree,他们在项目主目录下以只读方式工作
|
|
972
|
+
- 绝对禁止在其他开发组的 worktree 中操作代码
|
|
899
973
|
- 代码完成并通过 review 后,dev 自己提 PR 合并到 main 分支
|
|
900
974
|
- PM 不做 cherry-pick,只负责打 tag
|
|
901
|
-
- 合并完成后清理旧的 worktree
|
|
902
975
|
- 每次新任务/新 feature 必须基于最新的 main 分支创建新的 worktree,确保在最新代码上开发
|
|
903
|
-
- 禁止复用旧的 worktree 开发新任务,因为旧 worktree 的代码基线可能已过时
|
|
904
976
|
|
|
905
977
|
${sharedMemoryContent}`;
|
|
906
978
|
|
|
@@ -921,12 +993,9 @@ ${role.claudeMd || role.description}
|
|
|
921
993
|
// 有独立 worktree 的角色,覆盖代码工作目录
|
|
922
994
|
if (role.workDir) {
|
|
923
995
|
claudeMd += `
|
|
924
|
-
#
|
|
996
|
+
# 代码工作目录
|
|
925
997
|
${role.workDir}
|
|
926
|
-
|
|
927
|
-
绝对禁止直接操作项目主目录或其他组的 worktree,否则会覆盖其他开发组的修改。
|
|
928
|
-
代码完成并通过 review 后,自己提 PR 合并到 main。
|
|
929
|
-
此 worktree 仅用于当前任务,合并后会被清理,新任务会创建新的 worktree。
|
|
998
|
+
所有代码操作请使用此路径。不要使用项目主目录。
|
|
930
999
|
`;
|
|
931
1000
|
}
|
|
932
1001
|
|
|
@@ -2265,7 +2334,9 @@ function sendStatusUpdate(session) {
|
|
|
2265
2334
|
icon: r.icon,
|
|
2266
2335
|
description: r.description,
|
|
2267
2336
|
isDecisionMaker: r.isDecisionMaker || false,
|
|
2268
|
-
model: r.model
|
|
2337
|
+
model: r.model,
|
|
2338
|
+
roleType: r.roleType,
|
|
2339
|
+
groupIndex: r.groupIndex
|
|
2269
2340
|
})),
|
|
2270
2341
|
activeRoles: Array.from(session.roleStates.entries())
|
|
2271
2342
|
.filter(([, s]) => s.turnActive)
|
|
@@ -2275,7 +2346,8 @@ function sendStatusUpdate(session) {
|
|
|
2275
2346
|
.filter(([, s]) => s.turnActive && s.currentTool)
|
|
2276
2347
|
.map(([name, s]) => [name, s.currentTool])
|
|
2277
2348
|
),
|
|
2278
|
-
features: Array.from(session.features.values())
|
|
2349
|
+
features: Array.from(session.features.values()),
|
|
2350
|
+
groupNames: session.groupNames || {}
|
|
2279
2351
|
});
|
|
2280
2352
|
|
|
2281
2353
|
// 异步更新持久化
|
package/history.js
CHANGED
|
@@ -63,6 +63,11 @@ export function getWorkDirFromProjectFolder(projectFolderPath, folderName) {
|
|
|
63
63
|
|
|
64
64
|
// 获取指定目录的历史会话列表
|
|
65
65
|
export async function getHistorySessions(workDir) {
|
|
66
|
+
// 过滤掉 crew 角色目录的 sessions
|
|
67
|
+
if (workDir && /[/\\]\.crew[/\\]roles[/\\]/.test(workDir)) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
const projectsDir = getClaudeProjectsDir();
|
|
67
72
|
const projectFolder = pathToProjectFolder(workDir);
|
|
68
73
|
const projectPath = join(projectsDir, projectFolder);
|
|
@@ -224,6 +229,12 @@ export async function handleListFolders(msg) {
|
|
|
224
229
|
const stats = statSync(entryPath);
|
|
225
230
|
|
|
226
231
|
if (stats.isDirectory()) {
|
|
232
|
+
// 过滤掉 crew 角色的 session 文件夹
|
|
233
|
+
// crew 角色的 cwd 在 .crew/roles/{roleName} 下,对应的文件夹名包含 --crew-roles-
|
|
234
|
+
if (entry.includes('--crew-roles-')) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
|
|
227
238
|
// 从 session 文件读取真实的工作目录路径
|
|
228
239
|
const originalPath = getWorkDirFromProjectFolder(entryPath, entry);
|
|
229
240
|
|