sillyspec 3.7.25 → 3.7.27

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sillyspec",
3
- "version": "3.7.25",
3
+ "version": "3.7.27",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "icon": "logo.jpg",
6
6
  "homepage": "https://sillyspec.ppdmq.top/",
package/src/index.js CHANGED
@@ -189,7 +189,7 @@ async function main() {
189
189
  }
190
190
  case 'run': {
191
191
  const { runCommand } = await import('./run.js')
192
- runCommand(filteredArgs.slice(1), dir)
192
+ await runCommand(filteredArgs.slice(1), dir)
193
193
  break
194
194
  }
195
195
  case 'dashboard': {
package/src/progress.js CHANGED
@@ -39,7 +39,7 @@ function emptyStage() {
39
39
  function makeInitialProgress(project) {
40
40
  const stages = {};
41
41
  for (const s of VALID_STAGES) stages[s] = emptyStage();
42
- return { _version: CURRENT_VERSION, project: project || '', currentStage: '', stages, lastActive: null };
42
+ return { _version: CURRENT_VERSION, project: project || '', currentStage: '', currentChange: null, stages, lastActive: null };
43
43
  }
44
44
 
45
45
  // ── ProgressManager ──
@@ -133,9 +133,9 @@ export class ProgressManager {
133
133
  data.currentStage = stage;
134
134
  if (stageData.status === 'pending') {
135
135
  stageData.status = 'in-progress';
136
- stageData.startedAt = new Date().toISOString();
136
+ stageData.startedAt = new Date().toLocaleString('zh-CN',{hour12:false});
137
137
  }
138
- data.lastActive = new Date().toISOString();
138
+ data.lastActive = new Date().toLocaleString('zh-CN',{hour12:false});
139
139
 
140
140
  this._backup(cwd);
141
141
  this._write(cwd, data);
@@ -154,7 +154,7 @@ export class ProgressManager {
154
154
  }
155
155
 
156
156
  stageData.steps.push({ name: stepName, status: 'pending' });
157
- data.lastActive = new Date().toISOString();
157
+ data.lastActive = new Date().toLocaleString('zh-CN',{hour12:false});
158
158
 
159
159
  this._backup(cwd);
160
160
  this._write(cwd, data);
@@ -183,11 +183,11 @@ export class ProgressManager {
183
183
  // 检查是否所有步骤都 completed
184
184
  if (stageData.steps.length > 0 && stageData.steps.every(s => s.status === 'completed')) {
185
185
  stageData.status = 'completed';
186
- stageData.completedAt = new Date().toISOString();
186
+ stageData.completedAt = new Date().toLocaleString('zh-CN',{hour12:false});
187
187
  console.log(`✅ 阶段 ${stage} 所有步骤已完成,阶段已标记为 completed`);
188
188
  }
189
189
 
190
- data.lastActive = new Date().toISOString();
190
+ data.lastActive = new Date().toLocaleString('zh-CN',{hour12:false});
191
191
  this._backup(cwd);
192
192
  this._write(cwd, data);
193
193
  console.log(`✅ 步骤已更新: ${stage}/${stepName} → ${status || step.status}`);
@@ -205,7 +205,7 @@ export class ProgressManager {
205
205
  if (!data.stages[stage]) data.stages[stage] = emptyStage();
206
206
  const stageData = data.stages[stage];
207
207
  stageData.status = 'completed';
208
- stageData.completedAt = new Date().toISOString();
208
+ stageData.completedAt = new Date().toLocaleString('zh-CN',{hour12:false});
209
209
 
210
210
  // 标记所有未完成步骤为 completed
211
211
  for (const step of stageData.steps) {
@@ -228,7 +228,7 @@ export class ProgressManager {
228
228
  if (!data.stages[nextStage]) data.stages[nextStage] = emptyStage();
229
229
  if (data.stages[nextStage].status === 'pending') {
230
230
  data.stages[nextStage].status = 'in-progress';
231
- data.stages[nextStage].startedAt = new Date().toISOString();
231
+ data.stages[nextStage].startedAt = new Date().toLocaleString('zh-CN',{hour12:false});
232
232
  }
233
233
  console.log(`✅ 阶段 ${stage} 已完成,推进到: ${STAGE_LABELS[nextStage] || nextStage}`);
234
234
  } else {
@@ -236,12 +236,12 @@ export class ProgressManager {
236
236
  console.log(`✅ 阶段 ${stage} 已完成(已是最后阶段)`);
237
237
  }
238
238
 
239
- data.lastActive = new Date().toISOString();
239
+ data.lastActive = new Date().toLocaleString('zh-CN',{hour12:false});
240
240
 
241
241
  // 归档到 history/
242
242
  const historyDir = this._path(cwd, 'history');
243
243
  mkdirSync(historyDir, { recursive: true });
244
- const ts = new Date().toISOString().replace(/[:.]/g, '-');
244
+ const ts = new Date().toLocaleString('zh-CN',{hour12:false}).replace(/[:.]/g, '-');
245
245
  writeFileSync(join(historyDir, `${stage}-${ts}.json`), JSON.stringify({ stage, data: stageData, completedAt: stageData.completedAt }, null, 2) + '\n');
246
246
 
247
247
  this._backup(cwd);
@@ -336,7 +336,7 @@ export class ProgressManager {
336
336
  if (!data) { console.log('❌ 无法读取 progress.json'); return; }
337
337
  if (!data.stages[stage]) { console.log(`❌ 未知阶段: ${stage}`); return; }
338
338
  data.stages[stage] = emptyStage();
339
- data.lastActive = new Date().toISOString();
339
+ data.lastActive = new Date().toLocaleString('zh-CN',{hour12:false});
340
340
  this._write(cwd, data);
341
341
  console.log(`✅ 已重置阶段: ${stage}`);
342
342
  } else {
package/src/run.js CHANGED
@@ -12,13 +12,40 @@ import { buildExecuteSteps } from './stages/execute.js'
12
12
  /**
13
13
  * 获取阶段的步骤定义(execute 需要动态构建)
14
14
  */
15
- function getStageSteps(stageName, cwd) {
15
+ async function getStageSteps(stageName, cwd, progress) {
16
16
  if (stageName === 'execute') {
17
- const plansDir = join(cwd, '.sillyspec', 'plans')
17
+ const changesDir = join(cwd, '.sillyspec', 'changes')
18
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])
19
+ // 优先用 currentChange 指定的变更名
20
+ if (progress.currentChange) {
21
+ const target = join(changesDir, progress.currentChange, 'plan.md')
22
+ if (existsSync(target)) planFile = target
23
+ }
24
+ // fallback:扫描 changes/ 非 archive 目录下的 plan.md
25
+ if (!planFile && existsSync(changesDir)) {
26
+ const candidates = []
27
+ for (const entry of readdirSync(changesDir, { withFileTypes: true })) {
28
+ if (!entry.isDirectory() || entry.name === 'archive') continue
29
+ const p = join(changesDir, entry.name, 'plan.md')
30
+ if (existsSync(p)) candidates.push({ name: entry.name, path: p })
31
+ }
32
+ if (candidates.length === 1) {
33
+ planFile = candidates[0].path
34
+ } else if (candidates.length > 1) {
35
+ console.log('⚠️ 检测到多个变更,请选择:')
36
+ candidates.forEach((c, i) => console.log(` ${i + 1}. ${c.name}`))
37
+ const readline = await import('readline')
38
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
39
+ const answer = await new Promise(resolve => {
40
+ rl.question(`\n请输入编号(默认 1):`, input => {
41
+ rl.close()
42
+ const num = parseInt(input) || 1
43
+ resolve(num >= 1 && num <= candidates.length ? num - 1 : 0)
44
+ })
45
+ })
46
+ planFile = candidates[answer].path
47
+ console.log(`✅ 已选择:${candidates[answer].name}\n`)
48
+ }
22
49
  }
23
50
  return buildExecuteSteps(planFile)
24
51
  }
@@ -29,16 +56,16 @@ function getStageSteps(stageName, cwd) {
29
56
  /**
30
57
  * 确保阶段的 steps 已初始化到 progress.json
31
58
  */
32
- function ensureStageSteps(progress, stageName, cwd) {
59
+ async function ensureStageSteps(progress, stageName, cwd) {
33
60
  if (!progress.stages) progress.stages = {}
34
61
 
35
- const steps = getStageSteps(stageName, cwd)
62
+ const steps = await getStageSteps(stageName, cwd, progress)
36
63
  if (!steps) return false
37
64
 
38
65
  if (!progress.stages[stageName] || !progress.stages[stageName].steps || progress.stages[stageName].steps.length === 0) {
39
66
  progress.stages[stageName] = {
40
67
  status: 'in-progress',
41
- startedAt: new Date().toISOString(),
68
+ startedAt: new Date().toLocaleString('zh-CN',{hour12:false}),
42
69
  completedAt: null,
43
70
  steps: steps.map(s => ({ name: s.name, status: 'pending' }))
44
71
  }
@@ -78,9 +105,10 @@ function outputStep(stageName, stepIndex, steps, cwd) {
78
105
  console.log(step.prompt)
79
106
  console.log(`\n### ⚠️ 铁律`)
80
107
  console.log('- 只做本步骤描述的操作,不得自行扩展或跳过')
81
- console.log('- 不要回头修改已完成的步骤')
108
+ console.log('- 不要回头修改已完成的步骤')
82
109
  console.log('- 完成后立即执行 --done 命令,不得跳过')
83
110
  console.log('- 生成的文件头部必须包含 author(git 用户名)和 created_at(精确到秒)')
111
+ console.log('- 执行构建/测试前必须先读 local.yaml,优先使用其中配置的命令、路径和环境变量;未配置时才使用默认值')
84
112
  console.log(`\n### 完成后执行`)
85
113
  console.log(`sillyspec run ${stageName} --done --output "你的摘要"`)
86
114
  }
@@ -88,7 +116,7 @@ function outputStep(stageName, stepIndex, steps, cwd) {
88
116
  /**
89
117
  * sillyspec run <stage> 主命令
90
118
  */
91
- export function runCommand(args, cwd) {
119
+ export async function runCommand(args, cwd) {
92
120
  // 解析参数
93
121
  const stageName = args[0]
94
122
  const flags = args.slice(1)
@@ -111,6 +139,13 @@ export function runCommand(args, cwd) {
111
139
  outputText = flags[outputIdx + 1]
112
140
  }
113
141
 
142
+ // 解析 --change <name>
143
+ let changeName = null
144
+ const changeIdx = flags.indexOf('--change')
145
+ if (changeIdx !== -1 && flags[changeIdx + 1]) {
146
+ changeName = flags[changeIdx + 1]
147
+ }
148
+
114
149
  const isAuxiliary = auxiliaryStages.includes(stageName)
115
150
 
116
151
  const pm = new ProgressManager()
@@ -125,13 +160,22 @@ export function runCommand(args, cwd) {
125
160
  progress = pm.init(cwd)
126
161
  }
127
162
 
163
+ // --change 设置当前变更名
164
+ if (changeName) {
165
+ progress.currentChange = changeName
166
+ progress.lastActive = new Date().toLocaleString('zh-CN', { hour12: false })
167
+ pm._write(cwd, progress)
168
+ console.log(`✅ 当前变更设置为:${changeName}`)
169
+ return
170
+ }
171
+
128
172
  // --reset
129
173
  if (isReset) {
130
- return resetStage(pm, progress, stageName, cwd)
174
+ return await resetStage(pm, progress, stageName, cwd)
131
175
  }
132
176
 
133
177
  // 确保步骤已初始化
134
- const changed = ensureStageSteps(progress, stageName, cwd)
178
+ const changed = await ensureStageSteps(progress, stageName, cwd)
135
179
  if (changed) {
136
180
  pm._write(cwd, progress)
137
181
  progress = pm.read(cwd)
@@ -144,19 +188,19 @@ export function runCommand(args, cwd) {
144
188
 
145
189
  // --skip
146
190
  if (isSkip) {
147
- return skipStep(pm, progress, stageName, cwd)
191
+ return await skipStep(pm, progress, stageName, cwd)
148
192
  }
149
193
 
150
194
  // --done
151
195
  if (isDone) {
152
- return completeStep(pm, progress, stageName, cwd, outputText)
196
+ return await completeStep(pm, progress, stageName, cwd, outputText)
153
197
  }
154
198
 
155
199
  // 默认:输出当前步骤
156
- return runStage(pm, progress, stageName, cwd)
200
+ return await runStage(pm, progress, stageName, cwd)
157
201
  }
158
202
 
159
- function runStage(pm, progress, stageName, cwd) {
203
+ async function runStage(pm, progress, stageName, cwd) {
160
204
  const stageData = progress.stages[stageName]
161
205
  if (!stageData || !stageData.steps) {
162
206
  console.error(`❌ 阶段 ${stageName} 未初始化`)
@@ -167,17 +211,25 @@ function runStage(pm, progress, stageName, cwd) {
167
211
  let currentIdx = steps.findIndex(s => s.status !== 'completed' && s.status !== 'skipped')
168
212
 
169
213
  if (currentIdx === -1) {
170
- // 阶段已完成,自动重置,允许重复执行
214
+ // 阶段已完成
215
+ console.log(`✅ ${stageName} 阶段已完成。`)
216
+ console.log(` 继续执行将重新开始,可用 --reset 显式重置。\n`)
217
+ // 自动重置,允许重复执行
171
218
  steps.forEach(s => { s.status = 'pending'; s.completedAt = null; s.output = null; s.startedAt = null })
172
219
  stageData.status = 'in_progress'
173
220
  stageData.completedAt = null
174
- console.log(`🔄 ${stageName} 阶段已完成,重新开始...\n`)
175
221
  pm._write(cwd, progress)
176
222
  currentIdx = 0
223
+ } else if (currentIdx > 0) {
224
+ // 有进行中的步骤,提示用户
225
+ const completed = currentIdx
226
+ const total = steps.length
227
+ console.log(`⚠️ ${stageName} 已进行到第 ${currentIdx + 1}/${total} 步(前 ${completed} 步已完成)。`)
228
+ console.log(` 继续执行将从中断处恢复,用 --reset 可重新开始。\n`)
177
229
  }
178
230
 
179
231
  const stageDef = stageRegistry[stageName]
180
- const defSteps = getStageSteps(stageName, cwd)
232
+ const defSteps = await getStageSteps(stageName, cwd, progress)
181
233
  if (defSteps && defSteps[currentIdx]) {
182
234
  outputStep(stageName, currentIdx, defSteps, cwd)
183
235
  }
@@ -215,7 +267,7 @@ function validateMetadata(cwd, stageName) {
215
267
  }
216
268
  }
217
269
 
218
- function completeStep(pm, progress, stageName, cwd, outputText) {
270
+ async function completeStep(pm, progress, stageName, cwd, outputText) {
219
271
  const stageData = progress.stages[stageName]
220
272
  if (!stageData || !stageData.steps) {
221
273
  console.error(`❌ 阶段 ${stageName} 未初始化`)
@@ -232,7 +284,7 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
232
284
 
233
285
  // 标记完成
234
286
  steps[currentIdx].status = 'completed'
235
- steps[currentIdx].completedAt = new Date().toISOString()
287
+ steps[currentIdx].completedAt = new Date().toLocaleString('zh-CN',{hour12:false})
236
288
  if (outputText) {
237
289
  const MAX_OUTPUT = 200
238
290
  if (outputText.length > MAX_OUTPUT) {
@@ -240,7 +292,7 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
240
292
  // Save full output to artifacts/
241
293
  const artifactsDir = join(cwd, '.sillyspec', '.runtime', 'artifacts')
242
294
  mkdirSync(artifactsDir, { recursive: true })
243
- const ts = new Date().toISOString().replace(/[:.]/g, '-')
295
+ const ts = new Date().toISOString().slice(0,19).replace(/[-T:]/g, '')
244
296
  writeFileSync(join(artifactsDir, `${stageName}-step${currentIdx + 1}-${ts}.txt`), outputText)
245
297
  } else {
246
298
  steps[currentIdx].output = outputText
@@ -253,7 +305,7 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
253
305
  if (nextPendingIdx === -1) {
254
306
  // 全部完成
255
307
  stageData.status = 'completed'
256
- stageData.completedAt = new Date().toISOString()
308
+ stageData.completedAt = new Date().toLocaleString('zh-CN',{hour12:false})
257
309
 
258
310
  const next = getNextStage(stageName)
259
311
  if (next) {
@@ -261,17 +313,17 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
261
313
  if (!progress.stages[next]) progress.stages[next] = { status: 'pending', steps: [], startedAt: null, completedAt: null }
262
314
  if (progress.stages[next].status === 'pending' || !progress.stages[next].status) {
263
315
  progress.stages[next].status = 'in-progress'
264
- progress.stages[next].startedAt = new Date().toISOString()
316
+ progress.stages[next].startedAt = new Date().toLocaleString('zh-CN',{hour12:false})
265
317
  }
266
318
  }
267
319
 
268
- progress.lastActive = new Date().toISOString()
320
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
269
321
  pm._write(cwd, progress)
270
322
 
271
323
  // Append to user-inputs.md
272
324
  if (outputText) {
273
325
  const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
274
- const entry = `\n## ${new Date().toISOString()} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
326
+ const entry = `\n## ${new Date().toLocaleString('zh-CN',{hour12:false})} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
275
327
  appendFileSync(inputsPath, entry)
276
328
  }
277
329
 
@@ -287,22 +339,22 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
287
339
  return
288
340
  }
289
341
 
290
- progress.lastActive = new Date().toISOString()
342
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
291
343
  pm._write(cwd, progress)
292
344
 
293
345
  // Append to user-inputs.md
294
346
  if (outputText) {
295
347
  const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
296
- const entry = `\n## ${new Date().toISOString()} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
348
+ const entry = `\n## ${new Date().toLocaleString('zh-CN',{hour12:false})} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
297
349
  appendFileSync(inputsPath, entry)
298
350
  }
299
351
 
300
- const defSteps = getStageSteps(stageName, cwd)
352
+ const defSteps = await getStageSteps(stageName, cwd, progress)
301
353
  console.log(`✅ Step ${currentIdx + 1}/${steps.length} 完成:${steps[currentIdx].name}\n`)
302
354
  outputStep(stageName, nextPendingIdx, defSteps, cwd)
303
355
  }
304
356
 
305
- function skipStep(pm, progress, stageName, cwd) {
357
+ async function skipStep(pm, progress, stageName, cwd) {
306
358
  const stageData = progress.stages[stageName]
307
359
  if (!stageData || !stageData.steps) {
308
360
  console.error(`❌ 阶段 ${stageName} 未初始化`)
@@ -317,7 +369,7 @@ function skipStep(pm, progress, stageName, cwd) {
317
369
  process.exit(1)
318
370
  }
319
371
 
320
- const defSteps = getStageSteps(stageName, cwd)
372
+ const defSteps = await getStageSteps(stageName, cwd, progress)
321
373
  const stepDef = defSteps ? defSteps[currentIdx] : null
322
374
  if (stepDef && !stepDef.optional) {
323
375
  console.error(`❌ 步骤 "${steps[currentIdx].name}" 不可跳过`)
@@ -325,8 +377,8 @@ function skipStep(pm, progress, stageName, cwd) {
325
377
  }
326
378
 
327
379
  steps[currentIdx].status = 'skipped'
328
- steps[currentIdx].skippedAt = new Date().toISOString()
329
- progress.lastActive = new Date().toISOString()
380
+ steps[currentIdx].skippedAt = new Date().toLocaleString('zh-CN',{hour12:false})
381
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
330
382
  pm._write(cwd, progress)
331
383
 
332
384
  console.log(`⏭️ Step ${currentIdx + 1}/${steps.length} 已跳过:${steps[currentIdx].name}`)
@@ -365,15 +417,15 @@ function showStatus(progress, stageName) {
365
417
  })
366
418
  }
367
419
 
368
- function resetStage(pm, progress, stageName, cwd) {
369
- const defSteps = getStageSteps(stageName, cwd)
420
+ async function resetStage(pm, progress, stageName, cwd) {
421
+ const defSteps = await getStageSteps(stageName, cwd, progress)
370
422
  progress.stages[stageName] = {
371
423
  status: 'in-progress',
372
- startedAt: new Date().toISOString(),
424
+ startedAt: new Date().toLocaleString('zh-CN',{hour12:false}),
373
425
  completedAt: null,
374
426
  steps: defSteps ? defSteps.map(s => ({ name: s.name, status: 'pending' })) : []
375
427
  }
376
- progress.lastActive = new Date().toISOString()
428
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
377
429
  pm._write(cwd, progress)
378
430
  console.log(`🔄 ${stageName} 阶段已重置`)
379
431
  }
@@ -107,7 +107,7 @@ Wave 3(依赖 Wave 2):Task 4`,
107
107
  prompt: `保存计划文件,更新进度。
108
108
 
109
109
  ### 操作
110
- 1. 保存到 \`.sillyspec/plans/YYYY-MM-DD-<change-name>.md\`
110
+ 1. 保存到 \`.sillyspec/changes/<变更名>/plan.md\`
111
111
 
112
112
  ### 输出
113
113
  计划文件路径 + 下一步命令`,