@rigour-labs/cli 6.14.0-rc.3 → 7.0.0-rc.10
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 +11 -1
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +23 -2
- package/dist/commands/hooks-git.js +4 -2
- package/dist/commands/hooks-push.js +6 -1
- package/dist/commands/hooks-stop.js +9 -2
- package/dist/commands/hooks.d.ts +2 -0
- package/dist/commands/hooks.js +19 -6
- package/dist/commands/learn-reviews.d.ts +2 -0
- package/dist/commands/learn-reviews.js +22 -3
- package/dist/commands/profile.js +15 -3
- package/dist/commands/repo-setup.js +12 -10
- package/dist/commands/report-only.d.ts +13 -0
- package/dist/commands/report-only.js +45 -0
- package/dist/commands/semantic.js +31 -5
- package/dist/commands/setup.d.ts +4 -0
- package/dist/commands/setup.js +100 -14
- package/dist/commands/studio-learning.d.ts +24 -0
- package/dist/commands/studio-learning.js +27 -8
- package/dist/commands/team.js +8 -2
- package/dist/commands/uninstall.js +2 -1
- package/package.json +3 -3
- package/studio-dist/assets/index-Ciq07cxl.css +1 -0
- package/studio-dist/assets/index-CmWtilgA.js +166 -0
- package/studio-dist/index.html +2 -2
- package/studio-dist/assets/index-UWYx4t8T.css +0 -1
- package/studio-dist/assets/index-pu9kGWhF.js +0 -166
package/dist/commands/setup.js
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* at user level, guarded so they run only where Rigour is switched on; this repository is switched
|
|
6
6
|
* on inside its git directory (personal.ts). For trying Rigour, or using it alone.
|
|
7
7
|
*
|
|
8
|
-
* Team (`--team
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Team (`--team`): rigour.yml, the project's agent hooks and its MCP server are written into the
|
|
9
|
+
* repository, so everyone who clones gets the same gate. Agent instruction files only with
|
|
10
|
+
* `--instructions`. Only `--team` ever writes into the working tree: in a repository that commits its
|
|
11
|
+
* own Rigour hooks, a plain `rigour setup` leaves every file as committed (the team's install runs).
|
|
11
12
|
*
|
|
12
13
|
* Both install semantic search once per machine (semantic.ts) unless --no-semantic, end with the
|
|
13
14
|
* same checks as `rigour doctor`, and are taken back out by `rigour uninstall`.
|
|
@@ -18,22 +19,40 @@ import fs from 'fs';
|
|
|
18
19
|
import path from 'path';
|
|
19
20
|
import { printRepoSetup } from './doctor.js';
|
|
20
21
|
import { installGitPushHook } from './hooks-git.js';
|
|
21
|
-
import { hooksInitCommand, installMachineHooks, pinnedCliCommand } from './hooks.js';
|
|
22
|
+
import { hooksInitCommand, installMachineHooks, pinnedCliCommand, projectHookPaths } from './hooks.js';
|
|
22
23
|
import { initCommand, registerProjectMcp, resolveMCPServerConfig } from './init.js';
|
|
23
24
|
import { writeAgentInstructions } from './init-handshake.js';
|
|
24
25
|
import { disableHere, enableHere, registerUserMcp } from './personal.js';
|
|
25
|
-
import { applyConfigMigrations } from './setup-migrations.js';
|
|
26
|
+
import { applyConfigMigrations, isOldEditHook } from './setup-migrations.js';
|
|
27
|
+
import { getCliVersion } from '../utils/cli-version.js';
|
|
26
28
|
import { setupSemantic } from './semantic.js';
|
|
29
|
+
import { setReportOnlyHere, TEAM_REQUIRES_BLOCKING } from './report-only.js';
|
|
30
|
+
import yaml from 'yaml';
|
|
27
31
|
const AGENT_NAME = { claude: 'Claude Code', cursor: 'Cursor', cline: 'Cline', windsurf: 'Windsurf' };
|
|
28
32
|
export async function setupCommand(cwd = process.cwd(), options = {}) {
|
|
29
|
-
const team = options.team
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
const team = !!options.team;
|
|
34
|
+
const committed = team ? [] : committedRigourHooks(cwd);
|
|
35
|
+
const mode = team ? 'team: committed to this repository' : committed.length ? "the team's install: nothing in your working tree" : 'personal: nothing in your working tree';
|
|
36
|
+
console.log(chalk.bold.cyan(`\nRigour setup (${mode})\n`));
|
|
37
|
+
if (team) {
|
|
38
|
+
const touched = trackedFiles(cwd, ['rigour.yml', ...rigourInstallPaths()]);
|
|
39
|
+
if (touched.length)
|
|
40
|
+
console.log(chalk.yellow(`Writes into these committed files (commit the result so everyone gets it): ${touched.join(', ')}\n`));
|
|
32
41
|
await teamSetup(cwd, options);
|
|
33
|
-
|
|
42
|
+
}
|
|
43
|
+
else if (committed.length)
|
|
44
|
+
await leaveTeamInstall(cwd, committed, !!options.instructions);
|
|
45
|
+
else {
|
|
46
|
+
if (trackedFiles(cwd, ['rigour.yml']).length)
|
|
47
|
+
console.log(chalk.dim('This repository commits rigour.yml; `rigour setup --team` commits hooks for everyone. This setup is yours alone.\n'));
|
|
34
48
|
await personalSetup(cwd, options.brief !== false);
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
}
|
|
50
|
+
// rigour.yml is the team's file: only --team changes it (doctor's checks below name what is due).
|
|
51
|
+
if (team)
|
|
52
|
+
for (const change of applyConfigMigrations(cwd))
|
|
53
|
+
console.log(chalk.green(`✔ Migrated ${change}`));
|
|
54
|
+
if (options.reportOnly || options.block)
|
|
55
|
+
setGateMode(cwd, team, !!options.reportOnly);
|
|
37
56
|
if (options.semantic !== false)
|
|
38
57
|
await setupSemantic(cwd);
|
|
39
58
|
await printRepoSetup(cwd);
|
|
@@ -75,7 +94,74 @@ async function teamSetup(cwd, options) {
|
|
|
75
94
|
else
|
|
76
95
|
await initCommand(cwd, { instructions: options.instructions, brief: options.brief !== false });
|
|
77
96
|
}
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Report-only or blocking, for this repository: the person's switch in the git directory, and with --team the team's
|
|
99
|
+
* in rigour.yml (`hooks.report_only`: true, or false with --block), so everyone who clones starts the same way.
|
|
100
|
+
*/
|
|
101
|
+
function setGateMode(cwd, team, report) {
|
|
102
|
+
setReportOnlyHere(cwd, report);
|
|
103
|
+
const file = path.join(cwd, 'rigour.yml');
|
|
104
|
+
if (!team && report && fs.existsSync(file) && yaml.parse(fs.readFileSync(file, 'utf8'))?.hooks?.report_only === false) {
|
|
105
|
+
console.log(chalk.yellow(TEAM_REQUIRES_BLOCKING));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (team && fs.existsSync(file)) {
|
|
109
|
+
const doc = yaml.parseDocument(fs.readFileSync(file, 'utf8'));
|
|
110
|
+
// A team that turns blocking on means block: an explicit false, the floor no person's switch lowers.
|
|
111
|
+
doc.setIn(['hooks', 'report_only'], report);
|
|
112
|
+
fs.writeFileSync(file, doc.toString());
|
|
113
|
+
}
|
|
114
|
+
console.log(report
|
|
115
|
+
? chalk.green(`✔ Report only: the edit check, the stop review and the push gate say what they would block and block nothing${team ? ' (hooks.report_only in rigour.yml)' : ''}. A real credential is still refused. Make them block: rigour setup${team ? ' --team' : ''} --block`)
|
|
116
|
+
: chalk.green('✔ The edit check, the stop review and the push gate block again.'));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The team's install, as committed here: tracked project hook and MCP files that run Rigour. Their agents already run
|
|
120
|
+
* them, so a person's setup changes none of them, and personal hooks beside them would run every check twice.
|
|
121
|
+
*/
|
|
122
|
+
async function leaveTeamInstall(cwd, committed, instructions) {
|
|
123
|
+
disableHere(cwd, false);
|
|
124
|
+
const text = committed.map(file => fs.readFileSync(path.join(cwd, file), 'utf8')).join('\n');
|
|
125
|
+
const pins = [...new Set([...text.matchAll(/@rigour-labs\/cli@([0-9A-Za-z.-]+)/g)].map(m => m[1]))];
|
|
126
|
+
const installed = getCliVersion();
|
|
127
|
+
const stale = pins.filter(pin => pin !== installed);
|
|
128
|
+
console.log(chalk.green('✔ This repository commits its own Rigour install, and your agents run it. Left as committed:'));
|
|
129
|
+
for (const file of committed)
|
|
130
|
+
console.log(chalk.dim(` ${file}`));
|
|
131
|
+
if (stale.length) {
|
|
132
|
+
// The committed pin is what the agents run: name it, and what it costs, since this setup does not move it.
|
|
133
|
+
const caveat = isOldEditHook(text) ? ' (its edit hook checks nothing)' : '';
|
|
134
|
+
console.log(chalk.yellow(` These hooks pin Rigour ${pins.join(', ')}; they keep running that version${caveat} until someone runs \`rigour setup --team\` and commits the result. Installed here: ${installed}.`));
|
|
135
|
+
}
|
|
136
|
+
else
|
|
137
|
+
console.log(chalk.dim(' To change it for everyone: rigour setup --team, then commit the result.'));
|
|
138
|
+
// This clone's own: git's pre-push hook lives in the git directory, never in the working tree.
|
|
139
|
+
const push = installGitPushHook(cwd, pinnedCliCommand(), { workingTree: false });
|
|
140
|
+
if (push.action === 'managed elsewhere')
|
|
141
|
+
console.log(chalk.yellow(`Git pre-push hooks are managed outside this repository (${push.path}); add: ${pinnedCliCommand()} hooks push --git "$@" || exit $?`));
|
|
142
|
+
else if (push.action !== 'no repository')
|
|
143
|
+
console.log(chalk.green(`✔ Push gate: git's pre-push hook (${push.action}); the only thing installed here`));
|
|
144
|
+
// Asked for by name: written only where the repository has none, so no committed file changes.
|
|
145
|
+
if (instructions)
|
|
146
|
+
await writeAgentInstructions(cwd);
|
|
147
|
+
}
|
|
148
|
+
/** Where a team's Rigour install lives in the repository: the project hook and MCP configs. */
|
|
149
|
+
function rigourInstallPaths() {
|
|
150
|
+
return [...projectHookPaths(), '.mcp.json', '.cursor/mcp.json'];
|
|
151
|
+
}
|
|
152
|
+
/** Which of these paths git tracks here. */
|
|
153
|
+
function trackedFiles(cwd, paths) {
|
|
154
|
+
const tracked = spawnSync('git', ['ls-files', '--', ...paths], { cwd, encoding: 'utf8' });
|
|
155
|
+
return tracked.status === 0 ? tracked.stdout.split('\n').filter(Boolean) : [];
|
|
156
|
+
}
|
|
157
|
+
/** The tracked files of a team's Rigour install: project hook and MCP configs that name Rigour. */
|
|
158
|
+
function committedRigourHooks(cwd) {
|
|
159
|
+
return trackedFiles(cwd, rigourInstallPaths()).filter(file => {
|
|
160
|
+
try {
|
|
161
|
+
return /rigour/i.test(fs.readFileSync(path.join(cwd, file), 'utf8'));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
81
167
|
}
|
|
@@ -50,6 +50,30 @@ export interface LessonJourney {
|
|
|
50
50
|
};
|
|
51
51
|
/** A candidate only review bots raised, nothing waiting on a person (core quietBotCandidate): hidden until a person asks to see bot points. */
|
|
52
52
|
fromBots?: true;
|
|
53
|
+
/** A person's last decision on a review lesson (promoted, rejected, not a rule) can be taken back to a candidate (core takeBackable). */
|
|
54
|
+
canTakeBack?: true;
|
|
55
|
+
/**
|
|
56
|
+
* Teammates' decisions on a review lesson, received from the team database (core team-decisions.ts): each with the
|
|
57
|
+
* teammate's display name and when the team database got it; and, when a later team decision settled the lesson
|
|
58
|
+
* against this person's own, both, with why this machine does not share theirs (`yoursOnly`) when it does not.
|
|
59
|
+
*/
|
|
60
|
+
team?: {
|
|
61
|
+
decisions: Array<{
|
|
62
|
+
kind: string;
|
|
63
|
+
name: string;
|
|
64
|
+
at: string;
|
|
65
|
+
detail?: string;
|
|
66
|
+
}>;
|
|
67
|
+
overruled?: {
|
|
68
|
+
yours: string;
|
|
69
|
+
team: {
|
|
70
|
+
kind: string;
|
|
71
|
+
name: string;
|
|
72
|
+
at: string;
|
|
73
|
+
};
|
|
74
|
+
yoursOnly?: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
53
77
|
}
|
|
54
78
|
export interface StudioLearning {
|
|
55
79
|
lessons: LessonJourney[];
|
|
@@ -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 { acceptSuggestedText, scopeLesson, decideCompiledCheck, decideLesson, fixLessonPrefix, proposeCompiledChecks, readCompiledChecks, suspension, localOutcomeMetrics, pendingDecision, quietBotCandidate, listKnowledgeLessons, readLessons } from '@rigour-labs/core';
|
|
10
|
+
import { acceptSuggestedText, scopeLesson, decideCompiledCheck, decideLesson, fixLessonPrefix, proposeCompiledChecks, readCompiledChecks, suspension, localOutcomeMetrics, pendingDecision, quietBotCandidate, listKnowledgeLessons, readLessons, lastDecision, takeBackable, readTeamDecisionCache } from '@rigour-labs/core';
|
|
11
11
|
import { decider } from './git-identity.js';
|
|
12
12
|
import { checkoutRoots, eventsAcross, storiesAcross } from './studio-checkouts.js';
|
|
13
13
|
const WEEK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -53,6 +53,7 @@ export function buildLearning(input) {
|
|
|
53
53
|
...(l.suggestedText ? { suggestedText: { text: l.suggestedText, why: l.suggestedWhy ?? 'reworded' } } : {}),
|
|
54
54
|
reach: { scope: l.scope ?? 'file', hasFile: !!l.file },
|
|
55
55
|
...(quietBotCandidate(l) ? { fromBots: true } : {}),
|
|
56
|
+
...(takeBackable(l) ? { canTakeBack: true } : {}),
|
|
56
57
|
})),
|
|
57
58
|
// Lessons back to a candidate when outcomes stopped promoting come first: a person decides each once.
|
|
58
59
|
].sort((a, b) => Number(!!b.reclassified) - Number(!!a.reclassified) || b.learnedAt.localeCompare(a.learnedAt))
|
|
@@ -71,6 +72,18 @@ export function buildLearning(input) {
|
|
|
71
72
|
});
|
|
72
73
|
return { lessons, weeks, prRecorded };
|
|
73
74
|
}
|
|
75
|
+
/** A review lesson's team decisions for Studio, and whether a later one settled it against this person's own. */
|
|
76
|
+
function teamOn(lesson, sharing) {
|
|
77
|
+
const decisions = lesson.evidence.filter(e => e.team).map(e => ({ kind: e.kind ?? 'point', name: e.team.name, at: e.team.receivedAt, ...(e.detail ? { detail: e.detail } : {}) }));
|
|
78
|
+
if (decisions.length === 0)
|
|
79
|
+
return {};
|
|
80
|
+
const settled = lastDecision(lesson);
|
|
81
|
+
const yours = lesson.evidence.filter(e => !e.team && e.author && (e.kind === 'accepted' || e.kind === 'rejected')).at(-1);
|
|
82
|
+
const overruled = settled?.team && yours?.kind && yours.kind !== settled.kind
|
|
83
|
+
? { yours: yours.kind, team: { kind: settled.kind ?? 'accepted', name: settled.team.name, at: settled.team.receivedAt }, ...(sharing && !sharing.shares && sharing.reason ? { yoursOnly: sharing.reason } : {}) }
|
|
84
|
+
: undefined;
|
|
85
|
+
return { team: { decisions, ...(overruled ? { overruled } : {}) } };
|
|
86
|
+
}
|
|
74
87
|
function learnedFromPoints(points) {
|
|
75
88
|
const authors = [...new Set(points.map(e => e.author).filter(Boolean))];
|
|
76
89
|
return `At PR ${[...new Set(points.map(e => `#${e.pr}`))].join(', ')}${authors.length ? `, from ${authors.join(', ')}` : ''}`;
|
|
@@ -78,7 +91,8 @@ function learnedFromPoints(points) {
|
|
|
78
91
|
/**
|
|
79
92
|
* What a person can decide on a review lesson, from the last of its evidence and decisions: taken back by evidence
|
|
80
93
|
* (promote it again, or drop it), back to a candidate when outcomes stopped promoting (promote it again, or dismiss),
|
|
81
|
-
*
|
|
94
|
+
* a candidate with a later fix on its lines (promote it, or dismiss the evidence), or any other candidate (promote it,
|
|
95
|
+
* or reject it): a person confirming a review point is how it becomes a lesson.
|
|
82
96
|
*/
|
|
83
97
|
function decisionFor(lesson) {
|
|
84
98
|
const last = pendingDecision(lesson);
|
|
@@ -88,12 +102,13 @@ function decisionFor(lesson) {
|
|
|
88
102
|
return { canDecide: true, takenBack: { detail: last.detail ?? '', prs: lesson.evidence.filter(e => e.kind === 'against').map(e => e.pr), at: last.at ?? '' } };
|
|
89
103
|
if (last?.kind === 'lines')
|
|
90
104
|
return { canDecide: true, suggested: { detail: last.detail ?? '', pr: last.pr, at: last.at ?? '' } };
|
|
91
|
-
return { canDecide:
|
|
105
|
+
return { canDecide: lesson.state === 'candidate' };
|
|
92
106
|
}
|
|
93
107
|
/** The reason a person gave in Studio, else that they decided there. */
|
|
94
108
|
function studioWhy(why) {
|
|
95
109
|
return typeof why === 'string' && why.trim() ? why.trim() : 'decided in Studio';
|
|
96
110
|
}
|
|
111
|
+
const REVIEW_DECISIONS = new Set(['accepted', 'rejected', 'dismissed', 'not-a-rule', 'took-back']);
|
|
97
112
|
/** A person's decision on a review lesson from Studio, recorded as `rigour learn-reviews --promote / --reject` records it: with their git email, and final. */
|
|
98
113
|
export function decideReviewLesson(cwd, body) {
|
|
99
114
|
const { id, decision, why } = (body ?? {});
|
|
@@ -103,8 +118,8 @@ export function decideReviewLesson(cwd, body) {
|
|
|
103
118
|
return rewordFromStudio(cwd, id);
|
|
104
119
|
if (decision === 'scope')
|
|
105
120
|
return scopeFromStudio(cwd, id, body.to, why);
|
|
106
|
-
if (decision
|
|
107
|
-
throw new Error('decision is accepted, rejected, dismissed, reworded or scope');
|
|
121
|
+
if (!REVIEW_DECISIONS.has(decision))
|
|
122
|
+
throw new Error('decision is accepted, rejected, dismissed, not-a-rule, took-back, reworded or scope');
|
|
108
123
|
const lesson = decideLesson(cwd, id, decision, decider(cwd), studioWhy(why));
|
|
109
124
|
if (!lesson)
|
|
110
125
|
throw new Error(`no review lesson ${id}`);
|
|
@@ -151,10 +166,14 @@ export function proposeChecksFromStudio(cwd) {
|
|
|
151
166
|
}
|
|
152
167
|
export async function loadLearning(cwd, now = new Date(), weeks = WEEKS) {
|
|
153
168
|
const roots = checkoutRoots(cwd);
|
|
154
|
-
const
|
|
169
|
+
const reviewLessons = readLessons(cwd);
|
|
170
|
+
const built = buildLearning({ now, lessons: await listKnowledgeLessons(cwd), reviewLessons, stories: storiesAcross(roots), events: eventsAcross(roots), weeks });
|
|
171
|
+
// Teammates' decisions (team database), with why this machine does not share its person's when it does not.
|
|
172
|
+
const sharing = readTeamDecisionCache(cwd)?.sharing;
|
|
173
|
+
const byId = new Map(reviewLessons.map(l => [l.id, l]));
|
|
174
|
+
const learning = { ...built, lessons: built.lessons.map(j => (byId.has(j.id) ? { ...j, ...teamOn(byId.get(j.id), sharing) } : j)) };
|
|
155
175
|
const outcomes = localOutcomeMetrics(cwd);
|
|
156
|
-
const
|
|
157
|
-
const withChecks = { ...learning, compiled: readCompiledChecks(cwd).map(c => ({ ...c, ...(suspension(c, lessons) ? { suspended: suspension(c, lessons) } : {}) })) };
|
|
176
|
+
const withChecks = { ...learning, compiled: readCompiledChecks(cwd).map(c => ({ ...c, ...(suspension(c, byId) ? { suspended: suspension(c, byId) } : {}) })) };
|
|
158
177
|
return outcomes ? { ...withChecks, outcomes } : withChecks;
|
|
159
178
|
}
|
|
160
179
|
/**
|
package/dist/commands/team.js
CHANGED
|
@@ -98,8 +98,14 @@ teamCommand
|
|
|
98
98
|
.description("Send queued lessons from the team's repositories, and your decisions on this repository's review lessons; receive the team's")
|
|
99
99
|
.option('--dry-run', 'Report pending records without sending them')
|
|
100
100
|
.action(async (options) => {
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
try {
|
|
102
|
+
console.log(JSON.stringify(await syncTeamOutbox({ dryRun: Boolean(options.dryRun), cwd: process.cwd() }), null, 2));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
// One line, with the fix when there is one; never the database driver's stack.
|
|
106
|
+
console.error(chalk.red(`Team sync failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
107
|
+
process.exitCode = 1;
|
|
108
|
+
}
|
|
103
109
|
});
|
|
104
110
|
/** Initialize the schema, or print why it failed (with the fix) and set exit code 1. */
|
|
105
111
|
async function initializeSchemaOrReport(databaseUrl, pgvector) {
|
|
@@ -22,6 +22,7 @@ import fs from 'fs';
|
|
|
22
22
|
import path from 'path';
|
|
23
23
|
import { APPENDED_COMMENT } from './hooks-git.js';
|
|
24
24
|
import { disableHere, enabledHere, uninstallMachine } from './personal.js';
|
|
25
|
+
import { setReportOnlyHere } from './report-only.js';
|
|
25
26
|
import { isEmptyConfig, isRigourScript, readInstallRecord, unchangedSinceInstall, withoutRigour } from './install-record.js';
|
|
26
27
|
import { GITIGNORE_PATTERNS } from './init.js';
|
|
27
28
|
const CONFIGS = ['.claude/settings.json', '.cursor/hooks.json', '.windsurf/hooks.json', '.cursor/mcp.json', '.mcp.json'];
|
|
@@ -78,7 +79,7 @@ export function uninstall(cwd, options = {}) {
|
|
|
78
79
|
gitHook(cwd, act, report);
|
|
79
80
|
// A personal install: the switch inside the git directory (and its exclude line, with .rigour/ itself).
|
|
80
81
|
if (enabledHere(cwd))
|
|
81
|
-
act('switch Rigour off for this repository (personal install)', () => disableHere(cwd, !!options.all));
|
|
82
|
+
act('switch Rigour off for this repository (personal install)', () => { disableHere(cwd, !!options.all); setReportOnlyHere(cwd, false); });
|
|
82
83
|
else if (options.all && !options.dryRun)
|
|
83
84
|
disableHere(cwd, true);
|
|
84
85
|
if (options.machine)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rigour-labs/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-rc.10",
|
|
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,12 +59,12 @@
|
|
|
59
59
|
"globby": "^14.0.1",
|
|
60
60
|
"ora": "^8.0.1",
|
|
61
61
|
"yaml": "^2.8.2",
|
|
62
|
-
"@rigour-labs/core": "
|
|
62
|
+
"@rigour-labs/core": "7.0.0-rc.10"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@types/fs-extra": "^11.0.4",
|
|
66
66
|
"@types/node": "^25.0.3",
|
|
67
|
-
"@rigour-labs/studio": "
|
|
67
|
+
"@rigour-labs/studio": "7.0.0-rc.10"
|
|
68
68
|
},
|
|
69
69
|
"scripts": {
|
|
70
70
|
"build": "tsc && pnpm bundle-studio",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.st-page{max-width:1040px;margin:0 auto;padding:8px 4px 64px;color:var(--text-main)}.st-h1{margin:0;font-size:28px;font-weight:600;line-height:1.3;color:var(--text-primary)}.st-lead{margin:6px 0 0;font-size:15px;line-height:1.6;color:var(--text-dim);max-width:720px}.st-sub{font-size:13px;color:var(--text-dim)}.st-mono{font-family:IBM Plex Mono,JetBrains Mono,ui-monospace,monospace}.st-card{border:1px solid var(--border-dim);border-radius:12px;padding:20px;background:var(--bg-card)}.st-grid{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:28px;margin-top:28px}.st-stack{display:flex;flex-direction:column;gap:10px}.st-side{display:flex;flex-direction:column;gap:16px}.st-big{font-size:40px;font-weight:600;line-height:1;color:var(--text-primary)}.st-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:40px;padding:0 18px;border:1px solid var(--text-dim);border-radius:8px;background:var(--bg-card);color:var(--text-primary);font:inherit;font-size:14px;font-weight:500;cursor:pointer;transition:background .12s,border-color .12s}.st-btn:hover{border-color:var(--text-primary);background:var(--bg-accent)}.st-btn:focus-visible{outline:2px solid var(--accent-primary);outline-offset:2px}.st-btn:disabled{opacity:.45;cursor:not-allowed}.st-btn.primary{background:var(--accent-primary);border-color:var(--accent-primary);color:#fff}.st-btn.primary:hover{background:var(--accent-tertiary);border-color:var(--accent-tertiary)}.st-chip{display:inline-block;padding:3px 10px;border-radius:999px;font-size:12px;background:var(--bg-accent);color:var(--text-dim);white-space:nowrap}.st-chip.ok{background:color-mix(in srgb,var(--status-success) 14%,transparent);color:var(--status-success)}.st-chip.warn{background:color-mix(in srgb,var(--status-warning) 16%,transparent);color:var(--status-warning)}.st-chip.bad{background:color-mix(in srgb,var(--status-error) 14%,transparent);color:var(--status-error)}.st-need{border:1px solid color-mix(in srgb,var(--status-warning) 45%,transparent);border-radius:12px;padding:20px;background:color-mix(in srgb,var(--status-warning) 6%,var(--bg-card))}.st-story{display:block;width:100%;padding:18px 20px;border:1px solid var(--border-dim);border-radius:12px;background:var(--bg-card);color:inherit;font:inherit;text-align:left;cursor:pointer}.st-story:hover{border-color:var(--border-med)}.st-story-body{border:1px solid var(--border-dim);border-top:0;border-radius:0 0 12px 12px;margin-top:-10px;padding:20px;background:var(--bg-surface)}.st-code{background:var(--code-bg);border:1px solid var(--border-dim);border-radius:8px;padding:10px 0;font-size:13px;line-height:1.7;overflow-x:auto}.st-ln{display:block;padding:0 14px;white-space:pre}.st-ln.add{background:color-mix(in srgb,var(--status-success) 12%,transparent)}.st-ln.del{background:color-mix(in srgb,var(--status-error) 12%,transparent)}.st-steps{display:flex;gap:24px}.st-step{flex:1;display:flex;gap:12px;align-items:flex-start;font-size:14px}.st-num{flex:none;width:28px;height:28px;border-radius:50%;background:var(--bg-accent);color:var(--accent-primary);display:flex;align-items:center;justify-content:center;font-weight:600}.st-journey{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));margin-top:16px;border:1px solid var(--border-dim);border-radius:10px;overflow:hidden}.st-journey>div{padding:12px 14px}.st-journey>div+div{border-left:1px solid var(--border-dim)}.st-bars{display:flex;gap:28px;align-items:flex-end;height:120px;margin-top:18px}.st-bar{flex:1;display:flex;flex-direction:column;align-items:center;gap:6px}.st-bar span{width:100%;max-width:72px;border-radius:6px 6px 0 0;background:var(--accent-primary)}.st-row{display:flex;gap:16px;align-items:center}.st-empty{border:1px dashed var(--border-med);border-radius:12px;padding:24px;color:var(--text-dim);font-size:14px;line-height:1.6}.st-inline-code{font-size:.9em;padding:1px 5px;border-radius:4px;background:var(--bg-accent)}.st-table{width:100%;border-collapse:collapse;font-size:14px}.st-table th{text-align:left;font-weight:500;font-size:12px;color:var(--text-dim);padding:8px 20px;border-bottom:1px solid var(--border-dim);white-space:nowrap}.st-table td{padding:10px 20px;border-bottom:1px solid var(--border-dim);font-variant-numeric:tabular-nums}.st-table tr:last-child td{border-bottom:0}@media(max-width:820px){.st-grid{grid-template-columns:minmax(0,1fr)}.st-steps,.st-row{flex-direction:column;align-items:flex-start}.st-journey{grid-template-columns:repeat(2,minmax(0,1fr))}}.st-seg{display:inline-flex;padding:2px;gap:2px;border:1px solid var(--text-dim);border-radius:8px;background:var(--bg-card)}.st-seg button{min-height:30px;padding:0 12px;border:0;border-radius:6px;background:transparent;color:var(--text-dim);font:inherit;font-size:13px;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s}.st-seg button:hover:not(:disabled):not(.on){background:var(--bg-accent);color:var(--text-primary)}.st-seg button.on{background:var(--accent-primary);color:#fff;font-weight:500}.st-seg button:focus-visible{outline:2px solid var(--accent-primary);outline-offset:1px}.st-seg button:disabled{cursor:not-allowed}.st-seg button:disabled:not(.on){opacity:.5}.st-setting{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px 24px;align-items:start}.st-setting-controls{display:grid;grid-template-columns:auto auto;gap:8px 12px;align-items:center;justify-items:start}@media(max-width:760px){.st-setting{grid-template-columns:1fr}}:root{--bg-main: #f7f8fa;--bg-sidebar: #ffffff;--bg-header: #ffffff;--bg-card: #ffffff;--bg-accent: #eef0f4;--bg-surface: #f3f4f7;--border-dim: #e3e5ea;--border-med: #d2d6de;--border-color: #e3e5ea;--text-main: #14161a;--text-dim: #5e6573;--text-vibrant: #000000;--text-primary: #14161a;--accent-primary: #4f46e5;--accent-secondary: #6366f1;--accent-tertiary: #3730a3;--accent-glow: rgba(79, 70, 229, .1);--status-ready: #17663f;--status-success: #17663f;--status-error: #a12a2a;--status-warning: #8a5300;--code-bg: #f1f2f5;--gradient-accent: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%)}[data-theme=dark]{--bg-main: #050506;--bg-sidebar: rgba(10, 10, 15, .72);--bg-header: rgba(10, 10, 15, .65);--bg-card: rgba(255, 255, 255, .04);--bg-accent: rgba(255, 255, 255, .06);--bg-surface: rgba(13, 13, 18, .85);--border-dim: rgba(255, 255, 255, .08);--border-med: rgba(255, 255, 255, .12);--border-color: rgba(255, 255, 255, .08);--text-main: #ededeef2;--text-dim: #8a8f98;--text-vibrant: #ffffff;--text-primary: #f8fafc;--accent-primary: #818cf8;--accent-secondary: #a5b4fc;--accent-tertiary: #c7d2fe;--accent-glow: rgba(129, 140, 248, .18);--status-ready: #34d399;--status-success: #34d399;--status-error: #f87171;--status-warning: #fbbf24;--code-bg: rgba(0, 0, 0, .35);--radius-card: 16px;--easing-cinema: cubic-bezier(.16, 1, .3, 1);--gradient-accent: linear-gradient(135deg, #6366f1 0%, #818cf8 100%);--bg-cinema: linear-gradient(180deg, #0a0a0f 0%, #050506 100%)}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Fira Sans,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background:var(--bg-cinema, var(--bg-main));background-color:var(--bg-main);color:var(--text-main);-webkit-font-smoothing:antialiased;overflow:hidden;letter-spacing:-.01em}code,pre,.font-mono,.kpi-value,.overview-dl dd{font-family:Fira Code,JetBrains Mono,ui-monospace,monospace}.studio{display:flex;height:100vh;width:100vw;position:relative;isolation:isolate}.sidebar{width:190px;background-color:var(--bg-sidebar);border-right:1px solid var(--border-dim);display:flex;flex-direction:column;padding:16px;flex-shrink:0}.brand{display:flex;align-items:center;gap:12px;padding:8px 12px;margin-bottom:32px}.logo-icon{width:28px;height:28px;background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;font-size:16px}.brand span{font-weight:700;font-size:18px;color:var(--text-vibrant);letter-spacing:-.5px}.version-pill{font-size:10px;background-color:var(--bg-accent);color:var(--text-dim);padding:2px 6px;border-radius:4px;font-family:JetBrains Mono,monospace;margin-left:auto}nav{display:flex;flex-direction:column;gap:4px;flex-grow:1}.nav-item{display:flex;align-items:center;gap:12px;padding:12px;min-height:44px;border-radius:8px;color:var(--text-dim);text-decoration:none;font-size:14px;font-weight:500;background:transparent;border:none;cursor:pointer;width:100%;text-align:left;transition:all .2s ease;position:relative}.status-indicator.connecting .pulse-emitter{background:var(--status-warning)}.status-indicator.offline .pulse-emitter{background:var(--status-error);animation:none}.system-health{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:8px;margin:10px 16px 0;padding:10px;border:1px solid var(--border-dim);border-radius:12px;background:var(--bg-surface)}.system-health.loading,.system-health.unavailable{display:flex;align-items:center;min-height:52px;color:var(--text-dim)}.system-health.unavailable{color:var(--status-error)}.system-health.unavailable button{margin-left:auto;min-height:36px}.system-health.stale{border-color:var(--status-warning)}.health-item{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;min-width:0;padding:7px 8px;border-left:2px solid var(--border-med)}.health-item.ready{border-left-color:var(--status-ready)}.health-item.warming{border-left-color:var(--status-warning)}.health-item.degraded{border-left-color:var(--status-error)}.health-item>span{min-width:0}.health-item strong,.health-item small{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.health-item strong{font-size:12px}.health-item small{margin-top:2px;color:var(--text-dim);font-size:10px}.health-state{grid-column:2;color:var(--text-dim);font-size:10px;text-transform:uppercase}@media(max-width:1200px){.system-health{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:760px){.system-health{grid-template-columns:1fr 1fr}}.nav-item:hover{background-color:var(--bg-accent);color:var(--text-main)}.nav-item.active{color:var(--text-vibrant)}.sidebar-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--border-dim);display:flex;align-items:center;gap:8px}.trust-indicator{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--text-dim);padding:6px 10px;background:#10b9810d;border:1px solid rgba(16,185,129,.1);border-radius:100px;margin-right:auto}.main-content{flex-grow:1;display:flex;flex-direction:column;overflow:hidden}header{height:56px;background-color:var(--bg-header);border-bottom:1px solid var(--border-dim);display:flex;align-items:center;justify-content:space-between;padding:0 24px;flex-shrink:0}.header-right{display:flex;align-items:center;gap:20px}.project-identity{display:flex;align-items:center;gap:12px;background:var(--bg-accent);padding:4px 12px;border-radius:8px;border:1px solid var(--border-med)}.project-name{font-weight:700;font-size:14px;color:var(--text-vibrant)}.connection-status{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--text-dim);background:#22d3ee0d;padding:4px 10px;border-radius:6px;border:1px solid rgba(34,211,238,.1);font-family:JetBrains Mono,monospace}.status-indicator{display:flex;align-items:center;gap:6px;font-size:11px;font-weight:700;color:var(--status-ready)}.pulse-emitter{width:6px;height:6px;background-color:var(--status-ready);border-radius:50%;position:relative;box-shadow:0 0 10px var(--status-ready)}.pulse-emitter:after{content:"";position:absolute;width:100%;height:100%;background-color:var(--status-ready);border-radius:50%;animation:pulse 2s infinite}@keyframes pulse{0%{transform:scale(1);opacity:.8}to{transform:scale(3);opacity:0}}.view-container{flex-grow:1;padding:32px;overflow-y:auto}.health-popover{position:fixed;z-index:50;top:64px;right:18px;width:min(620px,calc(100vw - 36px));padding:12px;border:1px solid var(--border-med);border-radius:13px;background:color-mix(in srgb,var(--bg-sidebar) 96%,transparent);box-shadow:0 24px 60px #0000006b;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.health-popover-title{display:flex;align-items:center;justify-content:space-between;padding:2px 4px 10px}.health-popover-title strong{font-size:12px}.health-popover-title button{width:30px;height:30px;display:grid;place-items:center;border:1px solid var(--border-dim);border-radius:8px;color:var(--text-dim);background:var(--bg-accent);cursor:pointer}.health-popover .system-health{margin:0;grid-template-columns:repeat(2,minmax(0,1fr))}.connection-status{cursor:pointer}.empty-state{text-align:center;display:flex;flex-direction:column;align-items:center;gap:16px;max-width:320px}.empty-state h3{font-weight:600;color:var(--text-vibrant)}.empty-state p{font-size:14px;color:var(--text-dim);line-height:1.5}.folder-icon{color:var(--accent-primary);opacity:.8}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:var(--bg-main)}::-webkit-scrollbar-thumb{background:var(--border-med);border-radius:10px}::-webkit-scrollbar-thumb:hover{background:var(--text-dim)}.theme-toggle{background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px;cursor:pointer;color:var(--text-dim);display:flex;align-items:center;transition:all .2s}.theme-toggle:hover{color:var(--text-main);border-color:var(--border-med)}.refresh-btn{background:var(--bg-accent);border:1px solid var(--border-dim);border-radius:8px;padding:8px;cursor:pointer;color:var(--text-dim);transition:all .2s}.refresh-btn:hover{color:var(--accent-primary);border-color:var(--accent-primary)}.refresh-btn .spinning{animation:spin 1s linear infinite}.learned-rules{margin-top:16px;display:flex;flex-direction:column;gap:10px}.learned-rules h4{font-size:12px;color:var(--text-dim);text-transform:uppercase}@keyframes rotate-spark{0%{transform:rotate(0) scale(1)}50%{transform:rotate(180deg) scale(1.2)}to{transform:rotate(360deg) scale(1)}}.spinning{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes pulse-alert{0%,to{opacity:1}50%{opacity:.6}}@keyframes spin-anim{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.overview-banner{display:flex;gap:12px;padding:12px 14px;border-radius:8px;border:1px solid rgba(217,119,6,.35);background:#d9770614;font-size:.85rem}.overview-banner strong{display:block;margin-bottom:2px}.overview-banner p{color:var(--text-dim)}.readonly-note{align-items:center;padding:8px 12px}.readonly-note span{flex:1}.readonly-note-close{background:none;border:0;color:var(--text-dim);cursor:pointer;display:flex;padding:2px}@keyframes blob-drift{0%{transform:translateZ(0) scale(1)}to{transform:translate3d(40px,24px,0) scale(1.08)}}@media(prefers-reduced-motion:reduce){.ambient-blob,.pulse-emitter{animation:none!important}}.studio>.sidebar,.studio>.main-content{position:relative;z-index:1}.glass-shell,.glass-card{backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid var(--border-dim);border-radius:var(--radius-card, 16px)}[data-theme=dark] .sidebar.glass-shell{background:var(--bg-sidebar);border-right:1px solid var(--border-dim);box-shadow:inset -1px 0 #22d3ee0a}[data-theme=dark] header.glass-shell{background:var(--bg-header);border-bottom:1px solid var(--border-dim)}[data-theme=dark] .glass-card{background:var(--bg-card)}.nav-item{cursor:pointer;transition:background .2s var(--easing-cinema, ease),color .2s ease,transform .16s ease}.nav-item:active{transform:scale(.98)}.nav-item:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--accent-primary);outline-offset:2px}[data-theme=dark] .nav-item.active{box-shadow:0 0 0 1px #22d3ee40,0 0 24px #22d3ee14}.project-version-pill{font-size:.72rem;color:var(--accent-secondary);border:1px solid var(--border-dim);border-radius:999px;padding:1px 8px;font-family:Fira Code,monospace}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@media(max-width:920px){.view-container{padding:20px}.main-content>header{gap:8px;padding:0 14px}.project-identity{min-width:0;gap:8px}.project-name{max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.header-right{gap:8px}}
|