sillyspec 3.20.2 → 3.20.3

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.
Files changed (208) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +1 -5
  2. package/.claude/skills/sillyspec-auto/SKILL.md +2 -8
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +1 -28
  4. package/.claude/skills/sillyspec-commit/SKILL.md +3 -4
  5. package/.claude/skills/sillyspec-continue/SKILL.md +4 -5
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +3 -12
  7. package/.claude/skills/sillyspec-execute/SKILL.md +2 -15
  8. package/.claude/skills/sillyspec-explore/SKILL.md +1 -14
  9. package/.claude/skills/sillyspec-plan/SKILL.md +36 -5
  10. package/.claude/skills/sillyspec-propose/SKILL.md +0 -4
  11. package/.claude/skills/sillyspec-quick/SKILL.md +1 -5
  12. package/.claude/skills/sillyspec-resume/SKILL.md +66 -23
  13. package/.claude/skills/sillyspec-scan/SKILL.md +1 -5
  14. package/.claude/skills/sillyspec-state/SKILL.md +8 -8
  15. package/.claude/skills/sillyspec-status/SKILL.md +1 -5
  16. package/.claude/skills/sillyspec-verify/SKILL.md +1 -5
  17. package/.claude/skills/sillyspec-workspace/SKILL.md +3 -11
  18. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +97 -0
  19. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +51 -0
  20. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +29 -0
  21. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +34 -0
  22. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +13 -0
  23. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +43 -0
  24. package/.sillyspec/changes/auto-mode/design.md +50 -0
  25. package/.sillyspec/changes/auto-mode/proposal.md +19 -0
  26. package/.sillyspec/changes/auto-mode/requirements.md +21 -0
  27. package/.sillyspec/changes/auto-mode/tasks.md +7 -0
  28. package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +206 -0
  29. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +199 -0
  30. package/.sillyspec/changes/dashboard/design.md +219 -0
  31. package/.sillyspec/changes/dashboard/design.md.braindraft +206 -0
  32. package/.sillyspec/changes/run-command-design/design.md +1230 -0
  33. package/.sillyspec/changes/unified-docs-design/design.md +199 -0
  34. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  35. package/.sillyspec/knowledge/INDEX.md +8 -0
  36. package/.sillyspec/knowledge/uncategorized.md +3 -0
  37. package/.sillyspec/plans/2026-04-05-dashboard.md +737 -0
  38. package/.sillyspec/projects/sillyspec.yaml +3 -0
  39. package/README.md +11 -13
  40. package/SKILL.md +40 -44
  41. package/dist/steps/brainstorm/01-load-context.md +30 -0
  42. package/dist/steps/brainstorm/02-reuse-check.md +6 -0
  43. package/dist/steps/brainstorm/03-prototype-analysis.md +11 -0
  44. package/dist/steps/brainstorm/04-module-split.md +23 -0
  45. package/dist/steps/brainstorm/05-dialog-explore.md +8 -0
  46. package/dist/steps/brainstorm/06-propose-approaches.md +3 -0
  47. package/dist/steps/brainstorm/07-present-design.md +3 -0
  48. package/dist/steps/brainstorm/08-write-design.md +21 -0
  49. package/dist/steps/brainstorm/09-self-review.md +15 -0
  50. package/dist/steps/brainstorm/10-user-confirm.md +3 -0
  51. package/dist/steps/brainstorm/11-output-spec.md +7 -0
  52. package/dist/steps/brainstorm/manifest.yaml +26 -0
  53. package/dist/steps/execute/01-load-context.md +41 -0
  54. package/dist/steps/execute/02-scan-conventions.md +47 -0
  55. package/dist/steps/execute/03-skill-mcp.md +19 -0
  56. package/dist/steps/execute/04-assign-task.md +22 -0
  57. package/dist/steps/execute/04b-prompt-template.md +54 -0
  58. package/dist/steps/execute/05-write-test.md +7 -0
  59. package/dist/steps/execute/06-write-code.md +8 -0
  60. package/dist/steps/execute/07-run-test.md +26 -0
  61. package/dist/steps/execute/08-fix-issues.md +28 -0
  62. package/dist/steps/execute/09-next-task.md +33 -0
  63. package/dist/steps/execute/manifest.yaml +28 -0
  64. package/dist/steps/plan/01-load-context.md +22 -0
  65. package/dist/steps/plan/02-anchor-confirm.md +1 -0
  66. package/dist/steps/plan/03-expand-tasks.md +33 -0
  67. package/dist/steps/plan/04-mark-order.md +15 -0
  68. package/dist/steps/plan/05-e2e-planning.md +17 -0
  69. package/dist/steps/plan/06-self-check.md +16 -0
  70. package/dist/steps/plan/07-save.md +1 -0
  71. package/dist/steps/plan/manifest.yaml +18 -0
  72. package/dist/steps/scan/01-env-detect.md +51 -0
  73. package/dist/steps/scan/02-tech-stack.md +16 -0
  74. package/dist/steps/scan/03-conventions.md +16 -0
  75. package/dist/steps/scan/04-structure.md +19 -0
  76. package/dist/steps/scan/05-quality.md +18 -0
  77. package/dist/steps/scan/06-complete.md +49 -0
  78. package/dist/steps/scan/manifest.yaml +16 -0
  79. package/dist/steps/verify/01-load-specs.md +28 -0
  80. package/dist/steps/verify/02-check-tasks.md +1 -0
  81. package/dist/steps/verify/03-check-design.md +6 -0
  82. package/dist/steps/verify/04-run-tests.md +7 -0
  83. package/dist/steps/verify/05-e2e-tests.md +27 -0
  84. package/dist/steps/verify/05b-e2e-fix.md +33 -0
  85. package/dist/steps/verify/06-code-quality.md +25 -0
  86. package/dist/steps/verify/07-lint-check.md +27 -0
  87. package/dist/steps/verify/08-output-report.md +14 -0
  88. package/dist/steps/verify/manifest.yaml +22 -0
  89. package/package.json +1 -7
  90. package/packages/dashboard/dist/assets/{index-Bq_Z2hne.js → index-D1EVTLmc.js} +1264 -1264
  91. package/packages/dashboard/dist/assets/index-DGe8CqeP.css +1 -0
  92. package/packages/dashboard/dist/index.html +2 -2
  93. package/packages/dashboard/package-lock.json +6 -6
  94. package/packages/dashboard/server/executor.js +1 -1
  95. package/packages/dashboard/server/index.js +18 -98
  96. package/packages/dashboard/server/parser.js +71 -139
  97. package/packages/dashboard/server/watcher.js +6 -14
  98. package/packages/dashboard/src/App.vue +185 -418
  99. package/packages/dashboard/src/components/ActionBar.vue +1 -10
  100. package/packages/dashboard/src/components/CommandPalette.vue +1 -5
  101. package/packages/dashboard/src/components/DocPreview.vue +8 -105
  102. package/packages/dashboard/src/components/DocTree.vue +19 -75
  103. package/packages/dashboard/src/components/PipelineStage.vue +2 -22
  104. package/packages/dashboard/src/components/PipelineView.vue +5 -32
  105. package/packages/dashboard/src/components/ProjectOverview.vue +139 -113
  106. package/packages/dashboard/src/components/StageBadge.vue +3 -17
  107. package/packages/dashboard/src/components/StepCard.vue +2 -7
  108. package/packages/dashboard/src/composables/useDashboard.js +0 -28
  109. package/src/derive.js +147 -0
  110. package/src/index.js +40 -688
  111. package/src/init.js +63 -119
  112. package/src/migrate.js +7 -7
  113. package/src/progress.js +248 -1474
  114. package/src/run.js +302 -3008
  115. package/src/setup.js +64 -2
  116. package/src/stages/archive.js +17 -123
  117. package/src/stages/brainstorm.js +48 -454
  118. package/src/stages/doctor.js +46 -99
  119. package/src/stages/execute.js +59 -420
  120. package/src/stages/index.js +18 -12
  121. package/src/stages/plan.js +189 -492
  122. package/src/stages/propose.js +11 -70
  123. package/src/stages/quick.js +13 -52
  124. package/src/stages/scan.js +68 -485
  125. package/src/stages/status.js +1 -1
  126. package/src/stages/verify.js +16 -203
  127. package/src/step.js +543 -0
  128. package/.claude/skills/sillyspec-knowledge/SKILL.md +0 -270
  129. package/.husky/pre-push +0 -13
  130. package/CLAUDE.md +0 -18
  131. package/docs/brainstorm-plan-contract.md +0 -64
  132. package/docs/plan-execute-contract.md +0 -123
  133. package/docs/platform-scan-protocol.md +0 -298
  134. package/docs/revision-mode.md +0 -115
  135. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
  136. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -218
  137. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
  138. package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
  139. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -193
  140. package/docs/sillyspec/file-lifecycle.md +0 -125
  141. package/docs/workflow-contract-regression.md +0 -106
  142. package/docs/worktree-isolation.md +0 -252
  143. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
  144. package/packages/dashboard/dist/prototype-dashboard.html +0 -836
  145. package/packages/dashboard/dist/prototype-overview.html +0 -256
  146. package/packages/dashboard/public/prototype-dashboard.html +0 -836
  147. package/packages/dashboard/public/prototype-overview.html +0 -256
  148. package/packages/dashboard/src/components/HResizeHandle.vue +0 -48
  149. package/packages/dashboard/src/components/ProjectCard.vue +0 -187
  150. package/packages/dashboard/src/components/VResizeHandle.vue +0 -61
  151. package/packages/dashboard/src/composables/useLayout.js +0 -131
  152. package/src/brainstorm-postcheck.js +0 -158
  153. package/src/change-list.js +0 -52
  154. package/src/change-risk-profile.js +0 -352
  155. package/src/classify-change.js +0 -73
  156. package/src/constants.js +0 -70
  157. package/src/contract-matrix.js +0 -278
  158. package/src/db.js +0 -201
  159. package/src/endpoint-extractor.js +0 -315
  160. package/src/hooks/claude-pre-tool-use.cjs +0 -125
  161. package/src/hooks/worktree-guard.js +0 -761
  162. package/src/knowledge-match.js +0 -130
  163. package/src/modules.js +0 -482
  164. package/src/scan-postcheck.js +0 -383
  165. package/src/stage-contract.js +0 -700
  166. package/src/stages/brainstorm-auto.js +0 -229
  167. package/src/stages/explore.js +0 -34
  168. package/src/stages/knowledge.js +0 -498
  169. package/src/stages/plan-postcheck.js +0 -513
  170. package/src/sync.js +0 -497
  171. package/src/task-review.js +0 -346
  172. package/src/workflow.js +0 -785
  173. package/src/worktree-apply.js +0 -549
  174. package/src/worktree.js +0 -932
  175. package/templates/workflows/archive-impact.yaml +0 -79
  176. package/templates/workflows/scan-docs.yaml +0 -132
  177. package/test/brainstorm-plan-contract.test.mjs +0 -273
  178. package/test/check-syntax.mjs +0 -26
  179. package/test/cli-top-level-aliases.test.mjs +0 -174
  180. package/test/contract-artifacts.test.mjs +0 -323
  181. package/test/decision-supersede.test.mjs +0 -277
  182. package/test/knowledge-match.test.mjs +0 -231
  183. package/test/plan-execute-contract.test.mjs +0 -330
  184. package/test/plan-optimization.test.mjs +0 -572
  185. package/test/platform-artifacts.test.mjs +0 -190
  186. package/test/platform-failure-samples.test.mjs +0 -199
  187. package/test/platform-recovery-chain.test.mjs +0 -179
  188. package/test/platform-recovery.test.mjs +0 -167
  189. package/test/platform-scan-p0.test.mjs +0 -175
  190. package/test/revision-v1.test.mjs +0 -1145
  191. package/test/run-sanitize-project-name.test.mjs +0 -51
  192. package/test/run-scan-postcheck-fail.test.mjs +0 -64
  193. package/test/run-scan-project-parse.test.mjs +0 -200
  194. package/test/run-tests.mjs +0 -48
  195. package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
  196. package/test/scan-knowledge.test.mjs +0 -175
  197. package/test/scan-paths.test.mjs +0 -68
  198. package/test/scan-postcheck-project-priority.test.mjs +0 -85
  199. package/test/scan-postcheck.test.mjs +0 -197
  200. package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
  201. package/test/spec-dir.test.mjs +0 -206
  202. package/test/stage-contract-failed-post-check.test.mjs +0 -102
  203. package/test/stage-contract.test.mjs +0 -299
  204. package/test/stage-definitions.test.mjs +0 -39
  205. package/test/wait-gates.test.mjs +0 -501
  206. package/test/workflow-spec-base.test.mjs +0 -142
  207. package/test/worktree-guard.test.mjs +0 -136
  208. package/test/worktree-native-overlay.test.mjs +0 -188
@@ -1,152 +1,178 @@
1
1
  <template>
2
- <div class="overview-section">
3
- <!-- 头部 -->
4
- <div class="section-header">
5
- <h1 class="section-title">
6
- 项目概览
7
- <span class="badge">{{ projects.length }} 个项目</span>
8
- </h1>
9
- <div class="section-actions">
10
- <button class="btn btn-icon" title="刷新" @click="refresh">↻</button>
11
- <button class="btn btn-secondary" @click="resetLayout">重置布局</button>
12
- </div>
2
+ <div v-if="project" class="project-overview">
3
+ <!-- Name + Path -->
4
+ <div class="ov-section">
5
+ <span class="ov-name">{{ project.name }}</span>
6
+ <span class="ov-path">{{ shortPath }}</span>
13
7
  </div>
8
+ <div class="ov-divider" />
14
9
 
15
- <!-- 卡片容器 -->
16
- <div class="cards-container">
17
- <ProjectCard
18
- v-for="project in projects"
19
- :key="project.name"
20
- :project="project"
21
- :is-selected="activeProject?.name === project.name"
22
- @select="handleSelect"
23
- />
10
+ <!-- Tech Stack -->
11
+ <div class="ov-section ov-clickable" @click="$emit('show-detail', 'tech')">
12
+ <span class="ov-label">技术栈</span>
13
+ <span v-for="t in overview.techStack" :key="t" class="ov-tag">{{ t }}</span>
14
+ </div>
15
+ <div class="ov-divider" />
16
+
17
+ <!-- Stage -->
18
+ <div v-if="project.state?.currentStage" class="ov-section">
19
+ <span class="ov-label">阶段</span>
20
+ <span class="ov-value">{{ project.state.currentStage }}</span>
21
+ </div>
22
+ <div v-else-if="project.state" class="ov-section">
23
+ <span class="ov-label">阶段</span>
24
+ <span class="ov-value" style="color:#9CA3AF;">未执行</span>
25
+ </div>
26
+ <div v-if="project.state?.currentStage" class="ov-divider" />
27
+
28
+ <!-- Last Active -->
29
+ <div v-if="overview.lastActive" class="ov-section">
30
+ <span class="ov-label">最近活跃</span>
31
+ <span class="ov-value">{{ formatTime(overview.lastActive) }}</span>
32
+ </div>
33
+ <div v-if="overview.lastActive" class="ov-divider" />
34
+
35
+ <!-- Doc Stats -->
36
+ <div v-if="overview.docStats.total > 0" class="ov-section ov-clickable" @click="$emit('show-detail', 'docs')">
37
+ <span class="ov-label">文档</span>
38
+ <span class="ov-value">{{ docSummary }}</span>
39
+ </div>
40
+ <div v-if="overview.docStats.total > 0" class="ov-divider" />
41
+
42
+ <!-- Git -->
43
+ <div class="ov-section ov-git ov-clickable" @click="$emit('show-detail', 'git')">
44
+ <span v-if="overview.git.branch" class="ov-branch">{{ overview.git.branch }}</span>
45
+ <span v-if="overview.git.lastCommit" class="ov-commit" :title="overview.git.lastCommit">{{ truncate(overview.git.lastCommit, 40) }}</span>
46
+ <span v-if="overview.git.dirtyCount > 0" class="ov-dirty">{{ overview.git.dirtyCount }} 未提交</span>
24
47
  </div>
25
48
  </div>
26
49
  </template>
27
50
 
28
51
  <script setup>
29
- import { defineProps, defineEmits } from 'vue'
30
- import ProjectCard from './ProjectCard.vue'
31
- import { useLayout } from '../composables/useLayout.js'
52
+ import { computed } from 'vue'
32
53
 
33
54
  const props = defineProps({
34
- projects: { type: Array, default: () => [] },
35
- activeProject: { type: Object, default: null }
55
+ project: { type: Object, default: null }
36
56
  })
37
57
 
38
- const emit = defineEmits(['select'])
58
+ defineEmits(['show-detail'])
39
59
 
40
- function handleSelect(project) {
41
- emit('select', project)
42
- }
60
+ const overview = computed(() => props.project?.overview || { techStack: [], lastActive: null, docStats: { design: 0, plan: 0, archive: 0, changes: 0, scan: 0, quicklog: 0, total: 0 }, git: { branch: '', lastCommit: '', dirtyCount: 0 } })
43
61
 
44
- function refresh() {
45
- // TODO: 实现刷新逻辑
46
- window.location.reload()
47
- }
62
+ const shortPath = computed(() => {
63
+ const p = props.project?.path || ''
64
+ return p.replace(/^\/Users\/[^/]+/, '~')
65
+ })
48
66
 
49
- function resetLayout() {
50
- if (confirm('确定要重置布局吗?')) {
51
- useLayout().resetLayout()
52
- }
53
- }
54
- </script>
67
+ const docSummary = computed(() => {
68
+ const s = overview.value.docStats
69
+ const parts = []
70
+ if (s.design) parts.push(`设计 ${s.design}`)
71
+ if (s.plan) parts.push(`计划 ${s.plan}`)
72
+ if (s.archive) parts.push(`归档 ${s.archive}`)
73
+ if (s.changes) parts.push(`变更 ${s.changes}`)
74
+ return parts.join(' / ') || `${s.total} 篇`
75
+ })
55
76
 
56
- <style scoped>
57
- .overview-section {
58
- height: 100%;
59
- background: #FFFFFF;
60
- padding: 16px 24px;
61
- display: flex;
62
- flex-direction: column;
77
+ function formatTime(iso) {
78
+ if (!iso) return ''
79
+ const d = new Date(iso)
80
+ const now = new Date()
81
+ const diffMs = now - d
82
+ if (diffMs < 60000) return '刚刚'
83
+ if (diffMs < 3600000) return `${Math.floor(diffMs / 60000)} 分钟前`
84
+ if (diffMs < 86400000) return `${Math.floor(diffMs / 3600000)} 小时前`
85
+ if (diffMs < 604800000) return `${Math.floor(diffMs / 86400000)} 天前`
86
+ return `${d.getMonth() + 1}/${d.getDate()}`
63
87
  }
64
88
 
65
- .section-header {
66
- display: flex;
67
- justify-content: space-between;
68
- align-items: center;
69
- margin-bottom: 16px;
89
+ function truncate(s, n) {
90
+ return s.length > n ? s.slice(0, n) + '…' : s
70
91
  }
92
+ </script>
71
93
 
72
- .section-title {
73
- font-size: 18px;
74
- font-weight: 600;
75
- color: #1A1A1A;
94
+ <style scoped>
95
+ .project-overview {
76
96
  display: flex;
77
97
  align-items: center;
98
+ background: #FFFFFF;
99
+ border-bottom: 1px solid #E5E5EA;
100
+ padding: 8px 16px;
101
+ min-height: 48px;
78
102
  gap: 8px;
103
+ flex-shrink: 0;
104
+ overflow-x: auto;
79
105
  }
80
-
81
- .badge {
82
- font-size: 12px;
83
- padding: 2px 8px;
84
- background: #E5E7EB;
85
- border-radius: 10px;
86
- color: #6B7280;
87
- font-weight: 500;
88
- }
89
-
90
- .section-actions {
106
+ .ov-section {
91
107
  display: flex;
92
- gap: 8px;
93
108
  align-items: center;
109
+ gap: 6px;
110
+ white-space: nowrap;
111
+ flex-shrink: 0;
94
112
  }
95
-
96
- .btn {
97
- padding: 6px 12px;
98
- border-radius: 6px;
99
- border: none;
113
+ .ov-clickable {
100
114
  cursor: pointer;
101
- font-size: 13px;
102
- font-weight: 500;
103
- transition: all 0.2s;
115
+ border-radius: 4px;
116
+ padding: 2px 6px;
117
+ margin: -2px -6px;
118
+ transition: background 0.15s;
104
119
  }
105
-
106
- .btn:hover {
107
- opacity: 0.9;
120
+ .ov-clickable:hover {
121
+ background: rgba(217,119,6,0.06);
108
122
  }
109
-
110
- .btn-secondary {
111
- background: #E5E7EB;
112
- color: #374151;
123
+ .ov-name {
124
+ font-weight: 600;
125
+ font-size: 13px;
126
+ color: #1C1C1E;
113
127
  }
114
-
115
- .btn-icon {
116
- padding: 6px;
117
- width: 32px;
118
- height: 32px;
119
- display: flex;
120
- align-items: center;
121
- justify-content: center;
122
- background: #F3F4F6;
123
- border-radius: 6px;
128
+ .ov-path {
129
+ font-size: 11px;
130
+ color: #636366;
124
131
  }
125
-
126
- .btn-icon:hover {
127
- background: #E5E7EB;
132
+ .ov-label {
133
+ font-size: 11px;
134
+ color: #636366;
128
135
  }
129
-
130
- .cards-container {
131
- flex: 1;
132
- display: flex;
133
- gap: 16px;
134
- overflow-x: auto;
135
- overflow-y: hidden;
136
- padding-bottom: 8px;
136
+ .ov-value {
137
+ font-size: 12px;
138
+ color: #1C1C1E;
139
+ font-weight: 500;
137
140
  }
138
-
139
- .cards-container::-webkit-scrollbar {
140
- height: 8px;
141
+ .ov-tag {
142
+ font-size: 10px;
143
+ padding: 1px 8px;
144
+ border-radius: 10px;
145
+ background: rgba(217,119,6,0.08);
146
+ color: #D97706;
147
+ font-weight: 500;
141
148
  }
142
-
143
- .cards-container::-webkit-scrollbar-track {
144
- background: #F3F4F6;
149
+ .ov-divider {
150
+ width: 1px;
151
+ height: 20px;
152
+ background: #E5E5EA;
153
+ flex-shrink: 0;
154
+ }
155
+ .ov-git {
156
+ margin-left: auto;
157
+ gap: 10px;
158
+ }
159
+ .ov-branch {
160
+ font-size: 11px;
161
+ color: #636366;
162
+ background: #F5F5F7;
163
+ padding: 1px 8px;
145
164
  border-radius: 4px;
146
165
  }
147
-
148
- .cards-container::-webkit-scrollbar-thumb {
149
- background: #D1D5DB;
150
- border-radius: 4px;
166
+ .ov-commit {
167
+ font-size: 11px;
168
+ color: #636366;
169
+ max-width: 200px;
170
+ overflow: hidden;
171
+ text-overflow: ellipsis;
172
+ }
173
+ .ov-dirty {
174
+ font-size: 11px;
175
+ color: #D97706;
176
+ font-weight: 500;
151
177
  }
152
178
  </style>
@@ -19,15 +19,7 @@ const props = defineProps({
19
19
 
20
20
  const displayLabel = computed(() => {
21
21
  if (props.label) return props.label
22
- const labels = {
23
- 'completed': 'done',
24
- 'in-progress': 'running',
25
- 'blocked': 'blocked',
26
- 'failed': 'error',
27
- 'revising': 'revising',
28
- 'stale': 'stale',
29
- 'waiting': 'waiting',
30
- }
22
+ const labels = { 'completed': 'done', 'in-progress': 'running', 'blocked': 'blocked', 'failed': 'error' }
31
23
  return labels[props.status] || ''
32
24
  })
33
25
 
@@ -39,10 +31,7 @@ const badgeStyle = computed(() => {
39
31
  'in-progress': { background: 'rgba(251,191,36,0.1)', color: '#D97706' },
40
32
  'blocked': { background: 'rgba(251,146,60,0.1)', color: '#EA580C' },
41
33
  'failed': { background: 'rgba(239,68,68,0.1)', color: '#DC2626' },
42
- 'pending': { background: 'rgba(82,82,82,0.15)', color: '#6B7280' },
43
- 'revising': { background: 'rgba(139,92,246,0.1)', color: '#7C3AED' },
44
- 'stale': { background: 'rgba(234,179,8,0.1)', color: '#CA8A04' },
45
- 'waiting': { background: 'rgba(59,130,246,0.1)', color: '#2563EB' },
34
+ 'pending': { background: 'rgba(82,82,82,0.15)', color: '#6B7280' }
46
35
  }
47
36
  return styles[props.status] || styles.pending
48
37
  })
@@ -57,10 +46,7 @@ const dotStyle = computed(() => {
57
46
  'in-progress': '#D97706',
58
47
  'blocked': '#EA580C',
59
48
  'failed': '#DC2626',
60
- 'pending': '#6B7280',
61
- 'revising': '#7C3AED',
62
- 'stale': '#CA8A04',
63
- 'waiting': '#2563EB',
49
+ 'pending': '#6B7280'
64
50
  }
65
51
  return { background: colors[props.status] || '#6B7280' }
66
52
  })
@@ -62,10 +62,7 @@ const emit = defineEmits(['select'])
62
62
  const hovered = ref(false)
63
63
 
64
64
  const barColor = computed(() => {
65
- const colors = {
66
- 'completed': '#16A34A', 'in-progress': '#D97706', 'blocked': '#EA580C', 'failed': '#DC2626', 'pending': '#E5E5EA',
67
- 'revising': '#7C3AED', 'stale': '#CA8A04', 'waiting': '#2563EB',
68
- }
65
+ const colors = { 'completed': '#16A34A', 'in-progress': '#D97706', 'blocked': '#EA580C', 'failed': '#DC2626', 'pending': '#E5E5EA' }
69
66
  return colors[props.step.status] || colors.pending
70
67
  })
71
68
 
@@ -75,9 +72,7 @@ const cardStyle = computed(() => ({
75
72
  }))
76
73
 
77
74
  const statusLabel = computed(() => {
78
- const labels = { 'completed': '完成', 'in-progress': '进行中', 'blocked': '阻塞', 'failed': '失败', 'pending': '待办',
79
- 'revising': '修订中', 'stale': '已失效', 'waiting': '等待中',
80
- }
75
+ const labels = { 'completed': '完成', 'in-progress': '进行中', 'blocked': '阻塞', 'failed': '失败', 'pending': '待办' }
81
76
  return labels[props.step.status] || '待办'
82
77
  })
83
78
 
@@ -125,35 +125,8 @@ export function useDashboard() {
125
125
  const updated = getProject(state.activeProject.path)
126
126
  if (updated) {
127
127
  state.activeProject = updated
128
- return
129
128
  }
130
129
  }
131
-
132
- state.activeProject = state.projects[0] || null
133
- }
134
-
135
- /**
136
- * Merge a single project update into the current project list.
137
- * @param {object} project - Partial project payload containing at least path
138
- */
139
- function updateProject(project) {
140
- if (!project?.path) return
141
-
142
- const index = state.projects.findIndex(p => p.path === project.path)
143
- if (index === -1) return
144
-
145
- const updated = {
146
- ...state.projects[index],
147
- ...project,
148
- state: project.state ?? state.projects[index].state,
149
- overview: project.overview ?? state.projects[index].overview
150
- }
151
-
152
- state.projects.splice(index, 1, updated)
153
-
154
- if (state.activeProject?.path === project.path) {
155
- state.activeProject = updated
156
- }
157
130
  }
158
131
 
159
132
  /**
@@ -196,7 +169,6 @@ export function useDashboard() {
196
169
  openPanel,
197
170
  closePanel,
198
171
  updateProjects,
199
- updateProject,
200
172
  setExecuting,
201
173
  isExecuting,
202
174
  activeProjectName,
package/src/derive.js ADDED
@@ -0,0 +1,147 @@
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
+ }