@yeaft/webchat-agent 0.0.177 → 0.0.179
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/crew.js +115 -83
- package/package.json +1 -1
package/crew.js
CHANGED
|
@@ -985,49 +985,12 @@ ${roles.length > 0 ? roles.map(r => `- ${roleLabel(r)}(${r.name}): ${r.descripti
|
|
|
985
985
|
- PM 不做 cherry-pick,只负责打 tag
|
|
986
986
|
- 每次新任务/新 feature 必须基于最新的 main 分支创建新的 worktree,确保在最新代码上开发
|
|
987
987
|
|
|
988
|
-
# Feature
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
## 文件格式
|
|
996
|
-
\`\`\`markdown
|
|
997
|
-
# Feature: {taskTitle}
|
|
998
|
-
- task-id: {task-id}
|
|
999
|
-
- 状态: 待开发 | 开发中 | 待审查 | 审查中 | 已完成 | 已阻塞
|
|
1000
|
-
- 负责人: {dev角色name}
|
|
1001
|
-
- 审查者: {reviewer角色name}
|
|
1002
|
-
- 测试者: {tester角色name}
|
|
1003
|
-
- 创建时间: {ISO时间}
|
|
1004
|
-
|
|
1005
|
-
## 需求描述
|
|
1006
|
-
{PM写的需求}
|
|
1007
|
-
|
|
1008
|
-
## 实现记录
|
|
1009
|
-
_由开发者填写_
|
|
1010
|
-
|
|
1011
|
-
## 审查记录
|
|
1012
|
-
_由审查者填写_
|
|
1013
|
-
|
|
1014
|
-
## 测试记录
|
|
1015
|
-
_由测试者填写_
|
|
1016
|
-
\`\`\`
|
|
1017
|
-
|
|
1018
|
-
## 状态流转规则
|
|
1019
|
-
1. PM 创建文件 → 状态「待开发」
|
|
1020
|
-
2. dev 开始工作 → 更新为「开发中」,填写实现方案
|
|
1021
|
-
3. dev 完成 → 更新为「待审查」,记录改动摘要(reviewer 和 tester 并行工作)
|
|
1022
|
-
4. reviewer/tester 开始工作 → 更新为「审查中」,各自填写审查/测试记录
|
|
1023
|
-
5. 发现问题需返工 → 更新回「开发中」
|
|
1024
|
-
6. 全部通过 → PM 更新为「已完成」
|
|
1025
|
-
|
|
1026
|
-
## 角色职责
|
|
1027
|
-
- **PM**: 规划 feature 文件(通过 ROUTE 让 dev 创建),填写需求描述,最终标记完成
|
|
1028
|
-
- **Developer**: 更新开发状态,填写「实现记录」(方案、改动文件、注意事项)
|
|
1029
|
-
- **Reviewer**: 填写「审查记录」(评分、问题列表、是否通过)
|
|
1030
|
-
- **Tester**: 填写「测试记录」(测试用例、结果、发现的 Bug)
|
|
988
|
+
# Feature 工作记录
|
|
989
|
+
系统自动管理 \`context/features/{task-id}.md\` 工作记录文件:
|
|
990
|
+
- PM 通过 ROUTE 分配任务(带 task + taskTitle 字段)时自动创建
|
|
991
|
+
- 每次角色 ROUTE 传递时自动追加工作记录
|
|
992
|
+
- 角色收到消息时自动注入对应 task 文件内容作为上下文
|
|
993
|
+
角色不需要手动创建或更新这些文件。
|
|
1031
994
|
|
|
1032
995
|
${sharedMemoryContent}`;
|
|
1033
996
|
|
|
@@ -1070,6 +1033,86 @@ async function updateSharedClaudeMd(session) {
|
|
|
1070
1033
|
await writeSharedClaudeMd(session.sharedDir, session.goal, roles, session.projectDir, session.sharedKnowledge);
|
|
1071
1034
|
}
|
|
1072
1035
|
|
|
1036
|
+
// =====================================================================
|
|
1037
|
+
// Task File Management (auto-managed by system)
|
|
1038
|
+
// =====================================================================
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* 自动创建 task 进度文件
|
|
1042
|
+
* 当 ROUTE 带有 taskId + taskTitle 时,如果文件不存在则自动创建
|
|
1043
|
+
*/
|
|
1044
|
+
async function ensureTaskFile(session, taskId, taskTitle, assignee, summary) {
|
|
1045
|
+
const featuresDir = join(session.sharedDir, 'context', 'features');
|
|
1046
|
+
const filePath = join(featuresDir, `${taskId}.md`);
|
|
1047
|
+
|
|
1048
|
+
try {
|
|
1049
|
+
await fs.access(filePath);
|
|
1050
|
+
// 文件已存在,不覆盖
|
|
1051
|
+
return;
|
|
1052
|
+
} catch {
|
|
1053
|
+
// 文件不存在,创建
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
await fs.mkdir(featuresDir, { recursive: true });
|
|
1057
|
+
|
|
1058
|
+
const now = new Date().toISOString();
|
|
1059
|
+
const content = `# Feature: ${taskTitle}
|
|
1060
|
+
- task-id: ${taskId}
|
|
1061
|
+
- 状态: 待开发
|
|
1062
|
+
- 负责人: ${assignee}
|
|
1063
|
+
- 创建时间: ${now}
|
|
1064
|
+
|
|
1065
|
+
## 需求描述
|
|
1066
|
+
${summary}
|
|
1067
|
+
|
|
1068
|
+
## 工作记录
|
|
1069
|
+
`;
|
|
1070
|
+
|
|
1071
|
+
await fs.writeFile(filePath, content);
|
|
1072
|
+
|
|
1073
|
+
// 同步到 session.features
|
|
1074
|
+
if (!session.features.has(taskId)) {
|
|
1075
|
+
session.features.set(taskId, { taskId, taskTitle, createdAt: Date.now() });
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
console.log(`[Crew] Task file created: ${taskId} (${taskTitle})`);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* 追加工作记录到 task 文件
|
|
1083
|
+
* 当角色 ROUTE 时,自动将 summary 追加到对应 task 文件
|
|
1084
|
+
*/
|
|
1085
|
+
async function appendTaskRecord(session, taskId, roleName, summary) {
|
|
1086
|
+
const filePath = join(session.sharedDir, 'context', 'features', `${taskId}.md`);
|
|
1087
|
+
|
|
1088
|
+
try {
|
|
1089
|
+
await fs.access(filePath);
|
|
1090
|
+
} catch {
|
|
1091
|
+
// 文件不存在,跳过(不应该发生,但防御性处理)
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const role = session.roles.get(roleName);
|
|
1096
|
+
const label = role ? roleLabel(role) : roleName;
|
|
1097
|
+
const now = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
|
|
1098
|
+
const record = `\n### ${label} - ${now}\n${summary}\n`;
|
|
1099
|
+
|
|
1100
|
+
await fs.appendFile(filePath, record);
|
|
1101
|
+
console.log(`[Crew] Task record appended: ${taskId} by ${roleName}`);
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* 读取 task 文件内容(用于注入上下文)
|
|
1106
|
+
*/
|
|
1107
|
+
async function readTaskFile(session, taskId) {
|
|
1108
|
+
const filePath = join(session.sharedDir, 'context', 'features', `${taskId}.md`);
|
|
1109
|
+
try {
|
|
1110
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
1111
|
+
} catch {
|
|
1112
|
+
return null;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1073
1116
|
// =====================================================================
|
|
1074
1117
|
// Session Persistence
|
|
1075
1118
|
// =====================================================================
|
|
@@ -1359,46 +1402,13 @@ summary: 请实现注册页面,包括邮箱验证
|
|
|
1359
1402
|
- TASKS 块不需要在回复最末尾,可以放在任意位置`;
|
|
1360
1403
|
}
|
|
1361
1404
|
|
|
1362
|
-
// Feature
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
4. 所有子任务完成后,通过 ROUTE 让 dev 将状态更新为「已完成」
|
|
1370
|
-
|
|
1371
|
-
因为你不能使用 Write/Edit 工具,feature 文件的创建和更新都通过 ROUTE 给 developer 执行。在 ROUTE 的 summary 中明确要求 dev 创建/更新 feature 文件,并提供需求描述内容。`;
|
|
1372
|
-
} else if (role.roleType === 'developer') {
|
|
1373
|
-
prompt += `\n\n# Feature 进度记录
|
|
1374
|
-
收到任务后,你必须维护 feature 进度文件 \`context/features/{task-id}.md\`:
|
|
1375
|
-
1. 如果 PM 要求创建 feature 文件,先创建它(确保 context/features/ 目录存在)
|
|
1376
|
-
2. 开始开发时:将状态更新为「开发中」
|
|
1377
|
-
3. 开发完成时:将状态更新为「待审查」,在「实现记录」中填写:
|
|
1378
|
-
- 实现方案概述
|
|
1379
|
-
- 修改的文件列表
|
|
1380
|
-
- 需要注意的事项
|
|
1381
|
-
4. 收到审查/测试反馈需要修改时:将状态更新回「开发中」,追加修改记录`;
|
|
1382
|
-
} else if (role.roleType === 'reviewer') {
|
|
1383
|
-
prompt += `\n\n# Feature 进度记录
|
|
1384
|
-
完成代码审查后,你必须更新 feature 进度文件 \`context/features/{task-id}.md\`:
|
|
1385
|
-
1. 在「审查记录」中填写:
|
|
1386
|
-
- 代码质量评分(10分制)
|
|
1387
|
-
- 发现的问题列表(如有)
|
|
1388
|
-
- 审查结论(通过/需修改)
|
|
1389
|
-
2. 如果审查通过:不修改状态(等测试也通过后由 PM 标记完成)
|
|
1390
|
-
3. 如果需要修改:将状态更新为「开发中」`;
|
|
1391
|
-
} else if (role.roleType === 'tester') {
|
|
1392
|
-
prompt += `\n\n# Feature 进度记录
|
|
1393
|
-
完成测试后,你必须更新 feature 进度文件 \`context/features/{task-id}.md\`:
|
|
1394
|
-
1. 在「测试记录」中填写:
|
|
1395
|
-
- 测试用例列表及结果
|
|
1396
|
-
- 发现的 Bug(如有)
|
|
1397
|
-
- 测试结论(通过/不通过)
|
|
1398
|
-
2. 如果测试通过:不修改状态(等审查也通过后由 PM 标记完成)
|
|
1399
|
-
3. 如果发现 Bug:将状态更新为「开发中」`;
|
|
1400
|
-
}
|
|
1401
|
-
// designer 等其他角色不需要维护 feature 进度文件,无需注入额外 prompt
|
|
1405
|
+
// Feature 进度文件说明(系统自动管理,告知角色即可)
|
|
1406
|
+
prompt += `\n\n# Feature 工作记录
|
|
1407
|
+
系统会自动管理 \`context/features/{task-id}.md\` 工作记录文件:
|
|
1408
|
+
- PM 分配任务时自动创建文件(包含 task-id、标题、需求描述)
|
|
1409
|
+
- 每次 ROUTE 传递时自动追加工作记录(角色名、时间、summary)
|
|
1410
|
+
- 你收到的消息中会包含 <task-context> 标签,里面是该任务的完整工作记录
|
|
1411
|
+
你不需要手动创建或更新这些文件,专注于你的本职工作即可。`;
|
|
1402
1412
|
|
|
1403
1413
|
// 执行者角色的组绑定 prompt(count > 1 时)
|
|
1404
1414
|
if (role.groupIndex > 0 && role.roleType === 'developer') {
|
|
@@ -1909,6 +1919,19 @@ async function executeRoute(session, fromRole, route) {
|
|
|
1909
1919
|
return;
|
|
1910
1920
|
}
|
|
1911
1921
|
|
|
1922
|
+
// ★ Task 文件自动管理(fire-and-forget,不阻塞路由执行)
|
|
1923
|
+
if (taskId && summary) {
|
|
1924
|
+
// 如果是决策者发出的 ROUTE(分配任务),自动创建 task 文件
|
|
1925
|
+
const fromRoleConfig = session.roles.get(fromRole);
|
|
1926
|
+
if (fromRoleConfig?.isDecisionMaker && taskTitle && to !== 'human') {
|
|
1927
|
+
ensureTaskFile(session, taskId, taskTitle, to, summary)
|
|
1928
|
+
.catch(e => console.warn(`[Crew] Failed to create task file ${taskId}:`, e.message));
|
|
1929
|
+
}
|
|
1930
|
+
// 任何角色的 ROUTE 都追加工作记录
|
|
1931
|
+
appendTaskRecord(session, taskId, fromRole, summary)
|
|
1932
|
+
.catch(e => console.warn(`[Crew] Failed to append task record ${taskId}:`, e.message));
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1912
1935
|
// 发送路由消息(UI 显示 → @xxx)
|
|
1913
1936
|
sendCrewOutput(session, fromRole, 'route', null, { routeTo: to, routeSummary: summary });
|
|
1914
1937
|
|
|
@@ -1985,6 +2008,15 @@ async function dispatchToRole(session, roleName, content, fromSource, taskId, ta
|
|
|
1985
2008
|
roleState.currentTask = { taskId, taskTitle };
|
|
1986
2009
|
}
|
|
1987
2010
|
|
|
2011
|
+
// ★ Task 上下文注入:如果有 taskId,读取 task 文件注入到消息中
|
|
2012
|
+
const effectiveTaskId = taskId || roleState.currentTask?.taskId;
|
|
2013
|
+
if (effectiveTaskId && typeof content === 'string') {
|
|
2014
|
+
const taskContent = await readTaskFile(session, effectiveTaskId);
|
|
2015
|
+
if (taskContent) {
|
|
2016
|
+
content = `${content}\n\n---\n<task-context file="context/features/${effectiveTaskId}.md">\n${taskContent}\n</task-context>`;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
|
|
1988
2020
|
// 记录消息历史
|
|
1989
2021
|
session.messageHistory.push({
|
|
1990
2022
|
from: fromSource,
|