sillyspec 3.9.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +17 -0
  2. package/.claude/skills/sillyspec-auto/SKILL.md +78 -0
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +17 -0
  4. package/{templates/commit.md → .claude/skills/sillyspec-commit/SKILL.md} +32 -47
  5. package/.claude/skills/sillyspec-continue/SKILL.md +45 -0
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +27 -0
  7. package/.claude/skills/sillyspec-execute/SKILL.md +17 -0
  8. package/.claude/skills/sillyspec-explore/SKILL.md +105 -0
  9. package/.claude/skills/sillyspec-export/SKILL.md +53 -0
  10. package/.claude/skills/sillyspec-init/SKILL.md +170 -0
  11. package/.claude/skills/sillyspec-plan/SKILL.md +17 -0
  12. package/.claude/skills/sillyspec-propose/SKILL.md +17 -0
  13. package/.claude/skills/sillyspec-quick/SKILL.md +17 -0
  14. package/.claude/skills/sillyspec-resume/SKILL.md +111 -0
  15. package/.claude/skills/sillyspec-scan/SKILL.md +17 -0
  16. package/.claude/skills/sillyspec-state/SKILL.md +54 -0
  17. package/.claude/skills/sillyspec-status/SKILL.md +17 -0
  18. package/.claude/skills/sillyspec-verify/SKILL.md +17 -0
  19. package/.claude/skills/sillyspec-workspace/SKILL.md +149 -0
  20. package/README.md +19 -11
  21. package/SKILL.md +15 -10
  22. package/package.json +7 -9
  23. package/packages/dashboard/dist/assets/index-BcM2J-hv.css +1 -0
  24. package/packages/dashboard/dist/assets/index-DpLHK4jv.js +7446 -0
  25. package/packages/dashboard/dist/index.html +16 -16
  26. package/packages/dashboard/dist/prototype-dashboard.html +836 -0
  27. package/packages/dashboard/dist/prototype-overview.html +256 -0
  28. package/packages/dashboard/package-lock.json +226 -6
  29. package/packages/dashboard/package.json +8 -5
  30. package/packages/dashboard/public/logo.jpg +0 -0
  31. package/packages/dashboard/public/prototype-dashboard.html +836 -0
  32. package/packages/dashboard/public/prototype-overview.html +256 -0
  33. package/packages/dashboard/server/executor.js +1 -1
  34. package/packages/dashboard/server/index.js +341 -113
  35. package/packages/dashboard/server/parser.js +442 -30
  36. package/packages/dashboard/server/watcher.js +214 -134
  37. package/packages/dashboard/src/App.vue +475 -71
  38. package/packages/dashboard/src/components/ActionBar.vue +36 -43
  39. package/packages/dashboard/src/components/CommandPalette.vue +45 -66
  40. package/packages/dashboard/src/components/DetailPanel.vue +68 -53
  41. package/packages/dashboard/src/components/DocPreview.vue +257 -0
  42. package/packages/dashboard/src/components/DocTree.vue +114 -0
  43. package/packages/dashboard/src/components/HResizeHandle.vue +48 -0
  44. package/packages/dashboard/src/components/LogStream.vue +13 -33
  45. package/packages/dashboard/src/components/PipelineStage.vue +8 -8
  46. package/packages/dashboard/src/components/PipelineView.vue +99 -45
  47. package/packages/dashboard/src/components/ProjectCard.vue +187 -0
  48. package/packages/dashboard/src/components/ProjectList.vue +103 -45
  49. package/packages/dashboard/src/components/ProjectOverview.vue +152 -0
  50. package/packages/dashboard/src/components/StageBadge.vue +13 -13
  51. package/packages/dashboard/src/components/StepCard.vue +15 -15
  52. package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
  53. package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -0
  54. package/packages/dashboard/src/components/detail/GitDetail.vue +61 -0
  55. package/packages/dashboard/src/components/detail/TechDetail.vue +43 -0
  56. package/packages/dashboard/src/composables/useDashboard.js +48 -6
  57. package/packages/dashboard/src/composables/useKeyboard.js +6 -4
  58. package/packages/dashboard/src/composables/useLayout.js +131 -0
  59. package/packages/dashboard/src/main.js +4 -1
  60. package/packages/dashboard/src/style.css +17 -17
  61. package/src/index.js +141 -22
  62. package/src/init.js +93 -231
  63. package/src/migrate.js +117 -0
  64. package/src/progress.js +460 -0
  65. package/src/run.js +635 -0
  66. package/src/setup.js +2 -72
  67. package/src/stages/archive.js +54 -0
  68. package/src/stages/brainstorm.js +264 -0
  69. package/src/stages/doctor.js +303 -0
  70. package/src/stages/execute.js +287 -0
  71. package/src/stages/explore.js +34 -0
  72. package/src/stages/index.js +28 -0
  73. package/src/stages/plan.js +354 -0
  74. package/src/stages/propose.js +115 -0
  75. package/src/stages/quick.js +64 -0
  76. package/src/stages/scan.js +141 -0
  77. package/src/stages/status.js +65 -0
  78. package/src/stages/verify.js +135 -0
  79. package/.sillyspec/changes/dashboard/design.md +0 -219
  80. package/.sillyspec/plans/2026-04-05-dashboard.md +0 -737
  81. package/.sillyspec/specs/2026-04-05-dashboard-design.md +0 -206
  82. package/dist/steps/brainstorm/01-load-context.md +0 -30
  83. package/dist/steps/brainstorm/02-reuse-check.md +0 -6
  84. package/dist/steps/brainstorm/03-prototype-analysis.md +0 -11
  85. package/dist/steps/brainstorm/04-module-split.md +0 -23
  86. package/dist/steps/brainstorm/05-dialog-explore.md +0 -8
  87. package/dist/steps/brainstorm/06-propose-approaches.md +0 -3
  88. package/dist/steps/brainstorm/07-present-design.md +0 -3
  89. package/dist/steps/brainstorm/08-write-design.md +0 -21
  90. package/dist/steps/brainstorm/09-self-review.md +0 -15
  91. package/dist/steps/brainstorm/10-user-confirm.md +0 -3
  92. package/dist/steps/brainstorm/11-output-spec.md +0 -7
  93. package/dist/steps/brainstorm/manifest.yaml +0 -26
  94. package/dist/steps/execute/01-load-context.md +0 -41
  95. package/dist/steps/execute/02-scan-conventions.md +0 -47
  96. package/dist/steps/execute/03-skill-mcp.md +0 -19
  97. package/dist/steps/execute/04-assign-task.md +0 -22
  98. package/dist/steps/execute/04b-prompt-template.md +0 -54
  99. package/dist/steps/execute/05-write-test.md +0 -7
  100. package/dist/steps/execute/06-write-code.md +0 -8
  101. package/dist/steps/execute/07-run-test.md +0 -26
  102. package/dist/steps/execute/08-fix-issues.md +0 -28
  103. package/dist/steps/execute/09-next-task.md +0 -33
  104. package/dist/steps/execute/manifest.yaml +0 -28
  105. package/dist/steps/plan/01-load-context.md +0 -22
  106. package/dist/steps/plan/02-anchor-confirm.md +0 -1
  107. package/dist/steps/plan/03-expand-tasks.md +0 -33
  108. package/dist/steps/plan/04-mark-order.md +0 -15
  109. package/dist/steps/plan/05-e2e-planning.md +0 -17
  110. package/dist/steps/plan/06-self-check.md +0 -16
  111. package/dist/steps/plan/07-save.md +0 -1
  112. package/dist/steps/plan/manifest.yaml +0 -18
  113. package/dist/steps/scan/01-env-detect.md +0 -51
  114. package/dist/steps/scan/02-tech-stack.md +0 -16
  115. package/dist/steps/scan/03-conventions.md +0 -16
  116. package/dist/steps/scan/04-structure.md +0 -19
  117. package/dist/steps/scan/05-quality.md +0 -18
  118. package/dist/steps/scan/06-complete.md +0 -49
  119. package/dist/steps/scan/manifest.yaml +0 -16
  120. package/dist/steps/verify/01-load-specs.md +0 -28
  121. package/dist/steps/verify/02-check-tasks.md +0 -1
  122. package/dist/steps/verify/03-check-design.md +0 -6
  123. package/dist/steps/verify/04-run-tests.md +0 -7
  124. package/dist/steps/verify/05-e2e-tests.md +0 -27
  125. package/dist/steps/verify/05b-e2e-fix.md +0 -33
  126. package/dist/steps/verify/06-code-quality.md +0 -25
  127. package/dist/steps/verify/07-lint-check.md +0 -27
  128. package/dist/steps/verify/08-output-report.md +0 -14
  129. package/dist/steps/verify/manifest.yaml +0 -22
  130. package/docs/.vitepress/config.mts +0 -45
  131. package/docs/.vitepress/dist/404.html +0 -25
  132. package/docs/.vitepress/dist/assets/app.YytxICdd.js +0 -1
  133. package/docs/.vitepress/dist/assets/chunks/framework.Czhw_PXq.js +0 -19
  134. package/docs/.vitepress/dist/assets/chunks/theme.DusTRZQk.js +0 -1
  135. package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.js +0 -1
  136. package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.lean.js +0 -1
  137. package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  138. package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  139. package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  140. package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  141. package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  142. package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  143. package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  144. package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  145. package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  146. package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  147. package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  148. package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  149. package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  150. package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  151. package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.js +0 -15
  152. package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.lean.js +0 -1
  153. package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.js +0 -4
  154. package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.lean.js +0 -1
  155. package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.js +0 -1
  156. package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.lean.js +0 -1
  157. package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.js +0 -4
  158. package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.lean.js +0 -1
  159. package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.js +0 -5
  160. package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.lean.js +0 -1
  161. package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.js +0 -28
  162. package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.lean.js +0 -1
  163. package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.js +0 -30
  164. package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.lean.js +0 -1
  165. package/docs/.vitepress/dist/assets/style.DFTx90Kk.css +0 -1
  166. package/docs/.vitepress/dist/hashmap.json +0 -1
  167. package/docs/.vitepress/dist/index.html +0 -28
  168. package/docs/.vitepress/dist/sillyspec/commands.html +0 -42
  169. package/docs/.vitepress/dist/sillyspec/dashboard.html +0 -31
  170. package/docs/.vitepress/dist/sillyspec/file-io.html +0 -28
  171. package/docs/.vitepress/dist/sillyspec/getting-started.html +0 -31
  172. package/docs/.vitepress/dist/sillyspec/install.html +0 -32
  173. package/docs/.vitepress/dist/sillyspec/lifecycle.html +0 -55
  174. package/docs/.vitepress/dist/sillyspec/structure.html +0 -57
  175. package/docs/.vitepress/dist/vp-icons.css +0 -1
  176. package/docs/index.md +0 -34
  177. package/docs/sillyspec/commands.md +0 -218
  178. package/docs/sillyspec/dashboard.md +0 -51
  179. package/docs/sillyspec/file-io.md +0 -34
  180. package/docs/sillyspec/getting-started.md +0 -61
  181. package/docs/sillyspec/install.md +0 -51
  182. package/docs/sillyspec/lifecycle.md +0 -146
  183. package/docs/sillyspec/structure.md +0 -62
  184. package/packages/dashboard/dist/assets/index-Bh-GPjKY.css +0 -1
  185. package/packages/dashboard/dist/assets/index-CrCn5Gg6.js +0 -17
  186. package/src/step.js +0 -543
  187. package/templates/archive.md +0 -120
  188. package/templates/brainstorm.md +0 -170
  189. package/templates/continue.md +0 -32
  190. package/templates/execute.md +0 -304
  191. package/templates/explore.md +0 -59
  192. package/templates/export.md +0 -21
  193. package/templates/init.md +0 -61
  194. package/templates/plan.md +0 -146
  195. package/templates/quick.md +0 -135
  196. package/templates/scan-quick.md +0 -49
  197. package/templates/scan.md +0 -156
  198. package/templates/skills/playwright-e2e/SKILL.md +0 -340
  199. package/templates/status.md +0 -75
  200. package/templates/verify.md +0 -236
  201. package/templates/workspace-sync.md +0 -99
  202. package/templates/workspace.md +0 -70
  203. /package/{docs/.vitepress/dist/logo.jpg → logo.jpg} +0 -0
  204. /package/{docs/.vitepress → packages/dashboard}/dist/favicon.jpg +0 -0
  205. /package/{docs/public → packages/dashboard/dist}/logo.jpg +0 -0
  206. /package/{docs → packages/dashboard}/public/favicon.jpg +0 -0
package/src/init.js CHANGED
@@ -1,61 +1,36 @@
1
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, copyFileSync } from 'fs';
2
- import { join, resolve, dirname } from 'path';
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, statSync } from 'fs';
2
+ import { join, resolve, dirname, basename } from 'path';
3
3
  import { fileURLToPath } from 'url';
4
- import { homedir } from 'os';
5
- import { checkbox, select, confirm, input } from '@inquirer/prompts';
4
+ import { checkbox, confirm, input } from '@inquirer/prompts';
5
+ import { ProgressManager } from './progress.js';
6
6
  import chalk from 'chalk';
7
- import ora from 'ora';
8
7
 
9
8
  const __filename = fileURLToPath(import.meta.url);
10
9
  const __dirname = dirname(__filename);
11
- const TEMPLATE_DIR = resolve(__dirname, '..', 'templates');
10
+
11
+ // ── 递归复制目录 ──
12
+ function copyDirSync(src, dst) {
13
+ mkdirSync(dst, { recursive: true });
14
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
15
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
16
+ const srcPath = join(src, entry.name);
17
+ const dstPath = join(dst, entry.name);
18
+ if (entry.isDirectory()) {
19
+ copyDirSync(srcPath, dstPath);
20
+ } else if (entry.name.endsWith('.md')) {
21
+ writeFileSync(dstPath, readFileSync(srcPath));
22
+ }
23
+ }
24
+ }
12
25
 
13
26
  // ── 元数据映射 ──
14
27
 
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
- };
33
28
 
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
- };
53
29
 
54
- const VALID_TOOLS = ['claude', 'claude_skills', 'cursor', 'openclaw', 'codex', 'gemini', 'opencode'];
30
+ const VALID_TOOLS = ['claude', 'cursor', 'openclaw', 'codex', 'gemini', 'opencode'];
55
31
 
56
32
  const TOOL_LABELS = {
57
33
  claude: 'Claude Code',
58
- claude_skills: 'Claude Skills',
59
34
  cursor: 'Cursor',
60
35
  openclaw: 'OpenClaw',
61
36
  codex: 'OpenAI Codex (通过 AGENTS.md)',
@@ -63,8 +38,6 @@ const TOOL_LABELS = {
63
38
  opencode: 'OpenCode (通过 INSTRUCTIONS.md)',
64
39
  };
65
40
 
66
-
67
- // 指令文件工具:注入规范引用到指令文件
68
41
  const INSTRUCTION_TOOLS = ['codex', 'gemini', 'opencode'];
69
42
 
70
43
  const INSTRUCTION_FILE_MAP = {
@@ -78,83 +51,16 @@ const INJECTION_CONTENT = `## SillySpec — 规范驱动开发
78
51
  在执行开发任务时,遵循以下规范:
79
52
 
80
53
  ### 代码规范
81
- - 写代码前先读取 \`.sillyspec/codebase/CONVENTIONS.md\`(代码风格)和 \`.sillyspec/codebase/ARCHITECTURE.md\`(架构)
54
+ - 写代码前先读取 \`.sillyspec/docs/<project>/scan/CONVENTIONS.md\`(代码风格)和 \`.sillyspec/docs/<project>/scan/ARCHITECTURE.md\`(架构)
82
55
  - 调用已有方法前,用 grep 确认方法存在,不许编造
83
- - 遵循 \`.sillyspec/codebase/CONVENTIONS.md\` 中的代码风格
56
+ - 遵循 \`.sillyspec/docs/<project>/scan/CONVENTIONS.md\` 中的代码风格
84
57
 
85
58
  ### 工作流程
86
- - 读取 \`.sillyspec/STATE.md\` 确认当前阶段
59
+ - 读取 \`.sillyspec/.runtime/progress.json\` 确认当前阶段(使用 \`sillyspec progress show\`)
87
60
  - 各阶段产出文件位于 \`.sillyspec/changes/<变更名>/\` 下
88
- - 详细流程参考模板文件:\`.sillyspec/.templates/\`(brainstorm.md, plan.md, execute.md 等)
89
61
  `;
90
62
 
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
- // ── 指令文件注入 ──
63
+ // ── 注入指令文件 ──
158
64
 
159
65
  function injectInstructions(tool, projectDir) {
160
66
  const fileName = INSTRUCTION_FILE_MAP[tool];
@@ -180,7 +86,6 @@ function injectInstructions(tool, projectDir) {
180
86
  function detectTools(projectDir) {
181
87
  const found = [];
182
88
  if (existsSync(join(projectDir, '.claude'))) found.push('claude');
183
- if (existsSync(join(projectDir, '.claude', 'skills'))) found.push('claude_skills');
184
89
  if (existsSync(join(projectDir, '.cursor'))) found.push('cursor');
185
90
  if (existsSync(join(projectDir, '.openclaw'))) found.push('openclaw');
186
91
  if (existsSync(join(projectDir, 'AGENTS.md'))) found.push('codex');
@@ -198,22 +103,33 @@ function isTTY() {
198
103
 
199
104
  // ── 核心安装逻辑 ──
200
105
 
201
- async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
106
+ async function doInstall(projectDir, tools, subprojects = []) {
202
107
  // 创建基础目录
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 });
108
+ // .sillyspec/projects/ → 项目注册表
109
+ // .sillyspec/docs/<name>/统一文档中心
110
+ // .sillyspec/knowledge/ 跨项目共享知识库
111
+ // .sillyspec/.runtime/ progress (gitignored)
112
+
113
+ // 注册当前项目到 projects/
114
+ const projectName = basename(projectDir) || 'project';
115
+ const projectsDir = join(projectDir, '.sillyspec', 'projects');
116
+ mkdirSync(projectsDir, { recursive: true });
117
+ const projectYamlPath = join(projectsDir, `${projectName}.yaml`);
118
+ if (!existsSync(projectYamlPath)) {
119
+ writeFileSync(projectYamlPath, `name: ${projectName}\npath: .\nstatus: active\n`);
214
120
  }
215
121
 
216
- // 创建知识库骨架(所有模式)
122
+ // 创建 .sillyspec/docs/<projectName>/scan/ 子目录(代码扫描结果)
123
+ const scanDir = join(projectDir, '.sillyspec', 'docs', projectName, 'scan');
124
+ mkdirSync(scanDir, { recursive: true });
125
+ const gitkeepPath = join(scanDir, '.gitkeep');
126
+ if (!existsSync(gitkeepPath)) writeFileSync(gitkeepPath, '');
127
+
128
+ // 创建 shared/workspace 目录
129
+ mkdirSync(join(projectDir, '.sillyspec', 'shared'), { recursive: true });
130
+ mkdirSync(join(projectDir, '.sillyspec', 'workspace'), { recursive: true });
131
+
132
+ // 创建知识库骨架
217
133
  const knowledgeDir = join(projectDir, '.sillyspec', 'knowledge');
218
134
  mkdirSync(knowledgeDir, { recursive: true });
219
135
  const indexPath = join(knowledgeDir, 'INDEX.md');
@@ -231,6 +147,13 @@ async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
231
147
  mkdirSync(join(runtimeDir, sub), { recursive: true });
232
148
  }
233
149
 
150
+ // 创建初始 progress.json
151
+ const progressPath = join(runtimeDir, 'progress.json');
152
+ if (!existsSync(progressPath)) {
153
+ const pm = new ProgressManager();
154
+ pm.init(projectDir);
155
+ }
156
+
234
157
  // 创建初始 user-inputs.md
235
158
  const inputsPath = join(runtimeDir, 'user-inputs.md');
236
159
  if (!existsSync(inputsPath)) {
@@ -238,7 +161,7 @@ async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
238
161
  }
239
162
 
240
163
  const gitignorePath = join(projectDir, '.gitignore');
241
- const ignoreRules = ['.sillyspec/STATE.md', '.sillyspec/codebase/SCAN-RAW.md', '.sillyspec/local.yaml', '.sillyspec/.runtime/'];
164
+ const ignoreRules = ['.sillyspec/codebase/SCAN-RAW.md', '.sillyspec/local.yaml', '.sillyspec/.runtime/'];
242
165
  if (existsSync(gitignorePath)) {
243
166
  const content = readFileSync(gitignorePath, 'utf8');
244
167
  let updated = content.trimEnd();
@@ -252,83 +175,44 @@ async function doInstall(projectDir, tools, isWorkspace, subprojects = []) {
252
175
  writeFileSync(gitignorePath, ignoreRules.join('\n') + '\n');
253
176
  }
254
177
 
255
- // 生成 slash command 文件
256
- const templateFiles = readdirSync(TEMPLATE_DIR).filter(f => f.endsWith('.md'));
257
- let count = 0;
258
-
178
+ // 注入指令文件(codex/gemini/opencode)
259
179
  for (let i = 0; i < tools.length; i++) {
260
180
  const toolName = tools[i];
261
- const label = TOOL_LABELS[toolName] || toolName;
262
-
263
181
  if (INSTRUCTION_TOOLS.includes(toolName)) {
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;
182
+ injectInstructions(toolName, projectDir);
303
183
  }
304
184
  }
305
185
 
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
- );
186
+ // 复制 skills 到各工具目录
187
+ const skillToolDirs = {
188
+ claude: '.claude/skills',
189
+ codex: '.codex/skills',
190
+ openclaw: '.openclaw/skills',
191
+ opencode: '.opencode/skills',
192
+ }
193
+ const skillsSource = join(__dirname, '..', '.claude', 'skills');
194
+ if (existsSync(skillsSource)) {
195
+ const sillyspecSkills = readdirSync(skillsSource).filter(f => f.startsWith('sillyspec-') && statSync(join(skillsSource, f)).isDirectory());
196
+ if (sillyspecSkills.length > 0) {
197
+ for (const [tool, dir] of Object.entries(skillToolDirs)) {
198
+ if (!tools.includes(tool)) continue
199
+ const targetDir = join(projectDir, dir)
200
+ mkdirSync(targetDir, { recursive: true })
201
+ for (const skill of sillyspecSkills) {
202
+ copyDirSync(join(skillsSource, skill), join(targetDir, skill))
203
+ }
204
+ console.log(chalk.green(` ✓ ${TOOL_LABELS[tool]} skills 已同步 (${sillyspecSkills.length} 个)`))
320
205
  }
321
206
  }
207
+ } else {
208
+ console.log(chalk.yellow(' ⚠ 未找到 skills 目录(npm 包内无 .claude/skills/),跳过同步'));
322
209
  }
323
-
324
- return count;
325
210
  }
326
211
 
327
212
  // ── 安装完成总结 ──
328
213
 
329
- function showSummary(version, tools, isWorkspace, count) {
214
+ function showSummary(version, tools) {
330
215
  const toolLabels = tools.map(t => TOOL_LABELS[t] || t);
331
- const mode = isWorkspace ? '多项目工作区' : '单项目';
332
216
 
333
217
  console.log('');
334
218
  console.log(chalk.green(' ═══════════════════════════════════════'));
@@ -336,20 +220,13 @@ function showSummary(version, tools, isWorkspace, count) {
336
220
  console.log(chalk.green(' ═══════════════════════════════════════'));
337
221
  console.log('');
338
222
  console.log(` 已安装工具: ${chalk.cyan(toolLabels.join(', '))}`);
339
- console.log(` 模式: ${chalk.yellow(mode)}`);
340
- console.log('');
341
- console.log(` 📄 ${count} 个命令已就绪`);
342
223
  console.log(' 📁 .sillyspec/ — 项目规范目录');
343
224
  console.log('');
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
+ console.log(' 下一步:使用 AI 技能开始工作');
226
+ console.log(' OpenClaw: ' + chalk.bold('/sillyspec:brainstorm'));
227
+ console.log(' Claude Code: ' + chalk.bold('/sillyspec:brainstorm'));
350
228
  console.log('');
351
229
  console.log(chalk.dim(' 💡 推荐安装 MCP 工具增强 AI 能力:sillyspec setup'));
352
- console.log(chalk.dim(' Context7 — 查最新文档 | grep.app — 搜开源实现 | Chrome DevTools — 浏览器自动化'));
353
230
  console.log('');
354
231
  }
355
232
 
@@ -367,7 +244,7 @@ export function getVersion() {
367
244
  // ── 主命令 ──
368
245
 
369
246
  export async function cmdInit(projectDir, options = {}) {
370
- const { tool, workspace, interactive } = options;
247
+ const { tool, interactive } = options;
371
248
  const version = getVersion();
372
249
 
373
250
  // ── 交互式模式(--interactive 或 -i)──
@@ -399,20 +276,11 @@ export async function cmdInit(projectDir, options = {}) {
399
276
  validate: (answer) => answer.length > 0 || '至少选择一个工具',
400
277
  });
401
278
 
402
- // 工作区模式
403
- const isWorkspace = await select({
404
- message: '选择项目模式',
405
- choices: [
406
- { name: '单项目模式', value: 'false' },
407
- { name: '多项目工作区', value: 'true' },
408
- ],
409
- }) === 'true';
410
-
411
- // 工作区子项目引导
279
+ // 子项目引导(仅交互模式)
412
280
  let subprojects = [];
413
- if (isWorkspace) {
281
+ {
414
282
  console.log('');
415
- console.log(chalk.yellow('📋 工作区模式 — 添加子项目'));
283
+ console.log(chalk.yellow('📋 添加子项目'));
416
284
  console.log(chalk.dim(' 子项目是工作区中的独立项目目录(如 frontend/、backend/)'));
417
285
  console.log('');
418
286
 
@@ -475,8 +343,8 @@ export async function cmdInit(projectDir, options = {}) {
475
343
  }
476
344
 
477
345
  console.log('');
478
- const count = await doInstall(projectDir, selectedTools, isWorkspace, subprojects);
479
- showSummary(version, selectedTools, isWorkspace, count);
346
+ await doInstall(projectDir, selectedTools, subprojects);
347
+ showSummary(version, selectedTools);
480
348
  return;
481
349
  }
482
350
 
@@ -494,23 +362,17 @@ export async function cmdInit(projectDir, options = {}) {
494
362
  tools = detectTools(projectDir);
495
363
  }
496
364
 
497
- const count = await doInstall(projectDir, tools, !!workspace);
365
+ await doInstall(projectDir, tools);
498
366
 
499
367
  console.log('');
500
368
  console.log(chalk.green(` ✅ SillySpec v${version} 安装完成!`));
501
369
  console.log('');
502
- console.log(` 📄 ${count} 个命令已就绪`);
503
370
  console.log(' 📁 .sillyspec/ — 项目规范目录');
504
371
  console.log('');
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
+ console.log(' 下一步:使用 AI 技能开始工作');
373
+ console.log(` OpenClaw: ${chalk.bold('/sillyspec:brainstorm')}`);
374
+ console.log(` Claude Code: ${chalk.bold('/sillyspec:brainstorm')}`);
512
375
  console.log('');
513
376
  console.log(chalk.dim(' 💡 增强能力:sillyspec setup(安装 MCP 工具)'));
514
- console.log(chalk.dim(' 💡 完整配置:sillyspec init --interactive'));
515
377
  console.log('');
516
378
  }
package/src/migrate.js ADDED
@@ -0,0 +1,117 @@
1
+ import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync, renameSync, copyFileSync } from 'fs';
2
+ import { basename, join, resolve } from 'path';
3
+ import chalk from 'chalk';
4
+
5
+ /**
6
+ * Migrate old .sillyspec/ structure to unified .sillyspec/docs/<project>/ structure
7
+ * @param {string} projectDir - Path to the project directory
8
+ */
9
+ export function migrateDocs(projectDir) {
10
+ const sillyspecDir = join(projectDir, '.sillyspec');
11
+ if (!existsSync(sillyspecDir)) {
12
+ console.error('❌ .sillyspec/ 目录不存在');
13
+ process.exit(1);
14
+ }
15
+
16
+ // Determine project name from projects/*.yaml or directory name
17
+ let projectName = basename(resolve(projectDir));
18
+ const projectsDir = join(sillyspecDir, 'projects');
19
+ if (existsSync(projectsDir)) {
20
+ const yamlFiles = readdirSync(projectsDir).filter(f => f.endsWith('.yaml'));
21
+ if (yamlFiles.length > 0) {
22
+ projectName = yamlFiles[0].replace('.yaml', '');
23
+ }
24
+ }
25
+
26
+ console.log(chalk.cyan(`📦 迁移项目: ${projectName}`));
27
+ console.log('');
28
+
29
+ const docsBase = join(sillyspecDir, 'docs', projectName);
30
+ let migrated = 0;
31
+
32
+ // 1. codebase/ → .sillyspec/docs/<project>/scan/
33
+ const codebaseDir = join(sillyspecDir, 'codebase');
34
+ if (existsSync(codebaseDir)) {
35
+ const targetDir = join(docsBase, 'scan');
36
+ mkdirSync(targetDir, { recursive: true });
37
+ const files = readdirSync(codebaseDir).filter(f => f.endsWith('.md'));
38
+ for (const file of files) {
39
+ const src = join(codebaseDir, file);
40
+ const dest = join(targetDir, file);
41
+ if (!existsSync(dest)) {
42
+ copyFileSync(src, dest);
43
+ console.log(chalk.green(' ✅') + ` scan/${file}`);
44
+ migrated++;
45
+ } else {
46
+ console.log(chalk.yellow(' ⏭️') + ` scan/${file} (已存在)`);
47
+ }
48
+ }
49
+ }
50
+
51
+ // 2. specs/ is deprecated — designs live in changes/<变更名>/design.md
52
+
53
+ // 3. changes/archive/ → .sillyspec/docs/<project>/archive/
54
+ const archiveDir = join(sillyspecDir, 'changes', 'archive');
55
+ if (existsSync(archiveDir)) {
56
+ const targetDir = join(docsBase, 'archive');
57
+ mkdirSync(targetDir, { recursive: true });
58
+ const entries = readdirSync(archiveDir);
59
+ for (const entry of entries) {
60
+ const src = join(archiveDir, entry);
61
+ const dest = join(targetDir, entry);
62
+ if (!existsSync(dest)) {
63
+ copyFileSync(src, dest);
64
+ console.log(chalk.green(' ✅') + ` archive/${entry}`);
65
+ migrated++;
66
+ } else {
67
+ console.log(chalk.yellow(' ⏭️') + ` archive/${entry} (已存在)`);
68
+ }
69
+ }
70
+ }
71
+
72
+ // 4. knowledge/ → .sillyspec/docs/<project>/archive/ (append knowledge files)
73
+ const knowledgeDir = join(sillyspecDir, 'knowledge');
74
+ if (existsSync(knowledgeDir)) {
75
+ const targetDir = join(docsBase, 'archive');
76
+ mkdirSync(targetDir, { recursive: true });
77
+ const files = readdirSync(knowledgeDir).filter(f => f.endsWith('.md'));
78
+ for (const file of files) {
79
+ const src = join(knowledgeDir, file);
80
+ const dest = join(targetDir, `knowledge-${file}`);
81
+ if (!existsSync(dest)) {
82
+ copyFileSync(src, dest);
83
+ console.log(chalk.green(' ✅') + ` archive/knowledge-${file}`);
84
+ migrated++;
85
+ } else {
86
+ console.log(chalk.yellow(' ⏭️') + ` archive/knowledge-${file} (已存在)`);
87
+ }
88
+ }
89
+ }
90
+
91
+ // 5. quicklog/ → .sillyspec/docs/<project>/quicklog/
92
+ const quicklogDir = join(sillyspecDir, 'quicklog');
93
+ if (existsSync(quicklogDir)) {
94
+ const targetDir = join(docsBase, 'quicklog');
95
+ mkdirSync(targetDir, { recursive: true });
96
+ const files = readdirSync(quicklogDir).filter(f => f.endsWith('.md'));
97
+ for (const file of files) {
98
+ const src = join(quicklogDir, file);
99
+ const dest = join(targetDir, file);
100
+ if (!existsSync(dest)) {
101
+ copyFileSync(src, dest);
102
+ console.log(chalk.green(' ✅') + ` quicklog/${file}`);
103
+ migrated++;
104
+ } else {
105
+ console.log(chalk.yellow(' ⏭️') + ` quicklog/${file} (已存在)`);
106
+ }
107
+ }
108
+ }
109
+
110
+ console.log('');
111
+ if (migrated > 0) {
112
+ console.log(chalk.green(` ✅ 迁移完成,共迁移 ${migrated} 个文件`));
113
+ console.log(chalk.dim(' 旧文件保留在原位,确认无误后可手动删除'));
114
+ } else {
115
+ console.log(chalk.yellow(' 没有需要迁移的文件'));
116
+ }
117
+ }