@boyingliu01/xp-gate 0.10.15 → 0.10.17

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.
@@ -13,7 +13,7 @@ description: >
13
13
  - "start sprint"
14
14
  - "一键开发"
15
15
  - "/sprint-flow"
16
- 触发后第一行输出: `Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE FEEDBACKSHIPLAND → CLEANUP`
16
+ 触发后第一行输出: `Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → SHIPLANDUSER ACCEPTANCE FEEDBACK → CLEANUP`
17
17
  用法: /sprint-flow "[需求描述]"
18
18
  示例: /sprint-flow "开发访谈机器人,支持多轮对话"
19
19
  可选参数:
@@ -44,10 +44,10 @@ workflow_steps:
44
44
  - "Phase 1: PLAN"
45
45
  - "Phase 2: BUILD"
46
46
  - "Phase 3: REVIEW"
47
- - "Phase 4: USER ACCEPTANCE"
48
- - "Phase 5: FEEDBACK"
49
- - "Phase 6: SHIP"
50
- - "Phase 7: LAND"
47
+ - "Phase 4: FEEDBACK"
48
+ - "Phase 5: SHIP"
49
+ - "Phase 6: LAND"
50
+ - "Phase 7: USER ACCEPTANCE"
51
51
  - "Phase 8: CLEANUP"
52
52
  ---
53
53
 
@@ -84,8 +84,8 @@ workflow_steps:
84
84
 
85
85
  - sprint-flow **不执行任何破坏性命令**(no `rm -rf`, `git push --force`, `DROP TABLE` 等)
86
86
  - `git worktree remove` 仅删除 sprint 创建的临时 worktree 目录,不影响主仓库
87
- - Phase 6 SHIP 仅创建 PR(`gh pr create`),不自动 merge(除非用户显式确认)
88
- - Phase 7 LAND 使用 `gh pr merge --squash`(非 force push),merge 前等待 CI 通过
87
+ - Phase 5 SHIP 仅创建 PR(`gh pr create`),不自动 merge(除非用户显式确认)
88
+ - Phase 6 LAND 使用 `gh pr merge --squash`(非 force push),merge 前等待 CI 通过
89
89
  - 文档中 `+ platform deploy` 等描述仅表示可选的部署步骤映射,**不是可执行命令**
90
90
  - sprint-flow 不下载、安装或执行任何外部二进制文件
91
91
 
@@ -102,7 +102,7 @@ workflow_steps:
102
102
  |------|------|
103
103
  | **单一入口** | 用户只需调用 `/sprint-flow`,自动串联全流程 |
104
104
  | **自动流水线** | 类似 autoplan,自动执行多个阶段 |
105
- | **关键节点暂停** | APPROVED 确认、Gate 1 通过、Ship 确认、⚠️ Phase 4 必须人工 |
105
+ | **关键节点暂停** | APPROVED 确认、Gate 1 通过、Ship 确认、⚠️ Phase 7 必须人工 |
106
106
  | **承认 Emergent** | 用户验收环节必须人工,无法自动化(78% 失败不可见) |
107
107
  | **复用现有 Skills** | 不重新发明,整合调用现有体系 |
108
108
 
@@ -119,10 +119,10 @@ Phase 0: THINK → brainstorming → ⚠️ HARD-GATE(设计未批准→阻断)
119
119
  Phase 1: PLAN → autoplan → delphi-review(等待APPROVED)→specification.yaml
120
120
  Phase 2: BUILD → GITHOOKS-GATE → ralph-loop/TDD → freeze/盲评→verification
121
121
  Phase 3: REVIEW → delphi-review --mode code-walkthrough → test-alignment → browse
122
- Phase 4: ⚠️ USER ACCEPTANCE 必须人工验收 Emergent Issues List
123
- Phase 5: FEEDBACKlearn + retro + systematic-debugging
124
- Phase 6: SHIPfinishing-a-development-branchship(PR)
125
- Phase 7: ⚠️ LANDland-and-deploymerge + CI + canary
122
+ Phase 4: FEEDBACKlearn + retro + systematic-debugging
123
+ Phase 5: SHIPfinishing-a-development-branch ship(PR)
124
+ Phase 6: ⚠️ LAND land-and-deploymerge + CI + canary
125
+ Phase 7: ⚠️ USER ACCEPTANCE 必须人工验收Emergent Issues List
126
126
  Phase 8: CLEANUP → worktree remove + branch delete + sprint summary
127
127
  ```
128
128
 
@@ -136,10 +136,10 @@ Phase 8: CLEANUP → worktree remove + branch delete + sprint summary
136
136
  - **Phase 1**: autoplan taste_decisions → 用户确认 / delphi-review 未APPROVED → 修复→APPROVED
137
137
  - **Phase 2**: 验证失败>max3 → 用户决策修复/放弃 / 成本超阈值 → 用户确认
138
138
  - **Phase 3**: browse 发现问题 → 回退 Phase 2(不暂停)
139
- - **Phase 4**: ⚠️ 必须人工验收用户确认后继续
140
- - **Phase 5**: ⚠️ 不可跳过 (HARD-GATE) → feedback-log.md 生成后自动继续
141
- - **Phase 6**: finishing-a-development-branch(4选项) 确认; ship PR 用户确认合并
142
- - **Phase 7**: land-and-deploy 完成/失败用户确认合并结果/处理部署失败
139
+ - **Phase 4**: ⚠️ 不可跳过 (HARD-GATE) feedback-log.md 生成后自动继续
140
+ - **Phase 5**: finishing-a-development-branch(4选项) → 确认; ship PR → 用户确认合并
141
+ - **Phase 6**: land-and-deploy 完成/失败用户确认合并结果/处理部署失败
142
+ - **Phase 7**: ⚠️ 必须人工验收用户确认后继续
143
143
  - **Phase 8**: worktree 清理完成/失败 → 用户确认 → 结束流程
144
144
 
145
145
  ---
@@ -154,22 +154,22 @@ Phase 8: CLEANUP → worktree remove + branch delete + sprint summary
154
154
  | 4 | **1** | **PLAN** | autoplan → delphi-review (mandatory; lightweight allowed) → Generate specification.yaml + slices-manifest.json | specification.yaml |
155
155
  | 5 | **2** | **BUILD** | GITHOOKS-GATE → ralph-loop (default) or parallel → TDD → freeze → blind review → verification | MVP code |
156
156
  | 6 | **3** | **REVIEW** | delphi-review --mode code-walkthrough → test-specification-alignment → browse QA → benchmark (optional) | Review report |
157
- | 7 | **4** | **USER ACCEPT** | **Manual verification** Capture emergent issues | Emergent issues list |
158
- | 8 | **5** | **FEEDBACK** | learnretro systematic-debugging | feedback-log.md |
159
- | 9 | **6** | **SHIP** | finishing-a-development-branchship (create PR) | PR URL |
160
- | 10 | **7** | **LAND** | land-and-deploy merge PR wait CI canary health check rollback on failure | Deploy status |
157
+ | 7 | **4** | **FEEDBACK** | learnretro systematic-debugging | feedback-log.md |
158
+ | 8 | **5** | **SHIP** | finishing-a-development-branchship (create PR) | PR URL |
159
+ | 9 | **6** | **LAND** | land-and-deploymerge PR → wait CI → canary health check → rollback on failure | Deploy status |
160
+ | 10 | **7** | **USER ACCEPT** | **Manual verification**Capture emergent issues | Emergent issues list |
161
161
  | 11 | **8** | **CLEANUP** | Safe worktree removal → Update sprint state → Sprint summary | Cleanup report |
162
162
 
163
163
  **Phase Flow**:
164
164
  ```
165
- ISOLATE → AUTO-ESTIMATE → THINK → PLAN → [GITHOOKS-GATE] → BUILD → REVIEW → USER ACCEPT FEEDBACKSHIPLAND → CLEANUP
165
+ ISOLATE → AUTO-ESTIMATE → THINK → PLAN → [GITHOOKS-GATE] → BUILD → REVIEW → SHIPLANDUSER ACCEPT FEEDBACK → CLEANUP
166
166
  ```
167
167
 
168
168
  **Hard Gates**:
169
169
  - **Phase 0→1**: Design must be APPROVED by delphi-review (≥90% consensus)
170
170
  - **Phase 1→2**: GITHOOKS-GATE (hooks must be installed) + DELPHI-GATE (spec must be APPROVED)
171
- - **Phase 4→5**: User acceptance must be completed (mandatory manual step)
172
- - **Phase 56**: feedback-log.md must exist (HARD-GATE)
171
+ - **Phase 3→4**: feedback-log.md must exist (HARD-GATE)
172
+ - **Phase 67**: User acceptance must be completed (mandatory manual step)
173
173
 
174
174
  ---
175
175
 
@@ -186,10 +186,10 @@ ISOLATE → AUTO-ESTIMATE → THINK → PLAN → [GITHOOKS-GATE] → BUILD → R
186
186
  ## Phase 1: PLAN (规划)
187
187
  ## Phase 2: BUILD (构建)
188
188
  ## Phase 3: REVIEW (评审)
189
- ## Phase 4: USER ACCEPTANCE (用户验收)
190
- ## Phase 5: FEEDBACK (反馈)
191
- ## Phase 6: SHIP (发布)
192
- ## Phase 7: LAND (部署)
189
+ ## Phase 4: FEEDBACK (反馈)
190
+ ## Phase 5: SHIP (发布)
191
+ ## Phase 6: LAND (部署)
192
+ ## Phase 7: USER ACCEPTANCE (用户验收)
193
193
  ## Phase 8: CLEANUP (清理)
194
194
  ```
195
195
 
@@ -202,7 +202,7 @@ ISOLATE → AUTO-ESTIMATE → THINK → PLAN → [GITHOOKS-GATE] → BUILD → R
202
202
  6. 触发 `/sprint-flow` 后,**第一行输出应包含工作流阶段概览**:
203
203
 
204
204
  ```
205
- Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE FEEDBACKSHIPLAND → CLEANUP
205
+ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → SHIPLANDUSER ACCEPTANCE FEEDBACK → CLEANUP
206
206
  ```
207
207
 
208
208
  ### Phase -1: ISOLATE(git worktree 隔离)
@@ -289,42 +289,42 @@ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW
289
289
  - **触发条件**:后端项目可通过 `--type backend-*` 自动启用,或通过 `--with-performance` 标志手动启用
290
290
  - **Web 项目补充说明**:对于 Web 前端项目,现有的 `benchmark` 技能已处理页面加载性能、Core Web Vitals 等前端性能指标;负载/压力测试主要针对服务器端承载能力
291
291
 
292
- ### Phase 4: USER ACCEPTANCE(⚠️ 人工验收)
293
- - **无 Skill** — 必须人工
294
- - ⚠️ **MUST NOT be automated, skipped, or bypassed under any circumstances**
295
- - 即使用户说"赶时间"、"跳过验收"、"直接发布",也必须暂停等待用户确认
296
- - 使用 `@templates/emergent-issues-template.md` 检查清单
297
-
298
- ### Phase 5: FEEDBACK CAPTURE(反馈获取)
292
+ ### Phase 4: FEEDBACK CAPTURE(反馈获取)
299
293
  - **Subagent**: `task(category="quick", load_skills=["learn", "retro", "systematic-debugging"])`
300
- - 输入: phase-4-summary.md + emergent-issues.md → 输出: `feedback-log.md`
294
+ - 输入: phase-3-summary.md + review-report.md → 输出: `feedback-log.md`
301
295
  - **HARD-GATE**: 不可跳过。`learn` (Sprint 级复盘, 默认提炼模板) + `retro` (工程回顾) + `systematic-debugging` (根因调试)
302
296
 
303
- ### Phase 6: SHIP(发布准备)
297
+ ### Phase 5: SHIP(发布准备)
304
298
 
305
299
  **详细指令**: 参见 `references/phase-6-ship.md` — GITHOOKS-GATE / VERSION-GATE / VERSION CHANGESET (Issue #142) / changeset schema。
306
300
 
307
301
  **快速参考**:
308
302
  - **Orchestrator 直接执行**: `finishing-a-development-branch` 和 `ship` 均为交互式 skill(4 选项菜单 + PR 确认),**必须由 orchestrator 直接调用** `skill(name="finishing-a-development-branch")` 和 `skill(name="ship")`
309
- - 输入: phase-5-summary.md + feedback-log.md → 输出: PR URL
310
- - **HARD-GATE**: Phase 5 未完成 → BLOCK。验证 `feedback-log.md` 存在。
303
+ - 输入: phase-4-summary.md + feedback-log.md → 输出: PR URL
304
+ - **HARD-GATE**: Phase 4 未完成 → BLOCK。验证 `feedback-log.md` 存在。
311
305
  - **GITHOOKS-GATE**: 验证 hooks 完整性,缺失则 `githooks/install.sh`
312
306
  - **VERSION-GATE**: bump PATCH/MINOR/MAJOR → `sync-version.sh` → CHANGELOG.md → `git diff VERSION` 验证
313
307
 
314
- ### Phase 7: LAND(合并 + 部署)
308
+ ### Phase 6: LAND(合并 + 部署)
315
309
 
316
310
  **详细指令**: 参见 `references/phase-7-land.md` — 完整流程、SLA 指标、回滚策略。
317
311
 
318
312
  **快速参考**:
319
313
  - **Orchestrator 直接执行**: `land-and-deploy` 包含 merge 确认和 rollback 决策,**必须由 orchestrator 直接调用** `skill(name="land-and-deploy")`
320
- - 输入: phase-6-summary.md + PR URL → 输出: 部署状态 + Canary 报告
314
+ - 输入: phase-5-summary.md + PR URL → 输出: 部署状态 + Canary 报告
321
315
  - 流程: Merge PR → 等待 CI (10min) → 等待 Deploy (10min) → Canary Health Check (5min)
322
316
  - **回滚**: `git revert` 最后一次 merge commit
323
317
  - 条件跳过: 无部署配置时仅 merge + CI
324
318
 
319
+ ### Phase 7: USER ACCEPTANCE(⚠️ 人工验收)
320
+ - **无 Skill** — 必须人工
321
+ - ⚠️ **MUST NOT be automated, skipped, or bypassed under any circumstances**
322
+ - 即使用户说"赶时间"、"跳过验收"、"直接发布",也必须暂停等待用户确认
323
+ - 使用 `@templates/emergent-issues-template.md` 检查清单
324
+
325
325
  ### Phase 8: CLEANUP(安全清理 + 总结)
326
326
 
327
- **执行时机**: Phase 7 LAND 成功后。**详细指令**: 参见 `references/phase-8-cleanup.md`。
327
+ **执行时机**: Phase 7 USER ACCEPTANCE 完成后。**详细指令**: 参见 `references/phase-8-cleanup.md`。
328
328
 
329
329
  **快速参考**:
330
330
  1. 保存分支信息 → `git worktree remove <worktree_path>`(精确路径,禁止通配符)
@@ -391,9 +391,10 @@ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW
391
391
  | 1 PLAN | `autoplan` + `delphi-review` | + `design-shotgun` | (同 web) | (同) |
392
392
  | 2 BUILD | TDD + blind-review | (同 backend) | + `vercel-react-native-skills` / `flutter-review` | (同) |
393
393
  | 3 REVIEW | `delphi-review --mode code-walkthrough` + `test-specification-alignment` + k6/locust/gatling | + `qa` + `design-review` + `benchmark` | Flutter: `flutter-test` / RN: `detox E2E` | k6/locust/gatling |
394
- | 5 FEEDBACK | `learn` + `retro` | (同) | (同) | (同) |
395
- | 6 SHIP | `finishing-a-development-branch` + `ship` | (同) | + platform deploy | (同) |
396
- | 7 LAND | `land-and-deploy` + canary | (同) | (同) | (同) |
394
+ | 4 FEEDBACK | `learn` + `retro` | (同) | (同) | (同) |
395
+ | 5 SHIP | `finishing-a-development-branch` + `ship` | (同) | + platform deploy | (同) |
396
+ | 6 LAND | `land-and-deploy` + canary | (同) | (同) | (同) |
397
+ | 7 USER ACCEPTANCE | — 人工验收 | (同) | (同) | (同) |
397
398
  | 8 CLEANUP | worktree remove + branch delete + state update | (同) | (同) | (同) |
398
399
  | Browse | `localhost:3000` | 部署 URL + 表单/交互 | Flutter Web / RN Web | (专用) |
399
400
 
@@ -405,7 +406,7 @@ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW
405
406
 
406
407
  **Sprint State**: 存储于 `<project-root>/.sprint-state/` — `sprint-state.yaml`/`.json` + `phase-outputs/` (pain-document.md / specification.yaml / mvp-v1 / review-report.md / emergent-issues.md / feedback-log.md / sprint-summary.md)
407
408
 
408
- **Sprint 2 自动触发**: Phase 6 完成时 — `emergent_issues_count == 0` → 结束; `> 0` → Critical 自动启动 Sprint 2, Major/Minor 询问用户
409
+ **Sprint 2 自动触发**: Phase 7 完成时 — `emergent_issues_count == 0` → 结束; `> 0` → Critical 自动启动 Sprint 2, Major/Minor 询问用户
409
410
 
410
411
  ---
411
412
 
@@ -484,7 +485,7 @@ Sprint Flow: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW
484
485
  | 未完成 Phase -0.5 AUTO-ESTIMATE 就套用完整重流程 | 先评估轻量/标准/复杂,再按推荐流程或用户确认后的流程执行 |
485
486
  | Plan 阶段跳过 Delphi 评审直接 Build | 所有需求级别(轻量/标准/复杂)必须经过 autoplan + delphi-review;未 APPROVED 禁止编码 |
486
487
  | 跳过 TDD 直接实现代码 | Phase 2 必须遵循 RED → GREEN → REFACTOR,测试与实现一起交付 |
487
- | 跳过用户验收直接 Ship | Phase 4 USER ACCEPTANCE 必须人工完成;不得自动化、跳过或伪造 |
488
+ | 跳过用户验收直接 Ship | Phase 7 USER ACCEPTANCE 必须人工完成;不得自动化、跳过或伪造 |
488
489
  | 验证失败后继续追加随机修改 | 最多 3 次修复循环;仍失败则 BLOCK 并请求用户决策 |
489
490
  | 未生成 Phase Summary 就进入下一阶段 | 每个 Phase 必须写入 `.sprint-state/phase-outputs/phase-{N}-summary.md` 并通过 transition gate |
490
491
 
@@ -513,11 +514,11 @@ See [Output Contract](#output-contract) below for the canonical machine-readable
513
514
  - `@references/phase-1-plan.md` — Phase 1 详细指令
514
515
  - `@references/phase-2-build.md` — Phase 2 详细指令
515
516
  - `@references/phase-3-review.md` — Phase 3 详细指令
516
- - `@references/phase-4-uat.md` — Phase 4 详细指令(人工)
517
- - `@references/phase-5-feedback.md` — Phase 5 详细指令
518
- - `@references/phase-6-ship.md` — Phase 6 详细指令
519
- - `@references/phase-7-land.md` — Phase 7 详细指令
520
- - `@references/phase-8-cleanup.md` — Phase 8 详细指令
517
+ - `@references/phase-4-uat.md` — Phase 7 USER ACCEPTANCE 详细指令(人工)
518
+ - `@references/phase-5-feedback.md` — Phase 4 FEEDBACK 详细指令
519
+ - `@references/phase-6-ship.md` — Phase 5 SHIP 详细指令
520
+ - `@references/phase-7-land.md` — Phase 6 LAND 详细指令
521
+ - `@references/phase-8-cleanup.md` — Phase 8 CLEANUP 详细指令
521
522
 
522
523
  ---
523
524
 
@@ -538,7 +539,7 @@ See [Output Contract](#output-contract) below for the canonical machine-readable
538
539
  | One-shot = 单次迭代执行 | Boris Cherny interview | Phase 2 设计 |
539
540
  | 80% session 从 Plan Mode 开始 | Boris skill | Phase 1 设计 |
540
541
  | Verification improves 2-3x | Boris #1 tip | Phase 3 设计 |
541
- | Emergent requirements 无法消除 | Mike Cohn, Rafael Santos | Phase 4 人工设计 |
542
- | 78% failures invisible | arXiv research | Phase 4 必要性证明 |
542
+ | Emergent requirements 无法消除 | Mike Cohn, Rafael Santos | Phase 7 人工设计 |
543
+ | 78% failures invisible | arXiv research | Phase 7 必要性证明 |
543
544
  | Think → Plan → Build → Ship | gstack ETHOS | 整体流程设计 |
544
545
 
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-25
4
- **Commit:** ecb955b
3
+ **Generated:** 2026-06-26
4
+ **Commit:** afab469
5
5
  **Branch:** main
6
- **Version:** 0.10.15.0
6
+ **Version:** 0.10.17.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -0,0 +1,306 @@
1
+ // @no-test-required: TUI rendering plugin — tested via visual verification in OpenCode runtime
2
+ /**
3
+ * XP-Gate OpenCode TUI Slot Plugin
4
+ *
5
+ * Registers sidebar_content slot to display Sprint Flow progress
6
+ * from active sprint states discovered in .worktrees/sprint/ subdirectories.
7
+ *
8
+ * Rendered with @opentui/solid JSX — sidebar_content must return JSX.Element,
9
+ * NOT string (strings are silently ignored by OpenCode's TUI renderer).
10
+ *
11
+ * Users register this file in ~/.config/opencode/tui.json as:
12
+ * { "plugin": ["@boyingliu01/opencode-plugin/tui"] }
13
+ */
14
+ /** @jsxImportSource @opentui/solid */
15
+
16
+ import { existsSync, readFileSync, readdirSync } from "node:fs"
17
+ import { join, dirname, resolve, parse } from "node:path"
18
+ import { homedir } from "node:os"
19
+ import type { TuiPlugin, TuiSlotPlugin, TuiSlotProps } from "@opencode-ai/plugin/tui"
20
+ import { createMemo, Show, For } from "solid-js"
21
+
22
+ // ── Phase constants ──
23
+
24
+ const PHASE_NAMES: Record<string, string> = {
25
+ '-1': 'ISOLATE', '-0.5': 'AUTO-ESTIMATE', '0': 'THINK',
26
+ '1': 'PLAN', '2': 'BUILD', '3': 'REVIEW',
27
+ '4': 'SHIP', '5': 'LAND', '6': 'USER ACCEPT',
28
+ '7': 'FEEDBACK', '8': 'CLEANUP',
29
+ };
30
+
31
+ const PHASE_ORDER = ['-1', '-0.5', '0', '1', '2', '3', '4', '5', '6', '7', '8'];
32
+
33
+ // ── Types ──
34
+
35
+ interface SprintReq {
36
+ name?: string
37
+ status?: "completed" | "in_progress" | "pending"
38
+ }
39
+ interface SprintPhaseHistory {
40
+ phase: number | string
41
+ phase_name?: string
42
+ status?: "completed" | "in_progress" | "pending"
43
+ started_at?: string; completed_at?: string
44
+ duration_seconds?: number
45
+ reqs?: Record<string, SprintReq>
46
+ }
47
+ interface SprintState {
48
+ id?: string; phase?: number | string; status?: string; started_at?: string
49
+ task_description?: string
50
+ isolation?: { branch?: string; worktree_path?: string }
51
+ metrics?: { tests_passed?: number; tests_failed?: number; coverage_pct?: number }
52
+ phase_history?: SprintPhaseHistory[]
53
+ }
54
+ interface DiscoveredSprint {
55
+ state: SprintState; sourcePath: string; worktreeExists: boolean
56
+ }
57
+
58
+ // ── Discovery ──
59
+
60
+ const MAX_DISCOVERY_RESULTS = 5;
61
+
62
+ function findGitRoot(startDir: string): string | null {
63
+ let current = resolve(startDir);
64
+ const root = parse(current).root;
65
+ const seen = new Set<string>();
66
+ while (current !== root) {
67
+ if (seen.has(current)) break;
68
+ seen.add(current);
69
+ if (existsSync(join(current, '.git'))) return current;
70
+ const parent = dirname(current);
71
+ if (parent === current) break;
72
+ current = parent;
73
+ }
74
+ if (existsSync(join(root, '.git'))) return root;
75
+ return null;
76
+ }
77
+
78
+ function readSprintState(dir: string): SprintState | null {
79
+ try {
80
+ const sf = join(dir, '.sprint-state', 'sprint-state.json');
81
+ if (!existsSync(sf)) return null;
82
+ return JSON.parse(readFileSync(sf, 'utf8')) as SprintState;
83
+ } catch { return null; }
84
+ }
85
+
86
+ function checkWorktreeExists(p: string | undefined): boolean {
87
+ if (!p) return false;
88
+ try { return existsSync(p); } catch { return false; }
89
+ }
90
+
91
+ function parseTime(v: unknown): number {
92
+ return new Date(v as string).getTime();
93
+ }
94
+
95
+ function isStaleSprint(s: SprintState | null): boolean {
96
+ if (!s?.started_at) return false;
97
+ let latest = parseTime(s.started_at);
98
+ if (isNaN(latest)) return false;
99
+ for (const ph of s.phase_history ?? []) {
100
+ if (ph.completed_at) latest = Math.max(latest, parseTime(ph.completed_at));
101
+ if (ph.started_at) latest = Math.max(latest, parseTime(ph.started_at));
102
+ }
103
+ return latest > 0 && Date.now() - latest > 3_600_000;
104
+ }
105
+
106
+ function discoverActiveSprints(dir: string): DiscoveredSprint[] {
107
+ const gitRoot = findGitRoot(dir);
108
+ const results: DiscoveredSprint[] = [];
109
+
110
+ if (gitRoot) {
111
+ const base = join(gitRoot, '.worktrees', 'sprint');
112
+ let entries: { name: string; isDirectory: () => boolean }[] = [];
113
+ try { if (existsSync(base)) entries = readdirSync(base, { withFileTypes: true }); } catch { /* ok */ }
114
+ for (const e of entries) {
115
+ if (!e.isDirectory()) continue;
116
+ const d = join(base, e.name);
117
+ const state = readSprintState(d);
118
+ if (!state?.id) continue;
119
+ const wt = checkWorktreeExists(d);
120
+ if (isStaleSprint(state) && !wt) continue;
121
+ results.push({ state, sourcePath: join(d, '.sprint-state', 'sprint-state.json'), worktreeExists: wt });
122
+ }
123
+ }
124
+
125
+ const localState = readSprintState(dir);
126
+ if (localState?.id) {
127
+ const wp = localState.isolation?.worktree_path;
128
+ const hasWp = !!wp;
129
+ const wt = hasWp ? checkWorktreeExists(wp) : false;
130
+ if (!hasWp || wt) results.push({ state: localState, sourcePath: join(dir, '.sprint-state', 'sprint-state.json'), worktreeExists: wt });
131
+ }
132
+
133
+ const deduped = new Map<string, DiscoveredSprint>();
134
+ for (const e of results) {
135
+ const id = e.state.id!;
136
+ const existing = deduped.get(id);
137
+ if (!existing) { deduped.set(id, e); continue; }
138
+ if (e.worktreeExists && !existing.worktreeExists) { deduped.set(id, e); continue; }
139
+ if (!e.worktreeExists && existing.worktreeExists) continue;
140
+ const eTs = e.state.started_at ? new Date(e.state.started_at).getTime() : 0;
141
+ const xTs = existing.state.started_at ? new Date(existing.state.started_at).getTime() : 0;
142
+ if (eTs > xTs || (eTs === xTs && e.sourcePath < existing.sourcePath)) deduped.set(id, e);
143
+ }
144
+
145
+ return Array.from(deduped.values())
146
+ .sort((a, b) => {
147
+ const aTs = a.state.started_at ? new Date(a.state.started_at).getTime() : 0;
148
+ const bTs = b.state.started_at ? new Date(b.state.started_at).getTime() : 0;
149
+ return bTs - aTs || String(b.state.id).localeCompare(String(a.state.id));
150
+ })
151
+ .slice(0, MAX_DISCOVERY_RESULTS);
152
+ }
153
+
154
+ // ── Cache ──
155
+
156
+ let _cache: { data: DiscoveredSprint[]; upgradeNotice: string | null; ts: number; dir: string } | null = null;
157
+ const CACHE_TTL_MS = 5_000;
158
+
159
+ // ── Render helpers ──
160
+
161
+ function statusSymbol(status: string | undefined, currentPhase: string | number | undefined, key: string): string {
162
+ if (status === "completed") return "✓"
163
+ if (status === "in_progress") return "→"
164
+ if (String(currentPhase) === key) return "·"
165
+ return "○"
166
+ }
167
+
168
+ function buildPhaseLookup(state: SprintState): Record<string, SprintPhaseHistory> {
169
+ const lookup: Record<string, SprintPhaseHistory> = {}
170
+ for (const ph of state.phase_history ?? []) lookup[String(ph.phase)] = ph
171
+ return lookup
172
+ }
173
+
174
+ function phaseLine(key: string, history: SprintPhaseHistory | undefined, currentPhase: string | number | undefined): string {
175
+ const name = history?.phase_name || PHASE_NAMES[key] || key
176
+ const st = history?.status || (String(currentPhase) === key ? "in_progress" : "pending")
177
+ const sym = statusSymbol(st, currentPhase, key)
178
+ const suffix = st === "completed" ? "done" : st === "in_progress" ? "active" : ""
179
+ return `${sym} ${name.padEnd(14)} ${suffix}`.replace(/\s+$/, "")
180
+ }
181
+
182
+ function renderSprintTitle(state: SprintState): string {
183
+ if (state.task_description) return state.task_description;
184
+ if (state.id) {
185
+ const m = state.id.match(/sprint-(\d{4}-\d{2}-\d{2})-(\d+)/);
186
+ return m ? `Sprint ${m[1]} #${m[2]}` : state.id;
187
+ }
188
+ return 'Unknown Sprint';
189
+ }
190
+
191
+ // ── Upgrade notice ──
192
+
193
+ const UPGRADE_NOTICE_FILE = join(homedir(), ".xp-gate", "upgrade-notice.json")
194
+ const UPGRADE_NOTICE_TTL_MS = 86_400_000
195
+
196
+ function readUpgradeNotice(): string | null {
197
+ try {
198
+ if (!existsSync(UPGRADE_NOTICE_FILE)) return null
199
+ const raw = JSON.parse(readFileSync(UPGRADE_NOTICE_FILE, "utf8"))
200
+ if (Date.now() - raw.ts < UPGRADE_NOTICE_TTL_MS && raw.message) {
201
+ const icon = raw.kind === "upgraded" ? "✓" : raw.kind === "outdated" ? "↑" : "⚠"
202
+ return `${icon} ${raw.message}`
203
+ }
204
+ return null
205
+ } catch { return null; }
206
+ }
207
+
208
+ // ── JSX Components ──
209
+
210
+ function SprintCard(props: { sprint: DiscoveredSprint }) {
211
+ const { state } = props.sprint
212
+ const lookup = buildPhaseLookup(state)
213
+
214
+ return (
215
+ <box>
216
+ <text><b>SPRINT:</b> {renderSprintTitle(state)}</text>
217
+ <Show when={state.isolation?.branch}>
218
+ {(branch: () => string) => <text fg="#888888"> {branch()}</text>}
219
+ </Show>
220
+ <Show when={state.metrics?.tests_passed != null || state.metrics?.coverage_pct != null}>
221
+ <text fg="#888888">
222
+ {state.metrics?.tests_passed != null ? `tests:${state.metrics.tests_passed}` : ""}
223
+ {state.metrics?.coverage_pct != null ? ` cov:${state.metrics.coverage_pct}%` : ""}
224
+ </text>
225
+ </Show>
226
+ <Show when={isStaleSprint(state)}>
227
+ <text fg="#ffaa00"> ⚠ idle >1h</text>
228
+ </Show>
229
+ <For each={PHASE_ORDER}>
230
+ {(key: string) => {
231
+ const hist = lookup[key]
232
+ if (!hist && String(state.phase) !== key) return null
233
+ return <text>{phaseLine(key, hist, state.phase)}</text>
234
+ }}
235
+ </For>
236
+ </box>
237
+ )
238
+ }
239
+
240
+ function SprintSidebar(props: { sprints: DiscoveredSprint[] }) {
241
+ const display = props.sprints.slice(0, 3)
242
+
243
+ return (
244
+ <box>
245
+ <For each={display}>
246
+ {(sprint: DiscoveredSprint, i: () => number) => (
247
+ <>
248
+ <Show when={i() > 0}>
249
+ <text>---</text>
250
+ </Show>
251
+ <SprintCard sprint={sprint} />
252
+ </>
253
+ )}
254
+ </For>
255
+ <Show when={props.sprints.length > 3}>
256
+ <text>... +{props.sprints.length - 3} more</text>
257
+ </Show>
258
+ </box>
259
+ )
260
+ }
261
+
262
+ function renderContent(sprints: DiscoveredSprint[], upgradeNotice: string | null, dir: string) {
263
+ const components: any[] = []
264
+
265
+ if (sprints.length > 0) {
266
+ if (upgradeNotice) components.push(<text>{upgradeNotice}</text>)
267
+ components.push(<SprintSidebar sprints={sprints} />)
268
+ return components.length > 1 ? <box>{components}</box> : components[0]
269
+ }
270
+
271
+ const hasStateDir = existsSync(join(dir, ".sprint-state"))
272
+ const gitRoot = findGitRoot(dir)
273
+ const hasWorktreesRoot = gitRoot ? existsSync(join(gitRoot, ".worktrees")) : false
274
+
275
+ if (hasStateDir) return <box>{upgradeNotice ? <text>{upgradeNotice}</text> : null}<text><b>SPRINT FLOW</b></text><text> -> Initializing...</text></box>
276
+ if (hasWorktreesRoot) return <box>{upgradeNotice ? <text>{upgradeNotice}</text> : null}<text><b>SPRINT FLOW</b></text><text> · Ready...</text></box>
277
+ if (upgradeNotice) return <box><text>{upgradeNotice}</text></box>
278
+
279
+ return null
280
+ }
281
+
282
+ // ── TUI Slot Plugin ──
283
+
284
+ const tuiPlugin: TuiSlotPlugin = {
285
+ slots: {
286
+ sidebar_content: (_props: TuiSlotProps) => {
287
+ const dir = process.env.XP_GATE_PROJECT_DIR || process.cwd();
288
+ const now = Date.now();
289
+
290
+ if (_cache && _cache.dir === dir && now - _cache.ts < CACHE_TTL_MS) {
291
+ return renderContent(_cache.data, _cache.upgradeNotice, dir);
292
+ }
293
+
294
+ const sprints = discoverActiveSprints(dir);
295
+ const upgradeNotice = readUpgradeNotice()
296
+ _cache = { data: sprints, ts: now, dir, upgradeNotice };
297
+ return renderContent(sprints, upgradeNotice, dir);
298
+ },
299
+ },
300
+ }
301
+
302
+ const plugin: TuiPlugin = async (api, _options, _meta) => {
303
+ api.slots.register(tuiPlugin)
304
+ }
305
+
306
+ export { plugin as tui, readSprintState }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.10.15",
3
+ "version": "0.10.17",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 phases), and Delphi multi-expert review (>=90% consensus).",
6
6
  "author": {
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-25
4
- **Commit:** ecb955b
3
+ **Generated:** 2026-06-26
4
+ **Commit:** afab469
5
5
  **Branch:** main
6
- **Version:** 0.10.15.0
6
+ **Version:** 0.10.17.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-25
4
- **Commit:** ecb955b
3
+ **Generated:** 2026-06-26
4
+ **Commit:** afab469
5
5
  **Branch:** main
6
- **Version:** 0.10.15.0
6
+ **Version:** 0.10.17.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-25
4
- **Commit:** ecb955b
3
+ **Generated:** 2026-06-26
4
+ **Commit:** afab469
5
5
  **Branch:** main
6
- **Version:** 0.10.15.0
6
+ **Version:** 0.10.17.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -1,9 +1,9 @@
1
1
  # PRINCIPLES CHECKER MODULE
2
2
 
3
- **Generated:** 2026-06-25
4
- **Commit:** ecb955b
3
+ **Generated:** 2026-06-26
4
+ **Commit:** afab469
5
5
  **Branch:** main
6
- **Version:** 0.10.15.0
6
+ **Version:** 0.10.17.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Clean Code & SOLID principles checker — **Gate 4** of pre-commit. 14 rules × 9 language adapters, SARIF 2.1.0 output. Houses the **Boy Scout Rule** enforcement engine (Gate 6) and warning-baseline storage.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-06-25
4
- **Commit:** ecb955b
3
+ **Generated:** 2026-06-26
4
+ **Commit:** afab469
5
5
  **Branch:** main
6
- **Version:** 0.10.15.0
6
+ **Version:** 0.10.17.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.