@rigour-labs/cli 6.9.0-rc.3 → 6.9.0-rc.5
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 +2 -0
- package/dist/commands/outcomes.js +14 -0
- package/dist/commands/review-reviewer.d.ts +1 -1
- package/dist/commands/review-reviewer.js +3 -3
- package/dist/commands/review.d.ts +1 -0
- package/dist/commands/review.js +1 -1
- package/dist/commands/studio-learning.d.ts +3 -1
- package/dist/commands/studio-learning.js +5 -5
- package/package.json +3 -3
- package/studio-dist/assets/index--1-0a2Wg.js +166 -0
- package/studio-dist/index.html +1 -1
- package/studio-dist/assets/index-DvSqJYCS.js +0 -166
package/dist/cli.js
CHANGED
|
@@ -273,6 +273,8 @@ program
|
|
|
273
273
|
.option('--single', 'With --reviewer: one judge for this run, whatever your settings say (a team floor still applies)')
|
|
274
274
|
.option('--panel', 'With --reviewer: a panel of judges for this run; only what a majority confirms blocks')
|
|
275
275
|
.option('--no-panel', 'With --reviewer: no panel for this run (a team that requires one refuses this)')
|
|
276
|
+
.option('--orchestrator', 'With --reviewer: only the parts of the review this change needs, picked without a model, usually in one pass (experimental)')
|
|
277
|
+
.option('--no-orchestrator', 'With --reviewer: one judge for the whole review, for this run (a team that requires the orchestrator refuses this)')
|
|
276
278
|
.option('--status', 'What the background reviewer has done for this branch: running, last verdict, open items')
|
|
277
279
|
.option('--all', 'Show every finding, not the first five')
|
|
278
280
|
.option('--notes', 'List the notes that never block')
|
|
@@ -27,6 +27,9 @@ export async function outcomesCommand(cwd, options) {
|
|
|
27
27
|
for (const o of run.outcomes)
|
|
28
28
|
console.log(outcomeLine(o));
|
|
29
29
|
console.log(chalk.dim(`${run.outcomes.length} pull request(s), ${run.read} read now, the rest settled.${run.stopped ? ` Stopped early: ${run.stopped}.` : ''}`));
|
|
30
|
+
if (run.metrics)
|
|
31
|
+
for (const line of metricLines(run.metrics))
|
|
32
|
+
console.log(chalk.dim(line));
|
|
30
33
|
if (run.lessons?.added)
|
|
31
34
|
console.log(`Lessons: ${run.lessons.added} new piece(s) of evidence, ${run.lessons.suggested.length} to look at in Studio (a later fix changed their lines), ${run.lessons.demoted.length} taken back.`);
|
|
32
35
|
return 0;
|
|
@@ -37,3 +40,14 @@ function outcomeLine(o) {
|
|
|
37
40
|
const after = `${o.followUps.length} later commit(s) on its files${fixes ? `, ${chalk.yellow(`${fixes} fix(es)`)}` : ''}`;
|
|
38
41
|
return `#${o.pr} ${o.mergeSha.slice(0, 9)} merged ${o.mergedAt.slice(0, 10)}: ${ci}, ${after}${o.reverted ? `, ${chalk.red(`reverted by ${o.reverted.sha.slice(0, 9)}`)}` : ''}${o.settled ? '' : chalk.dim(' (window open)')}`;
|
|
39
42
|
}
|
|
43
|
+
/** The numbers, as counts; a percentage only where there are enough records for one (metrics.ts). Reviewed and not reviewed are side by side, never compared. */
|
|
44
|
+
function metricLines(m) {
|
|
45
|
+
const share = (s) => `${s.count} of ${s.of}${s.rate === null ? '' : ` (${Math.round(s.rate * 100)}%)`}`;
|
|
46
|
+
return [
|
|
47
|
+
`Numbers: ${m.records.merged} merged, ${m.records.settled} settled, ${m.records.unsettled} with the window still open.`,
|
|
48
|
+
` Settled: CI regressed on the merge ${share(m.settled.ciRegressed)}${m.settled.ciUnknown ? ` (${m.settled.ciUnknown} with no CI to read)` : ''}, reverted ${share(m.settled.reverted)}, fixed later ${share(m.settled.fixedLater)}.`,
|
|
49
|
+
` Reviewed by Rigour: ${m.settled.reviewed.prs} pull request(s), ${m.settled.reviewed.fixedLater.count} fixed later. Not reviewed: ${m.settled.notReviewed.prs}, ${m.settled.notReviewed.fixedLater.count} fixed later. (Teams choose what gets reviewed: not a comparison.)`,
|
|
50
|
+
` The model reviewer: ${m.model.share.model} of ${m.model.share.model + m.model.share.checks} finding(s) at first review on ${m.model.share.prs} pull request(s)${m.model.share.rate === null ? '' : ` (${Math.round(m.model.share.rate * 100)}%)`}; $${m.model.costPerPr.totalUsd.toFixed(2)} over ${m.model.costPerPr.prs} pull request(s)${m.model.costPerPr.medianUsd === null ? '' : `, median $${m.model.costPerPr.medianUsd.toFixed(2)}`}.`,
|
|
51
|
+
` Lessons: ${m.lessons.awaitingDecision} waiting on a person, ${m.lessons.promotedFromEvidence} promoted from evidence, ${m.lessons.dismissed} dismissed, ${m.lessons.takenBack} taken back.`,
|
|
52
|
+
];
|
|
53
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Config, type ReviewerResult, type ReviewResult, type RunChoice } from '@rigour-labs/core';
|
|
2
2
|
/** The base a branch review runs against: the one named, else where the branch left main. */
|
|
3
3
|
export declare function reviewerBase(cwd: string, named: string | undefined): string | undefined;
|
|
4
|
-
export declare function reviewerFor(cwd: string, base: string | undefined, config: Config, full: boolean, choice: RunChoice, review: ReviewResult, goal?: boolean): Promise<ReviewerResult>;
|
|
4
|
+
export declare function reviewerFor(cwd: string, base: string | undefined, config: Config, full: boolean, choice: RunChoice, review: ReviewResult, goal?: boolean, orchestrator?: boolean): Promise<ReviewerResult>;
|
|
5
5
|
export declare function printReviewer(result: ReviewerResult, options?: {
|
|
6
6
|
notes?: boolean;
|
|
7
7
|
}): void;
|
|
@@ -12,10 +12,10 @@ import { branchBase, itemLine, reviewerInputs, reviewStatus, runReviewer } from
|
|
|
12
12
|
export function reviewerBase(cwd, named) {
|
|
13
13
|
return named ?? branchBase(cwd)?.mainRef.replace(/^refs\/(remotes\/|heads\/)/, '');
|
|
14
14
|
}
|
|
15
|
-
export async function reviewerFor(cwd, base, config, full, choice, review, goal) {
|
|
15
|
+
export async function reviewerFor(cwd, base, config, full, choice, review, goal, orchestrator) {
|
|
16
16
|
if (!base)
|
|
17
17
|
return { outcome: 'unavailable', items: [], unverified: [], resolved: [], answerInReply: [], notes: [], advisory: [], disputed: [], dropped: [], dismissed: [], reason: 'no base to review against: pass --base, or fetch the main branch', reviewers: [], cached: false };
|
|
18
|
-
return runReviewer(cwd, base, config, undefined, undefined, { trigger: 'review', full, choice, ...(goal !== undefined ? { goal } : {}), ...reviewerInputs(review) });
|
|
18
|
+
return runReviewer(cwd, base, config, undefined, undefined, { trigger: 'review', full, choice, ...(goal !== undefined ? { goal } : {}), ...(orchestrator !== undefined ? { orchestrator } : {}), ...reviewerInputs(review) });
|
|
19
19
|
}
|
|
20
20
|
/** Verified should-fixes shown in full before the rest fold into a count. */
|
|
21
21
|
const ADVISORY_SHOWN = 5;
|
|
@@ -75,7 +75,7 @@ function printMode(result) {
|
|
|
75
75
|
if (!mode)
|
|
76
76
|
return;
|
|
77
77
|
if (mode.asked !== mode.ran || mode.degraded || mode.escalation) {
|
|
78
|
-
const why = [mode.degraded, mode.escalation].filter(Boolean).join('; ');
|
|
78
|
+
const why = [mode.degraded, mode.escalation, mode.specialists?.fallback].filter(Boolean).join('; ');
|
|
79
79
|
console.log(chalk.yellow(` ${mode.asked} asked (${mode.source}), ${mode.ran} ran${why ? `: ${why}` : ''}`));
|
|
80
80
|
}
|
|
81
81
|
for (const line of mode.refused ?? [])
|
package/dist/commands/review.js
CHANGED
|
@@ -62,7 +62,7 @@ export async function reviewCommand(cwd, options = {}) {
|
|
|
62
62
|
if (options.base)
|
|
63
63
|
recordPrCatches(cwd, result.findings);
|
|
64
64
|
const receipt = receiptFor(cwd, diff ?? changeDiff(cwd, source), config, !!options.independent);
|
|
65
|
-
const reviewer = options.reviewer ? await reviewerFor(cwd, reviewerBase(cwd, options.base), config, !!options.full, { ...(options.single ? { mode: 'single' } : {}), ...(options.panel !== undefined ? { panel: options.panel } : {}) }, result, options.goal) : undefined;
|
|
65
|
+
const reviewer = options.reviewer ? await reviewerFor(cwd, reviewerBase(cwd, options.base), config, !!options.full, { ...(options.single ? { mode: 'single' } : {}), ...(options.panel !== undefined ? { panel: options.panel } : {}) }, result, options.goal, options.orchestrator) : undefined;
|
|
66
66
|
const checked = options.json ? whatWasChecked(cwd, options, trustedRef, result) : undefined;
|
|
67
67
|
await print(result, options, receipt, reviewer, { cwd, scope: scopeOf(options), commits: commitsOf(cwd, options.base), ms: Date.now() - started }, checked, goalReport(goal, goalDescription, result));
|
|
68
68
|
if (!isDeep && !receipt && !options.ci && !options.json && !options.githubSummary) {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* lesson's subject prefix). Counts that Rigour cannot know here are null, never 0: PR catches
|
|
8
8
|
* recorded on another machine (CI) never reach this one.
|
|
9
9
|
*/
|
|
10
|
-
import { type AgentEvent, type LessonRecord, type ReviewLesson, type Story } from '@rigour-labs/core';
|
|
10
|
+
import { type OutcomeMetrics, type AgentEvent, type LessonRecord, type ReviewLesson, type Story } from '@rigour-labs/core';
|
|
11
11
|
export interface LessonJourney {
|
|
12
12
|
id: string;
|
|
13
13
|
text: string;
|
|
@@ -47,6 +47,8 @@ export interface StudioLearning {
|
|
|
47
47
|
reachedPr: number | null;
|
|
48
48
|
}>;
|
|
49
49
|
prRecorded: boolean;
|
|
50
|
+
/** What happened after merges this checkout read (core outcomes/metrics.ts); absent before `rigour outcomes` has run. */
|
|
51
|
+
outcomes?: OutcomeMetrics;
|
|
50
52
|
}
|
|
51
53
|
export declare function buildLearning(input: {
|
|
52
54
|
now: Date;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* lesson's subject prefix). Counts that Rigour cannot know here are null, never 0: PR catches
|
|
8
8
|
* recorded on another machine (CI) never reach this one.
|
|
9
9
|
*/
|
|
10
|
-
import { decideLesson, fixLessonPrefix, listKnowledgeLessons, readLessons } from '@rigour-labs/core';
|
|
10
|
+
import { decideLesson, fixLessonPrefix, localOutcomeMetrics, pendingDecision, listKnowledgeLessons, readLessons } from '@rigour-labs/core';
|
|
11
11
|
import { personOf } from './git-identity.js';
|
|
12
12
|
import { checkoutRoots, eventsAcross, storiesAcross } from './studio-checkouts.js';
|
|
13
13
|
const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -78,9 +78,7 @@ function learnedFromPoints(points) {
|
|
|
78
78
|
* or a candidate with a later fix on its lines (promote it, or dismiss the evidence).
|
|
79
79
|
*/
|
|
80
80
|
function decisionFor(lesson) {
|
|
81
|
-
|
|
82
|
-
return { canDecide: false };
|
|
83
|
-
const last = lesson.evidence.filter(e => e.kind === 'demoted' || e.kind === 'lines' || e.kind === 'reclassified' || e.kind === 'accepted' || e.kind === 'rejected' || e.kind === 'dismissed').at(-1);
|
|
81
|
+
const last = pendingDecision(lesson);
|
|
84
82
|
if (last?.kind === 'reclassified')
|
|
85
83
|
return { canDecide: true, reclassified: { detail: last.detail ?? '', evidence: lesson.evidence.filter(e => e.kind === 'outcome' || e.kind === 'lines').map(e => e.detail ?? '').filter(Boolean) } };
|
|
86
84
|
if (last?.kind === 'demoted')
|
|
@@ -103,7 +101,9 @@ export function decideReviewLesson(cwd, body) {
|
|
|
103
101
|
}
|
|
104
102
|
export async function loadLearning(cwd, now = new Date(), weeks = WEEKS) {
|
|
105
103
|
const roots = checkoutRoots(cwd);
|
|
106
|
-
|
|
104
|
+
const learning = buildLearning({ now, lessons: await listKnowledgeLessons(cwd), reviewLessons: readLessons(cwd), stories: storiesAcross(roots), events: eventsAcross(roots), weeks });
|
|
105
|
+
const outcomes = localOutcomeMetrics(cwd);
|
|
106
|
+
return outcomes ? { ...learning, outcomes } : learning;
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
109
|
* "Fixed before: Credential header follows redirects (semantic-bugs). The header…" → the defect,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/cli",
|
|
3
|
-
"version": "6.9.0-rc.
|
|
3
|
+
"version": "6.9.0-rc.5",
|
|
4
4
|
"description": "Code review that happens while your agent writes: deterministic checks on the lines it changed, quiet unless it can prove a defect, and learning from your team's fixes and dismissals. Works with Claude Code, Cursor, Codex, Cline and Windsurf.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.13"
|
|
@@ -59,13 +59,13 @@
|
|
|
59
59
|
"inquirer": "9.2.16",
|
|
60
60
|
"ora": "^8.0.1",
|
|
61
61
|
"yaml": "^2.8.2",
|
|
62
|
-
"@rigour-labs/core": "6.9.0-rc.
|
|
62
|
+
"@rigour-labs/core": "6.9.0-rc.5"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/fs-extra": "^11.0.4",
|
|
66
66
|
"@types/inquirer": "9.0.7",
|
|
67
67
|
"@types/node": "^25.0.3",
|
|
68
|
-
"@rigour-labs/studio": "6.9.0-rc.
|
|
68
|
+
"@rigour-labs/studio": "6.9.0-rc.5"
|
|
69
69
|
},
|
|
70
70
|
"scripts": {
|
|
71
71
|
"build": "tsc && pnpm bundle-studio",
|