sillyspec 3.7.13 → 3.7.15
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.
- package/.claude/skills/sillyspec-archive/SKILL.md +77 -0
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +591 -0
- package/.claude/skills/sillyspec-continue/SKILL.md +44 -0
- package/.claude/skills/sillyspec-execute/SKILL.md +233 -0
- package/.claude/skills/sillyspec-explore/SKILL.md +96 -0
- package/.claude/skills/sillyspec-export/SKILL.md +53 -0
- package/.claude/skills/sillyspec-init/SKILL.md +171 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +263 -0
- package/.claude/skills/sillyspec-propose/SKILL.md +248 -0
- package/.claude/skills/sillyspec-quick/SKILL.md +102 -0
- package/.claude/skills/sillyspec-resume/SKILL.md +111 -0
- package/{templates/scan.md → .claude/skills/sillyspec-scan/SKILL.md} +22 -60
- package/.claude/skills/sillyspec-state/SKILL.md +54 -0
- package/.claude/skills/sillyspec-status/SKILL.md +131 -0
- package/.claude/skills/sillyspec-verify/SKILL.md +146 -0
- package/.claude/skills/sillyspec-workspace/SKILL.md +149 -0
- package/.sillyspec/changes/run-command-design/design.md +1230 -0
- package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
- package/.sillyspec/knowledge/INDEX.md +8 -0
- package/.sillyspec/knowledge/uncategorized.md +3 -0
- package/.sillyspec/projects/sillyspec.yaml +3 -0
- package/README.md +4 -0
- package/logo.jpg +0 -0
- package/package.json +7 -1
- package/packages/dashboard/dist/assets/index-Bx0cgoK_.js +7446 -0
- package/packages/dashboard/dist/assets/index-DbkUSsNO.css +1 -0
- package/packages/dashboard/dist/favicon.jpg +0 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/dist/logo.jpg +0 -0
- package/packages/dashboard/package-lock.json +220 -0
- package/packages/dashboard/package.json +8 -5
- package/packages/dashboard/public/favicon.jpg +0 -0
- package/packages/dashboard/public/logo.jpg +0 -0
- package/packages/dashboard/server/index.js +92 -4
- package/packages/dashboard/server/parser.js +252 -28
- package/packages/dashboard/src/App.vue +139 -9
- package/packages/dashboard/src/components/ActionBar.vue +23 -39
- package/packages/dashboard/src/components/CommandPalette.vue +40 -65
- package/packages/dashboard/src/components/DetailPanel.vue +68 -53
- package/packages/dashboard/src/components/DocPreview.vue +137 -20
- package/packages/dashboard/src/components/DocTree.vue +48 -26
- package/packages/dashboard/src/components/LogStream.vue +12 -32
- package/packages/dashboard/src/components/PipelineStage.vue +8 -8
- package/packages/dashboard/src/components/PipelineView.vue +35 -43
- package/packages/dashboard/src/components/ProjectList.vue +52 -77
- package/packages/dashboard/src/components/ProjectOverview.vue +178 -0
- package/packages/dashboard/src/components/StageBadge.vue +13 -13
- package/packages/dashboard/src/components/StepCard.vue +11 -11
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -0
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -0
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -0
- package/packages/dashboard/src/main.js +4 -1
- package/packages/dashboard/src/style.css +13 -13
- package/src/index.js +55 -8
- package/src/init.js +66 -196
- package/src/migrate.js +1 -18
- package/src/progress.js +279 -281
- package/src/run.js +320 -0
- package/src/setup.js +1 -9
- package/src/stages/brainstorm.js +210 -0
- package/src/stages/execute.js +190 -0
- package/src/stages/index.js +22 -0
- package/src/stages/plan.js +118 -0
- package/src/stages/propose.js +115 -0
- package/src/stages/verify.js +98 -0
- package/packages/dashboard/dist/assets/index-5Rrvs0Rl.css +0 -1
- package/packages/dashboard/dist/assets/index-ZNToqi9V.js +0 -17
- package/templates/archive.md +0 -121
- package/templates/brainstorm.md +0 -246
- package/templates/commit.md +0 -123
- package/templates/continue.md +0 -32
- package/templates/execute.md +0 -314
- package/templates/explore.md +0 -60
- package/templates/export.md +0 -21
- package/templates/init.md +0 -61
- package/templates/plan.md +0 -157
- package/templates/progress-format.md +0 -90
- package/templates/propose.md +0 -73
- package/templates/quick.md +0 -135
- package/templates/resume-dialog.md +0 -55
- package/templates/resume.md +0 -53
- package/templates/scan-quick.md +0 -49
- package/templates/skills/playwright-e2e/SKILL.md +0 -340
- package/templates/status.md +0 -72
- package/templates/verify.md +0 -253
- package/templates/workspace-sync.md +0 -89
- package/templates/workspace.md +0 -67
- /package/.sillyspec/{docs/sillyspec/brainstorm → changes/brainstorm-archive}/2026-04-05-dashboard-design.md +0 -0
- /package/.sillyspec/{docs/sillyspec/brainstorm → changes/brainstorm-archive}/2026-04-05-unified-docs-design.md +0 -0
- /package/.sillyspec/{specs/2026-04-05-dashboard-design.md → changes/dashboard/design.md.braindraft} +0 -0
- /package/.sillyspec/{specs/2026-04-05-unified-docs-design.md → changes/unified-docs-design/design.md} +0 -0
package/src/run.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sillyspec run 命令实现
|
|
3
|
+
*
|
|
4
|
+
* CLI 成为流程引擎,AI 变成步骤执行器。
|
|
5
|
+
*/
|
|
6
|
+
import { basename, join } from 'path'
|
|
7
|
+
import { existsSync, readdirSync, mkdirSync, writeFileSync } from 'fs'
|
|
8
|
+
import { ProgressManager } from './progress.js'
|
|
9
|
+
import { stageRegistry, getNextStage } from './stages/index.js'
|
|
10
|
+
import { buildExecuteSteps } from './stages/execute.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 获取阶段的步骤定义(execute 需要动态构建)
|
|
14
|
+
*/
|
|
15
|
+
function getStageSteps(stageName, cwd) {
|
|
16
|
+
if (stageName === 'execute') {
|
|
17
|
+
const plansDir = join(cwd, '.sillyspec', 'plans')
|
|
18
|
+
let planFile = null
|
|
19
|
+
if (existsSync(plansDir)) {
|
|
20
|
+
const files = readdirSync(plansDir).filter(f => f.endsWith('.md')).sort()
|
|
21
|
+
if (files.length > 0) planFile = join(plansDir, files[files.length - 1])
|
|
22
|
+
}
|
|
23
|
+
return buildExecuteSteps(planFile)
|
|
24
|
+
}
|
|
25
|
+
const def = stageRegistry[stageName]
|
|
26
|
+
return def ? def.steps : null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 确保阶段的 steps 已初始化到 progress.json
|
|
31
|
+
*/
|
|
32
|
+
function ensureStageSteps(progress, stageName, cwd) {
|
|
33
|
+
if (!progress.stages) progress.stages = {}
|
|
34
|
+
|
|
35
|
+
const steps = getStageSteps(stageName, cwd)
|
|
36
|
+
if (!steps) return false
|
|
37
|
+
|
|
38
|
+
if (!progress.stages[stageName] || !progress.stages[stageName].steps || progress.stages[stageName].steps.length === 0) {
|
|
39
|
+
progress.stages[stageName] = {
|
|
40
|
+
status: 'in-progress',
|
|
41
|
+
startedAt: new Date().toISOString(),
|
|
42
|
+
completedAt: null,
|
|
43
|
+
steps: steps.map(s => ({ name: s.name, status: 'pending' }))
|
|
44
|
+
}
|
|
45
|
+
return true // 需要写入
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 检查步骤数量是否匹配(execute 动态步骤可能变化)
|
|
49
|
+
if (progress.stages[stageName].steps.length !== steps.length) {
|
|
50
|
+
// 保留已完成的状态,重新构建步骤列表
|
|
51
|
+
const oldSteps = progress.stages[stageName].steps
|
|
52
|
+
progress.stages[stageName].steps = steps.map((s, i) => {
|
|
53
|
+
const old = oldSteps[i]
|
|
54
|
+
if (old && old.name === s.name) return old
|
|
55
|
+
return { name: s.name, status: 'pending' }
|
|
56
|
+
})
|
|
57
|
+
return true
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 输出当前步骤的 prompt
|
|
65
|
+
*/
|
|
66
|
+
function outputStep(stageName, stepIndex, steps, cwd) {
|
|
67
|
+
const step = steps[stepIndex]
|
|
68
|
+
const total = steps.length
|
|
69
|
+
const projectName = basename(cwd)
|
|
70
|
+
|
|
71
|
+
console.log(`---`)
|
|
72
|
+
console.log(`stage: ${stageName}`)
|
|
73
|
+
console.log(`step: ${stepIndex + 1}/${total}`)
|
|
74
|
+
console.log(`stepName: ${step.name}`)
|
|
75
|
+
console.log(`project: ${projectName}`)
|
|
76
|
+
console.log(`---\n`)
|
|
77
|
+
console.log(`## Step ${stepIndex + 1}/${total}: ${step.name}\n`)
|
|
78
|
+
console.log(step.prompt)
|
|
79
|
+
console.log(`\n### 完成后执行`)
|
|
80
|
+
console.log(`sillyspec run ${stageName} --done --output "你的摘要"`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* sillyspec run <stage> 主命令
|
|
85
|
+
*/
|
|
86
|
+
export function runCommand(args, cwd) {
|
|
87
|
+
// 解析参数
|
|
88
|
+
const stageName = args[0]
|
|
89
|
+
const flags = args.slice(1)
|
|
90
|
+
|
|
91
|
+
if (!stageName || !stageRegistry[stageName]) {
|
|
92
|
+
console.error(`❌ 未知阶段: ${stageName || '(未指定)'}`)
|
|
93
|
+
console.error(`可选: ${Object.keys(stageRegistry).join(', ')}`)
|
|
94
|
+
process.exit(1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const isDone = flags.includes('--done')
|
|
98
|
+
const isSkip = flags.includes('--skip')
|
|
99
|
+
const isStatus = flags.includes('--status')
|
|
100
|
+
const isReset = flags.includes('--reset')
|
|
101
|
+
|
|
102
|
+
// 解析 --output
|
|
103
|
+
let outputText = null
|
|
104
|
+
const outputIdx = flags.indexOf('--output')
|
|
105
|
+
if (outputIdx !== -1 && flags[outputIdx + 1]) {
|
|
106
|
+
outputText = flags[outputIdx + 1]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const pm = new ProgressManager()
|
|
110
|
+
let progress = pm.read(cwd)
|
|
111
|
+
|
|
112
|
+
if (!progress) {
|
|
113
|
+
console.error('❌ 未找到 progress.json,请先运行 sillyspec init')
|
|
114
|
+
process.exit(1)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// --reset
|
|
118
|
+
if (isReset) {
|
|
119
|
+
return resetStage(pm, progress, stageName, cwd)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 确保步骤已初始化
|
|
123
|
+
const changed = ensureStageSteps(progress, stageName, cwd)
|
|
124
|
+
if (changed) {
|
|
125
|
+
pm._write(cwd, progress)
|
|
126
|
+
progress = pm.read(cwd)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// --status
|
|
130
|
+
if (isStatus) {
|
|
131
|
+
return showStatus(progress, stageName)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// --skip
|
|
135
|
+
if (isSkip) {
|
|
136
|
+
return skipStep(pm, progress, stageName, cwd)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// --done
|
|
140
|
+
if (isDone) {
|
|
141
|
+
return completeStep(pm, progress, stageName, cwd, outputText)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 默认:输出当前步骤
|
|
145
|
+
return runStage(progress, stageName, cwd)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function runStage(progress, stageName, cwd) {
|
|
149
|
+
const stageData = progress.stages[stageName]
|
|
150
|
+
if (!stageData || !stageData.steps) {
|
|
151
|
+
console.error(`❌ 阶段 ${stageName} 未初始化`)
|
|
152
|
+
process.exit(1)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const steps = stageData.steps
|
|
156
|
+
const currentIdx = steps.findIndex(s => s.status !== 'completed' && s.status !== 'skipped')
|
|
157
|
+
|
|
158
|
+
if (currentIdx === -1) {
|
|
159
|
+
const total = steps.length
|
|
160
|
+
console.log(`✅ ${stageName} 阶段已完成(${total}/${total} 步)`)
|
|
161
|
+
const next = getNextStage(stageName)
|
|
162
|
+
if (next) {
|
|
163
|
+
console.log(`\n下一步:sillyspec run ${next}`)
|
|
164
|
+
console.log(`或:/sillyspec:${next}`)
|
|
165
|
+
}
|
|
166
|
+
process.exit(2)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const stageDef = stageRegistry[stageName]
|
|
170
|
+
const defSteps = getStageSteps(stageName, cwd)
|
|
171
|
+
if (defSteps && defSteps[currentIdx]) {
|
|
172
|
+
outputStep(stageName, currentIdx, defSteps, cwd)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function completeStep(pm, progress, stageName, cwd, outputText) {
|
|
177
|
+
const stageData = progress.stages[stageName]
|
|
178
|
+
if (!stageData || !stageData.steps) {
|
|
179
|
+
console.error(`❌ 阶段 ${stageName} 未初始化`)
|
|
180
|
+
process.exit(1)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const steps = stageData.steps
|
|
184
|
+
const currentIdx = steps.findIndex(s => s.status === 'pending')
|
|
185
|
+
|
|
186
|
+
if (currentIdx === -1) {
|
|
187
|
+
console.error('没有待完成的步骤')
|
|
188
|
+
process.exit(1)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 标记完成
|
|
192
|
+
steps[currentIdx].status = 'completed'
|
|
193
|
+
steps[currentIdx].completedAt = new Date().toISOString()
|
|
194
|
+
if (outputText) {
|
|
195
|
+
const MAX_OUTPUT = 200
|
|
196
|
+
if (outputText.length > MAX_OUTPUT) {
|
|
197
|
+
steps[currentIdx].output = outputText.slice(0, MAX_OUTPUT) + '…'
|
|
198
|
+
// Save full output to artifacts/
|
|
199
|
+
const artifactsDir = join(cwd, '.sillyspec', '.runtime', 'artifacts')
|
|
200
|
+
mkdirSync(artifactsDir, { recursive: true })
|
|
201
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-')
|
|
202
|
+
writeFileSync(join(artifactsDir, `${stageName}-step${currentIdx + 1}-${ts}.txt`), outputText)
|
|
203
|
+
} else {
|
|
204
|
+
steps[currentIdx].output = outputText
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 检查是否还有下一步
|
|
209
|
+
const nextPendingIdx = steps.findIndex(s => s.status === 'pending')
|
|
210
|
+
|
|
211
|
+
if (nextPendingIdx === -1) {
|
|
212
|
+
// 全部完成
|
|
213
|
+
stageData.status = 'completed'
|
|
214
|
+
stageData.completedAt = new Date().toISOString()
|
|
215
|
+
|
|
216
|
+
const next = getNextStage(stageName)
|
|
217
|
+
if (next) {
|
|
218
|
+
progress.currentStage = next
|
|
219
|
+
if (!progress.stages[next]) progress.stages[next] = { status: 'pending', steps: [], startedAt: null, completedAt: null }
|
|
220
|
+
if (progress.stages[next].status === 'pending' || !progress.stages[next].status) {
|
|
221
|
+
progress.stages[next].status = 'in-progress'
|
|
222
|
+
progress.stages[next].startedAt = new Date().toISOString()
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
progress.lastActive = new Date().toISOString()
|
|
227
|
+
pm._write(cwd, progress)
|
|
228
|
+
|
|
229
|
+
const total = steps.length
|
|
230
|
+
console.log(`✅ ${stageName} 阶段已完成(${total}/${total} 步)`)
|
|
231
|
+
if (next) {
|
|
232
|
+
console.log(`\n下一步:sillyspec run ${next}`)
|
|
233
|
+
console.log(`或:/sillyspec:${next}`)
|
|
234
|
+
}
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
progress.lastActive = new Date().toISOString()
|
|
239
|
+
pm._write(cwd, progress)
|
|
240
|
+
|
|
241
|
+
const defSteps = getStageSteps(stageName, cwd)
|
|
242
|
+
console.log(`✅ Step ${currentIdx + 1}/${steps.length} 完成:${steps[currentIdx].name}\n`)
|
|
243
|
+
outputStep(stageName, nextPendingIdx, defSteps, cwd)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function skipStep(pm, progress, stageName, cwd) {
|
|
247
|
+
const stageData = progress.stages[stageName]
|
|
248
|
+
if (!stageData || !stageData.steps) {
|
|
249
|
+
console.error(`❌ 阶段 ${stageName} 未初始化`)
|
|
250
|
+
process.exit(1)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const steps = stageData.steps
|
|
254
|
+
const currentIdx = steps.findIndex(s => s.status === 'pending')
|
|
255
|
+
|
|
256
|
+
if (currentIdx === -1) {
|
|
257
|
+
console.error('没有待跳过的步骤')
|
|
258
|
+
process.exit(1)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const defSteps = getStageSteps(stageName, cwd)
|
|
262
|
+
const stepDef = defSteps ? defSteps[currentIdx] : null
|
|
263
|
+
if (stepDef && !stepDef.optional) {
|
|
264
|
+
console.error(`❌ 步骤 "${steps[currentIdx].name}" 不可跳过`)
|
|
265
|
+
process.exit(1)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
steps[currentIdx].status = 'skipped'
|
|
269
|
+
steps[currentIdx].skippedAt = new Date().toISOString()
|
|
270
|
+
progress.lastActive = new Date().toISOString()
|
|
271
|
+
pm._write(cwd, progress)
|
|
272
|
+
|
|
273
|
+
console.log(`⏭️ Step ${currentIdx + 1}/${steps.length} 已跳过:${steps[currentIdx].name}`)
|
|
274
|
+
|
|
275
|
+
// 输出下一步
|
|
276
|
+
const nextPendingIdx = steps.findIndex(s => s.status === 'pending')
|
|
277
|
+
if (nextPendingIdx !== -1 && defSteps) {
|
|
278
|
+
console.log('')
|
|
279
|
+
outputStep(stageName, nextPendingIdx, defSteps, cwd)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function showStatus(progress, stageName) {
|
|
284
|
+
const stageData = progress.stages[stageName]
|
|
285
|
+
const stageDef = stageRegistry[stageName]
|
|
286
|
+
|
|
287
|
+
if (!stageData || !stageData.steps || stageData.steps.length === 0) {
|
|
288
|
+
console.log(`阶段:${stageName}(${stageDef.title})`)
|
|
289
|
+
console.log(`进度:未初始化`)
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const steps = stageData.steps
|
|
294
|
+
const completed = steps.filter(s => s.status === 'completed' || s.status === 'skipped').length
|
|
295
|
+
const bar = '█'.repeat(completed) + '░'.repeat(steps.length - completed)
|
|
296
|
+
|
|
297
|
+
console.log(`阶段:${stageName}(${stageDef.title})`)
|
|
298
|
+
console.log(`进度:[${bar}] ${completed}/${steps.length}\n`)
|
|
299
|
+
|
|
300
|
+
const firstPending = steps.findIndex(s => s.status === 'pending')
|
|
301
|
+
|
|
302
|
+
steps.forEach((step, i) => {
|
|
303
|
+
const icon = step.status === 'completed' ? '✅' : step.status === 'skipped' ? '⏭️' : '⬜'
|
|
304
|
+
const isCurrent = step.status === 'pending' && i === firstPending
|
|
305
|
+
console.log(`${icon} Step ${i + 1}: ${step.name}${isCurrent ? ' ← 当前' : ''}`)
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resetStage(pm, progress, stageName, cwd) {
|
|
310
|
+
const defSteps = getStageSteps(stageName, cwd)
|
|
311
|
+
progress.stages[stageName] = {
|
|
312
|
+
status: 'in-progress',
|
|
313
|
+
startedAt: new Date().toISOString(),
|
|
314
|
+
completedAt: null,
|
|
315
|
+
steps: defSteps ? defSteps.map(s => ({ name: s.name, status: 'pending' })) : []
|
|
316
|
+
}
|
|
317
|
+
progress.lastActive = new Date().toISOString()
|
|
318
|
+
pm._write(cwd, progress)
|
|
319
|
+
console.log(`🔄 ${stageName} 阶段已重置`)
|
|
320
|
+
}
|
package/src/setup.js
CHANGED
|
@@ -11,15 +11,7 @@ const __dirname = dirname(__filename);
|
|
|
11
11
|
|
|
12
12
|
// ── Skill 定义 ──
|
|
13
13
|
|
|
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
|
-
];
|
|
14
|
+
const SKILLS = [];
|
|
23
15
|
|
|
24
16
|
// ── MCP 工具定义 ──
|
|
25
17
|
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
export const definition = {
|
|
2
|
+
name: 'brainstorm',
|
|
3
|
+
title: '头脑风暴',
|
|
4
|
+
description: '探索需求、分析技术方案、识别风险',
|
|
5
|
+
steps: [
|
|
6
|
+
{
|
|
7
|
+
name: '状态检查',
|
|
8
|
+
prompt: `检查 .sillyspec/.runtime/progress.json 确认当前状态。
|
|
9
|
+
|
|
10
|
+
### 操作
|
|
11
|
+
1. 运行 \`sillyspec progress show\`
|
|
12
|
+
2. 确认 currentStage 为 "brainstorm"
|
|
13
|
+
3. 如果有进行中的 brainstorm,提示选择继续或重新开始
|
|
14
|
+
4. 如果未初始化,提示先运行 sillyspec init
|
|
15
|
+
|
|
16
|
+
### 输出
|
|
17
|
+
当前状态摘要(1-2 句话)
|
|
18
|
+
|
|
19
|
+
### 注意
|
|
20
|
+
- 以 CLI 返回为准,不要自行推断阶段
|
|
21
|
+
- 如果阶段不对,输出正确提示并停止`,
|
|
22
|
+
outputHint: '状态摘要',
|
|
23
|
+
optional: false
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: '加载项目上下文',
|
|
27
|
+
prompt: `加载项目现有上下文,理解代码结构和约定。
|
|
28
|
+
|
|
29
|
+
### 操作
|
|
30
|
+
1. 检查是否工作区模式:\`ls .sillyspec/projects/*.yaml\`
|
|
31
|
+
2. 工作区模式:加载 CODEBASE-OVERVIEW.md + 共享规范 + 子项目上下文
|
|
32
|
+
3. 单项目模式:加载 PROJECT.md、REQUIREMENTS.md、ROADMAP.md
|
|
33
|
+
4. 棕地项目:读取 docs/<project>/scan/ 下的 STRUCTURE.md、CONVENTIONS.md、ARCHITECTURE.md
|
|
34
|
+
5. 查看进行中的变更:\`ls .sillyspec/changes/ | grep -v archive\`
|
|
35
|
+
|
|
36
|
+
### 输出
|
|
37
|
+
项目现状理解摘要(3-5 句话,关键约定和架构决策)
|
|
38
|
+
|
|
39
|
+
### 注意
|
|
40
|
+
- 工作区模式下需要询问本次需求属于哪个子项目
|
|
41
|
+
- 棕地项目必须读取数据模型章节`,
|
|
42
|
+
outputHint: '上下文摘要',
|
|
43
|
+
optional: false
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: '协作与复用检查',
|
|
47
|
+
prompt: `检查是否有同名变更或可复用模板。
|
|
48
|
+
|
|
49
|
+
### 操作
|
|
50
|
+
1. 检查已有变更:\`ls .sillyspec/changes/ | grep -v archive\`
|
|
51
|
+
- 有相关变更 → 提示用户,避免重复
|
|
52
|
+
2. 检查全局模板:\`ls ~/.sillyspec/templates/\`
|
|
53
|
+
- 有匹配模板 → 询问是否基于模板
|
|
54
|
+
3. 无相关内容 → 跳过,不输出
|
|
55
|
+
|
|
56
|
+
### 输出
|
|
57
|
+
检测到的相关变更和可用模板(无则输出"无冲突,继续")`,
|
|
58
|
+
outputHint: '已有变更和可用模板',
|
|
59
|
+
optional: true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: '原型/设计图分析',
|
|
63
|
+
prompt: `如果用户提供了截图、图片或 HTML 原型,分析提取结构。
|
|
64
|
+
|
|
65
|
+
### 操作
|
|
66
|
+
1. 识别图片中的页面结构(区域、组件、布局)
|
|
67
|
+
2. 提取表单字段(名称、类型、必填、选项)
|
|
68
|
+
3. 提取交互流程(页面跳转、按钮行为)
|
|
69
|
+
4. 提取标注和备注(业务规则、权限说明)
|
|
70
|
+
5. 展示分析结果,请用户确认遗漏
|
|
71
|
+
|
|
72
|
+
### 输出
|
|
73
|
+
页面结构树 + 字段列表 + 交互流程图
|
|
74
|
+
|
|
75
|
+
### 注意
|
|
76
|
+
- 没有原型则跳过此步骤
|
|
77
|
+
- 多页面时逐页分析,不要一次全部输出
|
|
78
|
+
- 图片信息 > 文字描述,不要忽略视觉信息`,
|
|
79
|
+
outputHint: '页面结构和交互流程',
|
|
80
|
+
optional: true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: '需求范围评估',
|
|
84
|
+
prompt: `评估需求复杂度,判断是否需要拆分。
|
|
85
|
+
|
|
86
|
+
### 操作
|
|
87
|
+
1. 根据分析结果判断复杂度
|
|
88
|
+
2. 满足以下任意 2 条建议拆分:
|
|
89
|
+
- 3+ 个可独立交付的功能模块
|
|
90
|
+
- 3+ 种角色有不同权限和视图
|
|
91
|
+
- 跨页面状态流转(审批流、多步表单)
|
|
92
|
+
- 模块间耦合度低可独立开发
|
|
93
|
+
3. 需要拆分 → 生成 MASTER.md,规划子阶段
|
|
94
|
+
4. 不需要拆分 → 继续
|
|
95
|
+
|
|
96
|
+
### 输出
|
|
97
|
+
拆分方案(如需要)或"无需拆分"确认
|
|
98
|
+
|
|
99
|
+
### 注意
|
|
100
|
+
- 简单 CRUD 不拆
|
|
101
|
+
- 拆分方案需用户确认`,
|
|
102
|
+
outputHint: '拆分方案或无需拆分确认',
|
|
103
|
+
optional: true
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: '对话式探索',
|
|
107
|
+
prompt: `通过对话探索需求细节。
|
|
108
|
+
|
|
109
|
+
### 操作
|
|
110
|
+
1. 从最核心的一个问题开始(用户到底想要什么?)
|
|
111
|
+
2. 等待用户回答后再问下一个
|
|
112
|
+
3. 根据回答判断:信息够了 → 进入方案 / 需要追问 → 只问一个
|
|
113
|
+
4. 探索顺序(按需):目的 → 约束 → 边界 → 成功标准
|
|
114
|
+
|
|
115
|
+
### 输出
|
|
116
|
+
需求理解摘要(用户确认的需求点列表)
|
|
117
|
+
|
|
118
|
+
### 铁律
|
|
119
|
+
- 一次只问一个问题
|
|
120
|
+
- 2-3 轮问答就应进入方案讨论
|
|
121
|
+
- 多选题优于开放式问题
|
|
122
|
+
- YAGNI — 砍掉不需要的功能`,
|
|
123
|
+
outputHint: '需求理解摘要',
|
|
124
|
+
optional: false
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: '提出 2-3 种方案',
|
|
128
|
+
prompt: `基于需求理解,提出 2-3 种实现方案。
|
|
129
|
+
|
|
130
|
+
### 操作
|
|
131
|
+
1. 每种方案列出:核心思路、优势、劣势
|
|
132
|
+
2. 给出推荐方案和理由
|
|
133
|
+
3. 等待用户选择或调整
|
|
134
|
+
|
|
135
|
+
### 输出
|
|
136
|
+
方案对比表 + 推荐方案
|
|
137
|
+
|
|
138
|
+
### 注意
|
|
139
|
+
- 方案差异要实质性的,不要为了凑数
|
|
140
|
+
- 推荐理由要具体`,
|
|
141
|
+
outputHint: '方案对比和推荐',
|
|
142
|
+
optional: false
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: '分段展示设计',
|
|
146
|
+
prompt: `按复杂度分段展示设计方案,逐段确认。
|
|
147
|
+
|
|
148
|
+
### 操作
|
|
149
|
+
1. 简单项目:几句话整体描述
|
|
150
|
+
2. 复杂项目:每段 200-300 字,逐段展示
|
|
151
|
+
3. 每段展示后等待用户确认
|
|
152
|
+
4. 收集修改意见,调整设计
|
|
153
|
+
|
|
154
|
+
### 输出
|
|
155
|
+
用户确认的完整设计方案
|
|
156
|
+
|
|
157
|
+
### 注意
|
|
158
|
+
- 不要一次输出大段文字
|
|
159
|
+
- 逐段确认,确保用户跟上`,
|
|
160
|
+
outputHint: '用户确认的设计方案',
|
|
161
|
+
optional: false
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: '写设计文档并自审',
|
|
165
|
+
prompt: `撰写 design 文档并进行 AI 自审。
|
|
166
|
+
|
|
167
|
+
### 操作
|
|
168
|
+
1. 将确认的设计写入 \`.sillyspec/changes/<变更名>/design.md\`
|
|
169
|
+
2. 自审检查:
|
|
170
|
+
- 需求覆盖:是否完整覆盖 Step 6 确认的需求
|
|
171
|
+
- 约束一致性:是否与 CONVENTIONS.md、ARCHITECTURE.md 一致
|
|
172
|
+
- 真实性:表名/字段名来自真实 schema 或标注"新增"
|
|
173
|
+
- YAGNI:是否包含不必要功能
|
|
174
|
+
- 验收标准:是否具体可测试
|
|
175
|
+
3. 自审发现问题 → 修改后重新检查
|
|
176
|
+
4. 全部通过 → 进入下一步
|
|
177
|
+
|
|
178
|
+
### 输出
|
|
179
|
+
design.md 文件路径 + 自审结果
|
|
180
|
+
|
|
181
|
+
### 注意
|
|
182
|
+
- 自审不通过不要进入下一步
|
|
183
|
+
- 不确定的问题标注「⚠️ 自审存疑」`,
|
|
184
|
+
outputHint: 'design.md 路径 + 自审结果',
|
|
185
|
+
optional: false
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: '用户确认并输出技术方案',
|
|
189
|
+
prompt: `用户确认设计方案,生成最终技术方案。
|
|
190
|
+
|
|
191
|
+
### 操作
|
|
192
|
+
1. 展示 design.md 摘要给用户
|
|
193
|
+
2. 请用户选择:✅ 确认 / ✏️ 修改 / ❌ 推翻重来
|
|
194
|
+
3. 确认后:
|
|
195
|
+
- 将技术方案写入 \`.sillyspec/changes/<变更名>/design.md\`
|
|
196
|
+
- 包含:架构决策、文件变更清单、数据模型、API 设计、代码风格参照
|
|
197
|
+
- Git 提交
|
|
198
|
+
|
|
199
|
+
### 输出
|
|
200
|
+
最终 design.md 路径 + Git commit hash
|
|
201
|
+
|
|
202
|
+
### 注意
|
|
203
|
+
- 必须等待用户明确确认
|
|
204
|
+
- 禁止在确认前推进到后续阶段
|
|
205
|
+
- 推翻重来回到 Step 6`,
|
|
206
|
+
outputHint: '最终 design.md 路径 + commit hash',
|
|
207
|
+
optional: false
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
}
|