sillyspec 3.17.13 → 3.17.14

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.17.13",
3
+ "version": "3.17.14",
4
4
  "description": "SillySpec CLI — 流程状态机,让 AI 严格按步骤来",
5
5
  "icon": "logo.jpg",
6
6
  "homepage": "https://sillyspec.ppdmq.top/",
package/src/run.js CHANGED
@@ -1073,9 +1073,7 @@ export async function runCommand(args, cwd, specDir = null) {
1073
1073
  }
1074
1074
 
1075
1075
  const isAuxiliary = auxiliaryStages.includes(stageName)
1076
- // scan 元数据追踪(供 postcheck 使用)
1077
- let _scanProjectListParsed = undefined // undefined=未到达step2, true=解析成功, false=解析失败
1078
- let _scanManifestWritten = undefined // undefined=未尝试, true=成功, false=失败
1076
+ // scan 元数据追踪(存储在 stageData.scanMeta 中,completeStep 通过 progress 访问)
1079
1077
 
1080
1078
  const pm = new ProgressManager({ specDir: specRoot })
1081
1079
  let progress = await pm.read(cwd, changeName)
@@ -1725,37 +1723,42 @@ async function completeStep(pm, progress, stageName, cwd, outputText, inputText
1725
1723
  if (stageName === 'scan' && steps[currentIdx]?.name === '构建扫描项目列表') {
1726
1724
  // 解析项目列表:从 step 2 输出提取,或回退读取 projects/*.yaml
1727
1725
  let projectNames = []
1726
+ // 项目名清洗:只保留 ASCII 字母/数字/横线/下划线/点,过滤中文和特殊字符
1727
+ const sanitizeProjectName = (name) => {
1728
+ const clean = name.replace(/[^a-zA-Z0-9_\-.]/g, '').trim()
1729
+ return clean || null
1730
+ }
1728
1731
  if (outputText) {
1729
1732
  // 匹配方式 1: "1. project-name" 编号列表
1730
1733
  const numbered = outputText.match(/^\s*\d+\.\s+(\S+)/gm)
1731
1734
  if (numbered) {
1732
- projectNames = numbered.map(m => m.replace(/^\s*\d+\.\s+/, '').replace(/[—\-:].*$/, '').trim())
1733
- if (projectNames.length > 0) { _scanProjectListParsed = true; stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = true; }
1735
+ const raw = numbered.map(m => m.replace(/^\s*\d+\.\s+/, '').replace(/[—\-:].*$/, '').trim())
1736
+ projectNames = raw.map(sanitizeProjectName).filter(Boolean)
1737
+ if (projectNames.length > 0) { stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = true; }
1734
1738
  }
1735
1739
  // 匹配方式 2: 括号枚举 "子项目frontend/order-service/user-service" 或 "项目: a, b, c"
1736
1740
  if (projectNames.length === 0) {
1737
1741
  const parenMatch = outputText.match(/(?:子项目|项目)[\s::]*(\S+(?:[\/、,,]+\S+)*)/)
1738
1742
  if (parenMatch) {
1739
- projectNames = parenMatch[1]
1740
- .split(/[\/、,,]+/)
1741
- .map(s => s.trim())
1742
- .filter(Boolean)
1743
- if (projectNames.length > 0) { _scanProjectListParsed = true; stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = true; }
1743
+ const raw = parenMatch[1].split(/[\/、,,]+/).map(s => s.trim()).filter(Boolean)
1744
+ projectNames = raw.map(sanitizeProjectName).filter(Boolean)
1745
+ if (projectNames.length > 0) { stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = true; }
1744
1746
  }
1745
1747
  }
1746
1748
  // 匹配方式 3: 结构化 YAML block "scan_projects:\n - id: name"
1747
1749
  if (projectNames.length === 0) {
1748
1750
  const yamlMatch = outputText.match(/scan_projects:\s*\n((?:\s+-\s+id:\s+\S+\s*\n?)+)/)
1749
1751
  if (yamlMatch) {
1750
- projectNames = [...yamlMatch[1].matchAll(/-\s+id:\s*(\S+)/g)].map(m => m[1])
1751
- if (projectNames.length > 0) { _scanProjectListParsed = true; stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = true; }
1752
+ const raw = [...yamlMatch[1].matchAll(/-\s+id:\s*(\S+)/g)].map(m => m[1])
1753
+ projectNames = raw.map(sanitizeProjectName).filter(Boolean)
1754
+ if (projectNames.length > 0) { stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = true; }
1752
1755
  }
1753
1756
  }
1754
1757
  }
1755
1758
  if (projectNames.length === 0) {
1756
1759
  // 回退:读取所有已注册项目
1757
1760
  console.warn('⚠️ 未能从 step 2 输出解析项目列表,回退扫描所有注册项目')
1758
- _scanProjectListParsed = false; stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = false;
1761
+ stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.projectListParsed = false;
1759
1762
  const projectsDir = join(specBase, 'projects')
1760
1763
  if (existsSync(projectsDir)) {
1761
1764
  projectNames = readdirSync(projectsDir)
@@ -1887,7 +1890,7 @@ async function completeStep(pm, progress, stageName, cwd, outputText, inputText
1887
1890
  // 平台模式:scan 完成后生成 manifest.json + post-check
1888
1891
  if (stageName === 'scan' && (platformOpts.specRoot || platformOpts.runtimeRoot)) {
1889
1892
  try {
1890
- _scanManifestWritten = false; stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.manifestWritten = false; // 默认失败
1893
+ stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.manifestWritten = false; // 默认失败
1891
1894
  const { mkdirSync, writeFileSync } = await import('fs')
1892
1895
  const { join } = await import('path')
1893
1896
  const { execSync } = await import('child_process')
@@ -1908,7 +1911,7 @@ async function completeStep(pm, progress, stageName, cwd, outputText, inputText
1908
1911
  const manifestPath = join(manifestDir, 'manifest.json')
1909
1912
  writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n')
1910
1913
  console.log(`📄 manifest.json 已写入: ${manifestPath}`)
1911
- _scanManifestWritten = true; stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.manifestWritten = true;
1914
+ stageData.scanMeta = stageData.scanMeta || {}; stageData.scanMeta.manifestWritten = true;
1912
1915
  if (!sourceCommit) {
1913
1916
  console.log(`⚠️ source_commit 无法获取(可能非 git 目录),已设为 null`)
1914
1917
  }
@@ -462,6 +462,11 @@ step1 → step2 → step3
462
462
  9. 检查 7 份文档 header 是否包含 author 和 created_at
463
463
  10. 检查 local.yaml 中 commands 是否在 package.json scripts 中真实存在,不存在的必须标记 unavailable
464
464
 
465
+ ### ⛔ API 错误处理
466
+ - 遇到 API Error 529(服务过载)或 rate_limit 时,**停止当前操作并报告**,不要自动重试
467
+ - 遇到 tool_use_error 时,记录错误信息并跳过该文件/操作,继续处理下一项
468
+ - 如果连续 3 次操作失败,输出失败摘要并停止
469
+
465
470
  ### ⛔ 最终状态判定
466
471
  如果出现以下**任意**情况,最终状态**不能**写"全部通过",只能写 \`completed_with_warnings\` 或 \`failed_post_check\`:
467
472
  - 源码目录下存在 docs(路径合规检查失败)