claude-coder 1.8.3 → 1.9.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.
Files changed (64) hide show
  1. package/README.md +59 -12
  2. package/bin/cli.js +20 -37
  3. package/package.json +4 -1
  4. package/recipes/_shared/roles/developer.md +11 -0
  5. package/recipes/_shared/roles/product.md +12 -0
  6. package/recipes/_shared/roles/tester.md +12 -0
  7. package/recipes/_shared/test/report-format.md +86 -0
  8. package/recipes/backend/base.md +27 -0
  9. package/recipes/backend/components/auth.md +18 -0
  10. package/recipes/backend/components/crud-api.md +18 -0
  11. package/recipes/backend/components/file-service.md +15 -0
  12. package/recipes/backend/manifest.json +20 -0
  13. package/recipes/backend/test/api-test.md +25 -0
  14. package/recipes/console/base.md +37 -0
  15. package/recipes/console/components/modal-form.md +20 -0
  16. package/recipes/console/components/pagination.md +17 -0
  17. package/recipes/console/components/search.md +17 -0
  18. package/recipes/console/components/table-list.md +18 -0
  19. package/recipes/console/components/tabs.md +14 -0
  20. package/recipes/console/components/tree.md +15 -0
  21. package/recipes/console/components/upload.md +15 -0
  22. package/recipes/console/manifest.json +24 -0
  23. package/recipes/console/test/crud-e2e.md +47 -0
  24. package/recipes/h5/base.md +26 -0
  25. package/recipes/h5/components/animation.md +11 -0
  26. package/recipes/h5/components/countdown.md +11 -0
  27. package/recipes/h5/components/share.md +11 -0
  28. package/recipes/h5/components/swiper.md +11 -0
  29. package/recipes/h5/manifest.json +21 -0
  30. package/recipes/h5/test/h5-e2e.md +20 -0
  31. package/src/commands/auth.js +87 -15
  32. package/src/commands/setup-modules/helpers.js +4 -3
  33. package/src/commands/setup-modules/mcp.js +44 -24
  34. package/src/commands/setup-modules/safety.js +1 -15
  35. package/src/commands/setup.js +8 -8
  36. package/src/common/assets.js +10 -1
  37. package/src/common/config.js +2 -2
  38. package/src/common/indicator.js +158 -120
  39. package/src/common/utils.js +60 -8
  40. package/src/core/coding.js +16 -38
  41. package/src/core/go.js +31 -77
  42. package/src/core/hooks.js +56 -89
  43. package/src/core/init.js +94 -100
  44. package/src/core/plan.js +85 -223
  45. package/src/core/prompts.js +36 -16
  46. package/src/core/repair.js +7 -17
  47. package/src/core/runner.js +306 -43
  48. package/src/core/scan.js +38 -34
  49. package/src/core/session.js +253 -39
  50. package/src/core/simplify.js +45 -24
  51. package/src/core/state.js +105 -0
  52. package/src/index.js +76 -0
  53. package/templates/codingSystem.md +2 -2
  54. package/templates/codingUser.md +1 -1
  55. package/templates/guidance.json +22 -3
  56. package/templates/planSystem.md +2 -2
  57. package/templates/scanSystem.md +3 -3
  58. package/templates/scanUser.md +1 -1
  59. package/templates/web-testing.md +17 -0
  60. package/types/index.d.ts +217 -0
  61. package/src/core/context.js +0 -117
  62. package/src/core/harness.js +0 -484
  63. package/src/core/query.js +0 -50
  64. package/templates/playwright.md +0 -17
@@ -0,0 +1,15 @@
1
+ # 树形结构
2
+
3
+ ## 任务分解指导
4
+ 树形通常与列表替换使用,作为独立 frontend 任务。
5
+
6
+ ## 实现要点
7
+ - 树形数据格式:id + parentId 或嵌套 children
8
+ - 展开/折叠:默认展开第一级
9
+ - 懒加载:大数据量时按需加载子节点
10
+ - 操作:点击节点 → 右侧显示详情或触发其他交互
11
+ - 搜索/过滤:树节点过滤 + 高亮匹配节点
12
+
13
+ ## 验证策略
14
+ - snapshot 验证树形结构渲染
15
+ - 点击展开 → 验证子节点显示
@@ -0,0 +1,15 @@
1
+ # 文件上传
2
+
3
+ ## 任务分解指导
4
+ 文件上传通常嵌入弹窗表单中,不单独拆任务。如上传逻辑较复杂(多文件、进度条、预览),可拆为独立 frontend 任务。
5
+
6
+ ## 实现要点
7
+ - 文件类型限制:accept 属性 + 前端校验
8
+ - 文件大小限制:前端校验 + 后端校验
9
+ - 上传进度:progress bar(XMLHttpRequest 或 fetch + ReadableStream)
10
+ - 上传成功后:显示文件名/预览图 + 返回 fileId
11
+ - 多文件上传:列表展示已上传文件,支持删除
12
+
13
+ ## 验证策略
14
+ - 选择超限文件 → 验证拒绝提示
15
+ - 上传正确文件 → 验证成功提示 + 预览显示
@@ -0,0 +1,24 @@
1
+ {
2
+ "id": "console",
3
+ "name": "管理后台",
4
+ "description": "企业级管理后台页面开发(搜索、列表、分页、弹窗等 CRUD 场景)",
5
+ "base": "base.md",
6
+ "components": [
7
+ { "id": "search", "label": "搜索框", "description": "关键词搜索、筛选条件、防抖", "file": "components/search.md", "default": true },
8
+ { "id": "table", "label": "数据表格", "description": "列定义、排序、行选择、批量操作", "file": "components/table-list.md", "default": true },
9
+ { "id": "pagination", "label": "分页", "description": "分页导航、每页条数、总数显示", "file": "components/pagination.md", "default": true },
10
+ { "id": "modal", "label": "弹窗/表单", "description": "新建/编辑弹窗、表单校验、提交回填", "file": "components/modal-form.md", "default": true },
11
+ { "id": "upload", "label": "文件上传", "description": "文件类型限制、大小限制、上传进度", "file": "components/upload.md", "default": false },
12
+ { "id": "tree", "label": "树形结构", "description": "树形数据展示、懒加载、展开折叠", "file": "components/tree.md", "default": false },
13
+ { "id": "tabs", "label": "标签页", "description": "多标签切换、路由联动、懒加载", "file": "components/tabs.md", "default": false }
14
+ ],
15
+ "test": {
16
+ "file": "test/crud-e2e.md",
17
+ "defaultEnabled": true
18
+ },
19
+ "defaults": {
20
+ "components": ["search", "table", "pagination", "modal"],
21
+ "role": "developer",
22
+ "test": true
23
+ }
24
+ }
@@ -0,0 +1,47 @@
1
+ # Console CRUD E2E 测试步骤模板
2
+
3
+ ## 任务分解指导
4
+ E2E 测试作为独立 test 类任务。steps 用优先级标签标记。
5
+
6
+ ## 标准测试用例
7
+
8
+ ### 【P0】列表加载
9
+ 1. 导航到列表页
10
+ 2. Playwright snapshot 验证表格存在
11
+ 3. 验证至少有 1 行数据(或空状态文案)
12
+
13
+ ### 【P0】搜索功能
14
+ 1. 在搜索框输入已知关键词
15
+ 2. 等待列表更新(waitForLoadState)
16
+ 3. snapshot 验证结果数量变化
17
+
18
+ ### 【P1】分页
19
+ 1. 点击第 2 页
20
+ 2. 验证页码高亮变化
21
+ 3. 验证数据不同于第 1 页
22
+
23
+ ### 【P0】新建
24
+ 1. 点击新建按钮
25
+ 2. snapshot 验证弹窗打开
26
+ 3. 填写表单各字段
27
+ 4. 提交
28
+ 5. 验证弹窗关闭 + 列表出现新数据
29
+
30
+ ### 【P1】编辑
31
+ 1. 点击某行编辑按钮
32
+ 2. 验证弹窗打开且回填数据正确
33
+ 3. 修改某字段,提交
34
+ 4. 验证列表数据更新
35
+
36
+ ### 【P1】删除
37
+ 1. 点击删除按钮
38
+ 2. 验证确认弹窗出现
39
+ 3. 确认删除
40
+ 4. 验证列表数据减少
41
+
42
+ ### 【P2】表单校验
43
+ 1. 打开新建弹窗
44
+ 2. 不填必填项直接提交
45
+ 3. 验证校验提示出现
46
+ 4. 填入格式错误的数据(如邮箱格式)
47
+ 5. 验证格式校验提示
@@ -0,0 +1,26 @@
1
+ # H5 活动页 — 基础食谱
2
+
3
+ ## 任务分解模式
4
+
5
+ H5 活动页按以下标准拆分:
6
+
7
+ 1. **infra**: 项目脚手架(如未有移动端模板,需要初始化)
8
+ - steps:脚手架搭建、移动端适配(viewport + rem/vw)、基础样式
9
+ - 验证:页面在移动端视口正常显示
10
+
11
+ 2. **frontend**: 页面主体(布局 + 核心交互组件)
12
+ - steps:页面结构、组件集成、数据对接
13
+ - 验证:Playwright 移动端 viewport snapshot
14
+
15
+ 3. **frontend**: 分享/营销功能(如需要)
16
+ - steps:分享配置、海报生成、统计埋点
17
+ - 验证:分享链接正确、海报生成成功
18
+
19
+ 4. **test**: 多端兼容测试
20
+ - steps:iOS Safari、Android Chrome、微信内置浏览器
21
+
22
+ ## 通用规则
23
+
24
+ - H5 页面通常较轻量,2-3 个任务即可
25
+ - 优先保证移动端适配(viewport meta + 响应式)
26
+ - 注意性能:图片懒加载、动画性能、首屏加载速度
@@ -0,0 +1,11 @@
1
+ # 动画效果
2
+
3
+ ## 实现要点
4
+ - 入场动画:元素进入视口时触发(Intersection Observer)
5
+ - 滚动动画:视差滚动、进度条等
6
+ - 交互动效:点击反馈、状态切换过渡
7
+ - 性能:使用 transform/opacity 动画,避免触发 layout
8
+
9
+ ## 验证策略
10
+ - snapshot 验证动画元素存在
11
+ - 滚动页面 → 验证动画触发
@@ -0,0 +1,11 @@
1
+ # 倒计时组件
2
+
3
+ ## 实现要点
4
+ - 服务端时间同步(避免客户端时间篡改)
5
+ - 天/时/分/秒 格式化显示
6
+ - 倒计时结束回调(如切换页面状态)
7
+ - requestAnimationFrame 或 setInterval 驱动
8
+
9
+ ## 验证策略
10
+ - snapshot 验证倒计时数字显示
11
+ - 等待数秒 → 验证数字递减
@@ -0,0 +1,11 @@
1
+ # 分享功能
2
+
3
+ ## 实现要点
4
+ - 微信 JS-SDK 分享配置(标题、描述、图片、链接)
5
+ - 浏览器原生 Web Share API 兜底
6
+ - 分享海报生成(html2canvas / canvas 绘制)
7
+ - 分享统计埋点
8
+
9
+ ## 验证策略
10
+ - 验证分享按钮可点击
11
+ - 验证海报生成成功(如适用)
@@ -0,0 +1,11 @@
1
+ # 轮播组件
2
+
3
+ ## 实现要点
4
+ - 图片/卡片横向轮播,支持触摸滑动
5
+ - 自动播放 + 手动暂停
6
+ - 指示器(圆点或数字)
7
+ - 图片懒加载,优化首屏性能
8
+
9
+ ## 验证策略
10
+ - snapshot 验证轮播容器和指示器存在
11
+ - 滑动/点击 → 验证切换到下一张
@@ -0,0 +1,21 @@
1
+ {
2
+ "id": "h5",
3
+ "name": "H5 活动页",
4
+ "description": "移动端 H5 活动页面开发(轮播、倒计时、分享、动画等营销场景)",
5
+ "base": "base.md",
6
+ "components": [
7
+ { "id": "swiper", "label": "轮播", "description": "图片/卡片轮播、自动播放、指示器", "file": "components/swiper.md", "default": true },
8
+ { "id": "countdown", "label": "倒计时", "description": "活动倒计时、服务端时间同步", "file": "components/countdown.md", "default": false },
9
+ { "id": "share", "label": "分享", "description": "微信/浏览器分享、海报生成", "file": "components/share.md", "default": true },
10
+ { "id": "animation", "label": "动画效果", "description": "入场动画、滚动动画、交互动效", "file": "components/animation.md", "default": false }
11
+ ],
12
+ "test": {
13
+ "file": "test/h5-e2e.md",
14
+ "defaultEnabled": true
15
+ },
16
+ "defaults": {
17
+ "components": ["swiper", "share"],
18
+ "role": "developer",
19
+ "test": true
20
+ }
21
+ }
@@ -0,0 +1,20 @@
1
+ # H5 活动页 E2E 测试步骤模板
2
+
3
+ ## 标准测试用例
4
+
5
+ ### 【P0】页面加载
6
+ 1. 使用移动端 viewport 导航到页面
7
+ 2. snapshot 验证页面主体内容渲染
8
+ 3. 验证无 JS 报错(console.error)
9
+
10
+ ### 【P0】核心交互
11
+ 1. 操作页面核心功能(如轮播滑动、按钮点击)
12
+ 2. 验证交互响应正确
13
+
14
+ ### 【P1】分享功能
15
+ 1. 点击分享按钮
16
+ 2. 验证分享面板/海报出现
17
+
18
+ ### 【P1】适配验证
19
+ 1. 切换不同 viewport(iPhone SE / iPhone 14 / Android)
20
+ 2. 验证布局无溢出、元素无遮挡
@@ -41,7 +41,11 @@ function updateGitignore(entry) {
41
41
  }
42
42
  }
43
43
 
44
- function updateMcpConfig(mcpPath, mode) {
44
+ // ─────────────────────────────────────────────────────────────
45
+ // .mcp.json 配置(Playwright / Chrome DevTools 共用)
46
+ // ─────────────────────────────────────────────────────────────
47
+
48
+ function updateMcpConfig(mcpPath, tool, mode) {
45
49
  let mcpConfig = {};
46
50
  if (fs.existsSync(mcpPath)) {
47
51
  try { mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf8')); } catch {}
@@ -49,6 +53,19 @@ function updateMcpConfig(mcpPath, mode) {
49
53
 
50
54
  if (!mcpConfig.mcpServers) mcpConfig.mcpServers = {};
51
55
 
56
+ if (tool === 'chrome-devtools') {
57
+ delete mcpConfig.mcpServers.playwright;
58
+ mcpConfig.mcpServers['chrome-devtools'] = {
59
+ command: 'npx',
60
+ args: ['-y', 'chrome-devtools-mcp@latest', '--autoConnect'],
61
+ };
62
+ fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2) + '\n', 'utf8');
63
+ log('ok', '.mcp.json 已配置 Chrome DevTools MCP (autoConnect)');
64
+ return;
65
+ }
66
+
67
+ // Playwright MCP
68
+ delete mcpConfig.mcpServers['chrome-devtools'];
52
69
  const args = ['@playwright/mcp@latest'];
53
70
  const projectRoot = assets.projectRoot;
54
71
 
@@ -75,23 +92,23 @@ function updateMcpConfig(mcpPath, mode) {
75
92
  log('ok', `.mcp.json 已配置 Playwright MCP (${mode} 模式)`);
76
93
  }
77
94
 
78
- function enableMcpPlaywrightEnv() {
95
+ function enableWebTestEnv(tool) {
79
96
  const envPath = assets.path('env');
80
97
  if (!envPath || !fs.existsSync(envPath)) return;
81
98
 
82
99
  let content = fs.readFileSync(envPath, 'utf8');
83
- if (/^MCP_PLAYWRIGHT=/m.test(content)) {
84
- content = content.replace(/^MCP_PLAYWRIGHT=.*/m, 'MCP_PLAYWRIGHT=true');
100
+ if (/^WEB_TEST_TOOL=/m.test(content)) {
101
+ content = content.replace(/^WEB_TEST_TOOL=.*/m, `WEB_TEST_TOOL=${tool}`);
85
102
  } else {
86
103
  const suffix = content.endsWith('\n') ? '' : '\n';
87
- content += `${suffix}MCP_PLAYWRIGHT=true\n`;
104
+ content += `${suffix}WEB_TEST_TOOL=${tool}\n`;
88
105
  }
89
106
  fs.writeFileSync(envPath, content, 'utf8');
90
- log('ok', '.claude-coder/.env 已设置 MCP_PLAYWRIGHT=true');
107
+ log('ok', `.claude-coder/.env 已设置 WEB_TEST_TOOL=${tool}`);
91
108
  }
92
109
 
93
110
  // ─────────────────────────────────────────────────────────────
94
- // 浏览器脚本(session cookie 自动持久化)
111
+ // 浏览器脚本(Playwright persistent 模式用)
95
112
  // ─────────────────────────────────────────────────────────────
96
113
 
97
114
  function buildBrowserScript(playwrightDir, profileDir, url) {
@@ -185,9 +202,9 @@ async function authPersistent(url) {
185
202
 
186
203
  const mcpPath = assets.path('mcpConfig');
187
204
  log('ok', '登录状态已保存到持久化配置');
188
- updateMcpConfig(mcpPath, 'persistent');
205
+ updateMcpConfig(mcpPath, 'playwright', 'persistent');
189
206
  updateGitignore('.claude-coder/.runtime/browser-profile');
190
- enableMcpPlaywrightEnv();
207
+ enableWebTestEnv('playwright');
191
208
 
192
209
  console.log('');
193
210
  log('ok', '配置完成!');
@@ -227,9 +244,9 @@ async function authIsolated(url) {
227
244
 
228
245
  const mcpPath = assets.path('mcpConfig');
229
246
  log('ok', '登录状态已保存到 playwright-auth.json');
230
- updateMcpConfig(mcpPath, 'isolated');
247
+ updateMcpConfig(mcpPath, 'playwright', 'isolated');
231
248
  updateGitignore('.claude-coder/playwright-auth.json');
232
- enableMcpPlaywrightEnv();
249
+ enableWebTestEnv('playwright');
233
250
 
234
251
  console.log('');
235
252
  log('ok', '配置完成!');
@@ -251,8 +268,8 @@ function authExtension() {
251
268
  console.log('');
252
269
 
253
270
  const mcpPath = assets.path('mcpConfig');
254
- updateMcpConfig(mcpPath, 'extension');
255
- enableMcpPlaywrightEnv();
271
+ updateMcpConfig(mcpPath, 'playwright', 'extension');
272
+ enableWebTestEnv('playwright');
256
273
 
257
274
  console.log('');
258
275
  log('ok', '配置完成!');
@@ -260,13 +277,68 @@ function authExtension() {
260
277
  log('info', '确保 Chrome/Edge 已运行且 Playwright MCP Bridge 扩展已启用');
261
278
  }
262
279
 
280
+ function authChromeDevTools() {
281
+ console.log('Chrome DevTools MCP 配置:');
282
+ console.log('');
283
+ console.log(' 此模式通过 Chrome DevTools Protocol 连接到已打开的 Chrome 浏览器。');
284
+ console.log(' 直接复用浏览器中已有的登录态、扩展和 DevTools 调试能力。');
285
+ console.log('');
286
+ console.log(' 前置条件:');
287
+ console.log(' 1. Node.js v20.19+(npx 自动下载 chrome-devtools-mcp 包)');
288
+ console.log(' 2. Chrome 144+ 版本');
289
+ console.log(' 3. 打开 chrome://inspect/#remote-debugging 启用远程调试');
290
+ console.log(' 4. 允许传入调试连接');
291
+ console.log('');
292
+ console.log(' 功能:');
293
+ console.log(' - 输入自动化: 点击、输入、表单填写');
294
+ console.log(' - 页面导航: 多页面管理、截图');
295
+ console.log(' - 性能分析: Trace 录制、Core Web Vitals、Lighthouse 审计');
296
+ console.log(' - 调试工具: Console 消息、网络请求检查、内存快照');
297
+ console.log('');
298
+ console.log(` 注意: 单实例限制,同一时间只能连接一个 Chrome 调试会话。`);
299
+ console.log(` 如需多实例并行,请配置 Playwright MCP(claude-coder setup)。`);
300
+ console.log('');
301
+
302
+ const mcpPath = assets.path('mcpConfig');
303
+ updateMcpConfig(mcpPath, 'chrome-devtools');
304
+ enableWebTestEnv('chrome-devtools');
305
+
306
+ console.log('');
307
+ log('ok', '配置完成!');
308
+ log('info', 'Chrome DevTools MCP 使用 autoConnect 模式');
309
+ log('info', '确保 Chrome 已启动且已在 chrome://inspect 中启用远程调试');
310
+ }
311
+
263
312
  async function auth(url) {
264
313
  assets.ensureDirs();
265
314
  const config = loadConfig();
266
- const mode = config.playwrightMode;
315
+ const tool = config.webTestTool;
316
+
317
+ if (!tool) {
318
+ log('error', '未配置浏览器测试工具');
319
+ log('info', '请先运行 claude-coder setup 选择 Playwright MCP 或 Chrome DevTools MCP');
320
+ return;
321
+ }
322
+
323
+ if (tool === 'chrome-devtools') {
324
+ const [major, minor] = process.versions.node.split('.').map(Number);
325
+ if (major < 20 || (major === 20 && minor < 19)) {
326
+ log('warn', `当前 Node.js 版本 v${process.versions.node},Chrome DevTools MCP 要求 v20.19+`);
327
+ log('info', 'nvm 用户请执行: nvm alias default 22 && nvm use 22');
328
+ log('info', '升级后重新运行此命令');
329
+ return;
330
+ }
331
+ log('info', '浏览器工具: Chrome DevTools MCP');
332
+ console.log('');
333
+ authChromeDevTools();
334
+ return;
335
+ }
336
+
337
+ // Playwright MCP
338
+ const mode = config.webTestMode;
267
339
  const targetUrl = normalizeUrl(url) || 'http://localhost:3000';
268
340
 
269
- log('info', `Playwright 模式: ${mode}`);
341
+ log('info', `浏览器工具: Playwright MCP (${mode} 模式)`);
270
342
  log('info', `目标 URL: ${targetUrl}`);
271
343
  console.log('');
272
344
 
@@ -76,11 +76,12 @@ function showCurrentConfig(existing) {
76
76
  console.log(` 提供商: ${existing.MODEL_PROVIDER || '未配置'}`);
77
77
  console.log(` BASE_URL: ${existing.ANTHROPIC_BASE_URL || '默认'}`);
78
78
  console.log(` 模型: ${existing.ANTHROPIC_MODEL || '默认'}`);
79
- console.log(` MCP: ${existing.MCP_PLAYWRIGHT === 'true' ? `已启用 (${existing.MCP_PLAYWRIGHT_MODE || 'persistent'})` : '未启用'}`);
80
- const compTimeout = existing.SESSION_COMPLETION_TIMEOUT || '300';
79
+ const webTool = existing.WEB_TEST_TOOL;
80
+ const webMode = existing.WEB_TEST_MODE;
81
+ console.log(` 浏览器工具: ${webTool ? `${webTool}${webMode ? ` (${webMode})` : ''}` : '未启用'}`);
81
82
  const turns = existing.SESSION_MAX_TURNS || '0';
82
83
  console.log(` 停顿超时: ${existing.SESSION_STALL_TIMEOUT || '600'} 秒`);
83
- console.log(` 完成检测: ${compTimeout} 秒`);
84
+ console.log(` 完成检测: Stop hook(SDK 原生)`);
84
85
  console.log(` 工具轮次: ${turns === '0' ? '无限制' : turns}`);
85
86
  const simplifyInterval = existing.SIMPLIFY_INTERVAL ?? '5';
86
87
  const simplifyCommits = existing.SIMPLIFY_COMMITS ?? '5';
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { ask, askChoice } = require('./helpers');
3
+ const { askChoice } = require('./helpers');
4
4
  const { log, COLOR, updateEnvVar } = require('../../common/config');
5
5
  const { assets } = require('../../common/assets');
6
6
 
@@ -8,34 +8,31 @@ const { assets } = require('../../common/assets');
8
8
 
9
9
  async function configureMCP(rl) {
10
10
  console.log('');
11
- console.log('是否启用 Playwright MCP(浏览器自动化测试)?');
11
+ console.log('是否启用浏览器测试工具?');
12
12
  console.log('');
13
- console.log(' Playwright MCP 由微软官方维护 (github.com/microsoft/playwright-mcp)');
14
- console.log(' 提供 browser_click、browser_snapshot 25+ 浏览器自动化工具');
15
- console.log(' 适用于有 Web 前端的项目,Agent 可用它做端到端测试');
16
- console.log('');
17
- console.log(' 1) 是 - 启用 Playwright MCP(项目有 Web 前端)');
18
- console.log(' 2) 否 - 跳过(纯后端 / CLI 项目)');
13
+ console.log(' 1) Playwright MCP 微软官方,25+ 浏览器自动化工具,支持多实例并行');
14
+ console.log(' 2) Chrome DevTools MCP — Google 官方,连接已打开的 Chrome,调试能力更强');
15
+ console.log(' (单实例限制,多开请配置 Playwright MCP)');
16
+ console.log(' 3) 跳过(纯后端 / CLI 项目)');
19
17
  console.log('');
20
18
 
21
- const mcpChoice = await askChoice(rl, '选择 [1-2]: ', 1, 2);
19
+ const toolChoice = await askChoice(rl, '选择 [1-3]: ', 1, 3);
22
20
 
23
- const mcpConfig = { enabled: false, mode: null };
21
+ const mcpConfig = { tool: '', mode: '' };
24
22
 
25
- if (mcpChoice === 1) {
26
- mcpConfig.enabled = true;
23
+ if (toolChoice === 1) {
24
+ mcpConfig.tool = 'playwright';
27
25
  console.log('');
28
26
  console.log('请选择 Playwright MCP 浏览器模式:');
29
27
  console.log('');
30
28
  console.log(' 1) persistent - 懒人模式(默认,推荐)');
31
- console.log(' 登录一次永久生效,适合 Google SSO、企业内网 API 拉取等日常开发');
29
+ console.log(' 登录一次永久生效,适合 Google SSO、企业内网等日常开发');
32
30
  console.log('');
33
31
  console.log(' 2) isolated - 开发模式');
34
32
  console.log(' 每次会话从快照加载,适合验证登录流程的自动化测试');
35
33
  console.log('');
36
34
  console.log(' 3) extension - 连接真实浏览器(实验性)');
37
35
  console.log(' 通过 Chrome 扩展复用已有登录态和插件');
38
- console.log(' 需要安装 "Playwright MCP Bridge" 扩展');
39
36
  console.log('');
40
37
 
41
38
  const modeChoice = await askChoice(rl, '选择 [1-3,默认 1]: ', 1, 3, 1);
@@ -58,6 +55,25 @@ async function configureMCP(rl) {
58
55
  console.log(' 使用 claude-coder auth <URL> 录制登录状态到 playwright-auth.json');
59
56
  console.log(' MCP 每次会话从此文件加载初始 cookies/localStorage');
60
57
  }
58
+ } else if (toolChoice === 2) {
59
+ mcpConfig.tool = 'chrome-devtools';
60
+
61
+ const [major, minor] = process.versions.node.split('.').map(Number);
62
+ if (major < 20 || (major === 20 && minor < 19)) {
63
+ console.log('');
64
+ console.log(` ${COLOR.yellow}⚠ 当前 Node.js v${process.versions.node},Chrome DevTools MCP 要求 v20.19+${COLOR.reset}`);
65
+ console.log(` ${COLOR.blue} nvm 用户: nvm alias default 22 && nvm use 22${COLOR.reset}`);
66
+ }
67
+
68
+ console.log('');
69
+ console.log(' Chrome DevTools MCP 将连接已打开的 Chrome 浏览器。');
70
+ console.log('');
71
+ console.log(' 前置条件:');
72
+ console.log(' 1. Node.js v20.19+(npx 自动下载 chrome-devtools-mcp 包)');
73
+ console.log(' 2. Chrome 144+');
74
+ console.log(' 3. 打开 chrome://inspect/#remote-debugging 启用远程调试');
75
+ console.log('');
76
+ console.log(' 运行 claude-coder auth 自动配置 .mcp.json');
61
77
  }
62
78
 
63
79
  return mcpConfig;
@@ -66,12 +82,12 @@ async function configureMCP(rl) {
66
82
  // ── MCP 配置追加到 lines ──
67
83
 
68
84
  function appendMcpConfig(lines, mcpConfig) {
69
- lines.push('', '# MCP 工具配置');
70
- if (mcpConfig.enabled) {
71
- lines.push('MCP_PLAYWRIGHT=true');
72
- if (mcpConfig.mode) lines.push(`MCP_PLAYWRIGHT_MODE=${mcpConfig.mode}`);
85
+ lines.push('', '# 浏览器测试工具配置');
86
+ if (mcpConfig.tool) {
87
+ lines.push(`WEB_TEST_TOOL=${mcpConfig.tool}`);
88
+ if (mcpConfig.mode) lines.push(`WEB_TEST_MODE=${mcpConfig.mode}`);
73
89
  } else {
74
- lines.push('MCP_PLAYWRIGHT=false');
90
+ lines.push('WEB_TEST_TOOL=');
75
91
  }
76
92
  }
77
93
 
@@ -79,11 +95,15 @@ function appendMcpConfig(lines, mcpConfig) {
79
95
 
80
96
  async function updateMCPOnly(rl) {
81
97
  const mcpConfig = await configureMCP(rl);
82
- updateEnvVar('MCP_PLAYWRIGHT', mcpConfig.enabled ? 'true' : 'false');
83
- if (mcpConfig.enabled && mcpConfig.mode) {
84
- updateEnvVar('MCP_PLAYWRIGHT_MODE', mcpConfig.mode);
98
+ updateEnvVar('WEB_TEST_TOOL', mcpConfig.tool);
99
+ if (mcpConfig.tool === 'playwright' && mcpConfig.mode) {
100
+ updateEnvVar('WEB_TEST_MODE', mcpConfig.mode);
101
+ const { updateMcpConfig } = require('../auth');
102
+ updateMcpConfig(assets.path('mcpConfig'), 'playwright', mcpConfig.mode);
103
+ } else if (mcpConfig.tool === 'chrome-devtools') {
104
+ updateEnvVar('WEB_TEST_MODE', '');
85
105
  const { updateMcpConfig } = require('../auth');
86
- updateMcpConfig(assets.path('mcpConfig'), mcpConfig.mode);
106
+ updateMcpConfig(assets.path('mcpConfig'), 'chrome-devtools');
87
107
  }
88
108
  log('ok', 'MCP 配置已更新');
89
109
  }
@@ -92,4 +112,4 @@ module.exports = {
92
112
  configureMCP,
93
113
  appendMcpConfig,
94
114
  updateMCPOnly,
95
- };
115
+ };
@@ -7,16 +7,14 @@ const { log, COLOR, updateEnvVar } = require('../../common/config');
7
7
 
8
8
  async function updateSafetyLimits(rl, existing) {
9
9
  const currentStall = existing.SESSION_STALL_TIMEOUT || '600';
10
- const currentCompletion = existing.SESSION_COMPLETION_TIMEOUT || '300';
11
10
  const currentTurns = existing.SESSION_MAX_TURNS || '0';
12
11
 
13
12
  console.log(`${COLOR.blue}当前安全限制:${COLOR.reset}`);
14
13
  console.log(` 停顿超时: ${currentStall} 秒 (${Math.floor(parseInt(currentStall) / 60)} 分钟)`);
15
- console.log(` 完成检测超时: ${currentCompletion} 秒 (${Math.ceil(parseInt(currentCompletion) / 60)} 分钟)`);
16
14
  console.log(` 最大工具轮次: ${currentTurns === '0' ? '无限制' : currentTurns}`);
17
15
  console.log('');
18
16
  console.log(`${COLOR.yellow}说明:${COLOR.reset}`);
19
- console.log(' 完成检测 — 模型写入 session_result.json 后缩短等待,解决"完成但不退出"');
17
+ console.log(' 完成检测 — 通过 SDK Stop hook 感知模型结束,无需额外超时');
20
18
  console.log(' 停顿超时 — 长时间无工具调用时自动中断(通用兜底)');
21
19
  console.log(' 最大轮次 — 限制总轮次,仅 CI 推荐(默认 0 = 无限制)');
22
20
  console.log('');
@@ -32,18 +30,6 @@ async function updateSafetyLimits(rl, existing) {
32
30
  }
33
31
  }
34
32
 
35
- console.log('');
36
- const compInput = await ask(rl, `完成检测超时秒数(回车保留 ${currentCompletion}): `);
37
- if (compInput.trim()) {
38
- const seconds = parseInt(compInput.trim(), 10);
39
- if (isNaN(seconds) || seconds < 30) {
40
- log('warn', '完成检测超时需 >= 30 秒,跳过');
41
- } else {
42
- updateEnvVar('SESSION_COMPLETION_TIMEOUT', String(seconds));
43
- log('ok', `完成检测超时已设置为 ${seconds} 秒`);
44
- }
45
- }
46
-
47
33
  console.log('');
48
34
  const turnsInput = await ask(rl, `最大工具轮次(回车保留 ${currentTurns === '0' ? '无限制' : currentTurns},输入 0 = 无限制): `);
49
35
  if (turnsInput.trim()) {
@@ -20,7 +20,7 @@ const {
20
20
  } = require('./setup-modules');
21
21
 
22
22
  const PRESERVED_KEYS = [
23
- 'SESSION_STALL_TIMEOUT', 'SESSION_COMPLETION_TIMEOUT',
23
+ 'SESSION_STALL_TIMEOUT',
24
24
  'SESSION_MAX_TURNS', 'SIMPLIFY_INTERVAL', 'SIMPLIFY_COMMITS',
25
25
  ];
26
26
 
@@ -61,10 +61,10 @@ async function setup() {
61
61
  writeConfig(envPath, configResult.lines);
62
62
  ensureGitignore();
63
63
 
64
- if (mcpConfig.enabled && mcpConfig.mode) {
64
+ if (mcpConfig.tool) {
65
65
  const { updateMcpConfig } = require('./auth');
66
66
  const mcpPath = assets.path('mcpConfig');
67
- updateMcpConfig(mcpPath, mcpConfig.mode);
67
+ updateMcpConfig(mcpPath, mcpConfig.tool, mcpConfig.mode);
68
68
  }
69
69
 
70
70
  console.log('');
@@ -98,7 +98,7 @@ async function setup() {
98
98
  console.log('');
99
99
  console.log(' 1) 切换模型提供商');
100
100
  console.log(' 2) 更新 API Key');
101
- console.log(' 3) 配置 MCP');
101
+ console.log(' 3) 配置浏览器测试工具');
102
102
  console.log(' 4) 配置安全限制');
103
103
  console.log(' 5) 配置自动审查');
104
104
  console.log(' 6) 完全重新配置');
@@ -119,8 +119,8 @@ async function setup() {
119
119
  const configResult = await selectProvider(rl, existing);
120
120
  preserveSafetyConfig(configResult.lines, existing);
121
121
  appendMcpConfig(configResult.lines, {
122
- enabled: existing.MCP_PLAYWRIGHT === 'true',
123
- mode: existing.MCP_PLAYWRIGHT_MODE || null,
122
+ tool: existing.WEB_TEST_TOOL || '',
123
+ mode: existing.WEB_TEST_MODE || '',
124
124
  });
125
125
  writeConfig(envPath, configResult.lines);
126
126
  log('ok', `已切换到: ${configResult.summary}`);
@@ -150,10 +150,10 @@ async function setup() {
150
150
  appendMcpConfig(configResult.lines, mcpConfig);
151
151
  writeConfig(envPath, configResult.lines);
152
152
 
153
- if (mcpConfig.enabled && mcpConfig.mode) {
153
+ if (mcpConfig.tool) {
154
154
  const { updateMcpConfig } = require('./auth');
155
155
  const mcpPath = assets.path('mcpConfig');
156
- updateMcpConfig(mcpPath, mcpConfig.mode);
156
+ updateMcpConfig(mcpPath, mcpConfig.tool, mcpConfig.mode);
157
157
  }
158
158
 
159
159
  log('ok', '配置已更新');
@@ -26,7 +26,7 @@ const REGISTRY = new Map([
26
26
  // Other Templates
27
27
  ['testRule', { file: 'test_rule.md', kind: 'template' }],
28
28
  ['guidance', { file: 'guidance.json', kind: 'template' }],
29
- ['playwright', { file: 'playwright.md', kind: 'template' }],
29
+ ['webTesting', { file: 'web-testing.md', kind: 'template' }],
30
30
  ['bashProcess', { file: 'bash-process.md', kind: 'template' }],
31
31
  ['requirements', { file: 'requirements.example.md', kind: 'template' }],
32
32
 
@@ -226,6 +226,15 @@ class AssetManager {
226
226
  return deployed;
227
227
  }
228
228
 
229
+ recipesDir() {
230
+ this._ensureInit();
231
+ const projectRecipes = path.join(this.loopDir, 'recipes');
232
+ if (fs.existsSync(projectRecipes) && fs.readdirSync(projectRecipes).length > 0) {
233
+ return projectRecipes;
234
+ }
235
+ return BUNDLED_RECIPES_DIR;
236
+ }
237
+
229
238
  clearCache() {
230
239
  this.cache.clear();
231
240
  }