@humanbased/crosscheck 0.14.0 → 0.15.0-beta.103

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 (145) hide show
  1. package/README.md +147 -4
  2. package/crosscheck.config.example.yml +3 -1
  3. package/dist/__tests__/backtrace.test.js +1 -1
  4. package/dist/__tests__/backtrace.test.js.map +1 -1
  5. package/dist/__tests__/durations.test.js +5 -1
  6. package/dist/__tests__/durations.test.js.map +1 -1
  7. package/dist/__tests__/fix.test.js +48 -1
  8. package/dist/__tests__/fix.test.js.map +1 -1
  9. package/dist/__tests__/kickass.test.js +362 -69
  10. package/dist/__tests__/kickass.test.js.map +1 -1
  11. package/dist/__tests__/optimize.test.js +3 -3
  12. package/dist/__tests__/optimize.test.js.map +1 -1
  13. package/dist/__tests__/pr-picker.test.js +8 -7
  14. package/dist/__tests__/pr-picker.test.js.map +1 -1
  15. package/dist/__tests__/pr-status.test.js +41 -20
  16. package/dist/__tests__/pr-status.test.js.map +1 -1
  17. package/dist/__tests__/pr-workflow-state.test.d.ts +2 -0
  18. package/dist/__tests__/pr-workflow-state.test.d.ts.map +1 -0
  19. package/dist/__tests__/pr-workflow-state.test.js +184 -0
  20. package/dist/__tests__/pr-workflow-state.test.js.map +1 -0
  21. package/dist/__tests__/review-models.test.js +1 -0
  22. package/dist/__tests__/review-models.test.js.map +1 -1
  23. package/dist/__tests__/run.test.d.ts +2 -0
  24. package/dist/__tests__/run.test.d.ts.map +1 -0
  25. package/dist/__tests__/run.test.js +81 -0
  26. package/dist/__tests__/run.test.js.map +1 -0
  27. package/dist/__tests__/runner.test.js +117 -1
  28. package/dist/__tests__/runner.test.js.map +1 -1
  29. package/dist/__tests__/smart-switch.test.js +1 -1
  30. package/dist/__tests__/smart-switch.test.js.map +1 -1
  31. package/dist/__tests__/tier-timeouts.test.d.ts +2 -0
  32. package/dist/__tests__/tier-timeouts.test.d.ts.map +1 -0
  33. package/dist/__tests__/tier-timeouts.test.js +23 -0
  34. package/dist/__tests__/tier-timeouts.test.js.map +1 -0
  35. package/dist/__tests__/webhook.test.d.ts +2 -0
  36. package/dist/__tests__/webhook.test.d.ts.map +1 -0
  37. package/dist/__tests__/webhook.test.js +197 -0
  38. package/dist/__tests__/webhook.test.js.map +1 -0
  39. package/dist/cli.js +38 -5
  40. package/dist/cli.js.map +1 -1
  41. package/dist/commands/detect-step.d.ts +5 -0
  42. package/dist/commands/detect-step.d.ts.map +1 -0
  43. package/dist/commands/detect-step.js +124 -0
  44. package/dist/commands/detect-step.js.map +1 -0
  45. package/dist/commands/kickass.d.ts +18 -10
  46. package/dist/commands/kickass.d.ts.map +1 -1
  47. package/dist/commands/kickass.js +234 -63
  48. package/dist/commands/kickass.js.map +1 -1
  49. package/dist/commands/review.d.ts.map +1 -1
  50. package/dist/commands/review.js +14 -5
  51. package/dist/commands/review.js.map +1 -1
  52. package/dist/commands/run.d.ts +16 -1
  53. package/dist/commands/run.d.ts.map +1 -1
  54. package/dist/commands/run.js +347 -44
  55. package/dist/commands/run.js.map +1 -1
  56. package/dist/commands/serve.d.ts.map +1 -1
  57. package/dist/commands/serve.js +41 -3
  58. package/dist/commands/serve.js.map +1 -1
  59. package/dist/commands/watch.d.ts.map +1 -1
  60. package/dist/commands/watch.js +200 -6
  61. package/dist/commands/watch.js.map +1 -1
  62. package/dist/config/schema.d.ts +52 -0
  63. package/dist/config/schema.d.ts.map +1 -1
  64. package/dist/config/schema.js +24 -1
  65. package/dist/config/schema.js.map +1 -1
  66. package/dist/github/client.d.ts +40 -1
  67. package/dist/github/client.d.ts.map +1 -1
  68. package/dist/github/client.js +69 -9
  69. package/dist/github/client.js.map +1 -1
  70. package/dist/github/review-status.d.ts.map +1 -1
  71. package/dist/github/review-status.js +7 -4
  72. package/dist/github/review-status.js.map +1 -1
  73. package/dist/github/webhook.d.ts +25 -1
  74. package/dist/github/webhook.d.ts.map +1 -1
  75. package/dist/github/webhook.js +37 -1
  76. package/dist/github/webhook.js.map +1 -1
  77. package/dist/lib/annotation.d.ts +4 -0
  78. package/dist/lib/annotation.d.ts.map +1 -1
  79. package/dist/lib/annotation.js +5 -1
  80. package/dist/lib/annotation.js.map +1 -1
  81. package/dist/lib/comment-bodies.d.ts.map +1 -1
  82. package/dist/lib/comment-bodies.js +3 -2
  83. package/dist/lib/comment-bodies.js.map +1 -1
  84. package/dist/lib/durations.d.ts.map +1 -1
  85. package/dist/lib/durations.js +5 -3
  86. package/dist/lib/durations.js.map +1 -1
  87. package/dist/lib/logger.d.ts +3 -0
  88. package/dist/lib/logger.d.ts.map +1 -1
  89. package/dist/lib/logger.js +29 -3
  90. package/dist/lib/logger.js.map +1 -1
  91. package/dist/lib/pr-picker.d.ts.map +1 -1
  92. package/dist/lib/pr-picker.js +5 -1
  93. package/dist/lib/pr-picker.js.map +1 -1
  94. package/dist/lib/pr-status.d.ts +4 -3
  95. package/dist/lib/pr-status.d.ts.map +1 -1
  96. package/dist/lib/pr-status.js +19 -13
  97. package/dist/lib/pr-status.js.map +1 -1
  98. package/dist/lib/pr-workflow-state.d.ts +68 -0
  99. package/dist/lib/pr-workflow-state.d.ts.map +1 -0
  100. package/dist/lib/pr-workflow-state.js +328 -0
  101. package/dist/lib/pr-workflow-state.js.map +1 -0
  102. package/dist/lib/product.d.ts +3 -0
  103. package/dist/lib/product.d.ts.map +1 -0
  104. package/dist/lib/product.js +5 -0
  105. package/dist/lib/product.js.map +1 -0
  106. package/dist/lib/repo-picker.d.ts +1 -0
  107. package/dist/lib/repo-picker.d.ts.map +1 -1
  108. package/dist/lib/repo-picker.js +50 -33
  109. package/dist/lib/repo-picker.js.map +1 -1
  110. package/dist/lib/runner.d.ts +19 -1
  111. package/dist/lib/runner.d.ts.map +1 -1
  112. package/dist/lib/runner.js +337 -54
  113. package/dist/lib/runner.js.map +1 -1
  114. package/dist/lib/smart-switch.js +1 -1
  115. package/dist/lib/smart-switch.js.map +1 -1
  116. package/dist/lib/vendor.d.ts +4 -0
  117. package/dist/lib/vendor.d.ts.map +1 -0
  118. package/dist/lib/vendor.js +14 -0
  119. package/dist/lib/vendor.js.map +1 -0
  120. package/dist/lib/workflow.d.ts +5 -0
  121. package/dist/lib/workflow.d.ts.map +1 -1
  122. package/dist/lib/workflow.js.map +1 -1
  123. package/dist/reviewers/claude.d.ts +3 -1
  124. package/dist/reviewers/claude.d.ts.map +1 -1
  125. package/dist/reviewers/claude.js +14 -9
  126. package/dist/reviewers/claude.js.map +1 -1
  127. package/dist/reviewers/codex.d.ts +1 -1
  128. package/dist/reviewers/codex.d.ts.map +1 -1
  129. package/dist/reviewers/codex.js +7 -10
  130. package/dist/reviewers/codex.js.map +1 -1
  131. package/dist/reviewers/conflict-resolve.d.ts +1 -1
  132. package/dist/reviewers/conflict-resolve.d.ts.map +1 -1
  133. package/dist/reviewers/conflict-resolve.js +3 -2
  134. package/dist/reviewers/conflict-resolve.js.map +1 -1
  135. package/dist/reviewers/fix.d.ts +5 -1
  136. package/dist/reviewers/fix.d.ts.map +1 -1
  137. package/dist/reviewers/fix.js +68 -2
  138. package/dist/reviewers/fix.js.map +1 -1
  139. package/dist/reviewers/tier-timeouts.d.ts +5 -0
  140. package/dist/reviewers/tier-timeouts.d.ts.map +1 -0
  141. package/dist/reviewers/tier-timeouts.js +14 -0
  142. package/dist/reviewers/tier-timeouts.js.map +1 -0
  143. package/get-started.md +56 -5
  144. package/get-started.zh.md +7 -1
  145. package/package.json +1 -1
@@ -0,0 +1,5 @@
1
+ export declare function runDetectStep(prUrl: string, opts?: {
2
+ config?: string;
3
+ json?: boolean;
4
+ }): Promise<void>;
5
+ //# sourceMappingURL=detect-step.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-step.d.ts","sourceRoot":"","sources":["../../src/commands/detect-step.ts"],"names":[],"mappings":"AAgDA,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7C,OAAO,CAAC,IAAI,CAAC,CAmFf"}
@@ -0,0 +1,124 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { createGithubClient } from '../github/client.js';
4
+ import { getGithubToken } from '../config/loader.js';
5
+ import { loadWorkflow } from '../lib/workflow.js';
6
+ import { fetchStepHistory, identifyNextWorkflowStep } from '../lib/pr-workflow-state.js';
7
+ function parsePRUrl(url) {
8
+ const m = url.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
9
+ if (!m)
10
+ return null;
11
+ return { owner: m[1], repo: m[2], number: parseInt(m[3], 10) };
12
+ }
13
+ function fmtDate(iso) {
14
+ const d = new Date(iso);
15
+ const pad = (n) => String(n).padStart(2, '0');
16
+ return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`;
17
+ }
18
+ function verdictColor(verdict) {
19
+ if (!verdict)
20
+ return chalk.dim('?');
21
+ if (verdict === 'APPROVE')
22
+ return chalk.green(verdict);
23
+ if (verdict === 'BLOCK')
24
+ return chalk.red(verdict);
25
+ return chalk.yellow(verdict.replace('_', ' '));
26
+ }
27
+ function printHistory(records) {
28
+ if (records.length === 0) {
29
+ console.log(chalk.dim(' (no crosscheck steps found)'));
30
+ return;
31
+ }
32
+ const typeWidth = Math.max(...records.map(r => r.type.length));
33
+ for (let i = 0; i < records.length; i++) {
34
+ const r = records[i];
35
+ const idx = chalk.dim(String(i + 1).padStart(2));
36
+ const date = chalk.dim(fmtDate(r.createdAt));
37
+ const type = r.type.padEnd(typeWidth);
38
+ const reviewer = r.reviewer ? chalk.cyan(r.reviewer) + (r.model ? chalk.dim(`·${r.model}`) : '') : chalk.dim('—');
39
+ const recordSha = r.sha ?? r.pushedSha;
40
+ const sha = recordSha ? chalk.dim(`sha=${recordSha.slice(0, 7)}`) : '';
41
+ const round = r.round > 1 ? chalk.dim(`round=${r.round}`) : '';
42
+ const source = r.source === 'commit' ? chalk.dim('source=commit') : '';
43
+ const verdict = r.type === 'review' || r.type === 'recheck' ? verdictColor(r.verdict) : '';
44
+ const parts = [reviewer, sha, source, round, verdict].filter(Boolean).join(' ');
45
+ console.log(` ${idx} ${date} ${type} ${parts}`);
46
+ }
47
+ }
48
+ export async function runDetectStep(prUrl, opts = {}) {
49
+ const parsed = parsePRUrl(prUrl);
50
+ if (!parsed) {
51
+ console.error(chalk.red('Invalid PR URL. Expected: https://github.com/owner/repo/pull/123'));
52
+ process.exit(1);
53
+ }
54
+ const { owner, repo, number } = parsed;
55
+ let token;
56
+ try {
57
+ token = getGithubToken();
58
+ }
59
+ catch (err) {
60
+ console.error(chalk.red(`✗ ${err instanceof Error ? err.message : String(err)}`));
61
+ process.exit(1);
62
+ }
63
+ const spinner = ora(`Fetching PR #${number}...`).start();
64
+ const octokit = createGithubClient(token);
65
+ const { data: prData } = await octokit.rest.pulls.get({ owner, repo, pull_number: number });
66
+ spinner.succeed(`PR #${number} · ${prData.title}`);
67
+ const steps = loadWorkflow(process.cwd());
68
+ const historySpinner = ora('Reading workflow history...').start();
69
+ const history = await fetchStepHistory(owner, repo, number, token);
70
+ historySpinner.stop();
71
+ const currentSha = prData.head.sha;
72
+ const nextResult = identifyNextWorkflowStep(history, steps, currentSha);
73
+ if (opts.json) {
74
+ console.log(JSON.stringify({
75
+ pr: { number, title: prData.title, headSha: currentSha, base: prData.base.ref },
76
+ history: history.map(r => ({
77
+ type: r.type,
78
+ verdict: r.verdict,
79
+ sha: r.sha,
80
+ pushedSha: r.pushedSha,
81
+ source: r.source,
82
+ round: r.round,
83
+ commentId: r.commentId,
84
+ createdAt: r.createdAt,
85
+ reviewer: r.reviewer,
86
+ model: r.model,
87
+ next_step: r.next_step,
88
+ })),
89
+ next: nextResult.step
90
+ ? {
91
+ step: nextResult.step.type,
92
+ stepName: nextResult.step.name,
93
+ round: nextResult.round,
94
+ reviewCommentId: nextResult.reviewComment?.id,
95
+ }
96
+ : null,
97
+ }, null, 2));
98
+ return;
99
+ }
100
+ console.log();
101
+ console.log(chalk.dim(` HEAD ${currentSha.slice(0, 7)} · base: ${prData.base.ref}`));
102
+ console.log(chalk.dim(` workflow steps: ${steps.map(s => s.name).join(' → ')}`));
103
+ console.log();
104
+ const divider = chalk.dim('─'.repeat(70));
105
+ console.log(` ${chalk.bold('Step history')} ${chalk.dim(`(${history.length} entr${history.length === 1 ? 'y' : 'ies'})`)}\n ${divider}`);
106
+ printHistory(history);
107
+ console.log(` ${divider}`);
108
+ console.log();
109
+ if (nextResult.step === null) {
110
+ const lastVerdict = [...history].reverse().find(r => r.verdict)?.verdict;
111
+ console.log(` ${chalk.green('✓')} Workflow complete${lastVerdict ? ` — last verdict: ${verdictColor(lastVerdict)}` : ''}`);
112
+ }
113
+ else {
114
+ const nextLabel = chalk.bold(nextResult.step.type);
115
+ const roundLabel = nextResult.round > 1 ? chalk.dim(` (round ${nextResult.round})`) : '';
116
+ console.log(` Next step: ${nextLabel}${roundLabel}`);
117
+ if (nextResult.reviewComment) {
118
+ const commentUrl = `https://github.com/${owner}/${repo}/pull/${number}#issuecomment-${nextResult.reviewComment.id}`;
119
+ console.log(` Context: review comment #${nextResult.reviewComment.id} · ${chalk.dim(commentUrl)}`);
120
+ }
121
+ }
122
+ console.log();
123
+ }
124
+ //# sourceMappingURL=detect-step.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-step.js","sourceRoot":"","sources":["../../src/commands/detect-step.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,19 @@
1
+ import type { Config } from '../config/schema.js';
2
+ import type { ErrorCategory } from '../lib/logger.js';
1
3
  import type { ScanPRStatus as PRStatus, ScanResult } from '../lib/pr-status.js';
2
4
  export interface KickassOpts {
3
5
  force?: boolean;
4
6
  staleAfter?: string;
5
7
  dryRun?: boolean;
8
+ roundMode?: 'crazy' | 'halfcrazy';
9
+ timeout?: string;
10
+ concurrent?: number;
11
+ staggerMs?: number;
6
12
  }
7
- export type KickassAction = 'review' | 'fix' | 'recheck' | 'merge' | 'skip';
13
+ export type KickassAction = 'review' | 'fix' | 'recheck' | 'skip';
8
14
  export type KickassSkipReason = 'fork_pr' | 'stale_signature';
9
- export type KickassFailureReason = 'error';
15
+ export type KickassFailureReason = ErrorCategory;
16
+ export type FixDeliveryMode = Config['post_review']['auto_fix']['delivery']['mode'];
10
17
  export interface PreflightItem {
11
18
  pr: PRStatus;
12
19
  action: KickassAction;
@@ -14,6 +21,7 @@ export interface PreflightItem {
14
21
  details: string[];
15
22
  explanation?: string;
16
23
  skipReason?: KickassSkipReason;
24
+ chainRecheck?: boolean;
17
25
  }
18
26
  export interface KickassExecutionResult {
19
27
  pr: PRStatus;
@@ -22,8 +30,7 @@ export interface KickassExecutionResult {
22
30
  }
23
31
  export interface ExecuteKickassDeps {
24
32
  getCurrentHeadSha: (item: PreflightItem) => Promise<string>;
25
- dispatchRun: (item: PreflightItem) => Promise<void>;
26
- dispatchMerge: (item: PreflightItem) => Promise<void>;
33
+ dispatchRun: (item: PreflightItem) => Promise<string | void>;
27
34
  }
28
35
  export interface KickassDeps {
29
36
  loadScanResult: (options: {
@@ -32,18 +39,19 @@ export interface KickassDeps {
32
39
  }) => Promise<ScanResult>;
33
40
  pickPRs: (prs: PRStatus[]) => Promise<PRStatus[]>;
34
41
  confirm: (message: string) => Promise<boolean>;
42
+ getFixDeliveryMode?: () => FixDeliveryMode | Promise<FixDeliveryMode>;
35
43
  getCurrentHeadSha: (item: PreflightItem) => Promise<string>;
36
- dispatchRun: (item: PreflightItem) => Promise<void>;
37
- dispatchMerge: (item: PreflightItem) => Promise<void>;
44
+ dispatchRun: (item: PreflightItem) => Promise<string | void>;
38
45
  }
39
46
  export declare function runKickass(opts?: KickassOpts): Promise<void>;
40
47
  export declare function runKickassWithDeps(opts: KickassOpts | undefined, deps: KickassDeps): Promise<void>;
41
- export declare function buildPreflightPlan(prs: PRStatus[]): PreflightItem[];
42
- export declare function executeKickassPlan(plan: PreflightItem[], deps: ExecuteKickassDeps): Promise<KickassExecutionResult[]>;
43
- export declare function printPreflight(plan: PreflightItem[]): void;
48
+ export declare function buildPreflightPlan(prs: PRStatus[], roundMode?: 'crazy' | 'halfcrazy', fixDeliveryMode?: FixDeliveryMode): PreflightItem[];
49
+ export declare function executeKickassPlan(plan: PreflightItem[], deps: ExecuteKickassDeps, concurrency?: number, staggerMs?: number): Promise<KickassExecutionResult[]>;
50
+ export declare function printPreflight(plan: PreflightItem[], mergeReady?: PRStatus[]): void;
51
+ export declare function printMergeReady(prs: PRStatus[]): void;
44
52
  export declare function summarizeExecutionResults(results: KickassExecutionResult[]): string;
45
53
  export declare function printExecutionSummary(results: KickassExecutionResult[]): void;
46
- export declare function buildKickassRunArgs(itemOrPR: PreflightItem | PRStatus): string[];
54
+ export declare function buildKickassRunArgs(itemOrPR: PreflightItem | PRStatus, roundMode?: 'crazy' | 'halfcrazy', timeout?: string): string[];
47
55
  export interface CliInvocation {
48
56
  command: string;
49
57
  args: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"kickass.d.ts","sourceRoot":"","sources":["../../src/commands/kickass.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"kickass.d.ts","sourceRoot":"","sources":["../../src/commands/kickass.ts"],"names":[],"mappings":"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"}
@@ -5,14 +5,13 @@ import { createInterface } from 'readline/promises';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { execa } from 'execa';
7
7
  import { createGithubClient } from '../github/client.js';
8
- import { mergePullRequest } from '../github/merge.js';
9
- import { getGithubToken } from '../config/loader.js';
8
+ import { getGithubToken, loadConfig } from '../config/loader.js';
10
9
  import { parseDuration } from '../lib/durations.js';
11
- import { logError } from '../lib/logger.js';
10
+ import { classifyError, logError } from '../lib/logger.js';
12
11
  import { pickPRs } from '../lib/pr-picker.js';
13
12
  import { handleScanError, loadScanResult } from './scan.js';
14
13
  export async function runKickass(opts = {}) {
15
- await runKickassWithDeps(opts, defaultKickassDeps());
14
+ await runKickassWithDeps(opts, defaultKickassDeps(opts));
16
15
  }
17
16
  export async function runKickassWithDeps(opts = {}, deps) {
18
17
  let staleAfterMs;
@@ -23,11 +22,38 @@ export async function runKickassWithDeps(opts = {}, deps) {
23
22
  console.error(chalk.red(`✗ ${err instanceof Error ? err.message : String(err)}`));
24
23
  process.exit(1);
25
24
  }
25
+ if (opts.timeout) {
26
+ try {
27
+ parseDuration(opts.timeout);
28
+ }
29
+ catch {
30
+ console.error(chalk.red(`✗ Invalid --timeout value "${opts.timeout}". Use a duration like 300s or 10m.`));
31
+ process.exit(1);
32
+ }
33
+ }
34
+ if (opts.concurrent !== undefined && (opts.concurrent < 0 || !Number.isInteger(opts.concurrent))) {
35
+ console.error(chalk.red('✗ --concurrent must be a non-negative integer (0 = one agent per selected PR)'));
36
+ process.exit(1);
37
+ }
26
38
  try {
27
39
  const scan = await deps.loadScanResult({ force: opts.force, staleAfterMs });
28
- const queue = scan.prs.filter(pr => pr.freshness === 'stale' && pr.nextAction !== null);
40
+ // Actionable = nextAction is set and is not merge (merge not dispatched in v1).
41
+ // Stale PRs shown first; not-stale actionable PRs follow.
42
+ const queue = scan.prs
43
+ .filter(pr => pr.nextAction !== null && pr.nextAction !== 'merge')
44
+ .sort((a, b) => {
45
+ if (a.freshness !== b.freshness)
46
+ return a.freshness === 'stale' ? -1 : 1;
47
+ return 0;
48
+ });
49
+ const mergeReady = scan.prs.filter(pr => pr.nextAction === 'merge');
50
+ if (queue.length === 0 && mergeReady.length === 0) {
51
+ printNoActionablePRsWarning(scan.cached);
52
+ return;
53
+ }
29
54
  if (queue.length === 0) {
30
- console.log(chalk.dim('No stale PRs need attention.'));
55
+ printMergeReady(mergeReady);
56
+ console.log(chalk.dim('\nNo PRs need review, fix, or recheck — all actionable work is merge-ready (manual).'));
31
57
  return;
32
58
  }
33
59
  const selected = await deps.pickPRs(queue);
@@ -35,8 +61,9 @@ export async function runKickassWithDeps(opts = {}, deps) {
35
61
  console.log(chalk.dim('No PRs selected.'));
36
62
  return;
37
63
  }
38
- const plan = buildPreflightPlan(selected);
39
- printPreflight(plan);
64
+ const fixDeliveryMode = deps.getFixDeliveryMode ? await deps.getFixDeliveryMode() : 'pull_request';
65
+ const plan = buildPreflightPlan(selected, opts.roundMode, fixDeliveryMode);
66
+ printPreflight(plan, mergeReady);
40
67
  if (opts.dryRun) {
41
68
  console.log(chalk.dim('\ndry-run: no mutations executed'));
42
69
  return;
@@ -46,7 +73,15 @@ export async function runKickassWithDeps(opts = {}, deps) {
46
73
  console.log(chalk.dim('Canceled.'));
47
74
  return;
48
75
  }
49
- const results = await executeKickassPlan(plan, deps);
76
+ // 0 = one agent per selected PR; undefined/1 = sequential
77
+ const resolvedConcurrency = opts.concurrent === 0
78
+ ? selected.length
79
+ : Math.max(1, opts.concurrent ?? 1);
80
+ const resolvedStagger = resolvedConcurrency > 1 ? (opts.staggerMs ?? 2_000) : 0;
81
+ if (resolvedConcurrency > 1) {
82
+ console.log(chalk.dim(`\n running ${resolvedConcurrency} agents in parallel (${resolvedStagger}ms stagger)`));
83
+ }
84
+ const results = await executeKickassPlan(plan, deps, resolvedConcurrency, resolvedStagger);
50
85
  printExecutionSummary(results);
51
86
  if (results.some(result => result.status === 'failed')) {
52
87
  process.exitCode = 2;
@@ -56,7 +91,9 @@ export async function runKickassWithDeps(opts = {}, deps) {
56
91
  handleScanError('kickass', err);
57
92
  }
58
93
  }
59
- export function buildPreflightPlan(prs) {
94
+ export function buildPreflightPlan(prs, roundMode, fixDeliveryMode = 'pull_request') {
95
+ const modeTag = roundMode ? ` [${roundMode}]` : '';
96
+ const chainRecheck = fixDeliveryMode === 'commit';
60
97
  return prs.map((pr) => {
61
98
  const fork = isForkPR(pr);
62
99
  if (pr.nextAction === 'fix' && fork) {
@@ -89,59 +126,166 @@ export function buildPreflightPlan(prs) {
89
126
  return {
90
127
  pr,
91
128
  action: 'fix',
92
- transition: `${pr.reviewState} -> Fix`,
93
- details: [`fixer ${fixerLabel(pr)}`, 'delivery commit'],
94
- };
95
- }
96
- if (pr.nextAction === 'recheck') {
97
- return {
98
- pr,
99
- action: 'recheck',
100
- transition: `${pr.reviewState} -> Recheck`,
101
- details: ['links latest review'],
129
+ transition: chainRecheck
130
+ ? `${pr.reviewState} -> fix→recheck${modeTag}`
131
+ : `${pr.reviewState} -> fix`,
132
+ details: [
133
+ `fixer ${fixerLabel(pr)}`,
134
+ `delivery ${fixDeliveryMode}`,
135
+ ...(chainRecheck ? [] : ['recheck deferred']),
136
+ ],
137
+ chainRecheck,
102
138
  };
103
139
  }
140
+ // nextAction === 'recheck' — fix was applied externally; close the loop with one recheck
104
141
  return {
105
142
  pr,
106
- action: 'merge',
107
- transition: 'APPROVE -> Merge',
108
- details: ['method squash', `checks ${checksLabel(pr)}`],
143
+ action: 'recheck',
144
+ transition: `${pr.reviewState} -> Recheck`,
145
+ details: ['links latest review'],
109
146
  };
110
147
  });
111
148
  }
112
- export async function executeKickassPlan(plan, deps) {
113
- const results = [];
114
- for (const item of plan) {
149
+ function printCapturedOutput(label, output) {
150
+ const lines = output.trimEnd().split('\n');
151
+ console.log(chalk.dim(`\n── ${label} ${'─'.repeat(Math.max(0, 48 - label.length))}`));
152
+ for (const line of lines)
153
+ console.log(` ${line}`);
154
+ }
155
+ function printNoActionablePRsWarning(fromCache) {
156
+ console.log(chalk.dim('No actionable PRs found.'));
157
+ if (fromCache) {
158
+ console.log(chalk.yellow(`⚠ This result came from the scan cache. Rerun with --force to refresh the queue.`));
159
+ }
160
+ }
161
+ export async function executeKickassPlan(plan, deps, concurrency = 1, staggerMs = 0) {
162
+ const results = new Array(plan.length);
163
+ const executeItem = async (item, index, attempt = 1) => {
115
164
  if (item.action === 'skip') {
116
165
  console.log(chalk.yellow(`↷ skip ${formatPRSignature(item.pr)} ${item.skipReason ?? 'skipped'}`));
117
- results.push({ pr: item.pr, status: 'skipped', reason: item.skipReason });
118
- continue;
166
+ results[index] = { pr: item.pr, status: 'skipped', reason: item.skipReason };
167
+ return;
119
168
  }
120
169
  try {
121
170
  const currentHeadSha = await deps.getCurrentHeadSha(item);
122
171
  if (currentHeadSha !== item.pr.headSha) {
123
172
  console.log(chalk.yellow(`↷ skip ${formatPRSignature(item.pr)} stale_signature`));
124
- results.push({ pr: item.pr, status: 'skipped', reason: 'stale_signature' });
125
- continue;
173
+ results[index] = { pr: item.pr, status: 'skipped', reason: 'stale_signature' };
174
+ return;
126
175
  }
127
- console.log(chalk.cyan(`\n→ ${item.transition} ${formatPRSignature(item.pr)}`));
128
- if (item.action === 'merge') {
129
- await deps.dispatchMerge(item);
176
+ const attemptLabel = attempt > 1 ? ` (retry ${attempt - 1})` : '';
177
+ console.log(chalk.cyan(`\n→ ${item.transition} ${formatPRSignature(item.pr)}${attemptLabel}`));
178
+ const output = await deps.dispatchRun(item);
179
+ if (typeof output === 'string' && output)
180
+ printCapturedOutput(formatPRSignature(item.pr), output);
181
+ if (item.action === 'fix' && item.chainRecheck === true) {
182
+ const fixedHeadSha = await deps.getCurrentHeadSha(item);
183
+ if (fixedHeadSha !== item.pr.headSha) {
184
+ const recheckItem = buildPostFixRecheckItem(item, fixedHeadSha);
185
+ console.log(chalk.cyan(`\n→ ${recheckItem.transition} ${formatPRSignature(recheckItem.pr)}`));
186
+ const recheckOutput = await deps.dispatchRun(recheckItem);
187
+ if (typeof recheckOutput === 'string' && recheckOutput)
188
+ printCapturedOutput(formatPRSignature(recheckItem.pr), recheckOutput);
189
+ }
190
+ else {
191
+ console.log(chalk.dim(` head SHA unchanged after fix — recheck deferred`));
192
+ }
130
193
  }
131
- else {
132
- await deps.dispatchRun(item);
133
- }
134
- results.push({ pr: item.pr, status: 'executed' });
194
+ results[index] = { pr: item.pr, status: 'executed' };
135
195
  }
136
196
  catch (err) {
137
- logError({ event: 'kickass_pr_failed', owner: item.pr.owner, repo: item.pr.repo, pr: item.pr.number }, err);
197
+ logError({ event: 'kickass_pr_failed', owner: item.pr.owner, repo: item.pr.repo, pr: item.pr.number, ...(attempt > 1 && { attempt }) }, err);
138
198
  console.error(chalk.red(`✗ failed ${formatPRSignature(item.pr)}`));
139
- results.push({ pr: item.pr, status: 'failed', reason: 'error' });
199
+ // Classify execa errors using structured fields, not the raw message.
200
+ // The raw message includes the full CLI invocation (e.g. "Command failed with exit
201
+ // code 1: node crosscheck run --timeout 300s --no-timeout"), so a text match against
202
+ // `message` would misclassify ordinary subprocess failures as 'timeout' whenever the
203
+ // command contains a --timeout flag.
204
+ const maybeExeca = err;
205
+ let msgForClassify;
206
+ if (maybeExeca.timedOut === true) {
207
+ // execa's structured timeout flag — reliable; bypass message matching entirely.
208
+ msgForClassify = 'timed out';
209
+ }
210
+ else if (typeof maybeExeca.exitCode === 'number') {
211
+ // Subprocess failure: prefer stderr (actual error output) over the message which
212
+ // includes the full command string. Strip the command suffix when stderr is absent.
213
+ const stderr = typeof maybeExeca.stderr === 'string' ? maybeExeca.stderr.trim() : '';
214
+ msgForClassify = stderr || (err instanceof Error ? err.message.replace(/:\s*\S.*$/, '') : String(err));
215
+ }
216
+ else {
217
+ msgForClassify = err instanceof Error ? err.message : String(err);
218
+ }
219
+ const category = classifyError(msgForClassify);
220
+ results[index] = { pr: item.pr, status: 'failed', reason: category };
221
+ }
222
+ };
223
+ if (concurrency <= 1) {
224
+ for (let i = 0; i < plan.length; i++)
225
+ await executeItem(plan[i], i);
226
+ }
227
+ else {
228
+ // Worker-pool: up to `concurrency` PRs run in parallel.
229
+ // staggerMs > 0 delays each worker's start by (workerIdx * staggerMs) to spread
230
+ // concurrent subprocess startup API calls over time rather than hitting GitHub simultaneously.
231
+ let ptr = 0;
232
+ const makeWorker = (workerIdx) => async () => {
233
+ if (staggerMs > 0 && workerIdx > 0) {
234
+ await new Promise(resolve => setTimeout(resolve, workerIdx * staggerMs));
235
+ }
236
+ while (ptr < plan.length) {
237
+ const i = ptr++;
238
+ await executeItem(plan[i], i);
239
+ }
240
+ };
241
+ await Promise.all(Array.from({ length: Math.min(concurrency, plan.length) }, (_, idx) => makeWorker(idx)()));
242
+ }
243
+ // Retry transient failures up to 4 times with escalating delays.
244
+ // Auth and permission failures are operator issues that won't self-heal.
245
+ const RETRYABLE = new Set(['network', 'timeout']);
246
+ const RETRY_DELAYS_MS = [60_000, 120_000, 300_000, 600_000];
247
+ for (let attempt = 2; attempt <= RETRY_DELAYS_MS.length + 1; attempt++) {
248
+ const delayMs = RETRY_DELAYS_MS[attempt - 2];
249
+ const retryItems = results
250
+ .map((r, i) => ({ r, i }))
251
+ .filter(({ r }) => r.status === 'failed' && RETRYABLE.has(r.reason));
252
+ if (retryItems.length === 0)
253
+ break;
254
+ const delaySec = delayMs / 1000;
255
+ const delayLabel = delaySec >= 60 ? `${delaySec / 60}m` : `${delaySec}s`;
256
+ console.log(chalk.dim(`\n ${retryItems.length} transient failure(s) — retry ${attempt - 1}/${RETRY_DELAYS_MS.length} in ${delayLabel}...`));
257
+ await new Promise(resolve => setTimeout(resolve, delayMs));
258
+ for (const { i } of retryItems) {
259
+ const priorResult = results[i];
260
+ await executeItem(plan[i], i, attempt);
261
+ // Stale-signature means the fix already committed in a prior attempt but the
262
+ // chained recheck failed transiently. Instead of reporting that failure as
263
+ // final, fetch the current head and run a bare recheck to actually retry it.
264
+ if (results[i].status === 'skipped' && results[i].reason === 'stale_signature'
265
+ && plan[i].action === 'fix' && plan[i].chainRecheck === true) {
266
+ try {
267
+ const currentHead = await deps.getCurrentHeadSha(plan[i]);
268
+ const recheckItem = buildPostFixRecheckItem(plan[i], currentHead);
269
+ await executeItem(recheckItem, i, attempt);
270
+ }
271
+ catch {
272
+ // If we cannot fetch the head (network failure), preserve the original failure.
273
+ results[i] = priorResult;
274
+ }
275
+ }
140
276
  }
141
277
  }
142
278
  return results;
143
279
  }
144
- export function printPreflight(plan) {
280
+ function buildPostFixRecheckItem(item, headSha) {
281
+ return {
282
+ pr: { ...item.pr, headSha, nextAction: 'recheck', reviewState: 'NEEDS_RECHECK' },
283
+ action: 'recheck',
284
+ transition: 'fix -> Recheck',
285
+ details: ['links latest review', `head ${headSha.slice(0, 7)}`],
286
+ };
287
+ }
288
+ export function printPreflight(plan, mergeReady = []) {
145
289
  console.log('\nPreflight');
146
290
  const grouped = groupPreflight(plan);
147
291
  for (const [transition, items] of grouped) {
@@ -151,6 +295,14 @@ export function printPreflight(plan) {
151
295
  console.log(` ${formatPRSignature(item.pr)} ${item.details.join(' ')}${explanation}`);
152
296
  }
153
297
  }
298
+ if (mergeReady.length > 0)
299
+ printMergeReady(mergeReady);
300
+ }
301
+ export function printMergeReady(prs) {
302
+ console.log(chalk.dim('\nneeds merge (manual — not selected)'));
303
+ for (const pr of prs) {
304
+ console.log(chalk.dim(` ${formatPRSignature(pr)} APPROVE`));
305
+ }
154
306
  }
155
307
  export function summarizeExecutionResults(results) {
156
308
  const executed = results.filter(result => result.status === 'executed').length;
@@ -161,18 +313,36 @@ export function summarizeExecutionResults(results) {
161
313
  export function printExecutionSummary(results) {
162
314
  console.log(chalk.dim(`\n${summarizeExecutionResults(results)}`));
163
315
  }
164
- export function buildKickassRunArgs(itemOrPR) {
316
+ export function buildKickassRunArgs(itemOrPR, roundMode, timeout) {
165
317
  const item = 'action' in itemOrPR ? itemOrPR : buildPreflightPlan([itemOrPR])[0];
166
- if (item.action === 'merge' || item.action === 'skip')
318
+ if (item.action === 'skip')
167
319
  return [];
168
- return [
169
- 'run',
170
- item.pr.url,
171
- '--steps',
172
- stepForAction(item.action),
173
- '--expected-head-sha',
174
- item.pr.headSha,
175
- ];
320
+ const args = ['run', item.pr.url];
321
+ // No --steps for normal review/recheck/fix actions: run.ts calls
322
+ // identifyNextWorkflowStep against live PR history to determine the correct
323
+ // next step. Exception: when kickass demoted a fix action to review because the
324
+ // latest annotation covers an older SHA (no_usable_review_comment), we must
325
+ // force --steps review so run.ts doesn't re-detect from live history and choose
326
+ // the stale review's fix step, applying fixes to the unreviewed new diff.
327
+ if (item.action === 'review' && item.explanation === 'no_usable_review_comment') {
328
+ args.push('--steps', 'review');
329
+ }
330
+ args.push('--expected-head-sha', item.pr.headSha);
331
+ if (item.action !== 'fix') {
332
+ if (roundMode === 'crazy')
333
+ args.push('--crazy');
334
+ else if (roundMode === 'halfcrazy')
335
+ args.push('--half-crazy');
336
+ }
337
+ else if (roundMode) {
338
+ // fix legs don't loop, but still need the no-timeout constraint lifted
339
+ args.push('--no-timeout');
340
+ }
341
+ // forward user-specified --timeout for runs that aren't already in a round mode
342
+ if (timeout && !roundMode)
343
+ args.push('--timeout', timeout);
344
+ args.push('--trigger', 'kickass');
345
+ return args;
176
346
  }
177
347
  export function resolveCliInvocation(options = {}) {
178
348
  const exists = options.exists ?? existsSync;
@@ -191,16 +361,20 @@ export function resolveCliInvocation(options = {}) {
191
361
  return invocationForEntry(sourceCli, execPath, localTsx, exists);
192
362
  throw new Error('Cannot resolve crosscheck CLI entrypoint. Run npm run build before kickass, or run from a source checkout with dev dependencies installed.');
193
363
  }
194
- function defaultKickassDeps() {
364
+ function defaultKickassDeps(opts = {}) {
195
365
  let cli;
196
366
  const getCli = () => {
197
367
  cli ??= resolveCliInvocation();
198
368
  return cli;
199
369
  };
370
+ // opts.concurrent = 0 means "one per PR" (fully parallel); undefined means sequential.
371
+ // Any explicit --concurrent value uses buffered stdio; sequential streams inline.
372
+ const isParallel = opts.concurrent !== undefined;
200
373
  return {
201
374
  loadScanResult,
202
375
  pickPRs,
203
376
  confirm: confirmMutation,
377
+ getFixDeliveryMode: () => loadConfig().post_review.auto_fix.delivery.mode,
204
378
  getCurrentHeadSha: async (item) => {
205
379
  const token = getGithubToken();
206
380
  const octokit = createGithubClient(token);
@@ -213,15 +387,12 @@ function defaultKickassDeps() {
213
387
  },
214
388
  dispatchRun: async (item) => {
215
389
  const invocation = getCli();
216
- await execa(invocation.command, [...invocation.args, ...buildKickassRunArgs(item)], { stdio: 'inherit' });
217
- },
218
- dispatchMerge: async (item) => {
219
- const token = getGithubToken();
220
- const octokit = createGithubClient(token);
221
- await mergePullRequest(octokit, item.pr.owner, item.pr.repo, item.pr.number, {
222
- method: 'squash',
223
- expectedHeadSha: item.pr.headSha,
224
- });
390
+ const args = [...invocation.args, ...buildKickassRunArgs(item, opts.roundMode, opts.timeout)];
391
+ if (isParallel) {
392
+ const result = await execa(invocation.command, args, { stdio: 'pipe', all: true });
393
+ return result.all ?? '';
394
+ }
395
+ await execa(invocation.command, args, { stdio: 'inherit' });
225
396
  },
226
397
  };
227
398
  }
@@ -268,10 +439,10 @@ function checksLabel(pr) {
268
439
  return 'green';
269
440
  return pr.merge.mergeStateStatus ?? 'unknown';
270
441
  }
271
- function stepForAction(action) {
272
- if (action === 'review')
442
+ function stepsForItem(item) {
443
+ if (item.action === 'review')
273
444
  return 'review';
274
- if (action === 'fix')
445
+ if (item.action === 'fix')
275
446
  return 'fix';
276
447
  return 'recheck';
277
448
  }