sillyspec 3.8.4 → 3.8.6
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/README.md +0 -6
- package/docs/.vitepress/config.mts +45 -0
- package/docs/.vitepress/dist/404.html +25 -0
- package/docs/.vitepress/dist/assets/app.YytxICdd.js +1 -0
- package/docs/.vitepress/dist/assets/chunks/framework.Czhw_PXq.js +19 -0
- package/docs/.vitepress/dist/assets/chunks/theme.DusTRZQk.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.js +1 -0
- package/docs/.vitepress/dist/assets/index.md.C3VCvtQA.lean.js +1 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/docs/.vitepress/dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.js +15 -0
- package/docs/.vitepress/dist/assets/sillyspec_commands.md.CXFFsj08.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.js +4 -0
- package/docs/.vitepress/dist/assets/sillyspec_dashboard.md.BuPXHqjX.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_file-io.md.Cz3x7llx.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.js +4 -0
- package/docs/.vitepress/dist/assets/sillyspec_getting-started.md.ClcvV8k3.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.js +5 -0
- package/docs/.vitepress/dist/assets/sillyspec_install.md.CKuR2tiT.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.js +28 -0
- package/docs/.vitepress/dist/assets/sillyspec_lifecycle.md.DY293cR1.lean.js +1 -0
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.js +30 -0
- package/docs/.vitepress/dist/assets/sillyspec_structure.md.sVYS4zPs.lean.js +1 -0
- package/docs/.vitepress/dist/assets/style.DFTx90Kk.css +1 -0
- package/docs/.vitepress/dist/hashmap.json +1 -0
- package/docs/.vitepress/dist/index.html +28 -0
- package/docs/.vitepress/dist/sillyspec/commands.html +42 -0
- package/docs/.vitepress/dist/sillyspec/dashboard.html +31 -0
- package/docs/.vitepress/dist/sillyspec/file-io.html +28 -0
- package/docs/.vitepress/dist/sillyspec/getting-started.html +31 -0
- package/docs/.vitepress/dist/sillyspec/install.html +32 -0
- package/docs/.vitepress/dist/sillyspec/lifecycle.html +55 -0
- package/docs/.vitepress/dist/sillyspec/structure.html +57 -0
- package/docs/.vitepress/dist/vp-icons.css +1 -0
- package/docs/index.md +34 -0
- package/docs/sillyspec/commands.md +218 -0
- package/docs/sillyspec/dashboard.md +51 -0
- package/docs/sillyspec/file-io.md +34 -0
- package/docs/sillyspec/getting-started.md +61 -0
- package/docs/sillyspec/install.md +51 -0
- package/docs/sillyspec/lifecycle.md +146 -0
- package/docs/sillyspec/structure.md +62 -0
- package/package.json +11 -9
- package/packages/dashboard/dist/assets/index-Bh-GPjKY.css +1 -0
- package/packages/dashboard/dist/assets/index-CrCn5Gg6.js +17 -0
- package/packages/dashboard/dist/index.html +2 -2
- package/packages/dashboard/package-lock.json +0 -220
- package/packages/dashboard/package.json +5 -8
- package/packages/dashboard/server/index.js +106 -255
- package/packages/dashboard/server/parser.js +29 -333
- package/packages/dashboard/server/watcher.js +131 -203
- package/packages/dashboard/src/App.vue +10 -181
- package/packages/dashboard/src/components/ActionBar.vue +42 -26
- package/packages/dashboard/src/components/CommandPalette.vue +65 -40
- package/packages/dashboard/src/components/DetailPanel.vue +53 -68
- package/packages/dashboard/src/components/LogStream.vue +33 -13
- package/packages/dashboard/src/components/PipelineStage.vue +8 -8
- package/packages/dashboard/src/components/PipelineView.vue +45 -80
- package/packages/dashboard/src/components/ProjectList.vue +45 -103
- package/packages/dashboard/src/components/StageBadge.vue +13 -13
- package/packages/dashboard/src/components/StepCard.vue +15 -15
- package/packages/dashboard/src/composables/useDashboard.js +6 -20
- package/packages/dashboard/src/composables/useKeyboard.js +4 -6
- package/packages/dashboard/src/main.js +1 -4
- package/packages/dashboard/src/style.css +17 -17
- package/src/index.js +12 -123
- package/src/init.js +227 -86
- package/src/setup.js +9 -1
- package/templates/archive.md +121 -0
- package/templates/brainstorm.md +240 -0
- package/{.claude/skills/sillyspec-commit/SKILL.md → templates/commit.md} +47 -29
- package/templates/continue.md +32 -0
- package/templates/execute.md +314 -0
- package/templates/explore.md +60 -0
- package/templates/export.md +21 -0
- package/templates/init.md +61 -0
- package/templates/plan.md +157 -0
- package/templates/quick.md +135 -0
- package/templates/scan-quick.md +49 -0
- package/templates/scan.md +172 -0
- package/templates/skills/playwright-e2e/SKILL.md +340 -0
- package/templates/status.md +75 -0
- package/templates/verify.md +253 -0
- package/templates/workspace-sync.md +99 -0
- package/templates/workspace.md +70 -0
- package/.claude/skills/sillyspec-archive/SKILL.md +0 -17
- package/.claude/skills/sillyspec-auto/SKILL.md +0 -77
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +0 -17
- package/.claude/skills/sillyspec-continue/SKILL.md +0 -44
- package/.claude/skills/sillyspec-doctor/SKILL.md +0 -22
- package/.claude/skills/sillyspec-execute/SKILL.md +0 -17
- package/.claude/skills/sillyspec-explore/SKILL.md +0 -96
- package/.claude/skills/sillyspec-export/SKILL.md +0 -53
- package/.claude/skills/sillyspec-init/SKILL.md +0 -170
- package/.claude/skills/sillyspec-plan/SKILL.md +0 -52
- package/.claude/skills/sillyspec-propose/SKILL.md +0 -17
- package/.claude/skills/sillyspec-quick/SKILL.md +0 -17
- package/.claude/skills/sillyspec-resume/SKILL.md +0 -111
- package/.claude/skills/sillyspec-scan/SKILL.md +0 -17
- package/.claude/skills/sillyspec-state/SKILL.md +0 -54
- package/.claude/skills/sillyspec-status/SKILL.md +0 -17
- package/.claude/skills/sillyspec-verify/SKILL.md +0 -17
- package/.claude/skills/sillyspec-workspace/SKILL.md +0 -149
- 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-unified-docs-design.md +0 -199
- 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/projects/sillyspec.yaml +0 -3
- package/packages/dashboard/dist/assets/index-D1EVTLmc.js +0 -7446
- package/packages/dashboard/dist/assets/index-DGe8CqeP.css +0 -1
- package/packages/dashboard/public/logo.jpg +0 -0
- package/packages/dashboard/src/components/DocPreview.vue +0 -160
- package/packages/dashboard/src/components/DocTree.vue +0 -58
- package/packages/dashboard/src/components/ProjectOverview.vue +0 -178
- package/packages/dashboard/src/components/detail/DocsDetail.vue +0 -48
- package/packages/dashboard/src/components/detail/GitDetail.vue +0 -61
- package/packages/dashboard/src/components/detail/TechDetail.vue +0 -43
- package/src/derive.js +0 -147
- package/src/migrate.js +0 -117
- package/src/progress.js +0 -495
- package/src/run.js +0 -640
- package/src/stages/archive.js +0 -54
- package/src/stages/brainstorm.js +0 -239
- package/src/stages/doctor.js +0 -312
- package/src/stages/execute.js +0 -258
- package/src/stages/index.js +0 -35
- package/src/stages/plan.js +0 -259
- package/src/stages/propose.js +0 -115
- package/src/stages/quick.js +0 -64
- package/src/stages/scan.js +0 -141
- package/src/stages/status.js +0 -65
- package/src/stages/verify.js +0 -135
- /package/.sillyspec/{changes/brainstorm-archive → specs}/2026-04-05-dashboard-design.md +0 -0
- /package/{packages/dashboard → docs/.vitepress}/dist/favicon.jpg +0 -0
- /package/{logo.jpg → docs/.vitepress/dist/logo.jpg} +0 -0
- /package/{packages/dashboard → docs}/public/favicon.jpg +0 -0
- /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
|
-
}
|
package/src/stages/archive.js
DELETED
|
@@ -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
|
-
}
|