@in-the-loop-labs/pair-review 4.0.0 → 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.
Files changed (42) hide show
  1. package/README.md +82 -7
  2. package/package.json +2 -1
  3. package/plugin/.claude-plugin/plugin.json +1 -1
  4. package/plugin-code-critic/.claude-plugin/plugin.json +3 -4
  5. package/plugin-pair-loop/.claude-plugin/plugin.json +19 -0
  6. package/plugin-pair-loop/skills/loop/SKILL.md +233 -0
  7. package/public/js/index.js +87 -10
  8. package/public/js/local.js +19 -9
  9. package/public/js/pr.js +64 -36
  10. package/public/js/repo-links.js +11 -3
  11. package/public/js/utils/analyze-params.js +69 -0
  12. package/public/js/utils/provider-model.js +66 -1
  13. package/public/js/vendor/pierre-diffs-worker.js +16158 -0
  14. package/public/js/vendor/pierre-diffs.js +1880 -0
  15. package/public/local.html +1 -0
  16. package/public/pr.html +1 -0
  17. package/public/setup.html +35 -16
  18. package/src/config.js +150 -28
  19. package/src/database.js +127 -3
  20. package/src/external/github-adapter.js +18 -3
  21. package/src/github/client.js +37 -0
  22. package/src/github/parser.js +41 -7
  23. package/src/interactive-analysis-config.js +2 -2
  24. package/src/links/repo-links.js +66 -28
  25. package/src/local-review.js +134 -5
  26. package/src/local-scope.js +38 -0
  27. package/src/main.js +199 -33
  28. package/src/routes/config.js +47 -12
  29. package/src/routes/external-comments.js +13 -1
  30. package/src/routes/github-collections.js +175 -13
  31. package/src/routes/local.js +11 -20
  32. package/src/routes/pr.js +63 -36
  33. package/src/routes/setup.js +63 -8
  34. package/src/routes/shared.js +85 -0
  35. package/src/routes/stack-analysis.js +39 -3
  36. package/src/server.js +74 -3
  37. package/src/setup/local-setup.js +23 -7
  38. package/src/setup/pr-setup.js +237 -39
  39. package/src/setup/stack-setup.js +7 -2
  40. package/src/single-port.js +73 -18
  41. package/src/utils/host-resolution.js +157 -0
  42. package/plugin-code-critic/skills/loop/SKILL.md +0 -373
@@ -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