@mfjjs/ruflo-setup 0.2.5 → 0.2.7

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/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [0.2.7](///compare/v0.2.6...v0.2.7) (2026-03-20)
6
+
7
+
8
+ ### Features
9
+
10
+ * **cli:** add update command to upgrade ruflo-setup to the latest version 569bc0b
11
+
12
+ ### [0.2.6](///compare/v0.2.5...v0.2.6) (2026-03-20)
13
+
14
+
15
+ ### Features
16
+
17
+ * **cli:** add cleanup command to remove Ruflo packages from npm global registry. 5151c70
18
+
5
19
  ### [0.2.5](///compare/v0.2.4...v0.2.5) (2026-03-17)
6
20
 
7
21
  ### [0.2.4](https://gitlab.mfj.local:8022/mario/ruflo-setup/compare/v0.2.3...v0.2.4) (2026-03-17)
package/README.md CHANGED
@@ -11,12 +11,26 @@ Cross-platform npm CLI to bootstrap a project with Ruflo on Windows and Linux.
11
11
  - Package name: `@mfjjs/ruflo-setup`
12
12
  - Command name: `ruflo-setup`
13
13
  - Platform support: Windows and Linux (plus macOS by default)
14
+
15
+ ## 💡 Why You Need This
16
+
17
+ If you're working on:
18
+
19
+ * A brownfield application that never had RuFlow configured, or
20
+ * A brand‑new project that hasn't been set up with RuFlow yet,
21
+
22
+ …then you currently have to configure RuFlow manually in each project. That means recreating the same structure, wiring, and boilerplate over and over.
23
+
24
+ `ruflo-setup` eliminates all of that. When you run it inside a project directory, it automatically generates the required RuFlow scaffolding — including all the files that belong in the `.claude/` folder — so every project starts from a clean, consistent baseline.
25
+
26
+ You only need to do this once per project. Just run the command and you're ready to go.
27
+
14
28
  ## 📋 Requirements
15
29
  <details>
16
30
  <summary>Click to toggle visibility</summary>
17
31
 
18
32
  - Node.js 20+
19
- - pnpm available on PATH
33
+ - pnpm **10.32.1 or higher** available on PATH
20
34
 
21
35
  Quickest pnpm install by platform:
22
36
 
@@ -41,26 +55,50 @@ corepack prepare pnpm@latest --activate
41
55
 
42
56
  ## 📦 Installation
43
57
 
44
- ```powershell
45
- pnpm -i -g @mfjjs/ruflo-setup
58
+ Install the CLI globally once:
59
+
60
+ ```bash
61
+ pnpm add -g @mfjjs/ruflo-setup
46
62
  ```
47
63
 
48
- ## 💡 Why You Need This
64
+ ## 🚀 Usage
49
65
 
50
- If you’re working on:
66
+ ### Setup
51
67
 
52
- * A brownfield application that never had RuFlow configured, or
53
- * A brand‑new project that hasn’t been set up with RuFlow yet,
68
+ **First, change to your project directory**, then run:
54
69
 
55
- …then you currently have to configure RuFlow manually in each project. That means recreating the same structure, wiring, and boilerplate over and over.
70
+ ```bash
71
+ ruflo-setup
72
+ ```
56
73
 
57
- The new command eliminates all of that. When you run it inside a project, it automatically generates the required RuFlow scaffolding — including all the files that belong in the folder — so every project starts from a clean, consistent baseline.
74
+ That's it for most users. The command will:
75
+ 1. Check for a newer version of itself and offer to update before proceeding
76
+ 2. Install `ruflo@latest` globally and run `ruflo init --full` to scaffold your project
77
+ 3. Write a platform-aware `.mcp.json`
78
+ 4. Install a global Claude Code `SessionStart` hook
58
79
 
59
- You only need to do this once in each folder. Just run the command and you’re ready to go.
80
+ Additional options:
60
81
 
61
- ## 🚀 Usage
82
+ ```bash
83
+ # non-interactive (skip all prompts)
84
+ ruflo-setup --yes
85
+
86
+ # preview what would happen without making any changes
87
+ ruflo-setup --dry-run
88
+
89
+ # skip the ruflo global install step
90
+ ruflo-setup --skip-init
91
+
92
+ # skip global hook installation
93
+ ruflo-setup --no-hooks
94
+
95
+ # hook operations
96
+ ruflo-setup hooks install
97
+ ruflo-setup hooks status
98
+ ```
62
99
 
63
100
  ### Status
101
+
64
102
  Check whether all Ruflo feature layers (0–8) are enabled in the current project:
65
103
 
66
104
  ```bash
@@ -70,7 +108,8 @@ ruflo-setup status
70
108
  This prints a layer-by-layer report showing which features are active — prerequisites, global packages, optional WASM/ML packages, MCP servers, tool groups, environment variables, project scaffolding, and the Docker chat UI stack.
71
109
 
72
110
  ### Bootstrap
73
- Use this once if you want Claude Code to expose the `/ruflo-setup` command globally.
111
+
112
+ Use this once if you want Claude Code to expose the `/ruflo-setup` command globally, so you can run it from inside Claude Code chat without using the shell.
74
113
 
75
114
  ```bash
76
115
  ruflo-setup hooks init
@@ -78,27 +117,34 @@ ruflo-setup hooks init
78
117
 
79
118
  After that, when you open Claude Code in a project that does not already have Ruflo configured, you can run [**/ruflo-setup**](noop:) to start the setup flow.
80
119
 
81
- ### Setup
82
- Use these commands when you want to run the setup directly from a shell.
120
+ ### Update
83
121
 
84
- From the target project directory, run one of the following:
122
+ Update `@mfjjs/ruflo-setup` itself to the latest published version:
85
123
 
86
124
  ```bash
87
- # full setup
88
- ruflo-setup
125
+ ruflo-setup update
126
+ ```
89
127
 
90
- # non-interactive
91
- ruflo-setup --yes
128
+ It is best to always have the latest version before running setup. When you run `ruflo-setup` without arguments it automatically checks for a newer version and will prompt you to update if one is found, so in most cases you will not need to run this manually.
92
129
 
93
- # preview only
94
- ruflo-setup --dry-run --skip-init
130
+ ```bash
131
+ # preview without making changes
132
+ ruflo-setup update --dry-run
133
+ ```
95
134
 
96
- # skip global hook install
97
- ruflo-setup --no-hooks
135
+ ### Cleanup
98
136
 
99
- # hook operations
100
- ruflo-setup hooks install
101
- ruflo-setup hooks status
137
+ If you previously installed any Ruflo packages via `npm install -g`, those npm-global copies can shadow or conflict with the pnpm-managed versions. Run this to remove them and give yourself a clean slate:
138
+
139
+ ```bash
140
+ ruflo-setup cleanup
141
+ ```
142
+
143
+ This uninstalls `ruflo`, `@mfjjs/ruflo-setup`, `ruflo-setup`, `claude-flow`, `@claude-flow/cli`, and `ruv-swarm` from the **npm** global registry only — it does not touch pnpm globals.
144
+
145
+ ```bash
146
+ # preview what would be removed without making changes
147
+ ruflo-setup cleanup --dry-run
102
148
  ```
103
149
 
104
150
  ## 🗂️ Project structure
@@ -137,6 +183,7 @@ Flow:
137
183
  3. `bin/ruflo-setup.js` forwards args to `runCli(...)`.
138
184
  4. `src/cli.js` parses command and flags.
139
185
  5. `src/setup.js` runs setup steps:
186
+ - checks for a newer version of itself and prompts to update
140
187
  - optional `pnpm add -g ruflo@latest` then `ruflo init --full`
141
188
  - writes platform-aware `.mcp.json`
142
189
  - copies `templates/CLAUDE.md`
@@ -144,6 +191,10 @@ Flow:
144
191
 
145
192
  When called as `ruflo-setup status`, step 5 dispatches to `src/status.js` which checks all layers (0–8) and prints a feature status report.
146
193
 
194
+ When called as `ruflo-setup update`, it runs `pnpm add -g @mfjjs/ruflo-setup@latest` to update the tool itself.
195
+
196
+ When called as `ruflo-setup cleanup`, it removes Ruflo packages from the npm global registry to eliminate conflicts with pnpm-managed versions.
197
+
147
198
  ## 🛠️ Local development with pnpm
148
199
 
149
200
  From this repository root (`setup-ruflo/`):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mfjjs/ruflo-setup",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Cross-platform setup CLI for Ruflo + Claude Flow projects",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { createRequire } from 'node:module';
4
4
  import { parseArgs } from './utils.js';
5
- import { runSetup } from './setup.js';
5
+ import { runSetup, runCleanup, runUpdate } from './setup.js';
6
6
  import { getGlobalHookStatus, installGlobalCheckRufloHook } from './hooks.js';
7
7
 
8
8
  const require = createRequire(import.meta.url);
@@ -19,6 +19,8 @@ function printHelp() {
19
19
  Usage:
20
20
  ruflo-setup [options]
21
21
  ruflo-setup status
22
+ ruflo-setup update [--dry-run]
23
+ ruflo-setup cleanup [--dry-run]
22
24
  ruflo-setup hooks install [options]
23
25
  ruflo-setup hooks status
24
26
 
@@ -31,9 +33,17 @@ Options:
31
33
  --version, -v Print version and exit
32
34
  --verbose Extra output
33
35
 
36
+ Commands:
37
+ update Update @mfjjs/ruflo-setup itself to the latest published version
38
+ cleanup Remove all Ruflo packages from the npm global registry
39
+ (ruflo, @mfjjs/ruflo-setup, claude-flow, @claude-flow/cli, ruv-swarm)
40
+
34
41
  Examples:
35
42
  ruflo-setup
43
+ ruflo-setup update
36
44
  ruflo-setup status
45
+ ruflo-setup cleanup
46
+ ruflo-setup cleanup --dry-run
37
47
  ruflo-setup --dry-run --skip-init
38
48
  ruflo-setup hooks status
39
49
  ruflo-setup hooks install --dry-run
@@ -93,6 +103,24 @@ export async function runCli(argv, cwd) {
93
103
  return 1;
94
104
  }
95
105
 
106
+ if (flags.command === 'update') {
107
+ runUpdate({ dryRun: flags.dryRun });
108
+ return 0;
109
+ }
110
+
111
+ if (flags.command === 'cleanup') {
112
+ if (!flags.dryRun && !flags.yes) {
113
+ const { confirm } = await import('./utils.js');
114
+ const ok = await confirm('Remove Ruflo packages from npm global registry? [y/N] ');
115
+ if (!ok) {
116
+ process.stdout.write('Aborted. No changes made.\n');
117
+ return 0;
118
+ }
119
+ }
120
+ runCleanup({ dryRun: flags.dryRun });
121
+ return 0;
122
+ }
123
+
96
124
  await runSetup({
97
125
  cwd,
98
126
  packageRoot,
package/src/setup.js CHANGED
@@ -33,30 +33,49 @@ function getPnpmInstallSuggestions(platform) {
33
33
  ];
34
34
  }
35
35
 
36
+ const MIN_PNPM_VERSION = '10.32.1';
37
+
38
+ function parseSemver(str) {
39
+ const [major = 0, minor = 0, patch = 0] = str.trim().split('.').map(Number);
40
+ return { major, minor, patch };
41
+ }
42
+
43
+ function semverGte(a, b) {
44
+ if (a.major !== b.major) return a.major > b.major;
45
+ if (a.minor !== b.minor) return a.minor > b.minor;
46
+ return a.patch >= b.patch;
47
+ }
48
+
36
49
  function ensurePnpmAvailable() {
37
50
  const check = spawnSync('pnpm', ['--version'], {
38
- stdio: 'ignore',
51
+ stdio: ['ignore', 'pipe', 'ignore'],
39
52
  shell: process.platform === 'win32'
40
53
  });
41
54
 
42
- if (check.status === 0 && !check.error) {
43
- return;
55
+ if (check.status !== 0 || check.error) {
56
+ const platformLabel = process.platform === 'win32'
57
+ ? 'Windows'
58
+ : process.platform === 'darwin'
59
+ ? 'macOS'
60
+ : 'Linux';
61
+ const suggestions = getPnpmInstallSuggestions(process.platform)
62
+ .map((command) => ` - ${command}`)
63
+ .join('\n');
64
+
65
+ throw new Error(
66
+ `pnpm is required but was not found in PATH.\n` +
67
+ `Install pnpm, then re-run ruflo-setup.\n` +
68
+ `Quick install options for ${platformLabel}:\n${suggestions}`
69
+ );
44
70
  }
45
71
 
46
- const platformLabel = process.platform === 'win32'
47
- ? 'Windows'
48
- : process.platform === 'darwin'
49
- ? 'macOS'
50
- : 'Linux';
51
- const suggestions = getPnpmInstallSuggestions(process.platform)
52
- .map((command) => ` - ${command}`)
53
- .join('\n');
54
-
55
- throw new Error(
56
- `pnpm is required but was not found in PATH.\n` +
57
- `Install pnpm, then re-run ruflo-setup.\n` +
58
- `Quick install options for ${platformLabel}:\n${suggestions}`
59
- );
72
+ const version = (check.stdout || '').toString().trim();
73
+ if (!semverGte(parseSemver(version), parseSemver(MIN_PNPM_VERSION))) {
74
+ throw new Error(
75
+ `pnpm ${MIN_PNPM_VERSION} or higher is required, but found ${version}.\n` +
76
+ `Upgrade with: pnpm self-update`
77
+ );
78
+ }
60
79
  }
61
80
 
62
81
  function runPnpmInit({ force, cwd, dryRun }) {
@@ -67,22 +86,46 @@ function runPnpmInit({ force, cwd, dryRun }) {
67
86
 
68
87
  if (dryRun) {
69
88
  logLine(` [DRY RUN] Would run: pnpm add -g ruflo@latest`);
89
+ logLine(` [DRY RUN] Would run: pnpm approve-builds -g --all (if changes detected)`);
70
90
  logLine(` [DRY RUN] Would run: ruflo ${initArgs.join(' ')}`);
71
91
  return;
72
92
  }
73
93
 
74
94
  ensurePnpmAvailable();
75
95
 
96
+ // Capture stdout to detect whether pnpm installed/updated anything.
97
+ // Progress spinners go to stderr (still shown to user); stdout has the summary.
76
98
  const install = spawnSync('pnpm', ['add', '-g', 'ruflo@latest'], {
77
99
  cwd,
78
- stdio: 'inherit',
100
+ stdio: ['inherit', 'pipe', 'inherit'],
79
101
  shell: process.platform === 'win32'
80
102
  });
81
103
 
104
+ const installOutput = (install.stdout || '').toString();
105
+ if (installOutput) {
106
+ process.stdout.write(installOutput);
107
+ }
108
+
82
109
  if (install.status !== 0) {
83
110
  throw new Error(`pnpm add -g ruflo@latest failed with exit code ${install.status}`);
84
111
  }
85
112
 
113
+ // pnpm prints a "Packages:" summary line and "+ pkg version" lines when
114
+ // something is actually installed or updated. When already up to date the
115
+ // stdout is empty or contains only "Already up to date".
116
+ const somethingChanged = /Packages:/i.test(installOutput) || /^\+\s/m.test(installOutput);
117
+ if (somethingChanged) {
118
+ logLine(' Changes detected — running pnpm approve-builds -g --all ...');
119
+ const approve = spawnSync('pnpm', ['approve-builds', '-g', '--all'], {
120
+ cwd,
121
+ stdio: 'inherit',
122
+ shell: process.platform === 'win32'
123
+ });
124
+ if (approve.status !== 0) {
125
+ throw new Error(`pnpm approve-builds -g --all failed with exit code ${approve.status}`);
126
+ }
127
+ }
128
+
86
129
  const run = spawnSync('ruflo', initArgs, {
87
130
  cwd,
88
131
  stdio: 'inherit',
@@ -128,6 +171,29 @@ function isAlreadyConfigured(cwd) {
128
171
  return pathExists(path.join(cwd, '.mcp.json')) || pathExists(path.join(cwd, '.claude', 'settings.json'));
129
172
  }
130
173
 
174
+ function getCurrentVersion(packageRoot) {
175
+ try {
176
+ const pkg = JSON.parse(fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'));
177
+ return pkg.version || '0.0.0';
178
+ } catch {
179
+ return '0.0.0';
180
+ }
181
+ }
182
+
183
+ function getLatestVersion() {
184
+ try {
185
+ const result = spawnSync('pnpm', ['view', '@mfjjs/ruflo-setup', 'version'], {
186
+ stdio: ['ignore', 'pipe', 'ignore'],
187
+ shell: process.platform === 'win32',
188
+ timeout: 8000
189
+ });
190
+ if (result.status !== 0 || result.error) return null;
191
+ return (result.stdout || '').toString().trim() || null;
192
+ } catch {
193
+ return null;
194
+ }
195
+ }
196
+
131
197
  export async function runSetup({
132
198
  cwd,
133
199
  packageRoot,
@@ -146,6 +212,24 @@ export async function runSetup({
146
212
  }
147
213
  logLine('');
148
214
 
215
+ // Check if a newer version of ruflo-setup itself is available.
216
+ if (!dryRun && !yes) {
217
+ const currentVersion = getCurrentVersion(packageRoot);
218
+ const latestVersion = getLatestVersion();
219
+ if (latestVersion && !semverGte(parseSemver(currentVersion), parseSemver(latestVersion))) {
220
+ logLine(`A newer version of ruflo-setup is available: ${latestVersion} (you have ${currentVersion}).`);
221
+ logLine('It is best to always have the latest version before running setup.');
222
+ const doUpdate = await confirm('Update @mfjjs/ruflo-setup now? [y/N] ');
223
+ if (doUpdate) {
224
+ runUpdate({ dryRun: false });
225
+ logLine('');
226
+ logLine('Please re-run ruflo-setup to continue with the updated version.');
227
+ return;
228
+ }
229
+ logLine('');
230
+ }
231
+ }
232
+
149
233
  logLine('Preflight: Syncing global /ruflo-setup command template ...');
150
234
  const preflightCommandResult = syncGlobalCommandTemplate({ packageRoot, dryRun });
151
235
  if (preflightCommandResult.changed) {
@@ -233,3 +317,64 @@ export async function runSetup({
233
317
  logLine(' 2. Run: claude');
234
318
  logLine(' 3. Verify hooks: ruflo-setup hooks status');
235
319
  }
320
+
321
+ const CLEANUP_NPM_PACKAGES = [
322
+ 'ruflo',
323
+ '@mfjjs/ruflo-setup',
324
+ 'ruflo-setup',
325
+ 'claude-flow',
326
+ '@claude-flow/cli',
327
+ 'ruv-swarm'
328
+ ];
329
+
330
+ export function runCleanup({ dryRun = false } = {}) {
331
+ logLine('');
332
+ logLine('Ruflo Cleanup — removing from npm global registry');
333
+ logLine(`Packages: ${CLEANUP_NPM_PACKAGES.join(', ')}`);
334
+ logLine('');
335
+
336
+ if (dryRun) {
337
+ logLine(` [DRY RUN] Would run: npm uninstall -g ${CLEANUP_NPM_PACKAGES.join(' ')}`);
338
+ logLine('');
339
+ return;
340
+ }
341
+
342
+ const result = spawnSync('npm', ['uninstall', '-g', ...CLEANUP_NPM_PACKAGES], {
343
+ stdio: 'inherit',
344
+ shell: process.platform === 'win32'
345
+ });
346
+
347
+ if (result.status !== 0) {
348
+ throw new Error(`npm uninstall -g failed with exit code ${result.status}`);
349
+ }
350
+
351
+ logLine('');
352
+ logLine('Cleanup complete.');
353
+ }
354
+
355
+ export function runUpdate({ dryRun = false } = {}) {
356
+ logLine('');
357
+ logLine('Ruflo Setup Update');
358
+ logLine('');
359
+
360
+ if (dryRun) {
361
+ logLine('[DRY RUN] Would run: pnpm add -g @mfjjs/ruflo-setup@latest');
362
+ logLine('');
363
+ return;
364
+ }
365
+
366
+ ensurePnpmAvailable();
367
+
368
+ logLine('Updating @mfjjs/ruflo-setup to latest...');
369
+ const result = spawnSync('pnpm', ['add', '-g', '@mfjjs/ruflo-setup@latest'], {
370
+ stdio: 'inherit',
371
+ shell: process.platform === 'win32'
372
+ });
373
+
374
+ if (result.status !== 0) {
375
+ throw new Error(`pnpm add -g @mfjjs/ruflo-setup@latest failed with exit code ${result.status}`);
376
+ }
377
+
378
+ logLine('');
379
+ logLine('Update complete. Re-run ruflo-setup to continue with the updated version.');
380
+ }
package/src/status.js CHANGED
@@ -40,12 +40,11 @@ function fileExists(p) {
40
40
  }
41
41
  }
42
42
 
43
- // Flatten npm/pnpm --json list result into a name->version map (1 level deep).
43
+ // Flatten pnpm --json list result into a name->version map (1 level deep).
44
44
  function buildPkgMap(jsonText) {
45
45
  const map = {};
46
46
  try {
47
47
  const parsed = JSON.parse(jsonText || '{}');
48
- // npm list -g returns an array with one element, pnpm returns an object
49
48
  const root = Array.isArray(parsed) ? parsed[0] : parsed;
50
49
  const deps = root?.dependencies ?? {};
51
50
  for (const [name, info] of Object.entries(deps)) {
@@ -60,13 +59,7 @@ function buildPkgMap(jsonText) {
60
59
  return map;
61
60
  }
62
61
 
63
- // Tries npm list -g first, falls back to pnpm list -g.
64
62
  function getGlobalPkgMap() {
65
- const npmRes = spawn('npm', ['list', '-g', '--depth=1', '--json']);
66
- if (npmRes.status === 0 && npmRes.stdout) {
67
- const m = buildPkgMap(npmRes.stdout);
68
- if (Object.keys(m).length > 0) return m;
69
- }
70
63
  const pnpmRes = spawn('pnpm', ['list', '-g', '--depth=1', '--json']);
71
64
  if (pnpmRes.status === 0 && pnpmRes.stdout) {
72
65
  return buildPkgMap(pnpmRes.stdout);
@@ -95,7 +88,7 @@ function checkLayer0() {
95
88
  const ver = (claudeRes.stdout || '').trim();
96
89
  lines.push(` ${OK} Claude Code CLI${ver ? ` ${ver}` : ''}`); ok += 1;
97
90
  } else {
98
- lines.push(` ${MISS} Claude Code CLI (install: npm install -g @anthropic-ai/claude-code)`);
91
+ lines.push(` ${MISS} Claude Code CLI (install: pnpm add -g @anthropic-ai/claude-code)`);
99
92
  }
100
93
 
101
94
  if (process.env.ANTHROPIC_API_KEY) {
@@ -113,7 +106,7 @@ function checkLayer1(pkgMap) {
113
106
  for (const name of ['ruflo', '@mfjjs/ruflo-setup']) {
114
107
  const ver = pkgMap[name];
115
108
  if (ver) { lines.push(` ${OK} ${name}${typeof ver === 'string' ? `@${ver}` : ''}`); ok += 1; }
116
- else { lines.push(` ${MISS} ${name} (install: npm install -g ${name})`); }
109
+ else { lines.push(` ${MISS} ${name} (install: pnpm add -g ${name})`); }
117
110
  }
118
111
  return { lines, ok, total: 2 };
119
112
  }
@@ -268,7 +261,7 @@ export async function runStatus({ cwd, packageRoot }) {
268
261
 
269
262
  const layers = [
270
263
  { title: 'Layer 0: Prerequisites', result: checkLayer0() },
271
- { title: 'Layer 1: Global npm Packages', result: checkLayer1(pkgMap) },
264
+ { title: 'Layer 1: Global Packages', result: checkLayer1(pkgMap) },
272
265
  { title: 'Layer 2: Optional Packages (WASM/ML) — enables AI features', result: checkLayer2(pkgMap) },
273
266
  { title: 'Layer 3: MCP Servers (.mcp.json)', result: checkLayer3(mcpJson) },
274
267
  { title: 'Layer 4: MCP Tool Groups', result: checkLayer4(mcpJson) },
@@ -5,7 +5,7 @@ Set up Ruflo + Claude Flow V3 in the current project directory.
5
5
  ## Requirements
6
6
 
7
7
  - Node.js 20+
8
- - pnpm installed and available on PATH
8
+ - pnpm 10.32.1+ installed and available on PATH
9
9
 
10
10
  Quickest pnpm install by platform:
11
11
 
@@ -41,15 +41,50 @@ Runs `pnpm add -g @mfjjs/ruflo-setup` then `ruflo-setup` which:
41
41
  3. Installs a global `SessionStart` hook in `~/.claude/settings.json` that warns when Ruflo is not configured
42
42
  4. May refresh `~/.claude/commands/ruflo-setup.md` from the latest packaged template when differences are detected
43
43
 
44
+ ## Options
45
+
46
+ ### cleanup
47
+
48
+ Removes all Ruflo-related packages from the **npm** global registry (does not touch pnpm globals).
49
+
50
+ Packages removed: `ruflo`, `@mfjjs/ruflo-setup`, `ruflo-setup`, `claude-flow`, `@claude-flow/cli`, `ruv-swarm`
51
+
44
52
  ## Instructions for Claude
45
53
 
46
54
  When the user runs /ruflo-setup:
47
55
 
56
+ ### Default (no arguments) — install
57
+
48
58
  1. Confirm the current working directory with the user
49
59
  2. Check if `.mcp.json` already exists — if so, warn and ask before overwriting
50
- 3. Run the setup CLI:
60
+ 3. Check pnpm version is at least 10.32.1:
61
+ ```bash
62
+ pnpm --version
63
+ ```
64
+ If the version is lower than 10.32.1, stop and tell the user to upgrade pnpm before continuing.
65
+ 4. Run the setup CLI and capture output to detect whether pnpm modified anything:
51
66
  ```bash
52
67
  pnpm add -g @mfjjs/ruflo-setup
68
+ pnpm add -g ruflo@latest 2>&1 | tee /tmp/ruflo-pnpm-add.log
69
+ ```
70
+ After the `pnpm add -g ruflo@latest` step, inspect the output. If pnpm installed or updated any packages (i.e. the output does NOT contain "Already up to date" or an equivalent no-change message), run:
71
+ ```bash
72
+ pnpm approve-builds -g --all
73
+ ```
74
+ Skip `approve-builds` if nothing changed.
75
+ 5. Run the setup tool:
76
+ ```bash
53
77
  ruflo-setup
54
78
  ```
55
- 4. Report what was installed and remind the user to restart Claude Code to load the new MCP servers
79
+ 6. Report what was installed and remind the user to restart Claude Code to load the new MCP servers
80
+
81
+ ### cleanup
82
+
83
+ When the user runs `/ruflo-setup cleanup`:
84
+
85
+ 1. Warn the user that this will remove Ruflo packages from the **npm** global registry and ask for confirmation
86
+ 2. On confirmation, run:
87
+ ```bash
88
+ ruflo-setup cleanup
89
+ ```
90
+ 3. Report which packages were removed and which were not found (not found is fine — it means they were already clean)