sillyspec 3.8.5 → 3.8.7

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 +120 -0
  83. package/templates/brainstorm.md +170 -0
  84. package/{.claude/skills/sillyspec-commit/SKILL.md → templates/commit.md} +45 -29
  85. package/templates/continue.md +32 -0
  86. package/templates/execute.md +304 -0
  87. package/templates/explore.md +59 -0
  88. package/templates/export.md +21 -0
  89. package/templates/init.md +61 -0
  90. package/templates/plan.md +146 -0
  91. package/templates/quick.md +135 -0
  92. package/templates/scan-quick.md +49 -0
  93. package/templates/scan.md +156 -0
  94. package/templates/skills/playwright-e2e/SKILL.md +340 -0
  95. package/templates/status.md +75 -0
  96. package/templates/verify.md +236 -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 -259
  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
package/src/init.js CHANGED
@@ -1,32 +1,55 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync } from 'fs';
2
- import { join, resolve, dirname, basename } from 'path';
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
2
+ import { join, resolve, dirname } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import { homedir } from 'os';
5
- import { checkbox, confirm, input } from '@inquirer/prompts';
6
- import { ProgressManager } from './progress.js';
5
+ import { checkbox, select, confirm, input } from '@inquirer/prompts';
7
6
  import chalk from 'chalk';
7
+ import ora from 'ora';
8
8
 
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = dirname(__filename);
11
-
12
- // ── 递归复制目录 ──
13
- function copyDirSync(src, dst) {
14
- mkdirSync(dst, { recursive: true });
15
- for (const entry of readdirSync(src, { withFileTypes: true })) {
16
- if (entry.name === 'node_modules' || entry.name === '.git') continue;
17
- const srcPath = join(src, entry.name);
18
- const dstPath = join(dst, entry.name);
19
- if (entry.isDirectory()) {
20
- copyDirSync(srcPath, dstPath);
21
- } else if (entry.name.endsWith('.md')) {
22
- writeFileSync(dstPath, readFileSync(srcPath));
23
- }
24
- }
25
- }
11
+ const TEMPLATE_DIR = resolve(__dirname, '..', 'templates');
26
12
 
27
13
  // ── 元数据映射 ──
28
14
 
15
+ const DESCRIPTIONS = {
16
+ init: '绿地项目初始化 — 深度提问、调研、需求文档、路线图',
17
+ scan: '代码库扫描 — 支持快速扫描和深度扫描两阶段',
18
+ explore: '自由思考模式 — 讨论、画图、调研,不写代码',
19
+ brainstorm: '需求探索 — 结构化头脑风暴,生成设计文档(创建性工作前必用)',
20
+ plan: '编写实现计划 — 任务拆分与实现路径',
21
+ execute: '波次执行 — 子代理并行 + 强制 TDD + 两阶段审查',
22
+ verify: '验证实现 — 对照规范检查 + 测试套件',
23
+ archive: '归档变更 — 规范沉淀,可追溯',
24
+ commit: '智能提交 — 自动收集变更信息,生成 commit message',
25
+ status: '查看项目进度和状态',
26
+ continue: '自动判断并执行下一步',
27
+ state: '查看当前工作状态 — 显示 STATE.md 内容',
28
+
29
+ quick: '快速任务 — 跳过完整流程,直接做',
30
+ workspace: '工作区管理 — 初始化、管理多项目工作区,查看子项目状态',
31
+ export: '导出成功方案为可复用模板',
32
+ };
29
33
 
34
+ const ARG_HINTS = {
35
+ init: '[项目名]',
36
+ scan: '[可选:指定区域,如 \'api\' 或 \'auth\'] [--deep 深度扫描]',
37
+ explore: '[探索主题]',
38
+ brainstorm: '[需求或想法描述]',
39
+
40
+ plan: '[计划名]',
41
+ execute: '[任务编号或 \'all\']',
42
+ verify: '[可选:指定验证范围]',
43
+ archive: '[变更名]',
44
+ commit: '[可选:自定义 commit message]',
45
+ status: '',
46
+ continue: '',
47
+ state: '[可选备注]',
48
+
49
+ quick: '[任务描述]',
50
+ workspace: '[可选:add/remove/status/info]',
51
+ export: '<change-name> [--to <path>]',
52
+ };
30
53
 
31
54
  const VALID_TOOLS = ['claude', 'claude_skills', 'cursor', 'openclaw', 'codex', 'gemini', 'opencode'];
32
55
 
@@ -40,6 +63,8 @@ const TOOL_LABELS = {
40
63
  opencode: 'OpenCode (通过 INSTRUCTIONS.md)',
41
64
  };
42
65
 
66
+
67
+ // 指令文件工具:注入规范引用到指令文件
43
68
  const INSTRUCTION_TOOLS = ['codex', 'gemini', 'opencode'];
44
69
 
45
70
  const INSTRUCTION_FILE_MAP = {
@@ -53,16 +78,83 @@ const INJECTION_CONTENT = `## SillySpec — 规范驱动开发
53
78
  在执行开发任务时,遵循以下规范:
54
79
 
55
80
  ### 代码规范
56
- - 写代码前先读取 \`.sillyspec/docs/<project>/scan/CONVENTIONS.md\`(代码风格)和 \`.sillyspec/docs/<project>/scan/ARCHITECTURE.md\`(架构)
81
+ - 写代码前先读取 \`.sillyspec/codebase/CONVENTIONS.md\`(代码风格)和 \`.sillyspec/codebase/ARCHITECTURE.md\`(架构)
57
82
  - 调用已有方法前,用 grep 确认方法存在,不许编造
58
- - 遵循 \`.sillyspec/docs/<project>/scan/CONVENTIONS.md\` 中的代码风格
83
+ - 遵循 \`.sillyspec/codebase/CONVENTIONS.md\` 中的代码风格
59
84
 
60
85
  ### 工作流程
61
- - 读取 \`.sillyspec/.runtime/progress.json\` 确认当前阶段(使用 \`sillyspec progress show\`)
86
+ - 读取 \`.sillyspec/STATE.md\` 确认当前阶段
62
87
  - 各阶段产出文件位于 \`.sillyspec/changes/<变更名>/\` 下
88
+ - 详细流程参考模板文件:\`.sillyspec/.templates/\`(brainstorm.md, plan.md, execute.md 等)
63
89
  `;
64
90
 
65
- // ── 注入指令文件 ──
91
+ // ── 适配器 ──
92
+
93
+ function generateClaude(projectDir, name, desc, body, argHint, version) {
94
+ const outDir = join(projectDir, '.claude', 'commands', 'sillyspec');
95
+ mkdirSync(outDir, { recursive: true });
96
+ writeFileSync(join(outDir, `${name}.md`),
97
+ `---
98
+ description: ${desc}
99
+ argument-hint: "${argHint}"
100
+ version: "${version}"
101
+ ---
102
+
103
+ ${body}`
104
+ );
105
+ }
106
+
107
+ function generateClaudeSkills(projectDir, name, desc, body, argHint, version) {
108
+ const outDir = join(projectDir, '.claude', 'skills', `sillyspec-${name}`);
109
+ mkdirSync(outDir, { recursive: true });
110
+ writeFileSync(join(outDir, 'SKILL.md'),
111
+ `---
112
+ name: sillyspec:${name}
113
+ description: ${desc}
114
+ version: "${version}"
115
+ ---
116
+
117
+ ${body}`
118
+ );
119
+ }
120
+
121
+ function generateCursor(projectDir, name, desc, body, argHint, version) {
122
+ const outDir = join(projectDir, '.cursor', 'commands');
123
+ mkdirSync(outDir, { recursive: true });
124
+ writeFileSync(join(outDir, `sillyspec-${name}.md`),
125
+ `---
126
+ name: /sillyspec-${name}
127
+ id: sillyspec-${name}
128
+ description: ${desc}
129
+ version: "${version}"
130
+ ---
131
+
132
+ ${body}`
133
+ );
134
+ }
135
+
136
+ function generateOpenclaw(projectDir, name, desc, body, argHint, version) {
137
+ const outDir = join(projectDir, '.openclaw', 'skills', `sillyspec-${name}`);
138
+ mkdirSync(outDir, { recursive: true });
139
+ writeFileSync(join(outDir, 'SKILL.md'),
140
+ `---
141
+ name: sillyspec:${name}
142
+ description: ${desc}
143
+ version: "${version}"
144
+ ---
145
+
146
+ ${body}`
147
+ );
148
+ }
149
+
150
+ const GENERATORS = {
151
+ claude: generateClaude,
152
+ claude_skills: generateClaudeSkills,
153
+ cursor: generateCursor,
154
+ openclaw: generateOpenclaw,
155
+ };
156
+
157
+ // ── 指令文件注入 ──
66
158
 
67
159
  function injectInstructions(tool, projectDir) {
68
160
  const fileName = INSTRUCTION_FILE_MAP[tool];
@@ -106,33 +198,22 @@ function isTTY() {
106
198
 
107
199
  // ── 核心安装逻辑 ──
108
200
 
109
- async function doInstall(projectDir, tools, subprojects = []) {
201
+ async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
110
202
  // 创建基础目录
111
- // .sillyspec/projects/ → 项目注册表
112
- // .sillyspec/docs/<name>/统一文档中心
113
- // .sillyspec/knowledge/ 跨项目共享知识库
114
- // .sillyspec/.runtime/ progress (gitignored)
115
-
116
- // 注册当前项目到 projects/
117
- const projectName = basename(projectDir) || 'project';
118
- const projectsDir = join(projectDir, '.sillyspec', 'projects');
119
- mkdirSync(projectsDir, { recursive: true });
120
- const projectYamlPath = join(projectsDir, `${projectName}.yaml`);
121
- if (!existsSync(projectYamlPath)) {
122
- writeFileSync(projectYamlPath, `name: ${projectName}\npath: .\nstatus: active\n`);
203
+ // .sillyspec/codebase/ → scan
204
+ // .sillyspec/codebase/details/scan (deep)
205
+ // .sillyspec/changes/ brainstorm/plan
206
+ // .sillyspec/changes/archive/ archive
207
+ // .sillyspec/quicklog/ → quick
208
+ // .sillyspec/knowledge/ → archive (spec 沉淀)
209
+ // .sillyspec/.runtime/ → runtime data (gitignored)
210
+ // (plan 内容已合并到 tasks.md)
211
+ if (isWorkspace) {
212
+ mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
213
+ mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
123
214
  }
124
215
 
125
- // 创建 docs/<projectName>/scan/ 子目录(代码扫描结果)
126
- const scanDir = join(projectDir, '.sillyspec', 'docs', projectName, 'scan');
127
- mkdirSync(scanDir, { recursive: true });
128
- const gitkeepPath = join(scanDir, '.gitkeep');
129
- if (!existsSync(gitkeepPath)) writeFileSync(gitkeepPath, '');
130
-
131
- // 创建 shared/workspace 目录
132
- mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
133
- mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
134
-
135
- // 创建知识库骨架
216
+ // 创建知识库骨架(所有模式)
136
217
  const knowledgeDir = join(projectDir, '.sillyspec', 'knowledge');
137
218
  mkdirSync(knowledgeDir, { recursive: true });
138
219
  const indexPath = join(knowledgeDir, 'INDEX.md');
@@ -150,13 +231,6 @@ async function doInstall(projectDir, tools, subprojects = []) {
150
231
  mkdirSync(join(runtimeDir, sub), { recursive: true });
151
232
  }
152
233
 
153
- // 创建初始 progress.json
154
- const progressPath = join(runtimeDir, 'progress.json');
155
- if (!existsSync(progressPath)) {
156
- const pm = new ProgressManager();
157
- pm.init(projectDir);
158
- }
159
-
160
234
  // 创建初始 user-inputs.md
161
235
  const inputsPath = join(runtimeDir, 'user-inputs.md');
162
236
  if (!existsSync(inputsPath)) {
@@ -164,7 +238,7 @@ async function doInstall(projectDir, tools, subprojects = []) {
164
238
  }
165
239
 
166
240
  const gitignorePath = join(projectDir, '.gitignore');
167
- const ignoreRules = ['.sillyspec/codebase/SCAN-RAW.md', '.sillyspec/local.yaml', '.sillyspec/.runtime/'];
241
+ const ignoreRules = ['.sillyspec/STATE.md', '.sillyspec/codebase/SCAN-RAW.md', '.sillyspec/local.yaml', '.sillyspec/.runtime/'];
168
242
  if (existsSync(gitignorePath)) {
169
243
  const content = readFileSync(gitignorePath, 'utf8');
170
244
  let updated = content.trimEnd();
@@ -178,38 +252,83 @@ async function doInstall(projectDir, tools, subprojects = []) {
178
252
  writeFileSync(gitignorePath, ignoreRules.join('\n') + '\n');
179
253
  }
180
254
 
181
- // 注入指令文件(codex/gemini/opencode)
255
+ // 生成 slash command 文件
256
+ const templateFiles = readdirSync(TEMPLATE_DIR).filter(f => f.endsWith('.md'));
257
+ let count = 0;
258
+
182
259
  for (let i = 0; i < tools.length; i++) {
183
260
  const toolName = tools[i];
261
+ const label = TOOL_LABELS[toolName] || toolName;
262
+
184
263
  if (INSTRUCTION_TOOLS.includes(toolName)) {
185
- injectInstructions(toolName, projectDir);
264
+ const spinner = ora(`安装 ${label}... (${i + 1}/${tools.length})`).start();
265
+ try {
266
+ injectInstructions(toolName, projectDir);
267
+ // 复制模板文件到 .sillyspec/.templates/
268
+ const templatesSourceDir = join(TEMPLATE_DIR);
269
+ const templatesDir = join(projectDir, '.sillyspec', '.templates');
270
+ if (!existsSync(templatesDir)) {
271
+ mkdirSync(templatesDir, { recursive: true });
272
+ for (const file of readdirSync(templatesSourceDir)) {
273
+ if (file.endsWith('.md')) {
274
+ copyFileSync(join(templatesSourceDir, file), join(templatesDir, file));
275
+ }
276
+ }
277
+ }
278
+ spinner.succeed(`${label} 完成`);
279
+ count++;
280
+ } catch (err) {
281
+ spinner.fail(`${label} 失败: ${err.message}`);
282
+ throw err;
283
+ }
284
+ continue;
285
+ }
286
+
287
+ const spinner = ora(`安装 ${label}... (${i + 1}/${tools.length})`).start();
288
+ try {
289
+ const gen = GENERATORS[toolName];
290
+ const ver = getVersion();
291
+ for (const file of templateFiles) {
292
+ const name = file.replace('.md', '');
293
+ const desc = DESCRIPTIONS[name] || `SillySpec ${name}`;
294
+ const argHint = ARG_HINTS[name] || '';
295
+ const body = readFileSync(join(TEMPLATE_DIR, file), 'utf8');
296
+ gen(projectDir, name, desc, body, argHint, ver);
297
+ count++;
298
+ }
299
+ spinner.succeed(`${label} 完成`);
300
+ } catch (err) {
301
+ spinner.fail(`${label} 失败: ${err.message}`);
302
+ throw err;
186
303
  }
187
304
  }
188
305
 
189
- // 复制 skills 到 .claude/skills/(给 Claude Code 使用)
190
- const claudeSkillsDir = join(projectDir, '.claude', 'skills');
191
- // 优先从 npm 包自带位置复制,其次从 ~/.agents/skills/
192
- const npmSkillsDir = join(__dirname, '..', '.claude', 'skills');
193
- const localSkillsDir = join(homedir(), '.agents', 'skills');
194
- const skillsSource = existsSync(npmSkillsDir) ? npmSkillsDir : existsSync(localSkillsDir) ? localSkillsDir : null;
195
- if (skillsSource) {
196
- const sillyspecSkills = readdirSync(skillsSource).filter(f => f.startsWith('sillyspec-') && statSync(join(skillsSource, f)).isDirectory());
197
- if (sillyspecSkills.length > 0) {
198
- mkdirSync(claudeSkillsDir, { recursive: true });
199
- for (const skill of sillyspecSkills) {
200
- copyDirSync(join(skillsSource, skill), join(claudeSkillsDir, skill));
306
+ // 工作区子项目配置 projects/*.yaml
307
+ if (isWorkspace && subprojects.length > 0) {
308
+ const projectsDir = join(projectDir, '.sillyspec', 'projects');
309
+ mkdirSync(projectsDir, { recursive: true });
310
+ for (const p of subprojects) {
311
+ const pFile = join(projectsDir, `${p.name}.yaml`);
312
+ if (!existsSync(pFile)) {
313
+ writeFileSync(pFile,
314
+ `# ${p.name}
315
+ name: ${p.name}
316
+ path: ${p.path}
317
+ role: ${p.role || p.name}${p.repo ? `\nrepo: ${p.repo}` : ''}
318
+ `
319
+ );
201
320
  }
202
- console.log(chalk.green(' ✓ Claude Code skills 已同步 (' + sillyspecSkills.length + ' 个)'));
203
321
  }
204
- } else {
205
- console.log(chalk.yellow(' ⚠ 未找到 skills 目录,跳过 Claude Code skills 同步'));
206
322
  }
323
+
324
+ return count;
207
325
  }
208
326
 
209
327
  // ── 安装完成总结 ──
210
328
 
211
- function showSummary(version, tools) {
329
+ function showSummary(version, tools, isWorkspace, count) {
212
330
  const toolLabels = tools.map(t => TOOL_LABELS[t] || t);
331
+ const mode = isWorkspace ? '多项目工作区' : '单项目';
213
332
 
214
333
  console.log('');
215
334
  console.log(chalk.green(' ═══════════════════════════════════════'));
@@ -217,13 +336,20 @@ function showSummary(version, tools) {
217
336
  console.log(chalk.green(' ═══════════════════════════════════════'));
218
337
  console.log('');
219
338
  console.log(` 已安装工具: ${chalk.cyan(toolLabels.join(', '))}`);
339
+ console.log(` 模式: ${chalk.yellow(mode)}`);
340
+ console.log('');
341
+ console.log(` 📄 ${count} 个命令已就绪`);
220
342
  console.log(' 📁 .sillyspec/ — 项目规范目录');
221
343
  console.log('');
222
- console.log(' 下一步:使用 AI 技能开始工作');
223
- console.log(' OpenClaw: ' + chalk.bold('/sillyspec:brainstorm'));
224
- console.log(' Claude Code: ' + chalk.bold('/sillyspec:brainstorm'));
344
+ console.log(' 入口选择:');
345
+ console.log(' 全新项目:' + chalk.bold('/sillyspec:init'));
346
+ console.log(' 已有代码:' + chalk.bold('/sillyspec:scan'));
347
+ console.log(' 自由思考:' + chalk.bold('/sillyspec:explore "你的想法"'));
348
+ console.log('');
349
+ console.log(chalk.gray(' 重启你的 AI 工具以使 slash commands 生效。'));
225
350
  console.log('');
226
351
  console.log(chalk.dim(' 💡 推荐安装 MCP 工具增强 AI 能力:sillyspec setup'));
352
+ console.log(chalk.dim(' Context7 — 查最新文档 | grep.app — 搜开源实现 | Chrome DevTools — 浏览器自动化'));
227
353
  console.log('');
228
354
  }
229
355
 
@@ -241,7 +367,7 @@ export function getVersion() {
241
367
  // ── 主命令 ──
242
368
 
243
369
  export async function cmdInit(projectDir, options = {}) {
244
- const { tool, interactive } = options;
370
+ const { tool, workspace, interactive } = options;
245
371
  const version = getVersion();
246
372
 
247
373
  // ── 交互式模式(--interactive 或 -i)──
@@ -273,11 +399,20 @@ export async function cmdInit(projectDir, options = {}) {
273
399
  validate: (answer) => answer.length > 0 || '至少选择一个工具',
274
400
  });
275
401
 
276
- // 子项目引导(始终执行)
402
+ // 工作区模式
403
+ const isWorkspace = await select({
404
+ message: '选择项目模式',
405
+ choices: [
406
+ { name: '单项目模式', value: 'false' },
407
+ { name: '多项目工作区', value: 'true' },
408
+ ],
409
+ }) === 'true';
410
+
411
+ // 工作区子项目引导
277
412
  let subprojects = [];
278
- {
413
+ if (isWorkspace) {
279
414
  console.log('');
280
- console.log(chalk.yellow('📋 添加子项目'));
415
+ console.log(chalk.yellow('📋 工作区模式 — 添加子项目'));
281
416
  console.log(chalk.dim(' 子项目是工作区中的独立项目目录(如 frontend/、backend/)'));
282
417
  console.log('');
283
418
 
@@ -340,8 +475,8 @@ export async function cmdInit(projectDir, options = {}) {
340
475
  }
341
476
 
342
477
  console.log('');
343
- await doInstall(projectDir, selectedTools, subprojects);
344
- showSummary(version, selectedTools);
478
+ const count = await doInstall(projectDir, selectedTools, isWorkspace, subprojects);
479
+ showSummary(version, selectedTools, isWorkspace, count);
345
480
  return;
346
481
  }
347
482
 
@@ -359,17 +494,23 @@ export async function cmdInit(projectDir, options = {}) {
359
494
  tools = detectTools(projectDir);
360
495
  }
361
496
 
362
- await doInstall(projectDir, tools);
497
+ const count = await doInstall(projectDir, tools, !!workspace);
363
498
 
364
499
  console.log('');
365
500
  console.log(chalk.green(` ✅ SillySpec v${version} 安装完成!`));
366
501
  console.log('');
502
+ console.log(` 📄 ${count} 个命令已就绪`);
367
503
  console.log(' 📁 .sillyspec/ — 项目规范目录');
368
504
  console.log('');
369
- console.log(' 下一步:使用 AI 技能开始工作');
370
- console.log(` OpenClaw: ${chalk.bold('/sillyspec:brainstorm')}`);
371
- console.log(` Claude Code: ${chalk.bold('/sillyspec:brainstorm')}`);
505
+ console.log(' 下一步:');
506
+ console.log(` 全新项目 → ${chalk.bold('/sillyspec:init')}`);
507
+ console.log(` 已有代码 ${chalk.bold('/sillyspec:scan')}`);
508
+ console.log(` 自由探索 → ${chalk.bold('/sillyspec:explore "你的想法"')}`);
509
+ if (workspace) {
510
+ console.log(` 管理子项目 → ${chalk.bold('/sillyspec:workspace add')}`);
511
+ }
372
512
  console.log('');
373
513
  console.log(chalk.dim(' 💡 增强能力:sillyspec setup(安装 MCP 工具)'));
514
+ console.log(chalk.dim(' 💡 完整配置:sillyspec init --interactive'));
374
515
  console.log('');
375
516
  }
package/src/setup.js CHANGED
@@ -11,7 +11,15 @@ const __dirname = dirname(__filename);
11
11
 
12
12
  // ── Skill 定义 ──
13
13
 
14
- const SKILLS = [];
14
+ const SKILLS = [
15
+ {
16
+ id: 'playwright-e2e',
17
+ name: 'Playwright E2E 测试参考',
18
+ description: 'E2E 测试编写最佳实践,AI 执行测试任务时自动读取',
19
+ source: join(__dirname, '..', 'templates', 'skills', 'playwright-e2e'),
20
+ target: 'playwright-e2e',
21
+ },
22
+ ];
15
23
 
16
24
  // ── MCP 工具定义 ──
17
25
 
@@ -0,0 +1,120 @@
1
+ ## 交互规范
2
+ **当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
3
+
4
+ ## 核心约束(必须遵守)
5
+ - ❌ 未经验证就归档(必须先确认验证通过)
6
+ - ❌ 未勾选的 checkbox 未告知用户就归档
7
+ - ❌ 归档后留下活跃变更的残留状态
8
+ - ❌ 覆盖已存在的归档目录
9
+
10
+ ## 变更名称
11
+ $ARGUMENTS
12
+
13
+ ---
14
+
15
+ ## 流程
16
+
17
+ ### 1. 前置检查(门禁)
18
+
19
+ 读取 `.sillyspec/changes/<change-name>/` 下所有必要文件,逐项检查:
20
+
21
+ - [ ] **文件完整性:** 检查 `design.md` 是否存在,缺失则警告
22
+ - [ ] **任务完成度:** 读取 `tasks.md`,统计已完成/未完成任务数。**有未完成的 → 用 AskUserQuestion 询问:**
23
+ - ① 继续归档(未完成任务将被标记完成)
24
+ - ② 取消,回去完成任务
25
+
26
+ > 任一门禁不通过且用户选择取消 → 终止流程。
27
+
28
+ ### 2. 展示归档清单
29
+
30
+ 展示即将归档的内容摘要:
31
+ - 变更目录名
32
+ - 包含的文件列表
33
+ - 任务完成统计(✅ 已完成 / ⬜ 未完成)
34
+ - 一句话总结本次变更
35
+ - quicklog 修改记录(如有 `.sillyspec/changes/<change-name>/quicklog/` 目录)
36
+
37
+ ### 3. Spec 沉淀
38
+
39
+ 将 `.sillyspec/changes/<change-name>/` 下的设计文档**复制到 `.sillyspec/knowledge/` 主目录**,确保已完成的设计规范可被后续变更参考。如目标已存在同名文件则跳过并提示。
40
+
41
+ ### 4. 用户确认
42
+
43
+ 用 AskUserQuestion 让用户确认:
44
+ - ① 确认归档
45
+ - ② 取消
46
+
47
+ ### 4.5 生成归档摘要
48
+
49
+ 在变更目录下自动生成 `SUMMARY.md`:
50
+
51
+ ```markdown
52
+ # <变更名> 归档
53
+
54
+ - 创建:YYYY-MM-DD
55
+ - 完成:YYYY-MM-DD
56
+ - 涉及阶段:brainstorm → plan → execute → verify
57
+
58
+ ## 关键决策
59
+ - (从 design.md 提取 3-5 条核心决策)
60
+
61
+ ## 产出文件
62
+ - design.md — 设计文档
63
+ - tasks.md — 任务清单
64
+ - quicklog/ — 关联 quick 修改(N次)
65
+ - quick1: 描述
66
+ - quick2: 描述
67
+
68
+ ## 代码变更统计
69
+ - 新增 X 文件,修改 Y 文件,删除 Z 文件
70
+ - 详见 CHANGELOG.md
71
+ ```
72
+
73
+ 在变更目录下自动生成 `CHANGELOG.md`:
74
+
75
+ ```bash
76
+ # 收集该变更相关的 git commit(按变更名过滤或按时间范围)
77
+ git log --oneline --no-merges -- .sillyspec/changes/<change-name>/ 2>/dev/null
78
+ # 以及变更目录创建后的所有 commit
79
+ git log --oneline --no-merges --since="<创建时间>" -- "*.ts" "*.js" "*.vue" "*.java" 2>/dev/null
80
+ ```
81
+
82
+ 写入 CHANGELOG.md,格式:
83
+ ```markdown
84
+ # <变更名> 变更日志
85
+
86
+ ## brainstorm 阶段
87
+ - (相关 commit)
88
+
89
+ ## plan 阶段
90
+ - (相关 commit)
91
+
92
+ ## execute 阶段
93
+ - (相关 commit)
94
+
95
+ ## quick 修改
96
+ - (相关 commit)
97
+
98
+ ## verify 阶段
99
+ - (相关 commit)
100
+ ```
101
+
102
+ ### 5. 执行归档
103
+
104
+ - 目标路径:`.sillyspec/changes/archive/YYYY-MM-DD-<change-name>/`
105
+ - **检查目标路径是否已存在**,存在则中止并报错,防止覆盖
106
+ - 移动变更目录到归档路径
107
+
108
+ ### 6. 归档后更新
109
+
110
+ - **tasks.md:** 确保所有 checkbox 都已勾选 `[x]`
111
+ - **ROADMAP.md**(如存在):标记对应 Phase 已完成
112
+ - **Git 暂存:** `git add .sillyspec/`
113
+
114
+ **工作区模式下:** 如果变更属于某个子项目,cd 到子项目目录执行 git add。工作区根目录无 git 则跳过。
115
+
116
+ 💡 归档产出已暂存。准备好后用 `/sillyspec:commit` 提交。
117
+
118
+ ### 最后说:
119
+
120
+ > ✅ 变更 `<change-name>` 已归档到 `archive/YYYY-MM-DD-<change-name>/`。继续:`/sillyspec:brainstorm "新想法"`