@motivation-labs/crosscheck 0.7.0 → 0.7.1-beta.577031d.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/dist/cli.js +17 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/onboard.d.ts +3 -2
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +209 -257
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/run.d.ts +8 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +141 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/serve.d.ts +1 -0
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +4 -1
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/watch.d.ts +1 -0
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +4 -1
- package/dist/commands/watch.js.map +1 -1
- package/dist/lib/repo-picker.d.ts +5 -3
- package/dist/lib/repo-picker.d.ts.map +1 -1
- package/dist/lib/repo-picker.js +140 -122
- package/dist/lib/repo-picker.js.map +1 -1
- package/dist/lib/runner.d.ts +2 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +19 -7
- package/dist/lib/runner.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { runDiagnose } from './commands/diagnose.js';
|
|
|
13
13
|
import { runOptimize } from './commands/optimize.js';
|
|
14
14
|
import { runImpact } from './commands/impact.js';
|
|
15
15
|
import { runIssue } from './commands/issue.js';
|
|
16
|
+
import { runRun } from './commands/run.js';
|
|
16
17
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
18
|
const { version } = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'));
|
|
18
19
|
const invokedAs = basename(process.argv[1] ?? 'crosscheck').replace(/\.js$/, '');
|
|
@@ -29,11 +30,12 @@ program
|
|
|
29
30
|
.action((opts) => runInit(opts.config));
|
|
30
31
|
program
|
|
31
32
|
.command('onboard')
|
|
32
|
-
.description('
|
|
33
|
-
.option('-c, --config <path>', 'config file path')
|
|
34
|
-
.option('--
|
|
35
|
-
.option('--
|
|
36
|
-
.option('--
|
|
33
|
+
.description('Guided setup — select repos to monitor and write config')
|
|
34
|
+
.option('-c, --config <path>', 'config file path to write')
|
|
35
|
+
.option('-y, --yes', 'skip confirmation prompts, accept defaults')
|
|
36
|
+
.option('--personal', 'pre-select personal deployment mode, skip persona prompt')
|
|
37
|
+
.option('--team', 'pre-select team deployment mode, skip persona prompt')
|
|
38
|
+
.option('--reconfigure', 're-run setup (accepted for compatibility; onboard always reconfigures)')
|
|
37
39
|
.action((opts) => void runOnboard(opts));
|
|
38
40
|
program
|
|
39
41
|
.command('serve')
|
|
@@ -42,6 +44,7 @@ program
|
|
|
42
44
|
.option('--personal', 'personal mode this session only (does not save to config)')
|
|
43
45
|
.option('--team', 'team mode this session only (does not save to config)')
|
|
44
46
|
.option('--reconfigure', 're-run deployment setup and save new choice to config')
|
|
47
|
+
.option('--no-backtrace', 'skip the startup scan for unreviewed open PRs this session')
|
|
45
48
|
.action((opts) => void runServe(opts));
|
|
46
49
|
program
|
|
47
50
|
.command('watch')
|
|
@@ -50,6 +53,7 @@ program
|
|
|
50
53
|
.option('--personal', 'personal mode this session only (does not save to config)')
|
|
51
54
|
.option('--team', 'team mode this session only (does not save to config)')
|
|
52
55
|
.option('--reconfigure', 're-run deployment setup and save new choice to config')
|
|
56
|
+
.option('--no-backtrace', 'skip the startup scan for unreviewed open PRs this session')
|
|
53
57
|
.action((opts) => void runWatch(opts));
|
|
54
58
|
program
|
|
55
59
|
.command('review <pr-url>')
|
|
@@ -57,6 +61,14 @@ program
|
|
|
57
61
|
.option('-c, --config <path>', 'config file path')
|
|
58
62
|
.option('-r, --reviewer <vendor>', 'force a specific reviewer: codex | claude (bypasses auto-detection)')
|
|
59
63
|
.action((prUrl, opts) => void runReview(prUrl, opts.config, opts.reviewer));
|
|
64
|
+
program
|
|
65
|
+
.command('run <pr-url>')
|
|
66
|
+
.description('Execute the full configured workflow against a single PR (review → fix → recheck)')
|
|
67
|
+
.option('-c, --config <path>', 'config file path')
|
|
68
|
+
.option('-r, --reviewer <vendor>', 'force a specific reviewer: codex | claude (bypasses attribution detection)')
|
|
69
|
+
.option('--steps <list>', 'run only these step types, comma-separated: review,fix,recheck')
|
|
70
|
+
.option('--dry-run', 'run the review but do not post a comment or apply fixes')
|
|
71
|
+
.action((prUrl, opts) => void runRun(prUrl, opts));
|
|
60
72
|
program
|
|
61
73
|
.command('status')
|
|
62
74
|
.description('Show auth state, config summary, and CLI versions')
|
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,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,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;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,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;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAE1C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACzD,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAAwB,CAAA;AAE/G,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;AAChF,MAAM,WAAW,GAAG,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;AAE5D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,OAAO,OAAO,EAAE,CAAC,CAAA;AAE5B,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,SAAS,CAAC;KAClB,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,qBAAqB,EAAE,2BAA2B,CAAC;KAC1D,MAAM,CAAC,WAAW,EAAE,4CAA4C,CAAC;KACjE,MAAM,CAAC,YAAY,EAAE,0DAA0D,CAAC;KAChF,MAAM,CAAC,QAAQ,EAAE,sDAAsD,CAAC;KACxE,MAAM,CAAC,eAAe,EAAE,wEAAwE,CAAC;KACjG,MAAM,CAAC,CAAC,IAAmG,EAAE,EAAE,CAAC,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;AAEzI,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,+DAA+D,CAAC;KAC5E,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,YAAY,EAAE,2DAA2D,CAAC;KACjF,MAAM,CAAC,QAAQ,EAAE,uDAAuD,CAAC;KACzE,MAAM,CAAC,eAAe,EAAE,uDAAuD,CAAC;KAChF,MAAM,CAAC,gBAAgB,EAAE,4DAA4D,CAAC;KACtF,MAAM,CAAC,CAAC,IAAyG,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;AAE7I,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,wDAAwD,CAAC;KACrE,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,YAAY,EAAE,2DAA2D,CAAC;KACjF,MAAM,CAAC,QAAQ,EAAE,uDAAuD,CAAC;KACzE,MAAM,CAAC,eAAe,EAAE,uDAAuD,CAAC;KAChF,MAAM,CAAC,gBAAgB,EAAE,4DAA4D,CAAC;KACtF,MAAM,CAAC,CAAC,IAAyG,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;AAE7I,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,cAAc,CAAC;KACvB,WAAW,CAAC,mFAAmF,CAAC;KAChG,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,yBAAyB,EAAE,4EAA4E,CAAC;KAC/G,MAAM,CAAC,gBAAgB,EAAE,gEAAgE,CAAC;KAC1F,MAAM,CAAC,WAAW,EAAE,yDAAyD,CAAC;KAC9E,MAAM,CAAC,CAAC,KAAa,EAAE,IAA8E,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;AAEtI,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;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,2FAA2F,CAAC;KACxG,MAAM,CAAC,QAAQ,EAAE,4BAA4B,CAAC;KAC9C,MAAM,CAAC,gBAAgB,EAAE,sDAAsD,CAAC;KAChF,MAAM,CAAC,CAAC,IAAwC,EAAE,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;AAE/E,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,gEAAgE,CAAC;KAC7E,MAAM,CAAC,SAAS,EAAE,kEAAkE,CAAC;KACrF,MAAM,CAAC,WAAW,EAAE,8CAA8C,CAAC;KACnE,MAAM,CAAC,kBAAkB,EAAE,wCAAwC,CAAC;KACpE,MAAM,CAAC,gBAAgB,EAAE,wCAAwC,CAAC;KAClE,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,CAAC,IAA4F,EAAE,EAAE,CAAC,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;AAEnI,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,8EAA8E,CAAC;KAC3F,MAAM,CAAC,QAAQ,EAAE,4BAA4B,CAAC;KAC9C,MAAM,CAAC,gBAAgB,EAAE,sDAAsD,CAAC;KAChF,MAAM,CAAC,SAAS,EAAE,mCAAmC,CAAC;KACtD,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,CAAC,IAA0E,EAAE,EAAE,CAAC,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC,CAAA;AAE/G,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,2FAA2F,CAAC;KACxG,MAAM,CAAC,gBAAgB,EAAE,2EAA2E,CAAC;KACrG,MAAM,CAAC,WAAW,EAAE,oCAAoC,CAAC;KACzD,MAAM,CAAC,WAAW,EAAE,6CAA6C,CAAC;KAClE,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,CAAC,IAA0E,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAA;AAE9G,OAAO,CAAC,KAAK,EAAE,CAAA"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export interface OnboardOpts {
|
|
2
|
+
config?: string;
|
|
3
|
+
yes?: boolean;
|
|
2
4
|
personal?: boolean;
|
|
3
5
|
team?: boolean;
|
|
4
6
|
reconfigure?: boolean;
|
|
5
|
-
config?: string;
|
|
6
7
|
}
|
|
7
|
-
export declare function runOnboard(opts
|
|
8
|
+
export declare function runOnboard(opts?: OnboardOpts): Promise<void>;
|
|
8
9
|
//# sourceMappingURL=onboard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"onboard.d.ts","sourceRoot":"","sources":["../../src/commands/onboard.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,OAAO,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AA6DD,wBAAsB,UAAU,CAAC,IAAI,GAAE,WAAgB,iBA6KtD"}
|
package/dist/commands/onboard.js
CHANGED
|
@@ -1,285 +1,237 @@
|
|
|
1
|
-
import { existsSync, mkdirSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
|
-
import { createInterface } from 'readline';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
import { createInterface } from 'readline';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import { getGithubToken, loadConfig, resolveConfigPath, detectGitHubLogin, promptDeploymentMode, patchDeploymentConfig, } from '../config/loader.js';
|
|
8
|
+
import { listUserRepos, listUserOrgs, listOrgRepos } from '../github/client.js';
|
|
9
|
+
import { checkCodexAuth } from '../reviewers/codex.js';
|
|
10
|
+
import { checkClaudeAuth } from '../reviewers/claude.js';
|
|
11
|
+
import { execSync } from 'child_process';
|
|
12
|
+
import { promptRepoPicker } from '../lib/repo-picker.js';
|
|
13
|
+
function ask(question) {
|
|
14
14
|
return new Promise(resolve => {
|
|
15
15
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
16
16
|
rl.question(question, answer => { rl.close(); resolve(answer.trim()); });
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
19
|
+
async function checkEnv() {
|
|
20
|
+
let aiCliCount = 0;
|
|
21
|
+
try {
|
|
22
|
+
execSync('codex --version 2>&1', { encoding: 'utf8' });
|
|
23
|
+
const auth = await checkCodexAuth();
|
|
24
|
+
if (auth.ok)
|
|
25
|
+
aiCliCount++;
|
|
26
|
+
const icon = auth.ok ? chalk.green('✓') : chalk.red('✗');
|
|
27
|
+
console.log(` ${icon} ${'codex CLI'.padEnd(20)} ${auth.detail}`);
|
|
28
|
+
if (!auth.ok)
|
|
29
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow('Run: codex login --device-auth')}`);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
console.log(` ${chalk.red('✗')} ${'codex CLI'.padEnd(20)} not found`);
|
|
33
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow('Install: npm install -g @openai/codex')}`);
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const auth = await checkClaudeAuth();
|
|
37
|
+
if (auth.ok)
|
|
38
|
+
aiCliCount++;
|
|
39
|
+
const icon = auth.ok ? chalk.green('✓') : chalk.red('✗');
|
|
40
|
+
console.log(` ${icon} ${'claude CLI'.padEnd(20)} ${auth.detail}`);
|
|
41
|
+
if (!auth.ok)
|
|
42
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow('Run: claude auth login')}`);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
console.log(` ${chalk.red('✗')} ${'claude CLI'.padEnd(20)} not found`);
|
|
46
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow('Install: npm install -g @anthropic-ai/claude-code')}`);
|
|
47
|
+
}
|
|
48
|
+
const envToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;
|
|
49
|
+
let ghAuthed = false;
|
|
50
|
+
try {
|
|
51
|
+
execSync('gh --version 2>&1', { encoding: 'utf8' });
|
|
52
|
+
let authOutput = '';
|
|
53
|
+
try {
|
|
54
|
+
authOutput = execSync('gh auth status 2>&1', { encoding: 'utf8' });
|
|
55
|
+
}
|
|
56
|
+
catch { /* GITHUB_TOKEN in use */ }
|
|
57
|
+
ghAuthed = authOutput.includes('Logged in') || !!envToken;
|
|
58
|
+
const icon = ghAuthed ? chalk.green('✓') : chalk.red('✗');
|
|
59
|
+
console.log(` ${icon} ${'gh CLI'.padEnd(20)} ${ghAuthed ? 'authenticated' : 'not authenticated'}`);
|
|
60
|
+
if (!ghAuthed)
|
|
61
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow('Run: gh auth login')}`);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
console.log(` ${chalk.red('✗')} ${'gh CLI'.padEnd(20)} not found`);
|
|
65
|
+
console.log(` ${chalk.dim('→')} ${chalk.yellow('Install: brew install gh && gh auth login')}`);
|
|
66
|
+
}
|
|
67
|
+
if (aiCliCount === 0) {
|
|
68
|
+
console.log(chalk.red('\nAt least one AI CLI (codex or claude) must be authenticated.\n'));
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (!ghAuthed) {
|
|
72
|
+
console.log(chalk.red('\nGitHub auth is required to fetch repos and register webhooks.\n'));
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
export async function runOnboard(opts = {}) {
|
|
78
|
+
if (!process.stdin.isTTY) {
|
|
79
|
+
console.error(chalk.red('onboard requires an interactive terminal.'));
|
|
80
|
+
console.error(chalk.dim('Run crosscheck init and edit crosscheck.config.yml manually.'));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
79
83
|
console.log(chalk.bold('\ncrosscheck onboard\n'));
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log();
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
// ── Step 1: Auth check ─────────────────────────────────────────────────────
|
|
85
|
+
console.log(chalk.bold('Step 1 — environment check'));
|
|
86
|
+
const ok = await checkEnv();
|
|
87
|
+
if (!ok)
|
|
88
|
+
process.exit(1);
|
|
89
|
+
console.log();
|
|
90
|
+
// ── Step 2: Deployment mode ────────────────────────────────────────────────
|
|
91
|
+
console.log(chalk.bold('Step 2 — deployment mode'));
|
|
92
|
+
const configPath = opts.config ?? resolveConfigPath() ?? join(homedir(), '.crosscheck', 'config.yml');
|
|
93
|
+
const existingConfig = existsSync(configPath) ? loadConfig(configPath) : null;
|
|
94
|
+
const currentDeployment = existingConfig?.deployment;
|
|
95
|
+
let deployment;
|
|
96
|
+
if (opts.personal) {
|
|
97
|
+
deployment = 'personal';
|
|
98
|
+
console.log(` Mode: ${chalk.cyan('personal')} (--personal flag)`);
|
|
99
|
+
}
|
|
100
|
+
else if (opts.team) {
|
|
101
|
+
deployment = 'team';
|
|
102
|
+
console.log(` Mode: ${chalk.cyan('team')} (--team flag)`);
|
|
98
103
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
for (const c of checks) {
|
|
103
|
-
const icon = c.ok ? chalk.green('✓') : chalk.red('✗');
|
|
104
|
-
const detail = c.ok ? chalk.dim(c.detail) : chalk.yellow(c.detail);
|
|
105
|
-
console.log(` ${icon} ${c.label.padEnd(22)} ${detail}`);
|
|
106
|
-
if (!c.ok && c.fix)
|
|
107
|
-
console.log(` ${chalk.dim('→')} ${chalk.dim(c.fix)}`);
|
|
104
|
+
else if (currentDeployment && !opts.yes) {
|
|
105
|
+
const keep = await ask(` Current mode: ${chalk.cyan(currentDeployment)}. Keep this? [Y/n]: `);
|
|
106
|
+
deployment = keep.toLowerCase() === 'n' ? await promptDeploymentMode() : currentDeployment;
|
|
108
107
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
108
|
+
else if (currentDeployment && opts.yes) {
|
|
109
|
+
deployment = currentDeployment;
|
|
110
|
+
console.log(` Using existing mode: ${chalk.cyan(deployment)}`);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
deployment = await promptDeploymentMode();
|
|
114
|
+
}
|
|
115
|
+
console.log();
|
|
116
|
+
// ── Step 3: Repo selection ─────────────────────────────────────────────────
|
|
117
|
+
console.log(chalk.bold('Step 3 — select repos to monitor'));
|
|
118
|
+
let token;
|
|
117
119
|
try {
|
|
118
120
|
token = getGithubToken();
|
|
119
|
-
login = detectGitHubLogin() ?? '';
|
|
120
|
-
detectedOrgs = await listUserOrgs(token);
|
|
121
121
|
}
|
|
122
|
-
catch {
|
|
123
|
-
console.
|
|
122
|
+
catch (err) {
|
|
123
|
+
console.error(chalk.red(err instanceof Error ? err.message : String(err)));
|
|
124
|
+
process.exit(1);
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
const login = detectGitHubLogin() ?? '';
|
|
127
|
+
console.log(chalk.dim(` Fetching repos for ${login || 'your account'}...`));
|
|
128
|
+
// Fetch personal repos and org repos in parallel
|
|
129
|
+
const [personalRepos, orgs] = await Promise.all([
|
|
130
|
+
login ? listUserRepos(login, token, true).catch(() => []) : Promise.resolve([]),
|
|
131
|
+
listUserOrgs(token).catch(() => []),
|
|
132
|
+
]);
|
|
133
|
+
const orgRepoLists = await Promise.all(orgs.map(org => listOrgRepos(org, token).catch(() => [])));
|
|
134
|
+
// Build flat list of "owner/repo" strings for the picker
|
|
135
|
+
const allRepos = [];
|
|
136
|
+
for (const r of personalRepos)
|
|
137
|
+
allRepos.push(`${r.owner}/${r.name}`);
|
|
138
|
+
for (let i = 0; i < orgs.length; i++) {
|
|
139
|
+
for (const r of orgRepoLists[i])
|
|
140
|
+
allRepos.push(`${r.owner}/${r.name}`);
|
|
131
141
|
}
|
|
132
|
-
//
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// Step 2: Scope
|
|
149
|
-
const orgPreview = detectedOrgs.slice(0, 2).map(o => `github.com/${o}/*`).join(', ') || 'your org repos';
|
|
150
|
-
const orgSuffix = detectedOrgs.length ? ` (detected: ${detectedOrgs.join(', ')})` : '';
|
|
151
|
-
const existingScope = existing
|
|
152
|
-
? (existing.users.length > 0 && existing.orgs.length > 0 ? 3
|
|
153
|
-
: existing.users.length > 0 ? 1
|
|
154
|
-
: existing.orgs.length > 0 ? 2
|
|
155
|
-
: existing.repos.length > 0 ? 1 // curated repos-only = personal scope
|
|
156
|
-
: 3)
|
|
157
|
-
: 3;
|
|
158
|
-
const scopeIdx = await pickOne(`What should crosscheck monitor?${orgSuffix}`, [
|
|
159
|
-
` ${chalk.bold('[1] My personal repos only')} — github.com/${login || 'you'}/* — side projects you own directly`,
|
|
160
|
-
` ${chalk.bold('[2] My org repos only')} — ${orgPreview}`,
|
|
161
|
-
` ${chalk.bold('[3] Both personal repos + orgs')} — everything across your GitHub account ← recommended`,
|
|
162
|
-
], existingScope);
|
|
163
|
-
const includePersonal = scopeIdx !== 2;
|
|
164
|
-
const includeOrgs = scopeIdx !== 1;
|
|
165
|
-
if (includeOrgs && detectedOrgs.length > 0) {
|
|
166
|
-
orgs = await promptOrgPicker(detectedOrgs, existing?.orgs.length ? existing.orgs : undefined);
|
|
167
|
-
}
|
|
168
|
-
if (includePersonal && token && login) {
|
|
169
|
-
if (scopeIdx === 1) {
|
|
170
|
-
// Curated mode (UC-01): pick specific repos
|
|
171
|
-
console.log(chalk.dim('\n Fetching your repos...'));
|
|
172
|
-
try {
|
|
173
|
-
const repoList = await fetchActiveRepos(login, token);
|
|
174
|
-
const existingNames = existing?.repos.map(r => `${r.owner}/${r.name}`) ?? [];
|
|
175
|
-
const picked = await promptRepoPicker(repoList, existingNames.length ? existingNames : undefined);
|
|
176
|
-
repos = picked.map(full => {
|
|
177
|
-
const [owner, name] = full.split('/');
|
|
178
|
-
return { owner: owner ?? login, name: name ?? full };
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
catch {
|
|
182
|
-
console.log(chalk.dim(' (Could not fetch repos — add them manually to config.repos)\n'));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// "Both" mode (UC-02): monitor all personal repos via users field
|
|
187
|
-
users = [login];
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
else if (includePersonal) {
|
|
191
|
-
users = login ? [login] : [];
|
|
142
|
+
// Pre-select repos already in config
|
|
143
|
+
const currentRepoKeys = new Set((existingConfig?.repos ?? []).map(r => `${r.owner}/${r.name}`));
|
|
144
|
+
const currentOrgs = new Set(existingConfig?.orgs ?? []);
|
|
145
|
+
// If running with --yes, keep existing selection
|
|
146
|
+
let selectedRepos;
|
|
147
|
+
let selectedOrgs;
|
|
148
|
+
if (opts.yes && existingConfig) {
|
|
149
|
+
selectedRepos = [...currentRepoKeys];
|
|
150
|
+
selectedOrgs = [...currentOrgs];
|
|
151
|
+
console.log(` Using existing repo selection (${selectedRepos.length} repos, ${selectedOrgs.length} orgs)`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
if (allRepos.length === 0) {
|
|
155
|
+
console.log(chalk.yellow(' No repos found. You can add repos manually in your config file.'));
|
|
156
|
+
selectedRepos = [];
|
|
157
|
+
selectedOrgs = [];
|
|
192
158
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
159
|
+
else {
|
|
160
|
+
// Pre-mark already-configured repos as selected via a sorted list
|
|
161
|
+
// where configured repos appear first
|
|
162
|
+
const sorted = [
|
|
163
|
+
...allRepos.filter(r => currentRepoKeys.has(r)),
|
|
164
|
+
...allRepos.filter(r => !currentRepoKeys.has(r)),
|
|
165
|
+
];
|
|
166
|
+
console.log(chalk.dim(` Found ${sorted.length} repos. Use arrows + space to select, enter to confirm.\n`));
|
|
167
|
+
const picked = await promptRepoPicker(sorted, {
|
|
168
|
+
title: 'Select repos to monitor:',
|
|
169
|
+
initialSelected: [...currentRepoKeys],
|
|
170
|
+
});
|
|
171
|
+
console.log();
|
|
172
|
+
// Check org offer: if ≥3 repos from the same real org, offer to monitor the entire org.
|
|
173
|
+
// Only count owners that appear in the fetched org list — excludes the personal login.
|
|
174
|
+
const orgSet = new Set(orgs);
|
|
175
|
+
const orgCounts = {};
|
|
176
|
+
for (const r of picked) {
|
|
177
|
+
const owner = r.split('/')[0];
|
|
178
|
+
if (orgSet.has(owner))
|
|
179
|
+
orgCounts[owner] = (orgCounts[owner] ?? 0) + 1;
|
|
203
180
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
console.log(chalk.dim(' Add routing.allowed_authors to your config to restrict later.\n'));
|
|
181
|
+
const orgOffers = Object.entries(orgCounts).filter(([, count]) => count >= 3).map(([org]) => org);
|
|
182
|
+
selectedOrgs = [...currentOrgs];
|
|
183
|
+
selectedRepos = picked;
|
|
184
|
+
for (const org of orgOffers) {
|
|
185
|
+
if (currentOrgs.has(org))
|
|
186
|
+
continue;
|
|
187
|
+
const answer = opts.yes ? 'n' : await ask(` Monitor all of ${chalk.cyan(org)} instead of individual repos? [y/N]: `);
|
|
188
|
+
if (answer.toLowerCase() === 'y') {
|
|
189
|
+
selectedOrgs.push(org);
|
|
190
|
+
selectedRepos = selectedRepos.filter(r => !r.startsWith(`${org}/`));
|
|
215
191
|
}
|
|
216
192
|
}
|
|
217
193
|
}
|
|
218
|
-
// ── Team path ─────────────────────────────────────────────────────────────
|
|
219
194
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
// Step 3: Author filter
|
|
230
|
-
const authorIdx = await pickOne('Whose PRs should be reviewed?', [
|
|
231
|
-
` ${chalk.bold('[1] All authors')} (no filter) — review every PR in the org`,
|
|
232
|
-
` ${chalk.bold('[2] Specific logins')} — restrict to listed team members`,
|
|
233
|
-
], 1);
|
|
234
|
-
if (authorIdx === 2) {
|
|
235
|
-
const raw = await ask(' Enter logins (comma-separated): ');
|
|
236
|
-
allowedAuthors = raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
237
|
-
}
|
|
238
|
-
// Step 4: Review depth
|
|
239
|
-
const depthIdx = await pickOne('How deep should the CR workflow go?', [
|
|
240
|
-
` ${chalk.bold('[1] CR only')} — post review comments; humans apply fixes`,
|
|
241
|
-
` ${chalk.bold('[2] CR + Auto-fix')} — crosscheck also proposes and commits fixes`,
|
|
242
|
-
], 1);
|
|
243
|
-
if (depthIdx === 2) {
|
|
244
|
-
autoFix = true;
|
|
245
|
-
const deliveryIdx = await pickOne('How should auto-fixes be delivered?', [
|
|
246
|
-
` ${chalk.bold('[1] Open a fix PR')} (human reviews and merges before merge) ← recommended`,
|
|
247
|
-
` ${chalk.bold('[2] Push directly')} onto the PR branch`,
|
|
248
|
-
], 1);
|
|
249
|
-
deliveryMode = deliveryIdx === 2 ? 'commit' : 'pull_request';
|
|
250
|
-
}
|
|
195
|
+
// ── Step 4: Confirm and write ──────────────────────────────────────────────
|
|
196
|
+
console.log(chalk.bold('Step 4 — review and write config'));
|
|
197
|
+
console.log();
|
|
198
|
+
console.log(` deployment ${chalk.cyan(deployment)}`);
|
|
199
|
+
if (selectedOrgs.length > 0) {
|
|
200
|
+
console.log(` orgs ${selectedOrgs.map(o => chalk.cyan(o)).join(', ')}`);
|
|
201
|
+
}
|
|
202
|
+
if (selectedRepos.length > 0) {
|
|
203
|
+
console.log(` repos ${selectedRepos.slice(0, 5).map(r => chalk.cyan(r)).join(', ')}${selectedRepos.length > 5 ? chalk.dim(` +${selectedRepos.length - 5} more`) : ''}`);
|
|
251
204
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
console.log(chalk.bold('\n Your crosscheck config:\n'));
|
|
257
|
-
console.log(` ${'persona'.padEnd(COL)}${deployment}`);
|
|
258
|
-
if (orgs.length)
|
|
259
|
-
console.log(` ${'orgs'.padEnd(COL)}${orgs.join(', ')}`);
|
|
260
|
-
if (users.length)
|
|
261
|
-
console.log(` ${'users'.padEnd(COL)}${users.join(', ')}`);
|
|
262
|
-
if (repos.length)
|
|
263
|
-
console.log(` ${'repos'.padEnd(COL)}${repos.map(r => `${r.owner}/${r.name}`).join(', ')}`);
|
|
264
|
-
console.log(` ${'filter'.padEnd(COL)}${allowedAuthors.length ? `author = ${allowedAuthors.join(', ')}` : 'all authors'}`);
|
|
265
|
-
if (autoFix)
|
|
266
|
-
console.log(` ${'auto-fix'.padEnd(COL)}${deliveryMode}`);
|
|
267
|
-
if (brand.service_name !== 'crosscheck')
|
|
268
|
-
console.log(` ${'brand'.padEnd(COL)}${brand.service_name}`);
|
|
269
|
-
console.log(` ${'config'.padEnd(COL)}${configPath}`);
|
|
205
|
+
if (selectedOrgs.length === 0 && selectedRepos.length === 0) {
|
|
206
|
+
console.log(` ${chalk.yellow('No repos or orgs selected. Config will have empty scope.')}`);
|
|
207
|
+
}
|
|
208
|
+
console.log(` config ${chalk.dim(configPath)}`);
|
|
270
209
|
console.log();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
210
|
+
if (!opts.yes) {
|
|
211
|
+
const confirm = await ask(` Write to config? [Y/n]: `);
|
|
212
|
+
if (confirm.toLowerCase() === 'n') {
|
|
213
|
+
console.log(chalk.dim(' Aborted — no changes written.'));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
275
216
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
217
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
218
|
+
// patchDeploymentConfig handles deployment, orgs, users, allowed_authors, author_routes
|
|
219
|
+
patchDeploymentConfig(configPath, deployment, login, selectedOrgs, true);
|
|
220
|
+
// Patch repos after — load the file written by patchDeploymentConfig and add repos
|
|
221
|
+
const raw = (yaml.load(readFileSync(configPath, 'utf8')) ?? {});
|
|
222
|
+
raw.repos = selectedRepos.map(r => {
|
|
223
|
+
const [owner, name] = r.split('/');
|
|
224
|
+
return { owner, name };
|
|
281
225
|
});
|
|
282
|
-
|
|
283
|
-
|
|
226
|
+
// Explicit repo/org selections take precedence over the `users` expansion in watch/serve.
|
|
227
|
+
// Clear `users` whenever the user has set any explicit scope — repos OR orgs.
|
|
228
|
+
// Without this, org-only selections still trigger the personal-user expansion in watch/serve.
|
|
229
|
+
if (selectedRepos.length > 0 || selectedOrgs.length > 0)
|
|
230
|
+
delete raw.users;
|
|
231
|
+
writeFileSync(configPath, yaml.dump(raw, { lineWidth: -1, noRefs: true }));
|
|
232
|
+
console.log(chalk.green(` ✓ config written to ${configPath}`));
|
|
233
|
+
console.log();
|
|
234
|
+
// ── Step 5: Next step hint ────────────────────────────────────────────────
|
|
235
|
+
console.log(chalk.dim(' Run crosscheck watch to start monitoring.\n'));
|
|
284
236
|
}
|
|
285
237
|
//# sourceMappingURL=onboard.js.map
|