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.
- 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 +125 -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 +431 -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 +625 -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 +511 -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 +330 -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/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,84 +1,93 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="h-12 flex items-center justify-between px-5 relative" style="background: rgba(17,17,19,0.9); backdrop-filter: blur(20px); border-top: 1px solid #F0F0F3;">
|
|
3
|
-
<!-- Ambient top glow -->
|
|
4
|
-
<div class="absolute inset-x-0 top-0 h-px" style="background: linear-gradient(90deg, transparent, rgba(251,191,36,0.1), transparent);"></div>
|
|
5
|
-
|
|
6
|
-
<!-- Left: Status -->
|
|
7
|
-
<div class="flex items-center gap-3">
|
|
8
|
-
<div v-if="project" class="flex items-center gap-2">
|
|
9
|
-
<span class="text-[11px] font-[JetBrains_Mono,monospace]" style="color: #6B7280;">{{ project.name }}</span>
|
|
10
|
-
<span style="color: #F0F0F3;">|</span>
|
|
11
|
-
<StageBadge v-if="project.state?.currentStage" :status="getProjectStatus()" :label="stageLabel()" />
|
|
12
|
-
</div>
|
|
13
|
-
<div v-else class="text-[10px] font-[JetBrains_Mono,monospace]" style="color: #D1D1D6;">
|
|
14
|
-
未选择项目
|
|
15
|
-
</div>
|
|
16
|
-
</div>
|
|
17
|
-
|
|
18
|
-
<!-- Center: Execution -->
|
|
19
|
-
<div class="flex items-center gap-2">
|
|
20
|
-
<div v-if="isExecuting" class="flex items-center gap-2">
|
|
21
|
-
<div class="w-1 h-1 rounded-full animate-pulse-dot" style="background: #D97706;" />
|
|
22
|
-
<span class="text-[10px] font-[JetBrains_Mono,monospace]" style="color: #D97706;">执行中...</span>
|
|
23
|
-
</div>
|
|
24
|
-
<div v-else-if="executionResult" class="flex items-center gap-1.5">
|
|
25
|
-
<span class="text-[10px] font-[JetBrains_Mono,monospace]" :style="{ color: executionResult.exitCode === 0 ? '#16A34A' : '#DC2626' }">
|
|
26
|
-
{{ executionResult.exitCode === 0 ? '● 完成' : `● 失败 (${executionResult.exitCode})` }}
|
|
27
|
-
</span>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<!-- Right: Actions -->
|
|
32
|
-
<div class="flex items-center gap-1">
|
|
33
|
-
<n-button quaternary size="tiny" @click="$emit('toggle-panel')" title="切换详情面板">
|
|
34
|
-
<template #icon>
|
|
35
|
-
<svg :class="['w-3.5 h-3.5 transition-transform duration-200', { 'rotate-180': !isPanelOpen }]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
37
|
-
</svg>
|
|
38
|
-
</template>
|
|
39
|
-
</n-button>
|
|
40
|
-
|
|
41
|
-
<n-button v-if="isExecuting" size="tiny" type="error" @click="$emit('kill')">
|
|
42
|
-
停止
|
|
43
|
-
</n-button>
|
|
44
|
-
|
|
45
|
-
<n-button quaternary size="tiny" @click="$emit('open-palette')" title="命令面板 (⌘K)">
|
|
46
|
-
<template #icon>
|
|
47
|
-
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
48
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
49
|
-
</svg>
|
|
50
|
-
</template>
|
|
51
|
-
</n-button>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
</template>
|
|
55
|
-
|
|
56
|
-
<script setup>
|
|
57
|
-
import StageBadge from './StageBadge.vue'
|
|
58
|
-
|
|
59
|
-
const props = defineProps({
|
|
60
|
-
project: { type: Object, default: null },
|
|
61
|
-
isExecuting: { type: Boolean, default: false },
|
|
62
|
-
executionResult: { type: Object, default: null },
|
|
63
|
-
isPanelOpen: { type: Boolean, default: true }
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
const emit = defineEmits(['execute', 'kill', 'toggle-panel', 'open-palette'])
|
|
67
|
-
|
|
68
|
-
function getProjectStatus() {
|
|
69
|
-
if (!props.project?.state) return 'pending'
|
|
70
|
-
const stage = props.project.state.currentStage
|
|
71
|
-
const steps = props.project.state.progress?.stages?.[stage]?.steps || []
|
|
72
|
-
if (steps.some(s => s.status === 'failed')) return 'failed'
|
|
73
|
-
if (steps.some(s => s.status === 'blocked')) return 'blocked'
|
|
74
|
-
if (steps.some(s => s.status === 'in-progress')) return 'in-progress'
|
|
75
|
-
if (steps.every(s => s.status === 'completed')) return 'completed'
|
|
76
|
-
return 'pending'
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function stageLabel() {
|
|
80
|
-
const stage = props.project?.state?.currentStage
|
|
81
|
-
const labels = {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-12 flex items-center justify-between px-5 relative" style="background: rgba(17,17,19,0.9); backdrop-filter: blur(20px); border-top: 1px solid #F0F0F3;">
|
|
3
|
+
<!-- Ambient top glow -->
|
|
4
|
+
<div class="absolute inset-x-0 top-0 h-px" style="background: linear-gradient(90deg, transparent, rgba(251,191,36,0.1), transparent);"></div>
|
|
5
|
+
|
|
6
|
+
<!-- Left: Status -->
|
|
7
|
+
<div class="flex items-center gap-3">
|
|
8
|
+
<div v-if="project" class="flex items-center gap-2">
|
|
9
|
+
<span class="text-[11px] font-[JetBrains_Mono,monospace]" style="color: #6B7280;">{{ project.name }}</span>
|
|
10
|
+
<span style="color: #F0F0F3;">|</span>
|
|
11
|
+
<StageBadge v-if="project.state?.currentStage" :status="getProjectStatus()" :label="stageLabel()" />
|
|
12
|
+
</div>
|
|
13
|
+
<div v-else class="text-[10px] font-[JetBrains_Mono,monospace]" style="color: #D1D1D6;">
|
|
14
|
+
未选择项目
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<!-- Center: Execution -->
|
|
19
|
+
<div class="flex items-center gap-2">
|
|
20
|
+
<div v-if="isExecuting" class="flex items-center gap-2">
|
|
21
|
+
<div class="w-1 h-1 rounded-full animate-pulse-dot" style="background: #D97706;" />
|
|
22
|
+
<span class="text-[10px] font-[JetBrains_Mono,monospace]" style="color: #D97706;">执行中...</span>
|
|
23
|
+
</div>
|
|
24
|
+
<div v-else-if="executionResult" class="flex items-center gap-1.5">
|
|
25
|
+
<span class="text-[10px] font-[JetBrains_Mono,monospace]" :style="{ color: executionResult.exitCode === 0 ? '#16A34A' : '#DC2626' }">
|
|
26
|
+
{{ executionResult.exitCode === 0 ? '● 完成' : `● 失败 (${executionResult.exitCode})` }}
|
|
27
|
+
</span>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<!-- Right: Actions -->
|
|
32
|
+
<div class="flex items-center gap-1">
|
|
33
|
+
<n-button quaternary size="tiny" @click="$emit('toggle-panel')" title="切换详情面板">
|
|
34
|
+
<template #icon>
|
|
35
|
+
<svg :class="['w-3.5 h-3.5 transition-transform duration-200', { 'rotate-180': !isPanelOpen }]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
36
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
37
|
+
</svg>
|
|
38
|
+
</template>
|
|
39
|
+
</n-button>
|
|
40
|
+
|
|
41
|
+
<n-button v-if="isExecuting" size="tiny" type="error" @click="$emit('kill')">
|
|
42
|
+
停止
|
|
43
|
+
</n-button>
|
|
44
|
+
|
|
45
|
+
<n-button quaternary size="tiny" @click="$emit('open-palette')" title="命令面板 (⌘K)">
|
|
46
|
+
<template #icon>
|
|
47
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
48
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
|
49
|
+
</svg>
|
|
50
|
+
</template>
|
|
51
|
+
</n-button>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script setup>
|
|
57
|
+
import StageBadge from './StageBadge.vue'
|
|
58
|
+
|
|
59
|
+
const props = defineProps({
|
|
60
|
+
project: { type: Object, default: null },
|
|
61
|
+
isExecuting: { type: Boolean, default: false },
|
|
62
|
+
executionResult: { type: Object, default: null },
|
|
63
|
+
isPanelOpen: { type: Boolean, default: true }
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const emit = defineEmits(['execute', 'kill', 'toggle-panel', 'open-palette'])
|
|
67
|
+
|
|
68
|
+
function getProjectStatus() {
|
|
69
|
+
if (!props.project?.state) return 'pending'
|
|
70
|
+
const stage = props.project.state.currentStage
|
|
71
|
+
const steps = props.project.state.progress?.stages?.[stage]?.steps || []
|
|
72
|
+
if (steps.some(s => s.status === 'failed')) return 'failed'
|
|
73
|
+
if (steps.some(s => s.status === 'blocked')) return 'blocked'
|
|
74
|
+
if (steps.some(s => s.status === 'in-progress')) return 'in-progress'
|
|
75
|
+
if (steps.every(s => s.status === 'completed')) return 'completed'
|
|
76
|
+
return 'pending'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function stageLabel() {
|
|
80
|
+
const stage = props.project?.state?.currentStage
|
|
81
|
+
const labels = {
|
|
82
|
+
scan: '代码扫描',
|
|
83
|
+
brainstorm: '头脑风暴',
|
|
84
|
+
plan: '规划',
|
|
85
|
+
execute: '执行',
|
|
86
|
+
verify: '验证',
|
|
87
|
+
archive: '归档',
|
|
88
|
+
quick: '快速任务',
|
|
89
|
+
explore: '自由探索'
|
|
90
|
+
}
|
|
91
|
+
return labels[stage] || stage || '未知'
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
@@ -1,92 +1,96 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<n-modal :show="isOpen" @update:show="$emit('close')" :mask-closable="true" transform-origin="center" style="max-width: 480px;">
|
|
3
|
-
<div class="overflow-hidden rounded-md" style="background: #FFFFFF; border: 1px solid #E5E5EA; box-shadow: 0 25px 60px rgba(0,0,0,0.1);">
|
|
4
|
-
<!-- Search -->
|
|
5
|
-
<div class="px-4 py-3" style="border-bottom: 1px solid #F0F0F3;">
|
|
6
|
-
<n-input v-model:value="searchQuery" placeholder="搜索项目或命令..." ref="searchInput" @keydown="handleKeydown" size="small">
|
|
7
|
-
<template #prefix>
|
|
8
|
-
<svg class="w-3.5 h-3.5" style="color: #6B7280;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
9
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
10
|
-
</svg>
|
|
11
|
-
</template>
|
|
12
|
-
</n-input>
|
|
13
|
-
</div>
|
|
14
|
-
|
|
15
|
-
<!-- Results -->
|
|
16
|
-
<div class="max-h-72 overflow-y-auto">
|
|
17
|
-
<n-empty v-if="filteredItems.length === 0" description="无结果" style="padding: 40px 0;" />
|
|
18
|
-
<div v-else class="py-0.5">
|
|
19
|
-
<div
|
|
20
|
-
v-for="(item, index) in filteredItems"
|
|
21
|
-
:key="item.id"
|
|
22
|
-
:class="['px-4 py-2.5 cursor-pointer transition-colors duration-100 flex items-center gap-3']"
|
|
23
|
-
:style="{ background: selectedIndex === index ? 'rgba(217,119,6,0.06)' : 'transparent' }"
|
|
24
|
-
@click="selectItem(item)"
|
|
25
|
-
@mouseenter="selectedIndex = index"
|
|
26
|
-
>
|
|
27
|
-
<div class="w-6 h-6 rounded-sm flex items-center justify-center text-[10px] font-[JetBrains_Mono,monospace] flex-shrink-0" style="background: #F0F0F3; border: 1px solid #F0F0F3; color: #6B7280;">
|
|
28
|
-
{{ item.type === 'project' ? '□' : '◇' }}
|
|
29
|
-
</div>
|
|
30
|
-
<div class="flex-1 min-w-0">
|
|
31
|
-
<div class="text-[12px] font-medium truncate font-[JetBrains_Mono,monospace]" style="color: #1C1C1E;">{{ item.title }}</div>
|
|
32
|
-
<div class="text-[10px] truncate" style="color: #6B7280;">{{ item.subtitle }}</div>
|
|
33
|
-
</div>
|
|
34
|
-
<StageBadge v-if="item.status" :status="item.status" size="sm" />
|
|
35
|
-
</div>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
|
|
39
|
-
<!-- Footer -->
|
|
40
|
-
<div class="px-4 py-2 flex items-center gap-4 text-[9px] font-mono-log" style="border-top: 1px solid #F0F0F3; color: #D1D1D6;">
|
|
41
|
-
<span><kbd class="px-1 rounded-sm" style="background: #F0F0F3; border: 1px solid #F0F0F3;">↑↓</kbd> 导航</span>
|
|
42
|
-
<span><kbd class="px-1 rounded-sm" style="background: #F0F0F3; border: 1px solid #F0F0F3;">↵</kbd> 打开</span>
|
|
43
|
-
<span><kbd class="px-1 rounded-sm" style="background: #F0F0F3; border: 1px solid #F0F0F3;">esc</kbd> 关闭</span>
|
|
44
|
-
</div>
|
|
45
|
-
</div>
|
|
46
|
-
</n-modal>
|
|
47
|
-
</template>
|
|
48
|
-
|
|
49
|
-
<script setup>
|
|
50
|
-
import { ref, computed, watch, nextTick } from 'vue'
|
|
51
|
-
import StageBadge from './StageBadge.vue'
|
|
52
|
-
|
|
53
|
-
const props = defineProps({ isOpen: { type: Boolean, default: false }, projects: { type: Array, default: () => [] } })
|
|
54
|
-
const emit = defineEmits(['close', 'select-project', 'select-stage'])
|
|
55
|
-
|
|
56
|
-
const searchQuery = ref('')
|
|
57
|
-
const searchInput = ref(null)
|
|
58
|
-
const selectedIndex = ref(0)
|
|
59
|
-
|
|
60
|
-
const stageNames = [
|
|
61
|
-
{ id: '
|
|
62
|
-
{ id: '
|
|
63
|
-
{ id: '
|
|
64
|
-
{ id: '
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<n-modal :show="isOpen" @update:show="$emit('close')" :mask-closable="true" transform-origin="center" style="max-width: 480px;">
|
|
3
|
+
<div class="overflow-hidden rounded-md" style="background: #FFFFFF; border: 1px solid #E5E5EA; box-shadow: 0 25px 60px rgba(0,0,0,0.1);">
|
|
4
|
+
<!-- Search -->
|
|
5
|
+
<div class="px-4 py-3" style="border-bottom: 1px solid #F0F0F3;">
|
|
6
|
+
<n-input v-model:value="searchQuery" placeholder="搜索项目或命令..." ref="searchInput" @keydown="handleKeydown" size="small">
|
|
7
|
+
<template #prefix>
|
|
8
|
+
<svg class="w-3.5 h-3.5" style="color: #6B7280;" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
9
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
10
|
+
</svg>
|
|
11
|
+
</template>
|
|
12
|
+
</n-input>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<!-- Results -->
|
|
16
|
+
<div class="max-h-72 overflow-y-auto">
|
|
17
|
+
<n-empty v-if="filteredItems.length === 0" description="无结果" style="padding: 40px 0;" />
|
|
18
|
+
<div v-else class="py-0.5">
|
|
19
|
+
<div
|
|
20
|
+
v-for="(item, index) in filteredItems"
|
|
21
|
+
:key="item.id"
|
|
22
|
+
:class="['px-4 py-2.5 cursor-pointer transition-colors duration-100 flex items-center gap-3']"
|
|
23
|
+
:style="{ background: selectedIndex === index ? 'rgba(217,119,6,0.06)' : 'transparent' }"
|
|
24
|
+
@click="selectItem(item)"
|
|
25
|
+
@mouseenter="selectedIndex = index"
|
|
26
|
+
>
|
|
27
|
+
<div class="w-6 h-6 rounded-sm flex items-center justify-center text-[10px] font-[JetBrains_Mono,monospace] flex-shrink-0" style="background: #F0F0F3; border: 1px solid #F0F0F3; color: #6B7280;">
|
|
28
|
+
{{ item.type === 'project' ? '□' : '◇' }}
|
|
29
|
+
</div>
|
|
30
|
+
<div class="flex-1 min-w-0">
|
|
31
|
+
<div class="text-[12px] font-medium truncate font-[JetBrains_Mono,monospace]" style="color: #1C1C1E;">{{ item.title }}</div>
|
|
32
|
+
<div class="text-[10px] truncate" style="color: #6B7280;">{{ item.subtitle }}</div>
|
|
33
|
+
</div>
|
|
34
|
+
<StageBadge v-if="item.status" :status="item.status" size="sm" />
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<!-- Footer -->
|
|
40
|
+
<div class="px-4 py-2 flex items-center gap-4 text-[9px] font-mono-log" style="border-top: 1px solid #F0F0F3; color: #D1D1D6;">
|
|
41
|
+
<span><kbd class="px-1 rounded-sm" style="background: #F0F0F3; border: 1px solid #F0F0F3;">↑↓</kbd> 导航</span>
|
|
42
|
+
<span><kbd class="px-1 rounded-sm" style="background: #F0F0F3; border: 1px solid #F0F0F3;">↵</kbd> 打开</span>
|
|
43
|
+
<span><kbd class="px-1 rounded-sm" style="background: #F0F0F3; border: 1px solid #F0F0F3;">esc</kbd> 关闭</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</n-modal>
|
|
47
|
+
</template>
|
|
48
|
+
|
|
49
|
+
<script setup>
|
|
50
|
+
import { ref, computed, watch, nextTick } from 'vue'
|
|
51
|
+
import StageBadge from './StageBadge.vue'
|
|
52
|
+
|
|
53
|
+
const props = defineProps({ isOpen: { type: Boolean, default: false }, projects: { type: Array, default: () => [] } })
|
|
54
|
+
const emit = defineEmits(['close', 'select-project', 'select-stage'])
|
|
55
|
+
|
|
56
|
+
const searchQuery = ref('')
|
|
57
|
+
const searchInput = ref(null)
|
|
58
|
+
const selectedIndex = ref(0)
|
|
59
|
+
|
|
60
|
+
const stageNames = [
|
|
61
|
+
{ id: 'scan', name: '代码扫描' },
|
|
62
|
+
{ id: 'brainstorm', name: '头脑风暴' },
|
|
63
|
+
{ id: 'plan', name: '规划' },
|
|
64
|
+
{ id: 'execute', name: '执行' },
|
|
65
|
+
{ id: 'verify', name: '验证' },
|
|
66
|
+
{ id: 'archive', name: '归档' },
|
|
67
|
+
{ id: 'quick', name: '快速任务' },
|
|
68
|
+
{ id: 'explore', name: '自由探索' }
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
const filteredItems = computed(() => {
|
|
72
|
+
const items = []
|
|
73
|
+
for (const project of props.projects) {
|
|
74
|
+
const pm = !searchQuery.value || project.name.toLowerCase().includes(searchQuery.value.toLowerCase()) || project.path.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
75
|
+
if (pm) items.push({ id: `project-${project.name}`, type: 'project', title: project.name, subtitle: project.path, data: project, status: getProjectStatus(project) })
|
|
76
|
+
for (const stage of stageNames) {
|
|
77
|
+
const sm = !searchQuery.value || stage.name.toLowerCase().includes(searchQuery.value.toLowerCase()) || stage.id.toLowerCase().includes(searchQuery.value.toLowerCase())
|
|
78
|
+
if (sm || pm) items.push({ id: `stage-${project.name}-${stage.id}`, type: 'stage', title: `${project.name} / ${stage.name}`, subtitle: `Jump to ${stage.name}`, data: { project, stage: stage.id }, status: getStageStatus(project, stage.id) })
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return items
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
function getProjectStatus(p) { const s = p.state?.progress?.stages?.[p.state?.currentStage]?.steps || []; if (s.some(x => x.status === 'failed')) return 'failed'; if (s.some(x => x.status === 'in-progress')) return 'in-progress'; if (s.every(x => x.status === 'completed')) return 'completed'; return 'pending' }
|
|
85
|
+
function getStageStatus(p, id) { const s = p.state?.progress?.stages?.[id]?.steps || []; if (!s.length) return 'pending'; if (s.some(x => x.status === 'failed')) return 'failed'; if (s.some(x => x.status === 'in-progress')) return 'in-progress'; if (s.every(x => x.status === 'completed')) return 'completed'; return 'pending' }
|
|
86
|
+
function close() { searchQuery.value = ''; selectedIndex.value = 0; emit('close') }
|
|
87
|
+
function selectItem(item) { if (item.type === 'project') emit('select-project', item.data); else emit('select-stage', item.data); close() }
|
|
88
|
+
function handleKeydown(e) {
|
|
89
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); selectedIndex.value = Math.min(selectedIndex.value + 1, filteredItems.value.length - 1) }
|
|
90
|
+
else if (e.key === 'ArrowUp') { e.preventDefault(); selectedIndex.value = Math.max(selectedIndex.value - 1, 0) }
|
|
91
|
+
else if (e.key === 'Enter') { e.preventDefault(); if (filteredItems.value[selectedIndex.value]) selectItem(filteredItems.value[selectedIndex.value]) }
|
|
92
|
+
else if (e.key === 'Escape') { e.preventDefault(); close() }
|
|
93
|
+
}
|
|
94
|
+
watch(filteredItems, () => { selectedIndex.value = 0 })
|
|
95
|
+
watch(() => props.isOpen, (v) => { if (v) { nextTick(() => { searchInput.value?.focus() }) } })
|
|
96
|
+
</script>
|