sillyspec 3.9.1 → 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.
- package/.claude/skills/sillyspec-commit/SKILL.md +1 -1
- package/.claude/skills/sillyspec-continue/SKILL.md +1 -1
- package/.claude/skills/sillyspec-explore/SKILL.md +9 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +0 -35
- package/.claude/skills/sillyspec-workspace/SKILL.md +1 -1
- package/README.md +7 -6
- package/SKILL.md +15 -10
- package/package.json +1 -1
- package/packages/dashboard/dist/assets/index-BcM2J-hv.css +1 -0
- package/packages/dashboard/dist/assets/{index-RsLVPAy7.js → index-DpLHK4jv.js} +974 -974
- package/packages/dashboard/dist/index.html +16 -17
- package/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/packages/dashboard/dist/prototype-overview.html +256 -0
- package/packages/dashboard/public/prototype-dashboard.html +836 -0
- package/packages/dashboard/public/prototype-overview.html +256 -0
- package/packages/dashboard/server/index.js +18 -13
- package/packages/dashboard/server/parser.js +109 -1
- package/packages/dashboard/server/watcher.js +14 -6
- package/packages/dashboard/src/App.vue +414 -186
- package/packages/dashboard/src/components/ActionBar.vue +10 -1
- package/packages/dashboard/src/components/CommandPalette.vue +5 -1
- package/packages/dashboard/src/components/DocPreview.vue +105 -8
- package/packages/dashboard/src/components/DocTree.vue +75 -19
- package/packages/dashboard/src/components/HResizeHandle.vue +48 -0
- package/packages/dashboard/src/components/PipelineView.vue +23 -4
- package/packages/dashboard/src/components/ProjectCard.vue +187 -0
- package/packages/dashboard/src/components/ProjectOverview.vue +113 -139
- package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
- package/packages/dashboard/src/composables/useDashboard.js +28 -0
- package/packages/dashboard/src/composables/useLayout.js +131 -0
- package/src/index.js +7 -0
- package/src/init.js +17 -10
- package/src/migrate.js +5 -5
- package/src/progress.js +2 -1
- package/src/run.js +72 -61
- package/src/stages/brainstorm.js +28 -3
- package/src/stages/execute.js +52 -27
- package/src/stages/explore.js +34 -0
- package/src/stages/index.js +3 -1
- package/src/stages/plan.js +86 -14
- package/src/stages/scan.js +11 -11
- package/src/stages/status.js +1 -1
- package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +0 -97
- package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +0 -51
- package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +0 -29
- package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +0 -34
- package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +0 -13
- package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +0 -43
- package/.sillyspec/changes/auto-mode/design.md +0 -50
- package/.sillyspec/changes/auto-mode/proposal.md +0 -19
- package/.sillyspec/changes/auto-mode/requirements.md +0 -21
- package/.sillyspec/changes/auto-mode/tasks.md +0 -7
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +0 -206
- package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +0 -199
- package/.sillyspec/changes/dashboard/design.md +0 -219
- package/.sillyspec/changes/dashboard/design.md.braindraft +0 -206
- package/.sillyspec/changes/run-command-design/design.md +0 -1230
- package/.sillyspec/changes/unified-docs-design/design.md +0 -199
- package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
- package/.sillyspec/knowledge/INDEX.md +0 -8
- package/.sillyspec/knowledge/uncategorized.md +0 -3
- package/.sillyspec/plans/2026-04-05-dashboard.md +0 -737
- package/.sillyspec/projects/sillyspec.yaml +0 -3
- package/packages/dashboard/dist/assets/index-CntACGUN.css +0 -1
package/src/run.js
CHANGED
|
@@ -10,57 +10,59 @@ import { stageRegistry, auxiliaryStages } from './stages/index.js'
|
|
|
10
10
|
import { buildExecuteSteps } from './stages/execute.js'
|
|
11
11
|
import { buildPlanSteps } from './stages/plan.js'
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* 统一查找变更目录
|
|
15
|
+
*/
|
|
16
|
+
function resolveChangeDir(cwd, progress) {
|
|
17
|
+
const changesDir = join(cwd, '.sillyspec', 'changes')
|
|
18
|
+
if (!existsSync(changesDir)) return null
|
|
19
|
+
|
|
20
|
+
// 1. 优先用 currentChange
|
|
21
|
+
if (progress.currentChange) {
|
|
22
|
+
const target = join(changesDir, progress.currentChange)
|
|
23
|
+
if (existsSync(target)) return target
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 2. fallback:唯一非 archive 目录
|
|
27
|
+
const entries = readdirSync(changesDir, { withFileTypes: true })
|
|
28
|
+
.filter(e => e.isDirectory() && e.name !== 'archive')
|
|
29
|
+
if (entries.length === 1) return join(changesDir, entries[0].name)
|
|
30
|
+
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 自动探测并设置 currentChange(唯一变更目录时)
|
|
36
|
+
* @returns {boolean} 是否设置了 currentChange
|
|
37
|
+
*/
|
|
38
|
+
function autoDetectChange(progress, cwd) {
|
|
39
|
+
if (progress.currentChange) return false
|
|
40
|
+
const changesDir = join(cwd, '.sillyspec', 'changes')
|
|
41
|
+
if (!existsSync(changesDir)) return false
|
|
42
|
+
const entries = readdirSync(changesDir, { withFileTypes: true })
|
|
43
|
+
.filter(e => e.isDirectory() && e.name !== 'archive')
|
|
44
|
+
if (entries.length === 1) {
|
|
45
|
+
progress.currentChange = entries[0].name
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
|
|
13
51
|
/**
|
|
14
52
|
* 获取阶段的步骤定义(execute 需要动态构建)
|
|
15
53
|
*/
|
|
16
54
|
async function getStageSteps(stageName, cwd, progress) {
|
|
17
55
|
if (stageName === 'execute') {
|
|
18
|
-
const
|
|
56
|
+
const changeDir = resolveChangeDir(cwd, progress)
|
|
19
57
|
let planFile = null
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
}
|
|
58
|
+
if (changeDir) {
|
|
59
|
+
const p = join(changeDir, 'plan.md')
|
|
60
|
+
if (existsSync(p)) planFile = p
|
|
50
61
|
}
|
|
51
62
|
return buildExecuteSteps(planFile)
|
|
52
63
|
}
|
|
53
64
|
if (stageName === 'plan') {
|
|
54
|
-
const
|
|
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
|
-
}
|
|
65
|
+
const changeDir = resolveChangeDir(cwd, progress)
|
|
64
66
|
return buildPlanSteps(changeDir)
|
|
65
67
|
}
|
|
66
68
|
const def = stageRegistry[stageName]
|
|
@@ -119,7 +121,9 @@ function outputStep(stageName, stepIndex, steps, cwd) {
|
|
|
119
121
|
verify: `### 🔍 你的角色:QA 专家
|
|
120
122
|
你是一位吹毛求疵的 QA 专家。假设所有代码都有 bug,用最坏情况测试。关注边界、异常、并发。有问题直说,用证据说话,不写"看起来没问题"。`,
|
|
121
123
|
quick: `### 💻 你的角色:全栈老兵
|
|
122
|
-
|
|
124
|
+
你是一位实战经验丰富的全栈工程师。不纠结架构和流程,理解需求就直接干。不确定的地方先问清楚再动手,先读后写,改完就收。问题排查思路开阔,前端报错不一定是前端问题——可能是后端数据、浏览器兼容、甚至设备硬件。解决方案实用接地气,用户描述有误敢于直接指出。`,
|
|
125
|
+
explore: `### 🧭 你的角色:技术探索伙伴
|
|
126
|
+
你帮助用户澄清问题、调查代码库、比较方案和暴露风险。探索阶段不写实现代码,不安装依赖,不把讨论强行推进成开发。`
|
|
123
127
|
}
|
|
124
128
|
|
|
125
129
|
console.log(`---`)
|
|
@@ -252,6 +256,12 @@ export async function runCommand(args, cwd) {
|
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
async function runStage(pm, progress, stageName, cwd) {
|
|
259
|
+
// 自动探测 currentChange
|
|
260
|
+
if (autoDetectChange(progress, cwd)) {
|
|
261
|
+
progress.lastActive = new Date().toLocaleString('zh-CN', { hour12: false })
|
|
262
|
+
pm._write(cwd, progress)
|
|
263
|
+
}
|
|
264
|
+
|
|
255
265
|
const stageData = progress.stages[stageName]
|
|
256
266
|
if (!stageData || !stageData.steps) {
|
|
257
267
|
console.error(`❌ 阶段 ${stageName} 未初始化`)
|
|
@@ -352,25 +362,26 @@ async function completeStep(pm, progress, stageName, cwd, outputText, inputText
|
|
|
352
362
|
}
|
|
353
363
|
}
|
|
354
364
|
|
|
355
|
-
// plan 阶段 "展开任务"
|
|
356
|
-
if (stageName === 'plan' && steps[currentIdx]?.name === '展开任务并分组'
|
|
357
|
-
const
|
|
358
|
-
if (
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
365
|
+
// plan 阶段 "展开任务" 完成后,动态插入任务蓝图协调器步骤
|
|
366
|
+
if (stageName === 'plan' && steps[currentIdx]?.name === '展开任务并分组') {
|
|
367
|
+
const changeDir = resolveChangeDir(cwd, progress)
|
|
368
|
+
if (changeDir) {
|
|
369
|
+
const planFile = join(changeDir, 'plan.md')
|
|
370
|
+
if (existsSync(planFile)) {
|
|
371
|
+
const planContent = readFileSync(planFile, 'utf8')
|
|
372
|
+
const { buildPlanSteps, fixedPrefix, fixedSuffix } = await import('./stages/plan.js')
|
|
373
|
+
const fullSteps = buildPlanSteps(changeDir, planContent)
|
|
374
|
+
const prefixLen = fixedPrefix.length
|
|
375
|
+
const suffixLen = fixedSuffix.length
|
|
376
|
+
// 提取协调器步骤(prefix 和 suffix 之间)
|
|
377
|
+
const coordinatorSteps = fullSteps.slice(prefixLen, suffixLen > 0 ? -suffixLen : undefined)
|
|
378
|
+
if (coordinatorSteps.length > 0) {
|
|
379
|
+
// 在当前步骤之后插入协调器步骤
|
|
380
|
+
for (let i = 0; i < coordinatorSteps.length; i++) {
|
|
381
|
+
steps.splice(currentIdx + 1 + i, 0, { name: coordinatorSteps[i].name, status: 'pending' })
|
|
382
|
+
}
|
|
383
|
+
console.log(` 📝 已动态插入 ${coordinatorSteps.length} 个任务蓝图步骤(${coordinatorSteps.map(s => s.name).join(', ')})`)
|
|
384
|
+
}
|
|
374
385
|
}
|
|
375
386
|
}
|
|
376
387
|
}
|
package/src/stages/brainstorm.js
CHANGED
|
@@ -31,7 +31,7 @@ export const definition = {
|
|
|
31
31
|
2. 加载项目信息:\`cat .sillyspec/projects/*.yaml 2>/dev/null\`
|
|
32
32
|
3. 加载本地配置:\`cat .sillyspec/local.yaml 2>/dev/null\`
|
|
33
33
|
4. 询问本次需求属于哪个子项目
|
|
34
|
-
5. 棕地项目:读取 docs/<project>/scan/ 下的 STRUCTURE.md、CONVENTIONS.md、ARCHITECTURE.md
|
|
34
|
+
5. 棕地项目:读取 .sillyspec/docs/<project>/scan/ 下的 STRUCTURE.md、CONVENTIONS.md、ARCHITECTURE.md
|
|
35
35
|
6. 查看进行中的变更:\`ls .sillyspec/changes/ | grep -v archive\`
|
|
36
36
|
|
|
37
37
|
### 输出
|
|
@@ -173,22 +173,47 @@ export const definition = {
|
|
|
173
173
|
2. 复杂项目:每段 200-300 字,逐段展示
|
|
174
174
|
3. 每段展示后等待用户确认
|
|
175
175
|
4. 收集修改意见,调整设计
|
|
176
|
+
5. 确认设计后,确定变更名(格式:\`YYYY-MM-DD-<简短描述>\`,例如 \`2026-05-13-user-auth\`)
|
|
176
177
|
|
|
177
178
|
### 输出
|
|
178
|
-
用户确认的完整设计方案
|
|
179
|
+
用户确认的完整设计方案 + 变更名
|
|
179
180
|
|
|
180
181
|
### 注意
|
|
181
182
|
- 不要一次输出大段文字
|
|
182
|
-
-
|
|
183
|
+
- 逐段确认,确保用户跟上
|
|
184
|
+
- 变更名必须以当天日期开头(YYYY-MM-DD-),后跟英文短横线分隔的简短描述`,
|
|
183
185
|
outputHint: '用户确认的设计方案',
|
|
184
186
|
optional: false
|
|
185
187
|
},
|
|
188
|
+
{
|
|
189
|
+
name: 'HTML 原型生成',
|
|
190
|
+
prompt: `为设计方案生成可交互的 HTML 原型,帮助用户可视化确认。
|
|
191
|
+
|
|
192
|
+
### 操作
|
|
193
|
+
1. 判断本次设计是否适合生成 HTML 原型:
|
|
194
|
+
- 适合:有 UI 组件/布局/交互流程/状态转换/架构图
|
|
195
|
+
- 不适合:纯后端逻辑/配置修改/无可视化意义
|
|
196
|
+
2. 如果适合,生成一个独立的 HTML 文件(内联 CSS + JS),保存到:
|
|
197
|
+
\`.sillyspec/changes/<变更名>/prototype-<名称>.html\`(变更名格式:YYYY-MM-DD-<简短描述>)
|
|
198
|
+
3. 原型要求:
|
|
199
|
+
- 单文件,浏览器直接打开
|
|
200
|
+
- 展示关键布局结构和交互流程
|
|
201
|
+
- 不需要完整功能,重点是让用户确认设计方向
|
|
202
|
+
- 使用 ASCII/流程图/线框图风格,不需要精美 UI
|
|
203
|
+
4. 展示给用户确认设计方向
|
|
204
|
+
|
|
205
|
+
### 输出
|
|
206
|
+
HTML 原型文件路径(或"跳过"如果不适合)`,
|
|
207
|
+
outputHint: '原型文件路径或跳过',
|
|
208
|
+
optional: true
|
|
209
|
+
},
|
|
186
210
|
{
|
|
187
211
|
name: '写设计文档并自审',
|
|
188
212
|
prompt: `撰写 design 文档并进行 AI 自审。
|
|
189
213
|
|
|
190
214
|
### 操作
|
|
191
215
|
1. 确认变更目录存在:\`mkdir -p .sillyspec/changes/<变更名>\`(Windows 用 \`mkdir .sillyspec\\changes\\<变更名>\` 或 PowerShell \`New-Item -ItemType Directory -Force -Path .sillyspec/changes/<变更名>\`)
|
|
216
|
+
- 变更名格式必须为 \`YYYY-MM-DD-<简短描述>\`(如 \`2026-05-13-user-auth\`)
|
|
192
217
|
2. 将确认的设计写入 \`.sillyspec/changes/<变更名>/design.md\`
|
|
193
218
|
3. 自审检查:
|
|
194
219
|
- 需求覆盖:是否完整覆盖 Step 6 确认的需求
|
package/src/stages/execute.js
CHANGED
|
@@ -168,38 +168,67 @@ function parseWavesFromPlan(planContent) {
|
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
|
-
* 为 Wave 生成 prompt
|
|
171
|
+
* 为 Wave 生成 prompt(强制子代理执行)
|
|
172
172
|
*/
|
|
173
173
|
function buildWavePrompt(wave, waveIndex, changeDir) {
|
|
174
|
-
|
|
174
|
+
// 构建子代理 prompt 模板(每个任务的蓝图内容)
|
|
175
|
+
const subagentTemplates = wave.tasks.map((t, ti) => {
|
|
175
176
|
const taskNum = String(t.index || (ti + 1)).padStart(2, '0')
|
|
176
177
|
const taskFile = changeDir ? `${changeDir}/tasks/task-${taskNum}.md` : ''
|
|
177
178
|
const taskFileExists = taskFile && existsSync(taskFile)
|
|
178
|
-
let
|
|
179
|
-
if (t.file) s += ` (${t.file})`
|
|
179
|
+
let taskContent = ''
|
|
180
180
|
if (taskFileExists) {
|
|
181
|
-
let taskContent = ''
|
|
182
181
|
try { taskContent = readFileSync(taskFile, 'utf8').trim() } catch { taskContent = '(无法读取任务蓝图文件)' }
|
|
183
|
-
s += `
|
|
184
|
-
\n### 📋 任务蓝图(task-${taskNum}.md)\n${taskContent}`
|
|
185
182
|
}
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
const fileInfo = t.file ? ` (${t.file})` : ''
|
|
184
|
+
return `\`\`\`
|
|
185
|
+
任务:${t.name}${fileInfo}
|
|
186
|
+
${taskContent ? `蓝图内容:\n${taskContent}` : '(无蓝图文件,按任务描述执行)'}
|
|
187
|
+
|
|
188
|
+
操作:
|
|
189
|
+
1. 先读后写 — 读取要修改的文件,理解现有结构
|
|
190
|
+
2. 按 TDD 步骤实现
|
|
191
|
+
3. 运行对应测试确认通过
|
|
192
|
+
4. 报告改动文件和测试结果
|
|
193
|
+
|
|
194
|
+
铁律:
|
|
195
|
+
- 只做蓝图/任务描述里写的事,不增不减
|
|
196
|
+
- 蓝图有问题 → 报告问题,不要自己改
|
|
197
|
+
- 先写测试,再写代码
|
|
198
|
+
- grep 确认方法存在,不要编造
|
|
199
|
+
\`\`\``
|
|
200
|
+
}).join('\n\n')
|
|
201
|
+
|
|
202
|
+
const taskList = wave.tasks.map((t, ti) => {
|
|
203
|
+
const taskNum = String(t.index || (ti + 1)).padStart(2, '0')
|
|
204
|
+
let s = `- [ ] ${t.name}`
|
|
205
|
+
if (t.file) s += ` (${t.file})`
|
|
188
206
|
return s
|
|
189
207
|
}).join('\n')
|
|
190
|
-
|
|
191
|
-
const hasTaskBlueprints = changeDir && existsSync(join(changeDir, 'tasks'))
|
|
192
|
-
const taskBlueprintRule = hasTaskBlueprints
|
|
193
|
-
? '每个任务有独立的 task-N.md 蓝图——只做蓝图里写的事,不要实现蓝图之外的功能。如果蓝图有问题,**停下来反馈**,不要自己改。问题归因:实现困难 → task 蓝图没写好 → plan 没做好 → design 有缺陷。'
|
|
194
|
-
: '如果发现 plan 不合理,**停下来反馈**,不要自己改方案。问题归因:实现困难 → plan 没做好 → design 有缺陷。'
|
|
208
|
+
|
|
195
209
|
return `## Wave ${waveIndex}: 执行以下任务
|
|
196
210
|
|
|
211
|
+
## 执行方式(必须严格遵守)
|
|
212
|
+
|
|
213
|
+
**每个任务必须由独立子代理执行,你不要自己写代码。**
|
|
214
|
+
|
|
215
|
+
你的角色是调度者 + 审查者:
|
|
216
|
+
1. 为每个任务启动一个子代理(Agent tool),同 Wave 内可并行
|
|
217
|
+
2. 子代理完成后审查结果
|
|
218
|
+
3. 勾选 plan.md 中的 checkbox
|
|
219
|
+
4. 记录改动文件和测试结果
|
|
220
|
+
|
|
221
|
+
### 子代理 prompt 模板
|
|
222
|
+
为每个任务使用以下 prompt 启动子代理:
|
|
223
|
+
|
|
224
|
+
${subagentTemplates}
|
|
225
|
+
|
|
197
226
|
### Wave 开始前
|
|
198
227
|
1. 读取 design.md 的「编码铁律」章节(如果存在),严格遵守
|
|
199
228
|
2. 读取 plan.md 了解全局任务划分和依赖关系
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
229
|
+
3. 确认本 Wave 的输入/输出契约(前置 Wave 产出了什么,本 Wave 需要消费什么)
|
|
230
|
+
4. 检查前置 Wave 的产出是否完整(文件是否存在、测试是否通过)
|
|
231
|
+
5. **上下文分层加载**:
|
|
203
232
|
- 🔥 热上下文:design.md 编码铁律 + 当前 Wave 任务(必须加载)
|
|
204
233
|
- 🌡️ 温上下文:CONVENTIONS.md + ARCHITECTURE.md(需要时加载)
|
|
205
234
|
- ❄️ 冷上下文:其他变更的 design.md、历史 plan.md(不要主动加载,除非明确需要)
|
|
@@ -207,21 +236,17 @@ const hasTaskBlueprints = changeDir && existsSync(join(changeDir, 'tasks'))
|
|
|
207
236
|
### 本 Wave 任务
|
|
208
237
|
${taskList}
|
|
209
238
|
|
|
210
|
-
###
|
|
211
|
-
1.
|
|
212
|
-
2.
|
|
213
|
-
3.
|
|
214
|
-
4. **Reverse Sync**:发现 Bug 或实现与 design.md/task-N.md 不一致时,先检查是代码错了还是文档有遗漏,有遗漏则先修文档再修代码。
|
|
215
|
-
4. **不要频繁编译!** 编译很慢,只在以下情况运行:
|
|
239
|
+
### 调度要求
|
|
240
|
+
1. 同一 Wave 内任务可并行启动子代理
|
|
241
|
+
2. **Reverse Sync**:子代理报告实现与 design.md 不一致时,先检查是代码错了还是文档有遗漏
|
|
242
|
+
3. **不要频繁编译!** 编译很慢,只在以下情况运行:
|
|
216
243
|
- 写了大量代码后需要验证语法正确性
|
|
217
244
|
- 最后一个 Wave 完成后做一次全量编译验证
|
|
218
245
|
- 用户明确要求编译时
|
|
219
|
-
|
|
220
|
-
6. 每个任务完成后:
|
|
221
|
-
- 勾选 task-N.md 中的验收标准 checkbox
|
|
246
|
+
4. 每个任务完成后:
|
|
222
247
|
- 勾选 plan.md / tasks.md 中对应任务的 checkbox
|
|
223
248
|
- 记录改动文件和测试结果
|
|
224
|
-
|
|
249
|
+
5. 遇到 BLOCKED → 记录原因,选择:重试/跳过/停止
|
|
225
250
|
|
|
226
251
|
### 完成后
|
|
227
252
|
运行 sillyspec run execute --done --input "用户原始反馈" --output "Wave ${waveIndex} 结果摘要"`
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const definition = {
|
|
2
|
+
name: 'explore',
|
|
3
|
+
title: '自由探索',
|
|
4
|
+
description: '讨论、调研、画图,不写实现代码',
|
|
5
|
+
auxiliary: true,
|
|
6
|
+
steps: [
|
|
7
|
+
{
|
|
8
|
+
name: '自由探索',
|
|
9
|
+
prompt: `围绕用户给出的话题做技术探索,不进入实现。
|
|
10
|
+
|
|
11
|
+
### 操作
|
|
12
|
+
1. 明确探索边界:这次只讨论、调研、画图和识别风险
|
|
13
|
+
2. 如果需要代码库上下文,可以读取:
|
|
14
|
+
- \`.sillyspec/projects/*.yaml\`
|
|
15
|
+
- \`.sillyspec/docs/<project>/scan/ARCHITECTURE.md\`
|
|
16
|
+
- \`.sillyspec/docs/<project>/scan/CONVENTIONS.md\`
|
|
17
|
+
- \`.sillyspec/changes/<change-name>/design.md\`
|
|
18
|
+
3. 可以用 \`rg\` / \`ls\` / \`cat\` 调查已有结构和集成点
|
|
19
|
+
4. 输出 2-3 个有价值方向、关键风险和下一步建议
|
|
20
|
+
5. 如果用户要求保存结论,先明确保存位置,再写入对应文档
|
|
21
|
+
|
|
22
|
+
### 输出
|
|
23
|
+
探索结论、选项对比、风险清单或 ASCII 图
|
|
24
|
+
|
|
25
|
+
### 铁律
|
|
26
|
+
- 不写实现代码
|
|
27
|
+
- 不安装依赖
|
|
28
|
+
- 不修改文件,除非用户明确要求保存探索结论
|
|
29
|
+
- 不强行推进到 brainstorm/plan/execute`,
|
|
30
|
+
outputHint: '探索结论',
|
|
31
|
+
optional: false
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
package/src/stages/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { definition as execute } from './execute.js'
|
|
|
5
5
|
import { definition as verify } from './verify.js'
|
|
6
6
|
import { definition as scan } from './scan.js'
|
|
7
7
|
import { definition as quick } from './quick.js'
|
|
8
|
+
import { definition as explore } from './explore.js'
|
|
8
9
|
import { definition as archive } from './archive.js'
|
|
9
10
|
import { definition as status } from './status.js'
|
|
10
11
|
import { definition as doctor } from './doctor.js'
|
|
@@ -17,10 +18,11 @@ export const stageRegistry = {
|
|
|
17
18
|
verify,
|
|
18
19
|
scan,
|
|
19
20
|
quick,
|
|
21
|
+
explore,
|
|
20
22
|
archive,
|
|
21
23
|
status,
|
|
22
24
|
doctor
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
// 辅助命令(在没有 progress.json 时也可执行)
|
|
26
|
-
export const auxiliaryStages = ['scan', 'quick', 'archive', 'status', 'doctor']
|
|
28
|
+
export const auxiliaryStages = ['scan', 'quick', 'explore', 'archive', 'status', 'doctor']
|
package/src/stages/plan.js
CHANGED
|
@@ -218,6 +218,88 @@ ${taskName}
|
|
|
218
218
|
任务蓝图内容摘要`
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/**
|
|
222
|
+
* 构建任务蓝图协调器步骤(单步,子代理并行写蓝图)
|
|
223
|
+
*/
|
|
224
|
+
export function buildCoordinatorStep(changeDir, taskNames) {
|
|
225
|
+
const taskList = taskNames.map((name, i) => {
|
|
226
|
+
const num = String(i + 1).padStart(2, '0')
|
|
227
|
+
return `- task-${num}: ${name}`
|
|
228
|
+
}).join('\n')
|
|
229
|
+
|
|
230
|
+
const subagentPrompts = taskNames.map((name, i) => {
|
|
231
|
+
const num = String(i + 1).padStart(2, '0')
|
|
232
|
+
return `\`\`\`
|
|
233
|
+
任务编号:task-${num}
|
|
234
|
+
任务名称:${name}
|
|
235
|
+
文件路径:${changeDir}/tasks/task-${num}.md
|
|
236
|
+
|
|
237
|
+
操作:
|
|
238
|
+
1. 读取 ${changeDir}/design.md 和 ${changeDir}/plan.md 了解上下文
|
|
239
|
+
2. 读取相关源文件了解现有代码
|
|
240
|
+
3. 按以下格式编写任务蓝图并保存到 ${changeDir}/tasks/task-${num}.md:
|
|
241
|
+
|
|
242
|
+
# task-${num}: ${name}
|
|
243
|
+
|
|
244
|
+
## 修改文件
|
|
245
|
+
- 文件路径列表
|
|
246
|
+
|
|
247
|
+
## 实现要求
|
|
248
|
+
1. 具体做什么
|
|
249
|
+
|
|
250
|
+
## 接口定义
|
|
251
|
+
(代码类任务必填)
|
|
252
|
+
|
|
253
|
+
## 边界处理
|
|
254
|
+
- 异常场景
|
|
255
|
+
|
|
256
|
+
## 参考
|
|
257
|
+
- 可参考的模式
|
|
258
|
+
|
|
259
|
+
## TDD 步骤
|
|
260
|
+
1. 写测试 → 2. 确认失败 → 3. 写代码 → 4. 确认通过
|
|
261
|
+
|
|
262
|
+
## 验收标准
|
|
263
|
+
- [ ] 具体可测试的条件
|
|
264
|
+
|
|
265
|
+
关键规则:
|
|
266
|
+
- 必须独立完整,execute 子代理只读这一个文件就能干活
|
|
267
|
+
- 不要依赖其他 task-N.md 的内容
|
|
268
|
+
- 接口定义写到"搬砖工照着做"的程度
|
|
269
|
+
- 写完后用 Write tool 保存到文件
|
|
270
|
+
\`\`\``
|
|
271
|
+
}).join('\n\n')
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
name: '生成任务蓝图(子代理并行)',
|
|
275
|
+
prompt: `为 plan.md 中的每个任务生成独立蓝图文件。
|
|
276
|
+
|
|
277
|
+
## 任务清单
|
|
278
|
+
${taskList}
|
|
279
|
+
|
|
280
|
+
## 执行方式(必须严格遵守)
|
|
281
|
+
|
|
282
|
+
**你必须使用 Agent tool 启动子代理来写每个蓝图,不要自己写。**
|
|
283
|
+
|
|
284
|
+
1. 确认 \`${changeDir}/tasks/\` 目录存在(不存在则创建)
|
|
285
|
+
2. 为每个任务启动一个独立子代理(Agent tool),可并行启动多个
|
|
286
|
+
3. 每个子代理使用对应的 prompt(见下方模板)
|
|
287
|
+
4. 等待所有子代理完成
|
|
288
|
+
5. 验证每个 task-N.md 文件已生成且非空
|
|
289
|
+
|
|
290
|
+
### 子代理 prompt 模板
|
|
291
|
+
为每个任务使用以下 prompt 启动子代理:
|
|
292
|
+
|
|
293
|
+
${subagentPrompts}
|
|
294
|
+
|
|
295
|
+
## 验收
|
|
296
|
+
- 每个 task-N.md 文件存在且非空
|
|
297
|
+
- 包含所有必要章节:修改文件、实现要求、接口定义、边界处理、TDD 步骤、验收标准`,
|
|
298
|
+
outputHint: '蓝图生成结果',
|
|
299
|
+
optional: false
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
221
303
|
/**
|
|
222
304
|
* 从 plan.md 解析任务名列表
|
|
223
305
|
*/
|
|
@@ -255,8 +337,7 @@ export function buildPlanSteps(changeDir = null, planContent = null) {
|
|
|
255
337
|
return [...fixedPrefix, ...fixedSuffix]
|
|
256
338
|
}
|
|
257
339
|
|
|
258
|
-
//
|
|
259
|
-
// 尝试从 plan.md 解析任务名
|
|
340
|
+
// 解析任务名
|
|
260
341
|
let taskNames = []
|
|
261
342
|
if (planContent) {
|
|
262
343
|
taskNames = parseTaskNames(planContent)
|
|
@@ -267,16 +348,7 @@ export function buildPlanSteps(changeDir = null, planContent = null) {
|
|
|
267
348
|
}
|
|
268
349
|
}
|
|
269
350
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
taskSteps.push({
|
|
274
|
-
name: `写任务蓝图 task-${String(i).padStart(2, '0')}`,
|
|
275
|
-
prompt: `### 注意\n这是第 ${i}/${taskCount} 个任务蓝图。focus 在这一个任务上,不要写其他任务的内容。\n\n${buildTaskPrompt(i, taskName, changeDir)}`,
|
|
276
|
-
outputHint: `task-${String(i).padStart(2, '0')} 蓝图`,
|
|
277
|
-
optional: false
|
|
278
|
-
})
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return [...fixedPrefix, ...taskSteps, ...fixedSuffix]
|
|
351
|
+
// 生成单个协调器步骤(子代理并行写蓝图)
|
|
352
|
+
const coordinatorStep = buildCoordinatorStep(changeDir, taskNames)
|
|
353
|
+
return [...fixedPrefix, coordinatorStep, ...fixedSuffix]
|
|
282
354
|
}
|
package/src/stages/scan.js
CHANGED
|
@@ -10,8 +10,8 @@ export const definition = {
|
|
|
10
10
|
|
|
11
11
|
### 操作
|
|
12
12
|
1. \`ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q .\` — 检查已有文档
|
|
13
|
-
1. \`ls docs/*/scan/ 2>/dev/null\` — 检查已有文档
|
|
14
|
-
2. \`wc -l docs/*/scan/*.md 2>/dev/null\` — 文档行数
|
|
13
|
+
1. \`ls .sillyspec/docs/*/scan/ 2>/dev/null\` — 检查已有文档
|
|
14
|
+
2. \`wc -l .sillyspec/docs/*/scan/*.md 2>/dev/null\` — 文档行数
|
|
15
15
|
3. 已有 3 份 → 建议升级深度扫描;已有 7 份 → 建议刷新或跳过
|
|
16
16
|
5. 显示子项目列表供选择扫描范围
|
|
17
17
|
|
|
@@ -27,7 +27,7 @@ export const definition = {
|
|
|
27
27
|
### 操作
|
|
28
28
|
1. \`cat package.json pom.xml build.gradle go.mod Cargo.toml requirements.txt pyproject.toml Gemfile composer.json 2>/dev/null\`
|
|
29
29
|
2. \`find . -maxdepth 2 -name "*.config.*" -not -path "*/node_modules/*" -not -path "*/.git/*" | head -20 | xargs cat 2>/dev/null\`
|
|
30
|
-
3. 结果保存到
|
|
30
|
+
3. 结果保存到 \`.sillyspec/docs/<project>/scan/_env-detect.md\`(临时文件,扫描完删除)
|
|
31
31
|
|
|
32
32
|
### 输出
|
|
33
33
|
环境探测结果摘要`,
|
|
@@ -56,7 +56,7 @@ export const definition = {
|
|
|
56
56
|
### 操作
|
|
57
57
|
1. 用 grep/rg 搜索(\`@Entity\`、\`schema.prisma\`、\`models.py\` 等),**禁止读源码全文**
|
|
58
58
|
2. Schema 只记表名+说明+字段数
|
|
59
|
-
3. 写入
|
|
59
|
+
3. 写入 \`.sillyspec/docs/<project>/scan/ARCHITECTURE.md\`
|
|
60
60
|
4. 包含 \`## 技术栈\` \`## 架构概览\` \`## 数据模型(摘要)\`
|
|
61
61
|
|
|
62
62
|
### 输出
|
|
@@ -75,7 +75,7 @@ ARCHITECTURE.md 路径
|
|
|
75
75
|
1. 用 grep 搜索拦截器/插件/逻辑删除/基类/审计字段,**禁止读源码全文**
|
|
76
76
|
2. 根据检测到的语言/框架自行决定搜索什么模式
|
|
77
77
|
3. 提取 3-5 个典型示例
|
|
78
|
-
4. 写入
|
|
78
|
+
4. 写入 \`.sillyspec/docs/<project>/scan/CONVENTIONS.md\`
|
|
79
79
|
5. 包含 \`## 框架隐形规则\` \`## 实体继承规范\` \`## 代码风格\`
|
|
80
80
|
|
|
81
81
|
### 输出
|
|
@@ -93,8 +93,8 @@ CONVENTIONS.md 路径
|
|
|
93
93
|
### 操作
|
|
94
94
|
1. 用 find/ls/tree 和 grep,**禁止读源码全文**
|
|
95
95
|
2. 搜索 API 调用、MQ 配置、缓存、第三方 SDK
|
|
96
|
-
3. 写入
|
|
97
|
-
4. 写入
|
|
96
|
+
3. 写入 \`.sillyspec/docs/<project>/scan/STRUCTURE.md\`(目录树+模块说明)
|
|
97
|
+
4. 写入 \`.sillyspec/docs/<project>/scan/INTEGRATIONS.md\`(按类型分组)
|
|
98
98
|
|
|
99
99
|
### 输出
|
|
100
100
|
STRUCTURE.md 和 INTEGRATIONS.md 路径
|
|
@@ -110,9 +110,9 @@ STRUCTURE.md 和 INTEGRATIONS.md 路径
|
|
|
110
110
|
|
|
111
111
|
### 操作
|
|
112
112
|
1. 用 grep 搜索测试文件、TODO/FIXME、过时依赖,**禁止读源码全文**
|
|
113
|
-
2. 写入
|
|
114
|
-
3. 写入
|
|
115
|
-
4. 写入
|
|
113
|
+
2. 写入 \`.sillyspec/docs/<project>/scan/TESTING.md\`(测试结构)
|
|
114
|
+
3. 写入 \`.sillyspec/docs/<project>/scan/CONCERNS.md\`(按严重程度分组)
|
|
115
|
+
4. 写入 \`.sillyspec/docs/<project>/scan/PROJECT.md\`(项目信息)
|
|
116
116
|
|
|
117
117
|
### 输出
|
|
118
118
|
TESTING.md、CONCERNS.md、PROJECT.md 路径`,
|
|
@@ -126,7 +126,7 @@ TESTING.md、CONCERNS.md、PROJECT.md 路径`,
|
|
|
126
126
|
### 操作
|
|
127
127
|
1. 检查 7 份文档是否全部生成
|
|
128
128
|
2. 自检门控:ARCHITECTURE(技术栈+Schema摘要)、CONVENTIONS(隐形规则+代码风格)、STRUCTURE(目录结构)、INTEGRATIONS(外部依赖)、TESTING(测试现状)、CONCERNS(技术债务)、PROJECT(项目概览)
|
|
129
|
-
3. 清理:\`rm -f docs/<project>/scan/_env-detect.md\`
|
|
129
|
+
3. 清理:\`rm -f .sillyspec/docs/<project>/scan/_env-detect.md\`
|
|
130
130
|
4. \`git add .\` — **不要 commit**,由用户通过统一提交工具处理
|
|
131
131
|
|
|
132
132
|
### 输出
|
package/src/stages/status.js
CHANGED
|
@@ -11,7 +11,7 @@ export const definition = {
|
|
|
11
11
|
### 操作
|
|
12
12
|
1. \`cat .sillyspec/PROJECT.md 2>/dev/null || echo "未初始化"\`
|
|
13
13
|
2. 获取 project 名
|
|
14
|
-
3. \`ls docs/<project>/scan/ 2>/dev/null | head -10\`
|
|
14
|
+
3. \`ls .sillyspec/docs/<project>/scan/ 2>/dev/null | head -10\`
|
|
15
15
|
4. \`cat .sillyspec/REQUIREMENTS.md 2>/dev/null | head -20\`
|
|
16
16
|
5. \`cat .sillyspec/ROADMAP.md 2>/dev/null\`
|
|
17
17
|
|