@in-the-loop-labs/pair-review 3.9.1 → 4.1.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/README.md +92 -17
- package/package.json +6 -9
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin-code-critic/.claude-plugin/plugin.json +3 -4
- package/plugin-pair-loop/.claude-plugin/plugin.json +19 -0
- package/plugin-pair-loop/skills/loop/SKILL.md +233 -0
- package/public/js/components/AdvancedConfigTab.js +1 -1
- package/public/js/components/AnalysisConfigModal.js +2 -2
- package/public/js/index.js +87 -10
- package/public/js/local.js +20 -10
- package/public/js/pr.js +67 -39
- package/public/js/repo-links.js +11 -3
- package/public/js/utils/analyze-params.js +69 -0
- package/public/js/utils/provider-model.js +67 -2
- package/public/js/vendor/pierre-diffs-worker.js +16158 -0
- package/public/js/vendor/pierre-diffs.js +1880 -0
- package/public/local.html +1 -0
- package/public/pr.html +1 -0
- package/public/setup.html +35 -16
- package/src/ai/analyzer.js +4 -4
- package/src/ai/antigravity-provider.js +594 -0
- package/src/ai/index.js +1 -1
- package/src/ai/opencode-provider.js +1 -1
- package/src/ai/provider.js +5 -5
- package/src/ai/stream-parser.js +1 -52
- package/src/chat/acp-bridge.js +1 -1
- package/src/chat/chat-providers.js +1 -9
- package/src/config.js +153 -31
- package/src/database.js +128 -4
- package/src/external/github-adapter.js +18 -3
- package/src/github/client.js +37 -0
- package/src/github/parser.js +41 -7
- package/src/interactive-analysis-config.js +2 -2
- package/src/links/repo-links.js +66 -28
- package/src/local-review.js +134 -5
- package/src/local-scope.js +38 -0
- package/src/main.js +203 -37
- package/src/routes/config.js +49 -14
- package/src/routes/external-comments.js +13 -1
- package/src/routes/github-collections.js +175 -13
- package/src/routes/local.js +11 -20
- package/src/routes/pr.js +63 -36
- package/src/routes/setup.js +63 -8
- package/src/routes/shared.js +85 -0
- package/src/routes/stack-analysis.js +39 -3
- package/src/server.js +74 -3
- package/src/setup/local-setup.js +23 -7
- package/src/setup/pr-setup.js +237 -39
- package/src/setup/stack-setup.js +7 -2
- package/src/single-port.js +73 -18
- package/src/utils/host-resolution.js +157 -0
- package/plugin-code-critic/skills/loop/SKILL.md +0 -373
- package/src/ai/gemini-provider.js +0 -752
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Copyright 2026 Tim Perkins (tjwp) | SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Shared per-PR host resolution helpers for dual (github.com + alt-host) repos.
|
|
4
|
+
*
|
|
5
|
+
* A single source of truth for the "legacy NULL" back-compat convention that
|
|
6
|
+
* translates a stored `pr_metadata.host` value into the `options` argument for
|
|
7
|
+
* `resolveHostBinding`. Multiple entry points need this exact mapping
|
|
8
|
+
* (PR-mode routes, PR setup, external-comment sync, stack analysis); keeping it
|
|
9
|
+
* here stops the copies from drifting.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { getRepoConfig, isExclusiveAltHost, resolveHostBinding } = require('../config');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Translate a stored `pr_metadata.host` value into the `options` object for
|
|
16
|
+
* `resolveHostBinding`, applying the legacy-NULL back-compat convention:
|
|
17
|
+
*
|
|
18
|
+
* - `undefined` (no pr_metadata row) → `undefined` (ambiguity rule; caller
|
|
19
|
+
* should pass `{}` / omit the option to `resolveHostBinding`).
|
|
20
|
+
* - `null` on an EXCLUSIVE alt-host repo → `undefined`. A legacy NULL row
|
|
21
|
+
* predates host stamping and that repo has no github.com presence, so
|
|
22
|
+
* `{ host: null }` would throw. Falling back to the ambiguity rule binds
|
|
23
|
+
* to its alt host exactly as before this feature.
|
|
24
|
+
* - `null` on a plain or dual repo → `{ host: null }` (github.com).
|
|
25
|
+
* - a URL string → `{ host: '<url>' }` (that alt host).
|
|
26
|
+
*
|
|
27
|
+
* @param {Object} config - Application config
|
|
28
|
+
* @param {string} bindingRepository - `repos[...]` config-lookup key
|
|
29
|
+
* @param {string|null|undefined} storedHost - Value from `getPRHost`
|
|
30
|
+
* @returns {{ host: string|null }|undefined} The `resolveHostBinding` option,
|
|
31
|
+
* or `undefined` to signal "use the ambiguity rule".
|
|
32
|
+
*/
|
|
33
|
+
function storedHostToOption(config, bindingRepository, storedHost) {
|
|
34
|
+
if (storedHost === undefined) return undefined;
|
|
35
|
+
if (storedHost === null && isExclusiveAltHost(getRepoConfig(config, bindingRepository))) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
return { host: storedHost };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A DUAL repo has an `api_host` configured but is NOT exclusive — its PRs may
|
|
43
|
+
* live on github.com OR the alt host, so a host-unknown setup must probe.
|
|
44
|
+
* Exclusive alt-host repos and plain github repos are NOT dual.
|
|
45
|
+
*
|
|
46
|
+
* This is the `repoConfig`-shaped predicate; callers that hold a `repos[...]`
|
|
47
|
+
* entry directly (e.g. repo-links, pr-setup) use it, while `isDualHostRepo`
|
|
48
|
+
* resolves a binding key to its entry first.
|
|
49
|
+
*
|
|
50
|
+
* @param {Object|null|undefined} repoConfig - A single `repos[...]` entry
|
|
51
|
+
* @returns {boolean}
|
|
52
|
+
*/
|
|
53
|
+
function isDualHostRepoConfig(repoConfig) {
|
|
54
|
+
const apiHost = (repoConfig && typeof repoConfig.api_host === 'string' && repoConfig.api_host)
|
|
55
|
+
? repoConfig.api_host
|
|
56
|
+
: null;
|
|
57
|
+
return apiHost !== null && isExclusiveAltHost(repoConfig) === false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Binding-key-shaped variant of {@link isDualHostRepoConfig}: resolves the
|
|
62
|
+
* `repos[...]` entry for `bindingRepository` before applying the predicate.
|
|
63
|
+
*
|
|
64
|
+
* @param {Object} config - Application config
|
|
65
|
+
* @param {string} bindingRepository - `repos[...]` config-lookup key
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
function isDualHostRepo(config, bindingRepository) {
|
|
69
|
+
return isDualHostRepoConfig(getRepoConfig(config, bindingRepository));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The configured `api_host` URL string for a binding key, or `null` when the
|
|
74
|
+
* repo has none (plain github). Used by credential preflights that need to
|
|
75
|
+
* resolve the alt-host binding as a second candidate.
|
|
76
|
+
*
|
|
77
|
+
* @param {Object} config - Application config
|
|
78
|
+
* @param {string} bindingRepository - `repos[...]` config-lookup key
|
|
79
|
+
* @returns {string|null}
|
|
80
|
+
*/
|
|
81
|
+
function getConfiguredApiHost(config, bindingRepository) {
|
|
82
|
+
const repoConfig = getRepoConfig(config, bindingRepository);
|
|
83
|
+
return (repoConfig && typeof repoConfig.api_host === 'string' && repoConfig.api_host)
|
|
84
|
+
? repoConfig.api_host
|
|
85
|
+
: null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the binding used for a credential PREFLIGHT (fail-fast gate before
|
|
90
|
+
* network work), tolerating a dual repo whose host is still unknown.
|
|
91
|
+
*
|
|
92
|
+
* The primary binding is resolved against `host` (undefined = unknown →
|
|
93
|
+
* ambiguity rule; null = github; api_host string = that alt host). When the
|
|
94
|
+
* host is unknown AND the repo is dual, the ambiguity rule yields the github.com
|
|
95
|
+
* binding — but the downstream probe (`resolvePrHostBinding`) tries the alt host
|
|
96
|
+
* first, so an alt-only repo token IS usable even though the github binding has
|
|
97
|
+
* none. In that case return the alt binding so the caller does not falsely
|
|
98
|
+
* reject; the caller only fails when NEITHER candidate has a token.
|
|
99
|
+
*
|
|
100
|
+
* Shared by the CLI (`src/main.js`) and the setup route (`src/routes/setup.js`).
|
|
101
|
+
*
|
|
102
|
+
* @param {string} bindingRepository - `repos[...]` config-lookup key
|
|
103
|
+
* @param {Object} config
|
|
104
|
+
* @param {string|null|undefined} host - explicit host (URL paste / body) or undefined
|
|
105
|
+
* @returns {{ apiHost: string|null, token: string }} A binding; empty `.token`
|
|
106
|
+
* signals the caller to reject (missing credential).
|
|
107
|
+
*/
|
|
108
|
+
function resolvePreflightBinding(bindingRepository, config, host) {
|
|
109
|
+
const primary = resolveHostBinding(
|
|
110
|
+
bindingRepository,
|
|
111
|
+
config,
|
|
112
|
+
host !== undefined ? { host } : {}
|
|
113
|
+
);
|
|
114
|
+
if (primary.token) return primary;
|
|
115
|
+
if (host === undefined && isDualHostRepo(config, bindingRepository)) {
|
|
116
|
+
const apiHost = getConfiguredApiHost(config, bindingRepository);
|
|
117
|
+
if (apiHost) {
|
|
118
|
+
const alt = resolveHostBinding(bindingRepository, config, { host: apiHost });
|
|
119
|
+
if (alt.token) return alt;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return primary;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Map a parser/stored host to the VALUE for the setup `?host=` query param, or
|
|
127
|
+
* `null` to omit the param. Single source for the CLI cold-start, single-port
|
|
128
|
+
* delegation, and (mirrored) the web paste flow — so a pasted alt URL binds the
|
|
129
|
+
* alt host directly instead of re-probing at setup.
|
|
130
|
+
*
|
|
131
|
+
* - alt api_host URL string → that string (setup binds the alt host)
|
|
132
|
+
* - `null` on a DUAL repo → the `'github'` sentinel (setup binds github.com,
|
|
133
|
+
* no probe — avoids a loud failure if the alt host is down for a PR we KNOW
|
|
134
|
+
* is on github)
|
|
135
|
+
* - anything else (plain/exclusive repo, or unknown host) → `null` (omit; no
|
|
136
|
+
* probe happens for those, and omitting avoids the exclusive-null throw)
|
|
137
|
+
*
|
|
138
|
+
* Callers append the value as `host=${encodeURIComponent(value)}`.
|
|
139
|
+
*
|
|
140
|
+
* @param {string|null|undefined} host - parser/stored host
|
|
141
|
+
* @param {boolean} isDual - whether the repo is dual (github + alt-host)
|
|
142
|
+
* @returns {string|null} the param value, or null to omit
|
|
143
|
+
*/
|
|
144
|
+
function hostSetupParamValue(host, isDual) {
|
|
145
|
+
if (typeof host === 'string' && host) return host;
|
|
146
|
+
if (host === null && isDual) return 'github';
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
storedHostToOption,
|
|
152
|
+
isDualHostRepo,
|
|
153
|
+
isDualHostRepoConfig,
|
|
154
|
+
getConfiguredApiHost,
|
|
155
|
+
resolvePreflightBinding,
|
|
156
|
+
hostSetupParamValue,
|
|
157
|
+
};
|
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: loop
|
|
3
|
-
description: >
|
|
4
|
-
Implement code, review changes with AI analysis, fix issues, and repeat until clean
|
|
5
|
-
or max iterations reached. Creates a tight implement-review-fix feedback loop.
|
|
6
|
-
Use when the user says "critic loop", "code review loop", "implement and review", "build with review loop",
|
|
7
|
-
or wants iterative development with automated quality checks.
|
|
8
|
-
arguments:
|
|
9
|
-
maxIterations:
|
|
10
|
-
description: "Maximum number of review-fix cycles after initial implementation (default: 3)"
|
|
11
|
-
required: false
|
|
12
|
-
default: 3
|
|
13
|
-
tier:
|
|
14
|
-
description: "Analysis tier: fast (Haiku-class), balanced (Sonnet-class, default), or thorough (Opus-class)"
|
|
15
|
-
required: false
|
|
16
|
-
default: balanced
|
|
17
|
-
skipLevel3:
|
|
18
|
-
description: "Skip Level 3 (codebase context) analysis. Set 'true' to skip, 'false' to force, or 'auto' to let the analysis agent decide based on change scope (default: auto)."
|
|
19
|
-
required: false
|
|
20
|
-
default: auto
|
|
21
|
-
customInstructions:
|
|
22
|
-
description: "Optional review-specific instructions (e.g., 'focus on security', 'this repo uses X pattern')"
|
|
23
|
-
required: false
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
# Critic Loop
|
|
27
|
-
|
|
28
|
-
Implement an objective, then iterate with AI-powered code review — addressing both review findings and incomplete work — until the objective is fully realized and the code is clean.
|
|
29
|
-
|
|
30
|
-
## ZERO PREAMBLE RULE
|
|
31
|
-
|
|
32
|
-
**Do NOT read source files, grep for patterns, or explore the project structure before starting.**
|
|
33
|
-
Go DIRECTLY to the Initialize phase. The Task agents will do their own exploration.
|
|
34
|
-
|
|
35
|
-
**Your role is orchestrator.** You launch Tasks, read their short return summaries, run lightweight Bash commands (git status, file writes), and make loop decisions. That is ALL. You *should* still read the loop log file and run lightweight git commands as needed for orchestration.
|
|
36
|
-
|
|
37
|
-
## Architecture: File-Mediated Analysis
|
|
38
|
-
|
|
39
|
-
All heavy work runs in Task agents. Analysis results are written to a **directory on disk** — the main session never sees the full analysis output. This keeps per-iteration context growth to ~300-400 tokens.
|
|
40
|
-
|
|
41
|
-
```
|
|
42
|
-
.critic-loop/{id}/
|
|
43
|
-
├── loop.log # High-level orchestration log
|
|
44
|
-
├── analysis.1.json # Iteration 1 analysis (kept for history)
|
|
45
|
-
├── analysis.2.json # Iteration 2 analysis
|
|
46
|
-
├── implementation.0.md # What initial Implement task built
|
|
47
|
-
├── implementation.1.md # What Fix iteration 1 addressed
|
|
48
|
-
└── implementation.2.md # What Fix iteration 2 addressed
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
Main session → Implement Task → writes implementation.0.md → returns summary
|
|
53
|
-
Main session → Analysis Task → writes analysis.{N}.json → returns summary
|
|
54
|
-
Main session → reads summary, decides continue/stop
|
|
55
|
-
Main session → Fix Task → reads analysis.{N}.json → writes implementation.{N}.md → returns summary
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
**History is preserved.** Analysis and implementation files are kept for debugging and so subsequent iterations can avoid re-suggesting already-addressed issues.
|
|
59
|
-
|
|
60
|
-
## Workflow
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
IMPLEMENT → ANALYZE → EVALUATE ──→ done → REPORT
|
|
64
|
-
↑ ↓
|
|
65
|
-
└──── FIX ←──── continue
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Each iteration works toward two goals simultaneously: resolving review findings **and** making further progress on the original objective. The loop converges when the objective is complete and the code is clean.
|
|
69
|
-
|
|
70
|
-
The user's request text (after any named arguments) is the **objective** — what to build or change.
|
|
71
|
-
|
|
72
|
-
### Context Recovery
|
|
73
|
-
|
|
74
|
-
After `/compact` or session restart, read `{LOG_FILE}` to recover the objective, iteration count, and history. The log header contains everything needed to resume the loop. The SKILL.md instructions will be re-injected automatically since the skill is still active.
|
|
75
|
-
|
|
76
|
-
If context grows large after many iterations, you may `/compact` between iterations. The log file preserves all state needed to resume.
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## Phase: Initialize
|
|
81
|
-
|
|
82
|
-
1. Generate a unique log ID: `date +%s`
|
|
83
|
-
2. Get the project root: `git rev-parse --show-toplevel`
|
|
84
|
-
3. Create the loop directory: `mkdir -p {project_root}/.critic-loop/{id}`
|
|
85
|
-
4. If `.critic-loop/` is not already in `.gitignore`, add it:
|
|
86
|
-
```bash
|
|
87
|
-
grep -qxF '.critic-loop/' .gitignore 2>/dev/null || echo '.critic-loop/' >> .gitignore
|
|
88
|
-
```
|
|
89
|
-
5. Store these values for use in subsequent phases:
|
|
90
|
-
- `LOOP_DIR` = `{project_root}/.critic-loop/{id}`
|
|
91
|
-
- `LOG_FILE` = `{LOOP_DIR}/loop.log`
|
|
92
|
-
- Analysis files are dynamic: `{LOOP_DIR}/analysis.{iteration}.json` (iteration starts at 1)
|
|
93
|
-
- Implementation files are dynamic: `{LOOP_DIR}/implementation.{iteration}.md` (iteration 0 = initial implementation)
|
|
94
|
-
6. Create the log file at `{LOG_FILE}`:
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
# Critic Loop Log
|
|
98
|
-
- Objective: {the user's objective, verbatim}
|
|
99
|
-
- Project root: {project_root}
|
|
100
|
-
- Loop directory: {LOOP_DIR}
|
|
101
|
-
- maxIterations: {value}
|
|
102
|
-
- tier: {value}
|
|
103
|
-
- skipLevel3: {value}
|
|
104
|
-
- customInstructions: {value or "none"}
|
|
105
|
-
- iteration: 0 # completed fix cycles (implementation.0.md is the initial build)
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
This header block is the source of truth for the loop's state. It must survive `/compact` and be parseable on resume.
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## Phase: Implement
|
|
113
|
-
|
|
114
|
-
Launch a **Task agent** (subagent_type: "general-purpose") to implement the objective.
|
|
115
|
-
|
|
116
|
-
**Task prompt must include:**
|
|
117
|
-
- The full objective text from the user's request
|
|
118
|
-
- The current working directory and relevant context (branch name, repo structure hints)
|
|
119
|
-
- Instructions to work autonomously and make all necessary code changes
|
|
120
|
-
- **Do NOT commit changes** — leave them as working tree modifications
|
|
121
|
-
- **`git add -N` (intent-to-add) any newly created files** — this makes them visible to `git diff` commands used in analysis
|
|
122
|
-
- Write a summary file at `{LOOP_DIR}/implementation.0.md` with this structure:
|
|
123
|
-
```markdown
|
|
124
|
-
# Initial Implementation
|
|
125
|
-
|
|
126
|
-
## What was built
|
|
127
|
-
- {bullet points of what was created/modified}
|
|
128
|
-
|
|
129
|
-
## Key decisions
|
|
130
|
-
- {any notable choices made}
|
|
131
|
-
|
|
132
|
-
## Caveats
|
|
133
|
-
- {any incomplete items or known limitations}
|
|
134
|
-
```
|
|
135
|
-
- Return a concise summary (3-5 sentences) to the main session: files modified, key decisions, any caveats
|
|
136
|
-
|
|
137
|
-
**Before proceeding**, verify the task succeeded and made changes:
|
|
138
|
-
```bash
|
|
139
|
-
git status --short
|
|
140
|
-
```
|
|
141
|
-
If no changes exist, inform the user and stop.
|
|
142
|
-
|
|
143
|
-
All analysis throughout the loop uses `git diff HEAD` to capture the full set of uncommitted working tree changes. Because the implementation and fix tasks `git add -N` any new files they create, untracked files are included in the diff output.
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## Phase: Analyze
|
|
148
|
-
|
|
149
|
-
Launch a **single Task agent** (subagent_type: "general-purpose") that performs the full analysis pipeline and writes results to disk. The main session does NOT see the analysis details.
|
|
150
|
-
|
|
151
|
-
**Determine the analysis file path:** `{LOOP_DIR}/analysis.{iteration}.json` where iteration is 1 for the first analysis, 2 after the first fix, etc. (The iteration counter in the log file shows how many fix cycles have completed; add 1 for the current analysis.)
|
|
152
|
-
|
|
153
|
-
**Build aggregate custom instructions** to pass to the analysis task. These give the analysis agent context about the loop:
|
|
154
|
-
|
|
155
|
-
```markdown
|
|
156
|
-
## Loop Context
|
|
157
|
-
|
|
158
|
-
**Objective:** {the user's objective verbatim from the log file}
|
|
159
|
-
|
|
160
|
-
**Iteration:** {current iteration} of {maxIterations}
|
|
161
|
-
|
|
162
|
-
{User's customInstructions if provided, otherwise omit this section}
|
|
163
|
-
|
|
164
|
-
## History Reference
|
|
165
|
-
Previous iteration files are available at:
|
|
166
|
-
- `{LOOP_DIR}/analysis.*.json` — Prior analysis results (1 through current-1)
|
|
167
|
-
- `{LOOP_DIR}/implementation.*.md` — What was implemented/fixed (0 = initial, 1+ = fixes)
|
|
168
|
-
|
|
169
|
-
If you see a suggestion that was already made and addressed in a previous iteration,
|
|
170
|
-
do NOT re-suggest it unless the fix introduced a new problem.
|
|
171
|
-
|
|
172
|
-
## Merge Readiness Assessment
|
|
173
|
-
In addition to objective status, assess whether these changes are ready to merge:
|
|
174
|
-
- `ready` = Objective complete, no blocking issues. Minor polish suggestions may exist but would not block a code review approval.
|
|
175
|
-
- `needs-fixes` = Issues that should be addressed before merging (bugs, security, incomplete features).
|
|
176
|
-
- `blocked` = Major issues that would definitely block merging (critical bugs, security vulnerabilities, broken functionality).
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**Task prompt:**
|
|
180
|
-
|
|
181
|
-
> You are a code review analysis agent. Your job is to run a three-level code review and write the curated results to a JSON file.
|
|
182
|
-
>
|
|
183
|
-
> **Output file**: Write the final curated JSON to `{LOOP_DIR}/analysis.{iteration}.json`
|
|
184
|
-
>
|
|
185
|
-
> **Return to me**: ONLY a brief summary in this exact format:
|
|
186
|
-
> ```
|
|
187
|
-
> Suggestions: {N} line-level, {M} file-level
|
|
188
|
-
> Types: {comma-separated list of types found, e.g. "bug, improvement, praise"}
|
|
189
|
-
> Level 3: {ran|skipped} — {reason if auto}
|
|
190
|
-
> Objective status: complete|incomplete|partial — {brief reason}
|
|
191
|
-
> Merge readiness: ready|needs-fixes|blocked — {brief reason}
|
|
192
|
-
> Summary: {2 sentences describing the most significant findings}
|
|
193
|
-
> ```
|
|
194
|
-
> Do NOT return the full JSON. Do NOT return individual suggestions. Just the summary above.
|
|
195
|
-
>
|
|
196
|
-
> **How to run the analysis:**
|
|
197
|
-
>
|
|
198
|
-
> 1. Find the analysis tools:
|
|
199
|
-
> - Use Glob to find `**/analyze/scripts/git-diff-lines` — this is the diff annotation script
|
|
200
|
-
> - Use Glob to find `**/analyze/references/` — this directory contains the analysis prompts
|
|
201
|
-
> - Resolve the absolute path to the scripts directory from the Glob result. Use this resolved path in all `PATH=` commands below and include it in each sub-task prompt.
|
|
202
|
-
> - Read the reference files for tier "{tier}" (read Level 3 only if it will be needed based on `skipLevel3` setting):
|
|
203
|
-
> - `references/level1-{tier}.md`
|
|
204
|
-
> - `references/level2-{tier}.md`
|
|
205
|
-
> - `references/level3-{tier}.md` (skip if `skipLevel3` is `true`; read if `false` or `auto` since the agent may need it)
|
|
206
|
-
> - `references/orchestration-{tier}.md`
|
|
207
|
-
>
|
|
208
|
-
> 2. Get the annotated diff:
|
|
209
|
-
> - Run `PATH="{scripts-dir}:$PATH" git-diff-lines HEAD` to get the diff with line numbers
|
|
210
|
-
> - Run `git diff --name-only HEAD` to get the list of changed files
|
|
211
|
-
>
|
|
212
|
-
> 3. Determine which analysis levels to run:
|
|
213
|
-
> - **Level 1 and Level 2**: Always run these.
|
|
214
|
-
> - **Level 3 (codebase context)**: Depends on the `skipLevel3` setting:
|
|
215
|
-
> - If `skipLevel3` is `true`: **Skip Level 3** — do not launch a Level 3 task.
|
|
216
|
-
> - If `skipLevel3` is `false`: **Run Level 3** — always launch a Level 3 task.
|
|
217
|
-
> - If `skipLevel3` is `auto`: Assess the diff to decide. Run Level 3 if ANY of these apply: changes affect shared utilities/APIs/types/interfaces, entire modules are added or deleted, changes are cross-cutting (e.g., renaming a widely-used function). Skip Level 3 if changes are small and localized (CSS/copy/config/test-only, no cross-cutting concerns). When in doubt, run Level 3.
|
|
218
|
-
>
|
|
219
|
-
> Launch the analysis levels you determined above as **parallel Task agents** (subagent_type: "general-purpose"). Each task:
|
|
220
|
-
> - Receives the full prompt text from its reference file as core instructions
|
|
221
|
-
> - Receives the annotated diff output and list of changed files
|
|
222
|
-
> - Must return valid JSON (no markdown wrapping) matching the schema in its prompt
|
|
223
|
-
> - Include the resolved `PATH="{scripts-dir}:$PATH"` command in each sub-task prompt so they can invoke `git-diff-lines` directly
|
|
224
|
-
>
|
|
225
|
-
> 4. After all levels complete, launch one **orchestration Task agent** that:
|
|
226
|
-
> - Receives the orchestration prompt from the reference file
|
|
227
|
-
> - Receives the JSON output from all completed levels (pass empty `[]` for Level 3 if it was skipped)
|
|
228
|
-
> - Merges, deduplicates, and curates suggestions
|
|
229
|
-
> - Returns final curated JSON
|
|
230
|
-
>
|
|
231
|
-
> 5. Write the orchestrated JSON to the output file path specified above.
|
|
232
|
-
>
|
|
233
|
-
> 6. Count suggestions and fileLevelSuggestions from the curated result and return ONLY the brief summary format specified above.
|
|
234
|
-
>
|
|
235
|
-
> **Aggregate custom instructions:**
|
|
236
|
-
> {the aggregate custom instructions block built above}
|
|
237
|
-
|
|
238
|
-
The Task agent returns ~5 lines. That is all the main session sees. **Do NOT delete analysis files** — they are kept for history.
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Phase: Evaluate
|
|
243
|
-
|
|
244
|
-
This phase runs in the **main session** — it is lightweight.
|
|
245
|
-
|
|
246
|
-
Parse the summary returned by the Analysis Task. It contains:
|
|
247
|
-
- Suggestion counts (line-level and file-level)
|
|
248
|
-
- Types of suggestions found
|
|
249
|
-
- Level 3 status (ran or skipped, with reason if auto-decided)
|
|
250
|
-
- Objective status (`complete`, `incomplete`, or `partial`) with a brief reason
|
|
251
|
-
- Merge readiness (`ready`, `needs-fixes`, or `blocked`) with a brief reason
|
|
252
|
-
- A 2-sentence summary of findings
|
|
253
|
-
|
|
254
|
-
**Decision criteria** (evaluate in this order):
|
|
255
|
-
|
|
256
|
-
1. **Iteration counter >= maxIterations** → Max reached. Go to **Completion** with note that issues remain.
|
|
257
|
-
2. **Merge readiness is `ready`** → SUCCESS. Go to **Completion**. The objective is complete and the code is clean enough to merge. Minor polish suggestions may exist but they don't block approval.
|
|
258
|
-
3. **Objective status is `incomplete` or `partial`** → Continue to the Fix phase.
|
|
259
|
-
4. **Merge readiness is `blocked`** → Continue to the Fix phase. Critical issues must be addressed.
|
|
260
|
-
5. **Merge readiness is `needs-fixes` AND suggestions contain actionable types** (`bug`, `security`, `performance`, `improvement`, or `design`) → Continue to the Fix phase.
|
|
261
|
-
6. **No actionable suggestions remaining** (only `praise` and/or `code-style`) → Go to **Completion** (clean).
|
|
262
|
-
|
|
263
|
-
The key difference from simpler logic: a `ready` merge status stops the loop even if there are minor improvement suggestions. This prevents chasing perfection to max iterations when the code is already good enough.
|
|
264
|
-
|
|
265
|
-
Append a decision line to the log file:
|
|
266
|
-
```
|
|
267
|
-
Iteration {N}/{max}: {line-level} line-level + {file-level} file-level suggestions, merge={merge_readiness} → {continuing|clean|ready|max reached}
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
---
|
|
271
|
-
|
|
272
|
-
## Phase: Fix
|
|
273
|
-
|
|
274
|
-
Launch a **Task agent** (subagent_type: "general-purpose") to address findings and continue the objective.
|
|
275
|
-
|
|
276
|
-
**Determine the current fix iteration**: Read the `- iteration: {N}` line from the log file. Set `CURRENT = N + 1` (the fix iteration number, since N represents completed cycles). Use `CURRENT` for all file paths in this phase.
|
|
277
|
-
|
|
278
|
-
**Before launching**, append the iteration header to the log:
|
|
279
|
-
```
|
|
280
|
-
## Iteration {CURRENT}
|
|
281
|
-
- Analysis summary: {the summary from the Analyze phase}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
**Task prompt:**
|
|
285
|
-
|
|
286
|
-
> You are a code fix agent. Read the analysis results and fix valid issues while continuing progress on the objective.
|
|
287
|
-
>
|
|
288
|
-
> **Original objective**: {objective}
|
|
289
|
-
>
|
|
290
|
-
> **Analysis file**: Read `{LOOP_DIR}/analysis.{CURRENT}.json` — it contains the full curated analysis JSON with `suggestions` (line-level) and `fileLevelSuggestions` (file-level) arrays.
|
|
291
|
-
>
|
|
292
|
-
> **Iteration history**: Read `{LOG_FILE}` and the implementation files in `{LOOP_DIR}/implementation.*.md` — these show what was already tried in previous iterations. Take a different approach if an issue reappears.
|
|
293
|
-
>
|
|
294
|
-
> **For each suggestion** (from both arrays):
|
|
295
|
-
> 1. Read the referenced file and lines
|
|
296
|
-
> 2. Evaluate whether the suggestion is valid or a false positive
|
|
297
|
-
> 3. If valid, make the code change
|
|
298
|
-
> 4. If false positive or not worth fixing, skip and note why
|
|
299
|
-
>
|
|
300
|
-
> **Also**: Continue implementing any incomplete parts of the objective that the analysis identified as missing.
|
|
301
|
-
>
|
|
302
|
-
> **Rules**:
|
|
303
|
-
> - Do NOT commit changes — leave them as working tree modifications
|
|
304
|
-
> - `git add -N` (intent-to-add) any newly created files
|
|
305
|
-
> - Write a summary file at `{LOOP_DIR}/implementation.{CURRENT}.md` with this structure:
|
|
306
|
-
> ```markdown
|
|
307
|
-
> # Iteration {CURRENT} Response
|
|
308
|
-
>
|
|
309
|
-
> ## Suggestions addressed
|
|
310
|
-
> - {suggestion type}: {file:line} — {what was fixed}
|
|
311
|
-
>
|
|
312
|
-
> ## Suggestions skipped
|
|
313
|
-
> - {suggestion type}: {file:line} — {reason: false positive / not worth fixing / style preference}
|
|
314
|
-
>
|
|
315
|
-
> ## Objective progress
|
|
316
|
-
> - {any additional work done toward the objective}
|
|
317
|
-
> ```
|
|
318
|
-
>
|
|
319
|
-
> **Return to me**: ONLY a brief summary (3-5 sentences): what was fixed, what was skipped (and why), what progress was made on the objective.
|
|
320
|
-
|
|
321
|
-
After the Fix Task completes:
|
|
322
|
-
|
|
323
|
-
1. Append the fix summary to the log:
|
|
324
|
-
```
|
|
325
|
-
- Fix actions: {the summary returned by the fix task}
|
|
326
|
-
```
|
|
327
|
-
2. Increment the iteration counter in the log file header — update the `- iteration: {N}` line to the new value.
|
|
328
|
-
|
|
329
|
-
---
|
|
330
|
-
|
|
331
|
-
## MANDATORY: Return to Analyze
|
|
332
|
-
|
|
333
|
-
**After completing the Fix phase, you MUST go back to the Analyze phase.**
|
|
334
|
-
|
|
335
|
-
The ONLY ways to exit the loop are:
|
|
336
|
-
1. The Evaluate phase decides the work is clean and complete → goes to Completion
|
|
337
|
-
2. The Evaluate phase decides maxIterations was reached → goes to Completion
|
|
338
|
-
|
|
339
|
-
There are NO other valid reasons to stop looping. Do not stop after one fix cycle. Do not stop because "things look good." Only the Evaluate phase's decision logic can end the loop.
|
|
340
|
-
|
|
341
|
-
**Go to the Analyze phase now.**
|
|
342
|
-
|
|
343
|
-
---
|
|
344
|
-
|
|
345
|
-
## Completion
|
|
346
|
-
|
|
347
|
-
Report the final status to the user:
|
|
348
|
-
|
|
349
|
-
- **Iterations performed**: How many review-fix cycles ran
|
|
350
|
-
- **Outcome**: Whether the objective is complete and the code is clean/ready, or max iterations were reached
|
|
351
|
-
- **Changes summary**: What was implemented and what was fixed across all iterations
|
|
352
|
-
- **Remaining issues**: If max iterations hit, list the summary from the last analysis
|
|
353
|
-
- **Files modified**: Complete list (run `git diff --name-only HEAD`)
|
|
354
|
-
|
|
355
|
-
Then offer next steps (do not perform them automatically):
|
|
356
|
-
- Run tests if applicable
|
|
357
|
-
- Use `pair-review:local` or `pair-review:pr` to open a human review in the pair-review web UI (requires the pair-review plugin)
|
|
358
|
-
- Stage or commit the changes when satisfied
|
|
359
|
-
|
|
360
|
-
**Clean up based on outcome:**
|
|
361
|
-
|
|
362
|
-
If the outcome is clean (merge readiness was `ready` or no actionable suggestions remained):
|
|
363
|
-
```bash
|
|
364
|
-
# Remove the entire loop directory
|
|
365
|
-
rm -rf {LOOP_DIR}
|
|
366
|
-
# Remove parent .critic-loop directory if empty
|
|
367
|
-
rmdir .critic-loop 2>/dev/null
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
If max iterations were reached with remaining issues, **leave the loop directory intact** and mention its location in the completion report so the user can inspect the history:
|
|
371
|
-
- `{LOOP_DIR}/loop.log` — orchestration log with all iteration summaries
|
|
372
|
-
- `{LOOP_DIR}/analysis.*.json` — analysis results from each iteration
|
|
373
|
-
- `{LOOP_DIR}/implementation.*.md` — what was implemented/fixed in each iteration
|