@boyingliu01/xp-gate 0.7.0 → 0.7.1
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/bin/xp-gate.js +4 -9
- package/lib/__tests__/audit-log.test.ts +107 -0
- package/lib/__tests__/detect-deps.test.js +179 -42
- package/lib/__tests__/doctor.test.js +1 -0
- package/lib/__tests__/gate-audit.test.ts +59 -0
- package/lib/__tests__/init.test.js +15 -0
- package/lib/__tests__/install-skill.test.js +1 -45
- package/lib/__tests__/ui-detector.test.ts +122 -8
- package/lib/__tests__/ui-review.test.ts +119 -0
- package/lib/__tests__/uninstall.test.js +1 -0
- package/lib/detect-deps.js +178 -37
- package/lib/doctor.js +7 -9
- package/lib/gate-audit.ts +26 -42
- package/lib/init.js +42 -13
- package/lib/install-skill.js +20 -48
- package/lib/rollback.js +1 -16
- package/lib/shared-paths.js +30 -0
- package/lib/shared-utils.js +25 -0
- package/lib/ui-detector.ts +3 -3
- package/lib/ui-review.ts +58 -53
- package/lib/uninstall.js +8 -9
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/skills/delphi-review/SKILL.md +112 -77
- package/skills/ralph-loop/SKILL.md +165 -34
- package/skills/sprint-flow/SKILL.md +428 -105
- package/skills/sprint-flow/references/components/skill-invocations.md +1 -1
- package/skills/sprint-flow/references/components/tool-descriptions.md +1 -1
- package/skills/sprint-flow/references/force-levels.md +203 -0
- package/skills/sprint-flow/references/phase-1-plan.md +4 -4
- package/skills/sprint-flow/references/phase-minus-0-5-auto-estimate.md +20 -1
- package/skills/sprint-flow/templates/auto-estimate-output-template.md +7 -5
- package/skills/sprint-flow/templates/sprint-progress-template.md +151 -0
- package/skills/test-specification-alignment/SKILL.md +98 -24
- package/plugins/qoder/AGENTS.md +0 -93
- package/plugins/qoder/README.md +0 -87
- package/plugins/qoder/widgets/quality-report.html +0 -163
- package/plugins/qoder/widgets/sprint-dashboard.html +0 -172
- package/skills/delphi-review/references/qoder-multi-model.md +0 -191
- package/skills/sprint-flow/references/qoder-adaptation.md +0 -173
package/plugins/qoder/AGENTS.md
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# QODER PLUGIN KNOWLEDGE BASE
|
|
2
|
-
|
|
3
|
-
**Generated:** 2026-06-03
|
|
4
|
-
**Version:** v0.6.0
|
|
5
|
-
|
|
6
|
-
## OVERVIEW
|
|
7
|
-
Cross-platform plugin for Qoder IDE — shared skills, Qoder-specific enhancements (genui widgets, Canvas, Memory, CodeReview subagent, browser-use MCP). No hooks.json (Qoder uses skill-embedded gates instead of file-system event hooks).
|
|
8
|
-
|
|
9
|
-
## STRUCTURE
|
|
10
|
-
```
|
|
11
|
-
plugins/qoder/
|
|
12
|
-
├── README.md # Installation and usage guide
|
|
13
|
-
├── AGENTS.md # This knowledge base
|
|
14
|
-
├── skills/ # Auto-populated by build-plugin.sh (8 skills)
|
|
15
|
-
│ ├── sprint-flow/SKILL.md # 7-phase pipeline + Qoder Pre-Edit Gate + Agent mapping
|
|
16
|
-
│ ├── delphi-review/SKILL.md # Multi-expert consensus + Qoder multi-model adaptation
|
|
17
|
-
│ ├── test-specification-alignment/SKILL.md # 2-phase test-spec verification
|
|
18
|
-
│ ├── ralph-loop/SKILL.md # REQ-level iterative build + Memory integration
|
|
19
|
-
│ ├── test-driven-development/SKILL.md # TDD enforcement (zero modification)
|
|
20
|
-
│ ├── improve-codebase-architecture/SKILL.md # Architecture health + Canvas output
|
|
21
|
-
│ ├── to-issues/SKILL.md # Vertical slice issue splitting (zero modification)
|
|
22
|
-
│ └── admin-template-guidelines/SKILL.md # 6 maintainability rules (zero modification)
|
|
23
|
-
├── widgets/ # genui show_widget templates
|
|
24
|
-
│ ├── quality-report.html # Quality gate report dashboard
|
|
25
|
-
│ └── sprint-dashboard.html # Sprint status board
|
|
26
|
-
└── references/
|
|
27
|
-
└── qoder-adaptation.md # Platform differences and adaptation rules
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## WHERE TO LOOK
|
|
31
|
-
| Task | Location | Notes |
|
|
32
|
-
|------|----------|-------|
|
|
33
|
-
| Installation | README.md | User-level (~/.qoder/skills/) and project-level (.qoder/skills/) |
|
|
34
|
-
| Skill adaptations | skills/*/SKILL.md | Qoder-specific paragraphs marked with "Qoder" header |
|
|
35
|
-
| Pre-Edit Gate | skills/sprint-flow/SKILL.md | Replaces Claude Code's delphi-review-guard.sh hook |
|
|
36
|
-
| Multi-model review | skills/delphi-review/references/qoder-multi-model.md | Expert→model mapping via Qoder Agent subagents |
|
|
37
|
-
| Agent dispatch | skills/sprint-flow/SKILL.md | Phase→Qoder Agent mapping table |
|
|
38
|
-
| External skill fallback | skills/sprint-flow/references/qoder-adaptation.md | superpowers/gstack replacements |
|
|
39
|
-
| Quality widget | widgets/quality-report.html | genui show_widget template for Phase 3 results |
|
|
40
|
-
| Sprint widget | widgets/sprint-dashboard.html | genui show_widget template for sprint status |
|
|
41
|
-
| Memory integration | skills/ralph-loop/SKILL.md | UpdateMemory for permanent/contextual learnings |
|
|
42
|
-
| CodeReview | skills/sprint-flow/SKILL.md | Phase 2 step 4 + Phase 3 code walkthrough |
|
|
43
|
-
| Browser MCP | skills/sprint-flow/references/qoder-adaptation.md | browser-use replaces gstack/browse |
|
|
44
|
-
|
|
45
|
-
## CONVENTIONS
|
|
46
|
-
- Plugin skills auto-populated by build-plugin.sh --platform qoder — never edit plugins/qoder/skills/ directly
|
|
47
|
-
- Qoder-specific skill modifications live in source skills/ directory (marked with "## Qoder" sections)
|
|
48
|
-
- No hooks.json — Qoder lacks file-system event hooks; gates embedded in skill instructions
|
|
49
|
-
- genui widgets use show_widget MCP tool with widget_path template mode
|
|
50
|
-
- Memory system replaces gstack/learn for learnings persistence
|
|
51
|
-
- CodeReview subagent replaces superpowers/requesting-code-review
|
|
52
|
-
- browser-use MCP replaces gstack/browse for Phase 3 browser testing
|
|
53
|
-
- External skills (superpowers/gstack) replaced by orchestrator inline execution or Qoder native capabilities
|
|
54
|
-
|
|
55
|
-
## ANTI-PATTERNS (THIS PLUGIN)
|
|
56
|
-
- Do NOT edit plugins/qoder/skills/ directly — rebuild from source via build-plugin.sh
|
|
57
|
-
- Do NOT assume xp-gate CLI is installed — skills must degrade gracefully
|
|
58
|
-
- Do NOT create hooks.json for Qoder — platform does not support file-system event hooks
|
|
59
|
-
- Do NOT use curl for API calls — curl is in Qoder's command deny list; use PowerShell Invoke-RestMethod or Qoder's built-in multi-model capability
|
|
60
|
-
- Do NOT reference OpenCode task() API in Qoder skills — use Agent tool (Browser/CodeReview/plan-agent) instead
|
|
61
|
-
|
|
62
|
-
## UNIQUE STYLES
|
|
63
|
-
- Qoder: SKILL.md + genui widgets + Canvas + Memory + CodeReview subagent + browser-use MCP
|
|
64
|
-
- Pre-Edit Gate: skill-embedded mandatory check replaces Claude Code's physical hook blocking
|
|
65
|
-
- Multi-model review: Qoder Agent subagent dispatch (plan-agent/CodeReview) replaces OpenCode task() API
|
|
66
|
-
- Graceful degradation: skills work even without xp-gate CLI installed
|
|
67
|
-
- Shared skill source: one SKILL.md → copied to Claude Code, OpenCode, and Qoder platforms
|
|
68
|
-
|
|
69
|
-
## COMMANDS
|
|
70
|
-
```bash
|
|
71
|
-
# Build Qoder plugin
|
|
72
|
-
npm run build:qoder-plugin # Build Qoder plugin with all 8 skills
|
|
73
|
-
bash scripts/build-plugin.sh --platform qoder # Same, via script
|
|
74
|
-
|
|
75
|
-
# Install skills
|
|
76
|
-
bash scripts/install-qoder-skills.sh --global # User-level: ~/.qoder/skills/
|
|
77
|
-
bash scripts/install-qoder-skills.sh --local # Project-level: .qoder/skills/
|
|
78
|
-
|
|
79
|
-
# Test plugin
|
|
80
|
-
bash scripts/test-plugins.sh # Includes Qoder platform tests
|
|
81
|
-
|
|
82
|
-
# CLI skill install
|
|
83
|
-
xp-gate install-skill sprint-flow --platform qoder --global
|
|
84
|
-
xp-gate install-skill delphi-review --platform qoder --local
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## NOTES
|
|
88
|
-
- v0.6.0+: Qoder plugin support introduced
|
|
89
|
-
- Qoder plugin: 8 skills (all source skills, including admin-template-guidelines)
|
|
90
|
-
- Claude Code plugin: 7 skills (no admin-template-guidelines)
|
|
91
|
-
- OpenCode plugin: 7 skills + 3 custom tools (gate-check, gate-principles, gate-arch)
|
|
92
|
-
- build-plugin.sh validates 8 expected skills for Qoder platform
|
|
93
|
-
- test-plugins.sh: includes Qoder-specific test cases (widgets, no hooks.json)
|
package/plugins/qoder/README.md
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# XP-Gate Qoder Plugin
|
|
2
|
-
|
|
3
|
-
Qoder IDE plugin exposing xp-gate quality gates, AI workflow skills, and visual dashboards.
|
|
4
|
-
|
|
5
|
-
## Skills (8 AI Workflow Skills)
|
|
6
|
-
|
|
7
|
-
| Skill | Trigger | Description |
|
|
8
|
-
|-------|---------|-------------|
|
|
9
|
-
| sprint-flow | `/sprint-flow "需求"` | 7-phase pipeline: THINK→PLAN→BUILD→REVIEW→ACCEPT→FEEDBACK→SHIP |
|
|
10
|
-
| delphi-review | `/delphi-review` | Multi-expert anonymous consensus review (design + code-walkthrough) |
|
|
11
|
-
| test-specification-alignment | `/test-specification-alignment` | 2-phase test-spec verification |
|
|
12
|
-
| ralph-loop | Via sprint-flow Phase 2 | REQ-level iterative build (saves 40-67% tokens) |
|
|
13
|
-
| test-driven-development | `/test-driven-development` | TDD enforcement (RED→GREEN→REFACTOR) |
|
|
14
|
-
| improve-codebase-architecture | `/improve-codebase-architecture` | Architecture health checks |
|
|
15
|
-
| to-issues | `/to-issues` | Vertical slice issue splitting |
|
|
16
|
-
| admin-template-guidelines | Via sprint-flow BUILD | 6 maintainability rules for admin templates |
|
|
17
|
-
|
|
18
|
-
## Qoder-Specific Enhancements
|
|
19
|
-
|
|
20
|
-
- **genui Widgets**: Quality report dashboard and Sprint status board (via `show_widget`)
|
|
21
|
-
- **CodeReview Subagent**: Deep integration for Phase 2 blind review and Phase 3 code walkthrough
|
|
22
|
-
- **browser-use MCP**: Phase 3 browser automation testing (replaces gstack/browse)
|
|
23
|
-
- **Memory System**: Learnings persistence via Qoder's built-in memory (replaces gstack/learn)
|
|
24
|
-
- **Canvas Output**: Architecture relationship diagrams (via `.canvas.tsx`)
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
|
|
28
|
-
### Option 1: User-Level (Global, All Projects)
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
# Via xp-gate CLI
|
|
32
|
-
xp-gate install-skill sprint-flow --platform qoder --global
|
|
33
|
-
xp-gate install-skill delphi-review --platform qoder --global
|
|
34
|
-
|
|
35
|
-
# Or via install script (all 8 skills)
|
|
36
|
-
bash scripts/install-qoder-skills.sh --global
|
|
37
|
-
# Installs to: ~/.qoder/skills/
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Option 2: Project-Level (Current Project Only)
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
# Via install script
|
|
44
|
-
bash scripts/install-qoder-skills.sh --local
|
|
45
|
-
# Installs to: .qoder/skills/
|
|
46
|
-
|
|
47
|
-
# Or via build script
|
|
48
|
-
bash scripts/build-plugin.sh --platform qoder
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Option 3: Build from Source
|
|
52
|
-
|
|
53
|
-
```bash
|
|
54
|
-
npm run build:qoder-plugin
|
|
55
|
-
# Produces plugins/qoder/ with all skills + widgets
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Requirements
|
|
59
|
-
|
|
60
|
-
- Qoder IDE
|
|
61
|
-
- xp-gate npm package installed globally (for git hooks: `npm install -g @boyingliu01/xp-gate && xp-gate init`)
|
|
62
|
-
- Node.js ≥ 18 (for principles checker)
|
|
63
|
-
|
|
64
|
-
## Pre-Edit Gate (Replaces Claude Code Hooks)
|
|
65
|
-
|
|
66
|
-
Qoder does not have file-system event hooks like Claude Code's `PreToolUse`/`PostToolUse`. Instead, xp-gate skills embed **mandatory pre-edit checks** in their SKILL.md instructions:
|
|
67
|
-
|
|
68
|
-
- **Delphi Gate**: Before any code edit in Phase 2+, skill verifies `.sprint-state/delphi-reviewed.json` verdict is `APPROVED`
|
|
69
|
-
- **Principles Check**: After each REQ completion, skill runs principles checker via terminal
|
|
70
|
-
|
|
71
|
-
## Graceful Degradation
|
|
72
|
-
|
|
73
|
-
If xp-gate CLI is unavailable, skills provide helpful install instructions instead of failing. Quality gates still run via git hooks (pre-commit) at commit time.
|
|
74
|
-
|
|
75
|
-
## Comparison with Other Platforms
|
|
76
|
-
|
|
77
|
-
| Feature | Claude Code | OpenCode | Qoder |
|
|
78
|
-
|---------|:-----------:|:--------:|:-----:|
|
|
79
|
-
| AI Skills (8) | ✅ | ✅ | ✅ |
|
|
80
|
-
| Git Hooks | ❌ (separate npm) | ❌ (separate npm) | ❌ (separate npm) |
|
|
81
|
-
| Event Hooks | ✅ PreToolUse/PostToolUse | ✅ tool() | ❌ (skill-embedded) |
|
|
82
|
-
| Custom Tools | ❌ | ✅ 3 tools | ❌ (MCP instead) |
|
|
83
|
-
| Widget UI | ❌ | ❌ | ✅ genui |
|
|
84
|
-
| Canvas Output | ❌ | ❌ | ✅ .canvas.tsx |
|
|
85
|
-
| Memory System | ❌ | ❌ | ✅ UpdateMemory |
|
|
86
|
-
| CodeReview Agent | ❌ | ❌ | ✅ subagent |
|
|
87
|
-
| Browser MCP | ❌ | ❌ | ✅ browser-use |
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
<style>
|
|
2
|
-
:root {
|
|
3
|
-
--qr-bg: var(--vscode-editor-background, #1e1e1e);
|
|
4
|
-
--qr-fg: var(--vscode-editor-foreground, #d4d4d4);
|
|
5
|
-
--qr-border: var(--vscode-panel-border, #333);
|
|
6
|
-
--qr-pass: var(--vscode-testing-iconPassed, #73c991);
|
|
7
|
-
--qr-fail: var(--vscode-testing-iconFailed, #f14c4c);
|
|
8
|
-
--qr-skip: var(--vscode-descriptionForeground, #888);
|
|
9
|
-
--qr-accent: var(--vscode-focusBorder, #007acc);
|
|
10
|
-
--qr-card-bg: var(--vscode-editor-widget-background, #252526);
|
|
11
|
-
}
|
|
12
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
13
|
-
body { font-family: var(--vscode-font-family, 'Segoe UI', sans-serif); color: var(--qr-fg); background: var(--qr-bg); padding: 16px; }
|
|
14
|
-
.qr-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
|
|
15
|
-
.qr-header h2 { font-size: 16px; font-weight: 600; }
|
|
16
|
-
.qr-verdict { padding: 4px 12px; border-radius: 4px; font-size: 13px; font-weight: 600; }
|
|
17
|
-
.qr-verdict.pass { background: color-mix(in srgb, var(--qr-pass) 20%, transparent); color: var(--qr-pass); border: 1px solid var(--qr-pass); }
|
|
18
|
-
.qr-verdict.fail { background: color-mix(in srgb, var(--qr-fail) 20%, transparent); color: var(--qr-fail); border: 1px solid var(--qr-fail); }
|
|
19
|
-
.qr-meta { font-size: 12px; color: var(--qr-skip); margin-bottom: 16px; display: flex; gap: 16px; }
|
|
20
|
-
.qr-score-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
|
|
21
|
-
.qr-score-bar .bar-track { flex: 1; height: 8px; background: var(--qr-border); border-radius: 4px; overflow: hidden; }
|
|
22
|
-
.qr-score-bar .bar-fill { height: 100%; border-radius: 4px; transition: width 0.4s ease; }
|
|
23
|
-
.qr-score-bar .bar-fill.pass { background: var(--qr-pass); }
|
|
24
|
-
.qr-score-bar .bar-fill.fail { background: var(--qr-fail); }
|
|
25
|
-
.qr-score-bar .score-label { font-size: 14px; font-weight: 600; min-width: 48px; text-align: right; }
|
|
26
|
-
.qr-gates { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; margin-bottom: 20px; }
|
|
27
|
-
.qr-gate-card { background: var(--qr-card-bg); border: 1px solid var(--qr-border); border-radius: 6px; padding: 12px; display: flex; align-items: flex-start; gap: 10px; }
|
|
28
|
-
.qr-gate-icon { width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; }
|
|
29
|
-
.qr-gate-icon.pass { background: color-mix(in srgb, var(--qr-pass) 20%, transparent); color: var(--qr-pass); }
|
|
30
|
-
.qr-gate-icon.fail { background: color-mix(in srgb, var(--qr-fail) 20%, transparent); color: var(--qr-fail); }
|
|
31
|
-
.qr-gate-icon.skip { background: color-mix(in srgb, var(--qr-skip) 20%, transparent); color: var(--qr-skip); }
|
|
32
|
-
.qr-gate-info { flex: 1; min-width: 0; }
|
|
33
|
-
.qr-gate-name { font-size: 13px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
34
|
-
.qr-gate-detail { font-size: 11px; color: var(--qr-skip); margin-top: 2px; }
|
|
35
|
-
.qr-section { margin-bottom: 16px; }
|
|
36
|
-
.qr-section h3 { font-size: 14px; font-weight: 600; margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid var(--qr-border); }
|
|
37
|
-
.qr-delphi-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; font-size: 13px; }
|
|
38
|
-
.qr-delphi-row .expert-badge { padding: 2px 8px; border-radius: 3px; font-size: 11px; font-weight: 600; background: var(--qr-card-bg); border: 1px solid var(--qr-border); }
|
|
39
|
-
.qr-delphi-row .verdict-tag { margin-left: auto; font-weight: 500; }
|
|
40
|
-
.qr-delphi-row .verdict-tag.approved { color: var(--qr-pass); }
|
|
41
|
-
.qr-delphi-row .verdict-tag.rejected { color: var(--qr-fail); }
|
|
42
|
-
.qr-metrics { display: flex; gap: 12px; flex-wrap: wrap; }
|
|
43
|
-
.qr-metric { background: var(--qr-card-bg); border: 1px solid var(--qr-border); border-radius: 6px; padding: 12px 16px; text-align: center; min-width: 120px; }
|
|
44
|
-
.qr-metric .value { font-size: 24px; font-weight: 700; }
|
|
45
|
-
.qr-metric .label { font-size: 11px; color: var(--qr-skip); margin-top: 4px; }
|
|
46
|
-
.qr-metric .value.pass { color: var(--qr-pass); }
|
|
47
|
-
.qr-metric .value.fail { color: var(--qr-fail); }
|
|
48
|
-
.qr-empty { text-align: center; padding: 32px; color: var(--qr-skip); font-size: 13px; }
|
|
49
|
-
</style>
|
|
50
|
-
|
|
51
|
-
<div id="qr-root"></div>
|
|
52
|
-
|
|
53
|
-
<script>
|
|
54
|
-
(function() {
|
|
55
|
-
const data = window.__WIDGET_DATA__ || {};
|
|
56
|
-
const root = document.getElementById('qr-root');
|
|
57
|
-
|
|
58
|
-
if (!data || (!data.overall && !data.gates)) {
|
|
59
|
-
root.innerHTML = '<div class="qr-empty">No quality report data available.<br>Run the quality gates pipeline to generate a report.</div>';
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const overall = data.overall || {};
|
|
64
|
-
const gates = data.gates || {};
|
|
65
|
-
const delphi = data.delphi || null;
|
|
66
|
-
const testAlign = data.testAlignment || null;
|
|
67
|
-
const isPass = overall.verdict === 'PASS';
|
|
68
|
-
|
|
69
|
-
let html = '';
|
|
70
|
-
|
|
71
|
-
// Header
|
|
72
|
-
html += '<div class="qr-header">';
|
|
73
|
-
html += '<h2>Quality Report' + (data.project ? ' — ' + esc(data.project) : '') + '</h2>';
|
|
74
|
-
html += '<span class="qr-verdict ' + (isPass ? 'pass' : 'fail') + '">' + esc(overall.verdict || 'N/A') + '</span>';
|
|
75
|
-
html += '</div>';
|
|
76
|
-
|
|
77
|
-
// Meta
|
|
78
|
-
if (data.generatedAt || data.commit) {
|
|
79
|
-
html += '<div class="qr-meta">';
|
|
80
|
-
if (data.generatedAt) html += '<span>Generated: ' + esc(formatDate(data.generatedAt)) + '</span>';
|
|
81
|
-
if (data.commit) html += '<span>Commit: ' + esc(data.commit.substring(0, 8)) + '</span>';
|
|
82
|
-
if (data.language) html += '<span>Language: ' + esc(data.language) + '</span>';
|
|
83
|
-
html += '</div>';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Score bar
|
|
87
|
-
const passed = overall.gatesPassed || 0;
|
|
88
|
-
const total = overall.gatesTotal || 0;
|
|
89
|
-
const score = overall.score || 0;
|
|
90
|
-
const pct = total > 0 ? Math.round((passed / total) * 100) : 0;
|
|
91
|
-
html += '<div class="qr-score-bar">';
|
|
92
|
-
html += '<div class="bar-track"><div class="bar-fill ' + (isPass ? 'pass' : 'fail') + '" style="width:' + pct + '%"></div></div>';
|
|
93
|
-
html += '<span class="score-label">' + passed + '/' + total + '</span>';
|
|
94
|
-
html += '</div>';
|
|
95
|
-
|
|
96
|
-
// Gate cards
|
|
97
|
-
html += '<div class="qr-gates">';
|
|
98
|
-
const gateKeys = Object.keys(gates).sort();
|
|
99
|
-
for (const key of gateKeys) {
|
|
100
|
-
const g = gates[key];
|
|
101
|
-
const status = (g.status || 'SKIP').toUpperCase();
|
|
102
|
-
const icon = status === 'PASS' ? '✓' : status === 'FAIL' ? '✗' : '—';
|
|
103
|
-
const cls = status === 'PASS' ? 'pass' : status === 'FAIL' ? 'fail' : 'skip';
|
|
104
|
-
html += '<div class="qr-gate-card">';
|
|
105
|
-
html += '<div class="qr-gate-icon ' + cls + '">' + icon + '</div>';
|
|
106
|
-
html += '<div class="qr-gate-info">';
|
|
107
|
-
html += '<div class="qr-gate-name">' + esc(g.name || key) + '</div>';
|
|
108
|
-
let detail = '';
|
|
109
|
-
if (g.tool) detail += 'Tool: ' + esc(g.tool);
|
|
110
|
-
if (g.metric) detail += (detail ? ' · ' : '') + esc(g.metric);
|
|
111
|
-
if (g.threshold) detail += (detail ? ' · ' : '') + 'Threshold: ' + esc(g.threshold);
|
|
112
|
-
if (g.coverage) detail += (detail ? ' · ' : '') + 'Coverage: ' + esc(g.coverage);
|
|
113
|
-
if (g.warnings !== undefined) detail += (detail ? ' · ' : '') + 'Warnings: ' + g.warnings;
|
|
114
|
-
if (!detail) detail = status;
|
|
115
|
-
html += '<div class="qr-gate-detail">' + detail + '</div>';
|
|
116
|
-
html += '</div></div>';
|
|
117
|
-
}
|
|
118
|
-
html += '</div>';
|
|
119
|
-
|
|
120
|
-
// Delphi Review section
|
|
121
|
-
if (delphi) {
|
|
122
|
-
html += '<div class="qr-section"><h3>Delphi Review</h3>';
|
|
123
|
-
if (delphi.experts) {
|
|
124
|
-
for (const exp of delphi.experts) {
|
|
125
|
-
const vCls = (exp.verdict || '').toUpperCase() === 'APPROVED' ? 'approved' : 'rejected';
|
|
126
|
-
html += '<div class="qr-delphi-row">';
|
|
127
|
-
html += '<span class="expert-badge">' + esc(exp.id || '?') + '</span>';
|
|
128
|
-
html += '<span>' + esc(exp.role || '') + '</span>';
|
|
129
|
-
html += '<span class="verdict-tag ' + vCls + '">' + esc(exp.verdict || 'N/A') + '</span>';
|
|
130
|
-
html += '</div>';
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
if (delphi.consensus !== undefined) {
|
|
134
|
-
html += '<div class="qr-delphi-row"><span>Consensus</span><span class="verdict-tag ' + (delphi.consensus >= 0.95 ? 'approved' : 'rejected') + '">' + Math.round(delphi.consensus * 100) + '%</span></div>';
|
|
135
|
-
}
|
|
136
|
-
html += '</div>';
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Metrics section (test alignment + coverage)
|
|
140
|
-
const hasMetrics = testAlign || (overall.coverage_pct !== undefined);
|
|
141
|
-
if (hasMetrics) {
|
|
142
|
-
html += '<div class="qr-section"><h3>Metrics</h3><div class="qr-metrics">';
|
|
143
|
-
if (testAlign) {
|
|
144
|
-
const alignScore = testAlign.score || 0;
|
|
145
|
-
const alignCls = alignScore >= 80 ? 'pass' : 'fail';
|
|
146
|
-
html += '<div class="qr-metric"><div class="value ' + alignCls + '">' + alignScore + '%</div><div class="label">Test-Spec Alignment</div></div>';
|
|
147
|
-
}
|
|
148
|
-
if (overall.coverage_pct !== undefined) {
|
|
149
|
-
const covCls = overall.coverage_pct >= 80 ? 'pass' : 'fail';
|
|
150
|
-
html += '<div class="qr-metric"><div class="value ' + covCls + '">' + overall.coverage_pct + '%</div><div class="label">Coverage</div></div>';
|
|
151
|
-
}
|
|
152
|
-
if (overall.score !== undefined) {
|
|
153
|
-
html += '<div class="qr-metric"><div class="value">' + overall.score + '</div><div class="label">Overall Score</div></div>';
|
|
154
|
-
}
|
|
155
|
-
html += '</div></div>';
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
root.innerHTML = html;
|
|
159
|
-
|
|
160
|
-
function esc(s) { const d = document.createElement('div'); d.textContent = String(s); return d.innerHTML; }
|
|
161
|
-
function formatDate(iso) { try { return new Date(iso).toLocaleString(); } catch { return iso; } }
|
|
162
|
-
})();
|
|
163
|
-
</script>
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
<style>
|
|
2
|
-
:root {
|
|
3
|
-
--sd-bg: var(--vscode-editor-background, #1e1e1e);
|
|
4
|
-
--sd-fg: var(--vscode-editor-foreground, #d4d4d4);
|
|
5
|
-
--sd-border: var(--vscode-panel-border, #333);
|
|
6
|
-
--sd-pass: var(--vscode-testing-iconPassed, #73c991);
|
|
7
|
-
--sd-fail: var(--vscode-testing-iconFailed, #f14c4c);
|
|
8
|
-
--sd-skip: var(--vscode-descriptionForeground, #888);
|
|
9
|
-
--sd-accent: var(--vscode-focusBorder, #007acc);
|
|
10
|
-
--sd-card-bg: var(--vscode-editor-widget-background, #252526);
|
|
11
|
-
--sd-active: var(--vscode-charts-yellow, #e5c07b);
|
|
12
|
-
}
|
|
13
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
14
|
-
body { font-family: var(--vscode-font-family, 'Segoe UI', sans-serif); color: var(--sd-fg); background: var(--sd-bg); padding: 16px; }
|
|
15
|
-
.sd-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
|
|
16
|
-
.sd-header h2 { font-size: 16px; font-weight: 600; }
|
|
17
|
-
.sd-status-badge { padding: 4px 12px; border-radius: 4px; font-size: 12px; font-weight: 600; text-transform: uppercase; }
|
|
18
|
-
.sd-status-badge.running { background: color-mix(in srgb, var(--sd-accent) 20%, transparent); color: var(--sd-accent); border: 1px solid var(--sd-accent); }
|
|
19
|
-
.sd-status-badge.paused { background: color-mix(in srgb, var(--sd-active) 20%, transparent); color: var(--sd-active); border: 1px solid var(--sd-active); }
|
|
20
|
-
.sd-status-badge.completed, .sd-status-badge.merged { background: color-mix(in srgb, var(--sd-pass) 20%, transparent); color: var(--sd-pass); border: 1px solid var(--sd-pass); }
|
|
21
|
-
.sd-meta { font-size: 12px; color: var(--sd-skip); margin-bottom: 16px; }
|
|
22
|
-
.sd-phase-track { display: flex; align-items: center; gap: 4px; margin-bottom: 20px; overflow-x: auto; padding-bottom: 4px; }
|
|
23
|
-
.sd-phase { display: flex; flex-direction: column; align-items: center; min-width: 72px; }
|
|
24
|
-
.sd-phase-dot { width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; border: 2px solid var(--sd-border); color: var(--sd-skip); background: var(--sd-bg); }
|
|
25
|
-
.sd-phase-dot.done { background: color-mix(in srgb, var(--sd-pass) 20%, transparent); border-color: var(--sd-pass); color: var(--sd-pass); }
|
|
26
|
-
.sd-phase-dot.active { background: color-mix(in srgb, var(--sd-accent) 25%, transparent); border-color: var(--sd-accent); color: var(--sd-accent); animation: pulse 1.5s infinite; }
|
|
27
|
-
.sd-phase-dot.future { border-color: var(--sd-border); color: var(--sd-skip); }
|
|
28
|
-
.sd-phase-label { font-size: 10px; margin-top: 4px; color: var(--sd-skip); text-align: center; white-space: nowrap; }
|
|
29
|
-
.sd-phase-label.active { color: var(--sd-accent); font-weight: 600; }
|
|
30
|
-
.sd-phase-label.done { color: var(--sd-pass); }
|
|
31
|
-
.sd-phase-line { width: 16px; height: 2px; background: var(--sd-border); margin-bottom: 20px; }
|
|
32
|
-
.sd-phase-line.done { background: var(--sd-pass); }
|
|
33
|
-
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }
|
|
34
|
-
.sd-section { margin-bottom: 16px; }
|
|
35
|
-
.sd-section h3 { font-size: 14px; font-weight: 600; margin-bottom: 10px; padding-bottom: 6px; border-bottom: 1px solid var(--sd-border); }
|
|
36
|
-
.sd-progress-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
|
37
|
-
.sd-progress-row .label { font-size: 13px; min-width: 100px; }
|
|
38
|
-
.sd-progress-row .bar-track { flex: 1; height: 10px; background: var(--sd-border); border-radius: 5px; overflow: hidden; }
|
|
39
|
-
.sd-progress-row .bar-fill { height: 100%; border-radius: 5px; background: var(--sd-accent); transition: width 0.4s ease; }
|
|
40
|
-
.sd-progress-row .count { font-size: 13px; font-weight: 600; min-width: 60px; text-align: right; }
|
|
41
|
-
.sd-outputs { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 8px; }
|
|
42
|
-
.sd-output-item { background: var(--sd-card-bg); border: 1px solid var(--sd-border); border-radius: 4px; padding: 8px 10px; font-size: 12px; }
|
|
43
|
-
.sd-output-item .key { color: var(--sd-skip); font-size: 11px; }
|
|
44
|
-
.sd-output-item .val { margin-top: 2px; font-weight: 500; word-break: break-all; }
|
|
45
|
-
.sd-metrics { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
|
|
46
|
-
.sd-metric-card { background: var(--sd-card-bg); border: 1px solid var(--sd-border); border-radius: 6px; padding: 10px 14px; text-align: center; min-width: 100px; }
|
|
47
|
-
.sd-metric-card .value { font-size: 22px; font-weight: 700; color: var(--sd-accent); }
|
|
48
|
-
.sd-metric-card .label { font-size: 11px; color: var(--sd-skip); margin-top: 2px; }
|
|
49
|
-
.sd-learnings { list-style: none; }
|
|
50
|
-
.sd-learnings li { padding: 6px 0; font-size: 12px; border-bottom: 1px solid var(--sd-border); }
|
|
51
|
-
.sd-learnings li:last-child { border-bottom: none; }
|
|
52
|
-
.sd-learnings .cat { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 10px; font-weight: 600; background: var(--sd-card-bg); border: 1px solid var(--sd-border); margin-right: 6px; }
|
|
53
|
-
.sd-empty { text-align: center; padding: 32px; color: var(--sd-skip); font-size: 13px; }
|
|
54
|
-
</style>
|
|
55
|
-
|
|
56
|
-
<div id="sd-root"></div>
|
|
57
|
-
|
|
58
|
-
<script>
|
|
59
|
-
(function() {
|
|
60
|
-
const data = window.__WIDGET_DATA__ || {};
|
|
61
|
-
const root = document.getElementById('sd-root');
|
|
62
|
-
|
|
63
|
-
if (!data || (!data.phase && !data.id && !data.status)) {
|
|
64
|
-
root.innerHTML = '<div class="sd-empty">No sprint state data available.<br>Start a sprint with /sprint-flow to see the dashboard.</div>';
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const PHASES = [
|
|
69
|
-
{ num: -1, name: 'ISOLATE' },
|
|
70
|
-
{ num: 0, name: 'THINK' },
|
|
71
|
-
{ num: 1, name: 'PLAN' },
|
|
72
|
-
{ num: 2, name: 'BUILD' },
|
|
73
|
-
{ num: 3, name: 'REVIEW' },
|
|
74
|
-
{ num: 4, name: 'UAT' },
|
|
75
|
-
{ num: 5, name: 'FEEDBACK' },
|
|
76
|
-
{ num: 6, name: 'SHIP' },
|
|
77
|
-
{ num: 7, name: 'LAND' },
|
|
78
|
-
{ num: 8, name: 'CLEANUP' }
|
|
79
|
-
];
|
|
80
|
-
|
|
81
|
-
const currentPhase = data.phase !== undefined ? data.phase : -2;
|
|
82
|
-
const status = data.status || 'unknown';
|
|
83
|
-
const outputs = data.outputs || {};
|
|
84
|
-
const metrics = data.metrics || {};
|
|
85
|
-
const learnings = data.learnings || [];
|
|
86
|
-
|
|
87
|
-
let html = '';
|
|
88
|
-
|
|
89
|
-
// Header
|
|
90
|
-
html += '<div class="sd-header">';
|
|
91
|
-
html += '<h2>Sprint' + (data.id ? ' — ' + esc(data.id) : '') + '</h2>';
|
|
92
|
-
const statusCls = ['running', 'paused', 'completed', 'merged'].includes(status) ? status : 'running';
|
|
93
|
-
html += '<span class="sd-status-badge ' + statusCls + '">' + esc(status) + '</span>';
|
|
94
|
-
html += '</div>';
|
|
95
|
-
|
|
96
|
-
// Phase progress track
|
|
97
|
-
html += '<div class="sd-phase-track">';
|
|
98
|
-
for (let i = 0; i < PHASES.length; i++) {
|
|
99
|
-
const p = PHASES[i];
|
|
100
|
-
if (i > 0) {
|
|
101
|
-
const lineCls = p.num <= currentPhase ? 'done' : '';
|
|
102
|
-
html += '<div class="sd-phase-line ' + lineCls + '"></div>';
|
|
103
|
-
}
|
|
104
|
-
let dotCls = 'future';
|
|
105
|
-
if (p.num < currentPhase) dotCls = 'done';
|
|
106
|
-
else if (p.num === currentPhase) dotCls = 'active';
|
|
107
|
-
const lblCls = p.num < currentPhase ? 'done' : p.num === currentPhase ? 'active' : '';
|
|
108
|
-
const icon = p.num < currentPhase ? '✓' : p.num === currentPhase ? '▶' : String(p.num);
|
|
109
|
-
html += '<div class="sd-phase">';
|
|
110
|
-
html += '<div class="sd-phase-dot ' + dotCls + '">' + icon + '</div>';
|
|
111
|
-
html += '<div class="sd-phase-label ' + lblCls + '">' + esc(p.name) + '</div>';
|
|
112
|
-
html += '</div>';
|
|
113
|
-
}
|
|
114
|
-
html += '</div>';
|
|
115
|
-
|
|
116
|
-
// Metrics cards
|
|
117
|
-
if (metrics.tests_passed !== undefined || metrics.coverage_pct !== undefined || metrics.tests_failed !== undefined) {
|
|
118
|
-
html += '<div class="sd-metrics">';
|
|
119
|
-
if (metrics.tests_passed !== undefined) {
|
|
120
|
-
html += '<div class="sd-metric-card"><div class="value">' + metrics.tests_passed + '</div><div class="label">Tests Passed</div></div>';
|
|
121
|
-
}
|
|
122
|
-
if (metrics.tests_failed !== undefined) {
|
|
123
|
-
html += '<div class="sd-metric-card"><div class="value" style="color:' + (metrics.tests_failed > 0 ? 'var(--sd-fail)' : 'var(--sd-pass)') + '">' + metrics.tests_failed + '</div><div class="label">Tests Failed</div></div>';
|
|
124
|
-
}
|
|
125
|
-
if (metrics.coverage_pct !== undefined) {
|
|
126
|
-
html += '<div class="sd-metric-card"><div class="value" style="color:' + (metrics.coverage_pct >= 80 ? 'var(--sd-pass)' : 'var(--sd-fail)') + '">' + metrics.coverage_pct + '%</div><div class="label">Coverage</div></div>';
|
|
127
|
-
}
|
|
128
|
-
html += '</div>';
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// REQ progress
|
|
132
|
-
if (data.req_progress || data.reqs) {
|
|
133
|
-
const rp = data.req_progress || {};
|
|
134
|
-
const done = rp.done || 0;
|
|
135
|
-
const totalReqs = rp.total || (data.reqs ? data.reqs.length : 0);
|
|
136
|
-
const pct = totalReqs > 0 ? Math.round((done / totalReqs) * 100) : 0;
|
|
137
|
-
html += '<div class="sd-section"><h3>REQ Progress</h3>';
|
|
138
|
-
html += '<div class="sd-progress-row">';
|
|
139
|
-
html += '<span class="label">Completed</span>';
|
|
140
|
-
html += '<div class="bar-track"><div class="bar-fill" style="width:' + pct + '%"></div></div>';
|
|
141
|
-
html += '<span class="count">' + done + '/' + totalReqs + '</span>';
|
|
142
|
-
html += '</div></div>';
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Phase outputs
|
|
146
|
-
const outputKeys = Object.keys(outputs);
|
|
147
|
-
if (outputKeys.length > 0) {
|
|
148
|
-
html += '<div class="sd-section"><h3>Phase Outputs</h3>';
|
|
149
|
-
html += '<div class="sd-outputs">';
|
|
150
|
-
for (const key of outputKeys) {
|
|
151
|
-
html += '<div class="sd-output-item"><div class="key">' + esc(key) + '</div><div class="val">' + esc(outputs[key]) + '</div></div>';
|
|
152
|
-
}
|
|
153
|
-
html += '</div></div>';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Learnings
|
|
157
|
-
if (learnings.length > 0) {
|
|
158
|
-
html += '<div class="sd-section"><h3>Recent Learnings</h3>';
|
|
159
|
-
html += '<ul class="sd-learnings">';
|
|
160
|
-
const recent = learnings.slice(-5).reverse();
|
|
161
|
-
for (const l of recent) {
|
|
162
|
-
const cat = l.category || 'general';
|
|
163
|
-
html += '<li><span class="cat">' + esc(cat) + '</span>' + esc(l.summary || l.text || JSON.stringify(l)) + '</li>';
|
|
164
|
-
}
|
|
165
|
-
html += '</ul></div>';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
root.innerHTML = html;
|
|
169
|
-
|
|
170
|
-
function esc(s) { const d = document.createElement('div'); d.textContent = String(s); return d.innerHTML; }
|
|
171
|
-
})();
|
|
172
|
-
</script>
|