@mfjjs/ruflo-setup 0.1.8 โ 0.2.1
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 +24 -0
- package/README.md +38 -11
- package/package.json +1 -1
- package/src/cli.js +8 -0
- package/src/status.js +303 -0
- package/src/utils.js +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
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.1](https://gitlab.mfj.local:8022/mario/ruflo-setup/compare/v0.2.0...v0.2.1) (2026-03-14)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **docs:** update usage section by moving status command first ([e98f473](https://gitlab.mfj.local:8022/mario/ruflo-setup/commit/e98f473f25f53031d8cf08cf8d6bd3bfedcae949))
|
|
11
|
+
|
|
12
|
+
## [0.2.0](https://gitlab.mfj.local:8022/mario/ruflo-setup/compare/v0.1.9...v0.2.0) (2026-03-14)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### โ BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
* **cli:** The CLI now includes a new command which may affect existing scripts that rely on the previous command structure.
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **cli:** add 'status' command to check feature status ([d551664](https://gitlab.mfj.local:8022/mario/ruflo-setup/commit/d551664619635e7efbbc9a9810266ecfe2212d1c))
|
|
22
|
+
* **docs:** add detailed agents and skills sections to Ruflo benefit documentation ([c66d212](https://gitlab.mfj.local:8022/mario/ruflo-setup/commit/c66d212f4ee25390ab139e43131ecb548b7bcb5c))
|
|
23
|
+
* **docs:** add link to ruflo-benefits.md and renamed to plural ([2820aeb](https://gitlab.mfj.local:8022/mario/ruflo-setup/commit/2820aeb5cebbfad0ac7b568291aa55a49076c6c3))
|
|
24
|
+
* **docs:** update Ruflo benefit documentation with agents and skills sections ([8feb883](https://gitlab.mfj.local:8022/mario/ruflo-setup/commit/8feb8836c88ec5096a907eb4c842b0b3b4c55b4c))
|
|
25
|
+
* **status:** enhance directory checks to display count of agents and skills ([3ade22b](https://gitlab.mfj.local:8022/mario/ruflo-setup/commit/3ade22b10ba1840f405b8f239f2a723f6d4089d7))
|
|
26
|
+
|
|
27
|
+
### [0.1.9](https://gitlab.mfj.local:8022/mario/ruflo-setup/compare/v0.1.8...v0.1.9) (2026-03-13)
|
|
28
|
+
|
|
5
29
|
### [0.1.8](https://gitlab.mfj.local:8022/mario/ruflo-setup/compare/v0.1.7...v0.1.8) (2026-03-13)
|
|
6
30
|
|
|
7
31
|
### [0.1.7](https://gitlab.mfj.local:8022/mario/ruflo-setup/compare/v0.1.6...v0.1.7) (2026-03-13)
|
package/README.md
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Cross-platform npm CLI to bootstrap a project with Ruflo on Windows and Linux.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Deep dive:** [`docs/ruflo-benefits.md`](docs/ruflo-benefits.md) is a comprehensive reference (~900 lines) covering every feature layer, the MinCut/RuVector/RVF internals, all 80+ agents, 33 skills, 65 slash commands, and how agents and skills are invoked. Not a quick read.
|
|
6
|
+
|
|
7
|
+
## ๐ What this project is
|
|
6
8
|
|
|
7
9
|
`@mfjjs/ruflo-setup` implements the setup with a Node-based CLI command:
|
|
8
10
|
|
|
9
11
|
- Package name: `@mfjjs/ruflo-setup`
|
|
10
12
|
- Command name: `ruflo-setup`
|
|
11
13
|
- Platform support: Windows and Linux (plus macOS by default)
|
|
12
|
-
## Requirements
|
|
14
|
+
## ๐ Requirements
|
|
13
15
|
<details>
|
|
14
16
|
<summary>Click to toggle visibility</summary>
|
|
15
17
|
|
|
@@ -37,13 +39,35 @@ corepack prepare pnpm@latest --activate
|
|
|
37
39
|
```
|
|
38
40
|
</details>
|
|
39
41
|
|
|
40
|
-
## Installation
|
|
42
|
+
## ๐ฆ Installation
|
|
41
43
|
|
|
42
44
|
```powershell
|
|
43
45
|
pnpm -i -g @mfjjs/ruflo-setup
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
##
|
|
48
|
+
## ๐ก Why You Need This
|
|
49
|
+
|
|
50
|
+
If youโre working on:
|
|
51
|
+
|
|
52
|
+
* A brownfield application that never had RuFlow configured, or
|
|
53
|
+
* A brandโnew project that hasnโt been set up with RuFlow yet,
|
|
54
|
+
|
|
55
|
+
โฆthen you currently have to configure RuFlow manually in each project. That means recreating the same structure, wiring, and boilerplate over and over.
|
|
56
|
+
|
|
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.
|
|
58
|
+
|
|
59
|
+
You only need to do this once in each folder. Just run the command and youโre ready to go.
|
|
60
|
+
|
|
61
|
+
## ๐ Usage
|
|
62
|
+
|
|
63
|
+
### Status
|
|
64
|
+
Check whether all Ruflo feature layers (0โ8) are enabled in the current project:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ruflo-setup status
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
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.
|
|
47
71
|
|
|
48
72
|
### Bootstrap
|
|
49
73
|
Use this once if you want Claude Code to expose the `/ruflo-setup` command globally.
|
|
@@ -77,19 +101,20 @@ ruflo-setup hooks install
|
|
|
77
101
|
ruflo-setup hooks status
|
|
78
102
|
```
|
|
79
103
|
|
|
80
|
-
## Project structure
|
|
104
|
+
## ๐๏ธ Project structure
|
|
81
105
|
|
|
82
106
|
- `package.json`: npm metadata, scripts, and `bin` mapping
|
|
83
107
|
- `bin/ruflo-setup.js`: executable entry file the shell runs
|
|
84
108
|
- `src/cli.js`: command router and argument handling
|
|
85
109
|
- `src/setup.js`: setup workflow (`init`, `.mcp.json`, template copy)
|
|
110
|
+
- `src/status.js`: layer-by-layer feature status checker (Layers 0โ8)
|
|
86
111
|
- `src/hooks.js`: global `check-ruflo` hook install/status
|
|
87
112
|
- `src/utils.js`: reusable filesystem and argument helpers
|
|
88
113
|
- `templates/CLAUDE.md`: bundled template copied into target project
|
|
89
114
|
- `claude-hooks/check-ruflo.cjs`: SessionStart hook payload
|
|
90
115
|
- `tests/cli.test.mjs`: smoke tests for CLI behavior
|
|
91
116
|
|
|
92
|
-
## What the command line calls
|
|
117
|
+
## ๐ฅ๏ธ What the command line calls
|
|
93
118
|
|
|
94
119
|
After install/link, `ruflo-setup` resolves to your package `bin` entry:
|
|
95
120
|
|
|
@@ -103,7 +128,7 @@ After install/link, `ruflo-setup` resolves to your package `bin` entry:
|
|
|
103
128
|
|
|
104
129
|
The shell shim launches `bin/ruflo-setup.js`, which imports `src/cli.js`, which dispatches to setup or hook subcommands.
|
|
105
130
|
|
|
106
|
-
## How the CLI entry point works
|
|
131
|
+
## โ๏ธ How the CLI entry point works
|
|
107
132
|
|
|
108
133
|
Flow:
|
|
109
134
|
|
|
@@ -117,7 +142,9 @@ Flow:
|
|
|
117
142
|
- copies `templates/CLAUDE.md`
|
|
118
143
|
- installs global SessionStart hook (unless skipped)
|
|
119
144
|
|
|
120
|
-
|
|
145
|
+
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
|
+
|
|
147
|
+
## ๐ ๏ธ Local development with pnpm
|
|
121
148
|
|
|
122
149
|
From this repository root (`setup-ruflo/`):
|
|
123
150
|
|
|
@@ -127,7 +154,7 @@ pnpm test
|
|
|
127
154
|
pnpm run test:cli
|
|
128
155
|
```
|
|
129
156
|
|
|
130
|
-
## Link locally so command works everywhere
|
|
157
|
+
## ๐ Link locally so command works everywhere
|
|
131
158
|
|
|
132
159
|
```bash
|
|
133
160
|
# from setup-ruflo/
|
|
@@ -139,7 +166,7 @@ ruflo-setup --dry-run --skip-init
|
|
|
139
166
|
|
|
140
167
|
This is the fast edit loop: change files in `src/`, rerun `ruflo-setup`, and behavior updates immediately without reinstall.
|
|
141
168
|
|
|
142
|
-
## Simulate a real install (deploy-style testing)
|
|
169
|
+
## ๐งช Simulate a real install (deploy-style testing)
|
|
143
170
|
|
|
144
171
|
Create a tarball and install it into a clean test location.
|
|
145
172
|
|
|
@@ -154,7 +181,7 @@ ruflo-setup --dry-run --skip-init
|
|
|
154
181
|
|
|
155
182
|
This tests exactly what users get from a package install.
|
|
156
183
|
|
|
157
|
-
## Global hook behavior
|
|
184
|
+
## ๐ช Global hook behavior
|
|
158
185
|
|
|
159
186
|
`ruflo-setup` installs a global Claude SessionStart command hook that runs:
|
|
160
187
|
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ function printHelp() {
|
|
|
18
18
|
|
|
19
19
|
Usage:
|
|
20
20
|
ruflo-setup [options]
|
|
21
|
+
ruflo-setup status
|
|
21
22
|
ruflo-setup hooks install [options]
|
|
22
23
|
ruflo-setup hooks status
|
|
23
24
|
|
|
@@ -32,6 +33,7 @@ Options:
|
|
|
32
33
|
|
|
33
34
|
Examples:
|
|
34
35
|
ruflo-setup
|
|
36
|
+
ruflo-setup status
|
|
35
37
|
ruflo-setup --dry-run --skip-init
|
|
36
38
|
ruflo-setup hooks status
|
|
37
39
|
ruflo-setup hooks install --dry-run
|
|
@@ -58,6 +60,12 @@ export async function runCli(argv, cwd) {
|
|
|
58
60
|
const packageRoot = packageRootFromModule();
|
|
59
61
|
const flags = parseArgs(argv);
|
|
60
62
|
|
|
63
|
+
if (flags.command === 'status') {
|
|
64
|
+
const { runStatus } = await import('./status.js');
|
|
65
|
+
await runStatus({ cwd, packageRoot });
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
if (flags.command === 'hooks') {
|
|
62
70
|
const subcommand = argv[1] || 'status';
|
|
63
71
|
if (subcommand === 'status') {
|
package/src/status.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { spawnSync } from 'node:child_process';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
6
|
+
import { readJsonSafe } from './utils.js';
|
|
7
|
+
import { getGlobalHookStatus } from './hooks.js';
|
|
8
|
+
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
const OK = '[OK]';
|
|
11
|
+
const MISS = '[--]';
|
|
12
|
+
const ERR = '[!!]';
|
|
13
|
+
const IS_WIN = process.platform === 'win32';
|
|
14
|
+
|
|
15
|
+
function spawn(cmd, args) {
|
|
16
|
+
try {
|
|
17
|
+
return spawnSync(cmd, args, {
|
|
18
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
shell: IS_WIN
|
|
21
|
+
});
|
|
22
|
+
} catch {
|
|
23
|
+
return { status: 1, stdout: '' };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function dirExists(p) {
|
|
28
|
+
try {
|
|
29
|
+
return fs.statSync(p).isDirectory();
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function fileExists(p) {
|
|
36
|
+
try {
|
|
37
|
+
return fs.statSync(p).isFile();
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Flatten npm/pnpm --json list result into a name->version map (1 level deep).
|
|
44
|
+
function buildPkgMap(jsonText) {
|
|
45
|
+
const map = {};
|
|
46
|
+
try {
|
|
47
|
+
const parsed = JSON.parse(jsonText || '{}');
|
|
48
|
+
// npm list -g returns an array with one element, pnpm returns an object
|
|
49
|
+
const root = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
50
|
+
const deps = root?.dependencies ?? {};
|
|
51
|
+
for (const [name, info] of Object.entries(deps)) {
|
|
52
|
+
map[name] = info?.version ?? true;
|
|
53
|
+
for (const [nname, ninfo] of Object.entries(info?.dependencies ?? {})) {
|
|
54
|
+
if (!(nname in map)) map[nname] = ninfo?.version ?? true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// ignore parse errors
|
|
59
|
+
}
|
|
60
|
+
return map;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Tries npm list -g first, falls back to pnpm list -g.
|
|
64
|
+
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
|
+
const pnpmRes = spawn('pnpm', ['list', '-g', '--depth=1', '--json']);
|
|
71
|
+
if (pnpmRes.status === 0 && pnpmRes.stdout) {
|
|
72
|
+
return buildPkgMap(pnpmRes.stdout);
|
|
73
|
+
}
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Each checkLayer* returns { lines: string[], ok: number, total: number }
|
|
78
|
+
|
|
79
|
+
function checkLayer0() {
|
|
80
|
+
const lines = [];
|
|
81
|
+
let ok = 0;
|
|
82
|
+
const nodeMajor = parseInt(process.version.slice(1), 10);
|
|
83
|
+
if (nodeMajor >= 20) { lines.push(` ${OK} Node.js ${process.version} (>=20 required)`); ok += 1; }
|
|
84
|
+
else { lines.push(` ${ERR} Node.js ${process.version} (>=20 required โ upgrade Node.js)`); }
|
|
85
|
+
|
|
86
|
+
const pnpmRes = spawn('pnpm', ['--version']);
|
|
87
|
+
if (pnpmRes.status === 0 && pnpmRes.stdout.trim()) {
|
|
88
|
+
lines.push(` ${OK} pnpm ${pnpmRes.stdout.trim()}`); ok += 1;
|
|
89
|
+
} else {
|
|
90
|
+
lines.push(` ${MISS} pnpm (install: npm install -g pnpm)`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const claudeRes = spawn('claude', ['--version']);
|
|
94
|
+
if (claudeRes.status === 0) {
|
|
95
|
+
const ver = (claudeRes.stdout || '').trim();
|
|
96
|
+
lines.push(` ${OK} Claude Code CLI${ver ? ` ${ver}` : ''}`); ok += 1;
|
|
97
|
+
} else {
|
|
98
|
+
lines.push(` ${MISS} Claude Code CLI (install: npm install -g @anthropic-ai/claude-code)`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
102
|
+
lines.push(` ${OK} ANTHROPIC_API_KEY set`); ok += 1;
|
|
103
|
+
} else {
|
|
104
|
+
lines.push(` ${ERR} ANTHROPIC_API_KEY not set (required for LLM calls)`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return { lines, ok, total: 4 };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function checkLayer1(pkgMap) {
|
|
111
|
+
const lines = [];
|
|
112
|
+
let ok = 0;
|
|
113
|
+
for (const name of ['ruflo', '@mfjjs/ruflo-setup']) {
|
|
114
|
+
const ver = pkgMap[name];
|
|
115
|
+
if (ver) { lines.push(` ${OK} ${name}${typeof ver === 'string' ? `@${ver}` : ''}`); ok += 1; }
|
|
116
|
+
else { lines.push(` ${MISS} ${name} (install: npm install -g ${name})`); }
|
|
117
|
+
}
|
|
118
|
+
return { lines, ok, total: 2 };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function checkLayer2(pkgMap) {
|
|
122
|
+
const lines = [];
|
|
123
|
+
let ok = 0;
|
|
124
|
+
const ATTENTION_WIN_NOTE = '(Windows: requires Windows 11 SDK for NAPI; WASM fallback available)';
|
|
125
|
+
const pkgs = [
|
|
126
|
+
'@claude-flow/memory', '@ruvector/attention', '@claude-flow/aidefence', 'agentic-flow',
|
|
127
|
+
'@ruvector/sona', '@ruvector/router', '@ruvector/learning-wasm',
|
|
128
|
+
'@claude-flow/embeddings', '@claude-flow/guidance', '@claude-flow/codex'
|
|
129
|
+
];
|
|
130
|
+
for (const name of pkgs) {
|
|
131
|
+
const ver = pkgMap[name];
|
|
132
|
+
if (ver) { lines.push(` ${OK} ${name}${typeof ver === 'string' ? `@${ver}` : ''}`); ok += 1; }
|
|
133
|
+
else {
|
|
134
|
+
const note = (name === '@ruvector/attention' && IS_WIN) ? ` ${ATTENTION_WIN_NOTE}` : '';
|
|
135
|
+
lines.push(` ${MISS} ${name}${note}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { lines, ok, total: pkgs.length };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function checkLayer3(mcpJson) {
|
|
142
|
+
const lines = [];
|
|
143
|
+
let ok = 0;
|
|
144
|
+
const servers = mcpJson?.mcpServers ?? {};
|
|
145
|
+
|
|
146
|
+
if (servers['claude-flow']) {
|
|
147
|
+
const args = servers['claude-flow']?.args ?? [];
|
|
148
|
+
const pkgArg = args.find((a) => typeof a === 'string' && a.includes('@claude-flow/cli')) ?? '@claude-flow/cli@latest';
|
|
149
|
+
lines.push(` ${OK} claude-flow (${pkgArg})`); ok += 1;
|
|
150
|
+
} else {
|
|
151
|
+
lines.push(` ${MISS} claude-flow (run ruflo-setup to configure)`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (servers['ruv-swarm']) { lines.push(` ${OK} ruv-swarm (optional)`); ok += 1; }
|
|
155
|
+
else { lines.push(` ${MISS} ruv-swarm (optional)`); }
|
|
156
|
+
|
|
157
|
+
if (servers['flow-nexus']) { lines.push(` ${OK} flow-nexus (optional)`); ok += 1; }
|
|
158
|
+
else { lines.push(` ${MISS} flow-nexus (optional โ needs Cognitum.One account)`); }
|
|
159
|
+
|
|
160
|
+
return { lines, ok, total: 3 };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function checkLayer4(mcpJson) {
|
|
164
|
+
const lines = [];
|
|
165
|
+
let ok = 0;
|
|
166
|
+
const cfEnv = mcpJson?.mcpServers?.['claude-flow']?.env ?? {};
|
|
167
|
+
const resolve = (k) => { const v = cfEnv[k] ?? process.env[k]; return v === undefined ? null : String(v).toLowerCase(); };
|
|
168
|
+
|
|
169
|
+
for (const g of ['INTELLIGENCE', 'AGENTS', 'MEMORY', 'DEVTOOLS']) {
|
|
170
|
+
if (resolve(`MCP_GROUP_${g}`) === 'false') { lines.push(` ${MISS} ${g} (disabled via MCP_GROUP_${g}=false)`); }
|
|
171
|
+
else { lines.push(` ${OK} ${g} (default on)`); ok += 1; }
|
|
172
|
+
}
|
|
173
|
+
for (const g of ['SECURITY', 'BROWSER', 'NEURAL', 'AGENTIC_FLOW']) {
|
|
174
|
+
if (resolve(`MCP_GROUP_${g}`) === 'true') { lines.push(` ${OK} ${g} (enabled)`); ok += 1; }
|
|
175
|
+
else { lines.push(` ${MISS} ${g} (set MCP_GROUP_${g}=true in .mcp.json env)`); }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return { lines, ok, total: 8 };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function checkLayer5() {
|
|
182
|
+
const lines = [];
|
|
183
|
+
let ok = 0;
|
|
184
|
+
const checks = [
|
|
185
|
+
{ key: 'ANTHROPIC_API_KEY', req: true, note: '' },
|
|
186
|
+
{ key: 'OPENAI_API_KEY', req: false, note: '(optional โ enables GPT + Codex)' },
|
|
187
|
+
{ key: 'GOOGLE_API_KEY', req: false, note: '(optional โ enables Gemini)' },
|
|
188
|
+
{ key: 'OPENROUTER_API_KEY', req: false, note: '(optional โ multi-provider proxy)' }
|
|
189
|
+
];
|
|
190
|
+
for (const { key, req, note } of checks) {
|
|
191
|
+
if (process.env[key]) { lines.push(` ${OK} ${key}`); ok += 1; }
|
|
192
|
+
else if (req) { lines.push(` ${ERR} ${key} not set (required for LLM calls)`); }
|
|
193
|
+
else { lines.push(` ${MISS} ${key} ${note}`); }
|
|
194
|
+
}
|
|
195
|
+
return { lines, ok, total: checks.length };
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function checkLayer6(packageRoot) {
|
|
199
|
+
const lines = [];
|
|
200
|
+
let ok = 0;
|
|
201
|
+
const homeDir = os.homedir();
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
const hs = getGlobalHookStatus({ packageRoot });
|
|
205
|
+
const sp = hs.settingsPath ?? path.join(homeDir, '.claude', 'settings.json');
|
|
206
|
+
if (hs.installed) { lines.push(` ${OK} SessionStart hook (${sp})`); ok += 1; }
|
|
207
|
+
else { lines.push(` ${MISS} SessionStart hook (${sp})`); }
|
|
208
|
+
} catch {
|
|
209
|
+
lines.push(` ${MISS} SessionStart hook (could not read ~/.claude/settings.json)`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const commandFile = path.join(homeDir, '.claude', 'commands', 'ruflo-setup.md');
|
|
213
|
+
if (fileExists(commandFile)) { lines.push(` ${OK} /ruflo-setup command (${commandFile})`); ok += 1; }
|
|
214
|
+
else { lines.push(` ${MISS} /ruflo-setup command (${commandFile})`); }
|
|
215
|
+
|
|
216
|
+
return { lines, ok, total: 2 };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function checkLayer7(cwd) {
|
|
220
|
+
const lines = [];
|
|
221
|
+
let ok = 0;
|
|
222
|
+
const files = ['.mcp.json', 'CLAUDE.md', path.join('.claude', 'settings.json')];
|
|
223
|
+
const dirs = [
|
|
224
|
+
{ rel: path.join('.claude', 'agents'), hint: 'run: ruflo init --full' },
|
|
225
|
+
{ rel: path.join('.claude', 'skills'), hint: null },
|
|
226
|
+
{ rel: path.join('.claude', 'commands'), hint: null },
|
|
227
|
+
{ rel: '.claude-flow', hint: null }
|
|
228
|
+
];
|
|
229
|
+
|
|
230
|
+
for (const rel of files) {
|
|
231
|
+
if (fileExists(path.join(cwd, rel))) { lines.push(` ${OK} ${rel}`); ok += 1; }
|
|
232
|
+
else { lines.push(` ${MISS} ${rel}`); }
|
|
233
|
+
}
|
|
234
|
+
for (const { rel, hint } of dirs) {
|
|
235
|
+
const disp = `${rel}/`.replace(/\\/g, '/');
|
|
236
|
+
const full = path.join(cwd, rel);
|
|
237
|
+
if (dirExists(full)) {
|
|
238
|
+
let count = '';
|
|
239
|
+
if (rel.endsWith('agents') || rel.endsWith('skills')) {
|
|
240
|
+
try {
|
|
241
|
+
const n = fs.readdirSync(full).length;
|
|
242
|
+
const label = rel.endsWith('agents') ? 'agents' : 'skills';
|
|
243
|
+
count = ` (${n} ${label})`;
|
|
244
|
+
} catch { /* ignore */ }
|
|
245
|
+
}
|
|
246
|
+
lines.push(` ${OK} ${disp}${count}`); ok += 1;
|
|
247
|
+
} else {
|
|
248
|
+
lines.push(` ${MISS} ${disp}${hint ? ` (${hint})` : ''}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return { lines, ok, total: files.length + dirs.length };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function checkLayer8() {
|
|
256
|
+
const res = spawn('docker', ['--version']);
|
|
257
|
+
if (res.status === 0 && res.stdout.trim()) {
|
|
258
|
+
return { lines: [` ${OK} Docker ${res.stdout.trim()}`], ok: 1, total: 1 };
|
|
259
|
+
}
|
|
260
|
+
return { lines: [` ${MISS} Docker not detected (optional โ needed for ruvocal chat UI)`], ok: 0, total: 1 };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export async function runStatus({ cwd, packageRoot }) {
|
|
264
|
+
try {
|
|
265
|
+
const { version } = require('../package.json');
|
|
266
|
+
const mcpJson = readJsonSafe(path.join(cwd, '.mcp.json'), {});
|
|
267
|
+
const pkgMap = getGlobalPkgMap();
|
|
268
|
+
|
|
269
|
+
const layers = [
|
|
270
|
+
{ title: 'Layer 0: Prerequisites', result: checkLayer0() },
|
|
271
|
+
{ title: 'Layer 1: Global npm Packages', result: checkLayer1(pkgMap) },
|
|
272
|
+
{ title: 'Layer 2: Optional Packages (WASM/ML) โ enables AI features', result: checkLayer2(pkgMap) },
|
|
273
|
+
{ title: 'Layer 3: MCP Servers (.mcp.json)', result: checkLayer3(mcpJson) },
|
|
274
|
+
{ title: 'Layer 4: MCP Tool Groups', result: checkLayer4(mcpJson) },
|
|
275
|
+
{ title: 'Layer 5: Environment Variables', result: checkLayer5() },
|
|
276
|
+
{ title: 'Layer 6: Claude Code Hooks', result: checkLayer6(packageRoot) },
|
|
277
|
+
{ title: 'Layer 7: Project Scaffolding', result: checkLayer7(cwd) },
|
|
278
|
+
{ title: 'Layer 8: Docker Chat UI (optional)', result: checkLayer8() }
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
let totalOk = 0;
|
|
282
|
+
let totalChecks = 0;
|
|
283
|
+
|
|
284
|
+
process.stdout.write(`\nRuflo Feature Status (ruflo-setup v${version})\n`);
|
|
285
|
+
process.stdout.write(`Target: ${cwd}\n`);
|
|
286
|
+
|
|
287
|
+
for (const { title, result } of layers) {
|
|
288
|
+
process.stdout.write(`\n${title}\n`);
|
|
289
|
+
for (const line of result.lines) process.stdout.write(`${line}\n`);
|
|
290
|
+
totalOk += result.ok;
|
|
291
|
+
totalChecks += result.total;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
process.stdout.write(`\nSummary: ${totalOk}/${totalChecks} features enabled\n`);
|
|
295
|
+
process.stdout.write(`For agents, skills, and slash commands reference: docs/ruflo-benefit.md\n`);
|
|
296
|
+
|
|
297
|
+
const hasRequiredMissing = parseInt(process.version.slice(1), 10) < 20 || !process.env.ANTHROPIC_API_KEY;
|
|
298
|
+
if (hasRequiredMissing) process.stdout.write(`Run 'ruflo-setup' to configure missing required features.\n`);
|
|
299
|
+
process.stdout.write('\n');
|
|
300
|
+
} catch (error) {
|
|
301
|
+
process.stderr.write(`status error: ${error.message}\n`);
|
|
302
|
+
}
|
|
303
|
+
}
|
package/src/utils.js
CHANGED
|
@@ -90,7 +90,11 @@ export function toPlatformMcpConfig(platform) {
|
|
|
90
90
|
CLAUDE_FLOW_HOOKS_ENABLED: 'true',
|
|
91
91
|
CLAUDE_FLOW_TOPOLOGY: 'hierarchical-mesh',
|
|
92
92
|
CLAUDE_FLOW_MAX_AGENTS: '15',
|
|
93
|
-
CLAUDE_FLOW_MEMORY_BACKEND: 'hybrid'
|
|
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'
|
|
94
98
|
},
|
|
95
99
|
autoStart: false
|
|
96
100
|
},
|