@yeaft/webchat-agent 0.0.209 → 0.0.211

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.
Files changed (3) hide show
  1. package/crew-i18n.js +374 -0
  2. package/crew.js +127 -173
  3. package/package.json +1 -1
package/crew-i18n.js ADDED
@@ -0,0 +1,374 @@
1
+ /**
2
+ * Crew Mode i18n — localized template strings for crew system prompts and files.
3
+ *
4
+ * Supported languages: 'zh-CN' (default), 'en'
5
+ * Session language is set at creation and never changes.
6
+ */
7
+
8
+ const messages = {
9
+ 'zh-CN': {
10
+ // buildRoleSystemPrompt
11
+ teamCollab: '# 团队协作',
12
+ teamCollabIntro: (goal) => goal ? `你正在一个 AI 团队中工作。项目目标是: ${goal}` : '你正在一个 AI 团队中工作。等待用户提出任务或问题。',
13
+ teamMembers: '团队成员:',
14
+ decisionMakerTag: '决策者',
15
+
16
+ routingRules: '# 路由规则',
17
+ routingIntro: '当你完成当前任务并需要将结果传递给其他角色时,在你的回复最末尾添加一个 ROUTE 块:',
18
+ routeTargets: '可用的路由目标:',
19
+ humanTarget: '人工(只在决策者也无法决定时使用)',
20
+ routeNotes: (dm) => `注意:
21
+ - 如果你的工作还没完成,不需要添加 ROUTE 块
22
+ - 如果你遇到不确定的问题,@ 决策者 "${dm}",而不是直接 @ human
23
+ - 如果你是决策者且遇到需要人类判断的问题,才 @ human
24
+ - 可以一次发多个 ROUTE 块来并行分配任务给不同角色
25
+ - ROUTE 块必须在回复的最末尾
26
+ - 当你的任务已完成且不需要其他角色继续时,ROUTE 回决策者 "${dm}" 做总结
27
+ - 在正文中可用 @角色name 提及某个角色(如 @developer),但这不会触发路由,仅供阅读`,
28
+
29
+ // Decision maker sections
30
+ toolUsage: '# 工具使用',
31
+ toolUsageContent: (isDevTeam) =>
32
+ `PM 可以使用所有工具,包括 Read、Grep、Glob、Bash、Edit、Write。${isDevTeam ? '代码文件的改动仍建议 ROUTE 给 developer 执行,但不做硬性限制。' : ''}`,
33
+
34
+ dmRole: '# 决策者职责',
35
+ dmRoleContent: `你是团队的决策者。其他角色遇到不确定的情况会请求你的决策。
36
+ - 如果你有足够的信息做出决策,直接决定并 @相关角色执行
37
+ - 如果你需要更多信息,@具体角色请求补充
38
+ - 如果问题超出你的能力范围或需要业务判断,@human 请人类决定
39
+ - 你可以随时审查其他角色的工作并给出反馈`,
40
+
41
+ dmDevExtra: `
42
+ - PM 不做代码分析。收到需求后直接将原始需求 ROUTE 给空闲 dev 做技术分析,dev 分析完返回 PM,PM 再拆分任务并直接分配执行。
43
+ - PM 拥有 commit + push + tag 的自主权。只要修改没有大的 regression 影响(测试全通过),PM 可以自行决定 commit、push 和 tag,无需等待人工确认。只有当改动会直接影响对话交互逻辑时,才需要人工介入审核。`,
44
+
45
+ collabMode: '# 协作模式',
46
+ collabModeContent: `这是一个协作讨论团队,不走严格的 PM→执行→审查→测试 工作流。
47
+ - 角色之间可以自由讨论、相互请教、提出不同意见
48
+ - 不需要严格的"分配→执行→审查"流程,鼓励角色之间直接对话
49
+ - 当一个角色需要另一个角色的输入时,直接 ROUTE 给对方并说明需要什么
50
+ - 决策者负责把控整体方向和最终决策,但日常讨论不需要经过决策者中转
51
+ - 每次 ROUTE 仍建议包含 task 和 taskTitle 字段,用于消息按 feature 分组显示`,
52
+
53
+ execGroupStatus: '# 执行组状态',
54
+ parallelRules: '# 并行任务调度规则',
55
+ parallelRulesContent: (maxGroup) => `你有 ${maxGroup} 个开发组可以并行工作。拆分任务时:
56
+ 1. 每个子任务分配 task-id(如 task-1)和 taskTitle(如 "实现登录页面")
57
+ 2. 优先分配给**空闲**的开发组,避免给忙碌的 dev 发新任务
58
+ 3. 一次可以发**多个 ROUTE 块**来并行分配任务:`,
59
+ parallelExample: `4. 每个 dev 完成后会独立经过 reviewer 和 tester 审核,最后 ROUTE 回你
60
+ 5. 等待**所有子任务完成**后再做汇总报告
61
+ 6. **每次 ROUTE 都必须包含 task 和 taskTitle 字段,不能省略。没有 task 字段的 ROUTE 会导致消息无法按 feature 分组显示**`,
62
+
63
+ groupBusy: (task) => `忙:${task}`,
64
+ groupBusyShort: '忙',
65
+ groupIdle: '空闲',
66
+ groupLabel: (g) => `组${g}`,
67
+
68
+ workflowEnd: '# 工作流终结点',
69
+ workflowEndContent: (isDevTeam) =>
70
+ `团队的工作流有明确的结束条件。当以下任一条件满足时,你应该给出总结并结束当前工作流:
71
+ ${isDevTeam ? '1. **代码已提交** - 所有代码修改已经 commit(如需要,可让 developer 执行 git commit)\n' : ''}${isDevTeam ? '2' : '1'}. **需要用户输入** - 遇到需要用户决定的问题时,@human 提出具体问题,等待用户回复
72
+ ${isDevTeam ? '3' : '2'}. **任务完成** - 所有任务已完成,给出完成总结(列出完成了什么${isDevTeam ? '、变更了哪些文件' : ''}、还有什么后续建议)
73
+
74
+ 重要:不要无限循环地在角色之间传递。当工作实质性完成时,主动给出总结并结束。`,
75
+
76
+ taskList: '# 任务清单',
77
+ taskListContent: `你可以在回复中添加 TASKS 块来发布/更新任务清单,团队界面会自动展示:`,
78
+ taskListNotes: `注意:
79
+ - 每行一个任务,[ ] 表示待办,[x] 表示已完成
80
+ - #taskId 标注对应的 feature ID(如 #task-1),用于精确关联任务完成状态
81
+ - @角色name 标注负责人(可选)
82
+ - 后续回复中可更新 TASKS 块(标记完成的任务)
83
+ - TASKS 块不需要在回复最末尾,可以放在任意位置`,
84
+ taskExample: `---TASKS---
85
+ - [ ] 任务描述 #task-1 @角色name
86
+ - [x] 已完成的任务 #task-2 @角色name
87
+ ---END_TASKS---`,
88
+
89
+ featureRecordTitle: '# Feature 工作记录',
90
+ featureRecordContent: `系统会自动管理 \`context/features/{task-id}.md\` 工作记录文件:
91
+ - PM 分配任务时自动创建文件(包含 task-id、标题、需求描述)
92
+ - 每次 ROUTE 传递时自动追加工作记录(角色名、时间、summary)
93
+ - 你收到的消息中会包含 <task-context> 标签,里面是该任务的完整工作记录
94
+
95
+ 系统还维护以下文件(自动更新,无需手动管理):
96
+ - \`context/features/index.md\`:所有 feature 的索引(进行中/已完成分类),快速查看项目状态
97
+ - \`context/changelog.md\`:已完成任务的变更记录,每个任务完成时自动追加摘要
98
+ 你不需要手动创建或更新这些文件,专注于你的本职工作即可。`,
99
+
100
+ devGroupBinding: '# 开发组绑定',
101
+ devGroupBindingContent: (gi, revLabel, revName, testLabel, testName) =>
102
+ `你属于开发组 ${gi}。你的搭档:
103
+ - 审查者: ${revLabel} (${revName})
104
+ - 测试: ${testLabel} (${testName})
105
+
106
+ 开发完成后,请同时发两个 ROUTE 块分别给 ${revName} 和 ${testName}:`,
107
+ devGroupBindingNote: '两者会并行工作,各自完成后独立 ROUTE 回 PM。',
108
+
109
+ implLoginPage: '实现登录页面',
110
+ implLoginSummary: '请实现登录页面,包括表单验证和API调用',
111
+ implRegisterPage: '实现注册页面',
112
+ implRegisterSummary: '请实现注册页面,包括邮箱验证',
113
+ reviewCode: '请审查代码变更',
114
+ testFeature: '请测试功能',
115
+
116
+ // buildInitialTask
117
+ projectStart: '项目启动!',
118
+ goalLabel: '目标',
119
+ firstRoleInstruction: '你是第一个开始工作的角色。请分析目标,开始你的工作。\n完成后,通过 ROUTE 块将结果传递给下一个合适的角色。',
120
+ availableRoles: '团队中可用的角色:',
121
+
122
+ // writeSharedClaudeMd
123
+ projectGoal: '# 项目目标',
124
+ projectCodePath: '# 项目代码路径',
125
+ useAbsolutePath: '所有代码操作请使用此绝对路径。',
126
+ teamMembersTitle: '# 团队成员',
127
+ noMembers: '_暂无成员_',
128
+ workConventions: '# 工作约定',
129
+ workConventionsContent: `- 文档产出写入 context/ 目录
130
+ - 重要决策记录在 context/decisions.md
131
+ - 代码修改使用项目代码路径的绝对路径`,
132
+ stuckRules: '# 卡住上报规则',
133
+ stuckRulesContent: `当你遇到以下情况时,不要自己空转或反复重试,立即 ROUTE 给 PM(pm)请求协调:
134
+ 1. 缺少前置依赖(如需要的文件、目录、代码不存在)
135
+ 2. 等待其他角色的产出但迟迟没有收到
136
+ 3. 任务描述不清楚或有歧义,无法判断正确做法
137
+ 4. 遇到超出自己职责范围的问题
138
+ 5. 连续尝试 2 次相同操作仍然失败
139
+ 上报时请说明:你在做什么任务、卡在哪里、你认为需要谁来协助。PM 会统筹全局,判断是分配给合适的人还是调整任务顺序。`,
140
+ worktreeRules: '# Worktree 隔离规则',
141
+ worktreeRulesContent: `- dev/reviewer/tester 角色必须在各自分配的 worktree 中工作,绝对禁止在项目主目录或 main 分支上修改代码
142
+ - 每个角色的 CLAUDE.md 会标明「代码工作目录」,该路径就是你的 worktree,所有文件操作必须使用该路径
143
+ - PM 和 designer 不使用 worktree,他们在项目主目录下以只读方式工作
144
+ - 绝对禁止在其他开发组的 worktree 中操作代码
145
+ - 代码完成并通过 review 后,dev 自己提 PR 合并到 main 分支
146
+ - PM 不做 cherry-pick,只负责打 tag
147
+ - 每次新任务/新 feature 必须基于最新的 main 分支创建新的 worktree,确保在最新代码上开发`,
148
+ featureRecordShared: `# Feature 工作记录
149
+ 系统自动管理 \`context/features/{task-id}.md\` 工作记录文件:
150
+ - PM 通过 ROUTE 分配任务(带 task + taskTitle 字段)时自动创建
151
+ - 每次角色 ROUTE 传递时自动追加工作记录
152
+ - 角色收到消息时自动注入对应 task 文件内容作为上下文
153
+ 角色不需要手动创建或更新这些文件。`,
154
+ sharedMemoryTitle: '# 共享记忆',
155
+ sharedMemoryDefault: '_团队共同维护,记录重要的共识、决策和信息。_',
156
+
157
+ // writeRoleClaudeMd
158
+ roleTitle: (label) => `# 角色: ${label}`,
159
+ codeWorkDir: '# 代码工作目录',
160
+ codeWorkDirNote: '所有代码操作请使用此路径。不要使用项目主目录。',
161
+ personalMemory: '# 个人记忆',
162
+ personalMemoryDefault: '_在这里记录重要的信息、决策、进展和待办事项。_',
163
+
164
+ // ensureTaskFile
165
+ featureLabel: 'Feature',
166
+ statusPending: '待开发',
167
+ assigneeLabel: '负责人',
168
+ createdAtLabel: '创建时间',
169
+ requirementDesc: '## 需求描述',
170
+ workRecord: '## 工作记录',
171
+
172
+ // updateFeatureIndex
173
+ featureIndex: '# Feature Index',
174
+ lastUpdated: '最后更新',
175
+ inProgressGroup: (n) => `## 进行中 (${n})`,
176
+ completedGroup: (n) => `## 已完成 (${n})`,
177
+ colTaskId: 'task-id',
178
+ colTitle: '标题',
179
+ colCreatedAt: '创建时间',
180
+
181
+ // appendChangelog
182
+ changelogTitle: '# Changelog',
183
+ completedAt: '完成时间',
184
+ summaryLabel: '摘要',
185
+ noSummary: '(无详细摘要)',
186
+ },
187
+
188
+ 'en': {
189
+ // buildRoleSystemPrompt
190
+ teamCollab: '# Team Collaboration',
191
+ teamCollabIntro: (goal) => goal ? `You are working in an AI team. The project goal is: ${goal}` : 'You are working in an AI team. Waiting for the user to assign tasks or questions.',
192
+ teamMembers: 'Team members:',
193
+ decisionMakerTag: 'Decision Maker',
194
+
195
+ routingRules: '# Routing Rules',
196
+ routingIntro: 'When you finish your current task and need to pass the result to another role, add a ROUTE block at the very end of your reply:',
197
+ routeTargets: 'Available routing targets:',
198
+ humanTarget: 'Human (only when the decision maker cannot decide)',
199
+ routeNotes: (dm) => `Notes:
200
+ - If your work is not yet complete, do not add a ROUTE block
201
+ - If you encounter an uncertain issue, @ the decision maker "${dm}" instead of directly @ human
202
+ - Only @ human if you are the decision maker and the issue requires human judgment
203
+ - You can send multiple ROUTE blocks to assign tasks to different roles in parallel
204
+ - ROUTE blocks must be at the very end of your reply
205
+ - When your task is complete and no further role handoff is needed, ROUTE back to the decision maker "${dm}" for summary
206
+ - You can mention a role by @roleName in the body text (e.g., @developer) for reference only — this does not trigger routing`,
207
+
208
+ // Decision maker sections
209
+ toolUsage: '# Tool Usage',
210
+ toolUsageContent: (isDevTeam) =>
211
+ `PM can use all tools including Read, Grep, Glob, Bash, Edit, Write.${isDevTeam ? ' Code changes are still recommended to ROUTE to developer, but not strictly required.' : ''}`,
212
+
213
+ dmRole: '# Decision Maker Responsibilities',
214
+ dmRoleContent: `You are the team's decision maker. Other roles will request your decision when they encounter uncertainties.
215
+ - If you have enough information, make the decision directly and @ the relevant role to execute
216
+ - If you need more information, @ a specific role and request details
217
+ - If the issue is beyond your capability or requires business judgment, @human to let a human decide
218
+ - You can review other roles' work and provide feedback at any time`,
219
+
220
+ dmDevExtra: `
221
+ - PM does not analyze code. Upon receiving requirements, directly ROUTE the raw requirements to an idle dev for technical analysis. After dev analyzes, PM splits tasks and assigns execution.
222
+ - PM has autonomy for commit + push + tag. As long as changes have no significant regression impact (all tests pass), PM can decide to commit, push and tag without waiting for manual confirmation. Only when changes directly affect conversation interaction logic should manual review be required.`,
223
+
224
+ collabMode: '# Collaboration Mode',
225
+ collabModeContent: `This is a collaborative discussion team — no strict PM→Execute→Review→Test workflow.
226
+ - Roles can freely discuss, consult each other, and raise different opinions
227
+ - No strict "assign→execute→review" process — direct dialogue between roles is encouraged
228
+ - When a role needs input from another, directly ROUTE to them and explain what's needed
229
+ - The decision maker oversees the overall direction and final decisions, but daily discussions don't need to go through the decision maker
230
+ - Each ROUTE should still include task and taskTitle fields for message grouping by feature`,
231
+
232
+ execGroupStatus: '# Execution Group Status',
233
+ parallelRules: '# Parallel Task Scheduling Rules',
234
+ parallelRulesContent: (maxGroup) => `You have ${maxGroup} dev groups that can work in parallel. When splitting tasks:
235
+ 1. Assign each sub-task a task-id (e.g., task-1) and taskTitle (e.g., "Implement login page")
236
+ 2. Prioritize assigning to **idle** dev groups — avoid sending new tasks to busy devs
237
+ 3. You can send **multiple ROUTE blocks** to assign tasks in parallel:`,
238
+ parallelExample: `4. Each dev will independently go through reviewer and tester review, then ROUTE back to you
239
+ 5. Wait until **all sub-tasks are completed** before providing a summary report
240
+ 6. **Every ROUTE must include task and taskTitle fields — they cannot be omitted. ROUTEs without a task field will prevent messages from being grouped by feature**`,
241
+
242
+ groupBusy: (task) => `busy:${task}`,
243
+ groupBusyShort: 'busy',
244
+ groupIdle: 'idle',
245
+ groupLabel: (g) => `Group${g}`,
246
+
247
+ workflowEnd: '# Workflow Termination',
248
+ workflowEndContent: (isDevTeam) =>
249
+ `The team's workflow has clear end conditions. When any of the following conditions are met, you should provide a summary and conclude the current workflow:
250
+ ${isDevTeam ? '1. **Code committed** - All code changes have been committed (if needed, let developer execute git commit)\n' : ''}${isDevTeam ? '2' : '1'}. **User input needed** - When encountering issues that require user decision, @human with a specific question and wait for reply
251
+ ${isDevTeam ? '3' : '2'}. **Task completed** - All tasks are done, provide a completion summary (list what was accomplished${isDevTeam ? ', which files were changed' : ''}, and any follow-up suggestions)
252
+
253
+ Important: Do not loop endlessly between roles. When work is substantively complete, proactively provide a summary and conclude.`,
254
+
255
+ taskList: '# Task List',
256
+ taskListContent: `You can add a TASKS block in your reply to publish/update a task list, which the team interface will display automatically:`,
257
+ taskListNotes: `Notes:
258
+ - One task per line, [ ] for to-do, [x] for completed
259
+ - #taskId marks the corresponding feature ID (e.g., #task-1) for precise task completion tracking
260
+ - @roleName marks the assignee (optional)
261
+ - You can update the TASKS block in subsequent replies (mark completed tasks)
262
+ - TASKS block does not need to be at the end of the reply — it can be placed anywhere`,
263
+ taskExample: `---TASKS---
264
+ - [ ] Task description #task-1 @roleName
265
+ - [x] Completed task #task-2 @roleName
266
+ ---END_TASKS---`,
267
+
268
+ featureRecordTitle: '# Feature Work Records',
269
+ featureRecordContent: `The system automatically manages \`context/features/{task-id}.md\` work record files:
270
+ - Automatically created when PM assigns tasks (includes task-id, title, requirement description)
271
+ - Work records are appended automatically on each ROUTE handoff (role name, time, summary)
272
+ - Your received messages will include <task-context> tags containing the complete work record for that task
273
+
274
+ The system also maintains these files (auto-updated, no manual management needed):
275
+ - \`context/features/index.md\`: Index of all features (categorized as in-progress/completed) for quick project status overview
276
+ - \`context/changelog.md\`: Change log of completed tasks, with summary appended when each task completes
277
+ You don't need to manually create or update these files — focus on your core work.`,
278
+
279
+ devGroupBinding: '# Dev Group Binding',
280
+ devGroupBindingContent: (gi, revLabel, revName, testLabel, testName) =>
281
+ `You belong to dev group ${gi}. Your partners:
282
+ - Reviewer: ${revLabel} (${revName})
283
+ - Tester: ${testLabel} (${testName})
284
+
285
+ After development is complete, send two ROUTE blocks simultaneously to ${revName} and ${testName}:`,
286
+ devGroupBindingNote: 'Both will work in parallel and independently ROUTE back to PM when done.',
287
+
288
+ implLoginPage: 'Implement login page',
289
+ implLoginSummary: 'Please implement the login page including form validation and API calls',
290
+ implRegisterPage: 'Implement registration page',
291
+ implRegisterSummary: 'Please implement the registration page including email verification',
292
+ reviewCode: 'Please review code changes',
293
+ testFeature: 'Please test the feature',
294
+
295
+ // buildInitialTask
296
+ projectStart: 'Project started!',
297
+ goalLabel: 'Goal',
298
+ firstRoleInstruction: 'You are the first role to start working. Analyze the goal and begin your work.\nWhen done, pass the result to the next appropriate role via a ROUTE block.',
299
+ availableRoles: 'Available roles in the team:',
300
+
301
+ // writeSharedClaudeMd
302
+ projectGoal: '# Project Goal',
303
+ projectCodePath: '# Project Code Path',
304
+ useAbsolutePath: 'Use this absolute path for all code operations.',
305
+ teamMembersTitle: '# Team Members',
306
+ noMembers: '_No members yet_',
307
+ workConventions: '# Work Conventions',
308
+ workConventionsContent: `- Write documentation output to context/ directory
309
+ - Record important decisions in context/decisions.md
310
+ - Use the project code path (absolute path) for code changes`,
311
+ stuckRules: '# Escalation Rules',
312
+ stuckRulesContent: `When you encounter the following situations, do not spin or retry repeatedly — immediately ROUTE to PM (pm) for coordination:
313
+ 1. Missing prerequisites (required files, directories, or code don't exist)
314
+ 2. Waiting for another role's output that hasn't arrived
315
+ 3. Task description is unclear or ambiguous, cannot determine the correct approach
316
+ 4. Encountered issues outside your role's scope
317
+ 5. Same operation has failed after 2 consecutive attempts
318
+ When escalating, explain: what task you're working on, where you're stuck, and who you think should assist. PM will coordinate globally and decide whether to assign to the right person or adjust task order.`,
319
+ worktreeRules: '# Worktree Isolation Rules',
320
+ worktreeRulesContent: `- dev/reviewer/tester roles must work in their assigned worktrees — modifying code in the main project directory or main branch is strictly prohibited
321
+ - Each role's CLAUDE.md specifies the "Code Work Directory" — that path is your worktree, all file operations must use that path
322
+ - PM and designer don't use worktrees — they work read-only in the main project directory
323
+ - Operating code in another dev group's worktree is strictly prohibited
324
+ - After code passes review, dev creates a PR to merge into main branch
325
+ - PM doesn't cherry-pick, only manages tags
326
+ - Each new task/feature must create a new worktree based on the latest main branch to ensure development on latest code`,
327
+ featureRecordShared: `# Feature Work Records
328
+ The system automatically manages \`context/features/{task-id}.md\` work record files:
329
+ - Automatically created when PM assigns tasks via ROUTE (with task + taskTitle fields)
330
+ - Work records are appended on each role ROUTE handoff
331
+ - Task file content is auto-injected as context when a role receives a message
332
+ Roles don't need to manually create or update these files.`,
333
+ sharedMemoryTitle: '# Shared Memory',
334
+ sharedMemoryDefault: '_Team-maintained shared knowledge, decisions, and information._',
335
+
336
+ // writeRoleClaudeMd
337
+ roleTitle: (label) => `# Role: ${label}`,
338
+ codeWorkDir: '# Code Work Directory',
339
+ codeWorkDirNote: 'Use this path for all code operations. Do not use the main project directory.',
340
+ personalMemory: '# Personal Memory',
341
+ personalMemoryDefault: '_Record important information, decisions, progress, and to-do items here._',
342
+
343
+ // ensureTaskFile
344
+ featureLabel: 'Feature',
345
+ statusPending: 'Pending',
346
+ assigneeLabel: 'Assignee',
347
+ createdAtLabel: 'Created',
348
+ requirementDesc: '## Requirement Description',
349
+ workRecord: '## Work Record',
350
+
351
+ // updateFeatureIndex
352
+ featureIndex: '# Feature Index',
353
+ lastUpdated: 'Last updated',
354
+ inProgressGroup: (n) => `## In Progress (${n})`,
355
+ completedGroup: (n) => `## Completed (${n})`,
356
+ colTaskId: 'task-id',
357
+ colTitle: 'Title',
358
+ colCreatedAt: 'Created',
359
+
360
+ // appendChangelog
361
+ changelogTitle: '# Changelog',
362
+ completedAt: 'Completed',
363
+ summaryLabel: 'Summary',
364
+ noSummary: '(No detailed summary)',
365
+ }
366
+ };
367
+
368
+ /**
369
+ * Get a localized string for the given session language.
370
+ * Falls back to 'zh-CN' for unknown languages.
371
+ */
372
+ export function getMessages(language) {
373
+ return messages[language] || messages['zh-CN'];
374
+ }
package/crew.js CHANGED
@@ -19,6 +19,7 @@ import { homedir } from 'os';
19
19
  import { execFile as execFileCb } from 'child_process';
20
20
  import { promisify } from 'util';
21
21
  import ctx from './context.js';
22
+ import { getMessages } from './crew-i18n.js';
22
23
 
23
24
  const execFile = promisify(execFileCb);
24
25
 
@@ -329,6 +330,7 @@ async function saveSessionMeta(session) {
329
330
  username: session.username,
330
331
  agentId: session.agentId || null,
331
332
  teamType: session.teamType || 'dev',
333
+ language: session.language || 'zh-CN',
332
334
  costUsd: session.costUsd,
333
335
  totalInputTokens: session.totalInputTokens,
334
336
  totalOutputTokens: session.totalOutputTokens,
@@ -714,6 +716,7 @@ export async function resumeCrewSession(msg) {
714
716
  username: username || meta.username,
715
717
  agentId: meta.agentId || ctx.CONFIG?.agentName || null,
716
718
  teamType: meta.teamType || 'dev',
719
+ language: meta.language || 'zh-CN',
717
720
  createdAt: meta.createdAt || Date.now()
718
721
  };
719
722
  crewSessions.set(sessionId, session);
@@ -801,6 +804,7 @@ export async function createCrewSession(msg) {
801
804
  roles: rawRoles = [], // [{ name, displayName, icon, description, claudeMd, model, budget, isDecisionMaker, count }]
802
805
  maxRounds = 20,
803
806
  teamType = 'dev',
807
+ language = 'zh-CN',
804
808
  userId,
805
809
  username
806
810
  } = msg;
@@ -853,6 +857,7 @@ export async function createCrewSession(msg) {
853
857
  username,
854
858
  agentId: ctx.CONFIG?.agentName || null,
855
859
  teamType,
860
+ language,
856
861
  createdAt: Date.now()
857
862
  };
858
863
 
@@ -890,7 +895,7 @@ export async function createCrewSession(msg) {
890
895
  // 初始化共享区(角色目录 + CLAUDE.md)
891
896
  session.initProgress = 'roles';
892
897
  sendStatusUpdate(session);
893
- await initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge);
898
+ await initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge, language);
894
899
 
895
900
  // 初始化 git worktrees
896
901
  const groupIndices = [...new Set(roles.filter(r => r.groupIndex > 0).map(r => r.groupIndex))];
@@ -904,7 +909,7 @@ export async function createCrewSession(msg) {
904
909
  for (const role of roles) {
905
910
  if (role.groupIndex > 0 && worktreeMap.has(role.groupIndex)) {
906
911
  role.workDir = worktreeMap.get(role.groupIndex);
907
- await writeRoleClaudeMd(sharedDir, role);
912
+ await writeRoleClaudeMd(sharedDir, role, language);
908
913
  }
909
914
  }
910
915
 
@@ -923,7 +928,7 @@ export async function createCrewSession(msg) {
923
928
  if (goal && roles.length > 0 && session.status === 'running') {
924
929
  const firstRole = roles.find(r => r.name === 'pm') || roles[0];
925
930
  if (firstRole) {
926
- const initialPrompt = buildInitialTask(goal, firstRole, roles);
931
+ const initialPrompt = buildInitialTask(goal, firstRole, roles, language);
927
932
  await dispatchToRole(session, firstRole.name, initialPrompt, 'system');
928
933
  }
929
934
  }
@@ -985,7 +990,7 @@ export async function addRoleToSession(msg) {
985
990
  }
986
991
 
987
992
  // 初始化角色目录(CLAUDE.md + memory.md)
988
- await initRoleDir(session.sharedDir, r);
993
+ await initRoleDir(session.sharedDir, r, session.language || 'zh-CN');
989
994
 
990
995
  console.log(`[Crew] Role added: ${r.name} (${r.displayName}) to session ${sessionId}`);
991
996
 
@@ -1113,7 +1118,7 @@ export async function handleUpdateCrewSession(msg) {
1113
1118
  * └── {roleName}/
1114
1119
  * └── CLAUDE.md ← 角色定义 + 个人记忆
1115
1120
  */
1116
- async function initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge = '') {
1121
+ async function initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge = '', language = 'zh-CN') {
1117
1122
  await fs.mkdir(sharedDir, { recursive: true });
1118
1123
  await fs.mkdir(join(sharedDir, 'context'), { recursive: true });
1119
1124
  await fs.mkdir(join(sharedDir, 'sessions'), { recursive: true });
@@ -1121,17 +1126,17 @@ async function initSharedDir(sharedDir, goal, roles, projectDir, sharedKnowledge
1121
1126
 
1122
1127
  // 初始化每个角色的目录
1123
1128
  for (const role of roles) {
1124
- await initRoleDir(sharedDir, role);
1129
+ await initRoleDir(sharedDir, role, language);
1125
1130
  }
1126
1131
 
1127
1132
  // 生成 .crew/CLAUDE.md(共享级)
1128
- await writeSharedClaudeMd(sharedDir, goal, roles, projectDir, sharedKnowledge);
1133
+ await writeSharedClaudeMd(sharedDir, goal, roles, projectDir, sharedKnowledge, language);
1129
1134
  }
1130
1135
 
1131
1136
  /**
1132
1137
  * 初始化角色目录: .crew/roles/{roleName}/CLAUDE.md
1133
1138
  */
1134
- async function initRoleDir(sharedDir, role) {
1139
+ async function initRoleDir(sharedDir, role, language = 'zh-CN') {
1135
1140
  const roleDir = join(sharedDir, 'roles', role.name);
1136
1141
  await fs.mkdir(roleDir, { recursive: true });
1137
1142
 
@@ -1141,7 +1146,7 @@ async function initRoleDir(sharedDir, role) {
1141
1146
  await fs.access(claudeMdPath);
1142
1147
  // 已存在,不覆盖(保留角色自己写入的记忆)
1143
1148
  } catch {
1144
- await writeRoleClaudeMd(sharedDir, role);
1149
+ await writeRoleClaudeMd(sharedDir, role, language);
1145
1150
  }
1146
1151
  }
1147
1152
 
@@ -1149,50 +1154,32 @@ async function initRoleDir(sharedDir, role) {
1149
1154
  * 写入 .crew/CLAUDE.md — 共享级(所有角色自动继承)
1150
1155
  * 记忆直接写在 CLAUDE.md 中,Claude Code 会自动加载
1151
1156
  */
1152
- async function writeSharedClaudeMd(sharedDir, goal, roles, projectDir, sharedKnowledge = '') {
1157
+ async function writeSharedClaudeMd(sharedDir, goal, roles, projectDir, sharedKnowledge = '', language = 'zh-CN') {
1158
+ const m = getMessages(language);
1153
1159
  const sharedMemoryContent = sharedKnowledge
1154
- ? `# 共享记忆\n${sharedKnowledge}\n`
1155
- : `# 共享记忆\n_团队共同维护,记录重要的共识、决策和信息。_\n`;
1160
+ ? `${m.sharedMemoryTitle}\n${sharedKnowledge}\n`
1161
+ : `${m.sharedMemoryTitle}\n${m.sharedMemoryDefault}\n`;
1156
1162
 
1157
- const claudeMd = `# 项目目标
1163
+ const claudeMd = `${m.projectGoal}
1158
1164
  ${goal}
1159
1165
 
1160
- # 项目代码路径
1166
+ ${m.projectCodePath}
1161
1167
  ${projectDir}
1162
- 所有代码操作请使用此绝对路径。
1163
-
1164
- # 团队成员
1165
- ${roles.length > 0 ? roles.map(r => `- ${roleLabel(r)}(${r.name}): ${r.description}${r.isDecisionMaker ? ' (决策者)' : ''}`).join('\n') : '_暂无成员_'}
1166
-
1167
- # 工作约定
1168
- - 文档产出写入 context/ 目录
1169
- - 重要决策记录在 context/decisions.md
1170
- - 代码修改使用项目代码路径的绝对路径
1171
-
1172
- # 卡住上报规则
1173
- 当你遇到以下情况时,不要自己空转或反复重试,立即 ROUTE 给 PM(pm)请求协调:
1174
- 1. 缺少前置依赖(如需要的文件、目录、代码不存在)
1175
- 2. 等待其他角色的产出但迟迟没有收到
1176
- 3. 任务描述不清楚或有歧义,无法判断正确做法
1177
- 4. 遇到超出自己职责范围的问题
1178
- 5. 连续尝试 2 次相同操作仍然失败
1179
- 上报时请说明:你在做什么任务、卡在哪里、你认为需要谁来协助。PM 会统筹全局,判断是分配给合适的人还是调整任务顺序。
1180
-
1181
- # Worktree 隔离规则
1182
- - dev/reviewer/tester 角色必须在各自分配的 worktree 中工作,绝对禁止在项目主目录或 main 分支上修改代码
1183
- - 每个角色的 CLAUDE.md 会标明「代码工作目录」,该路径就是你的 worktree,所有文件操作必须使用该路径
1184
- - PM 和 designer 不使用 worktree,他们在项目主目录下以只读方式工作
1185
- - 绝对禁止在其他开发组的 worktree 中操作代码
1186
- - 代码完成并通过 review 后,dev 自己提 PR 合并到 main 分支
1187
- - PM 不做 cherry-pick,只负责打 tag
1188
- - 每次新任务/新 feature 必须基于最新的 main 分支创建新的 worktree,确保在最新代码上开发
1189
-
1190
- # Feature 工作记录
1191
- 系统自动管理 \`context/features/{task-id}.md\` 工作记录文件:
1192
- - PM 通过 ROUTE 分配任务(带 task + taskTitle 字段)时自动创建
1193
- - 每次角色 ROUTE 传递时自动追加工作记录
1194
- - 角色收到消息时自动注入对应 task 文件内容作为上下文
1195
- 角色不需要手动创建或更新这些文件。
1168
+ ${m.useAbsolutePath}
1169
+
1170
+ ${m.teamMembersTitle}
1171
+ ${roles.length > 0 ? roles.map(r => `- ${roleLabel(r)}(${r.name}): ${r.description}${r.isDecisionMaker ? ` (${m.decisionMakerTag})` : ''}`).join('\n') : m.noMembers}
1172
+
1173
+ ${m.workConventions}
1174
+ ${m.workConventionsContent}
1175
+
1176
+ ${m.stuckRules}
1177
+ ${m.stuckRulesContent}
1178
+
1179
+ ${m.worktreeRules}
1180
+ ${m.worktreeRulesContent}
1181
+
1182
+ ${m.featureRecordShared}
1196
1183
 
1197
1184
  ${sharedMemoryContent}`;
1198
1185
 
@@ -1203,25 +1190,26 @@ ${sharedMemoryContent}`;
1203
1190
  * 写入 .crew/roles/{roleName}/CLAUDE.md — 角色级
1204
1191
  * 记忆直接追加在此文件中,Claude Code 自动加载
1205
1192
  */
1206
- async function writeRoleClaudeMd(sharedDir, role) {
1193
+ async function writeRoleClaudeMd(sharedDir, role, language = 'zh-CN') {
1207
1194
  const roleDir = join(sharedDir, 'roles', role.name);
1195
+ const m = getMessages(language);
1208
1196
 
1209
- let claudeMd = `# 角色: ${roleLabel(role)}
1197
+ let claudeMd = `${m.roleTitle(roleLabel(role))}
1210
1198
  ${role.claudeMd || role.description}
1211
1199
  `;
1212
1200
 
1213
1201
  // 有独立 worktree 的角色,覆盖代码工作目录
1214
1202
  if (role.workDir) {
1215
1203
  claudeMd += `
1216
- # 代码工作目录
1204
+ ${m.codeWorkDir}
1217
1205
  ${role.workDir}
1218
- 所有代码操作请使用此路径。不要使用项目主目录。
1206
+ ${m.codeWorkDirNote}
1219
1207
  `;
1220
1208
  }
1221
1209
 
1222
1210
  claudeMd += `
1223
- # 个人记忆
1224
- _在这里记录重要的信息、决策、进展和待办事项。_
1211
+ ${m.personalMemory}
1212
+ ${m.personalMemoryDefault}
1225
1213
  `;
1226
1214
 
1227
1215
  await fs.writeFile(join(roleDir, 'CLAUDE.md'), claudeMd);
@@ -1232,7 +1220,7 @@ _在这里记录重要的信息、决策、进展和待办事项。_
1232
1220
  */
1233
1221
  async function updateSharedClaudeMd(session) {
1234
1222
  const roles = Array.from(session.roles.values());
1235
- await writeSharedClaudeMd(session.sharedDir, session.goal, roles, session.projectDir, session.sharedKnowledge);
1223
+ await writeSharedClaudeMd(session.sharedDir, session.goal, roles, session.projectDir, session.sharedKnowledge, session.language || 'zh-CN');
1236
1224
  }
1237
1225
 
1238
1226
  // =====================================================================
@@ -1257,17 +1245,18 @@ async function ensureTaskFile(session, taskId, taskTitle, assignee, summary) {
1257
1245
 
1258
1246
  await fs.mkdir(featuresDir, { recursive: true });
1259
1247
 
1248
+ const m = getMessages(session.language || 'zh-CN');
1260
1249
  const now = new Date().toISOString();
1261
- const content = `# Feature: ${taskTitle}
1250
+ const content = `# ${m.featureLabel}: ${taskTitle}
1262
1251
  - task-id: ${taskId}
1263
- - 状态: 待开发
1264
- - 负责人: ${assignee}
1265
- - 创建时间: ${now}
1252
+ - ${m.statusPending}
1253
+ - ${m.assigneeLabel}: ${assignee}
1254
+ - ${m.createdAtLabel}: ${now}
1266
1255
 
1267
- ## 需求描述
1256
+ ${m.requirementDesc}
1268
1257
  ${summary}
1269
1258
 
1270
- ## 工作记录
1259
+ ${m.workRecord}
1271
1260
  `;
1272
1261
 
1273
1262
  await fs.writeFile(filePath, content);
@@ -1340,6 +1329,7 @@ async function updateFeatureIndex(session) {
1340
1329
  const featuresDir = join(session.sharedDir, 'context', 'features');
1341
1330
  await fs.mkdir(featuresDir, { recursive: true });
1342
1331
 
1332
+ const m = getMessages(session.language || 'zh-CN');
1343
1333
  const completed = session._completedTaskIds || new Set();
1344
1334
  const allFeatures = Array.from(session.features.values());
1345
1335
 
@@ -1349,23 +1339,24 @@ async function updateFeatureIndex(session) {
1349
1339
  const inProgress = allFeatures.filter(f => !completed.has(f.taskId));
1350
1340
  const done = allFeatures.filter(f => completed.has(f.taskId));
1351
1341
 
1352
- const now = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
1353
- let content = `# Feature Index\n> 最后更新: ${now}\n`;
1342
+ const locale = (session.language === 'en') ? 'en-US' : 'zh-CN';
1343
+ const now = new Date().toLocaleString(locale, { timeZone: 'Asia/Shanghai' });
1344
+ let content = `${m.featureIndex}\n> ${m.lastUpdated}: ${now}\n`;
1354
1345
 
1355
- content += `\n## 进行中 (${inProgress.length})\n`;
1346
+ content += `\n${m.inProgressGroup(inProgress.length)}\n`;
1356
1347
  if (inProgress.length > 0) {
1357
- content += '| task-id | 标题 | 创建时间 |\n|---------|------|----------|\n';
1348
+ content += `| ${m.colTaskId} | ${m.colTitle} | ${m.colCreatedAt} |\n|---------|------|----------|\n`;
1358
1349
  for (const f of inProgress) {
1359
- const date = f.createdAt ? new Date(f.createdAt).toLocaleDateString('zh-CN') : '-';
1350
+ const date = f.createdAt ? new Date(f.createdAt).toLocaleDateString(locale) : '-';
1360
1351
  content += `| ${f.taskId} | ${f.taskTitle} | ${date} |\n`;
1361
1352
  }
1362
1353
  }
1363
1354
 
1364
- content += `\n## 已完成 (${done.length})\n`;
1355
+ content += `\n${m.completedGroup(done.length)}\n`;
1365
1356
  if (done.length > 0) {
1366
- content += '| task-id | 标题 | 创建时间 |\n|---------|------|----------|\n';
1357
+ content += `| ${m.colTaskId} | ${m.colTitle} | ${m.colCreatedAt} |\n|---------|------|----------|\n`;
1367
1358
  for (const f of done) {
1368
- const date = f.createdAt ? new Date(f.createdAt).toLocaleDateString('zh-CN') : '-';
1359
+ const date = f.createdAt ? new Date(f.createdAt).toLocaleDateString(locale) : '-';
1369
1360
  content += `| ${f.taskId} | ${f.taskTitle} | ${date} |\n`;
1370
1361
  }
1371
1362
  }
@@ -1383,6 +1374,8 @@ async function appendChangelog(session, taskId, taskTitle) {
1383
1374
  await fs.mkdir(contextDir, { recursive: true });
1384
1375
  const changelogPath = join(contextDir, 'changelog.md');
1385
1376
 
1377
+ const m = getMessages(session.language || 'zh-CN');
1378
+
1386
1379
  // 读取 feature 文件提取最后一条工作记录作为摘要
1387
1380
  const taskContent = await readTaskFile(session, taskId);
1388
1381
  let summaryText = '';
@@ -1397,7 +1390,7 @@ async function appendChangelog(session, taskId, taskTitle) {
1397
1390
  }
1398
1391
  }
1399
1392
  if (!summaryText) {
1400
- summaryText = '(无详细摘要)';
1393
+ summaryText = m.noSummary;
1401
1394
  }
1402
1395
 
1403
1396
  // 限制摘要长度
@@ -1405,8 +1398,9 @@ async function appendChangelog(session, taskId, taskTitle) {
1405
1398
  summaryText = summaryText.substring(0, 497) + '...';
1406
1399
  }
1407
1400
 
1408
- const now = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' });
1409
- const entry = `\n## ${taskId}: ${taskTitle}\n- 完成时间: ${now}\n- 摘要: ${summaryText}\n`;
1401
+ const locale = (session.language === 'en') ? 'en-US' : 'zh-CN';
1402
+ const now = new Date().toLocaleString(locale, { timeZone: 'Asia/Shanghai' });
1403
+ const entry = `\n## ${taskId}: ${taskTitle}\n- ${m.completedAt}: ${now}\n- ${m.summaryLabel}: ${summaryText}\n`;
1410
1404
 
1411
1405
  // 如果文件不存在,先写 header
1412
1406
  let exists = false;
@@ -1416,7 +1410,7 @@ async function appendChangelog(session, taskId, taskTitle) {
1416
1410
  } catch {}
1417
1411
 
1418
1412
  if (!exists) {
1419
- await fs.writeFile(changelogPath, `# Changelog\n${entry}`);
1413
+ await fs.writeFile(changelogPath, `${m.changelogTitle}\n${entry}`);
1420
1414
  } else {
1421
1415
  await fs.appendFile(changelogPath, entry);
1422
1416
  }
@@ -1587,68 +1581,52 @@ function buildRoleSystemPrompt(role, session) {
1587
1581
  routeTargets = allRoles.filter(r => r.name !== role.name);
1588
1582
  }
1589
1583
 
1590
- let prompt = `# 团队协作
1591
- 你正在一个 AI 团队中工作。${session.goal ? `项目目标是: ${session.goal}` : '等待用户提出任务或问题。'}
1584
+ const m = getMessages(session.language || 'zh-CN');
1585
+
1586
+ let prompt = `${m.teamCollab}
1587
+ ${m.teamCollabIntro(session.goal)}
1592
1588
 
1593
- 团队成员:
1594
- ${allRoles.map(r => `- ${roleLabel(r)}: ${r.description}${r.isDecisionMaker ? ' (决策者)' : ''}`).join('\n')}`;
1589
+ ${m.teamMembers}
1590
+ ${allRoles.map(r => `- ${roleLabel(r)}: ${r.description}${r.isDecisionMaker ? ` (${m.decisionMakerTag})` : ''}`).join('\n')}`;
1595
1591
 
1596
1592
  const hasMultiInstance = allRoles.some(r => r.groupIndex > 0);
1597
1593
 
1598
1594
  if (routeTargets.length > 0) {
1599
- prompt += `\n\n# 路由规则
1600
- 当你完成当前任务并需要将结果传递给其他角色时,在你的回复最末尾添加一个 ROUTE 块:
1595
+ prompt += `\n\n${m.routingRules}
1596
+ ${m.routingIntro}
1601
1597
 
1602
1598
  \`\`\`
1603
1599
  ---ROUTE---
1604
- to: <角色name>
1605
- summary: <简要说明要传递什么>
1600
+ to: <roleName>
1601
+ summary: <brief description>
1606
1602
  ---END_ROUTE---
1607
1603
  \`\`\`
1608
1604
 
1609
- 可用的路由目标:
1605
+ ${m.routeTargets}
1610
1606
  ${routeTargets.map(r => `- ${r.name}: ${roleLabel(r)} — ${r.description}`).join('\n')}
1611
- - human: 人工(只在决策者也无法决定时使用)
1607
+ - human: ${m.humanTarget}
1612
1608
 
1613
- 注意:
1614
- - 如果你的工作还没完成,不需要添加 ROUTE 块
1615
- - 如果你遇到不确定的问题,@ 决策者 "${session.decisionMaker}",而不是直接 @ human
1616
- - 如果你是决策者且遇到需要人类判断的问题,才 @ human
1617
- - 可以一次发多个 ROUTE 块来并行分配任务给不同角色
1618
- - ROUTE 块必须在回复的最末尾
1619
- - 当你的任务已完成且不需要其他角色继续时,ROUTE 回决策者 "${session.decisionMaker}" 做总结
1620
- - 在正文中可用 @角色name 提及某个角色(如 @developer),但这不会触发路由,仅供阅读`;
1609
+ ${m.routeNotes(session.decisionMaker)}`;
1621
1610
  }
1622
1611
 
1623
1612
  // 决策者额外 prompt
1624
1613
  if (role.isDecisionMaker) {
1625
1614
  const isDevTeam = session.teamType === 'dev';
1626
1615
 
1627
- prompt += `\n\n# 工具使用
1628
- PM 可以使用所有工具,包括 Read、Grep、Glob、Bash、Edit、Write。${isDevTeam ? '代码文件的改动仍建议 ROUTE 给 developer 执行,但不做硬性限制。' : ''}`;
1616
+ prompt += `\n\n${m.toolUsage}
1617
+ ${m.toolUsageContent(isDevTeam)}`;
1629
1618
 
1630
- prompt += `\n\n# 决策者职责
1631
- 你是团队的决策者。其他角色遇到不确定的情况会请求你的决策。
1632
- - 如果你有足够的信息做出决策,直接决定并 @相关角色执行
1633
- - 如果你需要更多信息,@具体角色请求补充
1634
- - 如果问题超出你的能力范围或需要业务判断,@human 请人类决定
1635
- - 你可以随时审查其他角色的工作并给出反馈`;
1619
+ prompt += `\n\n${m.dmRole}
1620
+ ${m.dmRoleContent}`;
1636
1621
 
1637
1622
  if (isDevTeam) {
1638
- prompt += `
1639
- - PM 不做代码分析。收到需求后直接将原始需求 ROUTE 给空闲 dev 做技术分析,dev 分析完返回 PM,PM 再拆分任务并直接分配执行。
1640
- - PM 拥有 commit + push + tag 的自主权。只要修改没有大的 regression 影响(测试全通过),PM 可以自行决定 commit、push 和 tag,无需等待人工确认。只有当改动会直接影响对话交互逻辑时,才需要人工介入审核。`;
1623
+ prompt += m.dmDevExtra;
1641
1624
  }
1642
1625
 
1643
1626
  // 非开发团队:注入讨论模式 prompt
1644
1627
  if (!isDevTeam) {
1645
- prompt += `\n\n# 协作模式
1646
- 这是一个协作讨论团队,不走严格的 PM→执行→审查→测试 工作流。
1647
- - 角色之间可以自由讨论、相互请教、提出不同意见
1648
- - 不需要严格的"分配→执行→审查"流程,鼓励角色之间直接对话
1649
- - 当一个角色需要另一个角色的输入时,直接 ROUTE 给对方并说明需要什么
1650
- - 决策者负责把控整体方向和最终决策,但日常讨论不需要经过决策者中转
1651
- - 每次 ROUTE 仍建议包含 task 和 taskTitle 字段,用于消息按 feature 分组显示`;
1628
+ prompt += `\n\n${m.collabMode}
1629
+ ${m.collabModeContent}`;
1652
1630
  }
1653
1631
 
1654
1632
  // 多实例模式(仅开发团队使用):注入开发组状态和调度规则
@@ -1662,80 +1640,55 @@ PM 可以使用所有工具,包括 Read、Grep、Glob、Bash、Edit、Write。
1662
1640
  const state = session.roleStates.get(r.name);
1663
1641
  const busy = state?.turnActive;
1664
1642
  const task = state?.currentTask;
1665
- if (busy && task) return `${r.name}(忙:${task.taskId} ${task.taskTitle})`;
1666
- if (busy) return `${r.name}()`;
1667
- return `${r.name}(空闲)`;
1643
+ if (busy && task) return `${r.name}(${m.groupBusy(task.taskId + ' ' + task.taskTitle)})`;
1644
+ if (busy) return `${r.name}(${m.groupBusyShort})`;
1645
+ return `${r.name}(${m.groupIdle})`;
1668
1646
  });
1669
- groupLines.push(`组${g}: ${memberStrs.join(' ')}`);
1647
+ groupLines.push(`${m.groupLabel(g)}: ${memberStrs.join(' ')}`);
1670
1648
  }
1671
1649
 
1672
- prompt += `\n\n# 执行组状态
1650
+ prompt += `\n\n${m.execGroupStatus}
1673
1651
  ${groupLines.join(' / ')}
1674
1652
 
1675
- # 并行任务调度规则
1676
- 你有 ${maxGroup} 个开发组可以并行工作。拆分任务时:
1677
- 1. 每个子任务分配 task-id(如 task-1)和 taskTitle(如 "实现登录页面")
1678
- 2. 优先分配给**空闲**的开发组,避免给忙碌的 dev 发新任务
1679
- 3. 一次可以发**多个 ROUTE 块**来并行分配任务:
1653
+ ${m.parallelRules}
1654
+ ${m.parallelRulesContent(maxGroup)}
1680
1655
 
1681
1656
  \`\`\`
1682
1657
  ---ROUTE---
1683
1658
  to: dev-1
1684
1659
  task: task-1
1685
- taskTitle: 实现登录页面
1686
- summary: 请实现登录页面,包括表单验证和API调用
1660
+ taskTitle: ${m.implLoginPage}
1661
+ summary: ${m.implLoginSummary}
1687
1662
  ---END_ROUTE---
1688
1663
 
1689
1664
  ---ROUTE---
1690
1665
  to: dev-2
1691
1666
  task: task-2
1692
- taskTitle: 实现注册页面
1693
- summary: 请实现注册页面,包括邮箱验证
1667
+ taskTitle: ${m.implRegisterPage}
1668
+ summary: ${m.implRegisterSummary}
1694
1669
  ---END_ROUTE---
1695
1670
  \`\`\`
1696
1671
 
1697
- 4. 每个 dev 完成后会独立经过 reviewer 和 tester 审核,最后 ROUTE 回你
1698
- 5. 等待**所有子任务完成**后再做汇总报告
1699
- 6. **每次 ROUTE 都必须包含 task 和 taskTitle 字段,不能省略。没有 task 字段的 ROUTE 会导致消息无法按 feature 分组显示**`;
1672
+ ${m.parallelExample}`;
1700
1673
  }
1701
1674
 
1702
1675
  prompt += `\n
1703
- # 工作流终结点
1704
- 团队的工作流有明确的结束条件。当以下任一条件满足时,你应该给出总结并结束当前工作流:
1705
- ${isDevTeam ? '1. **代码已提交** - 所有代码修改已经 commit(如需要,可让 developer 执行 git commit)\n' : ''}${isDevTeam ? '2' : '1'}. **需要用户输入** - 遇到需要用户决定的问题时,@human 提出具体问题,等待用户回复
1706
- ${isDevTeam ? '3' : '2'}. **任务完成** - 所有任务已完成,给出完成总结(列出完成了什么${isDevTeam ? '、变更了哪些文件' : ''}、还有什么后续建议)
1676
+ ${m.workflowEnd}
1677
+ ${m.workflowEndContent(isDevTeam)}
1707
1678
 
1708
- 重要:不要无限循环地在角色之间传递。当工作实质性完成时,主动给出总结并结束。
1709
-
1710
- # 任务清单
1711
- 你可以在回复中添加 TASKS 块来发布/更新任务清单,团队界面会自动展示:
1679
+ ${m.taskList}
1680
+ ${m.taskListContent}
1712
1681
 
1713
1682
  \`\`\`
1714
- ---TASKS---
1715
- - [ ] 任务描述 #task-1 @角色name
1716
- - [x] 已完成的任务 #task-2 @角色name
1717
- ---END_TASKS---
1683
+ ${m.taskExample}
1718
1684
  \`\`\`
1719
1685
 
1720
- 注意:
1721
- - 每行一个任务,[ ] 表示待办,[x] 表示已完成
1722
- - #taskId 标注对应的 feature ID(如 #task-1),用于精确关联任务完成状态
1723
- - @角色name 标注负责人(可选)
1724
- - 后续回复中可更新 TASKS 块(标记完成的任务)
1725
- - TASKS 块不需要在回复最末尾,可以放在任意位置`;
1686
+ ${m.taskListNotes}`;
1726
1687
  }
1727
1688
 
1728
1689
  // Feature 进度文件说明(系统自动管理,告知角色即可)
1729
- prompt += `\n\n# Feature 工作记录
1730
- 系统会自动管理 \`context/features/{task-id}.md\` 工作记录文件:
1731
- - PM 分配任务时自动创建文件(包含 task-id、标题、需求描述)
1732
- - 每次 ROUTE 传递时自动追加工作记录(角色名、时间、summary)
1733
- - 你收到的消息中会包含 <task-context> 标签,里面是该任务的完整工作记录
1734
-
1735
- 系统还维护以下文件(自动更新,无需手动管理):
1736
- - \`context/features/index.md\`:所有 feature 的索引(进行中/已完成分类),快速查看项目状态
1737
- - \`context/changelog.md\`:已完成任务的变更记录,每个任务完成时自动追加摘要
1738
- 你不需要手动创建或更新这些文件,专注于你的本职工作即可。`;
1690
+ prompt += `\n\n${m.featureRecordTitle}
1691
+ ${m.featureRecordContent}`;
1739
1692
 
1740
1693
  // 执行者角色的组绑定 prompt(count > 1 时)
1741
1694
  if (role.groupIndex > 0 && role.roleType === 'developer') {
@@ -1743,44 +1696,45 @@ ${isDevTeam ? '3' : '2'}. **任务完成** - 所有任务已完成,给出完
1743
1696
  const rev = allRoles.find(r => r.roleType === 'reviewer' && r.groupIndex === gi);
1744
1697
  const test = allRoles.find(r => r.roleType === 'tester' && r.groupIndex === gi);
1745
1698
  if (rev && test) {
1746
- prompt += `\n\n# 开发组绑定
1747
- 你属于开发组 ${gi}。你的搭档:
1748
- - 审查者: ${roleLabel(rev)} (${rev.name})
1749
- - 测试: ${roleLabel(test)} (${test.name})
1750
-
1751
- 开发完成后,请同时发两个 ROUTE 块分别给 ${rev.name} 和 ${test.name}:
1699
+ prompt += `\n\n${m.devGroupBinding}
1700
+ ${m.devGroupBindingContent(gi, roleLabel(rev), rev.name, roleLabel(test), test.name)}
1752
1701
 
1753
1702
  \`\`\`
1754
1703
  ---ROUTE---
1755
1704
  to: ${rev.name}
1756
- summary: 请审查代码变更
1705
+ summary: ${m.reviewCode}
1757
1706
  ---END_ROUTE---
1758
1707
 
1759
1708
  ---ROUTE---
1760
1709
  to: ${test.name}
1761
- summary: 请测试功能
1710
+ summary: ${m.testFeature}
1762
1711
  ---END_ROUTE---
1763
1712
  \`\`\`
1764
1713
 
1765
- 两者会并行工作,各自完成后独立 ROUTE 回 PM。`;
1714
+ ${m.devGroupBindingNote}`;
1766
1715
  }
1767
1716
  }
1768
1717
 
1718
+ // Language instruction
1719
+ if (session.language === 'en') {
1720
+ prompt += `\n\n# Language
1721
+ Always respond in English. Use English for all explanations, comments, and communications with the user. Technical terms and code identifiers should remain in their original form.`;
1722
+ } else {
1723
+ prompt += `\n\n# Language
1724
+ Always respond in 中文. Use 中文 for all explanations, comments, and communications with the user. Technical terms and code identifiers should remain in their original form.`;
1725
+ }
1726
+
1769
1727
  return prompt;
1770
1728
  }
1729
+ function buildInitialTask(goal, firstRole, allRoles, language = 'zh-CN') {
1730
+ const m = getMessages(language);
1731
+ return `${m.projectStart}
1771
1732
 
1772
- /**
1773
- * 构建初始任务 prompt
1774
- */
1775
- function buildInitialTask(goal, firstRole, allRoles) {
1776
- return `项目启动!
1777
-
1778
- 目标: ${goal}
1733
+ ${m.goalLabel}: ${goal}
1779
1734
 
1780
- 你是第一个开始工作的角色。请分析目标,开始你的工作。
1781
- 完成后,通过 ROUTE 块将结果传递给下一个合适的角色。
1735
+ ${m.firstRoleInstruction}
1782
1736
 
1783
- 团队中可用的角色:
1737
+ ${m.availableRoles}
1784
1738
  ${allRoles.map(r => `- ${r.name}: ${roleLabel(r)} - ${r.description}`).join('\n')}`;
1785
1739
  }
1786
1740
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.0.209",
3
+ "version": "0.0.211",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",