sillyspec 3.27.2 → 3.27.5

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 (221) hide show
  1. package/.claude/skills/sillyspec-execute/SKILL.md +8 -0
  2. package/.claude/skills/sillyspec-quick/SKILL.md +2 -1
  3. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-archive/SKILL.md +87 -0
  4. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-auto/SKILL.md +93 -0
  5. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-brainstorm/SKILL.md +114 -0
  6. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-commit/SKILL.md +67 -0
  7. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-continue/SKILL.md +47 -0
  8. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-doctor/SKILL.md +76 -0
  9. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-execute/SKILL.md +214 -0
  10. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-explore/SKILL.md +102 -0
  11. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-export/SKILL.md +57 -0
  12. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-init/SKILL.md +177 -0
  13. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  14. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-plan/SKILL.md +156 -0
  15. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-quick/SKILL.md +116 -0
  16. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-resume/SKILL.md +70 -0
  17. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-scan/SKILL.md +79 -0
  18. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-state/SKILL.md +64 -0
  19. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-status/SKILL.md +54 -0
  20. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-verify/SKILL.md +87 -0
  21. package/.claude/worktrees/sillyspec-data-model-research/.claude/skills/sillyspec-workspace/SKILL.md +175 -0
  22. package/.claude/worktrees/sillyspec-data-model-research/.gitattributes +2 -0
  23. package/.claude/worktrees/sillyspec-data-model-research/.husky/pre-push +20 -0
  24. package/.claude/worktrees/sillyspec-data-model-research/README.md +203 -0
  25. package/.claude/worktrees/sillyspec-data-model-research/SKILL.md +91 -0
  26. package/.claude/worktrees/sillyspec-data-model-research/bin/sillyspec.js +2 -0
  27. package/.claude/worktrees/sillyspec-data-model-research/logo.jpg +0 -0
  28. package/.claude/worktrees/sillyspec-data-model-research/package-lock.json +977 -0
  29. package/.claude/worktrees/sillyspec-data-model-research/package.json +39 -0
  30. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-Bq_Z2hne.js +7446 -0
  31. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  32. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/favicon.jpg +0 -0
  33. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/index.html +16 -0
  34. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/logo.jpg +0 -0
  35. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-dashboard.html +836 -0
  36. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/dist/prototype-overview.html +256 -0
  37. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/package.json +25 -0
  38. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/executor.js +95 -0
  39. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/index.js +679 -0
  40. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/parser.js +526 -0
  41. package/.claude/worktrees/sillyspec-data-model-research/packages/dashboard/server/watcher.js +357 -0
  42. package/.claude/worktrees/sillyspec-data-model-research/src/agent-session-log.js +803 -0
  43. package/.claude/worktrees/sillyspec-data-model-research/src/change-list.js +290 -0
  44. package/.claude/worktrees/sillyspec-data-model-research/src/change-risk-profile.js +270 -0
  45. package/.claude/worktrees/sillyspec-data-model-research/src/check-primitives.js +65 -0
  46. package/.claude/worktrees/sillyspec-data-model-research/src/classify-change.js +109 -0
  47. package/.claude/worktrees/sillyspec-data-model-research/src/commit-suggest.js +202 -0
  48. package/.claude/worktrees/sillyspec-data-model-research/src/config-cat.js +88 -0
  49. package/.claude/worktrees/sillyspec-data-model-research/src/config-schema.js +328 -0
  50. package/.claude/worktrees/sillyspec-data-model-research/src/constants.js +97 -0
  51. package/.claude/worktrees/sillyspec-data-model-research/src/contract-matrix.js +480 -0
  52. package/.claude/worktrees/sillyspec-data-model-research/src/datetime.js +20 -0
  53. package/.claude/worktrees/sillyspec-data-model-research/src/db-engine.js +55 -0
  54. package/.claude/worktrees/sillyspec-data-model-research/src/db.js +381 -0
  55. package/.claude/worktrees/sillyspec-data-model-research/src/decision-distill.js +517 -0
  56. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/local-agent.js +155 -0
  57. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/backends/sillyhub-mcp.js +245 -0
  58. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/probe.js +246 -0
  59. package/.claude/worktrees/sillyspec-data-model-research/src/dispatch/strategy.js +121 -0
  60. package/.claude/worktrees/sillyspec-data-model-research/src/docs-check.js +825 -0
  61. package/.claude/worktrees/sillyspec-data-model-research/src/docs-debt.js +393 -0
  62. package/.claude/worktrees/sillyspec-data-model-research/src/docs-gate.js +107 -0
  63. package/.claude/worktrees/sillyspec-data-model-research/src/doctor-diagnostics.js +1099 -0
  64. package/.claude/worktrees/sillyspec-data-model-research/src/endpoint-extractor.js +420 -0
  65. package/.claude/worktrees/sillyspec-data-model-research/src/foreign-declared.js +78 -0
  66. package/.claude/worktrees/sillyspec-data-model-research/src/fs-atomic.js +73 -0
  67. package/.claude/worktrees/sillyspec-data-model-research/src/git-helper.js +150 -0
  68. package/.claude/worktrees/sillyspec-data-model-research/src/hooks/claude-pre-tool-use.cjs +130 -0
  69. package/.claude/worktrees/sillyspec-data-model-research/src/hooks/worktree-guard.js +892 -0
  70. package/.claude/worktrees/sillyspec-data-model-research/src/index.js +2798 -0
  71. package/.claude/worktrees/sillyspec-data-model-research/src/init.js +626 -0
  72. package/.claude/worktrees/sillyspec-data-model-research/src/knowledge-match.js +231 -0
  73. package/.claude/worktrees/sillyspec-data-model-research/src/local-detect.js +128 -0
  74. package/.claude/worktrees/sillyspec-data-model-research/src/local-register.js +104 -0
  75. package/.claude/worktrees/sillyspec-data-model-research/src/machine-interface.js +543 -0
  76. package/.claude/worktrees/sillyspec-data-model-research/src/migrate.js +127 -0
  77. package/.claude/worktrees/sillyspec-data-model-research/src/module-impact.js +166 -0
  78. package/.claude/worktrees/sillyspec-data-model-research/src/module-resolve.js +222 -0
  79. package/.claude/worktrees/sillyspec-data-model-research/src/modules.js +580 -0
  80. package/.claude/worktrees/sillyspec-data-model-research/src/progress/change-registry.js +336 -0
  81. package/.claude/worktrees/sillyspec-data-model-research/src/progress/consistency-doctor.js +355 -0
  82. package/.claude/worktrees/sillyspec-data-model-research/src/progress/shared.js +36 -0
  83. package/.claude/worktrees/sillyspec-data-model-research/src/progress/stage-machine.js +594 -0
  84. package/.claude/worktrees/sillyspec-data-model-research/src/progress/step-store.js +265 -0
  85. package/.claude/worktrees/sillyspec-data-model-research/src/progress.js +1304 -0
  86. package/.claude/worktrees/sillyspec-data-model-research/src/quick-recommend.js +115 -0
  87. package/.claude/worktrees/sillyspec-data-model-research/src/quicklog.js +852 -0
  88. package/.claude/worktrees/sillyspec-data-model-research/src/review-tier.js +77 -0
  89. package/.claude/worktrees/sillyspec-data-model-research/src/run/command.js +1595 -0
  90. package/.claude/worktrees/sillyspec-data-model-research/src/run/complete-handlers.js +1552 -0
  91. package/.claude/worktrees/sillyspec-data-model-research/src/run/complete.js +1259 -0
  92. package/.claude/worktrees/sillyspec-data-model-research/src/run/concurrent-detect.js +156 -0
  93. package/.claude/worktrees/sillyspec-data-model-research/src/run/gates.js +1084 -0
  94. package/.claude/worktrees/sillyspec-data-model-research/src/run/multi-repo-context.js +208 -0
  95. package/.claude/worktrees/sillyspec-data-model-research/src/run/next.js +128 -0
  96. package/.claude/worktrees/sillyspec-data-model-research/src/run/prompt.js +843 -0
  97. package/.claude/worktrees/sillyspec-data-model-research/src/run/quick-audit.js +186 -0
  98. package/.claude/worktrees/sillyspec-data-model-research/src/run/scan-profile.js +269 -0
  99. package/.claude/worktrees/sillyspec-data-model-research/src/run/shared.js +1562 -0
  100. package/.claude/worktrees/sillyspec-data-model-research/src/run/stage.js +584 -0
  101. package/.claude/worktrees/sillyspec-data-model-research/src/run.js +23 -0
  102. package/.claude/worktrees/sillyspec-data-model-research/src/scan-diff.js +388 -0
  103. package/.claude/worktrees/sillyspec-data-model-research/src/scan-postcheck.js +512 -0
  104. package/.claude/worktrees/sillyspec-data-model-research/src/scan-staleness.js +95 -0
  105. package/.claude/worktrees/sillyspec-data-model-research/src/setup.js +414 -0
  106. package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/client.js +597 -0
  107. package/.claude/worktrees/sillyspec-data-model-research/src/sillyhub-mcp/config.js +62 -0
  108. package/.claude/worktrees/sillyspec-data-model-research/src/spec-dir-typo.js +50 -0
  109. package/.claude/worktrees/sillyspec-data-model-research/src/spec-sync.js +358 -0
  110. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-engine.js +171 -0
  111. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract-spec.js +550 -0
  112. package/.claude/worktrees/sillyspec-data-model-research/src/stage-contract.js +889 -0
  113. package/.claude/worktrees/sillyspec-data-model-research/src/stage-review.js +597 -0
  114. package/.claude/worktrees/sillyspec-data-model-research/src/stages/archive.js +199 -0
  115. package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm-auto.js +236 -0
  116. package/.claude/worktrees/sillyspec-data-model-research/src/stages/brainstorm.js +617 -0
  117. package/.claude/worktrees/sillyspec-data-model-research/src/stages/cmd-existence.js +145 -0
  118. package/.claude/worktrees/sillyspec-data-model-research/src/stages/doctor.js +540 -0
  119. package/.claude/worktrees/sillyspec-data-model-research/src/stages/execute.js +1275 -0
  120. package/.claude/worktrees/sillyspec-data-model-research/src/stages/explore.js +43 -0
  121. package/.claude/worktrees/sillyspec-data-model-research/src/stages/index.js +29 -0
  122. package/.claude/worktrees/sillyspec-data-model-research/src/stages/knowledge.js +514 -0
  123. package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan-postcheck.js +1344 -0
  124. package/.claude/worktrees/sillyspec-data-model-research/src/stages/plan.js +650 -0
  125. package/.claude/worktrees/sillyspec-data-model-research/src/stages/quick.js +167 -0
  126. package/.claude/worktrees/sillyspec-data-model-research/src/stages/scan.js +608 -0
  127. package/.claude/worktrees/sillyspec-data-model-research/src/stages/status.js +64 -0
  128. package/.claude/worktrees/sillyspec-data-model-research/src/stages/verify.js +246 -0
  129. package/.claude/worktrees/sillyspec-data-model-research/src/sync.js +1470 -0
  130. package/.claude/worktrees/sillyspec-data-model-research/src/task-review.js +1498 -0
  131. package/.claude/worktrees/sillyspec-data-model-research/src/taskcard.js +210 -0
  132. package/.claude/worktrees/sillyspec-data-model-research/src/verify-postcheck.js +1718 -0
  133. package/.claude/worktrees/sillyspec-data-model-research/src/verify-probes.js +277 -0
  134. package/.claude/worktrees/sillyspec-data-model-research/src/version.js +19 -0
  135. package/.claude/worktrees/sillyspec-data-model-research/src/workflow.js +600 -0
  136. package/.claude/worktrees/sillyspec-data-model-research/src/workspace.js +125 -0
  137. package/.claude/worktrees/sillyspec-data-model-research/src/worktree-apply.js +1446 -0
  138. package/.claude/worktrees/sillyspec-data-model-research/src/worktree-deps.js +576 -0
  139. package/.claude/worktrees/sillyspec-data-model-research/src/worktree.js +1715 -0
  140. package/.claude/worktrees/sillyspec-data-model-research/templates/claude-instruction.md +23 -0
  141. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/taskcard-rules.md +28 -0
  142. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/testcase-design.md +12 -0
  143. package/.claude/worktrees/sillyspec-data-model-research/templates/prompts/verify-probes.md +32 -0
  144. package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/archive-impact.yaml +79 -0
  145. package/.claude/worktrees/sillyspec-data-model-research/templates/workflows/scan-docs.yaml +132 -0
  146. package/eval/README.md +86 -0
  147. package/eval/eval.config.json +28 -0
  148. package/eval/lib/adapters/cli.mjs +63 -0
  149. package/eval/lib/adapters/dryrun.mjs +27 -0
  150. package/eval/lib/config.mjs +82 -0
  151. package/eval/lib/db.mjs +76 -0
  152. package/eval/lib/report.mjs +120 -0
  153. package/eval/lib/runner.mjs +157 -0
  154. package/eval/lib/tasks.mjs +73 -0
  155. package/eval/lib/util.mjs +107 -0
  156. package/eval/lib/workdir.mjs +30 -0
  157. package/eval/run.mjs +83 -0
  158. package/eval/tasks/001-slugify/files/package.json +6 -0
  159. package/eval/tasks/001-slugify/solution/src/slugify.mjs +26 -0
  160. package/eval/tasks/001-slugify/task.json +6 -0
  161. package/eval/tasks/001-slugify/verify.mjs +28 -0
  162. package/eval/tasks/002-each-day/files/package.json +6 -0
  163. package/eval/tasks/002-each-day/files/src/dates.mjs +4 -0
  164. package/eval/tasks/002-each-day/solution/src/dates.mjs +30 -0
  165. package/eval/tasks/002-each-day/task.json +6 -0
  166. package/eval/tasks/002-each-day/verify.mjs +31 -0
  167. package/eval/tasks/003-word-report/files/data/notes.txt +5 -0
  168. package/eval/tasks/003-word-report/files/package.json +6 -0
  169. package/eval/tasks/003-word-report/solution/bin/report.mjs +36 -0
  170. package/eval/tasks/003-word-report/task.json +6 -0
  171. package/eval/tasks/003-word-report/verify.mjs +38 -0
  172. package/package.json +4 -2
  173. package/src/agent-session-log.js +45 -5
  174. package/src/change-risk-profile.js +83 -14
  175. package/src/config-schema.js +18 -3
  176. package/src/contract-matrix.js +90 -29
  177. package/src/datetime.js +20 -0
  178. package/src/decision-distill.js +517 -0
  179. package/src/dispatch/backends/local-agent.js +4 -4
  180. package/src/docs-check.js +227 -0
  181. package/src/docs-debt.js +187 -24
  182. package/src/doctor-diagnostics.js +102 -2
  183. package/src/foreign-declared.js +78 -0
  184. package/src/index.js +89 -9
  185. package/src/init.js +9 -7
  186. package/src/knowledge-match.js +107 -6
  187. package/src/local-register.js +7 -1
  188. package/src/module-resolve.js +12 -1
  189. package/src/modules.js +2 -1
  190. package/src/plan-adopt-waves.js +126 -0
  191. package/src/quicklog.js +50 -4
  192. package/src/run/command.js +122 -43
  193. package/src/run/complete-handlers.js +48 -19
  194. package/src/run/complete.js +5 -2
  195. package/src/run/gates.js +87 -22
  196. package/src/run/prompt.js +84 -42
  197. package/src/run/quick-audit.js +11 -0
  198. package/src/run/shared.js +152 -10
  199. package/src/run.js +2 -2
  200. package/src/scan-postcheck.js +30 -17
  201. package/src/stage-contract-spec.js +2 -1
  202. package/src/stage-contract.js +68 -4
  203. package/src/stages/archive.js +53 -12
  204. package/src/stages/brainstorm-auto.js +7 -0
  205. package/src/stages/brainstorm.js +11 -0
  206. package/src/stages/doctor.js +157 -1
  207. package/src/stages/execute.js +32 -6
  208. package/src/stages/plan-postcheck.js +76 -17
  209. package/src/stages/plan.js +1 -0
  210. package/src/stages/quick.js +6 -1
  211. package/src/stages/verify.js +31 -14
  212. package/src/sync.js +41 -3
  213. package/src/task-review.js +2 -2
  214. package/src/taskcard-placeholders.js +21 -0
  215. package/src/taskcard.js +10 -1
  216. package/src/verify-postcheck.js +315 -13
  217. package/src/verify-probes.js +46 -4
  218. package/src/worktree-apply.js +184 -7
  219. package/src/worktree-deps.js +57 -0
  220. package/src/worktree.js +201 -58
  221. package/templates/prompts/taskcard-rules.md +1 -0
@@ -0,0 +1,414 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { execSync } from 'child_process';
4
+ import { gitQuiet } from './git-helper.js';
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import { checkbox, input } from '@inquirer/prompts';
8
+
9
+ // ── MCP 工具定义 ──
10
+
11
+ const MCP_TOOLS = [
12
+ {
13
+ id: 'context7',
14
+ name: 'Context7',
15
+ description: '查询最新库文档和 API 参考',
16
+ command: 'npx',
17
+ // 版本锁定(sec-c):MCP server 常驻且有系统访问能力,@latest = trust-on-future-publish,
18
+ // 上游被攻破会直接进用户 AI 工具执行环境。升级走 setup 重跑。
19
+ args: ['-y', '@upstash/context7-mcp@4.0.2'],
20
+ url: 'https://github.com/upstash/context7-mcp',
21
+ },
22
+ {
23
+ id: 'chrome-devtools',
24
+ name: 'Chrome DevTools MCP',
25
+ description: '浏览器自动化,支持 E2E 测试(需 Chrome 已运行)',
26
+ command: 'npx',
27
+ args: ['chrome-devtools-mcp@1.7.0'],
28
+ url: 'https://github.com/ChromeDevTools/chrome-devtools-mcp',
29
+ },
30
+ {
31
+ id: 'agent-browser',
32
+ name: 'Agent Browser (Vercel)',
33
+ description: 'Rust 原生浏览器 CLI,token 消耗极低,50+ 命令覆盖导航/表单/截图/网络',
34
+ command: 'npx',
35
+ args: ['agent-browser@0.34.0'],
36
+ url: 'https://github.com/vercel-labs/agent-browser',
37
+ },
38
+ {
39
+ id: 'pinchtab',
40
+ name: 'PinchTab',
41
+ description: '12MB Go 二进制,零依赖,accessibility tree 极省 token,有 MCP 支持',
42
+ command: 'npx',
43
+ args: ['pinchtab-mcp@1.4.1'],
44
+ url: 'https://github.com/pinchtab/pinchtab',
45
+ },
46
+ ];
47
+
48
+ // ── 数据库 MCP 定义(需要连接信息)──
49
+
50
+ const DB_MCP_TOOLS = [
51
+ {
52
+ id: 'postgres',
53
+ name: 'PostgreSQL',
54
+ description: '只读访问 PostgreSQL 数据库',
55
+ command: 'npx',
56
+ args: ['-y', '@modelcontextprotocol/server-postgres@0.6.2'],
57
+ envTemplate: { POSTGRES_CONNECTION_STRING: 'postgresql://user:password@localhost:5432/dbname' },
58
+ url: 'https://github.com/modelcontextprotocol/servers/tree/main/src/postgres',
59
+ },
60
+ {
61
+ id: 'sqlite',
62
+ name: 'SQLite',
63
+ description: '访问 SQLite 数据库文件',
64
+ command: 'npx',
65
+ args: ['-y', 'mcp-server-sqlite@0.0.2'],
66
+ envTemplate: { SQLITE_DB_PATH: './data.db' },
67
+ url: 'https://github.com/modelcontextprotocol/servers/tree/main/src/sqlite',
68
+ },
69
+ {
70
+ id: 'mysql',
71
+ name: 'MySQL / MariaDB',
72
+ description: '访问 MySQL / MariaDB 数据库',
73
+ command: 'npx',
74
+ // 原 @nicobailon/mysql-mcp-server 已 404(包下架),换存在的 mysql-mcp-server(dpflucas,read-only)
75
+ args: ['-y', 'mysql-mcp-server@0.1.3'],
76
+ envTemplate: { MYSQL_HOST: 'localhost', MYSQL_PORT: '3306', MYSQL_USER: 'root', MYSQL_PASSWORD: '', MYSQL_DATABASE: '' },
77
+ url: 'https://github.com/modelcontextprotocol/servers',
78
+ },
79
+ {
80
+ id: 'redis',
81
+ name: 'Redis',
82
+ description: '访问 Redis 数据库,查看缓存和键值数据',
83
+ command: 'npx',
84
+ args: ['-y', '@modelcontextprotocol/server-redis@2025.4.25'],
85
+ envTemplate: { REDIS_URL: 'redis://localhost:6379' },
86
+ url: 'https://github.com/modelcontextprotocol/servers/tree/main/src/redis',
87
+ },
88
+ ];
89
+
90
+ // ── 全局工具定义 ──
91
+
92
+ const GLOBAL_TOOLS = [
93
+ {
94
+ id: 'playwright',
95
+ name: 'Playwright',
96
+ description: 'E2E 测试框架,支持浏览器自动化测试',
97
+ checkCommand: 'npx playwright --version',
98
+ installCommand: 'npm install -g @playwright/test && npx playwright install chromium',
99
+ url: 'https://playwright.dev',
100
+ },
101
+ ];
102
+
103
+ // ── MCP 配置文件路径 ──
104
+
105
+ const MCP_CONFIG_PATHS = [
106
+ { tool: 'Claude Code', path: '.claude/mcp.json', key: 'claude' },
107
+ { tool: 'Cursor', path: '.cursor/mcp.json', key: 'cursor' },
108
+ ];
109
+
110
+ // ── 工具函数 ──
111
+
112
+ function readMcpConfig(dir, configPath) {
113
+ const fullPath = join(dir, configPath);
114
+ if (!existsSync(fullPath)) return null;
115
+ try {
116
+ return JSON.parse(readFileSync(fullPath, 'utf8'));
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+
122
+ function writeMcpConfig(dir, configPath, config) {
123
+ const fullPath = join(dir, configPath);
124
+ mkdirSync(dirname(fullPath), { recursive: true });
125
+ writeFileSync(fullPath, JSON.stringify(config, null, 2) + '\n');
126
+ }
127
+
128
+ function installMcp(config, mcpTool) {
129
+ if (!config.mcpServers) config.mcpServers = {};
130
+ const entry = {
131
+ command: mcpTool.command,
132
+ args: mcpTool.args,
133
+ };
134
+ if (mcpTool.env) entry.env = mcpTool.env;
135
+ config.mcpServers[mcpTool.id] = entry;
136
+ return config;
137
+ }
138
+
139
+ function hasMcpInstalled(config, mcpId) {
140
+ return config?.mcpServers?.[mcpId] != null;
141
+ }
142
+
143
+ // ── 命令实现 ──
144
+
145
+ export async function cmdSetup(dir, options = {}) {
146
+ const { json } = options;
147
+
148
+ // 检查哪些 AI 工具有配置文件
149
+ const availableTools = MCP_CONFIG_PATHS.filter(({ path }) => {
150
+ return existsSync(join(dir, path));
151
+ });
152
+
153
+ // --list 模式:只查看状态(不依赖 AI 工具配置)
154
+ if (options.list) {
155
+ const results = {};
156
+
157
+ // MCP 状态
158
+ for (const { tool, path } of availableTools) {
159
+ const config = readMcpConfig(dir, path);
160
+ const mcpStatus = {};
161
+ for (const mcp of MCP_TOOLS) {
162
+ mcpStatus[mcp.id] = hasMcpInstalled(config, mcp.id);
163
+ }
164
+ for (const db of DB_MCP_TOOLS) {
165
+ mcpStatus[db.id] = hasMcpInstalled(config, db.id);
166
+ }
167
+ results[tool] = { configPath: path, mcp: mcpStatus };
168
+ }
169
+
170
+ // 全局工具状态
171
+ results['全局工具'] = {};
172
+ for (const g of GLOBAL_TOOLS) {
173
+ try {
174
+ execSync(g.checkCommand, { stdio: 'pipe', encoding: 'utf8' });
175
+ results['全局工具'][g.id] = true;
176
+ } catch {
177
+ results['全局工具'][g.id] = false;
178
+ }
179
+ }
180
+
181
+ if (json) {
182
+ console.log(JSON.stringify(results, null, 2));
183
+ } else {
184
+ console.log('');
185
+ for (const [tool, data] of Object.entries(results)) {
186
+ if (data.mcp) {
187
+ console.log(chalk.bold(`📋 ${tool} (${data.configPath})`));
188
+ for (const mcp of MCP_TOOLS) {
189
+ const status = data.mcp[mcp.id] ? chalk.green('✅') : chalk.gray('⬜');
190
+ console.log(` ${status} ${mcp.name} — ${mcp.description}`);
191
+ }
192
+ for (const db of DB_MCP_TOOLS) {
193
+ const status = data.mcp[db.id] ? chalk.green('✅') : chalk.gray('⬜');
194
+ console.log(` ${status} ${db.name} — ${db.description}`);
195
+ }
196
+ } else {
197
+ console.log(chalk.bold(`🛠️ ${tool}`));
198
+ for (const g of GLOBAL_TOOLS) {
199
+ const status = data[g.id] ? chalk.green('✅') : chalk.gray('⬜');
200
+ console.log(` ${status} ${g.name} — ${g.description}`);
201
+ }
202
+ }
203
+ console.log('');
204
+ }
205
+ }
206
+ return;
207
+ }
208
+
209
+ // 非交互模式(或没有 AI 工具但有全局工具要装)
210
+ if (!process.stdin.isTTY) {
211
+ if (json) {
212
+ console.log(JSON.stringify({ message: '交互模式需要 TTY,请运行 sillyspec setup' }));
213
+ } else {
214
+ console.log(chalk.yellow('⚠️ 请在交互式终端运行 sillyspec setup'));
215
+ }
216
+ return;
217
+ }
218
+
219
+ // ── 交互式安装 ──
220
+
221
+ console.log('');
222
+ console.log(chalk.cyan('🔧 SillySpec Setup — 增强工具安装'));
223
+ console.log('');
224
+
225
+ if (availableTools.length === 0) {
226
+ console.log(chalk.dim(' 未检测到 AI 工具配置文件,MCP 工具将跳过。'));
227
+ console.log(chalk.dim(' 请先运行 sillyspec init 初始化项目。'));
228
+ console.log('');
229
+ }
230
+
231
+ console.log(' 选择要安装的工具(已安装的会跳过):');
232
+ console.log('');
233
+
234
+ // ── MCP 工具选择 ──
235
+
236
+ const installedSet = new Set();
237
+ for (const { path } of availableTools) {
238
+ const config = readMcpConfig(dir, path);
239
+ for (const mcp of MCP_TOOLS) {
240
+ if (hasMcpInstalled(config, mcp.id)) installedSet.add(mcp.id);
241
+ }
242
+ }
243
+
244
+ const mcpChoices = MCP_TOOLS.filter(m => !installedSet.has(m.id)).map(m => ({
245
+ name: `${m.name} — ${m.description}`,
246
+ value: `mcp:${m.id}`,
247
+ checked: false,
248
+ }));
249
+
250
+ // ── 全局工具选择 ──
251
+
252
+ const globalInstalled = new Set();
253
+ for (const tool of GLOBAL_TOOLS) {
254
+ try {
255
+ execSync(tool.checkCommand, { stdio: 'pipe', encoding: 'utf8' });
256
+ globalInstalled.add(tool.id);
257
+ } catch {}
258
+ }
259
+
260
+ const globalChoices = GLOBAL_TOOLS.filter(t => !globalInstalled.has(t.id)).map(t => ({
261
+ name: `${t.name} — ${t.description}(全局安装)`,
262
+ value: `global:${t.id}`,
263
+ checked: false,
264
+ }));
265
+
266
+ // ── 数据库 MCP 选择 ──
267
+
268
+ const dbInstalled = new Set();
269
+ for (const { path } of availableTools) {
270
+ const config = readMcpConfig(dir, path);
271
+ for (const db of DB_MCP_TOOLS) {
272
+ if (hasMcpInstalled(config, db.id)) dbInstalled.add(db.id);
273
+ }
274
+ }
275
+
276
+ const dbChoices = DB_MCP_TOOLS.filter(d => !dbInstalled.has(d.id)).map(d => ({
277
+ name: `${d.name} — ${d.description}(需要连接信息)`,
278
+ value: `db:${d.id}`,
279
+ checked: false,
280
+ }));
281
+
282
+ const allChoices = [
283
+ ...mcpChoices.length > 0 ? [{ name: chalk.bold('── MCP 工具(AI 能力增强)──'), value: '_mcp_header', disabled: true }] : [],
284
+ ...mcpChoices,
285
+ ...dbChoices.length > 0 ? [{ name: chalk.bold('── 数据库 MCP ──'), value: '_db_header', disabled: true }] : [],
286
+ ...dbChoices,
287
+ ...globalChoices.length > 0 ? [{ name: chalk.bold('── 全局工具 ──'), value: '_global_header', disabled: true }] : [],
288
+ ...globalChoices,
289
+ ];
290
+
291
+ if (allChoices.length === 0) {
292
+ console.log(chalk.green(' ✅ 所有推荐工具已安装!'));
293
+ return;
294
+ }
295
+
296
+ const selected = await checkbox({
297
+ message: '选择要安装的工具',
298
+ choices: allChoices,
299
+ });
300
+
301
+ if (selected.length === 0) {
302
+ console.log(chalk.dim(' 未选择任何工具,退出。'));
303
+ return;
304
+ }
305
+
306
+ const selectedMcp = MCP_TOOLS.filter(m => selected.includes(`mcp:${m.id}`));
307
+ const selectedDb = DB_MCP_TOOLS.filter(d => selected.includes(`db:${d.id}`));
308
+ const selectedGlobal = GLOBAL_TOOLS.filter(t => selected.includes(`global:${t.id}`));
309
+
310
+ // ── 收集数据库连接信息 ──
311
+
312
+ if (selectedDb.length > 0) {
313
+ console.log('');
314
+ console.log(chalk.yellow('📡 数据库连接配置'));
315
+ console.log('');
316
+
317
+ for (const db of selectedDb) {
318
+ console.log(chalk.cyan(` ${db.name}:`));
319
+ const dbWithEnv = { ...db, env: {} };
320
+ for (const [key, placeholder] of Object.entries(db.envTemplate)) {
321
+ const value = await input({
322
+ message: ` ${key}`,
323
+ default: placeholder,
324
+ });
325
+ dbWithEnv.env[key] = value;
326
+ }
327
+ // 用带 env 的版本替换
328
+ const idx = selectedDb.indexOf(db);
329
+ selectedDb[idx] = dbWithEnv;
330
+ }
331
+ }
332
+
333
+ // ── 安装 MCP 工具 ──
334
+
335
+ const allMcp = [...selectedMcp, ...selectedDb];
336
+
337
+ if (allMcp.length > 0) {
338
+ const toolChoices = availableTools.map(t => ({
339
+ name: t.tool,
340
+ value: t.key,
341
+ checked: true,
342
+ }));
343
+
344
+ const targetTools = await checkbox({
345
+ message: 'MCP 工具安装到哪些 AI 工具?',
346
+ choices: toolChoices,
347
+ });
348
+
349
+ const targets = availableTools.filter(t => targetTools.includes(t.key));
350
+
351
+ console.log('');
352
+ for (const { tool, path } of targets) {
353
+ const spinner = ora(`安装 MCP 到 ${tool}...`).start();
354
+ let config = readMcpConfig(dir, path) || { mcpServers: {} };
355
+ for (const mcp of allMcp) {
356
+ config = installMcp(config, mcp);
357
+ }
358
+ writeMcpConfig(dir, path, config);
359
+ spinner.succeed(`${tool} MCP 完成 (${allMcp.length} 个工具)`);
360
+ // 凭据提交风险提示(sec-c):.claude/mcp.json / .cursor/mcp.json 是项目共享文件、通常随 git
361
+ // 提交,而 DB MCP 的 env 含明文连接串/密码。检测 git 跟踪状态,已跟踪/未忽略时显式警告。
362
+ if (selectedDb.length > 0) {
363
+ // QUAL-01 收口:裸 execFileSync → git-helper(-C 由 helper 统一注入)。
364
+ // 语义映射:ls-files --error-unmatch 命中(exit 0)= 已跟踪 → 警告;
365
+ // check-ignore -q 命中(exit 0)= 已忽略 → 不警告
366
+ if (gitQuiet(dir, ['ls-files', '--error-unmatch', path]) !== null) {
367
+ console.warn(chalk.yellow(` ⚠️ ${path} 已被 git 跟踪——其中数据库连接串/密码会随提交进入仓库,建议加入 .gitignore 或改用环境变量`));
368
+ } else if (gitQuiet(dir, ['check-ignore', '-q', path]) === null) {
369
+ console.warn(chalk.yellow(` ⚠️ ${path} 含数据库连接串/密码且未被 .gitignore 忽略——提交前请先加入 .gitignore 或改用环境变量`));
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ // ── 安装全局工具 ──
376
+
377
+ if (selectedGlobal.length > 0) {
378
+ console.log('');
379
+ for (const tool of selectedGlobal) {
380
+ const spinner = ora(`安装 ${tool.name}...`).start();
381
+ try {
382
+ execSync(tool.installCommand, { stdio: 'pipe', encoding: 'utf8', timeout: 120000 });
383
+ spinner.succeed(`${tool.name} 安装完成`);
384
+ } catch (err) {
385
+ spinner.fail(`${tool.name} 安装失败: ${err.message}`);
386
+ }
387
+ }
388
+ }
389
+
390
+ // ── 总结 ──
391
+
392
+ console.log('');
393
+ console.log(chalk.green(' ═══════════════════════════════════════'));
394
+ console.log(chalk.green(' ✅ 工具安装完成!'));
395
+ console.log(chalk.green(' ═══════════════════════════════════════'));
396
+ console.log('');
397
+ for (const mcp of selectedMcp) {
398
+ console.log(` 🔌 ${chalk.cyan(mcp.name)} — ${mcp.description}`);
399
+ console.log(chalk.dim(` ${mcp.url}`));
400
+ }
401
+ for (const db of selectedDb) {
402
+ console.log(` 🗄️ ${chalk.cyan(db.name)} — ${db.description}`);
403
+ console.log(chalk.dim(` ${db.url}`));
404
+ }
405
+ for (const g of selectedGlobal) {
406
+ console.log(` 🛠️ ${chalk.cyan(g.name)} — ${g.description}`);
407
+ console.log(chalk.dim(` ${g.url}`));
408
+ }
409
+ console.log('');
410
+ if (selectedMcp.length > 0) {
411
+ console.log(chalk.dim(' 重启你的 AI 工具以使 MCP 配置生效。'));
412
+ }
413
+ console.log('');
414
+ }