sillyspec 3.20.3 → 3.20.4

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 (224) hide show
  1. package/.claude/skills/sillyspec-archive/SKILL.md +21 -17
  2. package/.claude/skills/sillyspec-auto/SKILL.md +83 -77
  3. package/.claude/skills/sillyspec-brainstorm/SKILL.md +44 -17
  4. package/.claude/skills/sillyspec-commit/SKILL.md +106 -105
  5. package/.claude/skills/sillyspec-continue/SKILL.md +45 -44
  6. package/.claude/skills/sillyspec-doctor/SKILL.md +31 -22
  7. package/.claude/skills/sillyspec-execute/SKILL.md +30 -17
  8. package/.claude/skills/sillyspec-explore/SKILL.md +109 -96
  9. package/.claude/skills/sillyspec-knowledge/SKILL.md +270 -0
  10. package/.claude/skills/sillyspec-plan/SKILL.md +21 -52
  11. package/.claude/skills/sillyspec-propose/SKILL.md +21 -17
  12. package/.claude/skills/sillyspec-quick/SKILL.md +21 -17
  13. package/.claude/skills/sillyspec-resume/SKILL.md +68 -111
  14. package/.claude/skills/sillyspec-scan/SKILL.md +21 -17
  15. package/.claude/skills/sillyspec-state/SKILL.md +54 -54
  16. package/.claude/skills/sillyspec-status/SKILL.md +21 -17
  17. package/.claude/skills/sillyspec-verify/SKILL.md +21 -17
  18. package/.claude/skills/sillyspec-workspace/SKILL.md +157 -149
  19. package/.husky/pre-push +13 -0
  20. package/CLAUDE.md +18 -0
  21. package/README.md +198 -186
  22. package/SKILL.md +90 -87
  23. package/bin/sillyspec.js +2 -2
  24. package/docs/brainstorm-plan-contract.md +64 -0
  25. package/docs/plan-execute-contract.md +123 -0
  26. package/docs/platform-scan-protocol.md +298 -0
  27. package/docs/revision-mode.md +115 -0
  28. package/docs/sillyspec/file-lifecycle/known-implementation-gaps.md +99 -0
  29. package/docs/sillyspec/file-lifecycle/platform-workflows-sync.md +218 -0
  30. package/docs/sillyspec/file-lifecycle/stage-artifacts.md +167 -0
  31. package/docs/sillyspec/file-lifecycle/storage-and-state.md +148 -0
  32. package/docs/sillyspec/file-lifecycle/worktree-and-guard.md +211 -0
  33. package/docs/sillyspec/file-lifecycle.md +125 -0
  34. package/docs/workflow-contract-regression.md +106 -0
  35. package/docs/worktree-isolation.md +252 -0
  36. package/package.json +40 -34
  37. package/packages/dashboard/dist/assets/{index-D1EVTLmc.js → index-Bq_Z2hne.js} +7446 -7446
  38. package/packages/dashboard/dist/assets/index-O2W5RV4z.css +1 -0
  39. package/packages/dashboard/dist/index.html +16 -16
  40. package/packages/dashboard/dist/prototype-dashboard.html +836 -0
  41. package/packages/dashboard/dist/prototype-overview.html +256 -0
  42. package/packages/dashboard/index.html +15 -15
  43. package/packages/dashboard/package-lock.json +2384 -2384
  44. package/packages/dashboard/package.json +25 -25
  45. package/packages/dashboard/public/prototype-dashboard.html +836 -0
  46. package/packages/dashboard/public/prototype-overview.html +256 -0
  47. package/packages/dashboard/server/executor.js +86 -86
  48. package/packages/dashboard/server/index.js +588 -508
  49. package/packages/dashboard/server/parser.js +526 -458
  50. package/packages/dashboard/server/watcher.js +350 -342
  51. package/packages/dashboard/src/App.vue +558 -325
  52. package/packages/dashboard/src/components/ActionBar.vue +93 -84
  53. package/packages/dashboard/src/components/CommandPalette.vue +96 -92
  54. package/packages/dashboard/src/components/DetailPanel.vue +137 -137
  55. package/packages/dashboard/src/components/DocPreview.vue +105 -8
  56. package/packages/dashboard/src/components/DocTree.vue +75 -19
  57. package/packages/dashboard/src/components/HResizeHandle.vue +48 -0
  58. package/packages/dashboard/src/components/LogStream.vue +65 -65
  59. package/packages/dashboard/src/components/PipelineStage.vue +95 -75
  60. package/packages/dashboard/src/components/PipelineView.vue +156 -129
  61. package/packages/dashboard/src/components/ProjectCard.vue +187 -0
  62. package/packages/dashboard/src/components/ProjectList.vue +210 -210
  63. package/packages/dashboard/src/components/ProjectOverview.vue +113 -139
  64. package/packages/dashboard/src/components/StageBadge.vue +67 -53
  65. package/packages/dashboard/src/components/StepCard.vue +94 -89
  66. package/packages/dashboard/src/components/VResizeHandle.vue +61 -0
  67. package/packages/dashboard/src/components/detail/DocsDetail.vue +48 -48
  68. package/packages/dashboard/src/components/detail/GitDetail.vue +61 -61
  69. package/packages/dashboard/src/components/detail/TechDetail.vue +43 -43
  70. package/packages/dashboard/src/composables/useDashboard.js +192 -164
  71. package/packages/dashboard/src/composables/useKeyboard.js +119 -119
  72. package/packages/dashboard/src/composables/useLayout.js +131 -0
  73. package/packages/dashboard/src/composables/useWebSocket.js +129 -129
  74. package/packages/dashboard/src/main.js +8 -8
  75. package/packages/dashboard/src/style.css +132 -132
  76. package/packages/dashboard/vite.config.js +18 -18
  77. package/src/brainstorm-postcheck.js +158 -0
  78. package/src/change-list.js +52 -0
  79. package/src/change-risk-profile.js +352 -0
  80. package/src/classify-change.js +73 -0
  81. package/src/constants.js +70 -0
  82. package/src/contract-matrix.js +278 -0
  83. package/src/db.js +201 -0
  84. package/src/endpoint-extractor.js +315 -0
  85. package/src/hooks/claude-pre-tool-use.cjs +125 -0
  86. package/src/hooks/worktree-guard.js +761 -0
  87. package/src/index.js +922 -252
  88. package/src/init.js +431 -375
  89. package/src/knowledge-match.js +130 -0
  90. package/src/migrate.js +117 -117
  91. package/src/modules.js +482 -0
  92. package/src/progress.js +1734 -508
  93. package/src/run.js +3465 -652
  94. package/src/scan-postcheck.js +387 -0
  95. package/src/setup.js +398 -460
  96. package/src/stage-contract.js +700 -0
  97. package/src/stages/archive.js +160 -54
  98. package/src/stages/brainstorm-auto.js +229 -0
  99. package/src/stages/brainstorm.js +645 -239
  100. package/src/stages/doctor.js +365 -312
  101. package/src/stages/execute.js +625 -264
  102. package/src/stages/explore.js +34 -0
  103. package/src/stages/index.js +29 -35
  104. package/src/stages/knowledge.js +498 -0
  105. package/src/stages/plan-postcheck.js +511 -0
  106. package/src/stages/plan.js +582 -279
  107. package/src/stages/propose.js +174 -115
  108. package/src/stages/quick.js +102 -63
  109. package/src/stages/scan.js +558 -141
  110. package/src/stages/status.js +65 -65
  111. package/src/stages/verify.js +322 -135
  112. package/src/sync.js +497 -0
  113. package/src/task-review.js +346 -0
  114. package/src/workflow.js +785 -0
  115. package/src/worktree-apply.js +549 -0
  116. package/src/worktree-deps.js +185 -0
  117. package/src/worktree.js +982 -0
  118. package/templates/workflows/archive-impact.yaml +79 -0
  119. package/templates/workflows/scan-docs.yaml +132 -0
  120. package/test/brainstorm-plan-contract.test.mjs +273 -0
  121. package/test/check-syntax.mjs +26 -0
  122. package/test/cli-top-level-aliases.test.mjs +174 -0
  123. package/test/contract-artifacts.test.mjs +323 -0
  124. package/test/decision-supersede.test.mjs +277 -0
  125. package/test/knowledge-match.test.mjs +231 -0
  126. package/test/plan-execute-contract.test.mjs +330 -0
  127. package/test/plan-optimization.test.mjs +572 -0
  128. package/test/platform-artifacts.test.mjs +190 -0
  129. package/test/platform-failure-samples.test.mjs +199 -0
  130. package/test/platform-recovery-chain.test.mjs +179 -0
  131. package/test/platform-recovery.test.mjs +167 -0
  132. package/test/platform-scan-p0.test.mjs +175 -0
  133. package/test/revision-v1.test.mjs +1145 -0
  134. package/test/run-sanitize-project-name.test.mjs +51 -0
  135. package/test/run-scan-postcheck-fail.test.mjs +64 -0
  136. package/test/run-scan-project-parse.test.mjs +200 -0
  137. package/test/run-tests.mjs +48 -0
  138. package/test/scan-docs-yaml-placeholders.test.mjs +84 -0
  139. package/test/scan-knowledge.test.mjs +175 -0
  140. package/test/scan-paths.test.mjs +68 -0
  141. package/test/scan-postcheck-project-priority.test.mjs +85 -0
  142. package/test/scan-postcheck.test.mjs +197 -0
  143. package/test/scan-workflow-anyfailed-block.test.mjs +52 -0
  144. package/test/spec-dir.test.mjs +206 -0
  145. package/test/stage-contract-failed-post-check.test.mjs +102 -0
  146. package/test/stage-contract.test.mjs +299 -0
  147. package/test/stage-definitions.test.mjs +39 -0
  148. package/test/wait-gates.test.mjs +501 -0
  149. package/test/workflow-spec-base.test.mjs +142 -0
  150. package/test/worktree-deps-provision.test.mjs +148 -0
  151. package/test/worktree-guard.test.mjs +136 -0
  152. package/test/worktree-native-overlay.test.mjs +188 -0
  153. package/.sillyspec/changes/archive/2026-04-08-derive-state/design.md +0 -97
  154. package/.sillyspec/changes/archive/2026-04-08-derive-state/plan.md +0 -51
  155. package/.sillyspec/changes/archive/2026-04-08-derive-state/proposal.md +0 -29
  156. package/.sillyspec/changes/archive/2026-04-08-derive-state/requirements.md +0 -34
  157. package/.sillyspec/changes/archive/2026-04-08-derive-state/tasks.md +0 -13
  158. package/.sillyspec/changes/archive/2026-04-08-derive-state/verify-result.md +0 -43
  159. package/.sillyspec/changes/auto-mode/design.md +0 -50
  160. package/.sillyspec/changes/auto-mode/proposal.md +0 -19
  161. package/.sillyspec/changes/auto-mode/requirements.md +0 -21
  162. package/.sillyspec/changes/auto-mode/tasks.md +0 -7
  163. package/.sillyspec/changes/brainstorm-archive/2026-04-05-dashboard-design.md +0 -206
  164. package/.sillyspec/changes/brainstorm-archive/2026-04-05-unified-docs-design.md +0 -199
  165. package/.sillyspec/changes/dashboard/design.md +0 -219
  166. package/.sillyspec/changes/dashboard/design.md.braindraft +0 -206
  167. package/.sillyspec/changes/run-command-design/design.md +0 -1230
  168. package/.sillyspec/changes/unified-docs-design/design.md +0 -199
  169. package/.sillyspec/docs/sillyspec/scan/.gitkeep +0 -0
  170. package/.sillyspec/knowledge/INDEX.md +0 -8
  171. package/.sillyspec/knowledge/uncategorized.md +0 -3
  172. package/.sillyspec/plans/2026-04-05-dashboard.md +0 -737
  173. package/.sillyspec/projects/sillyspec.yaml +0 -3
  174. package/dist/steps/brainstorm/01-load-context.md +0 -30
  175. package/dist/steps/brainstorm/02-reuse-check.md +0 -6
  176. package/dist/steps/brainstorm/03-prototype-analysis.md +0 -11
  177. package/dist/steps/brainstorm/04-module-split.md +0 -23
  178. package/dist/steps/brainstorm/05-dialog-explore.md +0 -8
  179. package/dist/steps/brainstorm/06-propose-approaches.md +0 -3
  180. package/dist/steps/brainstorm/07-present-design.md +0 -3
  181. package/dist/steps/brainstorm/08-write-design.md +0 -21
  182. package/dist/steps/brainstorm/09-self-review.md +0 -15
  183. package/dist/steps/brainstorm/10-user-confirm.md +0 -3
  184. package/dist/steps/brainstorm/11-output-spec.md +0 -7
  185. package/dist/steps/brainstorm/manifest.yaml +0 -26
  186. package/dist/steps/execute/01-load-context.md +0 -41
  187. package/dist/steps/execute/02-scan-conventions.md +0 -47
  188. package/dist/steps/execute/03-skill-mcp.md +0 -19
  189. package/dist/steps/execute/04-assign-task.md +0 -22
  190. package/dist/steps/execute/04b-prompt-template.md +0 -54
  191. package/dist/steps/execute/05-write-test.md +0 -7
  192. package/dist/steps/execute/06-write-code.md +0 -8
  193. package/dist/steps/execute/07-run-test.md +0 -26
  194. package/dist/steps/execute/08-fix-issues.md +0 -28
  195. package/dist/steps/execute/09-next-task.md +0 -33
  196. package/dist/steps/execute/manifest.yaml +0 -28
  197. package/dist/steps/plan/01-load-context.md +0 -22
  198. package/dist/steps/plan/02-anchor-confirm.md +0 -1
  199. package/dist/steps/plan/03-expand-tasks.md +0 -33
  200. package/dist/steps/plan/04-mark-order.md +0 -15
  201. package/dist/steps/plan/05-e2e-planning.md +0 -17
  202. package/dist/steps/plan/06-self-check.md +0 -16
  203. package/dist/steps/plan/07-save.md +0 -1
  204. package/dist/steps/plan/manifest.yaml +0 -18
  205. package/dist/steps/scan/01-env-detect.md +0 -51
  206. package/dist/steps/scan/02-tech-stack.md +0 -16
  207. package/dist/steps/scan/03-conventions.md +0 -16
  208. package/dist/steps/scan/04-structure.md +0 -19
  209. package/dist/steps/scan/05-quality.md +0 -18
  210. package/dist/steps/scan/06-complete.md +0 -49
  211. package/dist/steps/scan/manifest.yaml +0 -16
  212. package/dist/steps/verify/01-load-specs.md +0 -28
  213. package/dist/steps/verify/02-check-tasks.md +0 -1
  214. package/dist/steps/verify/03-check-design.md +0 -6
  215. package/dist/steps/verify/04-run-tests.md +0 -7
  216. package/dist/steps/verify/05-e2e-tests.md +0 -27
  217. package/dist/steps/verify/05b-e2e-fix.md +0 -33
  218. package/dist/steps/verify/06-code-quality.md +0 -25
  219. package/dist/steps/verify/07-lint-check.md +0 -27
  220. package/dist/steps/verify/08-output-report.md +0 -14
  221. package/dist/steps/verify/manifest.yaml +0 -22
  222. package/packages/dashboard/dist/assets/index-DGe8CqeP.css +0 -1
  223. package/src/derive.js +0 -147
  224. package/src/step.js +0 -543
@@ -1,48 +1,48 @@
1
- <template>
2
- <div v-if="data?.length" class="px-4 py-3">
3
- <div v-for="group in data" :key="group.key" class="mb-4">
4
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">
5
- {{ group.icon }} {{ group.label }} ({{ group.files.length }})
6
- </h4>
7
- <div class="space-y-1">
8
- <div
9
- v-for="f in group.files"
10
- :key="f.path"
11
- class="flex items-center gap-2 px-2 py-1.5 rounded text-[11px] cursor-pointer hover:bg-[#FEF3C7] transition-colors"
12
- @click="$emit('open-file', f)"
13
- >
14
- <svg class="w-3 h-3 flex-shrink-0" fill="none" stroke="#9CA3AF" viewBox="0 0 24 24">
15
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
16
- </svg>
17
- <span class="truncate" style="color: #1C1C1E;">{{ f.name }}</span>
18
- <span class="ml-auto flex-shrink-0 font-mono text-[10px]" style="color: #9CA3AF;">{{ formatSize(f.size) }}</span>
19
- <span class="flex-shrink-0 text-[10px]" style="color: #9CA3AF;">{{ relativeTime(f.mtime) }}</span>
20
- </div>
21
- </div>
22
- </div>
23
- </div>
24
- <n-empty v-else description="无文档" style="padding: 48px 0;" />
25
- </template>
26
-
27
- <script setup>
28
- defineProps({ data: { type: Array, default: () => [] } })
29
- defineEmits(['open-file'])
30
-
31
- function formatSize(bytes) {
32
- if (!bytes) return '—'
33
- if (bytes < 1024) return bytes + ' B'
34
- if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'
35
- return (bytes / 1048576).toFixed(1) + ' MB'
36
- }
37
-
38
- function relativeTime(iso) {
39
- if (!iso) return ''
40
- const d = new Date(iso)
41
- const diff = Date.now() - d.getTime()
42
- if (diff < 60000) return '刚刚'
43
- if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
44
- if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
45
- if (diff < 604800000) return `${Math.floor(diff / 86400000)} 天前`
46
- return `${d.getMonth() + 1}/${d.getDate()}`
47
- }
48
- </script>
1
+ <template>
2
+ <div v-if="data?.length" class="px-4 py-3">
3
+ <div v-for="group in data" :key="group.key" class="mb-4">
4
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">
5
+ {{ group.icon }} {{ group.label }} ({{ group.files.length }})
6
+ </h4>
7
+ <div class="space-y-1">
8
+ <div
9
+ v-for="f in group.files"
10
+ :key="f.path"
11
+ class="flex items-center gap-2 px-2 py-1.5 rounded text-[11px] cursor-pointer hover:bg-[#FEF3C7] transition-colors"
12
+ @click="$emit('open-file', f)"
13
+ >
14
+ <svg class="w-3 h-3 flex-shrink-0" fill="none" stroke="#9CA3AF" viewBox="0 0 24 24">
15
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
16
+ </svg>
17
+ <span class="truncate" style="color: #1C1C1E;">{{ f.name }}</span>
18
+ <span class="ml-auto flex-shrink-0 font-mono text-[10px]" style="color: #9CA3AF;">{{ formatSize(f.size) }}</span>
19
+ <span class="flex-shrink-0 text-[10px]" style="color: #9CA3AF;">{{ relativeTime(f.mtime) }}</span>
20
+ </div>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ <n-empty v-else description="无文档" style="padding: 48px 0;" />
25
+ </template>
26
+
27
+ <script setup>
28
+ defineProps({ data: { type: Array, default: () => [] } })
29
+ defineEmits(['open-file'])
30
+
31
+ function formatSize(bytes) {
32
+ if (!bytes) return '—'
33
+ if (bytes < 1024) return bytes + ' B'
34
+ if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB'
35
+ return (bytes / 1048576).toFixed(1) + ' MB'
36
+ }
37
+
38
+ function relativeTime(iso) {
39
+ if (!iso) return ''
40
+ const d = new Date(iso)
41
+ const diff = Date.now() - d.getTime()
42
+ if (diff < 60000) return '刚刚'
43
+ if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
44
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
45
+ if (diff < 604800000) return `${Math.floor(diff / 86400000)} 天前`
46
+ return `${d.getMonth() + 1}/${d.getDate()}`
47
+ }
48
+ </script>
@@ -1,61 +1,61 @@
1
- <template>
2
- <div v-if="data" class="px-4 py-3">
3
- <!-- Branch -->
4
- <div class="mb-4">
5
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-1.5 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">当前分支</h4>
6
- <div class="text-[18px] font-bold font-[JetBrains_Mono,monospace]" style="color: #D97706;">{{ data.branch || '—' }}</div>
7
- </div>
8
-
9
- <!-- Commits -->
10
- <div v-if="data.commits?.length" class="mb-4">
11
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">最近提交</h4>
12
- <div class="space-y-2">
13
- <div v-for="c in data.commits" :key="c.hash" class="px-3 py-2 rounded-md" style="background: #F5F5F7;">
14
- <div class="flex items-center gap-2">
15
- <span class="text-[11px] font-mono font-semibold" style="color: #D97706;">{{ c.hash }}</span>
16
- <span class="text-[11px] flex-1 truncate" style="color: #1C1C1E;">{{ c.message }}</span>
17
- </div>
18
- <div class="flex items-center gap-2 mt-1 text-[10px]" style="color: #6B7280;">
19
- <span>{{ c.author }}</span>
20
- <span>·</span>
21
- <span>{{ relativeTime(c.date) }}</span>
22
- </div>
23
- </div>
24
- </div>
25
- </div>
26
-
27
- <!-- Untracked -->
28
- <div v-if="data.untracked?.length">
29
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">未提交文件</h4>
30
- <div class="space-y-1">
31
- <div v-for="f in data.untracked" :key="f.file" class="flex items-center gap-2 px-2 py-1 rounded text-[11px] font-[JetBrains_Mono,monospace]">
32
- <span class="font-semibold w-5 text-center" :style="{ color: statusColor(f.status) }">{{ f.status }}</span>
33
- <span class="truncate" style="color: #1C1C1E;">{{ f.file }}</span>
34
- </div>
35
- </div>
36
- </div>
37
- </div>
38
- <n-empty v-else description="无 Git 信息" style="padding: 48px 0;" />
39
- </template>
40
-
41
- <script setup>
42
- defineProps({ data: { type: Object, default: null } })
43
-
44
- function statusColor(s) {
45
- if (s.includes('M')) return '#F59E0B'
46
- if (s.includes('A') || s === '?') return '#10B981'
47
- if (s.includes('D')) return '#EF4444'
48
- return '#9CA3AF'
49
- }
50
-
51
- function relativeTime(iso) {
52
- if (!iso) return ''
53
- const d = new Date(iso)
54
- const diff = Date.now() - d.getTime()
55
- if (diff < 60000) return '刚刚'
56
- if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
57
- if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
58
- if (diff < 604800000) return `${Math.floor(diff / 86400000)} 天前`
59
- return `${d.getMonth() + 1}/${d.getDate()}`
60
- }
61
- </script>
1
+ <template>
2
+ <div v-if="data" class="px-4 py-3">
3
+ <!-- Branch -->
4
+ <div class="mb-4">
5
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-1.5 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">当前分支</h4>
6
+ <div class="text-[18px] font-bold font-[JetBrains_Mono,monospace]" style="color: #D97706;">{{ data.branch || '—' }}</div>
7
+ </div>
8
+
9
+ <!-- Commits -->
10
+ <div v-if="data.commits?.length" class="mb-4">
11
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">最近提交</h4>
12
+ <div class="space-y-2">
13
+ <div v-for="c in data.commits" :key="c.hash" class="px-3 py-2 rounded-md" style="background: #F5F5F7;">
14
+ <div class="flex items-center gap-2">
15
+ <span class="text-[11px] font-mono font-semibold" style="color: #D97706;">{{ c.hash }}</span>
16
+ <span class="text-[11px] flex-1 truncate" style="color: #1C1C1E;">{{ c.message }}</span>
17
+ </div>
18
+ <div class="flex items-center gap-2 mt-1 text-[10px]" style="color: #6B7280;">
19
+ <span>{{ c.author }}</span>
20
+ <span>·</span>
21
+ <span>{{ relativeTime(c.date) }}</span>
22
+ </div>
23
+ </div>
24
+ </div>
25
+ </div>
26
+
27
+ <!-- Untracked -->
28
+ <div v-if="data.untracked?.length">
29
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">未提交文件</h4>
30
+ <div class="space-y-1">
31
+ <div v-for="f in data.untracked" :key="f.file" class="flex items-center gap-2 px-2 py-1 rounded text-[11px] font-[JetBrains_Mono,monospace]">
32
+ <span class="font-semibold w-5 text-center" :style="{ color: statusColor(f.status) }">{{ f.status }}</span>
33
+ <span class="truncate" style="color: #1C1C1E;">{{ f.file }}</span>
34
+ </div>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <n-empty v-else description="无 Git 信息" style="padding: 48px 0;" />
39
+ </template>
40
+
41
+ <script setup>
42
+ defineProps({ data: { type: Object, default: null } })
43
+
44
+ function statusColor(s) {
45
+ if (s.includes('M')) return '#F59E0B'
46
+ if (s.includes('A') || s === '?') return '#10B981'
47
+ if (s.includes('D')) return '#EF4444'
48
+ return '#9CA3AF'
49
+ }
50
+
51
+ function relativeTime(iso) {
52
+ if (!iso) return ''
53
+ const d = new Date(iso)
54
+ const diff = Date.now() - d.getTime()
55
+ if (diff < 60000) return '刚刚'
56
+ if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
57
+ if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
58
+ if (diff < 604800000) return `${Math.floor(diff / 86400000)} 天前`
59
+ return `${d.getMonth() + 1}/${d.getDate()}`
60
+ }
61
+ </script>
@@ -1,43 +1,43 @@
1
- <template>
2
- <div v-if="data" class="px-4 py-3">
3
- <!-- Frameworks -->
4
- <div v-if="data.frameworks?.length" class="mb-4">
5
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">识别框架</h4>
6
- <div class="flex flex-wrap gap-2">
7
- <n-tag v-for="f in data.frameworks" :key="f" type="warning" size="small" round>{{ f }}</n-tag>
8
- </div>
9
- </div>
10
-
11
- <!-- Dependencies -->
12
- <div v-if="depEntries.length" class="mb-4">
13
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">dependencies ({{ depEntries.length }})</h4>
14
- <div class="space-y-1">
15
- <div v-for="[name, ver] in depEntries" :key="name" class="flex items-center gap-2 px-2 py-1 rounded text-[11px]" style="background: #F5F5F7;">
16
- <span class="font-mono font-medium truncate" style="color: #1C1C1E;">{{ name }}</span>
17
- <span class="font-mono ml-auto flex-shrink-0" style="color: #9CA3AF;">{{ ver }}</span>
18
- </div>
19
- </div>
20
- </div>
21
-
22
- <!-- Dev Dependencies -->
23
- <div v-if="devDepEntries.length">
24
- <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">devDependencies ({{ devDepEntries.length }})</h4>
25
- <div class="space-y-1">
26
- <div v-for="[name, ver] in devDepEntries" :key="name" class="flex items-center gap-2 px-2 py-1 rounded text-[11px]" style="background: #F5F5F7;">
27
- <span class="font-mono font-medium truncate" style="color: #1C1C1E;">{{ name }}</span>
28
- <span class="font-mono ml-auto flex-shrink-0" style="color: #9CA3AF;">{{ ver }}</span>
29
- </div>
30
- </div>
31
- </div>
32
- </div>
33
- <n-empty v-else description="无依赖信息" style="padding: 48px 0;" />
34
- </template>
35
-
36
- <script setup>
37
- import { computed } from 'vue'
38
-
39
- const props = defineProps({ data: { type: Object, default: null } })
40
-
41
- const depEntries = computed(() => props.data ? Object.entries(props.data.dependencies || {}) : [])
42
- const devDepEntries = computed(() => props.data ? Object.entries(props.data.devDependencies || {}) : [])
43
- </script>
1
+ <template>
2
+ <div v-if="data" class="px-4 py-3">
3
+ <!-- Frameworks -->
4
+ <div v-if="data.frameworks?.length" class="mb-4">
5
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">识别框架</h4>
6
+ <div class="flex flex-wrap gap-2">
7
+ <n-tag v-for="f in data.frameworks" :key="f" type="warning" size="small" round>{{ f }}</n-tag>
8
+ </div>
9
+ </div>
10
+
11
+ <!-- Dependencies -->
12
+ <div v-if="depEntries.length" class="mb-4">
13
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">dependencies ({{ depEntries.length }})</h4>
14
+ <div class="space-y-1">
15
+ <div v-for="[name, ver] in depEntries" :key="name" class="flex items-center gap-2 px-2 py-1 rounded text-[11px]" style="background: #F5F5F7;">
16
+ <span class="font-mono font-medium truncate" style="color: #1C1C1E;">{{ name }}</span>
17
+ <span class="font-mono ml-auto flex-shrink-0" style="color: #9CA3AF;">{{ ver }}</span>
18
+ </div>
19
+ </div>
20
+ </div>
21
+
22
+ <!-- Dev Dependencies -->
23
+ <div v-if="devDepEntries.length">
24
+ <h4 class="text-[9px] font-semibold uppercase tracking-[0.2em] mb-2 font-[JetBrains_Mono,monospace]" style="color: #6B7280;">devDependencies ({{ devDepEntries.length }})</h4>
25
+ <div class="space-y-1">
26
+ <div v-for="[name, ver] in devDepEntries" :key="name" class="flex items-center gap-2 px-2 py-1 rounded text-[11px]" style="background: #F5F5F7;">
27
+ <span class="font-mono font-medium truncate" style="color: #1C1C1E;">{{ name }}</span>
28
+ <span class="font-mono ml-auto flex-shrink-0" style="color: #9CA3AF;">{{ ver }}</span>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ <n-empty v-else description="无依赖信息" style="padding: 48px 0;" />
34
+ </template>
35
+
36
+ <script setup>
37
+ import { computed } from 'vue'
38
+
39
+ const props = defineProps({ data: { type: Object, default: null } })
40
+
41
+ const depEntries = computed(() => props.data ? Object.entries(props.data.dependencies || {}) : [])
42
+ const devDepEntries = computed(() => props.data ? Object.entries(props.data.devDependencies || {}) : [])
43
+ </script>