hive-rank 3.3.0 → 3.4.0
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/README.md +35 -1
- package/bin/install.js +308 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Crowdsourced SEO intelligence for AI agents.
|
|
|
4
4
|
|
|
5
5
|
## What is this?
|
|
6
6
|
|
|
7
|
-
Hive Rank aggregates anonymized search data from
|
|
7
|
+
Hive Rank aggregates anonymized search data from AI coding agents into a shared ranking dataset. Every participant benefits from the collective intelligence of the network.
|
|
8
8
|
|
|
9
9
|
**AI agents are searching for your product right now. Do you know where you rank?**
|
|
10
10
|
|
|
@@ -14,12 +14,46 @@ Hive Rank aggregates anonymized search data from Claude Code agents into a share
|
|
|
14
14
|
npx hive-rank
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
The installer auto-detects Claude Code, OpenCode, and Codex CLI and configures all detected platforms.
|
|
18
|
+
|
|
17
19
|
Or add the MCP server directly:
|
|
18
20
|
|
|
21
|
+
**Claude Code:**
|
|
19
22
|
```bash
|
|
20
23
|
claude mcp add --transport http hive-rank https://mcp.hive-rank.com/mcp
|
|
21
24
|
```
|
|
22
25
|
|
|
26
|
+
**OpenCode** — add to `~/.config/opencode/opencode.json`:
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcp": {
|
|
30
|
+
"hive-rank": {
|
|
31
|
+
"type": "remote",
|
|
32
|
+
"url": "https://mcp.hive-rank.com/mcp",
|
|
33
|
+
"enabled": true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Codex CLI** — add to `~/.codex/config.toml`:
|
|
40
|
+
```toml
|
|
41
|
+
[features]
|
|
42
|
+
experimental_use_rmcp_client = true
|
|
43
|
+
|
|
44
|
+
[mcp_servers.hive_rank]
|
|
45
|
+
url = "https://mcp.hive-rank.com/mcp"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Platform Capabilities
|
|
49
|
+
|
|
50
|
+
| Feature | Claude Code | OpenCode | Codex CLI |
|
|
51
|
+
|---------|------------|----------|-----------|
|
|
52
|
+
| MCP query tools | Yes | Yes | Yes |
|
|
53
|
+
| Data capture (hooks) | Yes | No | No |
|
|
54
|
+
| Slash commands (21) | Yes | No | No |
|
|
55
|
+
| Auto-install | Yes | Yes | Yes |
|
|
56
|
+
|
|
23
57
|
## What you get
|
|
24
58
|
|
|
25
59
|
### 8 MCP Tools
|
package/bin/install.js
CHANGED
|
@@ -29,6 +29,12 @@ const CLAUDE_DIR = path.join(HOME, '.claude');
|
|
|
29
29
|
const COMMANDS_DIR = path.join(CLAUDE_DIR, 'commands', 'hive');
|
|
30
30
|
const PKG_ROOT = path.resolve(new URL('..', import.meta.url).pathname);
|
|
31
31
|
|
|
32
|
+
const OPENCODE_CONFIG_DIR = path.join(HOME, '.config', 'opencode');
|
|
33
|
+
const OPENCODE_CONFIG_FILE = path.join(OPENCODE_CONFIG_DIR, 'opencode.json');
|
|
34
|
+
|
|
35
|
+
const CODEX_DIR = path.join(HOME, '.codex');
|
|
36
|
+
const CODEX_CONFIG_FILE = path.join(CODEX_DIR, 'config.toml');
|
|
37
|
+
|
|
32
38
|
const MCP_NAME = 'hive-rank';
|
|
33
39
|
const MCP_URL = 'https://mcp.hive-rank.com/mcp';
|
|
34
40
|
const SETTINGS_FILE = path.join(CLAUDE_DIR, 'settings.json');
|
|
@@ -58,6 +64,118 @@ function writeJsonAtomic(p, obj) {
|
|
|
58
64
|
fs.renameSync(tmp, p);
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
// ── TOML helpers (minimal, no deps) ──────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
function parseToml(text) {
|
|
70
|
+
const result = {};
|
|
71
|
+
let currentSection = null;
|
|
72
|
+
|
|
73
|
+
for (const raw of text.split('\n')) {
|
|
74
|
+
const line = raw.trim();
|
|
75
|
+
if (!line || line.startsWith('#')) continue;
|
|
76
|
+
|
|
77
|
+
// Section header: [foo] or [foo.bar]
|
|
78
|
+
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
|
79
|
+
if (sectionMatch) {
|
|
80
|
+
const parts = sectionMatch[1].split('.');
|
|
81
|
+
let target = result;
|
|
82
|
+
for (const part of parts) {
|
|
83
|
+
if (!target[part] || typeof target[part] !== 'object') target[part] = {};
|
|
84
|
+
target = target[part];
|
|
85
|
+
}
|
|
86
|
+
currentSection = parts;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Key = value
|
|
91
|
+
const kvMatch = line.match(/^(\w+)\s*=\s*(.+)$/);
|
|
92
|
+
if (kvMatch) {
|
|
93
|
+
const key = kvMatch[1];
|
|
94
|
+
let val = kvMatch[2].trim();
|
|
95
|
+
// Parse value type
|
|
96
|
+
if (val === 'true') val = true;
|
|
97
|
+
else if (val === 'false') val = false;
|
|
98
|
+
else if (/^-?\d+(\.\d+)?$/.test(val)) val = Number(val);
|
|
99
|
+
else if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'")))
|
|
100
|
+
val = val.slice(1, -1);
|
|
101
|
+
|
|
102
|
+
if (currentSection) {
|
|
103
|
+
let target = result;
|
|
104
|
+
for (const part of currentSection) target = target[part];
|
|
105
|
+
target[key] = val;
|
|
106
|
+
} else {
|
|
107
|
+
result[key] = val;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function serializeToml(obj, prefix = '') {
|
|
115
|
+
let lines = [];
|
|
116
|
+
const simple = {};
|
|
117
|
+
const sections = {};
|
|
118
|
+
|
|
119
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
120
|
+
if (v !== null && typeof v === 'object' && !Array.isArray(v)) {
|
|
121
|
+
sections[k] = v;
|
|
122
|
+
} else {
|
|
123
|
+
simple[k] = v;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Write simple key-value pairs
|
|
128
|
+
for (const [k, v] of Object.entries(simple)) {
|
|
129
|
+
if (typeof v === 'string') lines.push(`${k} = "${v}"`);
|
|
130
|
+
else lines.push(`${k} = ${v}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Write sections
|
|
134
|
+
for (const [k, v] of Object.entries(sections)) {
|
|
135
|
+
const sectionKey = prefix ? `${prefix}.${k}` : k;
|
|
136
|
+
// Check if this section has only simple values (leaf section)
|
|
137
|
+
const hasNestedObjects = Object.values(v).some(val => val !== null && typeof val === 'object' && !Array.isArray(val));
|
|
138
|
+
|
|
139
|
+
if (!hasNestedObjects) {
|
|
140
|
+
if (lines.length > 0) lines.push('');
|
|
141
|
+
lines.push(`[${sectionKey}]`);
|
|
142
|
+
for (const [sk, sv] of Object.entries(v)) {
|
|
143
|
+
if (typeof sv === 'string') lines.push(`${sk} = "${sv}"`);
|
|
144
|
+
else lines.push(`${sk} = ${sv}`);
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
// Recurse for nested sections, writing any simple values first
|
|
148
|
+
const nestedSimple = {};
|
|
149
|
+
const nestedSections = {};
|
|
150
|
+
for (const [sk, sv] of Object.entries(v)) {
|
|
151
|
+
if (sv !== null && typeof sv === 'object' && !Array.isArray(sv)) {
|
|
152
|
+
nestedSections[sk] = sv;
|
|
153
|
+
} else {
|
|
154
|
+
nestedSimple[sk] = sv;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (Object.keys(nestedSimple).length > 0) {
|
|
158
|
+
if (lines.length > 0) lines.push('');
|
|
159
|
+
lines.push(`[${sectionKey}]`);
|
|
160
|
+
for (const [sk, sv] of Object.entries(nestedSimple)) {
|
|
161
|
+
if (typeof sv === 'string') lines.push(`${sk} = "${sv}"`);
|
|
162
|
+
else lines.push(`${sk} = ${sv}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const nested = serializeToml(nestedSections, sectionKey);
|
|
166
|
+
if (nested) lines.push('', nested);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return lines.join('\n');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function writeTomlAtomic(p, obj) {
|
|
174
|
+
const tmp = p + '.tmp.' + crypto.randomBytes(4).toString('hex');
|
|
175
|
+
fs.writeFileSync(tmp, serializeToml(obj) + '\n', 'utf-8');
|
|
176
|
+
fs.renameSync(tmp, p);
|
|
177
|
+
}
|
|
178
|
+
|
|
61
179
|
function copyRecursive(src, dest) {
|
|
62
180
|
if (!fileExists(src)) return;
|
|
63
181
|
const stat = fs.statSync(src);
|
|
@@ -92,6 +210,118 @@ function hasClaudeCli() {
|
|
|
92
210
|
}
|
|
93
211
|
}
|
|
94
212
|
|
|
213
|
+
function hasOpenCode() {
|
|
214
|
+
// Check for opencode CLI in PATH
|
|
215
|
+
try {
|
|
216
|
+
const result = spawnSync('opencode', ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
217
|
+
if (result.status === 0) return true;
|
|
218
|
+
} catch { /* not in PATH */ }
|
|
219
|
+
// Check for config directory existence
|
|
220
|
+
return fileExists(OPENCODE_CONFIG_DIR);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function hasCodexCli() {
|
|
224
|
+
try {
|
|
225
|
+
const result = spawnSync('codex', ['--version'], { stdio: 'pipe', timeout: 5000 });
|
|
226
|
+
if (result.status === 0) return true;
|
|
227
|
+
} catch { /* not in PATH */ }
|
|
228
|
+
return fileExists(CODEX_DIR);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function registerCodexMcp() {
|
|
232
|
+
fs.mkdirSync(CODEX_DIR, { recursive: true });
|
|
233
|
+
|
|
234
|
+
let config = {};
|
|
235
|
+
if (fileExists(CODEX_CONFIG_FILE)) {
|
|
236
|
+
try {
|
|
237
|
+
config = parseToml(fs.readFileSync(CODEX_CONFIG_FILE, 'utf-8'));
|
|
238
|
+
} catch {
|
|
239
|
+
warn('~/.codex/config.toml exists but could not be parsed. Skipping Codex MCP registration.');
|
|
240
|
+
return 'skipped';
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Clean up legacy entries
|
|
245
|
+
if (config.mcp_servers) {
|
|
246
|
+
for (const oldName of ['gys_local', 'hive_seo', 'grow_your_shit', 'hive_rank']) {
|
|
247
|
+
delete config.mcp_servers[oldName];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Ensure feature flag is set
|
|
252
|
+
if (!config.features) config.features = {};
|
|
253
|
+
config.features.experimental_use_rmcp_client = true;
|
|
254
|
+
|
|
255
|
+
// Register MCP server
|
|
256
|
+
if (!config.mcp_servers) config.mcp_servers = {};
|
|
257
|
+
config.mcp_servers.hive_rank = {
|
|
258
|
+
url: MCP_URL,
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
writeTomlAtomic(CODEX_CONFIG_FILE, config);
|
|
262
|
+
return 'direct';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function removeCodexMcp() {
|
|
266
|
+
if (!fileExists(CODEX_CONFIG_FILE)) return;
|
|
267
|
+
try {
|
|
268
|
+
const config = parseToml(fs.readFileSync(CODEX_CONFIG_FILE, 'utf-8'));
|
|
269
|
+
let changed = false;
|
|
270
|
+
if (config.mcp_servers) {
|
|
271
|
+
for (const oldName of ['hive_rank', 'gys_local', 'hive_seo', 'grow_your_shit']) {
|
|
272
|
+
if (config.mcp_servers[oldName]) { delete config.mcp_servers[oldName]; changed = true; }
|
|
273
|
+
}
|
|
274
|
+
if (Object.keys(config.mcp_servers).length === 0) delete config.mcp_servers;
|
|
275
|
+
}
|
|
276
|
+
if (changed) writeTomlAtomic(CODEX_CONFIG_FILE, config);
|
|
277
|
+
} catch { /* ignore */ }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function registerOpenCodeMcp() {
|
|
281
|
+
fs.mkdirSync(OPENCODE_CONFIG_DIR, { recursive: true });
|
|
282
|
+
|
|
283
|
+
let config = {};
|
|
284
|
+
if (fileExists(OPENCODE_CONFIG_FILE)) {
|
|
285
|
+
try {
|
|
286
|
+
config = readJson(OPENCODE_CONFIG_FILE);
|
|
287
|
+
} catch {
|
|
288
|
+
warn('~/.config/opencode/opencode.json exists but is not valid JSON. Skipping OpenCode MCP registration.');
|
|
289
|
+
return 'skipped';
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Clean up old entries
|
|
294
|
+
if (config.mcp) {
|
|
295
|
+
for (const oldName of ['gys-local', 'hive-seo', 'grow-your-shit', MCP_NAME]) {
|
|
296
|
+
delete config.mcp[oldName];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!config.mcp) config.mcp = {};
|
|
301
|
+
config.mcp[MCP_NAME] = {
|
|
302
|
+
type: 'remote',
|
|
303
|
+
url: MCP_URL,
|
|
304
|
+
enabled: true
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
writeJsonAtomic(OPENCODE_CONFIG_FILE, config);
|
|
308
|
+
return 'direct';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function removeOpenCodeMcp() {
|
|
312
|
+
if (!fileExists(OPENCODE_CONFIG_FILE)) return;
|
|
313
|
+
try {
|
|
314
|
+
const config = readJson(OPENCODE_CONFIG_FILE);
|
|
315
|
+
let changed = false;
|
|
316
|
+
if (config.mcp) {
|
|
317
|
+
for (const oldName of [MCP_NAME, 'gys-local', 'hive-seo', 'grow-your-shit']) {
|
|
318
|
+
if (config.mcp[oldName]) { delete config.mcp[oldName]; changed = true; }
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (changed) writeJsonAtomic(OPENCODE_CONFIG_FILE, config);
|
|
322
|
+
} catch { /* ignore */ }
|
|
323
|
+
}
|
|
324
|
+
|
|
95
325
|
function registerMcpServer() {
|
|
96
326
|
if (hasClaudeCli()) {
|
|
97
327
|
// Remove old entries from all scopes
|
|
@@ -269,14 +499,19 @@ if (FLAG_HELP) {
|
|
|
269
499
|
What it does:
|
|
270
500
|
1. Copies dist + commands + docs to ~/.hive-rank/
|
|
271
501
|
2. Registers hooks in ~/.claude/settings.json
|
|
272
|
-
3. Registers remote MCP server
|
|
502
|
+
3. Registers remote MCP server for detected platforms:
|
|
503
|
+
- Claude Code: via \`claude mcp add\` or ~/.claude.json
|
|
504
|
+
- OpenCode: via ~/.config/opencode/opencode.json
|
|
505
|
+
- Codex CLI: via ~/.codex/config.toml
|
|
273
506
|
4. Installs slash commands to ~/.claude/commands/hive/
|
|
274
507
|
|
|
275
508
|
Config locations:
|
|
276
|
-
Hooks:
|
|
277
|
-
MCP
|
|
278
|
-
|
|
279
|
-
|
|
509
|
+
Hooks: ~/.claude/settings.json
|
|
510
|
+
MCP (Claude): ~/.claude.json (via \`claude mcp add --scope user\`)
|
|
511
|
+
MCP (OpenCode): ~/.config/opencode/opencode.json
|
|
512
|
+
MCP (Codex): ~/.codex/config.toml
|
|
513
|
+
Commands: ~/.claude/commands/hive/*.md
|
|
514
|
+
Privacy: ~/.hive-rank/PRIVACY.md
|
|
280
515
|
`);
|
|
281
516
|
process.exit(0);
|
|
282
517
|
}
|
|
@@ -287,7 +522,9 @@ if (FLAG_UNINSTALL) {
|
|
|
287
522
|
if (FLAG_DRY_RUN) {
|
|
288
523
|
console.log('\n Uninstalling Hive Rank — DRY RUN (no changes will be made)\n');
|
|
289
524
|
dryLog('Would remove hooks from ' + SETTINGS_FILE);
|
|
290
|
-
dryLog('Would remove MCP server');
|
|
525
|
+
dryLog('Would remove Claude Code MCP server');
|
|
526
|
+
if (fileExists(OPENCODE_CONFIG_FILE)) dryLog('Would remove OpenCode MCP server from ' + OPENCODE_CONFIG_FILE);
|
|
527
|
+
if (fileExists(CODEX_CONFIG_FILE)) dryLog('Would remove Codex CLI MCP server from ' + CODEX_CONFIG_FILE);
|
|
291
528
|
if (fileExists(COMMANDS_DIR)) dryLog('Would remove slash commands: ' + COMMANDS_DIR);
|
|
292
529
|
if (fileExists(INSTALL_DIR)) dryLog('Would remove ' + INSTALL_DIR + '/');
|
|
293
530
|
console.log('\n Dry run complete — no changes were made.\n');
|
|
@@ -300,7 +537,13 @@ if (FLAG_UNINSTALL) {
|
|
|
300
537
|
success('Removed hooks from settings.json');
|
|
301
538
|
|
|
302
539
|
removeMcpServer();
|
|
303
|
-
success('Removed MCP server');
|
|
540
|
+
success('Removed Claude Code MCP server');
|
|
541
|
+
|
|
542
|
+
removeOpenCodeMcp();
|
|
543
|
+
success('Removed OpenCode MCP server');
|
|
544
|
+
|
|
545
|
+
removeCodexMcp();
|
|
546
|
+
success('Removed Codex CLI MCP server');
|
|
304
547
|
|
|
305
548
|
// Remove old gys commands too
|
|
306
549
|
const oldCommandsDir = path.join(CLAUDE_DIR, 'commands', 'gys');
|
|
@@ -328,7 +571,7 @@ if (FLAG_UNINSTALL) {
|
|
|
328
571
|
}
|
|
329
572
|
}
|
|
330
573
|
|
|
331
|
-
console.log('\n Hive Rank has been uninstalled. Restart
|
|
574
|
+
console.log('\n Hive Rank has been uninstalled. Restart your agent to apply.\n');
|
|
332
575
|
process.exit(0);
|
|
333
576
|
}
|
|
334
577
|
|
|
@@ -435,8 +678,12 @@ async function install() {
|
|
|
435
678
|
dryLog('Would copy PRIVACY.md and AGENT.md to ~/.hive-rank/');
|
|
436
679
|
}
|
|
437
680
|
|
|
438
|
-
// ── Step 3: Register MCP
|
|
681
|
+
// ── Step 3: Register MCP servers ──
|
|
682
|
+
|
|
683
|
+
// Track which platforms were configured (for summary)
|
|
684
|
+
const platforms = { claudeCode: false, openCode: false, codexCli: false };
|
|
439
685
|
|
|
686
|
+
// Claude Code
|
|
440
687
|
if (FLAG_DRY_RUN) {
|
|
441
688
|
if (hasClaudeCli()) {
|
|
442
689
|
dryLog(`Would run: claude mcp add --scope user --transport http ${MCP_NAME} ${MCP_URL}`);
|
|
@@ -444,14 +691,48 @@ async function install() {
|
|
|
444
691
|
dryLog('Would register MCP server in ~/.claude.json');
|
|
445
692
|
}
|
|
446
693
|
} else {
|
|
447
|
-
log('Registering
|
|
694
|
+
log('Registering MCP server for Claude Code...');
|
|
448
695
|
const mcpMethod = registerMcpServer();
|
|
449
696
|
if (mcpMethod === 'cli') {
|
|
450
|
-
success('MCP server registered via `claude mcp add`
|
|
697
|
+
success('Claude Code: MCP server registered via `claude mcp add`');
|
|
698
|
+
platforms.claudeCode = true;
|
|
451
699
|
} else if (mcpMethod === 'direct') {
|
|
452
|
-
success('MCP server registered in ~/.claude.json');
|
|
700
|
+
success('Claude Code: MCP server registered in ~/.claude.json');
|
|
701
|
+
platforms.claudeCode = true;
|
|
453
702
|
} else {
|
|
454
|
-
warn('MCP
|
|
703
|
+
warn('Claude Code: MCP registration skipped');
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// OpenCode
|
|
708
|
+
if (hasOpenCode()) {
|
|
709
|
+
if (FLAG_DRY_RUN) {
|
|
710
|
+
dryLog(`Would register MCP server in ${OPENCODE_CONFIG_FILE}`);
|
|
711
|
+
} else {
|
|
712
|
+
log('Registering MCP server for OpenCode...');
|
|
713
|
+
const ocMethod = registerOpenCodeMcp();
|
|
714
|
+
if (ocMethod === 'direct') {
|
|
715
|
+
success('OpenCode: MCP server registered in ~/.config/opencode/opencode.json');
|
|
716
|
+
platforms.openCode = true;
|
|
717
|
+
} else {
|
|
718
|
+
warn('OpenCode: MCP registration skipped');
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// Codex CLI
|
|
724
|
+
if (hasCodexCli()) {
|
|
725
|
+
if (FLAG_DRY_RUN) {
|
|
726
|
+
dryLog(`Would register MCP server in ${CODEX_CONFIG_FILE}`);
|
|
727
|
+
} else {
|
|
728
|
+
log('Registering MCP server for Codex CLI...');
|
|
729
|
+
const cxMethod = registerCodexMcp();
|
|
730
|
+
if (cxMethod === 'direct') {
|
|
731
|
+
success('Codex CLI: MCP server registered in ~/.codex/config.toml');
|
|
732
|
+
platforms.codexCli = true;
|
|
733
|
+
} else {
|
|
734
|
+
warn('Codex CLI: MCP registration skipped');
|
|
735
|
+
}
|
|
455
736
|
}
|
|
456
737
|
}
|
|
457
738
|
|
|
@@ -510,12 +791,20 @@ async function install() {
|
|
|
510
791
|
node ${path.join(PKG_ROOT, 'bin', 'install.js')}${FLAG_FORCE ? ' --force' : ''}
|
|
511
792
|
`);
|
|
512
793
|
} else {
|
|
794
|
+
const platformLines = [];
|
|
795
|
+
platformLines.push(` Claude Code: ${platforms.claudeCode ? '+ MCP server + capture hooks' : '- not detected'}`);
|
|
796
|
+
platformLines.push(` OpenCode: ${platforms.openCode ? '+ MCP server (query tools)' : '- not detected'}`);
|
|
797
|
+
platformLines.push(` Codex CLI: ${platforms.codexCli ? '+ MCP server (query tools)' : '- not detected'}`);
|
|
798
|
+
|
|
513
799
|
console.log(`
|
|
514
800
|
────────────────────────────────────────────────
|
|
515
801
|
Hive Rank installed successfully!
|
|
516
802
|
────────────────────────────────────────────────
|
|
517
803
|
|
|
518
|
-
|
|
804
|
+
Platforms:
|
|
805
|
+
${platformLines.join('\n')}
|
|
806
|
+
|
|
807
|
+
Restart your agent, then try:
|
|
519
808
|
|
|
520
809
|
/hive:help — See all commands
|
|
521
810
|
/hive:kickstart — Bootstrap your SEO research
|
|
@@ -523,11 +812,11 @@ async function install() {
|
|
|
523
812
|
/hive:trends — See what's trending
|
|
524
813
|
|
|
525
814
|
Config:
|
|
526
|
-
Hooks:
|
|
527
|
-
MCP
|
|
528
|
-
Commands:
|
|
529
|
-
Privacy:
|
|
530
|
-
Controls:
|
|
815
|
+
Hooks: ${SETTINGS_FILE}
|
|
816
|
+
MCP (Claude): ${MCP_URL} (via HTTP)
|
|
817
|
+
${platforms.openCode ? `MCP (OpenCode): ${OPENCODE_CONFIG_FILE}\n ` : ''}${platforms.codexCli ? `MCP (Codex): ${CODEX_CONFIG_FILE}\n ` : ''}Commands: ${COMMANDS_DIR}/
|
|
818
|
+
Privacy: ~/.hive-rank/PRIVACY.md
|
|
819
|
+
Controls: /hive:privacy, /hive:delete-data
|
|
531
820
|
|
|
532
821
|
To uninstall: node ~/.hive-rank/bin/install.js --uninstall
|
|
533
822
|
`);
|
package/package.json
CHANGED