ultra-builder-pro-cli 0.5.0
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/.ultra-template/delivery-report.json +9 -0
- package/.ultra-template/docs/research/README.md +53 -0
- package/.ultra-template/specs/architecture.md +250 -0
- package/.ultra-template/specs/discovery.md +248 -0
- package/.ultra-template/specs/product.md +223 -0
- package/.ultra-template/tasks/contexts/TEMPLATE.md +57 -0
- package/.ultra-template/tasks/tasks.json +33 -0
- package/.ultra-template/test-report.json +8 -0
- package/CHANGELOG.md +194 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/adapters/_shared/codex-assets.cjs +1128 -0
- package/adapters/_shared/codex-ultra-tools-entry.cjs +11 -0
- package/adapters/_shared/file-ops.cjs +115 -0
- package/adapters/_shared/frontmatter.cjs +52 -0
- package/adapters/_shared/handbook.cjs +144 -0
- package/adapters/_shared/md-to-toml.cjs +54 -0
- package/adapters/_shared/path-rewrite.cjs +79 -0
- package/adapters/_shared/rtk-detect.cjs +86 -0
- package/adapters/_shared/runtime-assets.cjs +70 -0
- package/adapters/_shared/settings-merge.cjs +76 -0
- package/adapters/_shared/tests/file-ops.test.cjs +54 -0
- package/adapters/_shared/tests/frontmatter.test.cjs +47 -0
- package/adapters/_shared/tests/handbook.test.cjs +86 -0
- package/adapters/_shared/tests/md-to-toml.test.cjs +48 -0
- package/adapters/_shared/tests/path-rewrite.test.cjs +60 -0
- package/adapters/_shared/tests/resolve-target.test.cjs +85 -0
- package/adapters/_shared/tests/rtk-detect.test.cjs +116 -0
- package/adapters/_shared/tests/runtime-assets.test.cjs +107 -0
- package/adapters/_shared/tests/settings-merge.test.cjs +85 -0
- package/adapters/_shared/tests/validate.test.cjs +30 -0
- package/adapters/_shared/validate.cjs +24 -0
- package/adapters/claude.js +182 -0
- package/adapters/codex.js +385 -0
- package/adapters/gemini.js +204 -0
- package/adapters/opencode.js +506 -0
- package/adapters/tests/claude.test.cjs +117 -0
- package/adapters/tests/codex-hook.test.cjs +90 -0
- package/adapters/tests/codex.test.cjs +356 -0
- package/adapters/tests/gemini.test.cjs +112 -0
- package/adapters/tests/opencode.test.cjs +246 -0
- package/agents/code-reviewer.md +102 -0
- package/agents/debugger.md +127 -0
- package/agents/review-code.md +125 -0
- package/agents/review-comments.md +98 -0
- package/agents/review-coordinator.md +146 -0
- package/agents/review-design.md +133 -0
- package/agents/review-errors.md +87 -0
- package/agents/review-tests.md +93 -0
- package/agents/tdd-runner.md +92 -0
- package/bin/handbook.js +54 -0
- package/bin/install.js +254 -0
- package/bin/orchestrator.js +214 -0
- package/commands/learn.md +41 -0
- package/commands/ultra-deliver.md +45 -0
- package/commands/ultra-dev.md +50 -0
- package/commands/ultra-init.md +51 -0
- package/commands/ultra-plan.md +49 -0
- package/commands/ultra-research.md +59 -0
- package/commands/ultra-status.md +46 -0
- package/commands/ultra-test.md +48 -0
- package/commands/ultra-think.md +45 -0
- package/docs/AGENT-CONTEXT.md +143 -0
- package/docs/ARCHITECTURE.md +197 -0
- package/docs/COMMIT-HASH-BACKFILL.md +101 -0
- package/docs/LEGACY-HERMES.md +1175 -0
- package/docs/PLAN.zh-CN.md +1683 -0
- package/docs/ROADMAP.md +102 -0
- package/docs/RUNTIME-COMPAT-MATRIX.md +98 -0
- package/docs/STATE-DB-ACCESS-POLICY.md +109 -0
- package/docs/USER-HANDBOOK-CONTRACT.md +61 -0
- package/hooks/README.md +24 -0
- package/hooks/active_task_context.py +44 -0
- package/hooks/adapters/codex.py +302 -0
- package/hooks/health_check.py +61 -0
- package/hooks/pre_stop_check.py +56 -0
- package/hooks/subagent_tracker.py +116 -0
- package/hooks/workflow_checkpoint.py +56 -0
- package/hooks/workflow_context.py +63 -0
- package/hooks/workflow_resume.py +50 -0
- package/mcp-server/lib/init-project.cjs +162 -0
- package/mcp-server/lib/llm-client.cjs +157 -0
- package/mcp-server/lib/llm-client.test.cjs +156 -0
- package/mcp-server/lib/plan-store.cjs +96 -0
- package/mcp-server/lib/plan-store.test.cjs +132 -0
- package/mcp-server/lib/prd-parser.cjs +110 -0
- package/mcp-server/lib/prd-parser.test.cjs +171 -0
- package/mcp-server/lib/pricing.cjs +53 -0
- package/mcp-server/lib/projector.cjs +158 -0
- package/mcp-server/lib/state-db.cjs +113 -0
- package/mcp-server/lib/state-db.test.cjs +116 -0
- package/mcp-server/lib/state-ops.cjs +725 -0
- package/mcp-server/lib/task-expander.cjs +123 -0
- package/mcp-server/lib/task-expander.test.cjs +253 -0
- package/mcp-server/lib/telemetry.cjs +72 -0
- package/mcp-server/lib/topo.cjs +131 -0
- package/mcp-server/lib/topo.test.cjs +158 -0
- package/mcp-server/server.cjs +482 -0
- package/mcp-server/tests/circuit-breaker.test.cjs +125 -0
- package/mcp-server/tests/commit-hash-flow.test.cjs +117 -0
- package/mcp-server/tests/concurrency-update.test.cjs +96 -0
- package/mcp-server/tests/concurrency.test.cjs +112 -0
- package/mcp-server/tests/db-ops.test.cjs +117 -0
- package/mcp-server/tests/init-project.test.cjs +141 -0
- package/mcp-server/tests/migrate.test.cjs +144 -0
- package/mcp-server/tests/projector.test.cjs +130 -0
- package/mcp-server/tests/server-contract.test.cjs +750 -0
- package/mcp-server/tests/staleness.test.cjs +190 -0
- package/mcp-server/tests/state-ops.test.cjs +286 -0
- package/mcp-server/tests/tagged-tasks.test.cjs +163 -0
- package/mcp-server/tests/telemetry.test.cjs +205 -0
- package/orchestrator/auto-merge.cjs +109 -0
- package/orchestrator/code-graph-watcher.cjs +121 -0
- package/orchestrator/daemon.cjs +112 -0
- package/orchestrator/dispatch-rules.cjs +93 -0
- package/orchestrator/parallel-orchestrator.cjs +195 -0
- package/orchestrator/planner/expand-prompt.cjs +62 -0
- package/orchestrator/planner/expand-prompt.test.cjs +52 -0
- package/orchestrator/planner/plan-builder.cjs +116 -0
- package/orchestrator/planner/plan-builder.test.cjs +165 -0
- package/orchestrator/planner/prd-prompt.cjs +44 -0
- package/orchestrator/planner/prd-prompt.test.cjs +30 -0
- package/orchestrator/recovery.cjs +70 -0
- package/orchestrator/session-runner.cjs +224 -0
- package/orchestrator/skill-miner.cjs +133 -0
- package/orchestrator/tests/auto-merge.test.cjs +216 -0
- package/orchestrator/tests/code-graph-watcher.test.cjs +183 -0
- package/orchestrator/tests/daemon.test.cjs +260 -0
- package/orchestrator/tests/dispatch-rules.test.cjs +122 -0
- package/orchestrator/tests/parallel-orchestrator.test.cjs +233 -0
- package/orchestrator/tests/phase-8b-gate.test.cjs +183 -0
- package/orchestrator/tests/recovery.test.cjs +190 -0
- package/orchestrator/tests/session-runner.test.cjs +314 -0
- package/orchestrator/tests/skill-miner.test.cjs +147 -0
- package/orchestrator/tests/worktree-manager.test.cjs +148 -0
- package/orchestrator/worktree-manager.cjs +131 -0
- package/output-styles/coding-vibes.md +67 -0
- package/output-styles/structural-thinking.md +61 -0
- package/package.json +109 -0
- package/settings.json +8 -0
- package/skills/cc-collab/SKILL.md +41 -0
- package/skills/code-review-expert/SKILL.md +118 -0
- package/skills/code-review-expert/references/code-quality-checklist.md +112 -0
- package/skills/code-review-expert/references/integration-checklist.md +46 -0
- package/skills/code-review-expert/references/removal-plan.md +52 -0
- package/skills/code-review-expert/references/security-checklist.md +100 -0
- package/skills/code-review-expert/references/solid-checklist.md +65 -0
- package/skills/codex-collab/SKILL.md +52 -0
- package/skills/codex-collab/evals/evals.json +40 -0
- package/skills/gemini-collab/SKILL.md +51 -0
- package/skills/gemini-collab/evals/evals.json +42 -0
- package/skills/integration-rules/SKILL.md +71 -0
- package/skills/learn/SKILL.md +117 -0
- package/skills/security-rules/SKILL.md +60 -0
- package/skills/testing-rules/SKILL.md +56 -0
- package/skills/ultra-deliver/SKILL.md +171 -0
- package/skills/ultra-dev/SKILL.md +324 -0
- package/skills/ultra-init/SKILL.md +148 -0
- package/skills/ultra-plan/SKILL.md +314 -0
- package/skills/ultra-research/SKILL.md +156 -0
- package/skills/ultra-research/steps/step-00-problem-validation.md +154 -0
- package/skills/ultra-research/steps/step-01-opportunity-discovery.md +172 -0
- package/skills/ultra-research/steps/step-02-market-assessment.md +195 -0
- package/skills/ultra-research/steps/step-03-competitive-landscape.md +197 -0
- package/skills/ultra-research/steps/step-04-product-strategy.md +196 -0
- package/skills/ultra-research/steps/step-05-assumptions-validation.md +234 -0
- package/skills/ultra-research/steps/step-10-user-personas.md +169 -0
- package/skills/ultra-research/steps/step-11-user-scenarios.md +181 -0
- package/skills/ultra-research/steps/step-20-user-stories.md +156 -0
- package/skills/ultra-research/steps/step-21-features-scope.md +157 -0
- package/skills/ultra-research/steps/step-22-success-metrics.md +195 -0
- package/skills/ultra-research/steps/step-30-architecture-context.md +154 -0
- package/skills/ultra-research/steps/step-31-solution-strategy.md +172 -0
- package/skills/ultra-research/steps/step-32-building-blocks.md +188 -0
- package/skills/ultra-research/steps/step-40-deployment.md +123 -0
- package/skills/ultra-research/steps/step-41-quality-risks.md +135 -0
- package/skills/ultra-research/steps/step-99-synthesis.md +188 -0
- package/skills/ultra-review/SKILL.md +437 -0
- package/skills/ultra-review/references/unified-schema.md +127 -0
- package/skills/ultra-review/scripts/review_verdict_update.py +99 -0
- package/skills/ultra-review/scripts/review_wait.py +120 -0
- package/skills/ultra-status/SKILL.md +162 -0
- package/skills/ultra-test/SKILL.md +195 -0
- package/skills/ultra-think/SKILL.md +109 -0
- package/skills/ultra-verify/SKILL.md +111 -0
- package/skills/ultra-verify/evals/evals.json +51 -0
- package/skills/ultra-verify/references/confidence-system.md +38 -0
- package/skills/ultra-verify/references/cross-verify-modes.md +73 -0
- package/skills/ultra-verify/scripts/verify_wait.py +131 -0
- package/spec/cli-protocol.md +118 -0
- package/spec/mcp-tools.yaml +658 -0
- package/spec/schemas/command-manifest.schema.json +47 -0
- package/spec/schemas/context-file.v4.5.schema.json +54 -0
- package/spec/schemas/mcp-tools.schema.json +136 -0
- package/spec/schemas/skill-manifest.schema.json +94 -0
- package/spec/schemas/state-db.sql +154 -0
- package/spec/schemas/tasks.v4.5.schema.json +66 -0
- package/ultra-tools/cli.cjs +121 -0
- package/ultra-tools/commands/db.cjs +175 -0
- package/ultra-tools/commands/legacy-memory.cjs +159 -0
- package/ultra-tools/commands/migrate.cjs +354 -0
- package/ultra-tools/commands/session.cjs +180 -0
- package/ultra-tools/commands/status.cjs +158 -0
- package/ultra-tools/commands/task.cjs +95 -0
- package/ultra-tools/legacy-memory.test.cjs +80 -0
- package/ultra-tools/status-command.test.cjs +132 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ultra-review
|
|
3
|
+
description: "Parallel code review orchestration with 6 specialized agents + coordinator. Zero context pollution - all output via JSON files."
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /ultra-review - Ultra Review System
|
|
8
|
+
|
|
9
|
+
Orchestrates parallel code review using specialized agents. All findings written to JSON files to prevent context window pollution.
|
|
10
|
+
|
|
11
|
+
## Workflow Tracking (MANDATORY)
|
|
12
|
+
|
|
13
|
+
**On command start**, create tasks for each major step using `TaskCreate`:
|
|
14
|
+
|
|
15
|
+
| Step | Subject | activeForm |
|
|
16
|
+
|------|---------|------------|
|
|
17
|
+
| 1 | Setup & Scope Detection | Detecting diff scope... |
|
|
18
|
+
| 2 | Agent Selection | Selecting review agents... |
|
|
19
|
+
| 3 | Background Execution | Running review agents... |
|
|
20
|
+
| 4 | Wait & Coordinate | Waiting for agent results... |
|
|
21
|
+
| 5 | Report to User | Generating review report... |
|
|
22
|
+
|
|
23
|
+
**Before each step**: `TaskUpdate` → `status: "in_progress"`
|
|
24
|
+
**After each step**: `TaskUpdate` → `status: "completed"`
|
|
25
|
+
**On context recovery**: `TaskList` → resume from last incomplete step
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
/ultra-review # Full review (smart skip based on diff content)
|
|
31
|
+
/ultra-review all # Force ALL 6 agents, no auto-skip (pre-merge gate)
|
|
32
|
+
/ultra-review quick # Quick review (review-code only)
|
|
33
|
+
/ultra-review security # Security focus (review-code + review-errors)
|
|
34
|
+
/ultra-review tests # Test quality focus (review-tests only)
|
|
35
|
+
/ultra-review recheck # Re-check only P0/P1 files from last session
|
|
36
|
+
/ultra-review delta # Review only changes since last review session
|
|
37
|
+
/ultra-review <agents...> # Custom: e.g., /ultra-review tests errors types
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Scope Options (combinable with any mode)
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
/ultra-review --pr 123 # Review PR #123 diff (base...head)
|
|
44
|
+
/ultra-review --range main..HEAD # Review specific commit range
|
|
45
|
+
/ultra-review --range abc123 # Review single commit
|
|
46
|
+
/ultra-review security --pr 42 # Security review scoped to PR #42
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Orchestration Flow
|
|
50
|
+
|
|
51
|
+
### Phase 1: Setup & Scope Detection
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
# 1. Determine session context
|
|
55
|
+
BRANCH = git branch --show-current # e.g., "feat/task-3-auth"
|
|
56
|
+
ITER = count existing sessions matching this branch + 1 # iteration number
|
|
57
|
+
|
|
58
|
+
# 2. Create session directory with full context
|
|
59
|
+
SESSION_ID = "<YYYYMMDD-HHmmss>-<branch>-iter<N>"
|
|
60
|
+
# e.g., "20260214-103000-feat-task-3-auth-iter2"
|
|
61
|
+
SESSION_PATH = ".ultra/reviews/{SESSION_ID}/"
|
|
62
|
+
mkdir -p SESSION_PATH
|
|
63
|
+
|
|
64
|
+
# 3. Update session index (placeholder — verdict filled in Phase 5)
|
|
65
|
+
# Append entry to .ultra/reviews/index.json with verdict="pending"
|
|
66
|
+
# See Session Index below for full schema
|
|
67
|
+
|
|
68
|
+
# 4. Cleanup (see Lifecycle Management below)
|
|
69
|
+
|
|
70
|
+
# 5. Detect diff scope (priority order)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Scope resolution priority:**
|
|
74
|
+
|
|
75
|
+
| Flag | Diff Command | DIFF_RANGE |
|
|
76
|
+
|------|-------------|------------|
|
|
77
|
+
| `--pr NUMBER` | `gh pr diff NUMBER --name-only` | PR base...head |
|
|
78
|
+
| `--range RANGE` | `git diff RANGE --name-only` | Specified range |
|
|
79
|
+
| _(default)_ | `git diff --name-only HEAD` + `git diff --cached --name-only` | HEAD (unstaged + staged) |
|
|
80
|
+
|
|
81
|
+
**PR scope details** (`--pr NUMBER`):
|
|
82
|
+
```bash
|
|
83
|
+
# Get PR metadata for session naming
|
|
84
|
+
gh pr view NUMBER --json headRefName,baseRefName,title
|
|
85
|
+
# Get changed files
|
|
86
|
+
gh pr diff NUMBER --name-only
|
|
87
|
+
# Set DIFF_RANGE to base...head for agents
|
|
88
|
+
DIFF_RANGE="$(gh pr view NUMBER --json baseRefName -q .baseRefName)..HEAD"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Range scope details** (`--range RANGE`):
|
|
92
|
+
```bash
|
|
93
|
+
git diff RANGE --name-only
|
|
94
|
+
DIFF_RANGE="RANGE"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Phase 2: Agent Selection
|
|
98
|
+
|
|
99
|
+
Based on the mode argument and file classification:
|
|
100
|
+
|
|
101
|
+
**Mode: `full` (default)**
|
|
102
|
+
Select all applicable agents based on diff content:
|
|
103
|
+
|
|
104
|
+
| Agent | Skip Condition | Warning |
|
|
105
|
+
|-------|---------------|---------|
|
|
106
|
+
| review-code | Never skip | - |
|
|
107
|
+
| review-tests | No test files in diff AND no source files without tests | WARN: "Code changed without test updates" |
|
|
108
|
+
| review-errors | No error handling code detected | - |
|
|
109
|
+
| review-design | No type definitions AND no function-level changes in diff | - |
|
|
110
|
+
| review-comments | No comment changes in diff | - |
|
|
111
|
+
|
|
112
|
+
**Auto-skip detection commands:**
|
|
113
|
+
```bash
|
|
114
|
+
# Has type definitions?
|
|
115
|
+
grep -l "interface \|type \|class \|enum " <diff_files>
|
|
116
|
+
|
|
117
|
+
# Has comment changes?
|
|
118
|
+
git diff HEAD -- <diff_files> | grep "^[+-].*//\|^[+-].*\*\|^[+-].*#"
|
|
119
|
+
|
|
120
|
+
# Has test files?
|
|
121
|
+
echo "<diff_files>" | grep -E "\.(test|spec)\.(ts|tsx|js|jsx)$|test_.*\.py$|.*_test\.go$"
|
|
122
|
+
|
|
123
|
+
# Has error handling?
|
|
124
|
+
grep -l "try\|catch\|\.catch\|throw\|Error(" <diff_files>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Mode: `all`** → Force ALL 6 agents, no auto-skip. Use for pre-merge gates (`/ultra-dev`).
|
|
128
|
+
**Mode: `quick`** → review-code only
|
|
129
|
+
**Mode: `security`** → review-code + review-errors
|
|
130
|
+
**Mode: `tests`** → review-tests only
|
|
131
|
+
**Mode: `recheck`** → See Recheck Logic below
|
|
132
|
+
**Mode: `delta`** → See Delta Logic below
|
|
133
|
+
**Mode: custom** → Parse agent names from arguments
|
|
134
|
+
|
|
135
|
+
### Phase 3: Background Execution (Zero Context Pollution)
|
|
136
|
+
|
|
137
|
+
Launch ALL selected agents in **background mode** (`run_in_background: true`) using multiple Task tool calls in a single message. This prevents agent output from polluting the main conversation context.
|
|
138
|
+
|
|
139
|
+
Each agent receives this prompt template:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
You are running as part of /ultra-review pipeline.
|
|
143
|
+
|
|
144
|
+
SESSION_PATH: {SESSION_PATH}
|
|
145
|
+
OUTPUT_FILE: {agent-name}.json
|
|
146
|
+
DIFF_FILES: {comma-separated file list}
|
|
147
|
+
DIFF_RANGE: {resolved DIFF_RANGE from Phase 1}
|
|
148
|
+
|
|
149
|
+
Review the changed files and write your findings as JSON to:
|
|
150
|
+
{SESSION_PATH}/{agent-name}.json
|
|
151
|
+
|
|
152
|
+
Follow the ultra-review-findings-v1 schema exactly. Read the schema from:
|
|
153
|
+
$CLAUDE_PLUGIN_ROOT/skills/ultra-review/references/unified-schema.md
|
|
154
|
+
|
|
155
|
+
Only report findings with confidence >= 75.
|
|
156
|
+
|
|
157
|
+
After writing the JSON file, output one line: "Wrote N findings (P0:X P1:X P2:X P3:X) to <filepath>"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Important**: Use `subagent_type` matching the agent name (e.g., `review-code`, `review-tests`, etc.).
|
|
161
|
+
|
|
162
|
+
**Important**: Set `run_in_background: true` on every Task call. Do NOT read TaskOutput — this avoids injecting agent results into main context.
|
|
163
|
+
|
|
164
|
+
Maximum 12 findings per agent. If you find more, report only the top 12 by severity (P0 first),
|
|
165
|
+
then confidence. Note total count in your output line: "Wrote 12/23 findings..."
|
|
166
|
+
|
|
167
|
+
**CRITICAL PROHIBITION**: After launching background agents, you MUST:
|
|
168
|
+
1. Call `review_wait.py` IMMEDIATELY — do NOT process idle notifications
|
|
169
|
+
2. NEVER call TaskOutput for any review agent — their output goes to JSON files only
|
|
170
|
+
3. Ignore all agent idle/completion messages between launch and wait script return
|
|
171
|
+
4. The ONLY information path from agents is: wait script → Read SUMMARY.json
|
|
172
|
+
|
|
173
|
+
Violation of these rules causes context overflow. The review agents write to files; you read from files.
|
|
174
|
+
|
|
175
|
+
### Phase 4: Wait & Coordinate (File-Based)
|
|
176
|
+
|
|
177
|
+
**Step 4a: Wait for agents** — Use Bash to block until all agents finish writing:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
python3 "$CLAUDE_PLUGIN_ROOT/skills/ultra-review/scripts/review_wait.py" {SESSION_PATH} agents {AGENT_COUNT}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This polls for `review-*.json` files every 2 seconds. Timeout: 5 minutes.
|
|
184
|
+
Output is structured JSON on stdout:
|
|
185
|
+
- `status: "complete"` → all agents done, proceed normally
|
|
186
|
+
- `status: "partial"` → timeout, log missing agents, pass only `agents_done` to coordinator, note partial review in SUMMARY
|
|
187
|
+
- Exit code 1 (0 agents) → skip review, warn user, allow ultra-dev to continue
|
|
188
|
+
|
|
189
|
+
**Step 4b: Launch coordinator in background:**
|
|
190
|
+
|
|
191
|
+
Launch review-coordinator with `run_in_background: true`:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
You are the review coordinator.
|
|
195
|
+
|
|
196
|
+
SESSION_PATH: {SESSION_PATH}
|
|
197
|
+
AGENTS_RUN: {list of agents from wait output}
|
|
198
|
+
|
|
199
|
+
Read all review-*.json files in the session directory, deduplicate findings,
|
|
200
|
+
compute verdict, and generate SUMMARY.md + SUMMARY.json.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Step 4c: Wait for coordinator:**
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
python3 "$CLAUDE_PLUGIN_ROOT/skills/ultra-review/scripts/review_wait.py" {SESSION_PATH} summary
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Output is a single verdict line (~15 tokens).
|
|
210
|
+
|
|
211
|
+
### Phase 5: Report to User
|
|
212
|
+
|
|
213
|
+
After `review_wait.py summary` returns:
|
|
214
|
+
|
|
215
|
+
1. **Read SUMMARY.json** (one Read call, ~500 tokens) to get verdict and counts
|
|
216
|
+
2. **Update index.json** — fill in the placeholder entry with actual verdict, p0, p1, total from SUMMARY.json
|
|
217
|
+
3. Present a concise summary:
|
|
218
|
+
|
|
219
|
+
```markdown
|
|
220
|
+
## Review Complete
|
|
221
|
+
|
|
222
|
+
**Verdict**: {APPROVE | COMMENT | REQUEST_CHANGES}
|
|
223
|
+
**Findings**: X total (P0: A, P1: B, P2: C, P3: D)
|
|
224
|
+
**Agents**: N ran, M succeeded
|
|
225
|
+
|
|
226
|
+
### Top Findings
|
|
227
|
+
1. [P0] {title} - {file}:{line}
|
|
228
|
+
2. [P1] {title} - {file}:{line}
|
|
229
|
+
3. ...
|
|
230
|
+
(show up to 5 most critical findings)
|
|
231
|
+
|
|
232
|
+
Full report: {SESSION_PATH}/SUMMARY.md
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Then ask the user:
|
|
236
|
+
1. **Fix all** - Fix all P0 and P1 issues
|
|
237
|
+
2. **Fix P0 only** - Fix only critical issues
|
|
238
|
+
3. **View full report** - Read SUMMARY.md
|
|
239
|
+
4. **Skip** - No fixes needed
|
|
240
|
+
|
|
241
|
+
## Recheck Logic
|
|
242
|
+
|
|
243
|
+
When mode is `recheck`:
|
|
244
|
+
1. Find the most recent session **for the current branch** in `index.json`
|
|
245
|
+
2. Read its `SUMMARY.json`
|
|
246
|
+
3. Extract files with P0 or P1 findings
|
|
247
|
+
4. Run review-code only on those specific files
|
|
248
|
+
5. Compare with previous findings to show delta (resolved / new / unchanged)
|
|
249
|
+
|
|
250
|
+
## Delta Logic
|
|
251
|
+
|
|
252
|
+
When mode is `delta`:
|
|
253
|
+
1. Find the most recent session **for the current branch** in `index.json`
|
|
254
|
+
2. Read its `SUMMARY.json` to get the scope of last review
|
|
255
|
+
3. Identify files changed since the last review:
|
|
256
|
+
```bash
|
|
257
|
+
# If last session was commit-based, diff from that commit
|
|
258
|
+
git diff --name-only <last-reviewed-commit>..HEAD
|
|
259
|
+
# If last session was working-tree-based, diff current unstaged + staged
|
|
260
|
+
git diff --name-only HEAD
|
|
261
|
+
```
|
|
262
|
+
4. **Exclude** files that have NOT changed since last review (already reviewed)
|
|
263
|
+
5. Run full agent set only on the newly changed files
|
|
264
|
+
6. In coordinator, merge with previous session findings (carry forward unresolved P0/P1)
|
|
265
|
+
|
|
266
|
+
**Use case**: Iterative development — fix issues, add code, only review what's new.
|
|
267
|
+
|
|
268
|
+
## Session Index
|
|
269
|
+
|
|
270
|
+
A lightweight index file tracks all sessions for cross-referencing:
|
|
271
|
+
|
|
272
|
+
**File**: `.ultra/reviews/index.json`
|
|
273
|
+
|
|
274
|
+
```json
|
|
275
|
+
{
|
|
276
|
+
"sessions": [
|
|
277
|
+
{
|
|
278
|
+
"id": "20260214-103000-feat-task-3-auth-iter1",
|
|
279
|
+
"branch": "feat/task-3-auth",
|
|
280
|
+
"iteration": 1,
|
|
281
|
+
"mode": "full",
|
|
282
|
+
"timestamp": "2026-02-14T10:30:00Z",
|
|
283
|
+
"verdict": "REQUEST_CHANGES",
|
|
284
|
+
"p0": 2,
|
|
285
|
+
"p1": 3,
|
|
286
|
+
"total": 12,
|
|
287
|
+
"parent": null
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"id": "20260214-113000-feat-task-3-auth-iter2",
|
|
291
|
+
"branch": "feat/task-3-auth",
|
|
292
|
+
"iteration": 2,
|
|
293
|
+
"mode": "recheck",
|
|
294
|
+
"timestamp": "2026-02-14T11:30:00Z",
|
|
295
|
+
"verdict": "COMMENT",
|
|
296
|
+
"p0": 0,
|
|
297
|
+
"p1": 1,
|
|
298
|
+
"total": 4,
|
|
299
|
+
"parent": "20260214-103000-feat-task-3-auth-iter1"
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Fields**:
|
|
306
|
+
|
|
307
|
+
| Field | Description |
|
|
308
|
+
|-------|-------------|
|
|
309
|
+
| `id` | Session directory name |
|
|
310
|
+
| `branch` | Git branch at review time |
|
|
311
|
+
| `iteration` | Nth review for this branch (auto-incremented) |
|
|
312
|
+
| `mode` | Review mode used (full/quick/security/recheck/delta) |
|
|
313
|
+
| `timestamp` | ISO 8601 |
|
|
314
|
+
| `verdict` | APPROVE / COMMENT / REQUEST_CHANGES |
|
|
315
|
+
| `p0`, `p1`, `total` | Finding counts from SUMMARY.json |
|
|
316
|
+
| `parent` | Previous session ID for this branch (forms iteration chain) |
|
|
317
|
+
|
|
318
|
+
**Branch-scoped queries**:
|
|
319
|
+
- `recheck` / `delta`: filter `index.json` by `branch == current branch`, take latest
|
|
320
|
+
- Avoids cross-task pollution when multiple branches are active
|
|
321
|
+
|
|
322
|
+
## Lifecycle Management
|
|
323
|
+
|
|
324
|
+
**Cleanup strategy** (runs at Phase 1 of every `/ultra-review`):
|
|
325
|
+
|
|
326
|
+
| Rule | Action |
|
|
327
|
+
|------|--------|
|
|
328
|
+
| Session > 7 days AND verdict = APPROVE | Delete directory + remove from index |
|
|
329
|
+
| Session > 7 days AND verdict = COMMENT | Delete directory + remove from index |
|
|
330
|
+
| Session > 7 days AND verdict = REQUEST_CHANGES | **Keep** (unresolved P0s should not be silently deleted) |
|
|
331
|
+
| Session > 30 days (any verdict) | Delete directory + remove from index |
|
|
332
|
+
| Per-branch limit: > 5 sessions | Delete oldest APPROVE/COMMENT sessions for that branch |
|
|
333
|
+
|
|
334
|
+
**Implementation**:
|
|
335
|
+
```python
|
|
336
|
+
# In Phase 1, after creating session directory:
|
|
337
|
+
index = read index.json (or create if missing)
|
|
338
|
+
|
|
339
|
+
# 1. Remove stale sessions
|
|
340
|
+
now = current_time()
|
|
341
|
+
for session in index.sessions:
|
|
342
|
+
age_days = (now - session.timestamp).days
|
|
343
|
+
if age_days > 30:
|
|
344
|
+
delete(session)
|
|
345
|
+
elif age_days > 7 and session.verdict != 'REQUEST_CHANGES':
|
|
346
|
+
delete(session)
|
|
347
|
+
|
|
348
|
+
# 2. Per-branch cap (keep latest 5)
|
|
349
|
+
branch_sessions = [s for s in index.sessions if s.branch == current_branch]
|
|
350
|
+
if len(branch_sessions) > 5:
|
|
351
|
+
# Sort by timestamp, keep latest 5, delete rest (except REQUEST_CHANGES)
|
|
352
|
+
to_remove = sorted(branch_sessions, key=timestamp)[:-5]
|
|
353
|
+
for s in to_remove:
|
|
354
|
+
if s.verdict != 'REQUEST_CHANGES':
|
|
355
|
+
delete(s)
|
|
356
|
+
|
|
357
|
+
# 3. Write updated index
|
|
358
|
+
write index.json
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Fix Flow
|
|
362
|
+
|
|
363
|
+
When user selects "Fix all" or "Fix P0 only" after review:
|
|
364
|
+
|
|
365
|
+
**Step 0: Context Reset** (if verdict is REQUEST_CHANGES):
|
|
366
|
+
- Run `/compact` to clear TDD cycle history from context
|
|
367
|
+
- After compact, read `.ultra/workflow-state.json` to restore task context
|
|
368
|
+
- Read `SUMMARY.json` fresh to get actionable findings
|
|
369
|
+
- Then proceed with fixes below
|
|
370
|
+
|
|
371
|
+
1. **Read SUMMARY.json** — extract actionable P0/P1 items, sort by severity (P0 first)
|
|
372
|
+
2. **Group by file** — organize findings per file
|
|
373
|
+
3. **For each file with findings** (with circuit breaker tracking):
|
|
374
|
+
a. Read the file once
|
|
375
|
+
b. Apply ALL P0/P1 fixes for that file (batch within same file)
|
|
376
|
+
c. Run the RELEVANT test file only:
|
|
377
|
+
- Detect: `foo.ts` → `foo.test.ts` / `foo.spec.ts`
|
|
378
|
+
- Run: `npx jest <test-file> --bail` or `pytest <test-file> -x`
|
|
379
|
+
d. If test passes → mark findings as addressed, move to next file
|
|
380
|
+
e. If test fails from THIS fix → increment per-file failure counter, revert, try alternative approach
|
|
381
|
+
f. If test fails from pre-existing issue → note it, continue
|
|
382
|
+
g. **Circuit Breaker**: If same file has 3 consecutive fix failures (test fails after each attempt):
|
|
383
|
+
- Display: "Circuit breaker: {filename} — 3 consecutive fix failures. Likely architectural issue, not isolated bug."
|
|
384
|
+
- Use AskUserQuestion:
|
|
385
|
+
- A) "Skip this file, continue with others"
|
|
386
|
+
- B) "Manually inspect this file" (exit auto-fix, user takes over)
|
|
387
|
+
- C) "Abandon this fix round" (end fix flow, write UNRESOLVED.md)
|
|
388
|
+
h. **Global Circuit Breaker**: If 3+ files trigger per-file circuit breaker:
|
|
389
|
+
- Display: "Multiple files failing repeatedly — suggests systematic issue, not isolated bugs."
|
|
390
|
+
- Recommend ending fix flow, write all unresolved findings to UNRESOLVED.md with tag `ARCHITECTURAL_CONCERN`
|
|
391
|
+
4. **After all files fixed**: run FULL test suite once as validation
|
|
392
|
+
5. **Update verdict** — after all P0 fixes applied and tests pass:
|
|
393
|
+
```bash
|
|
394
|
+
python3 "$CLAUDE_PLUGIN_ROOT/skills/ultra-review/scripts/review_verdict_update.py" {SESSION_PATH}
|
|
395
|
+
```
|
|
396
|
+
This recalculates the verdict from current P0/P1 counts and updates both SUMMARY.json and index.json.
|
|
397
|
+
Without this step, pre_stop_check will block on stale REQUEST_CHANGES verdict.
|
|
398
|
+
6. **Optionally**: `/ultra-review recheck` (counts as iteration, respects max 2 cap)
|
|
399
|
+
|
|
400
|
+
**Important**: The main agent (not review agents) performs fixes. Review agents are read-only by design.
|
|
401
|
+
|
|
402
|
+
## Integration with ultra-dev
|
|
403
|
+
|
|
404
|
+
This skill integrates with the TDD workflow:
|
|
405
|
+
- After `tdd-runner` confirms tests pass
|
|
406
|
+
- Before `commit` or PR creation
|
|
407
|
+
- Quick mode for iterative development
|
|
408
|
+
- Full mode for pre-merge gate
|
|
409
|
+
- Recheck mode after fixing review findings
|
|
410
|
+
- Delta mode for incremental review during long development sessions
|
|
411
|
+
|
|
412
|
+
## Error Handling
|
|
413
|
+
|
|
414
|
+
- If no diff detected: inform user, suggest using `--range` or `--pr` to specify scope
|
|
415
|
+
- If `--pr` fails (no gh CLI or no PR): fall back to default diff and warn user
|
|
416
|
+
- If an agent fails to write JSON: note in coordinator input, skip that agent's findings
|
|
417
|
+
- If coordinator fails: fall back to reading individual JSON files and presenting raw findings
|
|
418
|
+
- If no agents selected (all skipped): inform user with skip reasons
|
|
419
|
+
|
|
420
|
+
## Directory Structure
|
|
421
|
+
|
|
422
|
+
```
|
|
423
|
+
.ultra/reviews/
|
|
424
|
+
├── index.json # Session index (all sessions metadata)
|
|
425
|
+
├── 20260214-103000-feat-task-3-auth-iter1/ # First review
|
|
426
|
+
│ ├── review-code.json
|
|
427
|
+
│ ├── review-tests.json
|
|
428
|
+
│ ├── review-errors.json
|
|
429
|
+
│ ├── review-design.json
|
|
430
|
+
│ ├── review-comments.json
|
|
431
|
+
│ ├── SUMMARY.json
|
|
432
|
+
│ └── SUMMARY.md
|
|
433
|
+
└── 20260214-113000-feat-task-3-auth-iter2/ # Recheck after fixes
|
|
434
|
+
├── review-code.json
|
|
435
|
+
├── SUMMARY.json
|
|
436
|
+
└── SUMMARY.md
|
|
437
|
+
```
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Ultra Review - Unified JSON Schema v1
|
|
2
|
+
|
|
3
|
+
All review agents MUST output findings in this exact format. No deviations.
|
|
4
|
+
|
|
5
|
+
## Schema
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"$schema": "ultra-review-findings-v1",
|
|
10
|
+
"agent": "<agent-name>",
|
|
11
|
+
"session": "<timestamp>-<branch>",
|
|
12
|
+
"timestamp": "<ISO 8601>",
|
|
13
|
+
"scope": {
|
|
14
|
+
"files_analyzed": ["src/foo.ts", "src/bar.ts"],
|
|
15
|
+
"total_lines_analyzed": 342,
|
|
16
|
+
"diff_only": true
|
|
17
|
+
},
|
|
18
|
+
"summary": {
|
|
19
|
+
"total_findings": 5,
|
|
20
|
+
"by_severity": { "P0": 1, "P1": 2, "P2": 1, "P3": 1 },
|
|
21
|
+
"verdict": "REQUEST_CHANGES"
|
|
22
|
+
},
|
|
23
|
+
"findings": [
|
|
24
|
+
{
|
|
25
|
+
"id": "<agent-name>-001",
|
|
26
|
+
"severity": "P0",
|
|
27
|
+
"confidence": 95,
|
|
28
|
+
"category": "security",
|
|
29
|
+
"subcategory": "injection",
|
|
30
|
+
"title": "SQL injection via string concatenation",
|
|
31
|
+
"file": "src/api/handler.ts",
|
|
32
|
+
"line": 45,
|
|
33
|
+
"line_end": 47,
|
|
34
|
+
"code_snippet": "const query = `SELECT * FROM users WHERE id = ${userId}`",
|
|
35
|
+
"description": "User-controlled input directly interpolated into SQL query string.",
|
|
36
|
+
"suggestion": "Use parameterized query: db.query('SELECT * FROM users WHERE id = $1', [userId])",
|
|
37
|
+
"claude_md_rule": "security.forbidden_patterns.sql_string_concat"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"positive_observations": [
|
|
41
|
+
"Clean separation between domain and infrastructure layers"
|
|
42
|
+
],
|
|
43
|
+
"metadata": {
|
|
44
|
+
"duration_ms": 15000,
|
|
45
|
+
"model": "claude-opus-4-6",
|
|
46
|
+
"turns_used": 12
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Field Definitions
|
|
52
|
+
|
|
53
|
+
### Top-Level
|
|
54
|
+
|
|
55
|
+
| Field | Type | Required | Description |
|
|
56
|
+
|-------|------|----------|-------------|
|
|
57
|
+
| `$schema` | string | YES | Always `"ultra-review-findings-v1"` |
|
|
58
|
+
| `agent` | string | YES | Agent name: `review-code`, `review-tests`, `review-errors`, `review-comments`, `review-design` |
|
|
59
|
+
| `session` | string | YES | Session ID provided in the task prompt |
|
|
60
|
+
| `timestamp` | string | YES | ISO 8601 timestamp when review completed |
|
|
61
|
+
| `scope` | object | YES | What was analyzed |
|
|
62
|
+
| `summary` | object | YES | Aggregate counts |
|
|
63
|
+
| `findings` | array | YES | Individual findings (can be empty) |
|
|
64
|
+
| `positive_observations` | array | YES | Good patterns found (can be empty) |
|
|
65
|
+
| `metadata` | object | YES | Execution metadata |
|
|
66
|
+
|
|
67
|
+
### Finding Object
|
|
68
|
+
|
|
69
|
+
| Field | Type | Required | Description |
|
|
70
|
+
|-------|------|----------|-------------|
|
|
71
|
+
| `id` | string | YES | Unique ID: `<agent-name>-<NNN>` (zero-padded 3 digits) |
|
|
72
|
+
| `severity` | enum | YES | `P0` (critical), `P1` (high), `P2` (medium), `P3` (low) |
|
|
73
|
+
| `confidence` | int | YES | 0-100. Only include findings with confidence >= 75 |
|
|
74
|
+
| `category` | enum | YES | See Category Enum below |
|
|
75
|
+
| `subcategory` | string | NO | Free-text refinement of category |
|
|
76
|
+
| `title` | string | YES | One-line summary (max 80 chars) |
|
|
77
|
+
| `file` | string | YES | Relative file path from repo root |
|
|
78
|
+
| `line` | int | YES | Start line number |
|
|
79
|
+
| `line_end` | int | NO | End line number (if multi-line) |
|
|
80
|
+
| `code_snippet` | string | YES | Relevant code (max 5 lines) |
|
|
81
|
+
| `description` | string | YES | Detailed explanation |
|
|
82
|
+
| `suggestion` | string | YES | Concrete fix or recommendation |
|
|
83
|
+
| `claude_md_rule` | string | NO | Reference to CLAUDE.md rule if applicable |
|
|
84
|
+
|
|
85
|
+
### Category Enum
|
|
86
|
+
|
|
87
|
+
| Value | Used By |
|
|
88
|
+
|-------|---------|
|
|
89
|
+
| `security` | review-code, review-errors |
|
|
90
|
+
| `error-handling` | review-errors, review-code |
|
|
91
|
+
| `code-quality` | review-code, review-design |
|
|
92
|
+
| `test-quality` | review-tests |
|
|
93
|
+
| `type-design` | review-design |
|
|
94
|
+
| `comments` | review-comments |
|
|
95
|
+
| `simplification` | review-design |
|
|
96
|
+
| `performance` | review-code, review-design |
|
|
97
|
+
| `architecture` | review-code, review-design |
|
|
98
|
+
| `integration` | review-code |
|
|
99
|
+
| `scope-drift` | review-code |
|
|
100
|
+
| `spec-compliance` | review-code |
|
|
101
|
+
| `forbidden-pattern` | review-code, review-tests, review-comments |
|
|
102
|
+
|
|
103
|
+
### Verdict Rules
|
|
104
|
+
|
|
105
|
+
| Condition | Verdict |
|
|
106
|
+
|-----------|---------|
|
|
107
|
+
| Any P0 finding | `REQUEST_CHANGES` |
|
|
108
|
+
| P1 count > 3 | `REQUEST_CHANGES` |
|
|
109
|
+
| P1 count > 0 | `COMMENT` |
|
|
110
|
+
| No P0 or P1 | `APPROVE` |
|
|
111
|
+
|
|
112
|
+
### Severity Mapping
|
|
113
|
+
|
|
114
|
+
| Level | Name | Criteria | Action |
|
|
115
|
+
|-------|------|----------|--------|
|
|
116
|
+
| **P0** | Critical | Security vulnerability, data loss, correctness bug, empty catch block | Must block merge |
|
|
117
|
+
| **P1** | High | Logic error, SOLID violation, performance regression, mock violation, catch returning null | Should fix before merge |
|
|
118
|
+
| **P2** | Medium | Code smell, maintainability, deep nesting, optional chaining hiding errors | Fix or create follow-up |
|
|
119
|
+
| **P3** | Low | Style, naming, minor suggestion, informational type quality notes | Optional |
|
|
120
|
+
|
|
121
|
+
## Output Rules
|
|
122
|
+
|
|
123
|
+
1. Write the JSON to the file path provided in the task prompt
|
|
124
|
+
2. JSON MUST be valid - use `JSON.stringify` equivalent formatting
|
|
125
|
+
3. Only report findings with confidence >= 75
|
|
126
|
+
4. Do NOT output the JSON to the conversation - write it to the file only
|
|
127
|
+
5. After writing the file, output a one-line summary: `"Wrote N findings (P0:X P1:X P2:X P3:X) to <filepath>"`
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Review Verdict Update - Update SUMMARY.json and index.json after P0 fixes.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 review_verdict_update.py <session_path> [new_verdict]
|
|
6
|
+
|
|
7
|
+
If new_verdict is omitted, recalculates from current P0/P1 counts in SUMMARY.json:
|
|
8
|
+
- P0 > 0 → REQUEST_CHANGES (no change)
|
|
9
|
+
- P1 > 3 → REQUEST_CHANGES (no change)
|
|
10
|
+
- P1 > 0 → COMMENT
|
|
11
|
+
- else → APPROVE
|
|
12
|
+
|
|
13
|
+
Exit codes:
|
|
14
|
+
0 - Updated successfully
|
|
15
|
+
1 - Error (file not found, parse error, etc.)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def recalculate_verdict(data: dict) -> str:
|
|
24
|
+
"""Recalculate verdict from finding counts."""
|
|
25
|
+
summary = data.get("summary", {})
|
|
26
|
+
by_severity = summary.get("by_severity", {})
|
|
27
|
+
p0 = by_severity.get("P0", 0)
|
|
28
|
+
p1 = by_severity.get("P1", 0)
|
|
29
|
+
|
|
30
|
+
if p0 > 0:
|
|
31
|
+
return "REQUEST_CHANGES"
|
|
32
|
+
if p1 > 3:
|
|
33
|
+
return "REQUEST_CHANGES"
|
|
34
|
+
if p1 > 0:
|
|
35
|
+
return "COMMENT"
|
|
36
|
+
return "APPROVE"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main():
|
|
40
|
+
if len(sys.argv) < 2:
|
|
41
|
+
print(__doc__, file=sys.stderr)
|
|
42
|
+
sys.exit(1)
|
|
43
|
+
|
|
44
|
+
session_path = Path(sys.argv[1])
|
|
45
|
+
forced_verdict = sys.argv[2] if len(sys.argv) >= 3 else None
|
|
46
|
+
|
|
47
|
+
summary_file = session_path / "SUMMARY.json"
|
|
48
|
+
if not summary_file.exists():
|
|
49
|
+
print(f"Error: {summary_file} not found", file=sys.stderr)
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
# Update SUMMARY.json
|
|
53
|
+
try:
|
|
54
|
+
data = json.loads(summary_file.read_text(encoding="utf-8"))
|
|
55
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
56
|
+
print(f"Error reading SUMMARY.json: {e}", file=sys.stderr)
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
old_verdict = data.get("verdict", "UNKNOWN")
|
|
60
|
+
|
|
61
|
+
if forced_verdict:
|
|
62
|
+
new_verdict = forced_verdict
|
|
63
|
+
else:
|
|
64
|
+
new_verdict = recalculate_verdict(data)
|
|
65
|
+
|
|
66
|
+
if old_verdict == new_verdict:
|
|
67
|
+
print(f"Verdict unchanged: {old_verdict}")
|
|
68
|
+
sys.exit(0)
|
|
69
|
+
|
|
70
|
+
data["verdict"] = new_verdict
|
|
71
|
+
summary_file.write_text(json.dumps(data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
72
|
+
print(f"SUMMARY.json: {old_verdict} → {new_verdict}")
|
|
73
|
+
|
|
74
|
+
# Update index.json
|
|
75
|
+
reviews_dir = session_path.parent
|
|
76
|
+
index_file = reviews_dir / "index.json"
|
|
77
|
+
session_id = session_path.name
|
|
78
|
+
|
|
79
|
+
if index_file.exists():
|
|
80
|
+
try:
|
|
81
|
+
index_data = json.loads(index_file.read_text(encoding="utf-8"))
|
|
82
|
+
for entry in index_data.get("sessions", []):
|
|
83
|
+
if entry.get("id") == session_id:
|
|
84
|
+
entry["verdict"] = new_verdict
|
|
85
|
+
# Update P0/P1 counts from summary
|
|
86
|
+
by_sev = data.get("summary", {}).get("by_severity", {})
|
|
87
|
+
entry["p0"] = by_sev.get("P0", 0)
|
|
88
|
+
entry["p1"] = by_sev.get("P1", 0)
|
|
89
|
+
break
|
|
90
|
+
index_file.write_text(json.dumps(index_data, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
91
|
+
print(f"index.json: updated session {session_id}")
|
|
92
|
+
except (json.JSONDecodeError, OSError) as e:
|
|
93
|
+
print(f"Warning: failed to update index.json: {e}", file=sys.stderr)
|
|
94
|
+
|
|
95
|
+
sys.exit(0)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|