@rigour-labs/cli 7.0.0-rc.11 → 7.0.0-rc.12

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.
@@ -8,6 +8,8 @@ export declare function recordCreated(cwd: string, relPath: string, content: str
8
8
  export declare function unchangedSinceInstall(record: InstallRecord, relPath: string, content: string): boolean;
9
9
  /** A server entry Rigour wrote (npx of its MCP package), which setup may re-pin; one the person changed is theirs. */
10
10
  export declare function isRigourMcpEntry(entry: unknown): boolean;
11
+ /** Whether an MCP server entry, or `claude mcp get`'s description of one, runs Rigour's server: the package, or a checkout's build. */
12
+ export declare function namesRigourMcp(entry: unknown): boolean;
11
13
  /** A hook script Rigour wrote (the Cline hooks): it names Rigour in its header or runs the Rigour CLI. */
12
14
  export declare function isRigourScript(text: string): boolean;
13
15
  /**
@@ -38,6 +38,11 @@ export function isRigourMcpEntry(entry) {
38
38
  const e = entry;
39
39
  return e?.command === 'npx' && Array.isArray(e.args) && e.args.some(a => typeof a === 'string' && a.startsWith('@rigour-labs/mcp'));
40
40
  }
41
+ /** Whether an MCP server entry, or `claude mcp get`'s description of one, runs Rigour's server: the package, or a checkout's build. */
42
+ export function namesRigourMcp(entry) {
43
+ const text = typeof entry === 'string' ? entry : JSON.stringify(entry ?? '');
44
+ return /@rigour-labs\/mcp|rigour-mcp[\\/]+dist/.test(text);
45
+ }
41
46
  /** A hook script Rigour wrote (the Cline hooks): it names Rigour in its header or runs the Rigour CLI. */
42
47
  export function isRigourScript(text) {
43
48
  return /hook for Rigour|@rigour-labs\/cli/.test(text) || isRigourCommand(text);
@@ -24,7 +24,10 @@ export declare function asUserLevel<T extends {
24
24
  path: string;
25
25
  content: string;
26
26
  }>(file: T): T;
27
- export type McpState = 'added' | 'updated' | 'present' | 'removed' | 'absent' | 'no CLI';
27
+ /** `taken`: a user-level server named rigour that is not Rigour's; Rigour's is not registered over it. */
28
+ export type McpState = 'added' | 'updated' | 'present' | 'removed' | 'absent' | 'no CLI' | 'taken';
29
+ /** What setup and doctor say when another server holds the name. */
30
+ export declare const MCP_NAME_TAKEN = "a user-level MCP server named rigour exists and is not Rigour's; rename it, then run rigour setup again";
28
31
  /**
29
32
  * The Rigour MCP server at user level: Claude Code through its own `claude mcp add --scope user`
30
33
  * (its state file is Claude's, not Rigour's to edit), Cursor merged into `~/.cursor/mcp.json`.
@@ -16,7 +16,7 @@ import fs from 'fs';
16
16
  import os from 'os';
17
17
  import path from 'path';
18
18
  import { rigourUserDir } from '@rigour-labs/core';
19
- import { isEmptyConfig, isRigourMcpEntry, readInstallRecord, recordCreated, unchangedSinceInstall, withoutRigour } from './install-record.js';
19
+ import { isEmptyConfig, isRigourMcpEntry, namesRigourMcp, readInstallRecord, recordCreated, unchangedSinceInstall, withoutRigour } from './install-record.js';
20
20
  const MARKER = 'rigour-enabled';
21
21
  /**
22
22
  * Where the agents keep their user-level configs: RIGOUR_AGENT_HOME when set (tests, a sandbox),
@@ -136,6 +136,8 @@ export function asUserLevel(file) {
136
136
  };
137
137
  return { ...file, path: target, content: JSON.stringify(guard(JSON.parse(file.content)), null, 4) };
138
138
  }
139
+ /** What setup and doctor say when another server holds the name. */
140
+ export const MCP_NAME_TAKEN = "a user-level MCP server named rigour exists and is not Rigour's; rename it, then run rigour setup again";
139
141
  /**
140
142
  * The Rigour MCP server at user level: Claude Code through its own `claude mcp add --scope user`
141
143
  * (its state file is Claude's, not Rigour's to edit), Cursor merged into `~/.cursor/mcp.json`.
@@ -147,6 +149,9 @@ export function registerUserMcp(server) {
147
149
  const get = claude(['mcp', 'get', 'rigour']);
148
150
  if (!get.ok)
149
151
  state = add() ? 'added' : 'no CLI';
152
+ // Another server holds the name: Rigour's is not registered over it, and the person is told.
153
+ else if (!namesRigourMcp(get.stdout))
154
+ state = 'taken';
150
155
  // Rigour's own registration at another version is moved to this one, as the hooks are; one the person changed stays.
151
156
  else if (/@rigour-labs\/mcp/.test(get.stdout) && !get.stdout.includes(server.args[server.args.length - 1])) {
152
157
  claude(['mcp', 'remove', '--scope', 'user', 'rigour']);
@@ -169,6 +174,8 @@ export function registerUserMcp(server) {
169
174
  }
170
175
  }
171
176
  const current = config?.mcpServers?.rigour;
177
+ if (current && !namesRigourMcp(current))
178
+ return { claude: state, cursor: 'taken' };
172
179
  if (current && (JSON.stringify(current) === JSON.stringify(server) || !isRigourMcpEntry(current)))
173
180
  return { claude: state, cursor: 'present' };
174
181
  config.mcpServers = { ...(config.mcpServers ?? {}), rigour: server };
@@ -7,7 +7,8 @@ import fs from 'fs';
7
7
  import path from 'path';
8
8
  import { getContextEvents } from '@rigour-labs/core';
9
9
  import { resolveMCPServerConfig } from './init.js';
10
- import { agentHome, enabledHere } from './personal.js';
10
+ import { agentHome, enabledHere, MCP_NAME_TAKEN } from './personal.js';
11
+ import { namesRigourMcp } from './install-record.js';
11
12
  import { isOldEditHook } from './setup-migrations.js';
12
13
  import { getCliVersion } from '../utils/cli-version.js';
13
14
  import { checkoutRoots, eventsAcross } from './studio-checkouts.js';
@@ -39,7 +40,7 @@ export async function checkRepoSetup(cwd, now = new Date(), toolCalls, installed
39
40
  configCheck(cwd, personal),
40
41
  editCheck(agents, now, events, setup),
41
42
  stopCheck(agents.map(a => a.config).join('\n'), now, events, setup),
42
- mcpCheck(mcpConfig, now, calls),
43
+ mcpCheck(mcpConfig, now, calls, nameTaken(readFrom(agentHome()))),
43
44
  ...(version ? [version] : []),
44
45
  prCheck(cwd),
45
46
  ];
@@ -93,8 +94,24 @@ function stopCheck(config, now, events, setup) {
93
94
  return { id: 'stop', name, state: 'missing', detail: 'No stop hook configured', fix: setup };
94
95
  return fired('stop', name, events.filter(e => e.type === 'stop_review'), now, 'finish checks');
95
96
  }
96
- function mcpCheck(mcpJson, now, calls) {
97
+ /** Which agent's user-level config holds a server named rigour that is not Rigour's (personal.ts MCP_NAME_TAKEN). */
98
+ function nameTaken(home) {
99
+ for (const [agent, rel] of [['Claude Code', '.claude.json'], ['Cursor', '.cursor/mcp.json']]) {
100
+ try {
101
+ const entry = JSON.parse(home(rel) || '{}')?.mcpServers?.rigour;
102
+ if (entry && !namesRigourMcp(entry))
103
+ return agent;
104
+ }
105
+ catch {
106
+ // not JSON: not Rigour's to judge
107
+ }
108
+ }
109
+ return undefined;
110
+ }
111
+ function mcpCheck(mcpJson, now, calls, taken) {
97
112
  const name = 'Rigour tools for agents (MCP)';
113
+ if (taken)
114
+ return { id: 'mcp', name, state: 'broken', detail: `${taken}: ${MCP_NAME_TAKEN}`, fix: 'rename the other server, then rigour setup' };
98
115
  if (!mcpJson.includes('rigour') && calls.length === 0) {
99
116
  return { id: 'mcp', name, state: 'missing', detail: 'Agents cannot ask Rigour for review tasks or lessons', fix: `claude mcp add --scope user rigour -- ${[resolveMCPServerConfig().command, ...resolveMCPServerConfig().args].join(' ')}` };
100
117
  }
@@ -22,7 +22,7 @@ import { installGitPushHook } from './hooks-git.js';
22
22
  import { hooksInitCommand, installMachineHooks, pinnedCliCommand, projectHookPaths } from './hooks.js';
23
23
  import { initCommand, registerProjectMcp, resolveMCPServerConfig } from './init.js';
24
24
  import { writeAgentInstructions } from './init-handshake.js';
25
- import { disableHere, enableHere, registerUserMcp } from './personal.js';
25
+ import { disableHere, enableHere, MCP_NAME_TAKEN, registerUserMcp } from './personal.js';
26
26
  import { applyConfigMigrations, isOldEditHook } from './setup-migrations.js';
27
27
  import { getCliVersion } from '../utils/cli-version.js';
28
28
  import { setupSemantic } from './semantic.js';
@@ -80,6 +80,9 @@ async function personalSetup(cwd, brief) {
80
80
  console.log(chalk.yellow(`Claude Code MCP: add it with ${chalk.cyan(`claude mcp add --scope user rigour -- ${[resolveMCPServerConfig().command, ...resolveMCPServerConfig().args].join(' ')}`)}`));
81
81
  else
82
82
  console.log(chalk.green(`✔ Rigour tools for agents (MCP): Claude Code ${mcp.claude}, Cursor ${mcp.cursor}`));
83
+ for (const [agent, state] of [['Claude Code', mcp.claude], ['Cursor', mcp.cursor]])
84
+ if (state === 'taken')
85
+ console.log(chalk.yellow(`${agent}: ${MCP_NAME_TAKEN}.`));
83
86
  }
84
87
  async function teamSetup(cwd, options) {
85
88
  // A personal switch here would run the machine hooks beside the committed ones.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigour-labs/cli",
3
- "version": "7.0.0-rc.11",
3
+ "version": "7.0.0-rc.12",
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.11"
62
+ "@rigour-labs/core": "7.0.0-rc.12"
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.11"
67
+ "@rigour-labs/studio": "7.0.0-rc.12"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "tsc && pnpm bundle-studio",