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/run.js DELETED
@@ -1,640 +0,0 @@
1
- /**
2
- * sillyspec run 命令实现
3
- *
4
- * CLI 成为流程引擎,AI 变成步骤执行器。
5
- */
6
- import { basename, join } from 'path'
7
- import { existsSync, readdirSync, mkdirSync, writeFileSync, appendFileSync, readFileSync, statSync } from 'fs'
8
- import { ProgressManager } from './progress.js'
9
- import { stageRegistry, getNextStage, auxiliaryStages } from './stages/index.js'
10
- import { buildExecuteSteps } from './stages/execute.js'
11
- import { buildPlanSteps } from './stages/plan.js'
12
-
13
- /**
14
- * 获取阶段的步骤定义(execute 需要动态构建)
15
- */
16
- async function getStageSteps(stageName, cwd, progress) {
17
- if (stageName === 'execute') {
18
- const changesDir = join(cwd, '.sillyspec', 'changes')
19
- let planFile = null
20
- // 优先用 currentChange 指定的变更名
21
- if (progress.currentChange) {
22
- const target = join(changesDir, progress.currentChange, 'plan.md')
23
- if (existsSync(target)) planFile = target
24
- }
25
- // fallback:扫描 changes/ 非 archive 目录下的 plan.md
26
- if (!planFile && existsSync(changesDir)) {
27
- const candidates = []
28
- for (const entry of readdirSync(changesDir, { withFileTypes: true })) {
29
- if (!entry.isDirectory() || entry.name === 'archive') continue
30
- const p = join(changesDir, entry.name, 'plan.md')
31
- if (existsSync(p)) candidates.push({ name: entry.name, path: p })
32
- }
33
- if (candidates.length === 1) {
34
- planFile = candidates[0].path
35
- } else if (candidates.length > 1) {
36
- console.log('⚠️ 检测到多个变更,请选择:')
37
- candidates.forEach((c, i) => console.log(` ${i + 1}. ${c.name}`))
38
- const readline = await import('readline')
39
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
40
- const answer = await new Promise(resolve => {
41
- rl.question(`\n请输入编号(默认 1):`, input => {
42
- rl.close()
43
- const num = parseInt(input) || 1
44
- resolve(num >= 1 && num <= candidates.length ? num - 1 : 0)
45
- })
46
- })
47
- planFile = candidates[answer].path
48
- console.log(`✅ 已选择:${candidates[answer].name}\n`)
49
- }
50
- }
51
- return buildExecuteSteps(planFile)
52
- }
53
- if (stageName === 'plan') {
54
- const changesDir = join(cwd, '.sillyspec', 'changes')
55
- let changeDir = null
56
- if (progress.currentChange) {
57
- const target = join(changesDir, progress.currentChange)
58
- if (existsSync(target)) changeDir = target
59
- }
60
- if (!changeDir && existsSync(changesDir)) {
61
- const entries = readdirSync(changesDir, { withFileTypes: true }).filter(e => e.isDirectory() && e.name !== 'archive')
62
- if (entries.length === 1) changeDir = join(changesDir, entries[0].name)
63
- }
64
- return buildPlanSteps(changeDir)
65
- }
66
- const def = stageRegistry[stageName]
67
- return def ? def.steps : null
68
- }
69
-
70
- /**
71
- * 确保阶段的 steps 已初始化到 progress.json
72
- */
73
- async function ensureStageSteps(progress, stageName, cwd) {
74
- if (!progress.stages) progress.stages = {}
75
-
76
- const steps = await getStageSteps(stageName, cwd, progress)
77
- if (!steps) return false
78
-
79
- if (!progress.stages[stageName] || !progress.stages[stageName].steps || progress.stages[stageName].steps.length === 0) {
80
- progress.stages[stageName] = {
81
- status: 'in-progress',
82
- startedAt: new Date().toLocaleString('zh-CN',{hour12:false}),
83
- completedAt: null,
84
- steps: steps.map(s => ({ name: s.name, status: 'pending' }))
85
- }
86
- return true // 需要写入
87
- }
88
-
89
- // 检查步骤数量是否匹配(execute 动态步骤可能变化)
90
- if (progress.stages[stageName].steps.length !== steps.length) {
91
- // 保留已完成的状态,重新构建步骤列表
92
- const oldSteps = progress.stages[stageName].steps
93
- progress.stages[stageName].steps = steps.map((s, i) => {
94
- const old = oldSteps[i]
95
- if (old && old.name === s.name) return old
96
- return { name: s.name, status: 'pending' }
97
- })
98
- return true
99
- }
100
-
101
- return false
102
- }
103
-
104
- /**
105
- * 输出当前步骤的 prompt
106
- */
107
- function outputStep(stageName, stepIndex, steps, cwd) {
108
- const step = steps[stepIndex]
109
- const total = steps.length
110
- const projectName = basename(cwd)
111
-
112
- const personas = {
113
- brainstorm: `### 🎯 你的角色:资深架构师
114
- 你是一位有 15 年经验的系统架构师。先理解业务本质,再设计技术方案。决策附理由,方案列 trade-off。不确定就说不确定,不猜。`,
115
- plan: `### 📋 你的角色:技术项目经理
116
- 你是一位经验丰富的技术项目经理。任务拆解粒度均匀,依赖关系明确。每个任务有完成标准,Wave 间有依赖说明。条理清晰,不做模糊描述。`,
117
- execute: `### 💻 你的角色:高级工程师
118
- 你是一位严谨的高级工程师。先读规范再写代码,严格遵循 CONVENTIONS.md 和 plan.md。**你不是设计师,是执行者——按 plan 搬砖,禁止发散思维。** 发现 plan 不合理就停下来反馈,不要自己改方案。代码有清晰职责划分,边界处理完善。少说多做,遇到规范冲突优先问。`,
119
- verify: `### 🔍 你的角色:QA 专家
120
- 你是一位吹毛求疵的 QA 专家。假设所有代码都有 bug,用最坏情况测试。关注边界、异常、并发。有问题直说,用证据说话,不写"看起来没问题"。`,
121
- quick: `### 💻 你的角色:全栈老兵
122
- 你是一位实战经验丰富的全栈工程师。不纠结架构和流程,理解需求就直接干。不确定的地方先问清楚再动手,先读后写,改完就收。问题排查思路开阔,前端报错不一定是前端问题——可能是后端数据、浏览器兼容、甚至设备硬件。解决方案实用接地气,用户描述有误敢于直接指出。`
123
- }
124
-
125
- console.log(`---`)
126
- console.log(`stage: ${stageName}`)
127
- console.log(`step: ${stepIndex + 1}/${total}`)
128
- console.log(`stepName: ${step.name}`)
129
- console.log(`project: ${projectName}`)
130
- console.log(`---\n`)
131
- if (personas[stageName]) {
132
- console.log(personas[stageName])
133
- console.log('')
134
- }
135
- console.log(`## Step ${stepIndex + 1}/${total}: ${step.name}\n`)
136
- console.log(step.prompt)
137
- console.log(`\n### ⚠️ 铁律`)
138
- console.log('- **文档是核心资产,代码是文档的产物。** 没有文档就没有代码——文档是 AI 的记忆,是团队协作的基础,是后续维护的唯一依据。任何代码产出必须先有对应的设计/规范文档支撑。')
139
- console.log('- 只做本步骤描述的操作,不得自行扩展或跳过')
140
- console.log('- 不要回头修改已完成的步骤')
141
- console.log('- 不要编造不存在的 CLI 子命令')
142
- console.log('- 完成后立即执行 --done 命令,不得跳过')
143
- console.log('- 文档类型文件(.md/.yaml/.json 等)头部必须包含 author(git 用户名)和 created_at(精确到秒)')
144
- console.log('- 执行构建/测试前必须先读 local.yaml,优先使用其中配置的命令、路径和环境变量;未配置时才使用默认值')
145
- console.log(`\n### 完成后执行`)
146
- console.log(`sillyspec run ${stageName} --done --input "用户原始需求/反馈" --output "你的摘要"`)
147
- }
148
-
149
- /**
150
- * sillyspec run <stage> 主命令
151
- */
152
- export async function runCommand(args, cwd) {
153
- // 解析参数
154
- const stageName = args[0]
155
- const flags = args.slice(1)
156
-
157
- if (!stageName) {
158
- console.error('❌ 请指定阶段,例如: sillyspec run brainstorm')
159
- console.error(`可选: ${Object.keys(stageRegistry).join(', ')}, auto`)
160
- process.exit(1)
161
- }
162
-
163
- if (!stageRegistry[stageName] && stageName !== 'auto') {
164
- console.error(`❌ 未知阶段: ${stageName}`)
165
- console.error(`可选: ${Object.keys(stageRegistry).join(', ')}, auto`)
166
- process.exit(1)
167
- }
168
-
169
- const isDone = flags.includes('--done')
170
- const isSkip = flags.includes('--skip')
171
- const isStatus = flags.includes('--status')
172
- const isReset = flags.includes('--reset')
173
-
174
- // 解析 --output
175
- let outputText = null
176
- const outputIdx = flags.indexOf('--output')
177
- if (outputIdx !== -1 && flags[outputIdx + 1]) {
178
- outputText = flags[outputIdx + 1]
179
- }
180
-
181
- // 解析 --input
182
- let inputText = null
183
- const inputIdx = flags.indexOf('--input')
184
- if (inputIdx !== -1 && flags[inputIdx + 1]) {
185
- inputText = flags[inputIdx + 1]
186
- }
187
-
188
- // 解析 --change <name>
189
- let changeName = null
190
- const changeIdx = flags.indexOf('--change')
191
- if (changeIdx !== -1 && flags[changeIdx + 1]) {
192
- changeName = flags[changeIdx + 1]
193
- }
194
-
195
- const isAuxiliary = auxiliaryStages.includes(stageName)
196
-
197
- const pm = new ProgressManager()
198
- let progress = pm.read(cwd)
199
-
200
- if (!progress) {
201
- // 辅助命令可以在没有 progress.json 时工作(比如 scan)
202
- if (!isAuxiliary) {
203
- console.error('❌ 未找到 progress.json,请先运行 sillyspec init')
204
- process.exit(1)
205
- }
206
- progress = pm.init(cwd)
207
- }
208
-
209
- // -- auto 模式:自动推进所有流程阶段
210
- if (stageName === 'auto') {
211
- return await runAutoMode(pm, progress, cwd, flags)
212
- }
213
-
214
- // --change 设置当前变更名
215
- if (changeName) {
216
- progress.currentChange = changeName
217
- progress.lastActive = new Date().toLocaleString('zh-CN', { hour12: false })
218
- pm._write(cwd, progress)
219
- console.log(`✅ 当前变更设置为:${changeName}`)
220
- return
221
- }
222
-
223
- // --reset
224
- if (isReset) {
225
- return await resetStage(pm, progress, stageName, cwd)
226
- }
227
-
228
- // 确保步骤已初始化
229
- const changed = await ensureStageSteps(progress, stageName, cwd)
230
- if (changed) {
231
- pm._write(cwd, progress)
232
- progress = pm.read(cwd)
233
- }
234
-
235
- // --status
236
- if (isStatus) {
237
- return showStatus(progress, stageName)
238
- }
239
-
240
- // --skip
241
- if (isSkip) {
242
- return await skipStep(pm, progress, stageName, cwd)
243
- }
244
-
245
- // --done
246
- if (isDone) {
247
- return await completeStep(pm, progress, stageName, cwd, outputText, inputText)
248
- }
249
-
250
- // 默认:输出当前步骤
251
- return await runStage(pm, progress, stageName, cwd)
252
- }
253
-
254
- async function runStage(pm, progress, stageName, cwd) {
255
- const stageData = progress.stages[stageName]
256
- if (!stageData || !stageData.steps) {
257
- console.error(`❌ 阶段 ${stageName} 未初始化`)
258
- process.exit(1)
259
- }
260
-
261
- const steps = stageData.steps
262
- let currentIdx = steps.findIndex(s => s.status !== 'completed' && s.status !== 'skipped')
263
-
264
- if (currentIdx === -1) {
265
- // 阶段已完成
266
- console.log(`✅ ${stageName} 阶段已完成。`)
267
- console.log(` 继续执行将重新开始,可用 --reset 显式重置。\n`)
268
- // 自动重置,允许重复执行
269
- steps.forEach(s => { s.status = 'pending'; s.completedAt = null; s.output = null; s.startedAt = null })
270
- stageData.status = 'in_progress'
271
- stageData.completedAt = null
272
- pm._write(cwd, progress)
273
- currentIdx = 0
274
- } else if (currentIdx > 0) {
275
- // 有进行中的步骤,提示用户
276
- const completed = currentIdx
277
- const total = steps.length
278
- console.log(`⚠️ ${stageName} 已进行到第 ${currentIdx + 1}/${total} 步(前 ${completed} 步已完成)。`)
279
- console.log(` 继续执行将从中断处恢复,用 --reset 可重新开始。\n`)
280
- }
281
-
282
- const stageDef = stageRegistry[stageName]
283
- const defSteps = await getStageSteps(stageName, cwd, progress)
284
- if (defSteps && defSteps[currentIdx]) {
285
- outputStep(stageName, currentIdx, defSteps, cwd)
286
- }
287
- }
288
-
289
- function validateMetadata(cwd, stageName) {
290
- const changesDir = join(cwd, '.sillyspec', 'changes')
291
- if (!existsSync(changesDir)) return
292
-
293
- // 找最近 10 分钟内修改的 md/yaml 文件
294
- const cutoff = Date.now() - 10 * 60 * 1000
295
- const missing = []
296
-
297
- function walk(dir) {
298
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
299
- const full = join(dir, entry.name)
300
- try {
301
- if (entry.isDirectory()) { walk(full); continue }
302
- if (!/\.(md|yaml|yml)$/.test(entry.name)) continue
303
- const mtime = statSync(full).mtimeMs
304
- if (mtime < cutoff) continue
305
- const content = readFileSync(full, 'utf-8')
306
- if (!content.includes('author:') && !content.includes('author:')) missing.push(full)
307
- if (!content.includes('created_at:') && !content.includes('created_at:')) missing.push(full)
308
- } catch (e) { /* skip unreadable files */ }
309
- }
310
- }
311
-
312
- walk(changesDir)
313
- const unique = [...new Set(missing)]
314
- if (unique.length > 0) {
315
- console.log(`\n⚠️ 以下文件缺少 author 或 created_at 元数据:`)
316
- unique.forEach(f => console.log(` - ${f.replace(cwd + '/', '')}`))
317
- console.log('请在文件头部添加 author(git 用户名)和 created_at(精确到秒)')
318
- }
319
- }
320
-
321
- async function completeStep(pm, progress, stageName, cwd, outputText, inputText = null) {
322
- const stageData = progress.stages[stageName]
323
- if (!stageData || !stageData.steps) {
324
- console.error(`❌ 阶段 ${stageName} 未初始化`)
325
- process.exit(1)
326
- }
327
-
328
- const steps = stageData.steps
329
- const currentIdx = steps.findIndex(s => s.status === 'pending')
330
-
331
- if (currentIdx === -1) {
332
- console.error('没有待完成的步骤')
333
- process.exit(1)
334
- }
335
-
336
- // 标记完成
337
- steps[currentIdx].status = 'completed'
338
- steps[currentIdx].completedAt = new Date().toLocaleString('zh-CN',{hour12:false})
339
- if (outputText) {
340
- const MAX_OUTPUT = 200
341
- if (outputText.length > MAX_OUTPUT) {
342
- steps[currentIdx].output = outputText.slice(0, MAX_OUTPUT) + '…'
343
- // Save full output to artifacts/
344
- const artifactsDir = join(cwd, '.sillyspec', '.runtime', 'artifacts')
345
- mkdirSync(artifactsDir, { recursive: true })
346
- const ts = new Date().toISOString().slice(0,19).replace(/[-T:]/g, '')
347
- writeFileSync(join(artifactsDir, `${stageName}-step${currentIdx + 1}-${ts}.txt`), outputText)
348
- } else {
349
- steps[currentIdx].output = outputText
350
- }
351
- }
352
-
353
- // 检查是否还有下一步
354
- // plan 阶段 Step 4(展开任务)完成后,动态追加 task 蓝图步骤
355
- if (stageName === 'plan' && currentIdx === 3 && progress.currentChange) {
356
- const planFile = join(cwd, '.sillyspec', 'changes', progress.currentChange, 'plan.md')
357
- if (existsSync(planFile)) {
358
- const planContent = readFileSync(planFile, 'utf8')
359
- const { buildPlanSteps } = await import('./stages/plan.js')
360
- const fullSteps = buildPlanSteps(join(cwd, '.sillyspec', 'changes', progress.currentChange), planContent)
361
- // fullSteps[0..4] = fixedPrefix, fullSteps[-2..-1] = fixedSuffix
362
- // 中间的是 task 蓝图步骤,插入到当前 steps 中
363
- // 当前 steps 已有 5 个 fixedPrefix,找到需要插入的 task 步骤
364
- const taskSteps = fullSteps.slice(5, -2) // 排除 5 个前缀和 2 个后缀
365
- if (taskSteps.length > 0) {
366
- // 插入审查一致性 + 保存步骤(后缀)
367
- const suffixSteps = fullSteps.slice(-2)
368
- for (const ts of taskSteps) {
369
- steps.push({ name: ts.name, status: 'pending', prompt: ts.prompt, outputHint: ts.outputHint, optional: false })
370
- }
371
- for (const ss of suffixSteps) {
372
- steps.push({ name: ss.name, status: 'pending', prompt: ss.prompt, outputHint: ss.outputHint, optional: false })
373
- }
374
- // 重新查找下一步
375
- }
376
- }
377
- }
378
-
379
- const nextPendingIdx = steps.findIndex(s => s.status === 'pending')
380
-
381
- if (nextPendingIdx === -1) {
382
- // 全部完成
383
- stageData.status = 'completed'
384
- stageData.completedAt = new Date().toLocaleString('zh-CN',{hour12:false})
385
-
386
- const next = getNextStage(stageName)
387
- if (next) {
388
- progress.currentStage = next
389
- if (!progress.stages[next]) progress.stages[next] = { status: 'pending', steps: [], startedAt: null, completedAt: null }
390
- if (progress.stages[next].status === 'pending' || !progress.stages[next].status) {
391
- progress.stages[next].status = 'in-progress'
392
- progress.stages[next].startedAt = new Date().toLocaleString('zh-CN',{hour12:false})
393
- }
394
- }
395
-
396
- progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
397
- pm._write(cwd, progress)
398
-
399
- // deriveState 轻量校验
400
- try {
401
- const { deriveState } = await import('./derive.js')
402
- const result = deriveState(cwd, { mode: 'light', fix: true, pm, progress })
403
- if (result.fixed > 0) {
404
- console.log(`⚠️ 状态修复:${result.fixed} 个步骤已从 artifacts 恢复`)
405
- }
406
- } catch {}
407
-
408
- // Append to user-inputs.md
409
- if (outputText) {
410
- const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
411
- const entry = `\n## ${new Date().toLocaleString('zh-CN',{hour12:false})} | ${stageName}: ${steps[currentIdx].name}\n${inputText ? "- 输入:" + inputText + "\n" : ""}- 输出:${outputText}\n`
412
- appendFileSync(inputsPath, entry)
413
- }
414
-
415
- // 验证:检查生成的文件是否包含 author 和 created_at
416
- validateMetadata(cwd, stageName)
417
-
418
- const total = steps.length
419
- console.log(`✅ ${stageName} 阶段已完成(${total}/${total} 步)`)
420
- if (next) {
421
- console.log(`\n下一步:sillyspec run ${next}`)
422
- console.log(`或:/sillyspec:${next}`)
423
- }
424
- return
425
- }
426
-
427
- progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
428
- pm._write(cwd, progress)
429
-
430
- // Append to user-inputs.md
431
- if (outputText) {
432
- const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
433
- const entry = `\n## ${new Date().toLocaleString('zh-CN',{hour12:false})} | ${stageName}: ${steps[currentIdx].name}\n${inputText ? "- 输入:" + inputText + "\n" : ""}- 输出:${outputText}\n`
434
- appendFileSync(inputsPath, entry)
435
- }
436
-
437
- const defSteps = await getStageSteps(stageName, cwd, progress)
438
- console.log(`✅ Step ${currentIdx + 1}/${steps.length} 完成:${steps[currentIdx].name}\n`)
439
- outputStep(stageName, nextPendingIdx, defSteps, cwd)
440
- }
441
-
442
- async function skipStep(pm, progress, stageName, cwd) {
443
- const stageData = progress.stages[stageName]
444
- if (!stageData || !stageData.steps) {
445
- console.error(`❌ 阶段 ${stageName} 未初始化`)
446
- process.exit(1)
447
- }
448
-
449
- const steps = stageData.steps
450
- const currentIdx = steps.findIndex(s => s.status === 'pending')
451
-
452
- if (currentIdx === -1) {
453
- console.error('没有待跳过的步骤')
454
- process.exit(1)
455
- }
456
-
457
- const defSteps = await getStageSteps(stageName, cwd, progress)
458
- const stepDef = defSteps ? defSteps[currentIdx] : null
459
- if (stepDef && !stepDef.optional) {
460
- console.error(`❌ 步骤 "${steps[currentIdx].name}" 不可跳过`)
461
- process.exit(1)
462
- }
463
-
464
- steps[currentIdx].status = 'skipped'
465
- steps[currentIdx].skippedAt = new Date().toLocaleString('zh-CN',{hour12:false})
466
- progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
467
- pm._write(cwd, progress)
468
-
469
- console.log(`⏭️ Step ${currentIdx + 1}/${steps.length} 已跳过:${steps[currentIdx].name}`)
470
-
471
- // 输出下一步
472
- const nextPendingIdx = steps.findIndex(s => s.status === 'pending')
473
- if (nextPendingIdx !== -1 && defSteps) {
474
- console.log('')
475
- outputStep(stageName, nextPendingIdx, defSteps, cwd)
476
- }
477
- }
478
-
479
- function showStatus(progress, stageName) {
480
- const stageData = progress.stages[stageName]
481
- const stageDef = stageRegistry[stageName]
482
-
483
- if (!stageData || !stageData.steps || stageData.steps.length === 0) {
484
- console.log(`阶段:${stageName}(${stageDef.title})`)
485
- console.log(`进度:未初始化`)
486
- return
487
- }
488
-
489
- const steps = stageData.steps
490
- const completed = steps.filter(s => s.status === 'completed' || s.status === 'skipped').length
491
- const bar = '█'.repeat(completed) + '░'.repeat(steps.length - completed)
492
-
493
- console.log(`阶段:${stageName}(${stageDef.title})`)
494
- console.log(`进度:[${bar}] ${completed}/${steps.length}\n`)
495
-
496
- const firstPending = steps.findIndex(s => s.status === 'pending')
497
-
498
- // 批量进度
499
- if (progress.batchProgress) {
500
- const bp = progress.batchProgress
501
- const total = bp.total || 0
502
- const completed = bp.completed || 0
503
- const failed = bp.failed || 0
504
- const skipped = bp.skipped || 0
505
- const barLen = 20
506
- const filled = Math.round((completed / Math.max(total, 1)) * barLen)
507
- const bar = '█'.repeat(filled) + '░'.repeat(barLen - filled)
508
- const parts = []
509
- if (failed > 0) parts.push(`${failed} 失败`)
510
- if (skipped > 0) parts.push(`${skipped} 跳过`)
511
- const suffix = parts.length ? ` (${parts.join(', ')})` : ''
512
- console.log(`\n📊 批量进度: ${bar} ${completed}/${total}${suffix}\n`)
513
- }
514
-
515
- steps.forEach((step, i) => {
516
- const icon = step.status === 'completed' ? '✅' : step.status === 'skipped' ? '⏭️' : '⬜'
517
- const isCurrent = step.status === 'pending' && i === firstPending
518
- console.log(`${icon} Step ${i + 1}: ${step.name}${isCurrent ? ' ← 当前' : ''}`)
519
- })
520
- }
521
-
522
- async function resetStage(pm, progress, stageName, cwd) {
523
- const defSteps = await getStageSteps(stageName, cwd, progress)
524
- progress.stages[stageName] = {
525
- status: 'in-progress',
526
- startedAt: new Date().toLocaleString('zh-CN',{hour12:false}),
527
- completedAt: null,
528
- steps: defSteps ? defSteps.map(s => ({ name: s.name, status: 'pending' })) : []
529
- }
530
- progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
531
- pm._write(cwd, progress)
532
- console.log(`🔄 ${stageName} 阶段已重置`)
533
- }
534
-
535
- /**
536
- * auto 模式:自动推进 brainstorm → plan → execute → verify
537
- */
538
- async function runAutoMode(pm, progress, cwd, flags) {
539
- const flowStages = ['brainstorm', 'plan', 'execute', 'verify']
540
- const isDone = flags.includes('--done')
541
- let outputText = null
542
- const outputIdx = flags.indexOf('--output')
543
- if (outputIdx !== -1 && flags[outputIdx + 1]) outputText = flags[outputIdx + 1]
544
- let inputText = null
545
- const inputIdx = flags.indexOf('--input')
546
- if (inputIdx !== -1 && flags[inputIdx + 1]) inputText = flags[inputIdx + 1]
547
-
548
- if (!isDone) {
549
- // 首次启动:显示当前状态和下一步
550
- const currentStage = progress.currentStage || flowStages[0]
551
- const stageIdx = flowStages.indexOf(currentStage)
552
- if (stageIdx === -1) {
553
- console.error(`❌ 当前阶段 ${currentStage} 不在 auto 流程中`)
554
- console.error(`auto 流程: ${flowStages.join(' → ')}`)
555
- process.exit(1)
556
- }
557
- // 显示进度概览
558
- console.log('════════════════════════════════════════')
559
- console.log(' 🤖 SillySpec Auto Mode')
560
- console.log('════════════════════════════════════════')
561
- console.log(` 流程: ${flowStages.join(' → ')}`)
562
- console.log(` 当前: ${currentStage}`)
563
- for (let i = 0; i < flowStages.length; i++) {
564
- const s = flowStages[i]
565
- const stageData = progress.stages[s]
566
- const done = stageData?.status === 'completed'
567
- const active = s === currentStage
568
- const total = stageData?.steps?.length || '?'
569
- const completed = stageData?.steps?.filter(st => st.status === 'completed').length || 0
570
- const icon = done ? '✅' : active ? '🔵' : '⬜'
571
- console.log(` ${icon} ${s} (${completed}/${total})`)
572
- }
573
- console.log('')
574
- // 输出当前步骤 prompt
575
- const steps = await getStageSteps(currentStage, cwd, progress)
576
- if (!steps) {
577
- console.error(`❌ 无法获取 ${currentStage} 步骤`)
578
- process.exit(1)
579
- }
580
- const pendingIdx = steps.findIndex(s => s.status === 'pending')
581
- if (pendingIdx === -1) {
582
- // 阶段已完成,提示进入下一阶段
583
- const next = getNextStage(currentStage)
584
- if (next) {
585
- console.log(`✅ ${currentStage} 已完成,下一步:sillyspec run auto --done --output "${currentStage} 完成"`)
586
- } else {
587
- console.log('🎉 全部流程已完成!')
588
- }
589
- return
590
- }
591
- outputStepPrompt(steps, pendingIdx, currentStage, cwd, progress)
592
- return
593
- }
594
-
595
- // --done:完成当前步骤,如果阶段完成则自动推进
596
- if (!outputText) {
597
- console.error('❌ auto --done 需要 --output 参数')
598
- process.exit(1)
599
- }
600
-
601
- const currentStage = progress.currentStage
602
- const stageIdx = flowStages.indexOf(currentStage)
603
- if (stageIdx === -1) {
604
- console.error(`❌ 当前阶段 ${currentStage} 不在 auto 流程中`)
605
- process.exit(1)
606
- }
607
-
608
- // 完成当前步骤
609
- const completed = await completeStep(pm, progress, currentStage, cwd, outputText, inputText)
610
- if (!completed) return
611
-
612
- // 检查阶段是否完成
613
- const nextPendingIdx = progress.stages[currentStage]?.steps?.findIndex(s => s.status === 'pending')
614
- if (nextPendingIdx === -1) {
615
- // 阶段已完成
616
- const next = getNextStage(currentStage)
617
- if (next) {
618
- console.log(`\n✅ ${currentStage} 阶段完成,自动进入 ${next}`)
619
- // 输出下一阶段第一步 prompt
620
- const nextSteps = await getStageSteps(next, cwd, progress)
621
- if (nextSteps) {
622
- const firstPending = nextSteps.findIndex(s => s.status === 'pending')
623
- if (firstPending !== -1) {
624
- outputStepPrompt(nextSteps, firstPending, next, cwd, progress)
625
- }
626
- }
627
- } else {
628
- console.log('\n🎉 全部流程已完成!建议运行 /sillyspec:commit 提交改动')
629
- }
630
- } else {
631
- // 阶段内下一步
632
- const steps = await getStageSteps(currentStage, cwd, progress)
633
- if (steps) {
634
- const firstPending = steps.findIndex(s => s.status === 'pending')
635
- if (firstPending !== -1) {
636
- outputStepPrompt(steps, firstPending, currentStage, cwd, progress)
637
- }
638
- }
639
- }
640
- }
@@ -1,54 +0,0 @@
1
- export const definition = {
2
- name: 'archive',
3
- title: '归档变更',
4
- description: '规范沉淀,可追溯',
5
- auxiliary: true,
6
- steps: [
7
- {
8
- name: '任务完成度检查',
9
- prompt: `检查 tasks.md 中所有 checkbox 是否已勾选。
10
-
11
- ### 操作
12
- 1. 读取 \`.sillyspec/changes/<change-name>/tasks.md\`
13
- 2. 检查所有 checkbox 是否已勾选
14
- 3. 如有遗漏 → 询问用户是否继续归档
15
-
16
- ### 输出
17
- 完成度报告`,
18
- outputHint: '完成度报告',
19
- optional: false
20
- },
21
- {
22
- name: '确认归档',
23
- prompt: `展示即将归档的内容,请用户确认。
24
-
25
- ### 操作
26
- 1. 展示:变更目录名、包含的文件列表、生成总结
27
- 2. 请用户确认是否执行归档
28
- 3. 确认后:将 \`.sillyspec/changes/<change-name>/\` 移动到 \`.sillyspec/changes/archive/YYYY-MM-DD-<change-name>/\`
29
- 4. 确保所有 checkbox 都已勾选
30
-
31
- ### 输出
32
- 归档确认`,
33
- outputHint: '归档确认',
34
- optional: false
35
- },
36
- {
37
- name: '更新路线图和提交',
38
- prompt: `更新路线图并暂存变更。
39
-
40
- ### 操作
41
- 1. 如果 \`.sillyspec/ROADMAP.md\` 存在,标记对应 Phase 为已完成
42
- 2. \`git add .sillyspec/\` — **不要 commit**,由用户通过统一提交工具处理
43
- 3. 更新 progress.json:
44
- - 清除当前变更信息(归档后不再活跃)
45
- - 如果是主变更(有 MASTER.md),标记所有阶段为 ✅,然后清除
46
- - 历史记录追加时间 + 归档完成
47
-
48
- ### 输出
49
- 归档完成确认 + 累积规范统计`,
50
- outputHint: '归档完成',
51
- optional: false
52
- }
53
- ]
54
- }