@ask-llm/plugin 0.15.0 → 0.16.3
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/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +979 -0
- package/README.md +2 -0
- package/agents/brainstorm-coordinator.md +1 -1
- package/agents/gemini-reviewer.md +1 -1
- package/dist/antigravity-run.js +0 -0
- package/dist/brainstorm-run.js +0 -0
- package/dist/codex-run.js +0 -0
- package/dist/grok-run.js +0 -0
- package/dist/ollama-run.js +0 -0
- package/dist/run.js +0 -0
- package/package.json +14 -14
- package/pi/extensions/provider-tools.ts +1 -1
- package/scripts/benchmark/README.md +114 -0
- package/scripts/benchmark/fixtures/README.md +29 -0
- package/scripts/codex-pair-debounce-worker.mjs +0 -0
- package/scripts/codex-pair-log.mjs +4 -13
- package/scripts/codex-pair-prompt-drain.mjs +1 -1
- package/scripts/codex-pair-session.mjs +2 -2
- package/scripts/codex-pair-stop-gate.mjs +8 -8
- package/scripts/codex-pair-watch.mjs +20 -39
- package/skills/gemini-review/SKILL.md +1 -1
- package/scripts/lib/broker-lifecycle.mjs +0 -575
- package/scripts/lib/broker-rpc.mjs +0 -203
- package/scripts/lib/broker-transport.mjs +0 -407
- package/scripts/lib/broker.mjs +0 -537
- package/scripts/lib/debounce-state.mjs +0 -208
- package/scripts/lib/parser.d.mts +0 -12
- package/scripts/lib/parser.mjs +0 -229
- package/scripts/lib/process.mjs +0 -56
- package/scripts/lib/prompt.d.mts +0 -8
- package/scripts/lib/prompt.mjs +0 -41
- package/scripts/lib/session-registry.mjs +0 -162
- package/scripts/lib/state.d.mts +0 -58
- package/scripts/lib/state.mjs +0 -733
- package/scripts/lib/stop-gate.mjs +0 -134
package/README.md
CHANGED
|
@@ -61,6 +61,8 @@ pi list
|
|
|
61
61
|
|
|
62
62
|
Pi discovers the portable skills as `/skill:<name>` commands and registers native `ask-codex`, `ask-gemini`, `ask-grok`, `ask-ollama`, `ask-antigravity`, model-neutral `ask-cursor-agent`, and bounded concurrent `ask-multi` tools. Pi intentionally has no built-in MCP client; do not configure these as MCP servers in Pi. `fable-review` and `grok-pair` are excluded from Pi discovery; Grok pairing currently has Claude/Cursor adapters, while Pi retains its dedicated Codex pairing lifecycle.
|
|
63
63
|
|
|
64
|
+
The plugin's `ask-gemini-run`, Gemini reviewer agent/skill, and Pi `ask-gemini` tool all delegate to the canonical Gemini executor: `gemini-3.1-pro-preview` remains primary and quota errors fall back to `gemini-3.8-flash` unless `ASK_GEMINI_FALLBACK_MODEL` overrides it.
|
|
65
|
+
|
|
64
66
|
For codex-pair, create `.codex-pair/context.md`, ensure Pi trusts the project, then run interactive `/codex-pair` to grant user-owned canonical-project consent. The marker alone never authorizes data transfer/cost. Revoke with `/codex-pair revoke`. Pi findings are non-blocking; blocking Stop-gate and one-shot print parity are not available.
|
|
65
67
|
|
|
66
68
|
```bash
|
|
@@ -231,7 +231,7 @@ node "${CLAUDE_PLUGIN_ROOT}/dist/grok-run.js" "$(cat "$workdir/prompt.md")" > "$
|
|
|
231
231
|
pid_grok=$!
|
|
232
232
|
|
|
233
233
|
# Only include this line if ollama was requested:
|
|
234
|
-
ollama run "${ASK_OLLAMA_MODEL:-qwen3.
|
|
234
|
+
ollama run "${ASK_OLLAMA_MODEL:-qwen3.8:27b}" < "$workdir/prompt.md" > "$workdir/ollama.out" 2> "$workdir/ollama.err" &
|
|
235
235
|
pid_ollama=$!
|
|
236
236
|
|
|
237
237
|
# Wait for each by PID so we capture per-provider exit codes independently.
|
|
@@ -14,7 +14,7 @@ tools:
|
|
|
14
14
|
<!-- PORTABLE-CONTRACT:START -->
|
|
15
15
|
## Portable contract
|
|
16
16
|
|
|
17
|
-
Review only the supplied changes and context. Ask Gemini for concrete correctness, security, and regression concerns; validate every candidate against source; require file/line evidence and reproduction for behavior claims; omit style-only or speculative findings; report provider failures explicitly.
|
|
17
|
+
Review only the supplied changes and context. Ask Gemini for concrete correctness, security, and regression concerns; validate every candidate against source; require file/line evidence and reproduction for behavior claims; omit style-only or speculative findings; report provider failures explicitly. Preserve the canonical `gemini-3.1-pro-preview` → `gemini-3.8-flash` quota fallback and disclose when fallback occurs.
|
|
18
18
|
<!-- PORTABLE-CONTRACT:END -->
|
|
19
19
|
|
|
20
20
|
<!-- HOST-ADAPTER:CLAUDE-CODE:START -->
|
package/dist/antigravity-run.js
CHANGED
|
File without changes
|
package/dist/brainstorm-run.js
CHANGED
|
File without changes
|
package/dist/codex-run.js
CHANGED
|
File without changes
|
package/dist/grok-run.js
CHANGED
|
File without changes
|
package/dist/ollama-run.js
CHANGED
|
File without changes
|
package/dist/run.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ask-llm/plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.3",
|
|
4
4
|
"description": "Ask LLM review, comparison, brainstorming, image, verification, and pairing workflows for Claude Code, Cursor Agent, and Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -83,12 +83,12 @@
|
|
|
83
83
|
]
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@ask-llm/antigravity-mcp": "^0.7.
|
|
87
|
-
"@ask-llm/codex-mcp": "^0.7.
|
|
88
|
-
"@ask-llm/gemini-mcp": "^1.
|
|
89
|
-
"@ask-llm/grok-mcp": "^0.1.
|
|
90
|
-
"@ask-llm/mcp": "^0.
|
|
91
|
-
"@ask-llm/ollama-mcp": "^0.
|
|
86
|
+
"@ask-llm/antigravity-mcp": "^0.7.5",
|
|
87
|
+
"@ask-llm/codex-mcp": "^0.7.8",
|
|
88
|
+
"@ask-llm/gemini-mcp": "^1.9.1",
|
|
89
|
+
"@ask-llm/grok-mcp": "^0.1.5",
|
|
90
|
+
"@ask-llm/mcp": "^0.9.3",
|
|
91
|
+
"@ask-llm/ollama-mcp": "^0.6.2"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|
|
94
94
|
"@earendil-works/pi-ai": "*",
|
|
@@ -99,15 +99,15 @@
|
|
|
99
99
|
"node": ">=20.0.0"
|
|
100
100
|
},
|
|
101
101
|
"devDependencies": {
|
|
102
|
-
"@biomejs/biome": "^2.
|
|
103
|
-
"@earendil-works/pi-ai": "^0.
|
|
104
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
105
|
-
"@types/node": "^22.
|
|
106
|
-
"typebox": "
|
|
102
|
+
"@biomejs/biome": "^2.5.10",
|
|
103
|
+
"@earendil-works/pi-ai": "^0.84.2",
|
|
104
|
+
"@earendil-works/pi-coding-agent": "^0.84.2",
|
|
105
|
+
"@types/node": "^22.20.1",
|
|
106
|
+
"typebox": "1.3.7",
|
|
107
107
|
"typescript": "^7.0.2",
|
|
108
|
-
"vitest": "^4.1.
|
|
108
|
+
"vitest": "^4.1.11"
|
|
109
109
|
},
|
|
110
110
|
"publishConfig": {
|
|
111
111
|
"access": "public"
|
|
112
112
|
}
|
|
113
|
-
}
|
|
113
|
+
}
|
|
@@ -203,7 +203,7 @@ export function registerProviderTools(pi: ExtensionAPI): void {
|
|
|
203
203
|
name: "ask-gemini",
|
|
204
204
|
label: "Ask Gemini",
|
|
205
205
|
description:
|
|
206
|
-
"Consult Gemini through Ask LLM's canonical executor
|
|
206
|
+
"Consult Gemini through Ask LLM's canonical executor (`gemini-3.1-pro-preview` → `gemini-3.8-flash` on quota), including validation, sessions, and structured response. Output is bounded to Pi's 50KB/2000-line limits.",
|
|
207
207
|
parameters: geminiSchema,
|
|
208
208
|
provider: "gemini",
|
|
209
209
|
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# codex-pair prompt A/B benchmark (ADR-100)
|
|
2
|
+
|
|
3
|
+
Empirical harness for validating prompt-template changes. Built initially
|
|
4
|
+
to test ADR-099 (Karpathy baseline principles), but reusable for any
|
|
5
|
+
future prompt change that wants empirical justification before shipping.
|
|
6
|
+
|
|
7
|
+
## What this measures
|
|
8
|
+
|
|
9
|
+
For each fixture, the driver:
|
|
10
|
+
|
|
11
|
+
1. Renders the review prompt twice — once against the **pre-baseline**
|
|
12
|
+
template (current production prompt without the Karpathy block) and
|
|
13
|
+
once against the **baseline** template (ADR-099's version with the
|
|
14
|
+
block).
|
|
15
|
+
2. Invokes `codex exec --json` with each rendered prompt against the
|
|
16
|
+
same fixture file + context.
|
|
17
|
+
3. Parses the verdict JSON and matches findings against the fixture's
|
|
18
|
+
`probes.json` ground truth.
|
|
19
|
+
4. Aggregates **recall** (true positives caught / total ground-truth
|
|
20
|
+
probes) and **extra findings** (findings not matching any probe —
|
|
21
|
+
either real concerns the probes didn't anticipate, or noise).
|
|
22
|
+
|
|
23
|
+
The headline output is a markdown report with a per-fixture caught /
|
|
24
|
+
missed / extra breakdown and an aggregate recall delta.
|
|
25
|
+
|
|
26
|
+
## Decision rule
|
|
27
|
+
|
|
28
|
+
Ship the prompt change to `prompts/review.txt` permanently if:
|
|
29
|
+
|
|
30
|
+
- **Recall delta ≥ +10 percentage points** (the baseline catches a
|
|
31
|
+
meaningfully larger fraction of ground-truth probes than the
|
|
32
|
+
pre-baseline prompt does)
|
|
33
|
+
- **Extra-finding delta ≤ +1 per fixture on average** (the baseline
|
|
34
|
+
doesn't add overwhelming noise)
|
|
35
|
+
|
|
36
|
+
If recall regresses or noise jumps, the ADR-099 rollback path applies
|
|
37
|
+
(two file edits + one test-assertion update; documented in ADR-099's
|
|
38
|
+
Consequences section).
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Full run (all fixtures, both arms)
|
|
44
|
+
node packages/claude-plugin/scripts/benchmark/prompt-ab.mjs \
|
|
45
|
+
--model gpt-5.5 \
|
|
46
|
+
--out benchmark-report.md
|
|
47
|
+
|
|
48
|
+
# Single-fixture quick check
|
|
49
|
+
node packages/claude-plugin/scripts/benchmark/prompt-ab.mjs \
|
|
50
|
+
--fixtures 01-overcomplication \
|
|
51
|
+
--out single-report.md
|
|
52
|
+
|
|
53
|
+
# Cheaper model for sanity
|
|
54
|
+
node packages/claude-plugin/scripts/benchmark/prompt-ab.mjs \
|
|
55
|
+
--model gpt-5.4-mini \
|
|
56
|
+
--timeout-ms 60000
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Cost
|
|
60
|
+
|
|
61
|
+
- Per fixture per arm: one `codex exec` review (~$0.04–0.07 at current
|
|
62
|
+
gpt-5.5 pricing)
|
|
63
|
+
- Four fixtures × two arms = ~$0.40 per full run
|
|
64
|
+
- Variance is high (codex sometimes returns longer responses); budget
|
|
65
|
+
$1 per careful run to absorb retries
|
|
66
|
+
|
|
67
|
+
## Output interpretation
|
|
68
|
+
|
|
69
|
+
The report has three layers:
|
|
70
|
+
|
|
71
|
+
1. **Aggregate table** — recall percentages per arm, plus a one-line
|
|
72
|
+
recall-delta and extra-finding-delta verdict line that mechanically
|
|
73
|
+
checks against the decision rule.
|
|
74
|
+
2. **Per-fixture sections** — for each fixture and each arm, the list
|
|
75
|
+
of probes caught (✅), missed (❌), and extra findings (⚠️ — these
|
|
76
|
+
require manual judgment whether they're real concerns or noise).
|
|
77
|
+
3. **Token usage** — the per-fixture input/output token counts. Useful
|
|
78
|
+
for cost analysis if the prompt grows further.
|
|
79
|
+
|
|
80
|
+
## Limitations
|
|
81
|
+
|
|
82
|
+
- **Keyword-based probe matching is crude.** A real finding that uses
|
|
83
|
+
different vocabulary than the probe's `keywords` array will be
|
|
84
|
+
flagged as missed. Tune keywords as needed when you see this happen.
|
|
85
|
+
Future enhancement: LLM-judge mode that asks gemini "did this finding
|
|
86
|
+
match this probe?" — costs another ~$0.01 per evaluation but better
|
|
87
|
+
signal.
|
|
88
|
+
- **Four fixtures is small.** Sample size is sufficient to see large
|
|
89
|
+
effects but won't reveal subtle improvements. Add more fixtures as
|
|
90
|
+
you encounter representative bug patterns in the wild — see
|
|
91
|
+
`fixtures/README.md` for the contribution shape.
|
|
92
|
+
- **Codex is non-deterministic.** Run the benchmark twice and you'll
|
|
93
|
+
see modest variance per fixture. Wide swings (>30% recall delta on a
|
|
94
|
+
single fixture) suggest the fixture is borderline and should be
|
|
95
|
+
refined rather than accepted as signal.
|
|
96
|
+
- **The fixtures are not the ADR-077 four-task benchmark.** ADR-077's
|
|
97
|
+
fixtures live on `experiment/codex-pair-poc` and exercise *bug
|
|
98
|
+
detection* (float-money precision etc.). These ADR-100 fixtures
|
|
99
|
+
exercise *prompt-rule detection* (does the Karpathy baseline change
|
|
100
|
+
what codex flags?). Different questions; both empirical.
|
|
101
|
+
|
|
102
|
+
## Adding fixtures
|
|
103
|
+
|
|
104
|
+
See `fixtures/README.md`. Three files per fixture: `code.ts`,
|
|
105
|
+
`context.md`, `probes.json`. The driver auto-discovers any directory
|
|
106
|
+
under `fixtures/` containing all three.
|
|
107
|
+
|
|
108
|
+
## Related ADRs
|
|
109
|
+
|
|
110
|
+
- **ADR-099** — codex-pair Karpathy baseline principles in review prompt
|
|
111
|
+
- **ADR-077** — original codex-pair POC + four-task benchmark
|
|
112
|
+
- **ADR-089** — externalized prompt template + golden fixture invariant
|
|
113
|
+
- **ADR-100** — this benchmark harness (the document you should read for
|
|
114
|
+
methodology rationale)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Benchmark fixtures (ADR-100)
|
|
2
|
+
|
|
3
|
+
Each fixture is a self-contained scenario crafted to exercise one of
|
|
4
|
+
the Karpathy-baseline rules from ADR-099. Each directory contains:
|
|
5
|
+
|
|
6
|
+
- **`code.ts`** — the file content sent to codex for review. Starts
|
|
7
|
+
with a comment block restating the task that was supposedly given to
|
|
8
|
+
Claude, so codex has the same context a real edit would have.
|
|
9
|
+
- **`context.md`** — marker-file content sent as `projectContext` in
|
|
10
|
+
the rendered prompt.
|
|
11
|
+
- **`probes.json`** — the ground-truth expectations: `should_flag`
|
|
12
|
+
entries are findings codex SHOULD raise (true positives); `should_not_flag`
|
|
13
|
+
entries are things codex should NOT raise (negative probes for noise).
|
|
14
|
+
|
|
15
|
+
## Fixture index
|
|
16
|
+
|
|
17
|
+
| Fixture | Tests | Why this exercises the rule |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `01-overcomplication` | Simplicity | Implements `getFullName` via a single-use interface, factory class, runtime null checks against TypeScript types, and unused configurability params — every kind of "code beyond what was asked" |
|
|
20
|
+
| `02-drive-by-refactor` | Surgical scope | Bug fix to `parsePagination` correctly applied, but the diff also includes an unrelated function rename, a reformatted constants block, and a history comment — all changes that should be in a separate PR |
|
|
21
|
+
| `03-orphan-imports` | Surgical scope | Removes `formatV1` correctly, but leaves `decodeBase64` and `logger` imports orphaned (no longer used by anything in the file) |
|
|
22
|
+
| `04-hidden-assumption` | Hidden assumptions | Implements `firstAvailableSlot` correctly for an already-sorted input, but the task asked for the "earliest" slot — depends on an unstated sort-order invariant. Should either sort internally or document the requirement. |
|
|
23
|
+
|
|
24
|
+
## Adding a new fixture
|
|
25
|
+
|
|
26
|
+
Create a new directory under `fixtures/` with the three files. The
|
|
27
|
+
driver auto-discovers any directory containing all three. Naming
|
|
28
|
+
convention: `NN-short-slug/` where `NN` is a two-digit sequence and
|
|
29
|
+
`slug` describes the rule being exercised.
|
|
File without changes
|
|
@@ -19,7 +19,7 @@ import { readFileSync } from "node:fs";
|
|
|
19
19
|
import { access } from "node:fs/promises";
|
|
20
20
|
import { homedir } from "node:os";
|
|
21
21
|
import { dirname, join, resolve } from "node:path";
|
|
22
|
-
import { CONTEXT_FILENAME, logPath as resolveLogPath
|
|
22
|
+
import { CONTEXT_FILENAME, PAIR_ROOT_DIR, logPath as resolveLogPath } from "./lib/state.mjs";
|
|
23
23
|
|
|
24
24
|
const MARKER_FILE = join(PAIR_ROOT_DIR, CONTEXT_FILENAME);
|
|
25
25
|
|
|
@@ -171,17 +171,10 @@ function showSummary(entries) {
|
|
|
171
171
|
}
|
|
172
172
|
if (e.fellBack) fallbackCount++;
|
|
173
173
|
if (e.verdict === "cached") cachedCount++;
|
|
174
|
-
if (
|
|
175
|
-
e.verdict === "none" ||
|
|
176
|
-
e.verdict === "concerns" ||
|
|
177
|
-
e.verdict === "cached"
|
|
178
|
-
) {
|
|
174
|
+
if (e.verdict === "none" || e.verdict === "concerns" || e.verdict === "cached") {
|
|
179
175
|
runCount++;
|
|
180
176
|
}
|
|
181
|
-
if (
|
|
182
|
-
e.durationMs != null &&
|
|
183
|
-
(e.verdict === "none" || e.verdict === "concerns")
|
|
184
|
-
) {
|
|
177
|
+
if (e.durationMs != null && (e.verdict === "none" || e.verdict === "concerns")) {
|
|
185
178
|
durationSum += e.durationMs;
|
|
186
179
|
durationCount++;
|
|
187
180
|
}
|
|
@@ -240,9 +233,7 @@ async function main() {
|
|
|
240
233
|
}
|
|
241
234
|
const markerDir = await findMarkerUp(process.cwd());
|
|
242
235
|
if (!markerDir) {
|
|
243
|
-
process.stderr.write(
|
|
244
|
-
`codex-pair-log: no .codex-pair/context.md marker found in cwd or parents\n`,
|
|
245
|
-
);
|
|
236
|
+
process.stderr.write(`codex-pair-log: no .codex-pair/context.md marker found in cwd or parents\n`);
|
|
246
237
|
process.exit(1);
|
|
247
238
|
}
|
|
248
239
|
const logPath = resolveLogPath(markerDir);
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
import { access } from "node:fs/promises";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { dirname, join, resolve } from "node:path";
|
|
16
|
-
import { CONTEXT_FILENAME, PAIR_ROOT_DIR } from "./lib/state.mjs";
|
|
17
16
|
import { drainPending, joinPendingForSurface } from "./lib/debounce-state.mjs";
|
|
18
17
|
import { collectSessionMarkers } from "./lib/session-registry.mjs";
|
|
18
|
+
import { CONTEXT_FILENAME, PAIR_ROOT_DIR } from "./lib/state.mjs";
|
|
19
19
|
|
|
20
20
|
const MARKER_FILE = join(PAIR_ROOT_DIR, CONTEXT_FILENAME);
|
|
21
21
|
|
|
@@ -13,16 +13,16 @@ import { homedir } from "node:os";
|
|
|
13
13
|
import { dirname, join, resolve } from "node:path";
|
|
14
14
|
import { bootstrapBroker, clearStaleBrokerState, teardownBroker } from "./lib/broker-lifecycle.mjs";
|
|
15
15
|
import { clearAllDebounceState } from "./lib/debounce-state.mjs";
|
|
16
|
+
import { clearSession } from "./lib/session-registry.mjs";
|
|
16
17
|
import {
|
|
17
18
|
appendLog,
|
|
18
|
-
clearAutoPause,
|
|
19
19
|
CONTEXT_FILENAME,
|
|
20
|
+
clearAutoPause,
|
|
20
21
|
PAIR_ROOT_DIR,
|
|
21
22
|
readPauseInfo,
|
|
22
23
|
readPluginVersion,
|
|
23
24
|
resolveAutoResume,
|
|
24
25
|
} from "./lib/state.mjs";
|
|
25
|
-
import { clearSession } from "./lib/session-registry.mjs";
|
|
26
26
|
|
|
27
27
|
const MARKER_FILE = join(PAIR_ROOT_DIR, CONTEXT_FILENAME);
|
|
28
28
|
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
// findMarkerUp is duplicated by design (zero-workspace-imports; see prompt-drain).
|
|
8
8
|
|
|
9
9
|
import { execFileSync } from "node:child_process";
|
|
10
|
-
import { existsSync,
|
|
10
|
+
import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
11
11
|
import { homedir } from "node:os";
|
|
12
12
|
import { dirname, join, resolve } from "node:path";
|
|
13
13
|
import { debounceRoot, drainPending, joinPendingForSurface, reviewingRoot } from "./lib/debounce-state.mjs";
|
|
14
|
+
import { collectSessionMarkers } from "./lib/session-registry.mjs";
|
|
14
15
|
import {
|
|
15
16
|
CONTEXT_FILENAME,
|
|
16
|
-
INFLIGHT_TTL_MIN_MS,
|
|
17
|
-
PAIR_ROOT_DIR,
|
|
18
17
|
contextPath,
|
|
18
|
+
INFLIGHT_TTL_MIN_MS,
|
|
19
19
|
inflightRoot,
|
|
20
20
|
logPath,
|
|
21
|
+
PAIR_ROOT_DIR,
|
|
21
22
|
readAcks,
|
|
22
23
|
} from "./lib/state.mjs";
|
|
23
24
|
import {
|
|
@@ -28,7 +29,6 @@ import {
|
|
|
28
29
|
parseGitPorcelain,
|
|
29
30
|
selectLatestEntries,
|
|
30
31
|
} from "./lib/stop-gate.mjs";
|
|
31
|
-
import { collectSessionMarkers } from "./lib/session-registry.mjs";
|
|
32
32
|
|
|
33
33
|
const MARKER_FILE = join(PAIR_ROOT_DIR, CONTEXT_FILENAME);
|
|
34
34
|
|
|
@@ -131,9 +131,7 @@ function readInFlightInputs(markerDir) {
|
|
|
131
131
|
function inflightFreshMs(markerDir) {
|
|
132
132
|
const fmTimeout = Number(readMarkerScalar(markerDir, "timeoutMs"));
|
|
133
133
|
const timeout =
|
|
134
|
-
Number.isFinite(fmTimeout) && fmTimeout > 0
|
|
135
|
-
? fmTimeout
|
|
136
|
-
: Number(process.env.ASK_CODEX_TIMEOUT_MS ?? 800_000);
|
|
134
|
+
Number.isFinite(fmTimeout) && fmTimeout > 0 ? fmTimeout : Number(process.env.ASK_CODEX_TIMEOUT_MS ?? 800_000);
|
|
137
135
|
return Math.max(timeout, INFLIGHT_TTL_MIN_MS) + 60_000;
|
|
138
136
|
}
|
|
139
137
|
|
|
@@ -266,6 +264,8 @@ async function main() {
|
|
|
266
264
|
}
|
|
267
265
|
|
|
268
266
|
main().catch((err) => {
|
|
269
|
-
process.stderr.write(
|
|
267
|
+
process.stderr.write(
|
|
268
|
+
`[codex-pair] WARNING: stop-gate failed (${err?.message ?? err}). Allowing turn end — HIGH findings may remain.\n`,
|
|
269
|
+
);
|
|
270
270
|
process.exit(0);
|
|
271
271
|
});
|
|
@@ -17,12 +17,11 @@
|
|
|
17
17
|
// invocation is inlined; semantics mirror `codexExecutor.ts` deliberately.
|
|
18
18
|
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
|
-
import { access, readFile } from "node:fs/promises";
|
|
21
20
|
import { readFileSync } from "node:fs";
|
|
22
|
-
import {
|
|
21
|
+
import { access, readFile } from "node:fs/promises";
|
|
23
22
|
import { homedir } from "node:os";
|
|
23
|
+
import { dirname, join, resolve } from "node:path";
|
|
24
24
|
import { fileURLToPath } from "node:url";
|
|
25
|
-
import { IS_WINDOWS, terminateProcessTree } from "./lib/process.mjs";
|
|
26
25
|
// M4: broker integration. Importing initializeBroker + isBrokerEnabled +
|
|
27
26
|
// submitReview from broker.mjs transitively pulls in broker-transport,
|
|
28
27
|
// broker-rpc, broker-lifecycle. ESM-static cost is paid on every hook
|
|
@@ -30,15 +29,14 @@ import { IS_WINDOWS, terminateProcessTree } from "./lib/process.mjs";
|
|
|
30
29
|
// isn't set, so the per-edit fast path is unaffected.
|
|
31
30
|
import { initializeBroker, isBrokerEnabled, readBrokerState, submitReview } from "./lib/broker.mjs";
|
|
32
31
|
import {
|
|
33
|
-
DEFAULT_DEBOUNCE_MS,
|
|
34
|
-
DEFAULT_DEBOUNCE_MAX_MS,
|
|
35
32
|
bumpEditRecord,
|
|
33
|
+
DEFAULT_DEBOUNCE_MAX_MS,
|
|
34
|
+
DEFAULT_DEBOUNCE_MS,
|
|
36
35
|
drainPending,
|
|
37
36
|
joinPendingForSurface,
|
|
38
37
|
markReviewed,
|
|
39
38
|
sweepStaleDebounce,
|
|
40
39
|
} from "./lib/debounce-state.mjs";
|
|
41
|
-
import { buildReviewPrompt } from "./lib/prompt.mjs";
|
|
42
40
|
import {
|
|
43
41
|
buildVerdictMessage,
|
|
44
42
|
DEFAULT_SURFACE_THRESHOLD,
|
|
@@ -48,20 +46,23 @@ import {
|
|
|
48
46
|
VALID_THRESHOLDS,
|
|
49
47
|
VERDICT_PREFIXES,
|
|
50
48
|
} from "./lib/parser.mjs";
|
|
49
|
+
import { IS_WINDOWS, terminateProcessTree } from "./lib/process.mjs";
|
|
50
|
+
import { buildReviewPrompt } from "./lib/prompt.mjs";
|
|
51
|
+
import { registerMarker } from "./lib/session-registry.mjs";
|
|
51
52
|
import {
|
|
52
|
-
appendLog,
|
|
53
53
|
AUTOPAUSE_FAILURE_THRESHOLD,
|
|
54
|
+
appendLog,
|
|
55
|
+
CONTEXT_FILENAME,
|
|
54
56
|
clearAutoPause,
|
|
55
57
|
clearReviewFailures,
|
|
56
58
|
computeCacheKey,
|
|
57
|
-
CONTEXT_FILENAME,
|
|
58
59
|
contextPath,
|
|
59
60
|
getBlockingFromShard,
|
|
60
61
|
getCachedConcerns,
|
|
61
62
|
hashConcernBody,
|
|
63
|
+
INFLIGHT_TTL_MIN_MS,
|
|
62
64
|
ignorePath,
|
|
63
65
|
includePath,
|
|
64
|
-
INFLIGHT_TTL_MIN_MS,
|
|
65
66
|
logPath,
|
|
66
67
|
PAIR_ROOT_DIR,
|
|
67
68
|
readPauseInfo,
|
|
@@ -74,7 +75,6 @@ import {
|
|
|
74
75
|
updateRepetitions,
|
|
75
76
|
writeAutoPause,
|
|
76
77
|
} from "./lib/state.mjs";
|
|
77
|
-
import { registerMarker } from "./lib/session-registry.mjs";
|
|
78
78
|
|
|
79
79
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
80
80
|
const DEFAULTS_PATH = join(SCRIPT_DIR, "..", "codex-pair-defaults.json");
|
|
@@ -293,10 +293,7 @@ function parseFrontmatter(content) {
|
|
|
293
293
|
valueRaw = valueRaw.slice(0, inlineComment.index);
|
|
294
294
|
}
|
|
295
295
|
let value = valueRaw.trim();
|
|
296
|
-
if (
|
|
297
|
-
(value.startsWith('"') && value.endsWith('"')) ||
|
|
298
|
-
(value.startsWith("'") && value.endsWith("'"))
|
|
299
|
-
) {
|
|
296
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
300
297
|
value = value.slice(1, -1);
|
|
301
298
|
}
|
|
302
299
|
if (value === "true") frontmatter[key] = true;
|
|
@@ -505,23 +502,13 @@ function resolveConfig(frontmatter) {
|
|
|
505
502
|
return {
|
|
506
503
|
model: typeof fm.model === "string" && fm.model.length > 0 ? fm.model : DEFAULT_MODEL,
|
|
507
504
|
fallbackModel:
|
|
508
|
-
typeof fm.fallbackModel === "string" && fm.fallbackModel.length > 0
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
timeoutMs:
|
|
512
|
-
typeof fm.timeoutMs === "number" && fm.timeoutMs > 0 ? fm.timeoutMs : DEFAULT_TIMEOUT_MS,
|
|
513
|
-
maxFileBytes:
|
|
514
|
-
typeof fm.maxFileBytes === "number" && fm.maxFileBytes > 0
|
|
515
|
-
? fm.maxFileBytes
|
|
516
|
-
: MAX_FILE_BYTES,
|
|
505
|
+
typeof fm.fallbackModel === "string" && fm.fallbackModel.length > 0 ? fm.fallbackModel : FALLBACK_MODEL,
|
|
506
|
+
timeoutMs: typeof fm.timeoutMs === "number" && fm.timeoutMs > 0 ? fm.timeoutMs : DEFAULT_TIMEOUT_MS,
|
|
507
|
+
maxFileBytes: typeof fm.maxFileBytes === "number" && fm.maxFileBytes > 0 ? fm.maxFileBytes : MAX_FILE_BYTES,
|
|
517
508
|
surfaceThreshold:
|
|
518
|
-
surfaceCandidate && VALID_THRESHOLDS.has(surfaceCandidate)
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
debounceMs:
|
|
522
|
-
typeof fm.debounceMs === "number" && fm.debounceMs >= 0 ? fm.debounceMs : DEBOUNCE_MS,
|
|
523
|
-
debounceMaxMs:
|
|
524
|
-
typeof fm.debounceMaxMs === "number" && fm.debounceMaxMs > 0 ? fm.debounceMaxMs : DEBOUNCE_MAX_MS,
|
|
509
|
+
surfaceCandidate && VALID_THRESHOLDS.has(surfaceCandidate) ? surfaceCandidate : DEFAULT_SURFACE_THRESHOLD,
|
|
510
|
+
debounceMs: typeof fm.debounceMs === "number" && fm.debounceMs >= 0 ? fm.debounceMs : DEBOUNCE_MS,
|
|
511
|
+
debounceMaxMs: typeof fm.debounceMaxMs === "number" && fm.debounceMaxMs > 0 ? fm.debounceMaxMs : DEBOUNCE_MAX_MS,
|
|
525
512
|
};
|
|
526
513
|
}
|
|
527
514
|
|
|
@@ -729,9 +716,7 @@ function spawnCodex({ prompt, model, timeoutMs }) {
|
|
|
729
716
|
setTimeout(() => {
|
|
730
717
|
terminateProcessTree(child, "SIGKILL");
|
|
731
718
|
}, 5000);
|
|
732
|
-
rejectCall(
|
|
733
|
-
taggedError(`codex exec timed out after ${Math.round(timeoutMs / 1000)}s`, "timeout"),
|
|
734
|
-
);
|
|
719
|
+
rejectCall(taggedError(`codex exec timed out after ${Math.round(timeoutMs / 1000)}s`, "timeout"));
|
|
735
720
|
}, timeoutMs);
|
|
736
721
|
|
|
737
722
|
child.on("error", (err) => {
|
|
@@ -757,9 +742,7 @@ function spawnCodex({ prompt, model, timeoutMs }) {
|
|
|
757
742
|
// tail (often just the stdin banner) — #176.
|
|
758
743
|
const errorEvents = extractJsonlErrorEvents(stdout);
|
|
759
744
|
const reason =
|
|
760
|
-
errorEvents.length > 0
|
|
761
|
-
? errorEvents[errorEvents.length - 1]
|
|
762
|
-
: stderrTail(stderr) || `codex exit ${code}`;
|
|
745
|
+
errorEvents.length > 0 ? errorEvents[errorEvents.length - 1] : stderrTail(stderr) || `codex exit ${code}`;
|
|
763
746
|
rejectCall(taggedError(reason, "error"));
|
|
764
747
|
}
|
|
765
748
|
});
|
|
@@ -1209,9 +1192,7 @@ async function main() {
|
|
|
1209
1192
|
verdict: "skipped",
|
|
1210
1193
|
reason: `unreadable: ${err.message}`,
|
|
1211
1194
|
});
|
|
1212
|
-
await emitSystemMessage(
|
|
1213
|
-
`codex-pair ${VERDICT_PREFIXES.skipped}: ${filePath} — unreadable (${err.message})`,
|
|
1214
|
-
);
|
|
1195
|
+
await emitSystemMessage(`codex-pair ${VERDICT_PREFIXES.skipped}: ${filePath} — unreadable (${err.message})`);
|
|
1215
1196
|
process.exit(0);
|
|
1216
1197
|
}
|
|
1217
1198
|
|
|
@@ -6,7 +6,7 @@ description: Get a second opinion from Gemini on your current code changes. Anal
|
|
|
6
6
|
<!-- PORTABLE-CONTRACT:START -->
|
|
7
7
|
## Portable contract
|
|
8
8
|
|
|
9
|
-
Gather the relevant staged, unstaged, and untracked code changes; build a bounded context brief; request a Gemini review; verify each reported finding against source; and return only prioritized, source-supported findings. Preserve
|
|
9
|
+
Gather the relevant staged, unstaged, and untracked code changes; build a bounded context brief; request a Gemini review; verify each reported finding against source; and return only prioritized, source-supported findings. Preserve the canonical `gemini-3.1-pro-preview` → `gemini-3.8-flash` quota fallback, timeout, and explicit failure disclosure.
|
|
10
10
|
<!-- PORTABLE-CONTRACT:END -->
|
|
11
11
|
|
|
12
12
|
## Host adapters
|