@mfjjs/ruflo-setup 0.2.6 → 0.2.8
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 +21 -0
- package/README.md +74 -38
- package/package.json +1 -1
- package/src/cli.js +9 -1
- package/src/setup.js +414 -312
- package/src/status.js +4 -11
- package/src/utils.js +120 -120
- package/templates/ruflo-setup.md +92 -90
package/src/status.js
CHANGED
|
@@ -40,12 +40,11 @@ function fileExists(p) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
// Flatten
|
|
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:
|
|
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:
|
|
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
|
|
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) },
|
package/src/utils.js
CHANGED
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import readline from 'node:readline';
|
|
4
|
-
|
|
5
|
-
export function pathExists(filePath) {
|
|
6
|
-
return fs.existsSync(filePath);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function readJsonSafe(filePath, fallbackValue = {}) {
|
|
10
|
-
if (!pathExists(filePath)) {
|
|
11
|
-
return fallbackValue;
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
15
|
-
} catch {
|
|
16
|
-
return fallbackValue;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function writeJson(filePath, value) {
|
|
21
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
22
|
-
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function copyFileSync(src, dest) {
|
|
26
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
27
|
-
fs.copyFileSync(src, dest);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export async function confirm(question) {
|
|
31
|
-
const rl = readline.createInterface({
|
|
32
|
-
input: process.stdin,
|
|
33
|
-
output: process.stdout
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const answer = await new Promise((resolve) => {
|
|
37
|
-
rl.question(question, resolve);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
rl.close();
|
|
41
|
-
return /^[Yy]$/.test((answer || '').trim());
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function parseArgs(argv) {
|
|
45
|
-
const flags = {
|
|
46
|
-
force: false,
|
|
47
|
-
dryRun: false,
|
|
48
|
-
yes: false,
|
|
49
|
-
noHooks: false,
|
|
50
|
-
skipInit: false,
|
|
51
|
-
verbose: false,
|
|
52
|
-
command: 'setup'
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const positional = [];
|
|
56
|
-
for (const item of argv) {
|
|
57
|
-
if (item === '--force' || item === '-f') flags.force = true;
|
|
58
|
-
else if (item === '--dry-run') flags.dryRun = true;
|
|
59
|
-
else if (item === '--yes' || item === '-y') flags.yes = true;
|
|
60
|
-
else if (item === '--no-hooks') flags.noHooks = true;
|
|
61
|
-
else if (item === '--skip-init') flags.skipInit = true;
|
|
62
|
-
else if (item === '--verbose') flags.verbose = true;
|
|
63
|
-
else positional.push(item);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (positional.length > 0) {
|
|
67
|
-
flags.command = positional[0];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return flags;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export function toPlatformMcpConfig(platform) {
|
|
74
|
-
const isWindows = platform === 'win32';
|
|
75
|
-
const command = isWindows ? 'cmd' : '
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const makeArgs = (pkg, extraArgs) => {
|
|
79
|
-
return [...
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
mcpServers: {
|
|
84
|
-
'claude-flow': {
|
|
85
|
-
command,
|
|
86
|
-
args: makeArgs('@claude-flow/cli@latest', ['mcp', 'start']),
|
|
87
|
-
env: {
|
|
88
|
-
npm_config_update_notifier: 'false',
|
|
89
|
-
CLAUDE_FLOW_MODE: 'v3',
|
|
90
|
-
CLAUDE_FLOW_HOOKS_ENABLED: 'true',
|
|
91
|
-
CLAUDE_FLOW_TOPOLOGY: 'hierarchical-mesh',
|
|
92
|
-
CLAUDE_FLOW_MAX_AGENTS: '15',
|
|
93
|
-
CLAUDE_FLOW_MEMORY_BACKEND: 'hybrid',
|
|
94
|
-
MCP_GROUP_SECURITY: 'true',
|
|
95
|
-
MCP_GROUP_BROWSER: 'true',
|
|
96
|
-
MCP_GROUP_NEURAL: 'true',
|
|
97
|
-
MCP_GROUP_AGENTIC_FLOW: 'true'
|
|
98
|
-
},
|
|
99
|
-
autoStart: false
|
|
100
|
-
},
|
|
101
|
-
'ruv-swarm': {
|
|
102
|
-
command,
|
|
103
|
-
args: makeArgs('ruv-swarm', ['mcp', 'start']),
|
|
104
|
-
env: {
|
|
105
|
-
npm_config_update_notifier: 'false'
|
|
106
|
-
},
|
|
107
|
-
optional: true
|
|
108
|
-
},
|
|
109
|
-
'flow-nexus': {
|
|
110
|
-
command,
|
|
111
|
-
args: makeArgs('flow-nexus@latest', ['mcp', 'start']),
|
|
112
|
-
env: {
|
|
113
|
-
npm_config_update_notifier: 'false'
|
|
114
|
-
},
|
|
115
|
-
optional: true,
|
|
116
|
-
requiresAuth: true
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
}
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import readline from 'node:readline';
|
|
4
|
+
|
|
5
|
+
export function pathExists(filePath) {
|
|
6
|
+
return fs.existsSync(filePath);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function readJsonSafe(filePath, fallbackValue = {}) {
|
|
10
|
+
if (!pathExists(filePath)) {
|
|
11
|
+
return fallbackValue;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
15
|
+
} catch {
|
|
16
|
+
return fallbackValue;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function writeJson(filePath, value) {
|
|
21
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
22
|
+
fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function copyFileSync(src, dest) {
|
|
26
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
27
|
+
fs.copyFileSync(src, dest);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function confirm(question) {
|
|
31
|
+
const rl = readline.createInterface({
|
|
32
|
+
input: process.stdin,
|
|
33
|
+
output: process.stdout
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const answer = await new Promise((resolve) => {
|
|
37
|
+
rl.question(question, resolve);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
rl.close();
|
|
41
|
+
return /^[Yy]$/.test((answer || '').trim());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseArgs(argv) {
|
|
45
|
+
const flags = {
|
|
46
|
+
force: false,
|
|
47
|
+
dryRun: false,
|
|
48
|
+
yes: false,
|
|
49
|
+
noHooks: false,
|
|
50
|
+
skipInit: false,
|
|
51
|
+
verbose: false,
|
|
52
|
+
command: 'setup'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const positional = [];
|
|
56
|
+
for (const item of argv) {
|
|
57
|
+
if (item === '--force' || item === '-f') flags.force = true;
|
|
58
|
+
else if (item === '--dry-run') flags.dryRun = true;
|
|
59
|
+
else if (item === '--yes' || item === '-y') flags.yes = true;
|
|
60
|
+
else if (item === '--no-hooks') flags.noHooks = true;
|
|
61
|
+
else if (item === '--skip-init') flags.skipInit = true;
|
|
62
|
+
else if (item === '--verbose') flags.verbose = true;
|
|
63
|
+
else positional.push(item);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (positional.length > 0) {
|
|
67
|
+
flags.command = positional[0];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return flags;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function toPlatformMcpConfig(platform) {
|
|
74
|
+
const isWindows = platform === 'win32';
|
|
75
|
+
const command = isWindows ? 'cmd' : 'npx';
|
|
76
|
+
const npxArgs = isWindows ? ['/c', 'npx', '-y'] : ['-y'];
|
|
77
|
+
|
|
78
|
+
const makeArgs = (pkg, extraArgs) => {
|
|
79
|
+
return [...npxArgs, pkg, ...extraArgs];
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
mcpServers: {
|
|
84
|
+
'claude-flow': {
|
|
85
|
+
command,
|
|
86
|
+
args: makeArgs('@claude-flow/cli@latest', ['mcp', 'start']),
|
|
87
|
+
env: {
|
|
88
|
+
npm_config_update_notifier: 'false',
|
|
89
|
+
CLAUDE_FLOW_MODE: 'v3',
|
|
90
|
+
CLAUDE_FLOW_HOOKS_ENABLED: 'true',
|
|
91
|
+
CLAUDE_FLOW_TOPOLOGY: 'hierarchical-mesh',
|
|
92
|
+
CLAUDE_FLOW_MAX_AGENTS: '15',
|
|
93
|
+
CLAUDE_FLOW_MEMORY_BACKEND: 'hybrid',
|
|
94
|
+
MCP_GROUP_SECURITY: 'true',
|
|
95
|
+
MCP_GROUP_BROWSER: 'true',
|
|
96
|
+
MCP_GROUP_NEURAL: 'true',
|
|
97
|
+
MCP_GROUP_AGENTIC_FLOW: 'true'
|
|
98
|
+
},
|
|
99
|
+
autoStart: false
|
|
100
|
+
},
|
|
101
|
+
'ruv-swarm': {
|
|
102
|
+
command,
|
|
103
|
+
args: makeArgs('ruv-swarm', ['mcp', 'start']),
|
|
104
|
+
env: {
|
|
105
|
+
npm_config_update_notifier: 'false'
|
|
106
|
+
},
|
|
107
|
+
optional: true
|
|
108
|
+
},
|
|
109
|
+
'flow-nexus': {
|
|
110
|
+
command,
|
|
111
|
+
args: makeArgs('flow-nexus@latest', ['mcp', 'start']),
|
|
112
|
+
env: {
|
|
113
|
+
npm_config_update_notifier: 'false'
|
|
114
|
+
},
|
|
115
|
+
optional: true,
|
|
116
|
+
requiresAuth: true
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
package/templates/ruflo-setup.md
CHANGED
|
@@ -1,90 +1,92 @@
|
|
|
1
|
-
# /ruflo-setup
|
|
2
|
-
|
|
3
|
-
Set up Ruflo + Claude Flow V3 in the current project directory.
|
|
4
|
-
|
|
5
|
-
## Requirements
|
|
6
|
-
|
|
7
|
-
- Node.js 20+
|
|
8
|
-
- pnpm 10.32.1+ installed and available on PATH
|
|
9
|
-
|
|
10
|
-
Quickest pnpm install by platform:
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
# Windows (recommended)
|
|
14
|
-
winget install -e --id pnpm.pnpm
|
|
15
|
-
|
|
16
|
-
# macOS (recommended)
|
|
17
|
-
brew install pnpm
|
|
18
|
-
|
|
19
|
-
# Linux (recommended)
|
|
20
|
-
curl -fsSL https://get.pnpm.io/install.sh | sh -
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Alternative (all platforms with recent Node.js):
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
corepack enable
|
|
27
|
-
corepack prepare pnpm@latest --activate
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## What this does
|
|
31
|
-
|
|
32
|
-
Runs `pnpm add -g @mfjjs/ruflo-setup` then `ruflo-setup` which:
|
|
33
|
-
|
|
34
|
-
1. Runs `pnpm add -g ruflo@latest` then `ruflo init --full` to install:
|
|
35
|
-
- `.claude/settings.json` with hooks, permissions, and Claude Flow config
|
|
36
|
-
- `.claude/helpers/` — hook-handler, statusline, auto-memory scripts
|
|
37
|
-
- `.claude/agents/` — 120+ agent definitions
|
|
38
|
-
- `.claude/skills/` — 30+ skill definitions
|
|
39
|
-
- `.claude/commands/` — slash commands
|
|
40
|
-
2. Writes a platform-aware `.mcp.json` (MCP server registration for claude-flow, ruv-swarm, flow-nexus)
|
|
41
|
-
3.
|
|
42
|
-
4.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
pnpm add -g
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
|
|
1
|
+
# /ruflo-setup
|
|
2
|
+
|
|
3
|
+
Set up Ruflo + Claude Flow V3 in the current project directory.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Node.js 20+
|
|
8
|
+
- pnpm 10.32.1+ installed and available on PATH
|
|
9
|
+
|
|
10
|
+
Quickest pnpm install by platform:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
# Windows (recommended)
|
|
14
|
+
winget install -e --id pnpm.pnpm
|
|
15
|
+
|
|
16
|
+
# macOS (recommended)
|
|
17
|
+
brew install pnpm
|
|
18
|
+
|
|
19
|
+
# Linux (recommended)
|
|
20
|
+
curl -fsSL https://get.pnpm.io/install.sh | sh -
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Alternative (all platforms with recent Node.js):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
corepack enable
|
|
27
|
+
corepack prepare pnpm@latest --activate
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## What this does
|
|
31
|
+
|
|
32
|
+
Runs `pnpm add -g @mfjjs/ruflo-setup` then `ruflo-setup` which:
|
|
33
|
+
|
|
34
|
+
1. Runs `pnpm add -g ruflo@latest` then `ruflo init --full` to install:
|
|
35
|
+
- `.claude/settings.json` with hooks, permissions, and Claude Flow config
|
|
36
|
+
- `.claude/helpers/` — hook-handler, statusline, auto-memory scripts
|
|
37
|
+
- `.claude/agents/` — 120+ agent definitions
|
|
38
|
+
- `.claude/skills/` — 30+ skill definitions
|
|
39
|
+
- `.claude/commands/` — slash commands
|
|
40
|
+
2. Writes a platform-aware `.mcp.json` (MCP server registration for claude-flow, ruv-swarm, flow-nexus; servers invoked via `npx` for cross-platform compatibility)
|
|
41
|
+
3. Adds `.mcp.json` and `.claude/settings.json` to the project's `.gitignore`
|
|
42
|
+
4. Installs a global `SessionStart` hook in `~/.claude/settings.json` that warns when Ruflo is not configured
|
|
43
|
+
5. May refresh `~/.claude/commands/ruflo-setup.md` from the latest packaged template when differences are detected
|
|
44
|
+
|
|
45
|
+
## Options
|
|
46
|
+
|
|
47
|
+
### cleanup
|
|
48
|
+
|
|
49
|
+
Removes all Ruflo-related packages from the **npm** global registry (does not touch pnpm globals).
|
|
50
|
+
|
|
51
|
+
Packages removed: `ruflo`, `@mfjjs/ruflo-setup`, `ruflo-setup`, `claude-flow`, `@claude-flow/cli`, `ruv-swarm`
|
|
52
|
+
|
|
53
|
+
## Instructions for Claude
|
|
54
|
+
|
|
55
|
+
When the user runs /ruflo-setup:
|
|
56
|
+
|
|
57
|
+
### Default (no arguments) — install
|
|
58
|
+
|
|
59
|
+
1. Confirm the current working directory with the user
|
|
60
|
+
2. Check if `.mcp.json` already exists — if so, warn and ask before overwriting
|
|
61
|
+
3. Check pnpm version is at least 10.32.1:
|
|
62
|
+
```bash
|
|
63
|
+
pnpm --version
|
|
64
|
+
```
|
|
65
|
+
If the version is lower than 10.32.1, stop and tell the user to upgrade pnpm before continuing.
|
|
66
|
+
4. Run the setup CLI and capture output to detect whether pnpm modified anything:
|
|
67
|
+
```bash
|
|
68
|
+
pnpm add -g @mfjjs/ruflo-setup
|
|
69
|
+
pnpm add -g ruflo@latest 2>&1 | tee /tmp/ruflo-pnpm-add.log
|
|
70
|
+
```
|
|
71
|
+
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:
|
|
72
|
+
```bash
|
|
73
|
+
pnpm approve-builds -g --all
|
|
74
|
+
```
|
|
75
|
+
Skip `approve-builds` if nothing changed.
|
|
76
|
+
5. Run the setup tool:
|
|
77
|
+
```bash
|
|
78
|
+
ruflo-setup
|
|
79
|
+
```
|
|
80
|
+
6. Ensure `.mcp.json` and `.claude/settings.json` are in the project's `.gitignore` (ruflo-setup does this automatically, but verify if using `--skip-init`)
|
|
81
|
+
7. Report what was installed and remind the user to restart Claude Code to load the new MCP servers
|
|
82
|
+
|
|
83
|
+
### cleanup
|
|
84
|
+
|
|
85
|
+
When the user runs `/ruflo-setup cleanup`:
|
|
86
|
+
|
|
87
|
+
1. Warn the user that this will remove Ruflo packages from the **npm** global registry and ask for confirmation
|
|
88
|
+
2. On confirmation, run:
|
|
89
|
+
```bash
|
|
90
|
+
ruflo-setup cleanup
|
|
91
|
+
```
|
|
92
|
+
3. Report which packages were removed and which were not found (not found is fine — it means they were already clean)
|