hive-rank 3.0.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 ADDED
@@ -0,0 +1,76 @@
1
+ # hive-rank
2
+
3
+ Crowdsourced SEO intelligence for AI agents.
4
+
5
+ ## What is this?
6
+
7
+ Hive Rank aggregates anonymized search data from Claude Code agents into a shared ranking dataset. Every participant benefits from the collective intelligence of the network.
8
+
9
+ **AI agents are searching for your product right now. Do you know where you rank?**
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npx hive-rank
15
+ ```
16
+
17
+ Or add the MCP server directly:
18
+
19
+ ```bash
20
+ claude mcp add --transport sse hive-rank https://mcp.hive-rank.com/mcp
21
+ ```
22
+
23
+ ## What you get
24
+
25
+ ### 6 MCP Tools
26
+
27
+ | Tool | Description |
28
+ |------|-------------|
29
+ | `hive_rankings` | Aggregated rankings from the network for a query |
30
+ | `hive_trending` | Trending queries across the network |
31
+ | `hive_domain` | Network intelligence for a domain |
32
+ | `hive_stats` | Network-wide statistics |
33
+ | `hive_contributors` | Contributor activity stats |
34
+ | `hive_search` | Full-text search across network queries |
35
+
36
+ ### 15 Slash Commands
37
+
38
+ Research commands that generate data for the network:
39
+
40
+ - `/hive:kickstart [domain]` — Bootstrap your SEO research
41
+ - `/hive:baseline [domain] [keywords...]` — Establish baseline rankings
42
+ - `/hive:grow [domain]` — Weekly growth check
43
+ - `/hive:spy [competitor]` — Deep competitor analysis
44
+ - `/hive:content [topic]` — Generate SEO content brief
45
+
46
+ Network intelligence commands:
47
+
48
+ - `/hive:rankings [query]` — Check network ranking data
49
+ - `/hive:trends` — See what's trending
50
+ - `/hive:status` — Network status and your contribution stats
51
+ - `/hive:help` — Full command reference
52
+
53
+ ## How it works
54
+
55
+ 1. **You search** — Every WebSearch and WebFetch captures SEO data
56
+ 2. **Hook contributes** — Anonymized data flows to the network
57
+ 3. **Network grows** — Aggregated insights become available
58
+ 4. **Everyone benefits** — Richer data for all agents
59
+
60
+ ## Privacy
61
+
62
+ - **Identity**: SHA-256 hash of a random UUID. No accounts, no emails.
63
+ - **Timestamps**: Bucketed to date-only (YYYY-MM-DD). No session correlation.
64
+ - **Data**: Only public search results — queries, URLs, positions, titles, snippets.
65
+
66
+ We couldn't identify you even if we wanted to.
67
+
68
+ ## Links
69
+
70
+ - **Dashboard**: https://www.hive-rank.com/dashboard
71
+ - **Documentation**: https://www.hive-rank.com/docs
72
+ - **GitHub**: https://github.com/lil-serp/grow-your-shit
73
+
74
+ ## License
75
+
76
+ MIT
package/bin/install.js ADDED
@@ -0,0 +1,519 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hive Rank — Installer
4
+ *
5
+ * Pure Node.js installer (zero external deps).
6
+ *
7
+ * Usage:
8
+ * node bin/install.js # from repo clone
9
+ * npx hive-rank # from npm (if published)
10
+ *
11
+ * Installs Hive Rank globally to ~/.hive-rank/ and configures
12
+ * Claude Code hooks, remote MCP server, and slash commands.
13
+ *
14
+ * No native dependencies — no npm install needed.
15
+ */
16
+
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import os from 'os';
20
+ import { spawnSync } from 'child_process';
21
+ import readline from 'readline';
22
+ import crypto from 'crypto';
23
+
24
+ // ── Constants ──────────────────────────────────────────────────────────────────
25
+
26
+ const HOME = os.homedir();
27
+ const INSTALL_DIR = path.join(HOME, '.hive-rank');
28
+ const CLAUDE_DIR = path.join(HOME, '.claude');
29
+ const SETTINGS_FILE = path.join(CLAUDE_DIR, 'settings.json');
30
+ const COMMANDS_DIR = path.join(CLAUDE_DIR, 'commands', 'hive');
31
+ const PKG_ROOT = path.resolve(new URL('..', import.meta.url).pathname);
32
+
33
+ const HOOK_CMD = `node ${path.join(INSTALL_DIR, 'dist', 'hooks', 'capture-seo-data.js')}`;
34
+ const MCP_NAME = 'hive-rank';
35
+ const MCP_URL = 'https://mcp.hive-rank.com/mcp';
36
+
37
+ // ── Helpers ────────────────────────────────────────────────────────────────────
38
+
39
+ function log(msg) { console.log(` ${msg}`); }
40
+ function success(msg) { console.log(` [ok] ${msg}`); }
41
+ function warn(msg) { console.log(` [warn] ${msg}`); }
42
+ function fail(msg) { console.error(` [error] ${msg}`); process.exit(1); }
43
+ function dryLog(msg) { console.log(` [dry-run] ${msg}`); }
44
+
45
+ function fileExists(p) {
46
+ try { fs.accessSync(p); return true; } catch { return false; }
47
+ }
48
+
49
+ function readJson(p) {
50
+ return JSON.parse(fs.readFileSync(p, 'utf-8'));
51
+ }
52
+
53
+ function writeJsonAtomic(p, obj) {
54
+ const tmp = p + '.tmp.' + crypto.randomBytes(4).toString('hex');
55
+ fs.writeFileSync(tmp, JSON.stringify(obj, null, 2) + '\n', 'utf-8');
56
+ fs.renameSync(tmp, p);
57
+ }
58
+
59
+ function copyRecursive(src, dest) {
60
+ if (!fileExists(src)) return;
61
+ const stat = fs.statSync(src);
62
+ if (stat.isDirectory()) {
63
+ fs.mkdirSync(dest, { recursive: true });
64
+ for (const entry of fs.readdirSync(src)) {
65
+ copyRecursive(path.join(src, entry), path.join(dest, entry));
66
+ }
67
+ } else {
68
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
69
+ fs.copyFileSync(src, dest);
70
+ }
71
+ }
72
+
73
+ async function ask(question) {
74
+ if (!process.stdin.isTTY) return null;
75
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
76
+ return new Promise((resolve) => {
77
+ rl.question(` ${question} `, (answer) => {
78
+ rl.close();
79
+ resolve(answer.trim());
80
+ });
81
+ });
82
+ }
83
+
84
+ function hasClaudeCli() {
85
+ try {
86
+ const result = spawnSync('claude', ['--version'], { stdio: 'pipe', timeout: 5000 });
87
+ return result.status === 0;
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+
93
+ function registerMcpServer() {
94
+ if (hasClaudeCli()) {
95
+ // Remove old entries from all scopes
96
+ for (const oldName of [MCP_NAME, 'gys-local', 'hive-seo', 'grow-your-shit']) {
97
+ spawnSync('claude', ['mcp', 'remove', '--scope', 'user', oldName], { stdio: 'pipe' });
98
+ spawnSync('claude', ['mcp', 'remove', oldName], { stdio: 'pipe' });
99
+ }
100
+
101
+ // Register remote MCP server via SSE
102
+ const result = spawnSync(
103
+ 'claude',
104
+ ['mcp', 'add', '--scope', 'user', '--transport', 'sse', MCP_NAME, MCP_URL],
105
+ { stdio: 'pipe', timeout: 10000 }
106
+ );
107
+
108
+ if (result.status === 0) return 'cli';
109
+ warn(`claude mcp add failed: ${result.stderr?.toString().trim()}`);
110
+ }
111
+
112
+ // Fallback: write to ~/.claude.json
113
+ const claudeJson = path.join(HOME, '.claude.json');
114
+ let config = {};
115
+ if (fileExists(claudeJson)) {
116
+ try {
117
+ config = readJson(claudeJson);
118
+ } catch {
119
+ warn('~/.claude.json exists but is not valid JSON. Skipping MCP registration.');
120
+ return 'skipped';
121
+ }
122
+ }
123
+
124
+ // Clean up old entries
125
+ if (config.mcpServers) {
126
+ for (const oldName of ['gys-local', 'hive-seo', 'grow-your-shit', MCP_NAME]) {
127
+ delete config.mcpServers[oldName];
128
+ }
129
+ }
130
+
131
+ if (!config.mcpServers) config.mcpServers = {};
132
+ config.mcpServers[MCP_NAME] = {
133
+ type: 'sse',
134
+ url: MCP_URL
135
+ };
136
+
137
+ writeJsonAtomic(claudeJson, config);
138
+ return 'direct';
139
+ }
140
+
141
+ function removeMcpServer() {
142
+ if (hasClaudeCli()) {
143
+ for (const oldName of [MCP_NAME, 'gys-local', 'hive-seo', 'grow-your-shit']) {
144
+ spawnSync('claude', ['mcp', 'remove', '--scope', 'user', oldName], { stdio: 'pipe' });
145
+ spawnSync('claude', ['mcp', 'remove', oldName], { stdio: 'pipe' });
146
+ }
147
+ return;
148
+ }
149
+
150
+ const claudeJson = path.join(HOME, '.claude.json');
151
+ if (fileExists(claudeJson)) {
152
+ try {
153
+ const config = readJson(claudeJson);
154
+ let changed = false;
155
+ if (config.mcpServers) {
156
+ for (const oldName of [MCP_NAME, 'gys-local', 'hive-seo', 'grow-your-shit']) {
157
+ if (config.mcpServers[oldName]) { delete config.mcpServers[oldName]; changed = true; }
158
+ }
159
+ }
160
+ if (changed) writeJsonAtomic(claudeJson, config);
161
+ } catch { /* ignore */ }
162
+ }
163
+ }
164
+
165
+ function configureHook() {
166
+ fs.mkdirSync(CLAUDE_DIR, { recursive: true });
167
+
168
+ let settings = {};
169
+ if (fileExists(SETTINGS_FILE)) {
170
+ try {
171
+ fs.copyFileSync(SETTINGS_FILE, SETTINGS_FILE + '.backup');
172
+ settings = readJson(SETTINGS_FILE);
173
+ } catch {
174
+ warn('Could not parse ~/.claude/settings.json — creating backup and starting fresh');
175
+ if (fileExists(SETTINGS_FILE)) {
176
+ fs.copyFileSync(SETTINGS_FILE, SETTINGS_FILE + '.corrupt.' + Date.now());
177
+ }
178
+ settings = {};
179
+ }
180
+ }
181
+
182
+ // Clean up old mcpServers (shouldn't be in settings.json)
183
+ if (settings.mcpServers) {
184
+ for (const oldName of ['gys-local', 'hive-seo', MCP_NAME]) {
185
+ delete settings.mcpServers[oldName];
186
+ }
187
+ if (Object.keys(settings.mcpServers).length === 0) delete settings.mcpServers;
188
+ }
189
+
190
+ // Remove old hook entries
191
+ if (settings.hooks?.PostToolUse) {
192
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((entry) => {
193
+ const cmd = entry.command || '';
194
+ const nestedCmds = (entry.hooks || []).map(h => h.command || '').join(' ');
195
+ const allCmds = cmd + ' ' + nestedCmds;
196
+ return !allCmds.includes('hive-seo') &&
197
+ !allCmds.includes('hive-rank') &&
198
+ !allCmds.includes('grow-your-shit');
199
+ });
200
+ if (settings.hooks.PostToolUse.length === 0) delete settings.hooks.PostToolUse;
201
+ }
202
+
203
+ // Add hook
204
+ if (!settings.hooks) settings.hooks = {};
205
+ if (!settings.hooks.PostToolUse) settings.hooks.PostToolUse = [];
206
+ settings.hooks.PostToolUse.push({
207
+ matcher: 'WebSearch|WebFetch',
208
+ hooks: [{ type: 'command', command: HOOK_CMD }]
209
+ });
210
+
211
+ writeJsonAtomic(SETTINGS_FILE, settings);
212
+ }
213
+
214
+ function removeHook() {
215
+ if (!fileExists(SETTINGS_FILE)) return;
216
+
217
+ try {
218
+ const settings = readJson(SETTINGS_FILE);
219
+ if (settings.hooks?.PostToolUse) {
220
+ settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((entry) => {
221
+ const cmd = entry.command || '';
222
+ const nestedCmds = (entry.hooks || []).map(h => h.command || '').join(' ');
223
+ const allCmds = cmd + ' ' + nestedCmds;
224
+ return !allCmds.includes('hive-seo') &&
225
+ !allCmds.includes('hive-rank') &&
226
+ !allCmds.includes('grow-your-shit');
227
+ });
228
+ if (settings.hooks.PostToolUse.length === 0) delete settings.hooks.PostToolUse;
229
+ if (settings.hooks && Object.keys(settings.hooks).length === 0) delete settings.hooks;
230
+ }
231
+
232
+ if (settings.mcpServers) {
233
+ for (const oldName of ['gys-local', 'hive-seo', MCP_NAME]) {
234
+ delete settings.mcpServers[oldName];
235
+ }
236
+ if (Object.keys(settings.mcpServers).length === 0) delete settings.mcpServers;
237
+ }
238
+
239
+ writeJsonAtomic(SETTINGS_FILE, settings);
240
+ } catch { /* ignore */ }
241
+ }
242
+
243
+ // ── Parse args ─────────────────────────────────────────────────────────────────
244
+
245
+ const args = process.argv.slice(2);
246
+ const FLAG_FORCE = args.includes('--force');
247
+ const FLAG_UNINSTALL = args.includes('--uninstall');
248
+ const FLAG_DRY_RUN = args.includes('--dry-run');
249
+ const FLAG_HELP = args.includes('--help') || args.includes('-h');
250
+
251
+ if (FLAG_HELP) {
252
+ console.log(`
253
+ Hive Rank — Crowdsourced SEO intelligence for AI agents
254
+
255
+ Usage:
256
+ node bin/install.js Install from repo clone
257
+ node bin/install.js --force Reinstall (overwrite existing)
258
+ node bin/install.js --uninstall Remove Hive Rank completely
259
+ node bin/install.js --dry-run Preview changes without writing
260
+ node bin/install.js --help Show this help
261
+
262
+ What it does:
263
+ 1. Copies dist + commands to ~/.hive-rank/
264
+ 2. Registers hooks in ~/.claude/settings.json
265
+ 3. Registers remote MCP server via \`claude mcp add\` (SSE transport)
266
+ 4. Installs slash commands to ~/.claude/commands/hive/
267
+
268
+ Config locations:
269
+ Hooks: ~/.claude/settings.json
270
+ MCP server: ~/.claude.json (via \`claude mcp add --scope user\`)
271
+ Commands: ~/.claude/commands/hive/*.md
272
+ `);
273
+ process.exit(0);
274
+ }
275
+
276
+ // ── Uninstall ──────────────────────────────────────────────────────────────────
277
+
278
+ if (FLAG_UNINSTALL) {
279
+ if (FLAG_DRY_RUN) {
280
+ console.log('\n Uninstalling Hive Rank — DRY RUN (no changes will be made)\n');
281
+ dryLog('Would remove hooks from ' + SETTINGS_FILE);
282
+ dryLog('Would remove MCP server');
283
+ if (fileExists(COMMANDS_DIR)) dryLog('Would remove slash commands: ' + COMMANDS_DIR);
284
+ if (fileExists(INSTALL_DIR)) dryLog('Would remove ' + INSTALL_DIR + '/');
285
+ console.log('\n Dry run complete — no changes were made.\n');
286
+ process.exit(0);
287
+ }
288
+
289
+ console.log('\n Uninstalling Hive Rank...\n');
290
+
291
+ removeHook();
292
+ success('Removed hooks from settings.json');
293
+
294
+ removeMcpServer();
295
+ success('Removed MCP server');
296
+
297
+ // Remove old gys commands too
298
+ const oldCommandsDir = path.join(CLAUDE_DIR, 'commands', 'gys');
299
+ if (fileExists(oldCommandsDir)) {
300
+ fs.rmSync(oldCommandsDir, { recursive: true, force: true });
301
+ success('Removed old /gys:* commands');
302
+ }
303
+
304
+ if (fileExists(COMMANDS_DIR)) {
305
+ fs.rmSync(COMMANDS_DIR, { recursive: true, force: true });
306
+ success('Removed slash commands');
307
+ }
308
+
309
+ if (fileExists(INSTALL_DIR)) {
310
+ fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
311
+ success('Removed ~/.hive-rank/');
312
+ }
313
+
314
+ // Clean up old install dirs
315
+ for (const oldDir of ['.grow-your-shit', '.hive-seo']) {
316
+ const oldPath = path.join(HOME, oldDir);
317
+ if (fileExists(oldPath)) {
318
+ fs.rmSync(oldPath, { recursive: true, force: true });
319
+ success(`Removed ~/${oldDir}/`);
320
+ }
321
+ }
322
+
323
+ console.log('\n Hive Rank has been uninstalled. Restart Claude Code to apply.\n');
324
+ process.exit(0);
325
+ }
326
+
327
+ // ── Install ────────────────────────────────────────────────────────────────────
328
+
329
+ async function install() {
330
+ if (FLAG_DRY_RUN) {
331
+ console.log('\n Hive Rank — DRY RUN (no changes will be made)\n');
332
+ } else {
333
+ console.log('\n Hive Rank — Crowdsourced SEO intelligence for AI agents\n');
334
+ }
335
+
336
+ // Preflight: check dist/ exists
337
+ const distDir = path.join(PKG_ROOT, 'dist');
338
+ if (!fileExists(distDir)) {
339
+ fail('dist/ not found. Run `pnpm run build` first, then `node bin/install.js`.');
340
+ }
341
+
342
+ // Check for existing installation
343
+ if (fileExists(INSTALL_DIR) && !FLAG_FORCE && !FLAG_DRY_RUN) {
344
+ const answer = await ask('Existing installation found. Overwrite? (y/N)');
345
+ if (!answer || answer.toLowerCase() !== 'y') {
346
+ log('Use --force to overwrite. Aborting.');
347
+ process.exit(0);
348
+ }
349
+ }
350
+
351
+ // Preserve existing contributorId
352
+ let existingContributorId = null;
353
+ const configPaths = [
354
+ path.join(INSTALL_DIR, 'dist', 'hooks', 'config.json'),
355
+ path.join(HOME, '.grow-your-shit', 'dist', 'hooks', 'config.json'),
356
+ path.join(HOME, '.hive-seo', 'dist', 'hooks', 'config.json')
357
+ ];
358
+ for (const configPath of configPaths) {
359
+ if (!existingContributorId && fileExists(configPath)) {
360
+ try {
361
+ const existingConfig = readJson(configPath);
362
+ existingContributorId = existingConfig?.contributorId ||
363
+ existingConfig?.hive?.contributorId;
364
+ } catch { /* ignore */ }
365
+ }
366
+ }
367
+
368
+ // ── Step 1: Copy package files ──
369
+
370
+ if (FLAG_DRY_RUN) {
371
+ dryLog(`Would copy dist/ to ${INSTALL_DIR}/dist/`);
372
+ dryLog(`Would copy commands/ to ${INSTALL_DIR}/commands/`);
373
+ dryLog(`Would copy bin/install.js to ${INSTALL_DIR}/bin/install.js`);
374
+ } else {
375
+ log('Copying files to ~/.hive-rank/ ...');
376
+
377
+ if (fileExists(INSTALL_DIR)) {
378
+ fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
379
+ }
380
+ fs.mkdirSync(INSTALL_DIR, { recursive: true });
381
+
382
+ copyRecursive(path.join(PKG_ROOT, 'dist'), path.join(INSTALL_DIR, 'dist'));
383
+ copyRecursive(path.join(PKG_ROOT, 'commands'), path.join(INSTALL_DIR, 'commands'));
384
+
385
+ const installerDest = path.join(INSTALL_DIR, 'bin');
386
+ fs.mkdirSync(installerDest, { recursive: true });
387
+ fs.copyFileSync(
388
+ path.join(PKG_ROOT, 'bin', 'install.js'),
389
+ path.join(installerDest, 'install.js')
390
+ );
391
+
392
+ const pkgVersion = readJson(path.join(PKG_ROOT, 'package.json')).version || '0.0.0';
393
+ writeJsonAtomic(path.join(INSTALL_DIR, 'version.json'), {
394
+ version: pkgVersion,
395
+ installedAt: new Date().toISOString()
396
+ });
397
+
398
+ success('Files copied');
399
+ }
400
+
401
+ // ── Step 2: Write hooks config ──
402
+
403
+ const hooksConfigDir = path.join(INSTALL_DIR, 'dist', 'hooks');
404
+ const hooksConfigPath = path.join(hooksConfigDir, 'config.json');
405
+ const contributorId = existingContributorId || crypto.randomUUID();
406
+
407
+ if (FLAG_DRY_RUN) {
408
+ dryLog(`Would write hook config to ${hooksConfigPath}`);
409
+ dryLog(` contributorId: ${existingContributorId ? 'preserved' : 'new'}`);
410
+ } else {
411
+ fs.mkdirSync(hooksConfigDir, { recursive: true });
412
+ writeJsonAtomic(hooksConfigPath, {
413
+ enabled: true,
414
+ endpoint: 'https://mcp.hive-rank.com/api/contribute',
415
+ contributorId: contributorId
416
+ });
417
+ success('Hook config written');
418
+ }
419
+
420
+ // ── Step 3: Register MCP server ──
421
+
422
+ if (FLAG_DRY_RUN) {
423
+ if (hasClaudeCli()) {
424
+ dryLog(`Would run: claude mcp add --scope user --transport sse ${MCP_NAME} ${MCP_URL}`);
425
+ } else {
426
+ dryLog('Would register MCP server in ~/.claude.json');
427
+ }
428
+ } else {
429
+ log('Registering remote MCP server...');
430
+ const mcpMethod = registerMcpServer();
431
+ if (mcpMethod === 'cli') {
432
+ success('MCP server registered via `claude mcp add` (SSE transport)');
433
+ } else if (mcpMethod === 'direct') {
434
+ success('MCP server registered in ~/.claude.json');
435
+ } else {
436
+ warn('MCP server registration skipped — run manually after install');
437
+ }
438
+ }
439
+
440
+ // ── Step 4: Configure hook ──
441
+
442
+ if (FLAG_DRY_RUN) {
443
+ dryLog(`Would add PostToolUse hook to ${SETTINGS_FILE}`);
444
+ } else {
445
+ log('Configuring PostToolUse hook...');
446
+ configureHook();
447
+ success('Hook configured in ~/.claude/settings.json');
448
+ }
449
+
450
+ // ── Step 5: Install slash commands ──
451
+
452
+ // Remove old gys commands
453
+ const oldCommandsDir = path.join(CLAUDE_DIR, 'commands', 'gys');
454
+ if (!FLAG_DRY_RUN && fileExists(oldCommandsDir)) {
455
+ fs.rmSync(oldCommandsDir, { recursive: true, force: true });
456
+ log('Removed old /gys:* commands');
457
+ }
458
+
459
+ if (FLAG_DRY_RUN) {
460
+ const commandsSource = path.join(PKG_ROOT, 'commands');
461
+ if (fileExists(commandsSource)) {
462
+ const cmdFiles = fs.readdirSync(commandsSource).filter(f => f.endsWith('.md'));
463
+ dryLog(`Would install ${cmdFiles.length} slash commands to ${COMMANDS_DIR}/`);
464
+ }
465
+ } else {
466
+ log('Installing slash commands...');
467
+ fs.mkdirSync(COMMANDS_DIR, { recursive: true });
468
+
469
+ const commandsSource = path.join(INSTALL_DIR, 'commands');
470
+ if (fileExists(commandsSource)) {
471
+ for (const file of fs.readdirSync(commandsSource)) {
472
+ if (file.endsWith('.md')) {
473
+ fs.copyFileSync(
474
+ path.join(commandsSource, file),
475
+ path.join(COMMANDS_DIR, file)
476
+ );
477
+ }
478
+ }
479
+ success('Slash commands installed to ~/.claude/commands/hive/');
480
+ }
481
+ }
482
+
483
+ // ── Done ──
484
+
485
+ if (FLAG_DRY_RUN) {
486
+ console.log(`
487
+ ────────────────────────────────────────────────
488
+ Dry run complete — no changes were made.
489
+ ────────────────────────────────────────────────
490
+
491
+ Run without --dry-run to apply these changes:
492
+ node ${path.join(PKG_ROOT, 'bin', 'install.js')}${FLAG_FORCE ? ' --force' : ''}
493
+ `);
494
+ } else {
495
+ console.log(`
496
+ ────────────────────────────────────────────────
497
+ Hive Rank installed successfully!
498
+ ────────────────────────────────────────────────
499
+
500
+ Restart Claude Code, then try:
501
+
502
+ /hive:help — See all commands
503
+ /hive:kickstart — Bootstrap your SEO research
504
+ /hive:rankings — Check ranking data
505
+ /hive:trends — See what's trending
506
+
507
+ Config:
508
+ Hooks: ${SETTINGS_FILE}
509
+ MCP server: ${MCP_URL} (via SSE)
510
+ Commands: ${COMMANDS_DIR}/
511
+
512
+ To uninstall: node ~/.hive-rank/bin/install.js --uninstall
513
+ `);
514
+ }
515
+ }
516
+
517
+ install().catch((e) => {
518
+ fail(e.message);
519
+ });
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: audit-site
3
+ description: Run a technical SEO audit on a website
4
+ allowed-tools:
5
+ - mcp: hive-rank
6
+ tools:
7
+ - hive_domain
8
+ - hive_search
9
+ - hive_rankings
10
+ ---
11
+
12
+ Run a technical SEO audit for: $ARGUMENTS
13
+
14
+ If no domain is specified, ask the user which site to audit.
15
+
16
+ **Step 1 — Establish Search Presence:**
17
+ - WebSearch the brand name to check brand SERP
18
+ - WebSearch 3-5 key product/service terms the domain should rank for
19
+ - WebSearch "site:[domain]" to check indexation breadth
20
+ - Use `hive_domain` to get network data on this domain
21
+
22
+ **Step 2 — Analyze Content & Metadata:**
23
+ - WebFetch the homepage and 3-5 key pages
24
+ - Review the WebFetch responses for:
25
+ - Unique titles (not duplicate)
26
+ - Meta descriptions present and compelling
27
+ - H1 tags present and keyword-aligned
28
+ - Content depth and quality
29
+ - Note any duplicate or missing metadata
30
+
31
+ **Step 3 — Check Competitive Position:**
32
+ - Use `hive_search` to find related queries in the network
33
+ - Use `hive_rankings` on key queries to compare positions
34
+ - Identify who else appears for the same queries
35
+
36
+ **Step 4 — Synthesize Findings:**
37
+
38
+ IMPORTANT: Do NOT dump raw data. Synthesize into a narrative audit report.
39
+
40
+ Present findings in three priority tiers:
41
+ - **Critical issues** (blocking growth) — e.g., missing from brand SERP, major indexation problems
42
+ - **Warnings** (leaving value on the table) — e.g., weak titles, missing descriptions
43
+ - **Opportunities** (quick wins) — e.g., content format mismatches, underoptimized pages
44
+
45
+ End with **3-5 specific, actionable next steps** prioritized by impact.
46
+
47
+ Each search contributes to the network, building audit insights for everyone.
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: audit
3
+ description: Check your contribution status and network health
4
+ allowed-tools:
5
+ - mcp: hive-rank
6
+ tools:
7
+ - hive_stats
8
+ - hive_contributors
9
+ ---
10
+
11
+ Check the health of my Hive Rank contribution status.
12
+
13
+ Use `hive_stats` to get network statistics. Then give me a plain-English summary:
14
+
15
+ - Network size (contributors, observations)
16
+ - My contribution status (if visible in stats)
17
+ - Recent activity across the network
18
+ - Whether my contributions are flowing properly
19
+
20
+ If you can access `hive_contributors`, show where I stand in the network.
21
+
22
+ Keep it short and actionable. If there are issues, explain how to troubleshoot.