@motivation-labs/crosscheck 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/crosscheck.config.example.yml +75 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +75 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/review.d.ts +2 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +69 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/serve.d.ts +2 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +92 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +57 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +177 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config/loader.d.ts +5 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +34 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +267 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +47 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/github/client.d.ts +16 -0
- package/dist/github/client.d.ts.map +1 -0
- package/dist/github/client.js +89 -0
- package/dist/github/client.js.map +1 -0
- package/dist/github/detector.d.ts +6 -0
- package/dist/github/detector.d.ts.map +1 -0
- package/dist/github/detector.js +35 -0
- package/dist/github/detector.js.map +1 -0
- package/dist/github/webhook.d.ts +27 -0
- package/dist/github/webhook.d.ts.map +1 -0
- package/dist/github/webhook.js +48 -0
- package/dist/github/webhook.js.map +1 -0
- package/dist/reviewers/claude.d.ts +7 -0
- package/dist/reviewers/claude.d.ts.map +1 -0
- package/dist/reviewers/claude.js +66 -0
- package/dist/reviewers/claude.js.map +1 -0
- package/dist/reviewers/codex.d.ts +7 -0
- package/dist/reviewers/codex.d.ts.map +1 -0
- package/dist/reviewers/codex.js +59 -0
- package/dist/reviewers/codex.js.map +1 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Motivation Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# crosscheck
|
|
2
|
+
|
|
3
|
+
Cross-vendor AI code review orchestrator. When Claude Code opens a PR, Codex reviews it. When Codex opens a PR, Claude reviews it. Runs locally using your existing subscriptions — no separate API billing required.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @motivation-labs/crosscheck # stable
|
|
7
|
+
npm install -g @motivation-labs/crosscheck@beta # latest features
|
|
8
|
+
crosscheck init
|
|
9
|
+
crosscheck review https://github.com/owner/repo/pull/123 --reviewer codex
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
**[→ Full documentation: get-started.md](./get-started.md)**
|
|
15
|
+
|
|
16
|
+
Covers prerequisites, install, all commands and flags, full config reference, and how it works under the hood.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Quick start
|
|
21
|
+
|
|
22
|
+
**1. Install CLIs and authenticate**
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# crosscheck
|
|
26
|
+
npm install -g @motivation-labs/crosscheck
|
|
27
|
+
|
|
28
|
+
# Claude Code — uses your claude.ai Pro/Max subscription
|
|
29
|
+
npm install -g @anthropic-ai/claude-code && claude
|
|
30
|
+
|
|
31
|
+
# Codex — uses your ChatGPT Plus/Pro subscription
|
|
32
|
+
npm install -g @openai/codex && codex login --device-auth
|
|
33
|
+
|
|
34
|
+
# GitHub CLI
|
|
35
|
+
brew install gh && gh auth login
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**2. Set env vars**
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
export GITHUB_TOKEN=ghp_... # needs repo + pull-requests:write scope
|
|
42
|
+
export CROSSCHECK_WEBHOOK_SECRET=secret # any random string
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**3. Init and test**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
crosscheck init
|
|
49
|
+
crosscheck review https://github.com/owner/repo/pull/123 --reviewer codex
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**4. Run continuously**
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Local dev — auto-creates smee.io tunnel, auto-registers webhook
|
|
56
|
+
crosscheck watch
|
|
57
|
+
|
|
58
|
+
# Always-on machine — listens on fixed port, you register webhook once
|
|
59
|
+
crosscheck serve
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Goals
|
|
65
|
+
|
|
66
|
+
- **Use your existing subscriptions** — no per-token API billing; runs `claude` and `codex` CLIs locally
|
|
67
|
+
- **Zero infrastructure** — a single command on any machine with both CLIs installed
|
|
68
|
+
- **Config-as-code** — one flat YAML file, readable and writable by coding agents
|
|
69
|
+
- **Two deployment modes** — `watch` for laptops, `serve` for always-on machines
|
|
70
|
+
|
|
71
|
+
## Non-goals
|
|
72
|
+
|
|
73
|
+
- Not a replacement for human code review
|
|
74
|
+
- Not a merge gate — posts comments, does not block PRs
|
|
75
|
+
- Not a hosted service — runs on your machine, against your CLIs
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
| | Install |
|
|
82
|
+
|---|---|
|
|
83
|
+
| Node.js 18+ | [nodejs.org](https://nodejs.org) |
|
|
84
|
+
| Claude Code CLI | `npm install -g @anthropic-ai/claude-code` |
|
|
85
|
+
| Codex CLI | `npm install -g @openai/codex` |
|
|
86
|
+
| GitHub CLI | `brew install gh` |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# crosscheck.config.yml
|
|
2
|
+
# This file is designed to be easily read and modified by coding agents.
|
|
3
|
+
# All fields are optional — crosscheck uses sensible defaults.
|
|
4
|
+
|
|
5
|
+
# ─── Mode ────────────────────────────────────────────────────────────────────
|
|
6
|
+
# single-vendor: one AI reviews all PRs
|
|
7
|
+
# cross-vendor: each AI reviews the other's PRs (Claude ↔ Codex)
|
|
8
|
+
mode: cross-vendor
|
|
9
|
+
|
|
10
|
+
# ─── Vendors ─────────────────────────────────────────────────────────────────
|
|
11
|
+
vendors:
|
|
12
|
+
codex:
|
|
13
|
+
enabled: true
|
|
14
|
+
model: o4-mini # o4-mini | o3 | gpt-4o (cost/quality tradeoff)
|
|
15
|
+
auth: subscription # subscription (device-auth OAuth) | api-key
|
|
16
|
+
|
|
17
|
+
claude:
|
|
18
|
+
enabled: true
|
|
19
|
+
model: sonnet # haiku | sonnet | opus
|
|
20
|
+
effort: medium # low | medium | high | max
|
|
21
|
+
|
|
22
|
+
# ─── Quality & Focus ─────────────────────────────────────────────────────────
|
|
23
|
+
quality:
|
|
24
|
+
tier: balanced # fast | balanced | thorough
|
|
25
|
+
# fast → quick scan, top issues only (~10s, lowest cost)
|
|
26
|
+
# balanced → full review, all issues with explanations (~30s)
|
|
27
|
+
# thorough → deep multi-pass, security + architecture (~60s+)
|
|
28
|
+
|
|
29
|
+
focus: # optional — narrows review scope
|
|
30
|
+
- security
|
|
31
|
+
- types
|
|
32
|
+
- performance
|
|
33
|
+
# - naming
|
|
34
|
+
# - test-coverage
|
|
35
|
+
# - documentation
|
|
36
|
+
|
|
37
|
+
custom_prompt: | # optional — appended to every review
|
|
38
|
+
Focus on correctness and security. Be concise.
|
|
39
|
+
|
|
40
|
+
# ─── Budget ──────────────────────────────────────────────────────────────────
|
|
41
|
+
budget:
|
|
42
|
+
codex_monthly_usd: 20 # null = unlimited (use if on subscription)
|
|
43
|
+
per_review_usd: 2.00 # hard stop per single review
|
|
44
|
+
|
|
45
|
+
# ── Orgs (watch/serve mode) ───────────────────────────────────────────────────
|
|
46
|
+
# Monitor entire orgs — all repos get covered with one webhook per org.
|
|
47
|
+
# Takes priority over `repos` when both are set.
|
|
48
|
+
orgs:
|
|
49
|
+
- motivation-labs
|
|
50
|
+
- codatta
|
|
51
|
+
|
|
52
|
+
# ─── Repos ───────────────────────────────────────────────────────────────────
|
|
53
|
+
# Omit entirely to auto-detect from `git remote` (useful in watch mode)
|
|
54
|
+
# repos: still works for monitoring specific repos only
|
|
55
|
+
# repos:
|
|
56
|
+
# - owner: acme
|
|
57
|
+
# name: specific-repo
|
|
58
|
+
repos:
|
|
59
|
+
- owner: codatta
|
|
60
|
+
name: my-repo
|
|
61
|
+
|
|
62
|
+
# ─── Routing (cross-vendor mode only) ────────────────────────────────────────
|
|
63
|
+
# Which AI reviews PRs from which author patterns
|
|
64
|
+
routing:
|
|
65
|
+
codex_reviews_patterns:
|
|
66
|
+
- "Generated with \\[Claude Code\\]" # Claude Code attribution footer
|
|
67
|
+
claude_reviews_patterns:
|
|
68
|
+
- "Generated with \\[OpenAI Codex\\]" # Codex attribution footer
|
|
69
|
+
- "Co-Authored-By: codex"
|
|
70
|
+
|
|
71
|
+
# ─── Server (serve mode only) ────────────────────────────────────────────────
|
|
72
|
+
server:
|
|
73
|
+
port: 7891
|
|
74
|
+
webhook_path: /webhook
|
|
75
|
+
# pid_file: ~/.crosscheck/crosscheck.pid # for daemon management
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { runInit } from './commands/init.js';
|
|
4
|
+
import { runServe } from './commands/serve.js';
|
|
5
|
+
import { runWatch } from './commands/watch.js';
|
|
6
|
+
import { runReview } from './commands/review.js';
|
|
7
|
+
import { runStatus } from './commands/status.js';
|
|
8
|
+
const program = new Command();
|
|
9
|
+
program
|
|
10
|
+
.name('crosscheck')
|
|
11
|
+
.description('Cross-vendor AI code review — Claude Code ↔ Codex')
|
|
12
|
+
.version('0.1.0');
|
|
13
|
+
program
|
|
14
|
+
.command('init')
|
|
15
|
+
.description('Check environment, verify CLI auth, write starter config')
|
|
16
|
+
.option('-c, --config <path>', 'path to write config file')
|
|
17
|
+
.action((opts) => runInit(opts.config));
|
|
18
|
+
program
|
|
19
|
+
.command('serve')
|
|
20
|
+
.description('[BETA] Always-on webhook server (mac-mini / home server mode)')
|
|
21
|
+
.option('-c, --config <path>', 'config file path')
|
|
22
|
+
.action((opts) => runServe(opts.config));
|
|
23
|
+
program
|
|
24
|
+
.command('watch')
|
|
25
|
+
.description('Local dev mode — listen for PRs on the current repo')
|
|
26
|
+
.option('-c, --config <path>', 'config file path')
|
|
27
|
+
.action((opts) => void runWatch(opts.config));
|
|
28
|
+
program
|
|
29
|
+
.command('review <pr-url>')
|
|
30
|
+
.description('Manually trigger a review for a single PR URL')
|
|
31
|
+
.option('-c, --config <path>', 'config file path')
|
|
32
|
+
.option('-r, --reviewer <vendor>', 'force a specific reviewer: codex | claude (bypasses auto-detection)')
|
|
33
|
+
.action((prUrl, opts) => void runReview(prUrl, opts.config, opts.reviewer));
|
|
34
|
+
program
|
|
35
|
+
.command('status')
|
|
36
|
+
.description('Show auth state, config summary, and CLI versions')
|
|
37
|
+
.option('-c, --config <path>', 'config file path')
|
|
38
|
+
.action((opts) => void runStatus(opts.config));
|
|
39
|
+
program.parse();
|
|
40
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +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,qDAAqD,CAAC;KAClE,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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AA8DA,wBAAsB,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,iBAwBhD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import { existsSync, writeFileSync, readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { checkCodexAuth } from '../reviewers/codex.js';
|
|
6
|
+
import { checkClaudeAuth } from '../reviewers/claude.js';
|
|
7
|
+
async function runChecks() {
|
|
8
|
+
const results = [];
|
|
9
|
+
// Check codex CLI
|
|
10
|
+
try {
|
|
11
|
+
const version = execSync('codex --version 2>&1', { encoding: 'utf8' }).trim();
|
|
12
|
+
const auth = await checkCodexAuth();
|
|
13
|
+
results.push({ label: 'codex CLI', ok: auth.ok, detail: `${version} — ${auth.detail}`, fix: auth.ok ? undefined : 'Run: codex login --device-auth' });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
results.push({ label: 'codex CLI', ok: false, detail: 'not found', fix: 'Install: npm install -g @openai/codex' });
|
|
17
|
+
}
|
|
18
|
+
// Check claude CLI
|
|
19
|
+
try {
|
|
20
|
+
const auth = await checkClaudeAuth();
|
|
21
|
+
results.push({ label: 'claude CLI', ok: auth.ok, detail: auth.detail, fix: auth.ok ? undefined : 'Run: claude auth login' });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
results.push({ label: 'claude CLI', ok: false, detail: 'not found', fix: 'Install: npm install -g @anthropic-ai/claude-code' });
|
|
25
|
+
}
|
|
26
|
+
// Check gh CLI
|
|
27
|
+
try {
|
|
28
|
+
const version = execSync('gh --version 2>&1', { encoding: 'utf8' }).split('\n')[0];
|
|
29
|
+
const auth = execSync('gh auth status 2>&1', { encoding: 'utf8' });
|
|
30
|
+
results.push({ label: 'gh CLI', ok: auth.includes('Logged in'), detail: version });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
results.push({ label: 'gh CLI', ok: false, detail: 'not found or not authed', fix: 'Install: brew install gh && gh auth login' });
|
|
34
|
+
}
|
|
35
|
+
// Check GITHUB_TOKEN
|
|
36
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
37
|
+
results.push({ label: 'GITHUB_TOKEN', ok: !!token, detail: token ? 'set' : 'missing', fix: 'Set GITHUB_TOKEN in your shell profile' });
|
|
38
|
+
// Check CROSSCHECK_WEBHOOK_SECRET
|
|
39
|
+
const secret = process.env.CROSSCHECK_WEBHOOK_SECRET ?? process.env.GITHUB_WEBHOOK_SECRET;
|
|
40
|
+
results.push({ label: 'WEBHOOK_SECRET', ok: !!secret, detail: secret ? 'set' : 'missing (only needed for serve/watch)', fix: 'Set CROSSCHECK_WEBHOOK_SECRET' });
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
function printCheck({ label, ok, detail, fix }) {
|
|
44
|
+
const icon = ok ? chalk.green('✓') : chalk.red('✗');
|
|
45
|
+
const labelStr = chalk.bold(label.padEnd(20));
|
|
46
|
+
console.log(` ${icon} ${labelStr} ${detail}`);
|
|
47
|
+
if (!ok && fix)
|
|
48
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow(fix)}`);
|
|
49
|
+
}
|
|
50
|
+
export async function runInit(configPath) {
|
|
51
|
+
console.log(chalk.bold('\ncrosscheck — environment check\n'));
|
|
52
|
+
const checks = await runChecks();
|
|
53
|
+
for (const check of checks)
|
|
54
|
+
printCheck(check);
|
|
55
|
+
const failures = checks.filter(c => !c.ok && c.fix);
|
|
56
|
+
if (failures.length > 0) {
|
|
57
|
+
console.log(chalk.yellow(`\n${failures.length} issue(s) need attention before crosscheck can run fully.\n`));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log(chalk.green('\nAll checks passed.\n'));
|
|
61
|
+
}
|
|
62
|
+
// Write example config if none exists
|
|
63
|
+
const dest = configPath ?? join(process.cwd(), 'crosscheck.config.yml');
|
|
64
|
+
if (!existsSync(dest)) {
|
|
65
|
+
const examplePath = new URL('../../crosscheck.config.example.yml', import.meta.url).pathname;
|
|
66
|
+
if (existsSync(examplePath)) {
|
|
67
|
+
writeFileSync(dest, readFileSync(examplePath));
|
|
68
|
+
console.log(chalk.dim(`Config written to ${dest} — edit to customize.\n`));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log(chalk.dim(`Config already exists at ${dest}\n`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"AAmBA,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,iBAyEzF"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
2
|
+
import { tmpdir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import ora from 'ora';
|
|
7
|
+
import { createGithubClient, postReviewComment } from '../github/client.js';
|
|
8
|
+
import { detectPROrigin, assignReviewer } from '../github/detector.js';
|
|
9
|
+
import { runCodexReview } from '../reviewers/codex.js';
|
|
10
|
+
import { runClaudeReview } from '../reviewers/claude.js';
|
|
11
|
+
import { loadConfig, getGithubToken } from '../config/loader.js';
|
|
12
|
+
function parsePRUrl(url) {
|
|
13
|
+
const m = url.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
14
|
+
if (!m)
|
|
15
|
+
return null;
|
|
16
|
+
return { owner: m[1], repo: m[2], number: parseInt(m[3], 10) };
|
|
17
|
+
}
|
|
18
|
+
export async function runReview(prUrl, configPath, forceReviewer) {
|
|
19
|
+
const config = loadConfig(configPath);
|
|
20
|
+
const token = getGithubToken();
|
|
21
|
+
const octokit = createGithubClient(token);
|
|
22
|
+
const parsed = parsePRUrl(prUrl);
|
|
23
|
+
if (!parsed) {
|
|
24
|
+
console.error(chalk.red('Invalid PR URL. Expected: https://github.com/owner/repo/pull/123'));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
const { owner, repo, number } = parsed;
|
|
28
|
+
const spinner = ora(`Fetching PR #${number}...`).start();
|
|
29
|
+
const { data: pr } = await octokit.rest.pulls.get({ owner, repo, pull_number: number });
|
|
30
|
+
spinner.succeed(`PR #${number}: ${pr.title}`);
|
|
31
|
+
let reviewer;
|
|
32
|
+
if (forceReviewer === 'codex' || forceReviewer === 'claude') {
|
|
33
|
+
reviewer = forceReviewer;
|
|
34
|
+
console.log(chalk.dim(` reviewer: ${reviewer} (forced)`));
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const origin = detectPROrigin(pr.body ?? '', config);
|
|
38
|
+
reviewer = assignReviewer(origin, config);
|
|
39
|
+
if (!reviewer) {
|
|
40
|
+
console.log(chalk.dim(` PR origin: ${origin} — no reviewer assigned (use --reviewer codex|claude to force)`));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
console.log(chalk.dim(` PR origin: ${origin} → assigned reviewer: ${reviewer}`));
|
|
44
|
+
}
|
|
45
|
+
// Clone the repo into a temp dir
|
|
46
|
+
const tmpDir = mkdtempSync(join(tmpdir(), 'crosscheck-repo-'));
|
|
47
|
+
const spinner2 = ora('Cloning repo for review...').start();
|
|
48
|
+
try {
|
|
49
|
+
execSync(`gh repo clone ${owner}/${repo} ${tmpDir} -- --depth=50 --quiet`, { stdio: 'pipe' });
|
|
50
|
+
execSync(`git fetch origin pull/${number}/head:pr-${number}`, { cwd: tmpDir, stdio: 'pipe' });
|
|
51
|
+
execSync(`git checkout pr-${number}`, { cwd: tmpDir, stdio: 'pipe' });
|
|
52
|
+
spinner2.succeed('Repo ready');
|
|
53
|
+
let reviewText;
|
|
54
|
+
const reviewSpinner = ora(`Running ${reviewer} review...`).start();
|
|
55
|
+
if (reviewer === 'codex') {
|
|
56
|
+
reviewText = await runCodexReview(tmpDir, pr.base.ref, pr.title, config.quality, config.vendors.codex.model, config.vendors.codex.auth, msg => reviewSpinner.text = msg);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
reviewText = await runClaudeReview(tmpDir, pr.base.ref, pr.title, config.quality, config.vendors.claude, config.budget.per_review_usd, msg => reviewSpinner.text = msg);
|
|
60
|
+
}
|
|
61
|
+
reviewSpinner.succeed('Review complete');
|
|
62
|
+
await postReviewComment(octokit, owner, repo, number, reviewText, reviewer);
|
|
63
|
+
console.log(chalk.green(`\n✓ Review posted to ${prUrl}\n`));
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
rmSync(tmpDir, { force: true, recursive: true });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/commands/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAoEA,wBAAgB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,QAsC3C"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
2
|
+
import { tmpdir, hostname } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { createWebhookServer } from '../github/webhook.js';
|
|
7
|
+
import { createGithubClient, postReviewComment } from '../github/client.js';
|
|
8
|
+
import { detectPROrigin, assignReviewer } from '../github/detector.js';
|
|
9
|
+
import { runCodexReview } from '../reviewers/codex.js';
|
|
10
|
+
import { runClaudeReview } from '../reviewers/claude.js';
|
|
11
|
+
import { loadConfig, getGithubToken, getWebhookSecret } from '../config/loader.js';
|
|
12
|
+
// Deduplication — keyed by owner/repo#pr@sha
|
|
13
|
+
const inFlight = new Set();
|
|
14
|
+
async function handlePR(event, config, token, log) {
|
|
15
|
+
const { pull_request: pr, repository: repo } = event;
|
|
16
|
+
const owner = repo.owner.login;
|
|
17
|
+
const repoName = repo.name;
|
|
18
|
+
const prNumber = event.number;
|
|
19
|
+
const key = `${owner}/${repoName}#${prNumber}@${pr.head.sha}`;
|
|
20
|
+
if (inFlight.has(key)) {
|
|
21
|
+
log(`PR #${prNumber} already in review — skipping duplicate event`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
inFlight.add(key);
|
|
25
|
+
log(`PR #${prNumber} ${event.action}: ${pr.title}`);
|
|
26
|
+
const origin = detectPROrigin(pr.body ?? '', config);
|
|
27
|
+
const reviewer = assignReviewer(origin, config);
|
|
28
|
+
if (!reviewer) {
|
|
29
|
+
log(` → origin=${origin}, no reviewer — skipping`);
|
|
30
|
+
inFlight.delete(key);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
log(` → origin=${origin}, reviewer=${reviewer}`);
|
|
34
|
+
const tmpDir = mkdtempSync(join(tmpdir(), 'crosscheck-repo-'));
|
|
35
|
+
try {
|
|
36
|
+
log(' → cloning...');
|
|
37
|
+
execSync(`gh repo clone ${owner}/${repoName} ${tmpDir} -- --depth=50 --quiet`, { stdio: 'pipe' });
|
|
38
|
+
execSync(`git fetch origin pull/${prNumber}/head:pr-${prNumber}`, { cwd: tmpDir, stdio: 'pipe' });
|
|
39
|
+
execSync(`git checkout pr-${prNumber}`, { cwd: tmpDir, stdio: 'pipe' });
|
|
40
|
+
log(' → running review...');
|
|
41
|
+
let reviewText;
|
|
42
|
+
if (reviewer === 'codex') {
|
|
43
|
+
reviewText = await runCodexReview(tmpDir, pr.base.ref, pr.title, config.quality, config.vendors.codex.model, config.vendors.codex.auth, log);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
reviewText = await runClaudeReview(tmpDir, pr.base.ref, pr.title, config.quality, config.vendors.claude, config.budget.per_review_usd, log);
|
|
47
|
+
}
|
|
48
|
+
const octokit = createGithubClient(token);
|
|
49
|
+
await postReviewComment(octokit, owner, repoName, prNumber, reviewText, reviewer);
|
|
50
|
+
log(` ✓ review posted to PR #${prNumber}`);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const error = err;
|
|
54
|
+
log(` ✗ review failed: ${error.message ?? 'unknown error'}`);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
rmSync(tmpDir, { force: true, recursive: true });
|
|
58
|
+
inFlight.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function runServe(configPath) {
|
|
62
|
+
const config = loadConfig(configPath);
|
|
63
|
+
const token = getGithubToken();
|
|
64
|
+
const webhookSecret = getWebhookSecret();
|
|
65
|
+
const log = (msg) => console.log(`[${new Date().toISOString()}] ${msg}`);
|
|
66
|
+
const server = createWebhookServer(config, webhookSecret, (event) => { void handlePR(event, config, token, log); }, log);
|
|
67
|
+
server.listen(config.server.port, () => {
|
|
68
|
+
const webhookUrl = `http://${hostname()}:${config.server.port}${config.server.webhook_path}`;
|
|
69
|
+
console.log(chalk.bold('\ncrosscheck serving\n'));
|
|
70
|
+
console.log(chalk.yellow(' ⚠ serve is in beta — report issues at github.com/Motivation-Labs/crosscheck/issues\n'));
|
|
71
|
+
console.log(` mode ${chalk.cyan(config.mode)}`);
|
|
72
|
+
console.log(` quality ${chalk.cyan(config.quality.tier)}`);
|
|
73
|
+
console.log(` port ${chalk.cyan(String(config.server.port))}`);
|
|
74
|
+
console.log(` endpoint ${chalk.cyan(webhookUrl)}`);
|
|
75
|
+
console.log();
|
|
76
|
+
if (config.orgs.length > 0) {
|
|
77
|
+
console.log(chalk.dim('Register the endpoint above as a GitHub org webhook (content-type: application/json).'));
|
|
78
|
+
for (const org of config.orgs) {
|
|
79
|
+
console.log(chalk.dim(` → https://github.com/organizations/${org}/settings/hooks`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log(chalk.dim('Register this URL as a GitHub webhook (content-type: application/json).'));
|
|
84
|
+
}
|
|
85
|
+
console.log(chalk.dim('Listening for pull_request events...\n'));
|
|
86
|
+
});
|
|
87
|
+
process.on('SIGINT', () => {
|
|
88
|
+
console.log('\nShutting down...');
|
|
89
|
+
server.close(() => process.exit(0));
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAWA,wBAAsB,SAAS,CAAC,UAAU,CAAC,EAAE,MAAM,iBAqDlD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { loadConfig } from '../config/loader.js';
|
|
4
|
+
import { checkCodexAuth } from '../reviewers/codex.js';
|
|
5
|
+
import { checkClaudeAuth } from '../reviewers/claude.js';
|
|
6
|
+
function row(label, value, ok) {
|
|
7
|
+
const indicator = ok === undefined ? ' ' : ok ? chalk.green('✓') : chalk.red('✗');
|
|
8
|
+
console.log(` ${indicator} ${chalk.bold(label.padEnd(22))} ${value}`);
|
|
9
|
+
}
|
|
10
|
+
export async function runStatus(configPath) {
|
|
11
|
+
const config = loadConfig(configPath);
|
|
12
|
+
console.log(chalk.bold('\ncrosscheck status\n'));
|
|
13
|
+
// Auth
|
|
14
|
+
console.log(chalk.dim(' Auth'));
|
|
15
|
+
const [codexAuth, claudeAuth] = await Promise.all([checkCodexAuth(), checkClaudeAuth()]);
|
|
16
|
+
row('codex', codexAuth.detail || 'authenticated', codexAuth.ok);
|
|
17
|
+
row('claude', claudeAuth.detail || 'authenticated', claudeAuth.ok);
|
|
18
|
+
const ghToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
19
|
+
row('GITHUB_TOKEN', ghToken ? 'set' : 'missing', !!ghToken);
|
|
20
|
+
const webhookSecret = process.env.CROSSCHECK_WEBHOOK_SECRET ?? process.env.GITHUB_WEBHOOK_SECRET;
|
|
21
|
+
row('WEBHOOK_SECRET', webhookSecret ? 'set' : 'missing (needed for serve/watch)', !!webhookSecret);
|
|
22
|
+
// Config
|
|
23
|
+
console.log();
|
|
24
|
+
console.log(chalk.dim(' Config'));
|
|
25
|
+
row('mode', config.mode);
|
|
26
|
+
row('quality tier', config.quality.tier);
|
|
27
|
+
row('codex auth', config.vendors.codex.auth);
|
|
28
|
+
row('claude model', config.vendors.claude.model ?? 'default');
|
|
29
|
+
row('per-review budget', config.vendors.codex.auth === 'subscription'
|
|
30
|
+
? 'subscription (unlimited)'
|
|
31
|
+
: `$${config.budget.per_review_usd.toFixed(2)}`);
|
|
32
|
+
if (config.repos.length > 0) {
|
|
33
|
+
row('repos', config.repos.map(r => `${r.owner}/${r.name}`).join(', '));
|
|
34
|
+
}
|
|
35
|
+
if (config.quality.focus.length > 0) {
|
|
36
|
+
row('focus', config.quality.focus.join(', '));
|
|
37
|
+
}
|
|
38
|
+
// CLI versions
|
|
39
|
+
console.log();
|
|
40
|
+
console.log(chalk.dim(' CLIs'));
|
|
41
|
+
try {
|
|
42
|
+
const codexVer = execSync('codex --version 2>&1', { encoding: 'utf8' }).trim();
|
|
43
|
+
row('codex', codexVer);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
row('codex', 'not found', false);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const claudeVer = execSync('claude --version 2>&1', { encoding: 'utf8' }).trim();
|
|
50
|
+
row('claude', claudeVer);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
row('claude', 'not found', false);
|
|
54
|
+
}
|
|
55
|
+
console.log();
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAoCA,wBAAsB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,iBAmKjD"}
|