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.
- package/README.md +59 -12
- package/bin/cli.js +20 -37
- package/package.json +4 -1
- package/recipes/_shared/roles/developer.md +11 -0
- package/recipes/_shared/roles/product.md +12 -0
- package/recipes/_shared/roles/tester.md +12 -0
- package/recipes/_shared/test/report-format.md +86 -0
- package/recipes/backend/base.md +27 -0
- package/recipes/backend/components/auth.md +18 -0
- package/recipes/backend/components/crud-api.md +18 -0
- package/recipes/backend/components/file-service.md +15 -0
- package/recipes/backend/manifest.json +20 -0
- package/recipes/backend/test/api-test.md +25 -0
- package/recipes/console/base.md +37 -0
- package/recipes/console/components/modal-form.md +20 -0
- package/recipes/console/components/pagination.md +17 -0
- package/recipes/console/components/search.md +17 -0
- package/recipes/console/components/table-list.md +18 -0
- package/recipes/console/components/tabs.md +14 -0
- package/recipes/console/components/tree.md +15 -0
- package/recipes/console/components/upload.md +15 -0
- package/recipes/console/manifest.json +24 -0
- package/recipes/console/test/crud-e2e.md +47 -0
- package/recipes/h5/base.md +26 -0
- package/recipes/h5/components/animation.md +11 -0
- package/recipes/h5/components/countdown.md +11 -0
- package/recipes/h5/components/share.md +11 -0
- package/recipes/h5/components/swiper.md +11 -0
- package/recipes/h5/manifest.json +21 -0
- package/recipes/h5/test/h5-e2e.md +20 -0
- package/src/commands/auth.js +87 -15
- package/src/commands/setup-modules/helpers.js +4 -3
- package/src/commands/setup-modules/mcp.js +44 -24
- package/src/commands/setup-modules/safety.js +1 -15
- package/src/commands/setup.js +8 -8
- package/src/common/assets.js +10 -1
- package/src/common/config.js +2 -2
- package/src/common/indicator.js +158 -120
- package/src/common/utils.js +60 -8
- package/src/core/coding.js +16 -38
- package/src/core/go.js +31 -77
- package/src/core/hooks.js +56 -89
- package/src/core/init.js +94 -100
- package/src/core/plan.js +85 -223
- package/src/core/prompts.js +36 -16
- package/src/core/repair.js +7 -17
- package/src/core/runner.js +306 -43
- package/src/core/scan.js +38 -34
- package/src/core/session.js +253 -39
- package/src/core/simplify.js +45 -24
- package/src/core/state.js +105 -0
- package/src/index.js +76 -0
- package/templates/codingSystem.md +2 -2
- package/templates/codingUser.md +1 -1
- package/templates/guidance.json +22 -3
- package/templates/planSystem.md +2 -2
- package/templates/scanSystem.md +3 -3
- package/templates/scanUser.md +1 -1
- package/templates/web-testing.md +17 -0
- package/types/index.d.ts +217 -0
- package/src/core/context.js +0 -117
- package/src/core/harness.js +0 -484
- package/src/core/query.js +0 -50
- 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,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. 验证布局无溢出、元素无遮挡
|
package/src/commands/auth.js
CHANGED
|
@@ -41,7 +41,11 @@ function updateGitignore(entry) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
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
|
|
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 (/^
|
|
84
|
-
content = content.replace(/^
|
|
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}
|
|
104
|
+
content += `${suffix}WEB_TEST_TOOL=${tool}\n`;
|
|
88
105
|
}
|
|
89
106
|
fs.writeFileSync(envPath, content, 'utf8');
|
|
90
|
-
log('ok',
|
|
107
|
+
log('ok', `.claude-coder/.env 已设置 WEB_TEST_TOOL=${tool}`);
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
// ─────────────────────────────────────────────────────────────
|
|
94
|
-
// 浏览器脚本(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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',
|
|
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
|
-
|
|
80
|
-
const
|
|
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(` 完成检测:
|
|
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 {
|
|
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('
|
|
11
|
+
console.log('是否启用浏览器测试工具?');
|
|
12
12
|
console.log('');
|
|
13
|
-
console.log(' Playwright MCP
|
|
14
|
-
console.log('
|
|
15
|
-
console.log('
|
|
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
|
|
19
|
+
const toolChoice = await askChoice(rl, '选择 [1-3]: ', 1, 3);
|
|
22
20
|
|
|
23
|
-
const mcpConfig = {
|
|
21
|
+
const mcpConfig = { tool: '', mode: '' };
|
|
24
22
|
|
|
25
|
-
if (
|
|
26
|
-
mcpConfig.
|
|
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
|
|
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('', '#
|
|
70
|
-
if (mcpConfig.
|
|
71
|
-
lines.push(
|
|
72
|
-
if (mcpConfig.mode) lines.push(`
|
|
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('
|
|
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('
|
|
83
|
-
if (mcpConfig.
|
|
84
|
-
updateEnvVar('
|
|
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'),
|
|
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(' 完成检测 —
|
|
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()) {
|
package/src/commands/setup.js
CHANGED
|
@@ -20,7 +20,7 @@ const {
|
|
|
20
20
|
} = require('./setup-modules');
|
|
21
21
|
|
|
22
22
|
const PRESERVED_KEYS = [
|
|
23
|
-
'SESSION_STALL_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.
|
|
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)
|
|
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
|
-
|
|
123
|
-
mode: existing.
|
|
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.
|
|
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', '配置已更新');
|
package/src/common/assets.js
CHANGED
|
@@ -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
|
-
['
|
|
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
|
}
|