@rigour-labs/cli 7.0.0-rc.4 → 7.0.0-rc.6

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 CHANGED
@@ -406,7 +406,9 @@ program
406
406
  .option('--include-bots', 'With --list: also list candidates only review bots raised (hidden by default)')
407
407
  .option('--promote <id>', 'Accept a candidate as a lesson (recorded as your decision)')
408
408
  .option('--reject <id>', 'Reject a candidate or lesson: the judges are told this team decided against it')
409
- .option('--why <text>', 'Why, recorded with --promote or --reject')
409
+ .option('--not-a-rule <id>', 'Drop a candidate that is a one-off for its pull request, not a practice either way: never served, the judges are told nothing')
410
+ .option('--take-back <id>', 'Take back the last decision on a lesson (promote, reject or not a rule): a candidate again; the decision stays in its history')
411
+ .option('--why <text>', 'Why, recorded with --promote, --reject, --not-a-rule or --take-back')
410
412
  .option('--scope <id>', 'Set how far a lesson reaches, with --to: its file, its folder, or every change (a team standard); recorded as your decision')
411
413
  .option('--to <reach>', 'With --scope: file, folder or repo')
412
414
  .option('--use-wording <id>', 'Take the corrected wording a newer version suggests for a lesson you decided (the old wording is kept as evidence)')
@@ -151,7 +151,7 @@ export async function printRepoSetup(cwd) {
151
151
  // Settings an older Rigour wrote that no longer mean what they say: loud, since nothing else reports them.
152
152
  for (const migration of configMigrations(cwd)) {
153
153
  console.log(` ${chalk.red('✘')} ${chalk.red(migration.problem)}`);
154
- console.log(chalk.dim(` fix: ${migration.automatic ? 'rigour setup' : 'edit rigour.yml'}`));
154
+ console.log(chalk.dim(` fix: ${migration.automatic ? 'rigour setup --team' : 'edit rigour.yml'}`));
155
155
  }
156
156
  console.log('');
157
157
  }
@@ -34,6 +34,8 @@ export interface HooksCheckOptions {
34
34
  }
35
35
  /** This CLI, pinned to its version, as a hook runs it (`npx --yes @rigour-labs/cli@6.6.6`). */
36
36
  export declare function pinnedCliCommand(): string;
37
+ /** Every project file Rigour's hooks are written to, for every agent: what a team commits when it installs Rigour. */
38
+ export declare function projectHookPaths(): string[];
37
39
  /**
38
40
  * The personal install's agent hooks, once per machine (personal.ts): every agent's user-level
39
41
  * config, each command guarded so it runs only in a repository switched on with `rigour setup`.
@@ -382,6 +382,11 @@ const GENERATORS = {
382
382
  cline: generateClineHooks,
383
383
  windsurf: generateWindsurfHooks,
384
384
  };
385
+ /** Every project file Rigour's hooks are written to, for every agent: what a team commits when it installs Rigour. */
386
+ export function projectHookPaths() {
387
+ const checker = resolveCheckerCommand();
388
+ return [...new Set(Object.keys(GENERATORS).flatMap(tool => GENERATORS[tool](checker, true).map(file => file.path)))];
389
+ }
385
390
  // ── File writing ─────────────────────────────────────────────────────
386
391
  function printDryRun(files) {
387
392
  console.log(chalk.cyan('\nDry run — files that would be created:\n'));
@@ -4,6 +4,8 @@ export interface LearnReviewsOptions {
4
4
  limit?: string;
5
5
  promote?: string;
6
6
  reject?: string;
7
+ notARule?: string;
8
+ takeBack?: string;
7
9
  why?: string;
8
10
  list?: boolean;
9
11
  /** With --list: also list candidates only review bots raised (hidden by default, and counted). */
@@ -18,6 +18,10 @@ export async function learnReviewsCommand(cwd, options) {
18
18
  return decide(cwd, options.promote, 'accepted', options.why);
19
19
  if (options.reject)
20
20
  return decide(cwd, options.reject, 'rejected', options.why);
21
+ if (options.notARule)
22
+ return decide(cwd, options.notARule, 'not-a-rule', options.why);
23
+ if (options.takeBack)
24
+ return decide(cwd, options.takeBack, 'took-back', options.why);
21
25
  if (options.list)
22
26
  return list(cwd, options.json, options.includeBots);
23
27
  if (options.useWording)
@@ -81,7 +85,7 @@ function list(cwd, json, includeBots) {
81
85
  console.log('No review lessons yet. Run `rigour learn-reviews`.');
82
86
  }
83
87
  function printLesson(l) {
84
- const label = { verified: chalk.green('lesson '), candidate: chalk.yellow('candidate'), rejected: chalk.red('rejected ') };
88
+ const label = { verified: chalk.green('lesson '), candidate: chalk.yellow('candidate'), rejected: chalk.red('rejected '), dropped: chalk.dim('not a rule') };
85
89
  const prs = [...new Set(l.evidence.map(e => `#${e.pr}`))].join(', ');
86
90
  const by = chalk.dim(`${l.promotedBy ? ` [${l.promotedBy}]` : ''}${l.scope ? ` [${l.scope === 'repo' ? 'every change' : 'its folder'}]` : ''}`);
87
91
  console.log(`${label[l.state]} ${chalk.dim(l.id)} ${l.file || '(no file)'}: ${l.text}${by} ${chalk.dim(`(${prs})`)}`);
@@ -199,17 +203,32 @@ function useWording(cwd, id) {
199
203
  console.log(chalk.green(`✔ Now reads: ${lesson.text}`));
200
204
  }
201
205
  /** A person's decision, kept as evidence with who made it (their git email) and why. */
206
+ /** What each decision tells the person it did. */
207
+ const DECIDED = {
208
+ accepted: text => chalk.green(`✔ A lesson now: ${text}`),
209
+ rejected: text => chalk.yellow(`✔ Rejected: ${text}. The judges are told this team decided against it.`),
210
+ 'not-a-rule': text => chalk.dim(`✔ Not a rule (a one-off for that pull request): ${text}. Never served, and the judges are told nothing.`),
211
+ 'took-back': text => chalk.yellow(`✔ Taken back, a candidate again: ${text}. The earlier decision is kept in its history.`),
212
+ };
202
213
  function decide(cwd, id, decision, why) {
203
214
  const by = deciderOrExit(cwd);
204
215
  if (!by)
205
216
  return;
206
- const lesson = decideLesson(cwd, id, decision, by, why);
217
+ let lesson;
218
+ try {
219
+ lesson = decideLesson(cwd, id, decision, by, why);
220
+ }
221
+ catch (e) {
222
+ console.error(chalk.red(e.message));
223
+ process.exitCode = 1;
224
+ return;
225
+ }
207
226
  if (!lesson) {
208
227
  console.error(chalk.red(`No lesson ${id}.`));
209
228
  process.exitCode = 1;
210
229
  return;
211
230
  }
212
- console.log(decision === 'accepted' ? chalk.green(`✔ A lesson now: ${lesson.text}`) : chalk.yellow(`✔ Rejected: ${lesson.text}. The judges are told this team decided against it.`));
231
+ console.log(DECIDED[decision](lesson.text));
213
232
  }
214
233
  /** The repository's rigour.yml, or the defaults when it cannot be read (no rigour.yml is the defaults already). */
215
234
  async function teamConfig(cwd) {
@@ -32,11 +32,13 @@ export async function checkRepoSetup(cwd, now = new Date(), toolCalls, installed
32
32
  { name: 'Windsurf', config: read('.windsurf/hooks.json') + home('.codeium/windsurf/hooks.json') },
33
33
  ].filter(a => a.config);
34
34
  const mcpConfig = read('.mcp.json') + read('.cursor/mcp.json') + home('.claude.json') + home('.cursor/mcp.json');
35
- const version = versionCheck(agents.map(a => a.config).join('\n'), mcpConfig, installed);
35
+ // The repository's own committed install: only `rigour setup --team` changes it (a plain setup leaves it as committed).
36
+ const setup = !personal && /rigour/i.test(agents.map(a => a.config).join('\n')) ? 'rigour setup --team' : 'rigour setup';
37
+ const version = versionCheck(agents.map(a => a.config).join('\n'), mcpConfig, installed, setup);
36
38
  return [
37
39
  configCheck(cwd, personal),
38
- editCheck(agents, now, events),
39
- stopCheck(agents.map(a => a.config).join('\n'), now, events),
40
+ editCheck(agents, now, events, setup),
41
+ stopCheck(agents.map(a => a.config).join('\n'), now, events, setup),
40
42
  mcpCheck(mcpConfig, now, calls),
41
43
  ...(version ? [version] : []),
42
44
  prCheck(cwd),
@@ -47,7 +49,7 @@ export async function checkRepoSetup(cwd, now = new Date(), toolCalls, installed
47
49
  * (`@rigour-labs/cli@6.12.4`), so an upgrade reaches them only when `rigour setup` rewrites the pin.
48
50
  * Nothing to say when nothing pins a version (a source checkout runs its own build).
49
51
  */
50
- function versionCheck(hooks, mcp, installed) {
52
+ function versionCheck(hooks, mcp, installed, setup) {
51
53
  const pins = (text, pkg) => [...new Set([...text.matchAll(new RegExp(`@rigour-labs/${pkg}@([0-9A-Za-z.-]+)`, 'g'))].map(m => m[1]))];
52
54
  const hookPins = pins(hooks, 'cli');
53
55
  const mcpPins = pins(mcp, 'mcp');
@@ -62,7 +64,7 @@ function versionCheck(hooks, mcp, installed) {
62
64
  ...mcpPins.filter(v => v !== installed).map(v => (/^\d+$/.test(v) || v === 'latest' ? `the MCP server floats on @${v}` : `the MCP server runs ${v}`)),
63
65
  ];
64
66
  return stale.length
65
- ? { id: 'version', name, state: 'broken', detail: `${stale.join(', ')}; installed is ${installed}`, fix: 'rigour setup' }
67
+ ? { id: 'version', name, state: 'broken', detail: `${stale.join(', ')}; installed is ${installed}`, fix: setup }
66
68
  : { id: 'version', name, state: 'working', detail: installed };
67
69
  }
68
70
  function configCheck(cwd, personal) {
@@ -73,22 +75,22 @@ function configCheck(cwd, personal) {
73
75
  : { id: 'config', name: 'Project settings', state: 'missing', detail: 'No rigour.yml: Rigour uses its defaults', fix: 'rigour setup' };
74
76
  }
75
77
  /** One check for every agent's edit hook: events do not say which agent fired them, so counts are not split. */
76
- function editCheck(agents, now, events) {
78
+ function editCheck(agents, now, events, setup) {
77
79
  const wired = agents.filter(a => a.config.includes('hooks check'));
78
80
  const name = wired.length ? `Checks ${listOf(wired.map(a => a.name))} as it writes` : 'Checks your agent as it writes';
79
81
  if (wired.length === 0)
80
- return { id: 'edit', name, state: 'missing', detail: 'No edit hook configured', fix: 'rigour setup' };
82
+ return { id: 'edit', name, state: 'missing', detail: 'No edit hook configured', fix: setup };
81
83
  const broken = wired.find(a => isOldEditHook(a.config));
82
84
  if (broken) {
83
85
  // setup rewrites Rigour's own entries where they live (the project, or the machine for a personal install).
84
- return { id: 'edit', name, state: 'broken', detail: `The ${broken.name} hook is the old form: it reads a variable the agent never sets, so no edit is checked`, fix: 'rigour setup' };
86
+ return { id: 'edit', name, state: 'broken', detail: `The ${broken.name} hook is the old form: it reads a variable the agent never sets, so no edit is checked`, fix: setup };
85
87
  }
86
88
  return fired('edit', name, events.filter(e => e.type === 'hook_check'), now, 'edit checks');
87
89
  }
88
- function stopCheck(config, now, events) {
90
+ function stopCheck(config, now, events, setup) {
89
91
  const name = 'Stops an agent finishing with a known bug';
90
92
  if (!config.includes('hooks stop'))
91
- return { id: 'stop', name, state: 'missing', detail: 'No stop hook configured', fix: 'rigour setup' };
93
+ return { id: 'stop', name, state: 'missing', detail: 'No stop hook configured', fix: setup };
92
94
  return fired('stop', name, events.filter(e => e.type === 'stop_review'), now, 'finish checks');
93
95
  }
94
96
  function mcpCheck(mcpJson, now, calls) {
@@ -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`, or a repository that already commits a rigour.yml): rigour.yml, the project's
9
- * agent hooks and its MCP server are written into the repository, so everyone who clones gets the
10
- * same gate. Agent instruction files only with `--instructions`.
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,36 @@ 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';
27
29
  const AGENT_NAME = { claude: 'Claude Code', cursor: 'Cursor', cline: 'Cline', windsurf: 'Windsurf' };
28
30
  export async function setupCommand(cwd = process.cwd(), options = {}) {
29
- const team = options.team || committedConfig(cwd);
30
- console.log(chalk.bold.cyan(`\nRigour setup (${team ? 'team: committed to this repository' : 'personal: nothing in your working tree'})\n`));
31
- if (team)
31
+ const team = !!options.team;
32
+ const committed = team ? [] : committedRigourHooks(cwd);
33
+ 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';
34
+ console.log(chalk.bold.cyan(`\nRigour setup (${mode})\n`));
35
+ if (team) {
36
+ const touched = trackedFiles(cwd, ['rigour.yml', ...rigourInstallPaths()]);
37
+ if (touched.length)
38
+ console.log(chalk.yellow(`Writes into these committed files (commit the result so everyone gets it): ${touched.join(', ')}\n`));
32
39
  await teamSetup(cwd, options);
33
- else
40
+ }
41
+ else if (committed.length)
42
+ await leaveTeamInstall(cwd, committed, !!options.instructions);
43
+ else {
44
+ if (trackedFiles(cwd, ['rigour.yml']).length)
45
+ console.log(chalk.dim('This repository commits rigour.yml; `rigour setup --team` commits hooks for everyone. This setup is yours alone.\n'));
34
46
  await personalSetup(cwd, options.brief !== false);
35
- for (const change of applyConfigMigrations(cwd))
36
- console.log(chalk.green(`✔ Migrated ${change}`));
47
+ }
48
+ // rigour.yml is the team's file: only --team changes it (doctor's checks below name what is due).
49
+ if (team)
50
+ for (const change of applyConfigMigrations(cwd))
51
+ console.log(chalk.green(`✔ Migrated ${change}`));
37
52
  if (options.semantic !== false)
38
53
  await setupSemantic(cwd);
39
54
  await printRepoSetup(cwd);
@@ -75,7 +90,53 @@ async function teamSetup(cwd, options) {
75
90
  else
76
91
  await initCommand(cwd, { instructions: options.instructions, brief: options.brief !== false });
77
92
  }
78
- /** A rigour.yml the repository tracks: the team has adopted Rigour, so setup completes their install. */
79
- function committedConfig(cwd) {
80
- return spawnSync('git', ['ls-files', '--error-unmatch', 'rigour.yml'], { cwd, encoding: 'utf8' }).status === 0;
93
+ /**
94
+ * The team's install, as committed here: tracked project hook and MCP files that run Rigour. Their agents already run
95
+ * them, so a person's setup changes none of them, and personal hooks beside them would run every check twice.
96
+ */
97
+ async function leaveTeamInstall(cwd, committed, instructions) {
98
+ disableHere(cwd, false);
99
+ const text = committed.map(file => fs.readFileSync(path.join(cwd, file), 'utf8')).join('\n');
100
+ const pins = [...new Set([...text.matchAll(/@rigour-labs\/cli@([0-9A-Za-z.-]+)/g)].map(m => m[1]))];
101
+ const installed = getCliVersion();
102
+ const stale = pins.filter(pin => pin !== installed);
103
+ console.log(chalk.green('✔ This repository commits its own Rigour install, and your agents run it. Left as committed:'));
104
+ for (const file of committed)
105
+ console.log(chalk.dim(` ${file}`));
106
+ if (stale.length) {
107
+ // The committed pin is what the agents run: name it, and what it costs, since this setup does not move it.
108
+ const caveat = isOldEditHook(text) ? ' (its edit hook checks nothing)' : '';
109
+ 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}.`));
110
+ }
111
+ else
112
+ console.log(chalk.dim(' To change it for everyone: rigour setup --team, then commit the result.'));
113
+ // This clone's own: git's pre-push hook lives in the git directory, never in the working tree.
114
+ const push = installGitPushHook(cwd, pinnedCliCommand(), { workingTree: false });
115
+ if (push.action === 'managed elsewhere')
116
+ console.log(chalk.yellow(`Git pre-push hooks are managed outside this repository (${push.path}); add: ${pinnedCliCommand()} hooks push --git "$@" || exit $?`));
117
+ else if (push.action !== 'no repository')
118
+ console.log(chalk.green(`✔ Push gate: git's pre-push hook (${push.action}); the only thing installed here`));
119
+ // Asked for by name: written only where the repository has none, so no committed file changes.
120
+ if (instructions)
121
+ await writeAgentInstructions(cwd);
122
+ }
123
+ /** Where a team's Rigour install lives in the repository: the project hook and MCP configs. */
124
+ function rigourInstallPaths() {
125
+ return [...projectHookPaths(), '.mcp.json', '.cursor/mcp.json'];
126
+ }
127
+ /** Which of these paths git tracks here. */
128
+ function trackedFiles(cwd, paths) {
129
+ const tracked = spawnSync('git', ['ls-files', '--', ...paths], { cwd, encoding: 'utf8' });
130
+ return tracked.status === 0 ? tracked.stdout.split('\n').filter(Boolean) : [];
131
+ }
132
+ /** The tracked files of a team's Rigour install: project hook and MCP configs that name Rigour. */
133
+ function committedRigourHooks(cwd) {
134
+ return trackedFiles(cwd, rigourInstallPaths()).filter(file => {
135
+ try {
136
+ return /rigour/i.test(fs.readFileSync(path.join(cwd, file), 'utf8'));
137
+ }
138
+ catch {
139
+ return false;
140
+ }
141
+ });
81
142
  }
@@ -50,6 +50,8 @@ 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;
53
55
  /**
54
56
  * Teammates' decisions on a review lesson, received from the team database (core team-decisions.ts): each with the
55
57
  * teammate's display name and when the team database got it; and, when a later team decision settled the lesson
@@ -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, lastDecision, readTeamDecisionCache } 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))
@@ -107,6 +108,7 @@ function decisionFor(lesson) {
107
108
  function studioWhy(why) {
108
109
  return typeof why === 'string' && why.trim() ? why.trim() : 'decided in Studio';
109
110
  }
111
+ const REVIEW_DECISIONS = new Set(['accepted', 'rejected', 'dismissed', 'not-a-rule', 'took-back']);
110
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. */
111
113
  export function decideReviewLesson(cwd, body) {
112
114
  const { id, decision, why } = (body ?? {});
@@ -116,8 +118,8 @@ export function decideReviewLesson(cwd, body) {
116
118
  return rewordFromStudio(cwd, id);
117
119
  if (decision === 'scope')
118
120
  return scopeFromStudio(cwd, id, body.to, why);
119
- if (decision !== 'accepted' && decision !== 'rejected' && decision !== 'dismissed')
120
- 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');
121
123
  const lesson = decideLesson(cwd, id, decision, decider(cwd), studioWhy(why));
122
124
  if (!lesson)
123
125
  throw new Error(`no review lesson ${id}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/cli",
3
- "version": "7.0.0-rc.4",
3
+ "version": "7.0.0-rc.6",
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": "7.0.0-rc.4"
62
+ "@rigour-labs/core": "7.0.0-rc.6"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/fs-extra": "^11.0.4",
66
66
  "@types/node": "^25.0.3",
67
- "@rigour-labs/studio": "7.0.0-rc.4"
67
+ "@rigour-labs/studio": "7.0.0-rc.6"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "tsc && pnpm bundle-studio",