@hiveforge/hivemind-mcp 2.3.0 → 2.5.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.
Files changed (65) hide show
  1. package/README.md +169 -27
  2. package/dist/cli.js +938 -77
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/schema.d.ts +125 -0
  5. package/dist/config/schema.d.ts.map +1 -0
  6. package/dist/config/schema.js +79 -0
  7. package/dist/config/schema.js.map +1 -0
  8. package/dist/graph/builder.d.ts +7 -3
  9. package/dist/graph/builder.d.ts.map +1 -1
  10. package/dist/graph/builder.js +51 -21
  11. package/dist/graph/builder.js.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +35 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/mcp/tool-generator.d.ts +3 -1
  16. package/dist/mcp/tool-generator.d.ts.map +1 -1
  17. package/dist/mcp/tool-generator.js +41 -5
  18. package/dist/mcp/tool-generator.js.map +1 -1
  19. package/dist/search/engine.d.ts +9 -1
  20. package/dist/search/engine.d.ts.map +1 -1
  21. package/dist/search/engine.js +16 -4
  22. package/dist/search/engine.js.map +1 -1
  23. package/dist/templates/builtin/people-management.d.ts +18 -0
  24. package/dist/templates/builtin/people-management.d.ts.map +1 -0
  25. package/dist/templates/builtin/people-management.js +533 -0
  26. package/dist/templates/builtin/people-management.js.map +1 -0
  27. package/dist/templates/builtin/research.d.ts +18 -0
  28. package/dist/templates/builtin/research.d.ts.map +1 -0
  29. package/dist/templates/builtin/research.js +359 -0
  30. package/dist/templates/builtin/research.js.map +1 -0
  31. package/dist/templates/builtin/worldbuilding.d.ts.map +1 -1
  32. package/dist/templates/builtin/worldbuilding.js +148 -0
  33. package/dist/templates/builtin/worldbuilding.js.map +1 -1
  34. package/dist/templates/community/architecture.d.ts +19 -0
  35. package/dist/templates/community/architecture.d.ts.map +1 -0
  36. package/dist/templates/community/architecture.js +296 -0
  37. package/dist/templates/community/architecture.js.map +1 -0
  38. package/dist/templates/community/index.d.ts +37 -0
  39. package/dist/templates/community/index.d.ts.map +1 -0
  40. package/dist/templates/community/index.js +45 -0
  41. package/dist/templates/community/index.js.map +1 -0
  42. package/dist/templates/community/ux-research.d.ts +21 -0
  43. package/dist/templates/community/ux-research.d.ts.map +1 -0
  44. package/dist/templates/community/ux-research.js +347 -0
  45. package/dist/templates/community/ux-research.js.map +1 -0
  46. package/dist/templates/loader.d.ts +65 -8
  47. package/dist/templates/loader.d.ts.map +1 -1
  48. package/dist/templates/loader.js +139 -12
  49. package/dist/templates/loader.js.map +1 -1
  50. package/dist/templates/registry.d.ts +31 -1
  51. package/dist/templates/registry.d.ts.map +1 -1
  52. package/dist/templates/registry.js +64 -0
  53. package/dist/templates/registry.js.map +1 -1
  54. package/dist/templates/types.d.ts +57 -0
  55. package/dist/templates/types.d.ts.map +1 -1
  56. package/dist/templates/validator.d.ts +166 -0
  57. package/dist/templates/validator.d.ts.map +1 -1
  58. package/dist/templates/validator.js +67 -2
  59. package/dist/templates/validator.js.map +1 -1
  60. package/dist/templates/version.d.ts +63 -0
  61. package/dist/templates/version.d.ts.map +1 -0
  62. package/dist/templates/version.js +119 -0
  63. package/dist/templates/version.js.map +1 -0
  64. package/dist/types/index.d.ts +12 -12
  65. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,29 +1,35 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, writeFileSync, existsSync } from 'fs';
3
- import { resolve, join, basename } from 'path';
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
3
+ import { resolve, join, basename, dirname } from 'path';
4
+ import { homedir } from 'os';
4
5
  import * as readline from 'readline/promises';
5
6
  import { stdin as input, stdout as output } from 'process';
6
7
  import { FolderMapper } from './templates/folder-mapper.js';
7
8
  import { templateRegistry } from './templates/registry.js';
8
9
  import { worldbuildingTemplate } from './templates/builtin/worldbuilding.js';
9
- const DEFAULT_CONFIG = {
10
- vault: {
11
- path: '',
12
- watchForChanges: true,
13
- debounceMs: 100
14
- },
15
- server: {
16
- transport: 'stdio'
17
- },
18
- indexing: {
19
- strategy: 'incremental',
20
- batchSize: 100,
21
- enableVectorSearch: false,
22
- enableFullTextSearch: true
23
- }
10
+ import { researchTemplate } from './templates/builtin/research.js';
11
+ import { peopleManagementTemplate } from './templates/builtin/people-management.js';
12
+ import { loadTemplateFile } from './templates/loader.js';
13
+ import { TemplateValidationError, TemplateDefinitionSchema } from './templates/validator.js';
14
+ import { communityTemplates, getCommunityTemplate, listCommunityTemplateIds } from './templates/community/index.js';
15
+ import { checkTemplateCompatibility, getHivemindVersion } from './templates/version.js';
16
+ /**
17
+ * All available templates from registry (built-in + community)
18
+ */
19
+ const AVAILABLE_TEMPLATES = {
20
+ // Built-in templates
21
+ 'worldbuilding': worldbuildingTemplate,
22
+ 'research': researchTemplate,
23
+ 'people-management': peopleManagementTemplate,
24
+ // Community templates are added dynamically
24
25
  };
26
+ // Add community templates to available templates
27
+ for (const template of communityTemplates) {
28
+ AVAILABLE_TEMPLATES[template.id] = template;
29
+ }
25
30
  async function init() {
26
- console.log('🧠 Hivemind MCP Server - Configuration Setup\n');
31
+ console.log('\nHivemind Setup Wizard');
32
+ console.log('=====================\n');
27
33
  const rl = readline.createInterface({ input, output });
28
34
  try {
29
35
  // Check if config already exists
@@ -36,7 +42,8 @@ async function init() {
36
42
  return;
37
43
  }
38
44
  }
39
- // Ask for vault path
45
+ // Step 1: Vault path
46
+ console.log('Step 1/4: Vault Path\n');
40
47
  const vaultPath = await rl.question('Enter your Obsidian vault path: ');
41
48
  if (!vaultPath.trim()) {
42
49
  console.error('❌ Vault path is required');
@@ -53,28 +60,56 @@ async function init() {
53
60
  return;
54
61
  }
55
62
  }
56
- // Optional: Enable vector search
57
- const enableVector = await rl.question('Enable vector search (experimental)? (y/N): ');
58
- // Create config
63
+ // Step 2: Template selection
64
+ console.log('\nStep 2/4: Template Selection\n');
65
+ console.log('Which template best fits your use case?');
66
+ console.log(' 1. worldbuilding - Fiction, games, RPGs (characters, locations, events)');
67
+ console.log(' 2. research - Academic, knowledge (papers, citations, concepts)');
68
+ console.log(' 3. people-management - Teams, HR (people, goals, teams, 1:1s)');
69
+ console.log(' 4. software-architecture - Engineers (systems, ADRs, components)');
70
+ console.log(' 5. ux-research - UX, product (interviews, insights, personas)');
71
+ const templateChoice = await rl.question('\nEnter choice (1-5) [default: 1]: ');
72
+ const templates = ['worldbuilding', 'research', 'people-management', 'software-architecture', 'ux-research'];
73
+ const selectedTemplate = templates[parseInt(templateChoice) - 1] || 'worldbuilding';
74
+ // Step 3: Vector search
75
+ console.log('\nStep 3/4: Features\n');
76
+ const enableVector = await rl.question('Enable vector search? (requires embedding setup) (y/N): ');
77
+ // Step 4: Write config
59
78
  const config = {
60
- ...DEFAULT_CONFIG,
61
79
  vault: {
62
- ...DEFAULT_CONFIG.vault,
63
- path: resolvedPath
80
+ path: resolvedPath,
81
+ watchForChanges: true,
82
+ debounceMs: 100,
83
+ },
84
+ server: {
85
+ transport: 'stdio',
86
+ },
87
+ template: {
88
+ activeTemplate: selectedTemplate,
64
89
  },
65
90
  indexing: {
66
- ...DEFAULT_CONFIG.indexing,
67
- enableVectorSearch: enableVector.toLowerCase() === 'y'
68
- }
91
+ strategy: 'incremental',
92
+ batchSize: 100,
93
+ enableVectorSearch: enableVector.toLowerCase() === 'y',
94
+ enableFullTextSearch: true,
95
+ },
69
96
  };
70
- // Write config
71
97
  writeFileSync(configPath, JSON.stringify(config, null, 2));
72
- console.log(`\n✅ Configuration saved to ${configPath}`);
73
- console.log('\n📝 Next steps:');
74
- console.log(' 1. Build the project: npm run build');
75
- console.log(' 2. Start the server: npm start');
76
- console.log(' 3. Configure your MCP client to connect to this server');
98
+ console.log(`\n✅ Created config.json`);
99
+ console.log(` Template: ${selectedTemplate}`);
100
+ // Step 5: MCP client setup
101
+ console.log('\nStep 4/4: MCP Client Setup\n');
102
+ const setupMcpNow = await rl.question('Set up MCP client config now? (Y/n): ');
77
103
  rl.close();
104
+ if (setupMcpNow.toLowerCase() !== 'n') {
105
+ await setupMcp();
106
+ return;
107
+ }
108
+ // Final next steps
109
+ console.log('\n📋 Next steps:');
110
+ console.log(' 1. Run: npx @hiveforge/hivemind-mcp setup-mcp');
111
+ console.log(' 2. Add frontmatter to your vault files');
112
+ console.log(' 3. Start querying via your AI assistant\n');
78
113
  }
79
114
  catch (error) {
80
115
  console.error('Error during setup:', error);
@@ -207,8 +242,15 @@ function getVaultPath() {
207
242
  return resolve(config.vault.path);
208
243
  }
209
244
  }
210
- catch {
211
- // Ignore config parse errors
245
+ catch (err) {
246
+ if (err instanceof SyntaxError) {
247
+ console.error('❌ config.json contains invalid JSON:');
248
+ console.error(` ${err.message}`);
249
+ console.error('\n Run: npx @hiveforge/hivemind-mcp validate');
250
+ process.exit(1);
251
+ }
252
+ // Re-throw unexpected errors
253
+ throw err;
212
254
  }
213
255
  }
214
256
  // Fall back to current directory
@@ -283,64 +325,851 @@ function generateId(name, entityType) {
283
325
  return `${entityType}-${slug}`;
284
326
  }
285
327
  async function validate() {
328
+ console.log('\nHivemind Configuration Validator');
329
+ console.log('================================\n');
330
+ // Show config detection order
331
+ console.log('📍 Config detection order:');
332
+ console.log(' 1. CLI --vault flag (highest priority)');
333
+ console.log(' 2. HIVEMIND_CONFIG_PATH environment variable');
334
+ console.log(' 3. ./config.json');
335
+ console.log(' 4. ./hivemind.config.json');
336
+ console.log(' 5. HIVEMIND_VAULT_PATH environment variable');
337
+ console.log('');
286
338
  const configPath = resolve(process.cwd(), 'config.json');
287
- console.log('🔍 Validating Hivemind configuration...\n');
288
339
  // Check config exists
289
340
  if (!existsSync(configPath)) {
290
341
  console.error('❌ config.json not found');
291
342
  console.log(' Run "npx @hiveforge/hivemind-mcp init" to create one');
292
343
  process.exit(1);
293
344
  }
345
+ console.log(`📂 Found config at: ${configPath}\n`);
294
346
  // Load and validate config
347
+ let config;
348
+ try {
349
+ config = JSON.parse(readFileSync(configPath, 'utf-8'));
350
+ }
351
+ catch (err) {
352
+ if (err instanceof SyntaxError) {
353
+ console.error('❌ config.json contains invalid JSON:');
354
+ console.error(` ${err.message}`);
355
+ console.error('\n Tip: Use a JSON validator like jsonlint.com to check syntax.');
356
+ }
357
+ else {
358
+ console.error('❌ Error reading config.json:', err);
359
+ }
360
+ process.exit(1);
361
+ }
362
+ console.log('✅ Valid JSON syntax');
363
+ // Import and use schema validation
364
+ const { validateConfig: schemaValidate, formatValidationErrors } = await import('./config/schema.js');
365
+ const validation = schemaValidate(config);
366
+ if (!validation.success) {
367
+ console.error('❌ Schema validation failed:');
368
+ console.error(formatValidationErrors(validation.errors));
369
+ process.exit(1);
370
+ }
371
+ console.log('✅ Schema validation passed');
372
+ // Check vault path
373
+ if (!config.vault || typeof config.vault !== 'object' || !('path' in config.vault)) {
374
+ console.error('❌ vault.path is missing in config');
375
+ process.exit(1);
376
+ }
377
+ const vaultPath = resolve(config.vault.path);
378
+ if (!existsSync(vaultPath)) {
379
+ console.error(`❌ Vault path does not exist: ${vaultPath}`);
380
+ console.log('\n Tip: Update the path in config.json or create the directory.');
381
+ process.exit(1);
382
+ }
383
+ console.log(`✅ Vault path exists: ${vaultPath}`);
384
+ // Check for .md files
385
+ const { readdirSync } = await import('fs');
386
+ let mdCount = 0;
387
+ function countMarkdown(dir) {
388
+ try {
389
+ const entries = readdirSync(dir, { withFileTypes: true });
390
+ for (const entry of entries) {
391
+ const fullPath = join(dir, entry.name);
392
+ if (entry.isDirectory() && !entry.name.startsWith('.')) {
393
+ countMarkdown(fullPath);
394
+ }
395
+ else if (entry.isFile() && entry.name.endsWith('.md')) {
396
+ mdCount++;
397
+ }
398
+ }
399
+ }
400
+ catch {
401
+ // Skip directories we can't read
402
+ }
403
+ }
404
+ countMarkdown(vaultPath);
405
+ console.log(`✅ Found ${mdCount} markdown file(s)`);
406
+ // Show active template if configured
407
+ if (config.template && typeof config.template === 'object' && 'activeTemplate' in config.template) {
408
+ console.log(`✅ Active template: ${config.template.activeTemplate}`);
409
+ }
410
+ // Check if built
411
+ const distPath = resolve(process.cwd(), 'dist', 'index.js');
412
+ if (!existsSync(distPath)) {
413
+ console.warn('⚠️ dist/index.js not found - run "npm run build"');
414
+ }
415
+ else {
416
+ console.log('✅ Server built (dist/index.js exists)');
417
+ }
418
+ console.log('\n✅ Configuration is valid!');
419
+ console.log('\nTo start the server: npx @hiveforge/hivemind-mcp start');
420
+ }
421
+ /**
422
+ * Get the MCP config file path for different clients
423
+ */
424
+ function getMcpConfigPath(client) {
425
+ const platform = process.platform;
426
+ switch (client) {
427
+ case '1': // Claude Desktop
428
+ if (platform === 'win32') {
429
+ return join(process.env.APPDATA || '', 'Claude', 'claude_desktop_config.json');
430
+ }
431
+ else if (platform === 'darwin') {
432
+ return join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
433
+ }
434
+ else {
435
+ return join(homedir(), '.config', 'claude', 'claude_desktop_config.json');
436
+ }
437
+ case '2': // GitHub Copilot
438
+ return join(homedir(), '.copilot', 'mcp-config.json');
439
+ default:
440
+ return '(varies by client)';
441
+ }
442
+ }
443
+ /**
444
+ * Generate MCP config for different clients
445
+ */
446
+ function generateMcpConfig(client, vaultPath) {
447
+ const baseConfig = {
448
+ command: 'npx',
449
+ args: vaultPath
450
+ ? ['-y', '@hiveforge/hivemind-mcp', '--vault', vaultPath]
451
+ : ['-y', '@hiveforge/hivemind-mcp', 'start'],
452
+ };
453
+ if (client === '2') {
454
+ // GitHub Copilot format
455
+ return {
456
+ mcpServers: {
457
+ hivemind: {
458
+ type: 'local',
459
+ ...baseConfig,
460
+ tools: ['*'],
461
+ },
462
+ },
463
+ };
464
+ }
465
+ // Claude Desktop and generic format
466
+ return {
467
+ mcpServers: {
468
+ hivemind: baseConfig,
469
+ },
470
+ };
471
+ }
472
+ /**
473
+ * Write MCP config to file, merging with existing config
474
+ */
475
+ function writeMcpConfig(configPath, newConfig) {
476
+ let existingConfig = { mcpServers: {} };
477
+ if (existsSync(configPath)) {
478
+ try {
479
+ existingConfig = JSON.parse(readFileSync(configPath, 'utf-8'));
480
+ }
481
+ catch {
482
+ // Start fresh if existing config is invalid
483
+ }
484
+ }
485
+ // Merge hivemind into existing config
486
+ existingConfig.mcpServers = existingConfig.mcpServers || {};
487
+ existingConfig.mcpServers.hivemind = newConfig.mcpServers.hivemind;
488
+ // Ensure directory exists
489
+ const dir = dirname(configPath);
490
+ if (!existsSync(dir)) {
491
+ mkdirSync(dir, { recursive: true });
492
+ }
493
+ writeFileSync(configPath, JSON.stringify(existingConfig, null, 2));
494
+ }
495
+ /**
496
+ * Setup MCP client configuration
497
+ */
498
+ async function setupMcp() {
499
+ const rl = readline.createInterface({ input, output });
500
+ console.log('\nHivemind MCP Client Setup');
501
+ console.log('=========================\n');
502
+ try {
503
+ // 1. Select MCP client
504
+ console.log('Which MCP client are you using?');
505
+ console.log(' 1. Claude Desktop');
506
+ console.log(' 2. GitHub Copilot');
507
+ console.log(' 3. Other (show generic config)');
508
+ const clientChoice = await rl.question('\nEnter choice (1-3): ');
509
+ // 2. Get vault path
510
+ const currentVault = getVaultPath();
511
+ console.log(`\nCurrent configured vault: ${currentVault || '(none)'}`);
512
+ const vaultInput = await rl.question('Vault path (press Enter to use current, or enter new path): ');
513
+ const resolvedVault = vaultInput.trim()
514
+ ? resolve(vaultInput.trim())
515
+ : currentVault || undefined;
516
+ // 3. Generate config based on client
517
+ const config = generateMcpConfig(clientChoice, resolvedVault);
518
+ // 4. Show config file location
519
+ const configPath = getMcpConfigPath(clientChoice);
520
+ console.log(`\n📁 Config file location:`);
521
+ console.log(` ${configPath}\n`);
522
+ console.log('📋 Add this to your MCP config:\n');
523
+ console.log(JSON.stringify(config, null, 2));
524
+ // 5. Offer to write config (only for known clients)
525
+ if (clientChoice === '1' || clientChoice === '2') {
526
+ const writeChoice = await rl.question('\nWrite to config file? (y/N): ');
527
+ if (writeChoice.toLowerCase() === 'y') {
528
+ writeMcpConfig(configPath, config);
529
+ console.log(`\n✅ Config written to ${configPath}`);
530
+ console.log(' Restart your MCP client to apply changes.');
531
+ }
532
+ }
533
+ console.log('\n📝 Next steps:');
534
+ console.log(' 1. If you didn\'t write the config, copy the JSON above to your MCP client config');
535
+ console.log(' 2. Restart your MCP client (Claude Desktop, VS Code, etc.)');
536
+ console.log(' 3. Start using Hivemind tools in your AI conversations\n');
537
+ }
538
+ finally {
539
+ rl.close();
540
+ }
541
+ }
542
+ /**
543
+ * Validate a template file
544
+ */
545
+ async function validateTemplate() {
546
+ const filePath = process.argv[3] || './template.json';
547
+ console.log(`🔍 Validating template file: ${filePath}\n`);
548
+ if (!existsSync(filePath)) {
549
+ console.error(`❌ Template file not found: ${filePath}`);
550
+ process.exit(1);
551
+ }
552
+ try {
553
+ const template = loadTemplateFile(filePath);
554
+ console.log('✓ Template is valid!\n');
555
+ console.log(`ID: ${template.id}`);
556
+ console.log(`Name: ${template.name}`);
557
+ console.log(`Version: ${template.version}`);
558
+ if (template.description) {
559
+ console.log(`Description: ${template.description}`);
560
+ }
561
+ console.log(`\nEntity Types (${template.entityTypes.length}):`);
562
+ for (const et of template.entityTypes) {
563
+ const requiredFields = et.fields.filter(f => f.required).length;
564
+ console.log(` - ${et.name} (${et.fields.length} fields, ${requiredFields} required)`);
565
+ }
566
+ if (template.relationshipTypes?.length) {
567
+ console.log(`\nRelationship Types (${template.relationshipTypes.length}):`);
568
+ for (const rt of template.relationshipTypes) {
569
+ const sourceStr = Array.isArray(rt.sourceTypes) ? rt.sourceTypes.join(', ') : rt.sourceTypes;
570
+ const targetStr = Array.isArray(rt.targetTypes) ? rt.targetTypes.join(', ') : rt.targetTypes;
571
+ console.log(` - ${rt.id}: ${sourceStr} → ${targetStr}${rt.bidirectional ? ' (bidirectional)' : ''}`);
572
+ }
573
+ }
574
+ console.log('\n✅ Template validation complete!');
575
+ }
576
+ catch (err) {
577
+ if (err instanceof TemplateValidationError) {
578
+ console.error(err.toUserMessage());
579
+ }
580
+ else {
581
+ console.error(`❌ Error: ${err instanceof Error ? err.message : String(err)}`);
582
+ }
583
+ process.exit(1);
584
+ }
585
+ }
586
+ /**
587
+ * Interactive template creation wizard
588
+ */
589
+ async function createTemplate() {
590
+ console.log('🧠 Hivemind Template Creator\n');
591
+ console.log('='.repeat(40) + '\n');
592
+ const rl = readline.createInterface({ input, output });
295
593
  try {
296
- const config = JSON.parse(readFileSync(configPath, 'utf-8'));
297
- console.log(' config.json found and valid JSON');
298
- // Check vault path
299
- if (!config.vault?.path) {
300
- console.error('❌ vault.path is missing in config');
594
+ // 1. Template metadata
595
+ const id = await promptRequired(rl, 'Template ID (lowercase with hyphens)');
596
+ if (!/^[a-z][a-z0-9-]*$/.test(id)) {
597
+ console.error('❌ Template ID must be lowercase alphanumeric with hyphens');
598
+ rl.close();
301
599
  process.exit(1);
302
600
  }
303
- const vaultPath = resolve(config.vault.path);
304
- if (!existsSync(vaultPath)) {
305
- console.error(`❌ Vault path does not exist: ${vaultPath}`);
601
+ const name = await promptRequired(rl, 'Template name');
602
+ const description = await rl.question('Description (optional): ');
603
+ const version = (await rl.question('Version (default 1.0.0): ')).trim() || '1.0.0';
604
+ if (!/^\d+\.\d+\.\d+$/.test(version)) {
605
+ console.error('❌ Version must follow semantic versioning (e.g., 1.0.0)');
606
+ rl.close();
306
607
  process.exit(1);
307
608
  }
308
- console.log(`✅ Vault path exists: ${vaultPath}`);
309
- // Check for .md files
310
- const { readdirSync } = await import('fs');
311
- let mdCount = 0;
312
- function countMarkdown(dir) {
313
- try {
314
- const entries = readdirSync(dir, { withFileTypes: true });
315
- for (const entry of entries) {
316
- const fullPath = join(dir, entry.name);
317
- if (entry.isDirectory() && !entry.name.startsWith('.')) {
318
- countMarkdown(fullPath);
609
+ // 2. Entity types
610
+ console.log('\n=== Entity Types ===\n');
611
+ const entityTypes = [];
612
+ while (true) {
613
+ const entityName = (await rl.question("Entity name (lowercase, or 'done'): ")).trim().toLowerCase();
614
+ if (entityName === 'done' || entityName === '') {
615
+ if (entityTypes.length === 0) {
616
+ console.log('⚠️ Template must have at least one entity type.');
617
+ continue;
618
+ }
619
+ break;
620
+ }
621
+ if (!/^[a-z][a-z0-9_]*$/.test(entityName)) {
622
+ console.log('❌ Entity name must be lowercase alphanumeric with underscores');
623
+ continue;
624
+ }
625
+ const displayName = await promptRequired(rl, 'Display name');
626
+ const pluralName = await promptRequired(rl, 'Plural name');
627
+ // Fields
628
+ const fields = [];
629
+ console.log('');
630
+ while (true) {
631
+ const fieldName = (await rl.question(" Field name (or 'done'): ")).trim();
632
+ if (fieldName === 'done' || fieldName === '') {
633
+ break;
634
+ }
635
+ if (!/^[a-z][a-zA-Z0-9_]*$/.test(fieldName)) {
636
+ console.log(' ❌ Field name must be camelCase or snake_case');
637
+ continue;
638
+ }
639
+ const fieldTypeInput = (await rl.question(' Field type [string/number/boolean/enum/array/date/record]: ')).trim().toLowerCase();
640
+ const validTypes = ['string', 'number', 'boolean', 'enum', 'array', 'date', 'record'];
641
+ if (!validTypes.includes(fieldTypeInput)) {
642
+ console.log(' ❌ Invalid field type');
643
+ continue;
644
+ }
645
+ const field = {
646
+ name: fieldName,
647
+ type: fieldTypeInput,
648
+ };
649
+ // Handle enum values
650
+ if (fieldTypeInput === 'enum') {
651
+ const enumValuesStr = await promptRequired(rl, ' Enum values (comma-separated)');
652
+ field.enumValues = enumValuesStr.split(',').map(v => v.trim()).filter(v => v);
653
+ if (field.enumValues.length === 0) {
654
+ console.log(' ❌ Enum must have at least one value');
655
+ continue;
319
656
  }
320
- else if (entry.isFile() && entry.name.endsWith('.md')) {
321
- mdCount++;
657
+ }
658
+ // Handle array item type
659
+ if (fieldTypeInput === 'array') {
660
+ const itemTypeInput = (await rl.question(' Array item type [string/number/boolean]: ')).trim().toLowerCase();
661
+ const validItemTypes = ['string', 'number', 'boolean'];
662
+ if (validItemTypes.includes(itemTypeInput)) {
663
+ field.arrayItemType = itemTypeInput;
664
+ }
665
+ else {
666
+ field.arrayItemType = 'string';
322
667
  }
323
668
  }
669
+ // Required?
670
+ const requiredInput = (await rl.question(' Required? (y/n): ')).trim().toLowerCase();
671
+ if (requiredInput === 'y' || requiredInput === 'yes') {
672
+ field.required = true;
673
+ }
674
+ fields.push(field);
675
+ console.log(` ✓ Added field: ${fieldName}\n`);
324
676
  }
325
- catch (err) {
326
- // Skip directories we can't read
677
+ entityTypes.push({
678
+ name: entityName,
679
+ displayName,
680
+ pluralName,
681
+ fields,
682
+ });
683
+ console.log(`✓ Added entity type: ${entityName}\n`);
684
+ }
685
+ // 3. Relationship types (optional)
686
+ console.log('\n=== Relationship Types (optional) ===\n');
687
+ const addRelationships = (await rl.question('Add relationship types? (y/n): ')).trim().toLowerCase();
688
+ const relationshipTypes = [];
689
+ if (addRelationships === 'y' || addRelationships === 'yes') {
690
+ while (true) {
691
+ const relId = (await rl.question("\nRelationship ID (snake_case, or 'done'): ")).trim().toLowerCase();
692
+ if (relId === 'done' || relId === '') {
693
+ break;
694
+ }
695
+ if (!/^[a-z][a-z0-9_]*$/.test(relId)) {
696
+ console.log('❌ Relationship ID must be snake_case');
697
+ continue;
698
+ }
699
+ const relDisplayName = await promptRequired(rl, 'Display name');
700
+ const sourceTypesInput = (await rl.question(`Source types (comma-separated, or 'any'): `)).trim();
701
+ const sourceTypes = sourceTypesInput.toLowerCase() === 'any'
702
+ ? 'any'
703
+ : sourceTypesInput.split(',').map(t => t.trim()).filter(t => t);
704
+ const targetTypesInput = (await rl.question(`Target types (comma-separated, or 'any'): `)).trim();
705
+ const targetTypes = targetTypesInput.toLowerCase() === 'any'
706
+ ? 'any'
707
+ : targetTypesInput.split(',').map(t => t.trim()).filter(t => t);
708
+ const bidirectionalInput = (await rl.question('Bidirectional? (y/n): ')).trim().toLowerCase();
709
+ const bidirectional = bidirectionalInput === 'y' || bidirectionalInput === 'yes';
710
+ let reverseId;
711
+ if (bidirectional) {
712
+ reverseId = await promptRequired(rl, 'Reverse ID (e.g., for "parent_of" the reverse is "child_of")');
713
+ }
714
+ relationshipTypes.push({
715
+ id: relId,
716
+ displayName: relDisplayName,
717
+ sourceTypes,
718
+ targetTypes,
719
+ bidirectional,
720
+ reverseId,
721
+ });
722
+ console.log(`✓ Added relationship type: ${relId}`);
327
723
  }
328
724
  }
329
- countMarkdown(vaultPath);
330
- console.log(`✅ Found ${mdCount} markdown file(s)`);
331
- // Check if built
332
- const distPath = resolve(process.cwd(), 'dist', 'index.js');
333
- if (!existsSync(distPath)) {
334
- console.warn('⚠️ dist/index.js not found - run "npm run build"');
725
+ // 4. Build and validate template
726
+ const template = {
727
+ id,
728
+ name,
729
+ version,
730
+ entityTypes,
731
+ };
732
+ if (description) {
733
+ template.description = description;
734
+ }
735
+ if (relationshipTypes && relationshipTypes.length > 0) {
736
+ template.relationshipTypes = relationshipTypes;
335
737
  }
336
- else {
337
- console.log('✅ Server built (dist/index.js exists)');
738
+ // Validate before writing
739
+ try {
740
+ const { TemplateDefinitionSchema } = await import('./templates/validator.js');
741
+ TemplateDefinitionSchema.parse(template);
742
+ }
743
+ catch (err) {
744
+ console.error('\n❌ Template validation failed:');
745
+ if (err instanceof TemplateValidationError) {
746
+ console.error(err.toUserMessage());
747
+ }
748
+ else {
749
+ console.error(err instanceof Error ? err.message : String(err));
750
+ }
751
+ rl.close();
752
+ process.exit(1);
338
753
  }
339
- console.log('\n✅ Configuration is valid!');
340
- console.log('\nTo start the server: npm start');
754
+ // Write template file
755
+ const outputPath = resolve(process.cwd(), 'template.json');
756
+ writeFileSync(outputPath, JSON.stringify(template, null, 2));
757
+ console.log('\n' + '='.repeat(40));
758
+ console.log(`\n✓ Template created: ${outputPath}\n`);
759
+ console.log('Next steps:');
760
+ console.log(' 1. Review and edit template.json as needed');
761
+ console.log(' 2. Validate: npx @hiveforge/hivemind-mcp validate-template');
762
+ console.log(' 3. Add to config.json or use standalone template.json');
763
+ console.log(' 4. Consider contributing: https://github.com/hiveforge-sh/hivemind/blob/master/CONTRIBUTING.md');
764
+ console.log('');
765
+ rl.close();
341
766
  }
342
767
  catch (error) {
343
- console.error('Error reading config.json:', error);
768
+ console.error('Error during template creation:', error);
769
+ rl.close();
770
+ process.exit(1);
771
+ }
772
+ }
773
+ /**
774
+ * Helper: prompt for required input
775
+ */
776
+ async function promptRequired(rl, prompt) {
777
+ while (true) {
778
+ const value = (await rl.question(`${prompt}: `)).trim();
779
+ if (value)
780
+ return value;
781
+ console.log(' ⚠️ This field is required.');
782
+ }
783
+ }
784
+ /**
785
+ * Add a template from registry or URL
786
+ */
787
+ async function addTemplate() {
788
+ const templateArg = process.argv[3];
789
+ console.log('🧠 Hivemind - Add Template\n');
790
+ // Show available templates if no argument
791
+ if (!templateArg) {
792
+ console.log('Available templates:\n');
793
+ console.log('Built-in:');
794
+ console.log(' - worldbuilding Characters, locations, events, factions, lore');
795
+ console.log(' - research Papers, citations, concepts, notes');
796
+ console.log(' - people-management People, goals, teams, 1:1 meetings');
797
+ console.log('\nCommunity:');
798
+ const communityIds = listCommunityTemplateIds();
799
+ if (communityIds.length === 0) {
800
+ console.log(' (no community templates available)');
801
+ }
802
+ else {
803
+ for (const id of communityIds) {
804
+ const template = getCommunityTemplate(id);
805
+ console.log(` - ${id.padEnd(20)} ${template?.description || template?.name || ''}`);
806
+ }
807
+ }
808
+ console.log('\nUsage:');
809
+ console.log(' npx @hiveforge/hivemind-mcp add-template <name> - Add from registry');
810
+ console.log(' npx @hiveforge/hivemind-mcp add-template <url> - Add from URL');
811
+ console.log(' npx @hiveforge/hivemind-mcp add-template <file.json> - Add from local file');
812
+ return;
813
+ }
814
+ let template;
815
+ // Check if it's a URL
816
+ if (templateArg.startsWith('http://') || templateArg.startsWith('https://')) {
817
+ console.log(`📥 Fetching template from: ${templateArg}\n`);
818
+ try {
819
+ const response = await fetch(templateArg);
820
+ if (!response.ok) {
821
+ console.error(`❌ Failed to fetch template: ${response.status} ${response.statusText}`);
822
+ process.exit(1);
823
+ }
824
+ const templateData = await response.json();
825
+ const result = TemplateDefinitionSchema.safeParse(templateData);
826
+ if (!result.success) {
827
+ throw new TemplateValidationError('Invalid template from URL', result.error.issues);
828
+ }
829
+ template = result.data;
830
+ }
831
+ catch (err) {
832
+ if (err instanceof TemplateValidationError) {
833
+ console.error(err.toUserMessage());
834
+ }
835
+ else {
836
+ console.error(`❌ Error fetching template: ${err instanceof Error ? err.message : String(err)}`);
837
+ }
838
+ process.exit(1);
839
+ }
840
+ }
841
+ // Check if it's a local file
842
+ else if (templateArg.endsWith('.json') || existsSync(templateArg)) {
843
+ console.log(`📂 Loading template from: ${templateArg}\n`);
844
+ try {
845
+ template = loadTemplateFile(templateArg);
846
+ }
847
+ catch (err) {
848
+ if (err instanceof TemplateValidationError) {
849
+ console.error(err.toUserMessage());
850
+ }
851
+ else {
852
+ console.error(`❌ Error loading template: ${err instanceof Error ? err.message : String(err)}`);
853
+ }
854
+ process.exit(1);
855
+ }
856
+ }
857
+ // Otherwise, look up in registry
858
+ else {
859
+ const registeredTemplate = AVAILABLE_TEMPLATES[templateArg];
860
+ if (!registeredTemplate) {
861
+ console.error(`❌ Template not found: ${templateArg}`);
862
+ console.log('\nAvailable templates:');
863
+ for (const id of Object.keys(AVAILABLE_TEMPLATES)) {
864
+ console.log(` - ${id}`);
865
+ }
866
+ process.exit(1);
867
+ }
868
+ template = registeredTemplate;
869
+ console.log(`📦 Using template from registry: ${templateArg}\n`);
870
+ }
871
+ // Show template info
872
+ console.log(`Template: ${template.name}`);
873
+ console.log(`ID: ${template.id}`);
874
+ console.log(`Version: ${template.version}`);
875
+ if (template.description) {
876
+ console.log(`Description: ${template.description}`);
877
+ }
878
+ console.log(`\nEntity Types (${template.entityTypes.length}):`);
879
+ for (const et of template.entityTypes) {
880
+ console.log(` - ${et.displayName} (${et.name})`);
881
+ }
882
+ if (template.relationshipTypes?.length) {
883
+ console.log(`\nRelationship Types (${template.relationshipTypes.length}):`);
884
+ for (const rt of template.relationshipTypes) {
885
+ console.log(` - ${rt.displayName} (${rt.id})`);
886
+ }
887
+ }
888
+ // Ask how to save
889
+ const rl = readline.createInterface({ input, output });
890
+ try {
891
+ console.log('\nHow do you want to add this template?\n');
892
+ console.log(' 1. Save as standalone template.json (recommended for custom templates)');
893
+ console.log(' 2. Add to config.json (inline definition)');
894
+ console.log(' 3. Just set as active template (for built-in/community templates)\n');
895
+ const choice = await rl.question('Enter choice (1/2/3): ');
896
+ switch (choice.trim()) {
897
+ case '1': {
898
+ // Save as standalone template.json
899
+ const outputPath = resolve(process.cwd(), 'template.json');
900
+ if (existsSync(outputPath)) {
901
+ const overwrite = await rl.question('template.json already exists. Overwrite? (y/N): ');
902
+ if (overwrite.toLowerCase() !== 'y') {
903
+ console.log('Cancelled.');
904
+ rl.close();
905
+ return;
906
+ }
907
+ }
908
+ writeFileSync(outputPath, JSON.stringify(template, null, 2));
909
+ console.log(`\n✅ Template saved to: ${outputPath}`);
910
+ console.log('\nThe template will be automatically loaded when Hivemind starts.');
911
+ break;
912
+ }
913
+ case '2': {
914
+ // Add to config.json
915
+ const configPath = resolve(process.cwd(), 'config.json');
916
+ let config = {};
917
+ if (existsSync(configPath)) {
918
+ try {
919
+ config = JSON.parse(readFileSync(configPath, 'utf-8'));
920
+ }
921
+ catch {
922
+ // Start fresh if config is invalid
923
+ }
924
+ }
925
+ // Ensure template section exists
926
+ if (!config.template) {
927
+ config.template = { activeTemplate: template.id, templates: [] };
928
+ }
929
+ if (!config.template.templates) {
930
+ config.template.templates = [];
931
+ }
932
+ // Check if template already exists
933
+ const existingIndex = config.template.templates.findIndex((t) => t.id === template.id);
934
+ if (existingIndex >= 0) {
935
+ const overwrite = await rl.question(`Template "${template.id}" already exists in config. Overwrite? (y/N): `);
936
+ if (overwrite.toLowerCase() !== 'y') {
937
+ console.log('Cancelled.');
938
+ rl.close();
939
+ return;
940
+ }
941
+ config.template.templates[existingIndex] = template;
942
+ }
943
+ else {
944
+ config.template.templates.push(template);
945
+ }
946
+ // Set as active template
947
+ config.template.activeTemplate = template.id;
948
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
949
+ console.log(`\n✅ Template added to: ${configPath}`);
950
+ console.log(` Active template set to: ${template.id}`);
951
+ break;
952
+ }
953
+ case '3': {
954
+ // Just update activeTemplate in config.json
955
+ const configPath = resolve(process.cwd(), 'config.json');
956
+ let config = {};
957
+ if (existsSync(configPath)) {
958
+ try {
959
+ config = JSON.parse(readFileSync(configPath, 'utf-8'));
960
+ }
961
+ catch {
962
+ // Start fresh if config is invalid
963
+ }
964
+ }
965
+ // Ensure template section exists
966
+ if (!config.template) {
967
+ config.template = { activeTemplate: template.id };
968
+ }
969
+ else {
970
+ config.template.activeTemplate = template.id;
971
+ }
972
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
973
+ console.log(`\n✅ Active template set to: ${template.id}`);
974
+ console.log(` Updated: ${configPath}`);
975
+ break;
976
+ }
977
+ default:
978
+ console.log('Cancelled.');
979
+ }
980
+ console.log('\nNext steps:');
981
+ console.log(' 1. Start Hivemind: npx @hiveforge/hivemind-mcp start');
982
+ console.log(' 2. Create notes with the new entity types');
983
+ console.log(` 3. Use MCP tools like query_${template.entityTypes[0]?.name || 'entity'}`);
984
+ }
985
+ finally {
986
+ rl.close();
987
+ }
988
+ }
989
+ /**
990
+ * List available templates
991
+ */
992
+ async function listTemplates() {
993
+ console.log('🧠 Hivemind - Available Templates\n');
994
+ console.log('Built-in Templates:');
995
+ console.log('─'.repeat(60));
996
+ const builtinIds = ['worldbuilding', 'research', 'people-management'];
997
+ for (const id of builtinIds) {
998
+ const template = AVAILABLE_TEMPLATES[id];
999
+ console.log(`\n ${template.name} (${template.id})`);
1000
+ if (template.description) {
1001
+ console.log(` ${template.description}`);
1002
+ }
1003
+ console.log(` Entity types: ${template.entityTypes.map(e => e.name).join(', ')}`);
1004
+ if (template.category) {
1005
+ console.log(` Category: ${template.category}`);
1006
+ }
1007
+ if (template.tags?.length) {
1008
+ console.log(` Tags: ${template.tags.join(', ')}`);
1009
+ }
1010
+ }
1011
+ console.log('\n\nCommunity Templates:');
1012
+ console.log('─'.repeat(60));
1013
+ const communityIds = listCommunityTemplateIds();
1014
+ if (communityIds.length === 0) {
1015
+ console.log('\n (no community templates available)');
1016
+ }
1017
+ else {
1018
+ for (const id of communityIds) {
1019
+ const template = getCommunityTemplate(id);
1020
+ if (template) {
1021
+ console.log(`\n ${template.name} (${template.id})`);
1022
+ if (template.description) {
1023
+ console.log(` ${template.description}`);
1024
+ }
1025
+ console.log(` Entity types: ${template.entityTypes.map(e => e.name).join(', ')}`);
1026
+ if (template.category) {
1027
+ console.log(` Category: ${template.category}`);
1028
+ }
1029
+ if (template.tags?.length) {
1030
+ console.log(` Tags: ${template.tags.join(', ')}`);
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ console.log('\n\nTo add a template:');
1036
+ console.log(' npx @hiveforge/hivemind-mcp add-template <template-id>');
1037
+ }
1038
+ /**
1039
+ * Generate a JSON catalog of all available templates
1040
+ */
1041
+ async function generateCatalog() {
1042
+ const outputArg = process.argv[3];
1043
+ const outputPath = outputArg || './template-catalog.json';
1044
+ console.log('🧠 Hivemind - Generating Template Catalog\n');
1045
+ const catalog = {
1046
+ version: '1.0.0',
1047
+ generatedAt: new Date().toISOString(),
1048
+ templates: [],
1049
+ };
1050
+ // Add built-in templates
1051
+ const builtinIds = ['worldbuilding', 'research', 'people-management'];
1052
+ for (const id of builtinIds) {
1053
+ const template = AVAILABLE_TEMPLATES[id];
1054
+ catalog.templates.push(templateToCatalogEntry(template, 'builtin'));
1055
+ }
1056
+ // Add community templates
1057
+ for (const template of communityTemplates) {
1058
+ catalog.templates.push(templateToCatalogEntry(template, 'community'));
1059
+ }
1060
+ // Write catalog
1061
+ writeFileSync(outputPath, JSON.stringify(catalog, null, 2));
1062
+ console.log(`✅ Template catalog generated: ${outputPath}`);
1063
+ console.log(` Total templates: ${catalog.templates.length}`);
1064
+ console.log(` Built-in: ${builtinIds.length}`);
1065
+ console.log(` Community: ${communityTemplates.length}`);
1066
+ // Show summary
1067
+ console.log('\nTemplates included:');
1068
+ for (const entry of catalog.templates) {
1069
+ console.log(` - ${entry.name} (${entry.id}) [${entry.source}]`);
1070
+ }
1071
+ }
1072
+ /**
1073
+ * Convert a template to a catalog entry
1074
+ */
1075
+ function templateToCatalogEntry(template, source) {
1076
+ return {
1077
+ id: template.id,
1078
+ name: template.name,
1079
+ version: template.version,
1080
+ description: template.description,
1081
+ category: template.category,
1082
+ tags: template.tags,
1083
+ author: template.author ? {
1084
+ name: template.author.name,
1085
+ url: template.author.url,
1086
+ } : undefined,
1087
+ repository: template.repository,
1088
+ sampleVault: template.sampleVault,
1089
+ license: template.license,
1090
+ minHivemindVersion: template.minHivemindVersion,
1091
+ source,
1092
+ entityTypes: template.entityTypes.map(et => ({
1093
+ name: et.name,
1094
+ displayName: et.displayName,
1095
+ pluralName: et.pluralName,
1096
+ fieldCount: et.fields.length,
1097
+ })),
1098
+ relationshipCount: template.relationshipTypes?.length || 0,
1099
+ };
1100
+ }
1101
+ /**
1102
+ * Check template compatibility with current Hivemind version
1103
+ */
1104
+ async function checkCompatibility() {
1105
+ const templateArg = process.argv[3];
1106
+ console.log('🧠 Hivemind - Template Compatibility Check\n');
1107
+ const hivemindVersion = getHivemindVersion();
1108
+ console.log(`Hivemind version: ${hivemindVersion}\n`);
1109
+ // If no argument, check all registered templates
1110
+ if (!templateArg) {
1111
+ console.log('Checking all templates:\n');
1112
+ let allCompatible = true;
1113
+ for (const [id, template] of Object.entries(AVAILABLE_TEMPLATES)) {
1114
+ const result = checkTemplateCompatibility(template.minHivemindVersion, template.version);
1115
+ const icon = result.compatible ? '✓' : '✗';
1116
+ console.log(` ${icon} ${id} (v${template.version})`);
1117
+ if (!result.compatible) {
1118
+ console.log(` ${result.message}`);
1119
+ allCompatible = false;
1120
+ }
1121
+ }
1122
+ console.log('');
1123
+ if (allCompatible) {
1124
+ console.log('✅ All templates are compatible');
1125
+ }
1126
+ else {
1127
+ console.log('⚠️ Some templates have compatibility issues');
1128
+ }
1129
+ return;
1130
+ }
1131
+ // Check specific template by name or file
1132
+ let template;
1133
+ // Check if it's a registered template
1134
+ if (AVAILABLE_TEMPLATES[templateArg]) {
1135
+ template = AVAILABLE_TEMPLATES[templateArg];
1136
+ }
1137
+ // Check if it's a file
1138
+ else if (existsSync(templateArg)) {
1139
+ try {
1140
+ template = loadTemplateFile(templateArg);
1141
+ }
1142
+ catch (err) {
1143
+ if (err instanceof TemplateValidationError) {
1144
+ console.error(err.toUserMessage());
1145
+ }
1146
+ else {
1147
+ console.error(`❌ Error loading template: ${err instanceof Error ? err.message : String(err)}`);
1148
+ }
1149
+ process.exit(1);
1150
+ }
1151
+ }
1152
+ else {
1153
+ console.error(`❌ Template not found: ${templateArg}`);
1154
+ console.log('\nUse "list-templates" to see available templates, or provide a file path.');
1155
+ process.exit(1);
1156
+ }
1157
+ // Check compatibility
1158
+ const result = checkTemplateCompatibility(template.minHivemindVersion, template.version);
1159
+ console.log(`Template: ${template.name} (${template.id})`);
1160
+ console.log(`Template version: ${template.version}`);
1161
+ if (template.minHivemindVersion) {
1162
+ console.log(`Requires Hivemind: >= ${template.minHivemindVersion}`);
1163
+ }
1164
+ else {
1165
+ console.log('Requires Hivemind: (not specified)');
1166
+ }
1167
+ console.log('');
1168
+ if (result.compatible) {
1169
+ console.log(`✅ ${result.message}`);
1170
+ }
1171
+ else {
1172
+ console.log(`❌ ${result.message}`);
344
1173
  process.exit(1);
345
1174
  }
346
1175
  }
@@ -361,18 +1190,50 @@ else {
361
1190
  case 'validate':
362
1191
  validate();
363
1192
  break;
1193
+ case 'setup-mcp':
1194
+ setupMcp();
1195
+ break;
364
1196
  case 'fix':
365
1197
  fix();
366
1198
  break;
1199
+ case 'create-template':
1200
+ createTemplate();
1201
+ break;
1202
+ case 'validate-template':
1203
+ validateTemplate();
1204
+ break;
1205
+ case 'add-template':
1206
+ addTemplate();
1207
+ break;
1208
+ case 'list-templates':
1209
+ listTemplates();
1210
+ break;
1211
+ case 'generate-catalog':
1212
+ generateCatalog();
1213
+ break;
1214
+ case 'check-compatibility':
1215
+ checkCompatibility();
1216
+ break;
367
1217
  default:
368
1218
  console.log('Hivemind MCP Server\n');
369
1219
  console.log('Usage:');
370
- console.log(' npx @hiveforge/hivemind-mcp init - Interactive configuration setup');
371
- console.log(' npx @hiveforge/hivemind-mcp validate - Validate configuration');
372
- console.log(' npx @hiveforge/hivemind-mcp start - Start the MCP server');
373
- console.log(' npx @hiveforge/hivemind-mcp fix - Add frontmatter to skipped files');
374
- console.log(' npx @hiveforge/hivemind-mcp --vault <path> - Start with specified vault path');
375
- console.log(' npx @hiveforge/hivemind-mcp --vault . - Start with current directory as vault');
1220
+ console.log(' npx @hiveforge/hivemind-mcp init - Interactive setup wizard');
1221
+ console.log(' npx @hiveforge/hivemind-mcp validate - Validate configuration');
1222
+ console.log(' npx @hiveforge/hivemind-mcp start - Start the MCP server');
1223
+ console.log(' npx @hiveforge/hivemind-mcp setup-mcp - Generate MCP client config');
1224
+ console.log(' npx @hiveforge/hivemind-mcp fix - Add frontmatter to skipped files');
1225
+ console.log('');
1226
+ console.log('Template commands:');
1227
+ console.log(' npx @hiveforge/hivemind-mcp list-templates - List available templates');
1228
+ console.log(' npx @hiveforge/hivemind-mcp add-template <name|url> - Add a template');
1229
+ console.log(' npx @hiveforge/hivemind-mcp create-template - Create a new template interactively');
1230
+ console.log(' npx @hiveforge/hivemind-mcp validate-template [file] - Validate a template file');
1231
+ console.log(' npx @hiveforge/hivemind-mcp generate-catalog [file] - Generate template catalog JSON');
1232
+ console.log(' npx @hiveforge/hivemind-mcp check-compatibility [name] - Check template compatibility');
1233
+ console.log('');
1234
+ console.log('Advanced:');
1235
+ console.log(' npx @hiveforge/hivemind-mcp --vault <path> - Start with specified vault path');
1236
+ console.log(' npx @hiveforge/hivemind-mcp --vault . - Start with current directory as vault');
376
1237
  process.exit(command ? 1 : 0);
377
1238
  }
378
1239
  }