@motivation-labs/crosscheck 0.2.1-beta.f07295b.0 → 0.3.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/AGENT.md +207 -0
- package/dist/__tests__/diagnose.test.d.ts +2 -0
- package/dist/__tests__/diagnose.test.d.ts.map +1 -0
- package/dist/__tests__/diagnose.test.js +164 -0
- package/dist/__tests__/diagnose.test.js.map +1 -0
- package/dist/__tests__/optimize.test.d.ts +2 -0
- package/dist/__tests__/optimize.test.d.ts.map +1 -0
- package/dist/__tests__/optimize.test.js +88 -0
- package/dist/__tests__/optimize.test.js.map +1 -0
- package/dist/cli.js +17 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/diagnose.d.ts +53 -0
- package/dist/commands/diagnose.d.ts.map +1 -0
- package/dist/commands/diagnose.js +280 -0
- package/dist/commands/diagnose.js.map +1 -0
- package/dist/commands/optimize.d.ts +16 -0
- package/dist/commands/optimize.d.ts.map +1 -0
- package/dist/commands/optimize.js +209 -0
- package/dist/commands/optimize.js.map +1 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +3 -1
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +6 -20
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +105 -73
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +22 -10
- package/dist/config/loader.js.map +1 -1
- package/dist/lib/instructions.d.ts +5 -0
- package/dist/lib/instructions.d.ts.map +1 -0
- package/dist/lib/instructions.js +61 -0
- package/dist/lib/instructions.js.map +1 -0
- package/dist/lib/languages.d.ts +3 -0
- package/dist/lib/languages.d.ts.map +1 -0
- package/dist/lib/languages.js +26 -0
- package/dist/lib/languages.js.map +1 -0
- package/dist/reviewers/claude.d.ts.map +1 -1
- package/dist/reviewers/claude.js +4 -5
- package/dist/reviewers/claude.js.map +1 -1
- package/dist/reviewers/codex.d.ts.map +1 -1
- package/dist/reviewers/codex.js +4 -11
- package/dist/reviewers/codex.js.map +1 -1
- package/package.json +5 -2
package/AGENT.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# AGENT.md — crosscheck optimize harness
|
|
2
|
+
|
|
3
|
+
You are improving crosscheck's AI code review instructions. crosscheck runs `codex` and
|
|
4
|
+
`claude` CLIs to review pull requests automatically. Both reviewers read
|
|
5
|
+
`~/.crosscheck/instructions.md` before every review. Your job is to read the diagnostic
|
|
6
|
+
report below and produce an improved `instructions.md` that increases review quality and
|
|
7
|
+
reduces failures.
|
|
8
|
+
|
|
9
|
+
> **Note:** `crosscheck optimize` selects which agent runs you based on your local config
|
|
10
|
+
> and log history — whichever reviewer has the highest success rate, or `claude` if there
|
|
11
|
+
> is no data. The instructions you produce are reviewer-agnostic: they are read by both
|
|
12
|
+
> `claude` and `codex`, so write in plain language that both understand.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Input you will receive
|
|
17
|
+
|
|
18
|
+
1. **Diagnostic JSON** from `crosscheck diagnose --json` — error patterns, review outcomes,
|
|
19
|
+
repos seen, language signals, and suggestions.
|
|
20
|
+
2. **Current `instructions.md`** — may be empty on first run.
|
|
21
|
+
|
|
22
|
+
## Output you must produce
|
|
23
|
+
|
|
24
|
+
Respond with only the new content of `instructions.md`. No explanation, no preamble, no
|
|
25
|
+
markdown fences — just the file content. The file uses plain Markdown.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Required sections (always present)
|
|
30
|
+
|
|
31
|
+
### `## Constraints`
|
|
32
|
+
What reviewers must NOT do. Each constraint is one bullet:
|
|
33
|
+
```
|
|
34
|
+
- Do not run [specific command].
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### `## Focus`
|
|
38
|
+
What reviewers should prioritize. Free-form prose or bullets.
|
|
39
|
+
|
|
40
|
+
### `## Verdict format` (never modify this section)
|
|
41
|
+
This section must always be preserved exactly as written in the current file. If it is
|
|
42
|
+
missing from the current file, add it verbatim:
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
## Verdict format
|
|
46
|
+
|
|
47
|
+
On the very last line of your response, write exactly one of:
|
|
48
|
+
|
|
49
|
+
VERDICT: APPROVE
|
|
50
|
+
VERDICT: NEEDS WORK
|
|
51
|
+
VERDICT: BLOCK
|
|
52
|
+
|
|
53
|
+
Use APPROVE for no issues or trivial nits only.
|
|
54
|
+
Use NEEDS WORK for addressable issues that are not blocking.
|
|
55
|
+
Use BLOCK for security risks, data loss, broken API contracts, or correctness bugs.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Rules for the `## Constraints` section
|
|
61
|
+
|
|
62
|
+
### Add a constraint when
|
|
63
|
+
- `diagnostic.errors` contains a `command_not_found` pattern with `count >= 1` for a
|
|
64
|
+
specific command.
|
|
65
|
+
- `diagnostic.errors` contains a `timeout` pattern with `count >= 2` for a reviewer that
|
|
66
|
+
is also producing `command_not_found` entries (likely spinning on a failed tool call).
|
|
67
|
+
|
|
68
|
+
### Remove a constraint when
|
|
69
|
+
- It was previously added for a specific command but that command no longer appears in
|
|
70
|
+
`diagnostic.errors` and has not appeared for the full log period analyzed.
|
|
71
|
+
|
|
72
|
+
### Phrasing rules
|
|
73
|
+
- Use the exact command name, not a category. Write `Do not run tsc.` not `Do not run
|
|
74
|
+
TypeScript build tools.`
|
|
75
|
+
- One command per bullet. Do not combine: `Do not run tsc or npm.` → split into two.
|
|
76
|
+
- Do not restrict reading. `Do not open package.json` is wrong. Constraints are for
|
|
77
|
+
execution only.
|
|
78
|
+
- Do not restrict security analysis. Never add a constraint that would prevent a reviewer
|
|
79
|
+
from flagging a vulnerability.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Language detection → constraint mapping
|
|
84
|
+
|
|
85
|
+
Use `diagnostic.languages_detected` (list of detected language/tool identifiers) to seed
|
|
86
|
+
the initial constraints. Add the corresponding constraint only if the language is detected
|
|
87
|
+
AND a related `command_not_found` error is present OR this is the first run with no
|
|
88
|
+
existing constraints.
|
|
89
|
+
|
|
90
|
+
| Detected signal | Constraint to add |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `typescript` / `tsconfig.json` / `package.json` | `Do not run tsc.` |
|
|
93
|
+
| `nodejs` / `package.json` | `Do not run npm, npx, yarn, or pnpm.` |
|
|
94
|
+
| `jest` / `vitest` in devDependencies | `Do not run jest or vitest.` |
|
|
95
|
+
| `python` / `requirements.txt` / `pyproject.toml` | `Do not run pytest, pip, or python scripts.` |
|
|
96
|
+
| `rust` / `Cargo.toml` | `Do not run cargo build or cargo test.` |
|
|
97
|
+
| `go` / `go.mod` | `Do not run go build or go test.` |
|
|
98
|
+
| `java` / `pom.xml` | `Do not run mvn.` |
|
|
99
|
+
| `kotlin` / `gradle` / `build.gradle` | `Do not run gradle.` |
|
|
100
|
+
| `ruby` / `Gemfile` | `Do not run bundle exec or rspec.` |
|
|
101
|
+
|
|
102
|
+
Do not add constraints for languages not detected. Do not add all of the above blindly.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Rules for the `## Focus` section
|
|
107
|
+
|
|
108
|
+
### Update focus when
|
|
109
|
+
- `diagnostic.verdict_distribution.APPROVE` percentage > 80% across >= 10 reviews → reviews
|
|
110
|
+
may be too lenient → add: "Apply strict scrutiny to error handling and edge cases."
|
|
111
|
+
- `diagnostic.verdict_distribution.BLOCK` percentage > 30% across >= 10 reviews → reviews
|
|
112
|
+
may be too strict → add: "Prefer NEEDS WORK over BLOCK unless the issue causes data loss
|
|
113
|
+
or a security vulnerability."
|
|
114
|
+
- `diagnostic.repos_seen` consistently includes infrastructure repos (name contains
|
|
115
|
+
`-infra`, `-deploy`, `-k8s`) → add: "Pay extra attention to secrets, env vars, and IAM
|
|
116
|
+
permissions in infrastructure changes."
|
|
117
|
+
|
|
118
|
+
### Default focus (use when no signals override it)
|
|
119
|
+
```markdown
|
|
120
|
+
## Focus
|
|
121
|
+
|
|
122
|
+
Review for correctness, security, and maintainability. Flag issues that would cause bugs
|
|
123
|
+
in production, expose sensitive data, or make the code significantly harder to maintain.
|
|
124
|
+
Nits and style preferences should be NEEDS WORK, not BLOCK.
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Quality principles
|
|
130
|
+
|
|
131
|
+
1. **Minimal** — fewer instructions produce better reviews. Each line must earn its place.
|
|
132
|
+
2. **Specific** — exact command names, concrete criteria, not vague categories.
|
|
133
|
+
3. **Evidence-based** — add only what the diagnostic shows is needed or what language
|
|
134
|
+
detection justifies.
|
|
135
|
+
4. **Reversible** — stale constraints (no matching errors in the log period) should be
|
|
136
|
+
removed, not accumulated.
|
|
137
|
+
5. **Reviewer-agnostic** — these instructions are read by both `codex` and `claude`. Write
|
|
138
|
+
them in plain language that both understand.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## What must never change
|
|
143
|
+
|
|
144
|
+
- The `## Verdict format` section content and label.
|
|
145
|
+
- Instructions that the user has manually annotated with `<!-- keep -->`.
|
|
146
|
+
- The overall Markdown structure (## headings, bullet lists).
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Worked example
|
|
151
|
+
|
|
152
|
+
### Input diagnostic (excerpt)
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"summary": { "total_reviews": 6, "successful": 3, "failed": 3, "failure_rate": 0.5 },
|
|
156
|
+
"errors": [
|
|
157
|
+
{ "pattern": "command_not_found", "command": "tsc", "count": 2, "reviewer": "codex" },
|
|
158
|
+
{ "pattern": "command_not_found", "command": "jest", "count": 1, "reviewer": "codex" },
|
|
159
|
+
{ "pattern": "base_branch_missing", "branch": "staging", "count": 2 }
|
|
160
|
+
],
|
|
161
|
+
"verdict_distribution": { "APPROVE": 2, "NEEDS_WORK": 1, "BLOCK": 0 },
|
|
162
|
+
"languages_detected": ["typescript", "nodejs"],
|
|
163
|
+
"suggestions": [
|
|
164
|
+
{ "type": "add_constraint", "instruction": "Do not run tsc.", "reason": "tsc not found ×2" },
|
|
165
|
+
{ "type": "add_constraint", "instruction": "Do not run jest.", "reason": "jest not found ×1" }
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Input current instructions.md
|
|
171
|
+
```markdown
|
|
172
|
+
## Verdict format
|
|
173
|
+
|
|
174
|
+
On the very last line of your response, write exactly one of:
|
|
175
|
+
...
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Correct output
|
|
179
|
+
```markdown
|
|
180
|
+
## Constraints
|
|
181
|
+
|
|
182
|
+
- Do not run tsc.
|
|
183
|
+
- Do not run jest.
|
|
184
|
+
- Do not run npm, npx, yarn, or pnpm.
|
|
185
|
+
|
|
186
|
+
## Focus
|
|
187
|
+
|
|
188
|
+
Review for correctness, security, and maintainability. Flag issues that would cause bugs
|
|
189
|
+
in production, expose sensitive data, or make the code significantly harder to maintain.
|
|
190
|
+
Nits and style preferences should be NEEDS WORK, not BLOCK.
|
|
191
|
+
|
|
192
|
+
## Verdict format
|
|
193
|
+
|
|
194
|
+
On the very last line of your response, write exactly one of:
|
|
195
|
+
|
|
196
|
+
VERDICT: APPROVE
|
|
197
|
+
VERDICT: NEEDS WORK
|
|
198
|
+
VERDICT: BLOCK
|
|
199
|
+
|
|
200
|
+
Use APPROVE for no issues or trivial nits only.
|
|
201
|
+
Use NEEDS WORK for addressable issues that are not blocking.
|
|
202
|
+
Use BLOCK for security risks, data loss, broken API contracts, or correctness bugs.
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Note: `Do not run npm, npx, yarn, or pnpm.` was added because `nodejs` was detected, even
|
|
206
|
+
though no npm error appeared yet — this is the language-detection pre-emptive path for
|
|
207
|
+
first-run seeding.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/diagnose.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { classifyError, buildDiagnoseReport } from '../commands/diagnose.js';
|
|
5
|
+
import { INDICATORS } from '../lib/languages.js';
|
|
6
|
+
const FIXTURES_DIR = join(fileURLToPath(import.meta.url), '..', 'fixtures');
|
|
7
|
+
describe('classifyError', () => {
|
|
8
|
+
it('detects command_not_found for tsc', () => {
|
|
9
|
+
const r = classifyError('sh: tsc: command not found');
|
|
10
|
+
expect(r.pattern).toBe('command_not_found');
|
|
11
|
+
expect(r.command).toBe('tsc');
|
|
12
|
+
});
|
|
13
|
+
it('detects command_not_found (bash format)', () => {
|
|
14
|
+
const r = classifyError('bash: jest: not found');
|
|
15
|
+
expect(r.pattern).toBe('command_not_found');
|
|
16
|
+
expect(r.command).toBe('jest');
|
|
17
|
+
});
|
|
18
|
+
it('detects base_branch_missing with quoted branch', () => {
|
|
19
|
+
const r = classifyError("fatal: no such branch: 'staging'");
|
|
20
|
+
expect(r.pattern).toBe('base_branch_missing');
|
|
21
|
+
expect(r.branch).toBe('staging');
|
|
22
|
+
});
|
|
23
|
+
it('detects base_branch_missing without quotes', () => {
|
|
24
|
+
const r = classifyError('fatal: no such branch: main');
|
|
25
|
+
expect(r.pattern).toBe('base_branch_missing');
|
|
26
|
+
expect(r.branch).toBe('main');
|
|
27
|
+
});
|
|
28
|
+
it('detects timeout', () => {
|
|
29
|
+
const r = classifyError('Request timed out after 120000ms');
|
|
30
|
+
expect(r.pattern).toBe('timeout');
|
|
31
|
+
});
|
|
32
|
+
it('detects timeout (ETIMEDOUT)', () => {
|
|
33
|
+
const r = classifyError('connect ETIMEDOUT 10.0.0.1:443');
|
|
34
|
+
expect(r.pattern).toBe('timeout');
|
|
35
|
+
});
|
|
36
|
+
it('detects auth_failure on 401', () => {
|
|
37
|
+
const r = classifyError('Response 401: bad credentials');
|
|
38
|
+
expect(r.pattern).toBe('auth_failure');
|
|
39
|
+
});
|
|
40
|
+
it('detects auth_failure on unauthorized', () => {
|
|
41
|
+
const r = classifyError('Error: Unauthorized — check your token');
|
|
42
|
+
expect(r.pattern).toBe('auth_failure');
|
|
43
|
+
});
|
|
44
|
+
it('falls back to other for unrecognised message', () => {
|
|
45
|
+
const r = classifyError('some completely unknown error');
|
|
46
|
+
expect(r.pattern).toBe('other');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe('LANGUAGE_CONSTRAINT_MAP / INDICATORS consistency', () => {
|
|
50
|
+
it('every language id in INDICATORS has a corresponding constraint entry in diagnose', () => {
|
|
51
|
+
// Import the internal map indirectly by checking that buildDiagnoseReport produces
|
|
52
|
+
// suggestions for every language id that INDICATORS can produce.
|
|
53
|
+
// We do this by asserting INDICATORS covers only known language ids, detected via
|
|
54
|
+
// a fixture with command_not_found errors for each mapped command.
|
|
55
|
+
// The key assertion: INDICATORS values are a subset of the constraint map keys.
|
|
56
|
+
// We expose this by checking that no language id from INDICATORS is silently dropped.
|
|
57
|
+
const indicatorLangs = new Set(INDICATORS.map(([, lang]) => lang));
|
|
58
|
+
// Build a minimal report with one error per language to trigger suggestions
|
|
59
|
+
// then assert the suggestions cover all indicator languages that are also
|
|
60
|
+
// in COMMAND_LANGUAGE_MAP (those have constraint entries).
|
|
61
|
+
// Simpler: just assert the known set matches expectation so any future addition
|
|
62
|
+
// to INDICATORS without a matching constraint fails here.
|
|
63
|
+
const expected = new Set(['typescript', 'nodejs', 'python', 'rust', 'golang', 'java', 'ruby']);
|
|
64
|
+
expect(indicatorLangs).toEqual(expected);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('buildDiagnoseReport', () => {
|
|
68
|
+
it('returns empty report when log dir does not exist', () => {
|
|
69
|
+
const r = buildDiagnoseReport(undefined, '/nonexistent/path/that/does/not/exist');
|
|
70
|
+
expect(r.summary.total_reviews).toBe(0);
|
|
71
|
+
expect(r.period.log_files).toBe(0);
|
|
72
|
+
expect(r.errors).toHaveLength(0);
|
|
73
|
+
});
|
|
74
|
+
it('parses fixture and counts reviews correctly', () => {
|
|
75
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
76
|
+
// 2026-01-10: 5 started, 3 complete; 2026-01-11: 3 started, 1 complete
|
|
77
|
+
expect(r.summary.total_reviews).toBe(8);
|
|
78
|
+
expect(r.summary.successful).toBe(4);
|
|
79
|
+
expect(r.summary.failed).toBe(4);
|
|
80
|
+
});
|
|
81
|
+
it('detects languages from review_started events', () => {
|
|
82
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
83
|
+
expect(r.languages_detected).toContain('typescript');
|
|
84
|
+
expect(r.languages_detected).toContain('nodejs');
|
|
85
|
+
});
|
|
86
|
+
it('identifies command_not_found errors', () => {
|
|
87
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
88
|
+
const tscErr = r.errors.find(e => e.pattern === 'command_not_found' && e.command === 'tsc');
|
|
89
|
+
expect(tscErr).toBeDefined();
|
|
90
|
+
expect(tscErr.count).toBe(1);
|
|
91
|
+
const jestErr = r.errors.find(e => e.pattern === 'command_not_found' && e.command === 'jest');
|
|
92
|
+
expect(jestErr).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
it('identifies base_branch_missing errors', () => {
|
|
95
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
96
|
+
const err = r.errors.find(e => e.pattern === 'base_branch_missing');
|
|
97
|
+
expect(err).toBeDefined();
|
|
98
|
+
expect(err.branch).toBe('staging');
|
|
99
|
+
});
|
|
100
|
+
it('identifies timeout errors', () => {
|
|
101
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
102
|
+
const err = r.errors.find(e => e.pattern === 'timeout');
|
|
103
|
+
expect(err).toBeDefined();
|
|
104
|
+
});
|
|
105
|
+
it('builds constraint suggestions from errors', () => {
|
|
106
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
107
|
+
const constraints = r.suggestions.filter(s => s.type === 'add_constraint');
|
|
108
|
+
expect(constraints.length).toBeGreaterThan(0);
|
|
109
|
+
expect(constraints.some(s => s.instruction?.includes('tsc'))).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
it('tracks verdict distribution', () => {
|
|
112
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
113
|
+
expect(r.verdict_distribution.APPROVE).toBe(2);
|
|
114
|
+
expect(r.verdict_distribution.NEEDS_WORK).toBe(1);
|
|
115
|
+
expect(r.verdict_distribution.BLOCK).toBe(1);
|
|
116
|
+
});
|
|
117
|
+
it('aggregates reviewer performance', () => {
|
|
118
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
119
|
+
const claude = r.reviewer_performance['claude'];
|
|
120
|
+
const codex = r.reviewer_performance['codex'];
|
|
121
|
+
expect(claude).toBeDefined();
|
|
122
|
+
expect(codex).toBeDefined();
|
|
123
|
+
// 2026-01-10: claude 2 starts, 2 complete; 2026-01-11: claude 1 start, 1 complete
|
|
124
|
+
expect(claude.attempts).toBe(3);
|
|
125
|
+
expect(claude.successes).toBe(3);
|
|
126
|
+
// 2026-01-10: codex 3 starts, 1 complete; 2026-01-11: codex 2 starts, 0 complete
|
|
127
|
+
expect(codex.attempts).toBe(5);
|
|
128
|
+
expect(codex.successes).toBe(1);
|
|
129
|
+
});
|
|
130
|
+
it('--since filters to the specified date file only', () => {
|
|
131
|
+
const r = buildDiagnoseReport('2026-01-11', FIXTURES_DIR);
|
|
132
|
+
expect(r.period.log_files).toBe(1);
|
|
133
|
+
expect(r.summary.total_reviews).toBe(3);
|
|
134
|
+
});
|
|
135
|
+
it('tolerates malformed NDJSON lines', () => {
|
|
136
|
+
// 2026-01-11 fixture contains one malformed line — should not throw
|
|
137
|
+
expect(() => buildDiagnoseReport(undefined, FIXTURES_DIR)).not.toThrow();
|
|
138
|
+
});
|
|
139
|
+
it('repos_seen collects unique repo names', () => {
|
|
140
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
141
|
+
expect(r.repos_seen).toContain('acme/api');
|
|
142
|
+
expect(r.repos_seen).toContain('acme/web');
|
|
143
|
+
});
|
|
144
|
+
it('period.from and period.to reflect file names', () => {
|
|
145
|
+
const r = buildDiagnoseReport(undefined, FIXTURES_DIR);
|
|
146
|
+
expect(r.period.from).toBe('2026-01-10');
|
|
147
|
+
expect(r.period.to).toBe('2026-01-11');
|
|
148
|
+
});
|
|
149
|
+
it('failedReviews is never negative when --since truncates review_started events', () => {
|
|
150
|
+
// 2026-01-11 file has 3 starts and 1 complete.
|
|
151
|
+
// Using --since on 2026-01-11 only reads that file — no negative possible here,
|
|
152
|
+
// but we assert the invariant holds regardless.
|
|
153
|
+
const r = buildDiagnoseReport('2026-01-11', FIXTURES_DIR);
|
|
154
|
+
expect(r.summary.failed).toBeGreaterThanOrEqual(0);
|
|
155
|
+
expect(r.summary.failure_rate).toBeGreaterThanOrEqual(0);
|
|
156
|
+
});
|
|
157
|
+
it('reviewer attribution on errors uses review_started entry', () => {
|
|
158
|
+
// 2026-01-10: PR #1 was started by codex and errored with tsc not found
|
|
159
|
+
const r = buildDiagnoseReport('2026-01-10', FIXTURES_DIR);
|
|
160
|
+
const tscErr = r.errors.find(e => e.pattern === 'command_not_found' && e.command === 'tsc');
|
|
161
|
+
expect(tscErr?.reviewer).toBe('codex');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=diagnose.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.test.js","sourceRoot":"","sources":["../../src/__tests__/diagnose.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimize.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/optimize.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { selectOptimizeAgent } from '../commands/optimize.js';
|
|
3
|
+
function makeConfig(claudeEnabled, codexEnabled) {
|
|
4
|
+
return {
|
|
5
|
+
mode: 'cross-vendor',
|
|
6
|
+
orgs: [],
|
|
7
|
+
repos: [],
|
|
8
|
+
routing: { codex_reviews_patterns: [], claude_reviews_patterns: [] },
|
|
9
|
+
server: { port: 7892, webhook_path: '/webhook' },
|
|
10
|
+
quality: { tier: 'balanced', focus: [], custom_prompt: undefined },
|
|
11
|
+
budget: { codex_monthly_usd: null, per_review_usd: 1 },
|
|
12
|
+
vendors: {
|
|
13
|
+
claude: { enabled: claudeEnabled, auth: 'subscription', effort: 'medium' },
|
|
14
|
+
codex: { enabled: codexEnabled, auth: 'subscription', effort: 'medium' },
|
|
15
|
+
},
|
|
16
|
+
logs: { enabled: false, retention_days: 7 },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function makeReport(claudeAttempts = 0, claudeSuccesses = 0, codexAttempts = 0, codexSuccesses = 0) {
|
|
20
|
+
const performance = {};
|
|
21
|
+
if (claudeAttempts > 0) {
|
|
22
|
+
performance['claude'] = {
|
|
23
|
+
attempts: claudeAttempts,
|
|
24
|
+
successes: claudeSuccesses,
|
|
25
|
+
failure_rate: (claudeAttempts - claudeSuccesses) / claudeAttempts,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (codexAttempts > 0) {
|
|
29
|
+
performance['codex'] = {
|
|
30
|
+
attempts: codexAttempts,
|
|
31
|
+
successes: codexSuccesses,
|
|
32
|
+
failure_rate: (codexAttempts - codexSuccesses) / codexAttempts,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
period: { from: 'N/A', to: 'N/A', log_files: 0 },
|
|
37
|
+
summary: { total_reviews: 0, successful: 0, failed: 0, failure_rate: 0 },
|
|
38
|
+
errors: [],
|
|
39
|
+
verdict_distribution: { APPROVE: 0, NEEDS_WORK: 0, BLOCK: 0 },
|
|
40
|
+
repos_seen: [],
|
|
41
|
+
languages_detected: [],
|
|
42
|
+
reviewer_performance: performance,
|
|
43
|
+
suggestions: [],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
describe('selectOptimizeAgent', () => {
|
|
47
|
+
it('returns codex when only codex is enabled', () => {
|
|
48
|
+
const { agent } = selectOptimizeAgent(makeConfig(false, true), makeReport());
|
|
49
|
+
expect(agent).toBe('codex');
|
|
50
|
+
});
|
|
51
|
+
it('returns claude when only claude is enabled', () => {
|
|
52
|
+
const { agent } = selectOptimizeAgent(makeConfig(true, false), makeReport());
|
|
53
|
+
expect(agent).toBe('claude');
|
|
54
|
+
});
|
|
55
|
+
it('returns claude as default when both enabled and no log data', () => {
|
|
56
|
+
const { agent, reason } = selectOptimizeAgent(makeConfig(true, true), makeReport());
|
|
57
|
+
expect(agent).toBe('claude');
|
|
58
|
+
expect(reason).toMatch(/default/);
|
|
59
|
+
});
|
|
60
|
+
it('returns codex when both enabled and codex has higher success rate', () => {
|
|
61
|
+
// codex 80%, claude 50%
|
|
62
|
+
const { agent } = selectOptimizeAgent(makeConfig(true, true), makeReport(10, 5, 10, 8));
|
|
63
|
+
expect(agent).toBe('codex');
|
|
64
|
+
});
|
|
65
|
+
it('returns claude when both enabled and claude has higher success rate', () => {
|
|
66
|
+
// claude 90%, codex 60%
|
|
67
|
+
const { agent } = selectOptimizeAgent(makeConfig(true, true), makeReport(10, 9, 10, 6));
|
|
68
|
+
expect(agent).toBe('claude');
|
|
69
|
+
});
|
|
70
|
+
it('returns claude (default) when both enabled and rates are equal', () => {
|
|
71
|
+
// both 70%
|
|
72
|
+
const { agent } = selectOptimizeAgent(makeConfig(true, true), makeReport(10, 7, 10, 7));
|
|
73
|
+
expect(agent).toBe('claude');
|
|
74
|
+
});
|
|
75
|
+
it('throws when no vendors are enabled', () => {
|
|
76
|
+
expect(() => selectOptimizeAgent(makeConfig(false, false), makeReport())).toThrow(/No vendors enabled/);
|
|
77
|
+
});
|
|
78
|
+
it('reason string mentions source when only one vendor enabled', () => {
|
|
79
|
+
const { reason } = selectOptimizeAgent(makeConfig(true, false), makeReport());
|
|
80
|
+
expect(reason).toMatch(/only enabled vendor/);
|
|
81
|
+
});
|
|
82
|
+
it('reason string includes success rates when chosen by data', () => {
|
|
83
|
+
const { reason } = selectOptimizeAgent(makeConfig(true, true), makeReport(10, 5, 10, 8));
|
|
84
|
+
expect(reason).toMatch(/80%/);
|
|
85
|
+
expect(reason).toMatch(/50%/);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=optimize.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimize.test.js","sourceRoot":"","sources":["../../src/__tests__/optimize.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,8 @@ import { runServe } from './commands/serve.js';
|
|
|
5
5
|
import { runWatch } from './commands/watch.js';
|
|
6
6
|
import { runReview } from './commands/review.js';
|
|
7
7
|
import { runStatus } from './commands/status.js';
|
|
8
|
+
import { runDiagnose } from './commands/diagnose.js';
|
|
9
|
+
import { runOptimize } from './commands/optimize.js';
|
|
8
10
|
const program = new Command();
|
|
9
11
|
program
|
|
10
12
|
.name('crosscheck')
|
|
@@ -36,5 +38,20 @@ program
|
|
|
36
38
|
.description('Show auth state, config summary, and CLI versions')
|
|
37
39
|
.option('-c, --config <path>', 'config file path')
|
|
38
40
|
.action((opts) => void runStatus(opts.config));
|
|
41
|
+
program
|
|
42
|
+
.command('diagnose')
|
|
43
|
+
.description('Analyze review logs — surface failure patterns, error trends, and improvement suggestions')
|
|
44
|
+
.option('--json', 'output full report as JSON')
|
|
45
|
+
.option('--since <date>', 'only analyze logs from this date onward (YYYY-MM-DD)')
|
|
46
|
+
.action((opts) => void runDiagnose(opts));
|
|
47
|
+
program
|
|
48
|
+
.command('optimize')
|
|
49
|
+
.description('Use AI to improve review instructions based on diagnose output')
|
|
50
|
+
.option('--apply', 'write the improved instructions to ~/.crosscheck/instructions.md')
|
|
51
|
+
.option('--dry-run', 'show diff without writing (default behavior)')
|
|
52
|
+
.option('--agent <vendor>', 'force a specific agent: claude | codex')
|
|
53
|
+
.option('--since <date>', 'limit the diagnose window (YYYY-MM-DD)')
|
|
54
|
+
.option('-c, --config <path>', 'config file path')
|
|
55
|
+
.action((opts) => void runOptimize(opts));
|
|
39
56
|
program.parse();
|
|
40
57
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
type ErrorPattern = 'command_not_found' | 'base_branch_missing' | 'timeout' | 'auth_failure' | 'other';
|
|
2
|
+
interface ErrorEntry {
|
|
3
|
+
pattern: ErrorPattern;
|
|
4
|
+
command?: string;
|
|
5
|
+
branch?: string;
|
|
6
|
+
count: number;
|
|
7
|
+
reviewer?: string;
|
|
8
|
+
}
|
|
9
|
+
interface ReviewerPerf {
|
|
10
|
+
attempts: number;
|
|
11
|
+
successes: number;
|
|
12
|
+
failure_rate: number;
|
|
13
|
+
}
|
|
14
|
+
interface Suggestion {
|
|
15
|
+
type: 'add_constraint' | 'investigate' | 'config_change';
|
|
16
|
+
instruction?: string;
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
export interface DiagnoseReport {
|
|
20
|
+
period: {
|
|
21
|
+
from: string;
|
|
22
|
+
to: string;
|
|
23
|
+
log_files: number;
|
|
24
|
+
};
|
|
25
|
+
summary: {
|
|
26
|
+
total_reviews: number;
|
|
27
|
+
successful: number;
|
|
28
|
+
failed: number;
|
|
29
|
+
failure_rate: number;
|
|
30
|
+
};
|
|
31
|
+
errors: ErrorEntry[];
|
|
32
|
+
verdict_distribution: {
|
|
33
|
+
APPROVE: number;
|
|
34
|
+
NEEDS_WORK: number;
|
|
35
|
+
BLOCK: number;
|
|
36
|
+
};
|
|
37
|
+
repos_seen: string[];
|
|
38
|
+
languages_detected: string[];
|
|
39
|
+
reviewer_performance: Record<string, ReviewerPerf>;
|
|
40
|
+
suggestions: Suggestion[];
|
|
41
|
+
}
|
|
42
|
+
export declare function classifyError(message: string): {
|
|
43
|
+
pattern: ErrorPattern;
|
|
44
|
+
command?: string;
|
|
45
|
+
branch?: string;
|
|
46
|
+
};
|
|
47
|
+
export declare function buildDiagnoseReport(since?: string, logDir?: string): DiagnoseReport;
|
|
48
|
+
export declare function runDiagnose(opts: {
|
|
49
|
+
json?: boolean;
|
|
50
|
+
since?: string;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=diagnose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/commands/diagnose.ts"],"names":[],"mappings":"AAQA,KAAK,YAAY,GAAG,mBAAmB,GAAG,qBAAqB,GAAG,SAAS,GAAG,cAAc,GAAG,OAAO,CAAA;AAEtG,UAAU,UAAU;IAClB,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,gBAAgB,GAAG,aAAa,GAAG,eAAe,CAAA;IACxD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IACvD,OAAO,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5F,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,oBAAoB,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAClD,WAAW,EAAE,UAAU,EAAE,CAAA;CAC1B;AAkDD,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAY3G;AA8DD,wBAAgB,mBAAmB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAkGnF;AAuED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzF"}
|