sillyspec 3.20.3 → 3.20.5
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-archive/SKILL.md +21 -17
- package/.claude/skills/sillyspec-auto/SKILL.md +83 -77
- package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -17
- package/.claude/skills/sillyspec-commit/SKILL.md +106 -105
- package/.claude/skills/sillyspec-continue/SKILL.md +45 -44
- package/.claude/skills/sillyspec-doctor/SKILL.md +31 -22
- package/.claude/skills/sillyspec-execute/SKILL.md +30 -17
- package/.claude/skills/sillyspec-explore/SKILL.md +109 -96
- package/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
- package/.claude/skills/sillyspec-plan/SKILL.md +21 -52
- package/.claude/skills/sillyspec-propose/SKILL.md +21 -17
- package/.claude/skills/sillyspec-quick/SKILL.md +21 -17
- package/.claude/skills/sillyspec-resume/SKILL.md +68 -111
- package/.claude/skills/sillyspec-scan/SKILL.md +21 -17
- package/.claude/skills/sillyspec-state/SKILL.md +54 -54
- package/.claude/skills/sillyspec-status/SKILL.md +21 -17
- package/.claude/skills/sillyspec-verify/SKILL.md +21 -17
- package/.claude/skills/sillyspec-workspace/SKILL.md +157 -149
- package/.husky/pre-push +13 -0
- package/CLAUDE.md +18 -0
- package/README.md +198 -186
- package/SKILL.md +90 -87
- package/bin/sillyspec.js +2 -2
- package/docs/brainstorm-plan-contract.md +64 -0
- package/docs/plan-execute-contract.md +123 -0
- package/docs/platform-scan-protocol.md +298 -0
- package/docs/revision-mode.md +115 -0
- package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -0
- package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -0
- package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -0
- package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -0
- package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -0
- package/docs/sillyspec/file-lifecycle.md +131 -0
- package/docs/workflow-contract-regression.md +106 -0
- package/docs/worktree-isolation.md +252 -0
- package/package.json +40 -34
- package/packages/dashboard/dist/assets/{index-D1EVTLmc.js → index-Bq_Z2hne.js} +7446 -7446
- package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
- package/packages/dashboard/dist/index.html +16 -16
- package/packages/dashboard/dist/prototype-dashboard.html +836 -0
- package/packages/dashboard/dist/prototype-overview.html +256 -0
- package/packages/dashboard/index.html +15 -15
- package/packages/dashboard/package-lock.json +2384 -2384
- package/packages/dashboard/package.json +25 -25
- package/packages/dashboard/public/prototype-dashboard.html +836 -0
- package/packages/dashboard/public/prototype-overview.html +256 -0
- package/packages/dashboard/server/executor.js +86 -86
- package/packages/dashboard/server/index.js +588 -508
- package/packages/dashboard/server/parser.js +526 -458
- package/packages/dashboard/server/watcher.js +350 -342
- package/packages/dashboard/src/App.vue +558 -325
- package/packages/dashboard/src/components/ActionBar.vue +93 -84
- package/packages/dashboard/src/components/CommandPalette.vue +96 -92
- package/packages/dashboard/src/components/DetailPanel.vue +137 -137
- 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/LogStream.vue +65 -65
- package/packages/dashboard/src/components/PipelineStage.vue +95 -75
- package/packages/dashboard/src/components/PipelineView.vue +156 -129
- package/packages/dashboard/src/components/ProjectCard.vue +187 -0
- package/packages/dashboard/src/components/ProjectList.vue +210 -210
- package/packages/dashboard/src/components/ProjectOverview.vue +113 -139
- package/packages/dashboard/src/components/StageBadge.vue +67 -53
- package/packages/dashboard/src/components/StepCard.vue +94 -89
- package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
- package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
- package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
- package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
- package/packages/dashboard/src/composables/useDashboard.js +192 -164
- package/packages/dashboard/src/composables/useKeyboard.js +119 -119
- package/packages/dashboard/src/composables/useLayout.js +131 -0
- package/packages/dashboard/src/composables/useWebSocket.js +129 -129
- package/packages/dashboard/src/main.js +8 -8
- package/packages/dashboard/src/style.css +132 -132
- package/packages/dashboard/vite.config.js +18 -18
- package/src/brainstorm-postcheck.js +158 -0
- package/src/change-list.js +52 -0
- package/src/change-risk-profile.js +352 -0
- package/src/classify-change.js +73 -0
- package/src/constants.js +70 -0
- package/src/contract-matrix.js +278 -0
- package/src/db.js +201 -0
- package/src/endpoint-extractor.js +315 -0
- package/src/hooks/claude-pre-tool-use.cjs +125 -0
- package/src/hooks/worktree-guard.js +761 -0
- package/src/index.js +922 -252
- package/src/init.js +470 -375
- package/src/knowledge-match.js +130 -0
- package/src/migrate.js +117 -117
- package/src/modules.js +482 -0
- package/src/progress.js +1734 -508
- package/src/run.js +3465 -652
- package/src/scan-postcheck.js +387 -0
- package/src/setup.js +398 -460
- package/src/stage-contract.js +700 -0
- package/src/stages/archive.js +160 -54
- package/src/stages/brainstorm-auto.js +229 -0
- package/src/stages/brainstorm.js +645 -239
- package/src/stages/doctor.js +365 -312
- package/src/stages/execute.js +634 -264
- package/src/stages/explore.js +34 -0
- package/src/stages/index.js +29 -35
- package/src/stages/knowledge.js +498 -0
- package/src/stages/plan-postcheck.js +518 -0
- package/src/stages/plan.js +582 -279
- package/src/stages/propose.js +174 -115
- package/src/stages/quick.js +102 -63
- package/src/stages/scan.js +558 -141
- package/src/stages/status.js +65 -65
- package/src/stages/verify.js +322 -135
- package/src/sync.js +497 -0
- package/src/task-review.js +346 -0
- package/src/workflow.js +785 -0
- package/src/worktree-apply.js +549 -0
- package/src/worktree-deps.js +185 -0
- package/src/worktree.js +982 -0
- package/templates/workflows/archive-impact.yaml +79 -0
- package/templates/workflows/scan-docs.yaml +132 -0
- package/test/brainstorm-plan-contract.test.mjs +273 -0
- package/test/check-syntax.mjs +26 -0
- package/test/cli-top-level-aliases.test.mjs +174 -0
- package/test/contract-artifacts.test.mjs +323 -0
- package/test/decision-supersede.test.mjs +277 -0
- package/test/knowledge-match.test.mjs +231 -0
- package/test/plan-execute-contract.test.mjs +357 -0
- package/test/plan-optimization.test.mjs +572 -0
- package/test/platform-artifacts.test.mjs +190 -0
- package/test/platform-failure-samples.test.mjs +199 -0
- package/test/platform-recovery-chain.test.mjs +179 -0
- package/test/platform-recovery.test.mjs +167 -0
- package/test/platform-scan-p0.test.mjs +175 -0
- package/test/revision-v1.test.mjs +1145 -0
- package/test/run-sanitize-project-name.test.mjs +51 -0
- package/test/run-scan-postcheck-fail.test.mjs +64 -0
- package/test/run-scan-project-parse.test.mjs +200 -0
- package/test/run-tests.mjs +48 -0
- package/test/runtime-cleanup-keeps-worktree.test.mjs +107 -0
- package/test/scan-docs-yaml-placeholders.test.mjs +84 -0
- package/test/scan-knowledge.test.mjs +175 -0
- package/test/scan-paths.test.mjs +68 -0
- package/test/scan-postcheck-project-priority.test.mjs +85 -0
- package/test/scan-postcheck.test.mjs +197 -0
- package/test/scan-workflow-anyfailed-block.test.mjs +52 -0
- package/test/spec-dir.test.mjs +206 -0
- package/test/stage-contract-failed-post-check.test.mjs +102 -0
- package/test/stage-contract.test.mjs +299 -0
- package/test/stage-definitions.test.mjs +39 -0
- package/test/wait-gates.test.mjs +501 -0
- package/test/workflow-spec-base.test.mjs +142 -0
- package/test/worktree-deps-provision.test.mjs +148 -0
- package/test/worktree-guard.test.mjs +136 -0
- package/test/worktree-native-overlay.test.mjs +188 -0
- 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/dist/steps/brainstorm/01-load-context.md +0 -30
- package/dist/steps/brainstorm/02-reuse-check.md +0 -6
- package/dist/steps/brainstorm/03-prototype-analysis.md +0 -11
- package/dist/steps/brainstorm/04-module-split.md +0 -23
- package/dist/steps/brainstorm/05-dialog-explore.md +0 -8
- package/dist/steps/brainstorm/06-propose-approaches.md +0 -3
- package/dist/steps/brainstorm/07-present-design.md +0 -3
- package/dist/steps/brainstorm/08-write-design.md +0 -21
- package/dist/steps/brainstorm/09-self-review.md +0 -15
- package/dist/steps/brainstorm/10-user-confirm.md +0 -3
- package/dist/steps/brainstorm/11-output-spec.md +0 -7
- package/dist/steps/brainstorm/manifest.yaml +0 -26
- package/dist/steps/execute/01-load-context.md +0 -41
- package/dist/steps/execute/02-scan-conventions.md +0 -47
- package/dist/steps/execute/03-skill-mcp.md +0 -19
- package/dist/steps/execute/04-assign-task.md +0 -22
- package/dist/steps/execute/04b-prompt-template.md +0 -54
- package/dist/steps/execute/05-write-test.md +0 -7
- package/dist/steps/execute/06-write-code.md +0 -8
- package/dist/steps/execute/07-run-test.md +0 -26
- package/dist/steps/execute/08-fix-issues.md +0 -28
- package/dist/steps/execute/09-next-task.md +0 -33
- package/dist/steps/execute/manifest.yaml +0 -28
- package/dist/steps/plan/01-load-context.md +0 -22
- package/dist/steps/plan/02-anchor-confirm.md +0 -1
- package/dist/steps/plan/03-expand-tasks.md +0 -33
- package/dist/steps/plan/04-mark-order.md +0 -15
- package/dist/steps/plan/05-e2e-planning.md +0 -17
- package/dist/steps/plan/06-self-check.md +0 -16
- package/dist/steps/plan/07-save.md +0 -1
- package/dist/steps/plan/manifest.yaml +0 -18
- package/dist/steps/scan/01-env-detect.md +0 -51
- package/dist/steps/scan/02-tech-stack.md +0 -16
- package/dist/steps/scan/03-conventions.md +0 -16
- package/dist/steps/scan/04-structure.md +0 -19
- package/dist/steps/scan/05-quality.md +0 -18
- package/dist/steps/scan/06-complete.md +0 -49
- package/dist/steps/scan/manifest.yaml +0 -16
- package/dist/steps/verify/01-load-specs.md +0 -28
- package/dist/steps/verify/02-check-tasks.md +0 -1
- package/dist/steps/verify/03-check-design.md +0 -6
- package/dist/steps/verify/04-run-tests.md +0 -7
- package/dist/steps/verify/05-e2e-tests.md +0 -27
- package/dist/steps/verify/05b-e2e-fix.md +0 -33
- package/dist/steps/verify/06-code-quality.md +0 -25
- package/dist/steps/verify/07-lint-check.md +0 -27
- package/dist/steps/verify/08-output-report.md +0 -14
- package/dist/steps/verify/manifest.yaml +0 -22
- package/packages/dashboard/dist/assets/index-DGe8CqeP.css +0 -1
- package/src/derive.js +0 -147
- package/src/step.js +0 -543
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
**当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
|
|
2
|
-
|
|
3
|
-
你现在是 SillySpec 代码库扫描器(编排器)。**你不读源码,只编排子代理或串行执行。**
|
|
4
|
-
|
|
5
|
-
## 绝对规则
|
|
6
|
-
- ❌ 修改代码 / 编造路径 / 主代理读源码全文
|
|
7
|
-
- ✅ 交互模式每步等用户 / 文档只写 `.sillyspec/docs/<project>/scan/`
|
|
8
|
-
|
|
9
|
-
## 参数处理
|
|
10
|
-
- 空白 → 交互式引导(逐步询问)
|
|
11
|
-
- `--deep` → 直接深度扫描
|
|
12
|
-
- 其他 → 快速扫描该区域
|
|
13
|
-
|
|
14
|
-
## 交互式引导(参数为空时)
|
|
15
|
-
|
|
16
|
-
### 检查工作区 & 已有文档
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
ls .sillyspec/projects/*.yaml 2>/dev/null | grep -q . # 有子项目配置 → 工作区模式
|
|
20
|
-
ls .sillyspec/docs/<project>/scan/ 2>/dev/null # 检查已有文档
|
|
21
|
-
wc -l .sillyspec/docs/<project>/scan/*.md 2>/dev/null
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
- 已有 3 份 → 建议升级深度扫描
|
|
25
|
-
- 已有 7 份 → 建议刷新或跳过
|
|
26
|
-
- 工作区 → 逐个扫描 / 选子项目 / 退出
|
|
27
|
-
|
|
28
|
-
### 选择扫描模式、范围、排除目录、确认
|
|
29
|
-
按原流程交互,确认后进入扫描。
|
|
30
|
-
|
|
31
|
-
## 构建环境探测(主代理执行)
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
cat package.json pom.xml build.gradle go.mod Cargo.toml requirements.txt pyproject.toml Gemfile composer.json 2>/dev/null
|
|
35
|
-
find . -maxdepth 2 -name "*.config.*" -not -path "*/node_modules/*" -not -path "*/.git/*" | head -20 | xargs cat 2>/dev/null
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
结果保存到 `.sillyspec/docs/<project>/scan/_env-detect.md`(临时文件,扫描完删除)。
|
|
39
|
-
|
|
40
|
-
## 断点续扫
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
for f in ARCHITECTURE STRUCTURE CONVENTIONS INTEGRATIONS TESTING CONCERNS PROJECT; do
|
|
44
|
-
[ -f ".sillyspec/docs/<project>/scan/${f}.md" ] && echo "✅ ${f}.md" || echo "⬜ ${f}.md"
|
|
45
|
-
done
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
只生成缺失的文档。
|
|
49
|
-
|
|
50
|
-
## 子代理可用性检测
|
|
51
|
-
检查是否有 Task/Spawn 工具。有 → 子代理模式,无 → 串行模式。
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
扫描技术栈 + 数据库 Schema + 架构模式,生成 `ARCHITECTURE.md`。
|
|
2
|
-
|
|
3
|
-
参考 `.sillyspec/docs/<project>/scan/_env-detect.md`。
|
|
4
|
-
|
|
5
|
-
用 grep/rg 搜索(`@Entity`、`schema.prisma`、`models.py` 等),**禁止读源码全文**。
|
|
6
|
-
|
|
7
|
-
Schema 只记表名+说明+字段数。
|
|
8
|
-
|
|
9
|
-
输出文件必须包含以下章节:
|
|
10
|
-
- `## 技术栈`
|
|
11
|
-
- `## 架构概览`
|
|
12
|
-
- `## 数据模型(摘要)`
|
|
13
|
-
|
|
14
|
-
路径用反引号,不编造。保存到 `.sillyspec/docs/<project>/scan/ARCHITECTURE.md`。
|
|
15
|
-
|
|
16
|
-
完成后立即写文件,下一个 area 开始前清除源码上下文。
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
扫描框架隐形规则 + 实体继承 + 代码风格,生成 `CONVENTIONS.md`。
|
|
2
|
-
|
|
3
|
-
参考 `.sillyspec/docs/<project>/scan/_env-detect.md`。
|
|
4
|
-
|
|
5
|
-
用 grep 搜索拦截器/插件/逻辑删除/基类/审计字段,**禁止读源码全文**。
|
|
6
|
-
|
|
7
|
-
根据检测到的语言/框架自行决定搜索什么模式,提取 3-5 个典型示例。
|
|
8
|
-
|
|
9
|
-
输出文件必须包含以下章节:
|
|
10
|
-
- `## 框架隐形规则`
|
|
11
|
-
- `## 实体继承规范`
|
|
12
|
-
- `## 代码风格`
|
|
13
|
-
|
|
14
|
-
路径用反引号,不编造。保存到 `.sillyspec/docs/<project>/scan/CONVENTIONS.md`。
|
|
15
|
-
|
|
16
|
-
完成后立即写文件,下一个 area 开始前清除源码上下文。
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
扫描目录结构 + 外部集成,生成 `STRUCTURE.md` 和 `INTEGRATIONS.md`。
|
|
2
|
-
|
|
3
|
-
参考 `.sillyspec/docs/<project>/scan/_env-detect.md`。
|
|
4
|
-
|
|
5
|
-
用 find/ls/tree 和 grep,**禁止读源码全文**。
|
|
6
|
-
|
|
7
|
-
搜索 API 调用、MQ 配置、缓存、第三方 SDK。
|
|
8
|
-
|
|
9
|
-
**STRUCTURE.md** 必须包含:
|
|
10
|
-
- 目录树(用 tree 或 find 生成)
|
|
11
|
-
- 模块说明
|
|
12
|
-
|
|
13
|
-
**INTEGRATIONS.md** 按类型分组(数据库、缓存、MQ、第三方 API 等)。
|
|
14
|
-
|
|
15
|
-
路径用反引号,不编造。分别保存到:
|
|
16
|
-
- `.sillyspec/docs/<project>/scan/STRUCTURE.md`
|
|
17
|
-
- `.sillyspec/docs/<project>/scan/INTEGRATIONS.md`
|
|
18
|
-
|
|
19
|
-
完成后立即写文件,下一个 area 开始前清除源码上下文。
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
扫描测试现状 + 技术债务 + 项目概览,生成 `TESTING.md`、`CONCERNS.md`、`PROJECT.md`。
|
|
2
|
-
|
|
3
|
-
参考 `.sillyspec/docs/<project>/scan/_env-detect.md`。
|
|
4
|
-
|
|
5
|
-
用 grep 搜索测试文件、TODO/FIXME、过时依赖,**禁止读源码全文**。
|
|
6
|
-
|
|
7
|
-
**TESTING.md** 必须包含测试结构(框架、目录、覆盖情况)。
|
|
8
|
-
|
|
9
|
-
**CONCERNS.md** 按严重程度分组(🔴 必须 / 🟡 建议 / 🔵 优化)。
|
|
10
|
-
|
|
11
|
-
**PROJECT.md** 包含项目基本信息。
|
|
12
|
-
|
|
13
|
-
路径用反引号,不编造。分别保存到:
|
|
14
|
-
- `.sillyspec/docs/<project>/scan/TESTING.md`
|
|
15
|
-
- `.sillyspec/docs/<project>/scan/CONCERNS.md`
|
|
16
|
-
- `.sillyspec/docs/<project>/scan/PROJECT.md`
|
|
17
|
-
|
|
18
|
-
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
## 扫描完成
|
|
2
|
-
|
|
3
|
-
```bash
|
|
4
|
-
# 路径校验
|
|
5
|
-
for f in ARCHITECTURE STRUCTURE CONVENTIONS INTEGRATIONS TESTING CONCERNS PROJECT; do
|
|
6
|
-
[ -f ".sillyspec/docs/<project>/scan/${f}.md" ] && echo "✅ ${f}.md"
|
|
7
|
-
done
|
|
8
|
-
|
|
9
|
-
# 生成知识库骨架
|
|
10
|
-
mkdir -p .sillyspec/knowledge
|
|
11
|
-
if [ ! -f ".sillyspec/knowledge/INDEX.md" ]; then
|
|
12
|
-
cat > .sillyspec/knowledge/INDEX.md << 'EOF'
|
|
13
|
-
# Knowledge Index
|
|
14
|
-
|
|
15
|
-
> 子代理任务开始前查询此文件,按关键词匹配,只读命中的知识文件。
|
|
16
|
-
> execute/quick 执行中发现的坑自动追加到 uncategorized.md,经用户确认后归类到对应文件。
|
|
17
|
-
|
|
18
|
-
<!-- 格式:关键词1|关键词2|关键词3 → 文件路径 -->
|
|
19
|
-
EOF
|
|
20
|
-
fi
|
|
21
|
-
if [ ! -f ".sillyspec/knowledge/uncategorized.md" ]; then
|
|
22
|
-
cat > .sillyspec/knowledge/uncategorized.md << 'EOF'
|
|
23
|
-
# 未分类知识
|
|
24
|
-
|
|
25
|
-
> execute/quick 执行中发现的坑暂存于此,用户审阅后归类到对应文件并更新 INDEX.md。
|
|
26
|
-
EOF
|
|
27
|
-
fi
|
|
28
|
-
|
|
29
|
-
# 记录状态
|
|
30
|
-
cat > .sillyspec/STATE.md << 'EOF'
|
|
31
|
-
# 项目状态
|
|
32
|
-
|
|
33
|
-
## 最近活动
|
|
34
|
-
- $(date '+%Y-%m-%d %H:%M:%S') scan 完成
|
|
35
|
-
EOF
|
|
36
|
-
|
|
37
|
-
# 清理
|
|
38
|
-
rm -f .sillyspec/docs/<project>/scan/_env-detect.md
|
|
39
|
-
git add .sillyspec/
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 自检门控
|
|
43
|
-
- [ ] ARCHITECTURE.md:技术栈 + Schema 摘要?
|
|
44
|
-
- [ ] CONVENTIONS.md:隐形规则 + 代码风格?
|
|
45
|
-
- [ ] STRUCTURE.md:目录结构?
|
|
46
|
-
- [ ] INTEGRATIONS.md:外部依赖?
|
|
47
|
-
- [ ] TESTING.md:测试现状?
|
|
48
|
-
- [ ] CONCERNS.md:技术债务?
|
|
49
|
-
- [ ] PROJECT.md:项目概览?
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
phase: scan
|
|
2
|
-
description: 项目代码库扫描
|
|
3
|
-
requires: []
|
|
4
|
-
steps:
|
|
5
|
-
- file: 01-env-detect.md
|
|
6
|
-
name: 环境探测
|
|
7
|
-
- file: 02-tech-stack.md
|
|
8
|
-
name: 技术栈识别
|
|
9
|
-
- file: 03-conventions.md
|
|
10
|
-
name: 代码风格提取
|
|
11
|
-
- file: 04-structure.md
|
|
12
|
-
name: 项目结构分析
|
|
13
|
-
- file: 05-quality.md
|
|
14
|
-
name: 质量评估
|
|
15
|
-
- file: 06-complete.md
|
|
16
|
-
name: 扫描完成
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
**当需要用户从多个选项中做出选择时,必须使用 Claude Code 内置的 AskUserQuestion 工具,将选项以参数传入。**
|
|
2
|
-
|
|
3
|
-
## 核心约束(必须遵守)
|
|
4
|
-
- ❌ 修改任何代码(只做检查和报告)
|
|
5
|
-
- ❌ 自行推进到下一阶段
|
|
6
|
-
|
|
7
|
-
## 加载规范
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# 确定变更目录
|
|
11
|
-
if [ -n "$ARGUMENTS" ]; then
|
|
12
|
-
CHANGE_DIR=".sillyspec/changes/$ARGUMENTS"
|
|
13
|
-
else
|
|
14
|
-
CHANGE_DIR=$(ls -d .sillyspec/changes/*/ 2>/dev/null | grep -v archive | tail -1)
|
|
15
|
-
fi
|
|
16
|
-
cat "$CHANGE_DIR"/{design,tasks}.md 2>/dev/null
|
|
17
|
-
cat .sillyspec/local.yaml 2>/dev/null
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
锚定确认实际存在的文件。
|
|
21
|
-
|
|
22
|
-
## 工作区模式处理
|
|
23
|
-
|
|
24
|
-
如果 `.sillyspec/projects/` 目录下有 yaml 文件:
|
|
25
|
-
1. 检查工作区根目录 `.sillyspec/changes/` 下的未归档变更
|
|
26
|
-
2. 检查每个子项目 `<子项目路径>/.sillyspec/changes/` 下的未归档变更
|
|
27
|
-
3. 列出所有未归档变更,让用户选择要验证哪个
|
|
28
|
-
4. 根据 $ARGUMENTS 或用户选择,cd 到对应目录执行验证
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
逐项检查 tasks.md,对每个 checkbox 报告:✅ 已完成 / ❌ 未完成 / ⚠️ 部分完成
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
检测项目中是否有 E2E 测试或测试步骤文件:
|
|
2
|
-
```bash
|
|
3
|
-
ls tests/e2e/ e2e/ cypress/e2e/ 2>/dev/null | head -5
|
|
4
|
-
cat .sillyspec/changes/*/e2e-steps.md 2>/dev/null | head -5
|
|
5
|
-
```
|
|
6
|
-
|
|
7
|
-
**无任何测试** → 跳过此步骤。
|
|
8
|
-
|
|
9
|
-
**有测试** → 确认修复策略(AskUserQuestion):
|
|
10
|
-
1. 自动修复,同一用例最多 5 次(超过停止,提示人工介入)
|
|
11
|
-
2. 一直修复直到全绿
|
|
12
|
-
3. 只报告,不自动修复
|
|
13
|
-
|
|
14
|
-
**按优先级执行:**
|
|
15
|
-
|
|
16
|
-
**优先级 1:专业 E2E 框架(Playwright/Cypress)**
|
|
17
|
-
```bash
|
|
18
|
-
npx playwright test 2>/dev/null || npx cypress run 2>/dev/null
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**优先级 2:通用测试框架(jest/vitest)**
|
|
22
|
-
```bash
|
|
23
|
-
npx vitest run 2>/dev/null || npx jest 2>/dev/null
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
**优先级 3:浏览器 MCP + e2e-steps.md(兜底)**
|
|
27
|
-
读取 `.sillyspec/changes/<变更名>/e2e-steps.md`,按步骤逐条执行。每条标注 ✅/❌。
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
**自动修复循环(选了策略 1 或 2 时):**
|
|
2
|
-
|
|
3
|
-
```
|
|
4
|
-
ROUND = 1
|
|
5
|
-
MAX_ROUNDS = 策略1时为5,策略2时为50
|
|
6
|
-
|
|
7
|
-
while ROUND <= MAX_ROUNDS:
|
|
8
|
-
1. 运行失败测试,捕获完整输出
|
|
9
|
-
2. 全部通过 → 跳出循环,标记 ✅
|
|
10
|
-
3. 对每个失败测试:
|
|
11
|
-
a. fixAttempts >= MAX_ROUNDS → 跳过,标记 ❌ MAX_REACHED
|
|
12
|
-
b. 否则 → 调用修复工具,prompt 必须包含失败测试路径、测试名、完整错误信息、相关源文件路径
|
|
13
|
-
c. 修复后重跑确认
|
|
14
|
-
d. 通过 → fixAttempts 不变;仍失败 → fixAttempts + 1
|
|
15
|
-
4. 写入 .sillyspec/local.yaml
|
|
16
|
-
5. ROUND++
|
|
17
|
-
6. 本轮无任何修复 → 跳出循环
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
**禁止行为:**
|
|
21
|
-
- ❌ 只看错误摘要就修复(必须看完整输出)
|
|
22
|
-
- ❌ 跳过 fixAttempts 计数
|
|
23
|
-
- ❌ 一次修复多个不相关的失败(逐个修复,每次修复后重跑确认)
|
|
24
|
-
- ❌ 直接修改代码(verify 阶段禁止改代码)
|
|
25
|
-
|
|
26
|
-
**更新测试结果到 `.sillyspec/local.yaml`:**
|
|
27
|
-
```yaml
|
|
28
|
-
e2e:
|
|
29
|
-
{变更名}:
|
|
30
|
-
{测试文件名}:
|
|
31
|
-
status: passed/failed
|
|
32
|
-
fixAttempts: 0
|
|
33
|
-
```
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
代码质量扫描:
|
|
2
|
-
|
|
3
|
-
```bash
|
|
4
|
-
grep -r "TODO\|FIXME\|HACK\|XXX" src/ lib/ app/ --include="*.ts" --include="*.tsx" --include="*.py" --include="*.js" 2>/dev/null | head -20
|
|
5
|
-
```
|
|
6
|
-
|
|
7
|
-
审查 design.md「文件变更」中列出的文件:
|
|
8
|
-
- 安全问题(输入校验、SQL拼接、硬编码敏感信息)
|
|
9
|
-
- 潜在 bug(空值、边界条件)
|
|
10
|
-
- 与 `.sillyspec/docs/<project>/scan/CONVENTIONS.md` 一致性
|
|
11
|
-
|
|
12
|
-
每个问题标 🔴必须 / 🟡建议 / 🔵优化。
|
|
13
|
-
|
|
14
|
-
## MCP 基础设施验证
|
|
15
|
-
|
|
16
|
-
检查当前可用工具列表中是否存在以下 MCP 工具:
|
|
17
|
-
- 数据库相关工具(postgres/sqlite/mysql/redis)
|
|
18
|
-
- 浏览器相关工具(browser/chrome/puppeteer/playwright/devtools)
|
|
19
|
-
- 搜索相关工具(search/web_search)
|
|
20
|
-
|
|
21
|
-
**数据库 MCP:** 对照 design.md 验证表/集合、字段类型、约束。⚠️ 只执行 SELECT 查询。
|
|
22
|
-
|
|
23
|
-
**浏览器 MCP:** 验证页面加载、UI 元素、基础交互。
|
|
24
|
-
|
|
25
|
-
**无 MCP → 跳过此部分。**
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
Lint / Format 检查:
|
|
2
|
-
|
|
3
|
-
```bash
|
|
4
|
-
# ESLint
|
|
5
|
-
if [ -f .eslintrc -o -f .eslintrc.js -o -f .eslintrc.cjs -o -f .eslintrc.json -o -f .eslintrc.yml ] || grep -q '"eslint"' package.json 2>/dev/null; then
|
|
6
|
-
npx eslint . --max-warnings 0 2>&1 | tail -50
|
|
7
|
-
fi
|
|
8
|
-
|
|
9
|
-
# Prettier(检查而非修复)
|
|
10
|
-
if [ -f .prettierrc -o -f .prettierrc.js -o -f .prettierrc.json -o -f .prettierrc.yml ] || grep -q '"prettier"' package.json 2>/dev/null; then
|
|
11
|
-
npx prettier --check . 2>&1 | tail -30
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
# TypeScript 类型检查
|
|
15
|
-
if [ -f tsconfig.json ]; then
|
|
16
|
-
npx tsc --noEmit 2>&1 | tail -30
|
|
17
|
-
fi
|
|
18
|
-
|
|
19
|
-
# Stylelint
|
|
20
|
-
if [ -f .stylelintrc -o -f .stylelintrc.js -o -f .stylelintrc.json ] || grep -q '"stylelint"' package.json 2>/dev/null; then
|
|
21
|
-
npx stylelint "**/*.{css,scss,less}" 2>&1 | tail -30
|
|
22
|
-
fi
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**处理策略(AskUserQuestion):**
|
|
26
|
-
1. **自动修复** — 对支持 `--fix` 的工具自动修复后重跑,同一问题最多修复 3 次
|
|
27
|
-
2. **只报告** — 仅列出所有 lint 错误,不修改代码
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
输出验证报告:
|
|
2
|
-
|
|
3
|
-
```markdown
|
|
4
|
-
# SillySpec 验证报告
|
|
5
|
-
## 任务完成度:X/Y
|
|
6
|
-
## 设计一致性
|
|
7
|
-
## 测试结果:passed N, failed N
|
|
8
|
-
## 技术债务标记
|
|
9
|
-
## 代码审查:🔴 N / 🟡 N / 🔵 N
|
|
10
|
-
## Lint 检查:ESLint ✅/❌ | Prettier ✅/❌ | TypeScript ✅/❌ | Stylelint ✅/❌
|
|
11
|
-
## MCP 基础设施验证:数据库 ✅/❌/跳过 | 页面 ✅/❌/跳过 | API ✅/❌/跳过
|
|
12
|
-
## E2E 测试:passed N / failed N / fixAttempts 详情
|
|
13
|
-
## 结论:✅ PASS / ⚠️ PASS WITH NOTES / ❌ FAIL
|
|
14
|
-
```
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
phase: verify
|
|
2
|
-
description: 验证与检查
|
|
3
|
-
requires: [execute]
|
|
4
|
-
steps:
|
|
5
|
-
- file: 01-load-specs.md
|
|
6
|
-
name: 加载规范
|
|
7
|
-
- file: 02-check-tasks.md
|
|
8
|
-
name: 检查任务完成度
|
|
9
|
-
- file: 03-check-design.md
|
|
10
|
-
name: 对照设计文档
|
|
11
|
-
- file: 04-run-tests.md
|
|
12
|
-
name: 运行单元测试
|
|
13
|
-
- file: 05-e2e-tests.md
|
|
14
|
-
name: E2E 测试
|
|
15
|
-
- file: 05b-e2e-fix.md
|
|
16
|
-
name: E2E 自动修复
|
|
17
|
-
- file: 06-code-quality.md
|
|
18
|
-
name: 代码质量扫描
|
|
19
|
-
- file: 07-lint-check.md
|
|
20
|
-
name: Lint 检查
|
|
21
|
-
- file: 08-output-report.md
|
|
22
|
-
name: 输出验证报告
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.slide-enter-active[data-v-b5f771a9],.slide-leave-active[data-v-b5f771a9]{transition:all .2s ease}.slide-enter-from[data-v-b5f771a9],.slide-leave-to[data-v-b5f771a9]{opacity:0;max-height:0;overflow:hidden}.slide-enter-to[data-v-b5f771a9],.slide-leave-from[data-v-b5f771a9]{max-height:300px}.line-clamp-2[data-v-fac2f74c]{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.doc-preview[data-v-fc57afbe]{font-size:13px;line-height:1.7;color:#374151}.doc-preview[data-v-fc57afbe] h1{color:#d97706;font-size:18px;font-weight:700;margin:0 0 16px;border-bottom:1px solid #E5E5EA;padding-bottom:8px}.doc-preview[data-v-fc57afbe] h2{color:#1c1c1e;font-size:15px;font-weight:600;margin:20px 0 10px}.doc-preview[data-v-fc57afbe] h3{color:#d97706;font-size:14px;font-weight:600;margin:16px 0 8px}.doc-preview[data-v-fc57afbe] p{margin:8px 0}.doc-preview[data-v-fc57afbe] strong{color:#1c1c1e;font-weight:600}.doc-preview[data-v-fc57afbe] a{color:#d97706;text-decoration:none}.doc-preview[data-v-fc57afbe] a:hover{text-decoration:underline}.doc-preview[data-v-fc57afbe] code{background:#e5e5ea;color:#1c1c1e;padding:1px 5px;border-radius:3px;font-size:12px;font-family:JetBrains Mono,monospace}.doc-preview[data-v-fc57afbe] pre{background:#1c1c1e;color:#e5e5e7;border:1px solid #E5E5EA;border-radius:6px;padding:12px 16px;overflow-x:auto;font-size:12px;margin:8px 0;line-height:1.5}.doc-preview[data-v-fc57afbe] pre code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.doc-preview[data-v-fc57afbe] ul,.doc-preview[data-v-fc57afbe] ol{padding-left:20px;margin:8px 0;color:#374151}.doc-preview[data-v-fc57afbe] li{margin:4px 0}.doc-preview[data-v-fc57afbe] blockquote{border-left:3px solid #D97706;padding-left:12px;margin:12px 0;color:#636366;font-style:italic}.doc-preview[data-v-fc57afbe] table{width:100%;border-collapse:collapse;margin:12px 0;font-size:12px}.doc-preview[data-v-fc57afbe] thead th{background:#f0f0f3;color:#1c1c1e;font-weight:600;text-align:left;padding:8px 12px;border:1px solid #E5E5EA}.doc-preview[data-v-fc57afbe] tbody td{padding:6px 12px;border:1px solid #E5E5EA;color:#374151}.doc-preview[data-v-fc57afbe] tbody tr:hover{background:#f9fafb}.doc-preview[data-v-fc57afbe] hr{border:none;border-top:1px solid #E5E5EA;margin:16px 0}.project-overview[data-v-543019d4]{display:flex;align-items:center;background:#fff;border-bottom:1px solid #E5E5EA;padding:8px 16px;min-height:48px;gap:8px;flex-shrink:0;overflow-x:auto}.ov-section[data-v-543019d4]{display:flex;align-items:center;gap:6px;white-space:nowrap;flex-shrink:0}.ov-clickable[data-v-543019d4]{cursor:pointer;border-radius:4px;padding:2px 6px;margin:-2px -6px;transition:background .15s}.ov-clickable[data-v-543019d4]:hover{background:#d977060f}.ov-name[data-v-543019d4]{font-weight:600;font-size:13px;color:#1c1c1e}.ov-path[data-v-543019d4],.ov-label[data-v-543019d4]{font-size:11px;color:#636366}.ov-value[data-v-543019d4]{font-size:12px;color:#1c1c1e;font-weight:500}.ov-tag[data-v-543019d4]{font-size:10px;padding:1px 8px;border-radius:10px;background:#d9770614;color:#d97706;font-weight:500}.ov-divider[data-v-543019d4]{width:1px;height:20px;background:#e5e5ea;flex-shrink:0}.ov-git[data-v-543019d4]{margin-left:auto;gap:10px}.ov-branch[data-v-543019d4]{font-size:11px;color:#636366;background:#f5f5f7;padding:1px 8px;border-radius:4px}.ov-commit[data-v-543019d4]{font-size:11px;color:#636366;max-width:200px;overflow:hidden;text-overflow:ellipsis}.ov-dirty[data-v-543019d4]{font-size:11px;color:#d97706;font-weight:500}*{margin:0;padding:0;box-sizing:border-box}body{font-family:DM Sans,-apple-system,BlinkMacSystemFont,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}#app{width:100vw;height:100vh;overflow:hidden}/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--leading-relaxed:1.625;--radius-sm:.25rem;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--animate-pulse-dot:pulse-dot 1.5s ease-in-out infinite}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.inset-0{inset:calc(var(--spacing) * 0)}.inset-x-0{inset-inline:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.bottom-0{bottom:calc(var(--spacing) * 0)}.left-0{left:calc(var(--spacing) * 0)}.z-10{z-index:10}.z-20{z-index:20}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.-mt-0\.5{margin-top:calc(var(--spacing) * -.5)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-1\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.ml-auto{margin-left:auto}.line-clamp-2{-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.list-item{display:list-item}.table{display:table}.h-1{height:calc(var(--spacing) * 1)}.h-2{height:calc(var(--spacing) * 2)}.h-3{height:calc(var(--spacing) * 3)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-12{height:calc(var(--spacing) * 12)}.h-\[2px\]{height:2px}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-72{max-height:calc(var(--spacing) * 72)}.w-0{width:calc(var(--spacing) * 0)}.w-1{width:calc(var(--spacing) * 1)}.w-2{width:calc(var(--spacing) * 2)}.w-3{width:calc(var(--spacing) * 3)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-8{width:calc(var(--spacing) * 8)}.w-\[2px\]{width:2px}.w-\[200px\]{width:200px}.w-\[340px\]{width:340px}.w-px{width:1px}.w-screen{width:100vw}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-pulse-dot{animation:var(--animate-pulse-dot)}.cursor-col-resize{cursor:col-resize}.cursor-pointer{cursor:pointer}.resize{resize:both}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}:where(.space-y-0\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-px>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(1px * var(--tw-space-y-reverse));margin-block-end:calc(1px * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.rounded-sm{border-radius:var(--radius-sm)}.border{border-style:var(--tw-border-style);border-width:1px}.p-3{padding:calc(var(--spacing) * 3)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-px{padding-block:1px}.pt-1\.5{padding-top:calc(var(--spacing) * 1.5)}.pt-4{padding-top:calc(var(--spacing) * 4)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pb-0{padding-bottom:calc(var(--spacing) * 0)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pb-5{padding-bottom:calc(var(--spacing) * 5)}.pl-3\.5{padding-left:calc(var(--spacing) * 3.5)}.pl-4{padding-left:calc(var(--spacing) * 4)}.pl-\[3px\]{padding-left:3px}.text-center{text-align:center}.font-\[DM_Sans\,sans-serif\]{font-family:DM Sans,sans-serif}.font-\[JetBrains_Mono\,monospace\]{font-family:JetBrains Mono,monospace}.font-mono{font-family:var(--font-mono)}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.text-\[18px\]{font-size:18px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[0\.2em\]{--tw-tracking:.2em;letter-spacing:.2em}.tracking-\[0\.15em\]{--tw-tracking:.15em;letter-spacing:.15em}.tracking-\[0\.25em\]{--tw-tracking:.25em;letter-spacing:.25em}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-black{color:var(--color-black)}.uppercase{text-transform:uppercase}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-100{--tw-duration:.1s;transition-duration:.1s}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.select-none{-webkit-user-select:none;user-select:none}@media(hover:hover){.hover\:bg-\[\#D97706\]:hover{background-color:#d97706}.hover\:bg-\[\#FEF3C7\]:hover{background-color:#fef3c7}}.active\:bg-\[\#D97706\]:active{background-color:#d97706}}@keyframes pulse-glow{0%,to{box-shadow:0 0 #fbbf2466}50%{box-shadow:0 0 12px 2px #fbbf2426}}@keyframes pulse-dot{0%,to{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(1.5)}}@keyframes breathe{0%,to{opacity:.5}50%{opacity:1}}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}@keyframes slide-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes glow-border{0%,to{border-color:#fbbf2426}50%{border-color:#fbbf2480}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-3px)}}.font-mono-log{font-family:JetBrains Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.noise-bg{position:relative}.noise-bg:before{content:"";pointer-events:none;z-index:0;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E");position:absolute;top:0;right:0;bottom:0;left:0}.accent-stripe{background:repeating-linear-gradient(-45deg,#0000,#0000 8px,#fbbf2408 8px,#fbbf2408 9px)}.progress-gradient{background:linear-gradient(90deg,#fbbf24,#f59e0b,#fb923c)}.skeleton-shimmer{background:linear-gradient(90deg,#1a1e28 25%,#2a3040,#1a1e28 75%) 0 0/200% 100%;animation:1.5s ease-in-out infinite shimmer}::-webkit-scrollbar{width:5px;height:5px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:#2a3040;border-radius:10px}::-webkit-scrollbar-thumb:hover{background:#3a4555}*{scrollbar-width:thin;scrollbar-color:#2a2a2d transparent}::selection{color:#fbbf24;background:#fbbf2433}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}
|
package/src/derive.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { readdirSync, existsSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 从 artifacts 文件系统反推状态,与 progress.json 交叉校验。
|
|
6
|
-
* 纯函数,零外部副作用(fix 模式除外)。
|
|
7
|
-
*
|
|
8
|
-
* @param {string} cwd - 项目根目录
|
|
9
|
-
* @param {object} options
|
|
10
|
-
* @param {'light'|'full'} options.mode - 轻量(当前步骤)或全量扫描
|
|
11
|
-
* @param {boolean} options.fix - 是否自动修复明显不一致
|
|
12
|
-
* @param {object} options.pm - ProgressManager 实例(fix 模式需要)
|
|
13
|
-
* @param {object} options.progress - 已加载的 progress 数据
|
|
14
|
-
* @returns {{ issues: Array<{type:string, severity:string, stage:string, step:number, message:string, suggestion:string}>, fixed: number }}
|
|
15
|
-
*/
|
|
16
|
-
export function deriveState(cwd, options = {}) {
|
|
17
|
-
const { mode = 'light', fix = false, pm = null, progress = null } = options;
|
|
18
|
-
|
|
19
|
-
const issues = [];
|
|
20
|
-
let fixed = 0;
|
|
21
|
-
|
|
22
|
-
const artifactsDir = join(cwd, '.sillyspec', '.runtime', 'artifacts');
|
|
23
|
-
if (!existsSync(artifactsDir)) {
|
|
24
|
-
return { issues: [{ type: 'no-artifacts', severity: 'info', stage: '-', step: 0, message: 'artifacts 目录不存在', suggestion: '正常,项目刚开始' }], fixed: 0 };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 扫描 artifacts 文件,解析 stage/step 信息
|
|
28
|
-
const artifactMap = {}; // { "stage:stepN": [filenames] }
|
|
29
|
-
const stageStepSet = new Set(); // "stage:stepN"
|
|
30
|
-
|
|
31
|
-
let files;
|
|
32
|
-
try {
|
|
33
|
-
files = readdirSync(artifactsDir).filter(f => f.endsWith('.txt'));
|
|
34
|
-
} catch {
|
|
35
|
-
return { issues: [], fixed: 0 };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
for (const file of files) {
|
|
39
|
-
// 格式: {stage}-step{N}-{timestamp}.txt
|
|
40
|
-
const match = file.match(/^(.+)-step(\d+)-\d+\.txt$/);
|
|
41
|
-
if (match) {
|
|
42
|
-
const [, stage, stepStr] = match;
|
|
43
|
-
const key = `${stage}:${stepStr}`;
|
|
44
|
-
if (!artifactMap[key]) artifactMap[key] = [];
|
|
45
|
-
artifactMap[key].push(file);
|
|
46
|
-
stageStepSet.add(key);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// 需要检查的阶段
|
|
51
|
-
let stagesToCheck = [];
|
|
52
|
-
if (progress) {
|
|
53
|
-
if (mode === 'light') {
|
|
54
|
-
// 轻量:只检查 currentStage
|
|
55
|
-
const currentStage = progress.currentStage || '';
|
|
56
|
-
if (currentStage) stagesToCheck.push(currentStage);
|
|
57
|
-
} else {
|
|
58
|
-
// 全量:检查所有阶段
|
|
59
|
-
stagesToCheck = Object.keys(progress.stages || {});
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
// 没有 progress 数据,从 artifacts 推断所有阶段
|
|
63
|
-
for (const key of stageStepSet) {
|
|
64
|
-
const stage = key.split(':')[0];
|
|
65
|
-
if (!stagesToCheck.includes(stage)) stagesToCheck.push(stage);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
for (const stage of stagesToCheck) {
|
|
70
|
-
const stageData = progress?.stages?.[stage];
|
|
71
|
-
const steps = stageData?.steps || [];
|
|
72
|
-
|
|
73
|
-
// 收集 artifacts 中该阶段的步骤编号
|
|
74
|
-
const artifactSteps = new Set();
|
|
75
|
-
for (const key of stageStepSet) {
|
|
76
|
-
const [s, n] = key.split(':');
|
|
77
|
-
if (s === stage) artifactSteps.add(parseInt(n));
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 检查1:artifacts 有但 progress 未标记完成
|
|
81
|
-
for (const stepNum of artifactSteps) {
|
|
82
|
-
const stepIdx = stepNum - 1;
|
|
83
|
-
if (stepIdx < steps.length) {
|
|
84
|
-
const step = steps[stepIdx];
|
|
85
|
-
if (step.status !== 'done') {
|
|
86
|
-
issues.push({
|
|
87
|
-
type: 'missing-progress',
|
|
88
|
-
severity: 'issue',
|
|
89
|
-
stage,
|
|
90
|
-
step: stepNum,
|
|
91
|
-
message: `artifacts 有 ${stage}-step${stepNum} 文件但 progress 未标记完成`,
|
|
92
|
-
suggestion: '标记该步骤为 done'
|
|
93
|
-
});
|
|
94
|
-
if (fix && pm && progress) {
|
|
95
|
-
step.status = 'done';
|
|
96
|
-
pm._write(cwd, progress);
|
|
97
|
-
fixed++;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 检查2:progress 有但 artifacts 无文件(warning,不修复)
|
|
104
|
-
for (let i = 0; i < steps.length; i++) {
|
|
105
|
-
const step = steps[i];
|
|
106
|
-
if (step.status === 'done' && !artifactSteps.has(i + 1)) {
|
|
107
|
-
issues.push({
|
|
108
|
-
type: 'missing-artifact',
|
|
109
|
-
severity: 'warning',
|
|
110
|
-
stage,
|
|
111
|
-
step: i + 1,
|
|
112
|
-
message: `${stage} step ${i + 1} 标记完成但 artifacts 无对应文件`,
|
|
113
|
-
suggestion: '可能被手动清理,忽略即可'
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 检查3:artifacts 有 step5 但 progress 只到 step3(中间漏记)
|
|
119
|
-
if (artifactSteps.size > 0) {
|
|
120
|
-
const maxArtifactStep = Math.max(...artifactSteps);
|
|
121
|
-
const maxProgressDoneStep = steps.reduce((max, s, i) => s.status === 'done' ? Math.max(max, i + 1) : max, 0);
|
|
122
|
-
|
|
123
|
-
if (maxArtifactStep > maxProgressDoneStep && maxProgressDoneStep > 0) {
|
|
124
|
-
// 检查中间是否有漏记的
|
|
125
|
-
for (let i = maxProgressDoneStep + 1; i <= maxArtifactStep; i++) {
|
|
126
|
-
if (artifactSteps.has(i) && i - 1 < steps.length && steps[i - 1].status !== 'done') {
|
|
127
|
-
// 已在检查1处理,跳过
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// progress 步骤数少于 artifacts 最大步骤号
|
|
133
|
-
if (maxArtifactStep > steps.length) {
|
|
134
|
-
issues.push({
|
|
135
|
-
type: 'missing-steps',
|
|
136
|
-
severity: 'issue',
|
|
137
|
-
stage,
|
|
138
|
-
step: steps.length + 1,
|
|
139
|
-
message: `artifacts 有 step${maxArtifactStep} 但 progress 只有 ${steps.length} 个步骤`,
|
|
140
|
-
suggestion: 'progress 数据可能不完整'
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return { issues, fixed };
|
|
147
|
-
}
|