sillyspec 3.20.1 → 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 (209) 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 +2 -8
  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 -685
  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 -2920
  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 -468
  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/.npmrc-tmp +0 -1
  131. package/CLAUDE.md +0 -18
  132. package/docs/brainstorm-plan-contract.md +0 -64
  133. package/docs/plan-execute-contract.md +0 -123
  134. package/docs/platform-scan-protocol.md +0 -298
  135. package/docs/revision-mode.md +0 -115
  136. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +0 -99
  137. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +0 -218
  138. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +0 -167
  139. package/docs/sillyspec/file-lifecycle/storage-and-state.md +0 -148
  140. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +0 -193
  141. package/docs/sillyspec/file-lifecycle.md +0 -125
  142. package/docs/workflow-contract-regression.md +0 -106
  143. package/docs/worktree-isolation.md +0 -252
  144. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +0 -1
  145. package/packages/dashboard/dist/prototype-dashboard.html +0 -836
  146. package/packages/dashboard/dist/prototype-overview.html +0 -256
  147. package/packages/dashboard/public/prototype-dashboard.html +0 -836
  148. package/packages/dashboard/public/prototype-overview.html +0 -256
  149. package/packages/dashboard/src/components/HResizeHandle.vue +0 -48
  150. package/packages/dashboard/src/components/ProjectCard.vue +0 -187
  151. package/packages/dashboard/src/components/VResizeHandle.vue +0 -61
  152. package/packages/dashboard/src/composables/useLayout.js +0 -131
  153. package/src/brainstorm-postcheck.js +0 -158
  154. package/src/change-list.js +0 -52
  155. package/src/change-risk-profile.js +0 -352
  156. package/src/classify-change.js +0 -73
  157. package/src/constants.js +0 -70
  158. package/src/contract-matrix.js +0 -278
  159. package/src/db.js +0 -201
  160. package/src/endpoint-extractor.js +0 -315
  161. package/src/hooks/claude-pre-tool-use.cjs +0 -125
  162. package/src/hooks/worktree-guard.js +0 -761
  163. package/src/knowledge-match.js +0 -130
  164. package/src/modules.js +0 -482
  165. package/src/scan-postcheck.js +0 -380
  166. package/src/stage-contract.js +0 -700
  167. package/src/stages/brainstorm-auto.js +0 -229
  168. package/src/stages/explore.js +0 -34
  169. package/src/stages/knowledge.js +0 -498
  170. package/src/stages/plan-postcheck.js +0 -513
  171. package/src/sync.js +0 -497
  172. package/src/task-review.js +0 -346
  173. package/src/workflow.js +0 -785
  174. package/src/worktree-apply.js +0 -549
  175. package/src/worktree.js +0 -932
  176. package/templates/workflows/archive-impact.yaml +0 -79
  177. package/templates/workflows/scan-docs.yaml +0 -132
  178. package/test/brainstorm-plan-contract.test.mjs +0 -273
  179. package/test/check-syntax.mjs +0 -26
  180. package/test/cli-top-level-aliases.test.mjs +0 -174
  181. package/test/contract-artifacts.test.mjs +0 -323
  182. package/test/decision-supersede.test.mjs +0 -277
  183. package/test/knowledge-match.test.mjs +0 -231
  184. package/test/plan-execute-contract.test.mjs +0 -330
  185. package/test/plan-optimization.test.mjs +0 -572
  186. package/test/platform-artifacts.test.mjs +0 -190
  187. package/test/platform-failure-samples.test.mjs +0 -199
  188. package/test/platform-recovery-chain.test.mjs +0 -179
  189. package/test/platform-recovery.test.mjs +0 -167
  190. package/test/platform-scan-p0.test.mjs +0 -175
  191. package/test/revision-v1.test.mjs +0 -1145
  192. package/test/run-sanitize-project-name.test.mjs +0 -51
  193. package/test/run-scan-postcheck-fail.test.mjs +0 -64
  194. package/test/run-scan-project-parse.test.mjs +0 -77
  195. package/test/run-tests.mjs +0 -48
  196. package/test/scan-docs-yaml-placeholders.test.mjs +0 -84
  197. package/test/scan-knowledge.test.mjs +0 -175
  198. package/test/scan-paths.test.mjs +0 -68
  199. package/test/scan-postcheck-project-priority.test.mjs +0 -85
  200. package/test/scan-postcheck.test.mjs +0 -183
  201. package/test/scan-workflow-anyfailed-block.test.mjs +0 -52
  202. package/test/spec-dir.test.mjs +0 -206
  203. package/test/stage-contract-failed-post-check.test.mjs +0 -102
  204. package/test/stage-contract.test.mjs +0 -299
  205. package/test/stage-definitions.test.mjs +0 -39
  206. package/test/wait-gates.test.mjs +0 -501
  207. package/test/workflow-spec-base.test.mjs +0 -142
  208. package/test/worktree-guard.test.mjs +0 -136
  209. 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
+ }