sillyspec 3.7.24 → 3.7.26

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.24",
3
+ "version": "3.7.26",
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} 未初始化`)
@@ -177,7 +221,7 @@ function runStage(pm, progress, stageName, cwd) {
177
221
  }
178
222
 
179
223
  const stageDef = stageRegistry[stageName]
180
- const defSteps = getStageSteps(stageName, cwd)
224
+ const defSteps = await getStageSteps(stageName, cwd, progress)
181
225
  if (defSteps && defSteps[currentIdx]) {
182
226
  outputStep(stageName, currentIdx, defSteps, cwd)
183
227
  }
@@ -215,7 +259,7 @@ function validateMetadata(cwd, stageName) {
215
259
  }
216
260
  }
217
261
 
218
- function completeStep(pm, progress, stageName, cwd, outputText) {
262
+ async function completeStep(pm, progress, stageName, cwd, outputText) {
219
263
  const stageData = progress.stages[stageName]
220
264
  if (!stageData || !stageData.steps) {
221
265
  console.error(`❌ 阶段 ${stageName} 未初始化`)
@@ -232,7 +276,7 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
232
276
 
233
277
  // 标记完成
234
278
  steps[currentIdx].status = 'completed'
235
- steps[currentIdx].completedAt = new Date().toISOString()
279
+ steps[currentIdx].completedAt = new Date().toLocaleString('zh-CN',{hour12:false})
236
280
  if (outputText) {
237
281
  const MAX_OUTPUT = 200
238
282
  if (outputText.length > MAX_OUTPUT) {
@@ -240,7 +284,7 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
240
284
  // Save full output to artifacts/
241
285
  const artifactsDir = join(cwd, '.sillyspec', '.runtime', 'artifacts')
242
286
  mkdirSync(artifactsDir, { recursive: true })
243
- const ts = new Date().toISOString().replace(/[:.]/g, '-')
287
+ const ts = new Date().toLocaleString('zh-CN',{hour12:false}).replace(/[:.]/g, '-')
244
288
  writeFileSync(join(artifactsDir, `${stageName}-step${currentIdx + 1}-${ts}.txt`), outputText)
245
289
  } else {
246
290
  steps[currentIdx].output = outputText
@@ -253,7 +297,7 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
253
297
  if (nextPendingIdx === -1) {
254
298
  // 全部完成
255
299
  stageData.status = 'completed'
256
- stageData.completedAt = new Date().toISOString()
300
+ stageData.completedAt = new Date().toLocaleString('zh-CN',{hour12:false})
257
301
 
258
302
  const next = getNextStage(stageName)
259
303
  if (next) {
@@ -261,17 +305,17 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
261
305
  if (!progress.stages[next]) progress.stages[next] = { status: 'pending', steps: [], startedAt: null, completedAt: null }
262
306
  if (progress.stages[next].status === 'pending' || !progress.stages[next].status) {
263
307
  progress.stages[next].status = 'in-progress'
264
- progress.stages[next].startedAt = new Date().toISOString()
308
+ progress.stages[next].startedAt = new Date().toLocaleString('zh-CN',{hour12:false})
265
309
  }
266
310
  }
267
311
 
268
- progress.lastActive = new Date().toISOString()
312
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
269
313
  pm._write(cwd, progress)
270
314
 
271
315
  // Append to user-inputs.md
272
316
  if (outputText) {
273
317
  const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
274
- const entry = `\n## ${new Date().toISOString()} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
318
+ const entry = `\n## ${new Date().toLocaleString('zh-CN',{hour12:false})} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
275
319
  appendFileSync(inputsPath, entry)
276
320
  }
277
321
 
@@ -287,22 +331,22 @@ function completeStep(pm, progress, stageName, cwd, outputText) {
287
331
  return
288
332
  }
289
333
 
290
- progress.lastActive = new Date().toISOString()
334
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
291
335
  pm._write(cwd, progress)
292
336
 
293
337
  // Append to user-inputs.md
294
338
  if (outputText) {
295
339
  const inputsPath = join(cwd, '.sillyspec', '.runtime', 'user-inputs.md')
296
- const entry = `\n## ${new Date().toISOString()} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
340
+ const entry = `\n## ${new Date().toLocaleString('zh-CN',{hour12:false})} | ${stageName}: ${steps[currentIdx].name}\n- 输出:${outputText}\n`
297
341
  appendFileSync(inputsPath, entry)
298
342
  }
299
343
 
300
- const defSteps = getStageSteps(stageName, cwd)
344
+ const defSteps = await getStageSteps(stageName, cwd, progress)
301
345
  console.log(`✅ Step ${currentIdx + 1}/${steps.length} 完成:${steps[currentIdx].name}\n`)
302
346
  outputStep(stageName, nextPendingIdx, defSteps, cwd)
303
347
  }
304
348
 
305
- function skipStep(pm, progress, stageName, cwd) {
349
+ async function skipStep(pm, progress, stageName, cwd) {
306
350
  const stageData = progress.stages[stageName]
307
351
  if (!stageData || !stageData.steps) {
308
352
  console.error(`❌ 阶段 ${stageName} 未初始化`)
@@ -317,7 +361,7 @@ function skipStep(pm, progress, stageName, cwd) {
317
361
  process.exit(1)
318
362
  }
319
363
 
320
- const defSteps = getStageSteps(stageName, cwd)
364
+ const defSteps = await getStageSteps(stageName, cwd, progress)
321
365
  const stepDef = defSteps ? defSteps[currentIdx] : null
322
366
  if (stepDef && !stepDef.optional) {
323
367
  console.error(`❌ 步骤 "${steps[currentIdx].name}" 不可跳过`)
@@ -325,8 +369,8 @@ function skipStep(pm, progress, stageName, cwd) {
325
369
  }
326
370
 
327
371
  steps[currentIdx].status = 'skipped'
328
- steps[currentIdx].skippedAt = new Date().toISOString()
329
- progress.lastActive = new Date().toISOString()
372
+ steps[currentIdx].skippedAt = new Date().toLocaleString('zh-CN',{hour12:false})
373
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
330
374
  pm._write(cwd, progress)
331
375
 
332
376
  console.log(`⏭️ Step ${currentIdx + 1}/${steps.length} 已跳过:${steps[currentIdx].name}`)
@@ -365,15 +409,15 @@ function showStatus(progress, stageName) {
365
409
  })
366
410
  }
367
411
 
368
- function resetStage(pm, progress, stageName, cwd) {
369
- const defSteps = getStageSteps(stageName, cwd)
412
+ async function resetStage(pm, progress, stageName, cwd) {
413
+ const defSteps = await getStageSteps(stageName, cwd, progress)
370
414
  progress.stages[stageName] = {
371
415
  status: 'in-progress',
372
- startedAt: new Date().toISOString(),
416
+ startedAt: new Date().toLocaleString('zh-CN',{hour12:false}),
373
417
  completedAt: null,
374
418
  steps: defSteps ? defSteps.map(s => ({ name: s.name, status: 'pending' })) : []
375
419
  }
376
- progress.lastActive = new Date().toISOString()
420
+ progress.lastActive = new Date().toLocaleString('zh-CN',{hour12:false})
377
421
  pm._write(cwd, progress)
378
422
  console.log(`🔄 ${stageName} 阶段已重置`)
379
423
  }
@@ -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
  计划文件路径 + 下一步命令`,