sillyspec 3.8.4 → 3.8.6

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 (163) hide show
  1. package/README.md +0 -6
  2. package/docs/.vitepress/config.mts +45 -0
  3. package/docs/.vitepress/dist/404.html +25 -0
  4. package/docs/.vitepress/dist/assets/app.YytxICdd.js +1 -0
  5. package/docs/.vitepress/dist/assets/chunks/framework.Czhw_PXq.js +19 -0
  6. package/docs/.vitepress/dist/assets/chunks/theme.DusTRZQk.js +1 -0
  7. package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.js +1 -0
  8. package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.lean.js +1 -0
  9. package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  10. package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  11. package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  12. package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  13. package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  14. package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  15. package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  16. package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  17. package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  18. package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  19. package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  20. package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  21. package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  22. package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  23. package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.js +15 -0
  24. package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.lean.js +1 -0
  25. package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.js +4 -0
  26. package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.lean.js +1 -0
  27. package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.js +1 -0
  28. package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.lean.js +1 -0
  29. package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.js +4 -0
  30. package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.lean.js +1 -0
  31. package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.js +5 -0
  32. package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.lean.js +1 -0
  33. package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.js +28 -0
  34. package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.lean.js +1 -0
  35. package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.js +30 -0
  36. package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.lean.js +1 -0
  37. package/docs/.vitepress/dist/assets/style.DFTx90Kk.css +1 -0
  38. package/docs/.vitepress/dist/hashmap.json +1 -0
  39. package/docs/.vitepress/dist/index.html +28 -0
  40. package/docs/.vitepress/dist/sillyspec/commands.html +42 -0
  41. package/docs/.vitepress/dist/sillyspec/dashboard.html +31 -0
  42. package/docs/.vitepress/dist/sillyspec/file-io.html +28 -0
  43. package/docs/.vitepress/dist/sillyspec/getting-started.html +31 -0
  44. package/docs/.vitepress/dist/sillyspec/install.html +32 -0
  45. package/docs/.vitepress/dist/sillyspec/lifecycle.html +55 -0
  46. package/docs/.vitepress/dist/sillyspec/structure.html +57 -0
  47. package/docs/.vitepress/dist/vp-icons.css +1 -0
  48. package/docs/index.md +34 -0
  49. package/docs/sillyspec/commands.md +218 -0
  50. package/docs/sillyspec/dashboard.md +51 -0
  51. package/docs/sillyspec/file-io.md +34 -0
  52. package/docs/sillyspec/getting-started.md +61 -0
  53. package/docs/sillyspec/install.md +51 -0
  54. package/docs/sillyspec/lifecycle.md +146 -0
  55. package/docs/sillyspec/structure.md +62 -0
  56. package/package.json +11 -9
  57. package/packages/dashboard/dist/assets/index-Bh-GPjKY.css +1 -0
  58. package/packages/dashboard/dist/assets/index-CrCn5Gg6.js +17 -0
  59. package/packages/dashboard/dist/index.html +2 -2
  60. package/packages/dashboard/package-lock.json +0 -220
  61. package/packages/dashboard/package.json +5 -8
  62. package/packages/dashboard/server/index.js +106 -255
  63. package/packages/dashboard/server/parser.js +29 -333
  64. package/packages/dashboard/server/watcher.js +131 -203
  65. package/packages/dashboard/src/App.vue +10 -181
  66. package/packages/dashboard/src/components/ActionBar.vue +42 -26
  67. package/packages/dashboard/src/components/CommandPalette.vue +65 -40
  68. package/packages/dashboard/src/components/DetailPanel.vue +53 -68
  69. package/packages/dashboard/src/components/LogStream.vue +33 -13
  70. package/packages/dashboard/src/components/PipelineStage.vue +8 -8
  71. package/packages/dashboard/src/components/PipelineView.vue +45 -80
  72. package/packages/dashboard/src/components/ProjectList.vue +45 -103
  73. package/packages/dashboard/src/components/StageBadge.vue +13 -13
  74. package/packages/dashboard/src/components/StepCard.vue +15 -15
  75. package/packages/dashboard/src/composables/useDashboard.js +6 -20
  76. package/packages/dashboard/src/composables/useKeyboard.js +4 -6
  77. package/packages/dashboard/src/main.js +1 -4
  78. package/packages/dashboard/src/style.css +17 -17
  79. package/src/index.js +12 -123
  80. package/src/init.js +227 -86
  81. package/src/setup.js +9 -1
  82. package/templates/archive.md +121 -0
  83. package/templates/brainstorm.md +240 -0
  84. package/{.claude/skills/sillyspec-commit/SKILL.md → templates/commit.md} +47 -29
  85. package/templates/continue.md +32 -0
  86. package/templates/execute.md +314 -0
  87. package/templates/explore.md +60 -0
  88. package/templates/export.md +21 -0
  89. package/templates/init.md +61 -0
  90. package/templates/plan.md +157 -0
  91. package/templates/quick.md +135 -0
  92. package/templates/scan-quick.md +49 -0
  93. package/templates/scan.md +172 -0
  94. package/templates/skills/playwright-e2e/SKILL.md +340 -0
  95. package/templates/status.md +75 -0
  96. package/templates/verify.md +253 -0
  97. package/templates/workspace-sync.md +99 -0
  98. package/templates/workspace.md +70 -0
  99. package/.claude/skills/sillyspec-archive/SKILL.md +0 -17
  100. package/.claude/skills/sillyspec-auto/SKILL.md +0 -77
  101. package/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -17
  102. package/.claude/skills/sillyspec-continue/SKILL.md +0 -44
  103. package/.claude/skills/sillyspec-doctor/SKILL.md +0 -22
  104. package/.claude/skills/sillyspec-execute/SKILL.md +0 -17
  105. package/.claude/skills/sillyspec-explore/SKILL.md +0 -96
  106. package/.claude/skills/sillyspec-export/SKILL.md +0 -53
  107. package/.claude/skills/sillyspec-init/SKILL.md +0 -170
  108. package/.claude/skills/sillyspec-plan/SKILL.md +0 -52
  109. package/.claude/skills/sillyspec-propose/SKILL.md +0 -17
  110. package/.claude/skills/sillyspec-quick/SKILL.md +0 -17
  111. package/.claude/skills/sillyspec-resume/SKILL.md +0 -111
  112. package/.claude/skills/sillyspec-scan/SKILL.md +0 -17
  113. package/.claude/skills/sillyspec-state/SKILL.md +0 -54
  114. package/.claude/skills/sillyspec-status/SKILL.md +0 -17
  115. package/.claude/skills/sillyspec-verify/SKILL.md +0 -17
  116. package/.claude/skills/sillyspec-workspace/SKILL.md +0 -149
  117. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +0 -97
  118. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +0 -51
  119. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +0 -29
  120. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +0 -34
  121. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +0 -13
  122. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +0 -43
  123. package/.sillyspec/changes/auto-mode/design.md +0 -50
  124. package/.sillyspec/changes/auto-mode/proposal.md +0 -19
  125. package/.sillyspec/changes/auto-mode/requirements.md +0 -21
  126. package/.sillyspec/changes/auto-mode/tasks.md +0 -7
  127. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +0 -199
  128. package/.sillyspec/changes/dashboard/design.md.braindraft +0 -206
  129. package/.sillyspec/changes/run-command-design/design.md +0 -1230
  130. package/.sillyspec/changes/unified-docs-design/design.md +0 -199
  131. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  132. package/.sillyspec/knowledge/INDEX.md +0 -8
  133. package/.sillyspec/knowledge/uncategorized.md +0 -3
  134. package/.sillyspec/projects/sillyspec.yaml +0 -3
  135. package/packages/dashboard/dist/assets/index-D1EVTLmc.js +0 -7446
  136. package/packages/dashboard/dist/assets/index-DGe8CqeP.css +0 -1
  137. package/packages/dashboard/public/logo.jpg +0 -0
  138. package/packages/dashboard/src/components/DocPreview.vue +0 -160
  139. package/packages/dashboard/src/components/DocTree.vue +0 -58
  140. package/packages/dashboard/src/components/ProjectOverview.vue +0 -178
  141. package/packages/dashboard/src/components/detail/DocsDetail.vue +0 -48
  142. package/packages/dashboard/src/components/detail/GitDetail.vue +0 -61
  143. package/packages/dashboard/src/components/detail/TechDetail.vue +0 -43
  144. package/src/derive.js +0 -147
  145. package/src/migrate.js +0 -117
  146. package/src/progress.js +0 -495
  147. package/src/run.js +0 -640
  148. package/src/stages/archive.js +0 -54
  149. package/src/stages/brainstorm.js +0 -239
  150. package/src/stages/doctor.js +0 -312
  151. package/src/stages/execute.js +0 -258
  152. package/src/stages/index.js +0 -35
  153. package/src/stages/plan.js +0 -259
  154. package/src/stages/propose.js +0 -115
  155. package/src/stages/quick.js +0 -64
  156. package/src/stages/scan.js +0 -141
  157. package/src/stages/status.js +0 -65
  158. package/src/stages/verify.js +0 -135
  159. /package/.sillyspec/{changes/brainstorm-archive → specs}/2026-04-05-dashboard-design.md +0 -0
  160. /package/{packages/dashboard → docs/.vitepress}/dist/favicon.jpg +0 -0
  161. /package/{logo.jpg → docs/.vitepress/dist/logo.jpg} +0 -0
  162. /package/{packages/dashboard → docs}/public/favicon.jpg +0 -0
  163. /package/{packages/dashboard/dist → docs/public}/logo.jpg +0 -0
@@ -0,0 +1,135 @@
1
+ ## 交互规范
2
+ **当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
3
+
4
+ ## 核心约束(必须遵守)
5
+ - ❌ 不写测试(底线是仍然要写测试)
6
+ - ❌ 修改无关文件
7
+ - ❌ 跳过测试因为"任务太简单"
8
+
9
+ ## 用法
10
+
11
+ - `/sillyspec:quick "修复用户创建接口漏了手机号校验"` — 独立记录到按用户名隔离的 QUICKLOG
12
+ - `/sillyspec:quick --change user-module "修复用户创建接口漏了手机号校验"` — 追加到 user-module 变更的 tasks.md
13
+
14
+ ## 任务
15
+ $ARGUMENTS
16
+
17
+ ---
18
+
19
+ ## 流程
20
+
21
+ 1. **解析参数:** 检查是否携带 `--change <变更名>`,确定记录方式
22
+ 1.5 **归属检查:** 如果没有 `--change` 参数,检查 `.sillyspec/changes/` 下是否有非 archive 的活跃变更目录:
23
+ ```bash
24
+ ls -d .sillyspec/changes/*/ 2>/dev/null | grep -v archive
25
+ ```
26
+ - 有活跃变更 → AskUserQuestion 询问本次 quick 归属哪个变更,默认选当前活跃的
27
+ - 用户选择后将日志写入 `.sillyspec/changes/<变更名>/quicklog/` 而非独立 QUICKLOG
28
+ - 用户选择"无归属" → 走原有的独立 QUICKLOG 流程
29
+ - 无活跃变更 → 走原有的独立 QUICKLOG 流程
30
+ 2. **理解任务:** 模糊则问一个问题确认
31
+ 3. **加载上下文:** `cat .sillyspec/docs/<project>/scan/{CONVENTIONS,ARCHITECTURE}.md 2>/dev/null`
32
+ 3b. **编码规范扫描:** 检测项目中的编码规范配置文件(`.eslintrc*`、`.prettierrc*`、`tsconfig.json`、`.editorconfig`、`tailwind.config.*`、`CONTRIBUTING.md`),提取关键规则生成摘要。写作代码时必须严格遵守这些规则(分号/引号/缩进/命名风格等),如不确定优先遵守规范约束。
33
+ 4. **知识库查询(强制步骤):**
34
+ ```bash
35
+ cat .sillyspec/knowledge/INDEX.md 2>/dev/null
36
+ ```
37
+ 根据当前任务描述中的关键词匹配 INDEX.md 条目,命中时 `cat` 对应知识文件,将内容纳入后续开发考量。未命中则跳过。
38
+ **MCP 检测:** 检查当前可用工具列表中是否存在 MCP 工具(Context7/浏览器/数据库/搜索等),根据检测结果动态利用:
39
+ - 有 Context7 → 查询不熟悉库/API 的最新文档
40
+ - 有浏览器 MCP → 验证页面改动效果
41
+ - 有数据库 MCP → 查询表结构和数据验证(只读)
42
+ - 有搜索 MCP → 搜索最佳实践和解决方案
43
+ - 有其他 MCP → 按任务需要灵活使用
44
+ - 无 MCP → 使用 web search
45
+ 5. **先读后写:** 调用已有方法前 `cat` 源文件确认签名,`grep` 确认方法存在
46
+ 6. **数据操作安全:** 任何改变现有数据的操作(非 SELECT 的数据库操作)必须暂停并报告给用户确认,不得自动执行。新建表不受此限制
47
+ 6. **TDD 执行:**
48
+ ```
49
+ 🔴 RED → 先写测试,运行确认失败
50
+ 🟢 GREEN → 写最少代码让测试通过
51
+ 🔵 REFACTOR → 清理,保持测试通过
52
+ ✅ STAGE → git add 暂存(测试文件必须包含在暂存中)
53
+ ```
54
+ 测试文件必须保留在项目中,不能删除。违反 TDD → 删掉代码从测试重新开始。
55
+ - 纯配置/数据/文档可跳过 TDD
56
+ - 其他情况一律走 TDD
57
+ 7. **运行测试:** 先检查 local.yaml 构建命令配置:
58
+ ```bash
59
+ cat .sillyspec/local.yaml 2>/dev/null
60
+ ```
61
+ 如果有则使用 local.yaml 中的命令;否则使用默认命令:
62
+ ```bash
63
+ mvn test -pl <模块> -Dtest=<测试类> 2>/dev/null || ./gradlew test --tests <测试类> 2>/dev/null || pnpm test 2>/dev/null || npm test 2>/dev/null || pytest <测试文件> 2>/dev/null
64
+ ```
65
+ 8. **Lint 校验(如项目配置了):** 写完代码后、暂存前,运行项目的 lint 工具验证代码质量:
66
+ ```bash
67
+ # 检测并运行可用的 lint 工具
68
+ npx eslint <修改的文件> 2>/dev/null || \
69
+ npx prettier --check <修改的文件> 2>/dev/null || \
70
+ npx tsc --noEmit 2>/dev/null || \
71
+ true # 没有 lint 工具则跳过
72
+ ```
73
+ - **有报错 → 自动修复:** `npx eslint --fix <修改的文件>` / `npx prettier --write <修改的文件>`
74
+ - **修复后仍有报错 → 在 QUICKLOG 中标注,提醒用户手动处理**
75
+ - **工作区模式下,在子项目目录中执行,不要在主项目目录执行**
76
+ 9. **Git 暂存:** `git add -A`。**不要 commit**,由用户通过 `/sillyspec:commit` 统一提交。**工作区模式下,确认当前在正确的子项目目录中执行暂存。**
77
+ 10. **记录:**
78
+ - **有 `--change`:** 在 `.sillyspec/changes/<变更名>/tasks.md` 追加 task 并勾选,**记录精确到秒的时间戳**:
79
+
80
+ ```
81
+ - [x] [YYYY-MM-DD HH:MM:SS] 任务描述
82
+ ```
83
+ - **无 `--change` 但步骤 1.5 确认了归属变更:** 记录到 `.sillyspec/changes/<变更名>/quicklog/YYYY-MM-DD-HHMMSS-任务简述.md`,格式:
84
+
85
+ ```markdown
86
+ # quick: 任务描述
87
+
88
+ - 时间:YYYY-MM-DD HH:MM:SS
89
+ - 关联变更:<变更名>
90
+ - 修改文件:
91
+ - `path/to/file1`
92
+ - `path/to/file2`
93
+ - 改动说明:(2-3 句描述做了什么)
94
+ - 发现的坑:(如有,简要记录)
95
+ ```
96
+
97
+ - **无 `--change` 且无归属:** 记录到 `.sillyspec/quicklog/QUICKLOG-<git用户名>.md`(见下方规则)
98
+ 10. **检查复杂度:** 任务比预期复杂 → 建议用完整流程
99
+
100
+ 11. **记录发现的坑:** 执行过程中如果发现项目特有的规律、陷阱或约定(如"某方法参数顺序容易搞反"、"某表有隐藏软删除字段"),追加到 `.sillyspec/knowledge/uncategorized.md`,格式:
101
+
102
+ ```markdown
103
+ ### [待确认] {简短标题}
104
+ > 来源:quick / {时间戳}
105
+
106
+ {坑的具体描述}
107
+ ```
108
+
109
+ **工作区模式下:** 只影响当前子项目 → 写入当前子项目 `.sillyspec/knowledge/uncategorized.md`;影响多个子项目 → 写入工作区根目录 `.sillyspec/knowledge/uncategorized.md`。
110
+
111
+ 12. **知识库审阅提示:** 如果本次执行向 knowledge/ 写入了新条目,提示用户:
112
+ > 📚 本次 quick 发现了新知识,请审阅:`cat .sillyspec/knowledge/uncategorized.md`
113
+ > 确认后请将 `[待确认]` 改为 `[已确认]`,并可归类到 knowledge/ 下的专题文件中更新 INDEX.md。
114
+
115
+ ### QUICKLOG 规则
116
+
117
+ **按 git 用户名隔离,避免多人同时操作冲突:**
118
+
119
+ ```bash
120
+ USER=$(git config user.name 2>/dev/null || echo "default")
121
+ LOG_FILE=".sillyspec/quicklog/QUICKLOG-${USER}.md"
122
+ ```
123
+
124
+ 文件路径:`$LOG_FILE`
125
+
126
+ **追加记录格式(时间精确到秒):**
127
+ ```markdown
128
+ ## YYYY-MM-DD HH:MM:SS | fix: 任务描述
129
+ - 文件:`修改的文件列表`
130
+ - 关联归档:`相关的已归档变更名`(如有)
131
+ ```
132
+
133
+ **文件轮转:** 追加前检查文件大小,超过 500 行则:
134
+ 1. 将当前文件重命名为 `QUICKLOG-${USER}-YYYY-MM-DD.md`
135
+ 2. 创建新的空 `QUICKLOG-${USER}.md`
@@ -0,0 +1,49 @@
1
+ ## 交互规范
2
+ **当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
3
+
4
+ > 本模板由 `/sillyspec:scan --quick` 触发。如需完整深度扫描,使用 `/sillyspec:scan`。
5
+
6
+ ## 核心约束(必须遵守)
7
+ - ❌ 修改任何代码
8
+ - ❌ 编造文件路径或代码模式(必须包含真实路径)
9
+ - ❌ 读源码文件(快扫只读配置和目录)
10
+
11
+ ## 快速扫描流程
12
+
13
+ ### Step 1: 检查工作区模式
14
+
15
+ ```bash
16
+ ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q .
17
+ ```
18
+
19
+ 有 `projects` 字段 → 工作区模式:逐个子项目快扫。
20
+
21
+ ### Step 2: 读配置文件
22
+
23
+ ```bash
24
+ cat package.json tsconfig.json requirements.txt Cargo.toml go.mod pom.xml build.gradle 2>/dev/null
25
+ find . -maxdepth 2 -name "*.config.*" -not -path "*/node_modules/*" -not -path "*/.git/*" | head -20 | xargs cat 2>/dev/null
26
+ ```
27
+
28
+ ### Step 3: 生成文档
29
+
30
+ `mkdir -p .sillyspec/codebase`,生成 4 份文档:
31
+
32
+ 1. **ARCHITECTURE.md** — 架构 + 技术栈(合并原 STACK.md)
33
+ 2. **STRUCTURE.md** — 目录结构(`find . -type f | head -200`)
34
+ 3. **CONVENTIONS.md** — 空骨架文件,含章节标题(注意事项、代码风格等),供后续 quick/execute 追加
35
+ 4. **PROJECT.md** — 项目概览
36
+
37
+ ### Step 4: 暂存修改
38
+
39
+ ```bash
40
+ git add .sillyspec/
41
+ ```
42
+
43
+ 工作区模式在每个子项目分别暂存。
44
+
45
+ 💡 扫描产出已暂存。准备好后用 `/sillyspec:commit` 提交。
46
+
47
+ ### 完成
48
+
49
+ 提示用户:快扫只提取基础信息。如需完整代码规范(框架规则、实体继承、代码风格),执行 `/sillyspec:scan` 进行深度扫描。
@@ -0,0 +1,172 @@
1
+ ## 交互规范
2
+ **当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
3
+
4
+ 你现在是 SillySpec 代码库扫描器(编排器)。**你不读源码,只编排子代理或串行执行。**
5
+
6
+ ## 参数处理
7
+ - 空白 → 交互式引导(逐步询问)
8
+ - `--deep` → 直接深度扫描
9
+ - 其他 → 快速扫描该区域
10
+
11
+ ## 流程控制(必须先执行)
12
+
13
+ ```bash
14
+ cat .sillyspec/STATE.md 2>/dev/null
15
+ ```
16
+
17
+ 有 STATE.md 且 phase 为 `scan` 或未记录 → 继续。
18
+ 其他 phase → STATE.md 中记录的下一步命令为准。
19
+
20
+ ---
21
+
22
+ ## 交互式引导(参数为空时)
23
+
24
+ ### 检查工作区 & 已有文档
25
+
26
+ ```bash
27
+ ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q . # 有子项目配置 → 工作区模式
28
+ ls .sillyspec/docs/<project>/scan/ 2>/dev/null # 检查已有文档
29
+ wc -l .sillyspec/docs/<project>/scan/*.md 2>/dev/null
30
+ ```
31
+
32
+ - 已有 3 份 → 建议升级深度扫描
33
+ - 已有 7 份 → 建议刷新或跳过
34
+ - 工作区 → 逐个扫描 / 选子项目 / 退出
35
+
36
+ ### 选择扫描模式、范围、排除目录、确认
37
+ 按原流程交互,确认后进入扫描。
38
+
39
+ ---
40
+
41
+ ## 构建环境探测(主代理执行)
42
+
43
+ ```bash
44
+ cat package.json pom.xml build.gradle go.mod Cargo.toml requirements.txt pyproject.toml Gemfile composer.json 2>/dev/null
45
+ find . -maxdepth 2 -name "*.config.*" -not -path "*/node_modules/*" -not -path "*/.git/*" | head -20 | xargs cat 2>/dev/null
46
+ ```
47
+
48
+ 结果保存到 `.sillyspec/docs/<project>/scan/_env-detect.md`(临时文件,扫描完删除)。
49
+
50
+ ---
51
+
52
+ ## 深度扫描
53
+
54
+ `mkdir -p .sillyspec/codebase`
55
+
56
+ ### 断点续扫
57
+
58
+ ```bash
59
+ for f in ARCHITECTURE STRUCTURE CONVENTIONS INTEGRATIONS TESTING CONCERNS PROJECT; do
60
+ [ -f ".sillyspec/docs/<project>/scan/${f}.md" ] && echo "✅ ${f}.md" || echo "⬜ ${f}.md"
61
+ done
62
+ ```
63
+
64
+ 只生成缺失的文档。
65
+
66
+ ### 检测子代理可用性
67
+ 检查是否有 Task/Spawn 工具。有 → 子代理模式,无 → 串行模式。
68
+
69
+ ---
70
+
71
+ ### 子代理模式(4 个并行)
72
+
73
+ #### Agent 1: tech → ARCHITECTURE.md
74
+ 扫描技术栈 + 数据库 Schema + 架构模式。参考 `_env-detect.md`。
75
+ 用 grep/rg 搜索(`@Entity`、`schema.prisma`、`models.py` 等),**禁止读源码全文**。
76
+ Schema 只记表名+说明+字段数。含 `## 技术栈` `## 架构概览` `## 数据模型(摘要)`。路径用反引号,不编造。
77
+
78
+ #### Agent 2: conventions → CONVENTIONS.md
79
+ 扫描框架隐形规则 + 实体继承 + 代码风格。参考 `_env-detect.md`。
80
+ 用 grep 搜索拦截器/插件/逻辑删除/基类/审计字段,**禁止读源码全文**。
81
+ 根据检测到的语言/框架自行决定搜索什么模式,提取 3-5 个典型示例。
82
+ 含 `## 框架隐形规则` `## 实体继承规范` `## 代码风格`。路径用反引号,不编造。
83
+
84
+ #### Agent 3: structure → STRUCTURE.md + INTEGRATIONS.md
85
+ 扫描目录结构 + 外部集成。参考 `_env-detect.md`。
86
+ 用 find/ls/tree 和 grep,**禁止读源码全文**。
87
+ 搜索 API 调用、MQ 配置、缓存、第三方 SDK。STRUCTURE.md 含目录树+模块说明。INTEGRATIONS.md 按类型分组。路径用反引号,不编造。
88
+
89
+ #### Agent 4: quality → TESTING.md + CONCERNS.md + PROJECT.md
90
+ 扫描测试现状 + 技术债务 + 项目概览。参考 `_env-detect.md`。
91
+ 用 grep 搜索测试文件、TODO/FIXME、过时依赖,**禁止读源码全文**。
92
+ TESTING.md 含测试结构。CONCERNS.md 按严重程度分组。PROJECT.md 含项目信息。路径用反引号,不编造。
93
+
94
+ ---
95
+
96
+ ### 串行模式(降级)
97
+ 无子代理时,按 tech → conventions → structure → quality 顺序执行。
98
+ 每个 area 完成后**立即写文件**,下一个 area 开始前清除源码上下文。
99
+
100
+ ---
101
+
102
+ ## 工作区模式
103
+ 对每个子项目:cd → 环境探测 → 扫描 → cd 回工作区。
104
+ 全部完成后汇总 `.sillyspec/workspace/CODEBASE-OVERVIEW.md`(只读各子项目的 ARCHITECTURE.md + CONVENTIONS.md)。
105
+
106
+ ---
107
+
108
+ ## 扫描完成
109
+
110
+ ```bash
111
+ # 路径校验
112
+ for f in ARCHITECTURE STRUCTURE CONVENTIONS INTEGRATIONS TESTING CONCERNS PROJECT; do
113
+ [ -f ".sillyspec/docs/<project>/scan/${f}.md" ] && echo "✅ ${f}.md"
114
+ done
115
+
116
+ # 生成知识库骨架
117
+ mkdir -p .sillyspec/knowledge
118
+ if [ ! -f ".sillyspec/knowledge/INDEX.md" ]; then
119
+ cat > .sillyspec/knowledge/INDEX.md << 'EOF'
120
+ # Knowledge Index
121
+
122
+ > 子代理任务开始前查询此文件,按关键词匹配,只读命中的知识文件。
123
+ > execute/quick 执行中发现的坑自动追加到 uncategorized.md,经用户确认后归类到对应文件。
124
+
125
+ <!-- 格式:关键词1|关键词2|关键词3 → 文件路径 -->
126
+ <!-- 示例:mybatis-plus|分页|Page → pagination.md -->
127
+ <!-- 示例:跨域|CORS|preflight → cors.md -->
128
+ EOF
129
+ fi
130
+ if [ ! -f ".sillyspec/knowledge/uncategorized.md" ]; then
131
+ cat > .sillyspec/knowledge/uncategorized.md << 'EOF'
132
+ # 未分类知识
133
+
134
+ > execute/quick 执行中发现的坑暂存于此,用户审阅后归类到对应文件并更新 INDEX.md。
135
+
136
+ EOF
137
+ fi
138
+
139
+ # 更新状态
140
+ cat > .sillyspec/STATE.md << 'EOF'
141
+ # 项目状态
142
+
143
+ ## 当前阶段
144
+ - 阶段:scan ✅
145
+ - 下一步:/sillyspec:brainstorm
146
+
147
+ ## 关键决策
148
+ - (扫描中发现的技术决策)
149
+
150
+ ## 历史记录
151
+ - $(date '+%Y-%m-%d %H:%M:%S') scan 完成
152
+ EOF
153
+
154
+ # 清理
155
+ rm -f .sillyspec/docs/<project>/scan/_env-detect.md
156
+ git add .sillyspec/
157
+ ```
158
+
159
+ 💡 扫描产出已暂存。准备好后用 `/sillyspec:commit` 提交。
160
+
161
+ ### 自检门控
162
+ - [ ] ARCHITECTURE.md:技术栈 + Schema 摘要?
163
+ - [ ] CONVENTIONS.md:隐形规则 + 代码风格?
164
+ - [ ] STRUCTURE.md:目录结构?
165
+ - [ ] INTEGRATIONS.md:外部依赖?
166
+ - [ ] TESTING.md:测试现状?
167
+ - [ ] CONCERNS.md:技术债务?
168
+ - [ ] PROJECT.md:项目概览?
169
+
170
+ ## 绝对规则
171
+ - ❌ 修改代码 / 编造路径 / 主代理读源码全文
172
+ - ✅ 交互模式每步等用户 / 文档只写 `.sillyspec/docs/<project>/scan/`
@@ -0,0 +1,340 @@
1
+ ---
2
+ name: playwright-e2e
3
+ description: Playwright E2E 测试编写参考。当任务涉及编写端到端测试、浏览器自动化测试、Playwright 测试用例时自动读取,不要凭记忆编写 Playwright API。
4
+ ---
5
+
6
+ # Playwright E2E 测试编写参考
7
+
8
+ > ⚠️ 执行 E2E/测试任务时必须先读取此文件,不要凭训练数据记忆编写 Playwright API。
9
+
10
+ ## 测试基本结构
11
+
12
+ ```typescript
13
+ import { test, expect } from '@playwright/test';
14
+
15
+ test.describe('功能名称', () => {
16
+ test.beforeEach(async ({ page }) => {
17
+ await page.goto('/');
18
+ });
19
+
20
+ test('具体场景', async ({ page }) => {
21
+ // Arrange
22
+ // Act
23
+ // Assert
24
+ });
25
+ });
26
+ ```
27
+
28
+ ## 选择器优先级
29
+
30
+ ```typescript
31
+ // ✅ 最推荐:data-testid(最稳定)
32
+ await page.locator('[data-testid="submit-btn"]').click();
33
+
34
+ // ✅ 推荐:role-based(语义化)
35
+ await page.getByRole('button', { name: 'Submit' }).click();
36
+ await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
37
+
38
+ // ✅ 推荐:文本匹配
39
+ await page.getByText('Sign in').click();
40
+ await page.getByText(/welcome back/i).click();
41
+
42
+ // ✅ 可用:语义 HTML
43
+ await page.locator('button[type="submit"]').click();
44
+ await page.locator('input[name="email"]').fill('test@test.com');
45
+
46
+ // ❌ 避免:CSS 类名和 ID(重构易变)
47
+ // await page.locator('.btn-primary').click();
48
+ // await page.locator('#submit').click();
49
+ ```
50
+
51
+ ## 断言
52
+
53
+ ```typescript
54
+ // URL
55
+ await expect(page).toHaveURL('/dashboard');
56
+ await expect(page).toHaveURL(/.*dashboard/);
57
+
58
+ // 文本
59
+ await expect(page.locator('h1')).toHaveText('Welcome');
60
+ await expect(page.locator('.message')).toContainText('success');
61
+ await expect(page.locator('.items')).toHaveText(['Item 1', 'Item 2']);
62
+
63
+ // 可见性
64
+ await expect(page.locator('.modal')).toBeVisible();
65
+ await expect(page.locator('.spinner')).toBeHidden();
66
+ await expect(page.locator('button')).toBeEnabled();
67
+ await expect(page.locator('input')).toBeDisabled();
68
+
69
+ // 数量
70
+ await expect(page.locator('.item')).toHaveCount(3);
71
+
72
+ // 输入值
73
+ await expect(page.locator('input')).toHaveValue('test@example.com');
74
+ ```
75
+
76
+ ## 表单操作
77
+
78
+ ```typescript
79
+ // 文本输入
80
+ await page.getByLabel('Email').fill('user@example.com');
81
+ await page.getByPlaceholder('Enter your name').fill('John Doe');
82
+
83
+ // 清空再输入
84
+ await page.locator('#username').clear();
85
+ await page.locator('#username').type('newuser', { delay: 100 });
86
+
87
+ // 复选框
88
+ await page.getByLabel('I agree').check();
89
+ await page.getByLabel('I agree').uncheck();
90
+
91
+ // 单选按钮
92
+ await page.getByLabel('Option 2').check();
93
+
94
+ // 下拉选择
95
+ await page.selectOption('select#country', 'usa');
96
+ await page.selectOption('select#country', { label: 'United States' });
97
+
98
+ // 多选下拉
99
+ await page.selectOption('select#colors', ['red', 'blue']);
100
+
101
+ // 文件上传
102
+ await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');
103
+ await page.setInputFiles('input[type="file"]', ['file1.pdf', 'file2.pdf']);
104
+ ```
105
+
106
+ ## 鼠标与键盘
107
+
108
+ ```typescript
109
+ // 点击
110
+ await page.click('button');
111
+ await page.click('button', { button: 'right' }); // 右键
112
+ await page.dblclick('button'); // 双击
113
+
114
+ // 悬停
115
+ await page.hover('.menu-item');
116
+
117
+ // 拖拽
118
+ await page.dragAndDrop('#source', '#target');
119
+
120
+ // 键盘
121
+ await page.keyboard.type('Hello', { delay: 100 });
122
+ await page.keyboard.press('Control+A');
123
+ await page.keyboard.press('Enter');
124
+ await page.keyboard.press('Tab');
125
+ ```
126
+
127
+ ## 测试数据准备
128
+
129
+ ```typescript
130
+ // ⚠️ E2E 测试不能因为"没有数据"就跳过!必须确保测试数据存在。
131
+
132
+ // 方案 1:通过 API 创建前置数据(推荐)
133
+ test.beforeEach(async ({ request }) => {
134
+ // 调用项目 API 创建测试所需数据
135
+ const resp = await request.post('/api/test/setup', {
136
+ data: { createUser: true, createOrder: true }
137
+ });
138
+ expect(resp.ok()).toBeTruthy();
139
+ });
140
+
141
+ // 方案 2:使用 storageState 复用认证状态
142
+ // 先登录一次保存状态,后续测试直接复用
143
+ test.use({ storageState: 'tests/auth/user.json' });
144
+
145
+ // 生成认证状态的脚本:
146
+ // npx playwright codegen --save-storage=tests/auth/user.json http://localhost:3000/login
147
+
148
+ // 方案 3:通过数据库 MCP 直接插入数据(有数据库 MCP 时)
149
+ // 在 beforeEach 中调用数据库 MCP 执行 INSERT 语句准备数据
150
+
151
+ // 方案 4:API Mock(不需要真实数据)
152
+ test('显示用户列表', async ({ page }) => {
153
+ await page.route('**/api/users', route => {
154
+ route.fulfill({
155
+ status: 200,
156
+ contentType: 'application/json',
157
+ body: JSON.stringify([{ id: 1, name: 'Test User' }])
158
+ });
159
+ });
160
+ await page.goto('/users');
161
+ await expect(page.locator('.user-name')).toHaveText('Test User');
162
+ });
163
+
164
+ // 清理:测试后清理数据,避免影响其他测试
165
+ test.afterEach(async ({ request }) => {
166
+ await request.post('/api/test/cleanup');
167
+ });
168
+ ```
169
+
170
+ **铁律:E2E 测试禁止因"没有数据"跳过。** 必须通过以上任一方案准备数据,确保测试可独立运行。
171
+
172
+ ## 弹窗与 iframe
173
+
174
+ ```typescript
175
+ // 新窗口/弹窗
176
+ const [popup] = await Promise.all([
177
+ page.waitForEvent('popup'),
178
+ page.click('button.open-popup'),
179
+ ]);
180
+ await popup.waitForLoadState();
181
+
182
+ // iframe
183
+ const frame = page.frameLocator('#my-iframe');
184
+ await frame.locator('button').click();
185
+ ```
186
+
187
+ ## 截图
188
+
189
+ ```typescript
190
+ // 全页截图
191
+ await page.screenshot({ path: 'screenshot.png', fullPage: true });
192
+
193
+ // 元素截图
194
+ await page.locator('.chart').screenshot({ path: 'chart.png' });
195
+
196
+ // 视觉回归对比(首次生成基线,后续对比)
197
+ await expect(page).toHaveScreenshot('homepage.png');
198
+
199
+ // ⚠️ playwright.config.ts 中配置失败自动截图:
200
+ // screenshot: 'only-on-failure'(已包含在上方 config 模板中)
201
+ ```
202
+
203
+ ## 等待策略
204
+
205
+ ```typescript
206
+ // ✅ 推荐:自动等待(Playwright 内置)
207
+ await expect(page.locator('.loaded')).toBeVisible();
208
+
209
+ // ✅ 推荐:等待 URL 变化
210
+ await page.waitForURL('**/dashboard');
211
+
212
+ // ✅ 推荐:等待网络响应
213
+ const responsePromise = page.waitForResponse('**/api/users');
214
+ await page.click('button#load-users');
215
+ const response = await responsePromise;
216
+
217
+ // ❌ 避免:硬编码 sleep
218
+ // await page.waitForTimeout(3000);
219
+ ```
220
+
221
+ ## Page Object Model
222
+
223
+ ```typescript
224
+ // pages/LoginPage.ts
225
+ export class LoginPage {
226
+ constructor(private page: Page) {}
227
+
228
+ async goto() {
229
+ await this.page.goto('/login');
230
+ }
231
+
232
+ async login(email: string, password: string) {
233
+ await this.page.getByLabel('Email').fill(email);
234
+ await this.page.getByLabel('Password').fill(password);
235
+ await this.page.getByRole('button', { name: 'Submit' }).click();
236
+ }
237
+ }
238
+
239
+ // tests/login.spec.ts
240
+ import { test, expect } from '@playwright/test';
241
+ import { LoginPage } from '../pages/LoginPage';
242
+
243
+ test('登录成功', async ({ page }) => {
244
+ const loginPage = new LoginPage(page);
245
+ await loginPage.goto();
246
+ await loginPage.login('test@example.com', 'password123');
247
+ await expect(page).toHaveURL('/dashboard');
248
+ });
249
+ ```
250
+
251
+ ## API Mock(隔离后端)
252
+
253
+ ```typescript
254
+ test('显示用户列表', async ({ page }) => {
255
+ await page.route('**/api/users', route => {
256
+ route.fulfill({
257
+ status: 200,
258
+ contentType: 'application/json',
259
+ body: JSON.stringify([{ id: 1, name: 'Test User' }])
260
+ });
261
+ });
262
+
263
+ await page.goto('/users');
264
+ await expect(page.locator('.user-name')).toHaveText('Test User');
265
+ });
266
+ ```
267
+
268
+ ## 常见错误
269
+
270
+ ### ❌ 用 CSS 类名选择元素
271
+ ```typescript
272
+ await page.click('.btn-primary'); // 脆弱!重构就坏
273
+ ```
274
+
275
+ ### ✅ 用 data-testid
276
+ ```typescript
277
+ await page.click('[data-testid="submit-btn"]'); // 稳定
278
+ ```
279
+
280
+ ### ❌ 测试之间有依赖
281
+ ```typescript
282
+ test('步骤1:登录', async ({ page }) => { /* ... */ });
283
+ test('步骤2:需要先登录', async ({ page }) => { /* 依赖步骤1,脆弱 */ });
284
+ ```
285
+
286
+ ### ✅ 每个测试独立
287
+ ```typescript
288
+ test.beforeEach(async ({ page }) => {
289
+ await page.goto('/login');
290
+ await page.getByLabel('Email').fill('test@example.com');
291
+ await page.getByLabel('Password').fill('password');
292
+ await page.getByRole('button', { name: 'Submit' }).click();
293
+ await expect(page).toHaveURL('/dashboard');
294
+ });
295
+ ```
296
+
297
+ ### ❌ 不处理异步状态
298
+ ```typescript
299
+ await page.click('[data-testid="submit"]');
300
+ await expect(page.locator('.result')).toBeVisible(); // 可能还没加载完
301
+ ```
302
+
303
+ ### ✅ 等待操作完成
304
+ ```typescript
305
+ await page.click('[data-testid="submit"]');
306
+ await expect(page.locator('.result')).toBeVisible({ timeout: 10000 });
307
+ ```
308
+
309
+ ## playwright.config.ts 模板
310
+
311
+ ```typescript
312
+ import { defineConfig } from '@playwright/test';
313
+
314
+ export default defineConfig({
315
+ testDir: './tests/e2e',
316
+ fullyParallel: true,
317
+ retries: process.env.CI ? 2 : 0,
318
+ timeout: 30000,
319
+ use: {
320
+ baseURL: 'http://localhost:3000', // ⚠️ 根据项目实际端口调整
321
+ trace: 'on-first-retry',
322
+ screenshot: 'only-on-failure',
323
+ },
324
+ webServer: {
325
+ command: 'npm run dev', // ⚠️ 根据项目实际命令调整
326
+ port: 3000,
327
+ reuseExistingServer: !process.env.CI,
328
+ },
329
+ });
330
+ ```
331
+
332
+ ## 调试命令
333
+
334
+ ```bash
335
+ npx playwright test --headed # 有头模式
336
+ npx playwright test --debug # 调试模式(带 inspector)
337
+ npx playwright test --slowmo=1000 # 慢放
338
+ npx playwright show-report # 查看报告
339
+ npx playwright codegen URL # 录制生成代码
340
+ ```