@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.
Files changed (47) hide show
  1. package/AGENT.md +207 -0
  2. package/dist/__tests__/diagnose.test.d.ts +2 -0
  3. package/dist/__tests__/diagnose.test.d.ts.map +1 -0
  4. package/dist/__tests__/diagnose.test.js +164 -0
  5. package/dist/__tests__/diagnose.test.js.map +1 -0
  6. package/dist/__tests__/optimize.test.d.ts +2 -0
  7. package/dist/__tests__/optimize.test.d.ts.map +1 -0
  8. package/dist/__tests__/optimize.test.js +88 -0
  9. package/dist/__tests__/optimize.test.js.map +1 -0
  10. package/dist/cli.js +17 -0
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/diagnose.d.ts +53 -0
  13. package/dist/commands/diagnose.d.ts.map +1 -0
  14. package/dist/commands/diagnose.js +280 -0
  15. package/dist/commands/diagnose.js.map +1 -0
  16. package/dist/commands/optimize.d.ts +16 -0
  17. package/dist/commands/optimize.d.ts.map +1 -0
  18. package/dist/commands/optimize.js +209 -0
  19. package/dist/commands/optimize.js.map +1 -0
  20. package/dist/commands/review.d.ts.map +1 -1
  21. package/dist/commands/review.js +3 -1
  22. package/dist/commands/review.js.map +1 -1
  23. package/dist/commands/status.d.ts.map +1 -1
  24. package/dist/commands/status.js +6 -20
  25. package/dist/commands/status.js.map +1 -1
  26. package/dist/commands/watch.d.ts.map +1 -1
  27. package/dist/commands/watch.js +105 -73
  28. package/dist/commands/watch.js.map +1 -1
  29. package/dist/config/loader.d.ts +4 -0
  30. package/dist/config/loader.d.ts.map +1 -1
  31. package/dist/config/loader.js +22 -10
  32. package/dist/config/loader.js.map +1 -1
  33. package/dist/lib/instructions.d.ts +5 -0
  34. package/dist/lib/instructions.d.ts.map +1 -0
  35. package/dist/lib/instructions.js +61 -0
  36. package/dist/lib/instructions.js.map +1 -0
  37. package/dist/lib/languages.d.ts +3 -0
  38. package/dist/lib/languages.d.ts.map +1 -0
  39. package/dist/lib/languages.js +26 -0
  40. package/dist/lib/languages.js.map +1 -0
  41. package/dist/reviewers/claude.d.ts.map +1 -1
  42. package/dist/reviewers/claude.js +4 -5
  43. package/dist/reviewers/claude.js.map +1 -1
  44. package/dist/reviewers/codex.d.ts.map +1 -1
  45. package/dist/reviewers/codex.js +4 -11
  46. package/dist/reviewers/codex.js.map +1 -1
  47. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=diagnose.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=optimize.test.d.ts.map
@@ -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;AAEhD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,0DAA0D,CAAC;KACvE,MAAM,CAAC,qBAAqB,EAAE,2BAA2B,CAAC;KAC1D,MAAM,CAAC,CAAC,IAAyB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AAE9D,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,+DAA+D,CAAC;KAC5E,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,CAAC,IAAyB,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AAE/D,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wDAAwD,CAAC;KACrE,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,CAAC,IAAyB,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AAEpE,OAAO;KACJ,OAAO,CAAC,iBAAiB,CAAC;KAC1B,WAAW,CAAC,+CAA+C,CAAC;KAC5D,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,yBAAyB,EAAE,qEAAqE,CAAC;KACxG,MAAM,CAAC,CAAC,KAAa,EAAE,IAA4C,EAAE,EAAE,CAAC,KAAK,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;AAE7H,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,mDAAmD,CAAC;KAChE,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,CAAC,IAAyB,EAAE,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AAErE,OAAO,CAAC,KAAK,EAAE,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"}