ruvnet-kb-first 6.2.0 → 6.3.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/bin/kb-first.js CHANGED
@@ -58,6 +58,9 @@ program
58
58
  .option('-f, --force', 'Overwrite existing configuration')
59
59
  .option('-t, --template <type>', 'Template type (basic, api, fullstack)', 'basic')
60
60
  .option('--no-hooks', 'Skip hook installation')
61
+ .option('--kb <schema>', 'Connect to existing KB schema (e.g., ask_ruvnet)')
62
+ .option('--kb-host <host>', 'KB database host', 'localhost')
63
+ .option('--kb-port <port>', 'KB database port', '5435')
61
64
  .action(initCommand);
62
65
 
63
66
  // Score command
@@ -113,11 +116,11 @@ program
113
116
  await startMCPServer(options);
114
117
  });
115
118
 
116
- // Parse arguments
117
- program.parse();
118
-
119
- // Show help if no command provided
119
+ // Show dashboard if no command provided (before parsing)
120
120
  if (!process.argv.slice(2).length) {
121
- console.log(banner);
122
- program.outputHelp();
121
+ const { dashboardCommand } = await import('../src/commands/dashboard.js');
122
+ await dashboardCommand();
123
+ } else {
124
+ // Parse arguments
125
+ program.parse();
123
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ruvnet-kb-first",
3
- "version": "6.2.0",
3
+ "version": "6.3.0",
4
4
  "description": "RuvNet KB-First Application Builder - Build intelligent applications on expert knowledge",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -56,19 +56,19 @@
56
56
  "glob": "^10.3.0",
57
57
  "inquirer": "^9.2.0",
58
58
  "ora": "^8.0.0",
59
- "pg": "^8.11.3",
59
+ "pg": "^8.16.3",
60
60
  "ruv-swarm": "^1.0.20",
61
61
  "ruvector": "^0.1.82"
62
62
  },
63
63
  "devDependencies": {
64
+ "@types/jest": "^29.5.11",
64
65
  "@types/node": "^20.10.0",
65
66
  "@types/pg": "^8.10.9",
66
- "typescript": "^5.3.0",
67
- "eslint": "^8.55.0",
68
67
  "@typescript-eslint/eslint-plugin": "^6.13.0",
69
68
  "@typescript-eslint/parser": "^6.13.0",
69
+ "eslint": "^8.55.0",
70
70
  "jest": "^29.7.0",
71
- "@types/jest": "^29.5.11"
71
+ "typescript": "^5.3.0"
72
72
  },
73
73
  "engines": {
74
74
  "node": ">=18.0.0"
@@ -0,0 +1,359 @@
1
+ /**
2
+ * RuvNet-KB-First Dashboard
3
+ *
4
+ * Shows comprehensive status when running `ruvnet-kb-first` with no arguments.
5
+ * Displays KB metrics, application scores, and actionable recommendations.
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+ import { existsSync, readFileSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { globSync } from 'glob';
12
+
13
+ export async function dashboardCommand() {
14
+ const cwd = process.cwd();
15
+ const projectName = cwd.split('/').pop();
16
+
17
+ // Banner
18
+ console.log('');
19
+ console.log(chalk.cyan('╔════════════════════════════════════════════════════════════════════════╗'));
20
+ console.log(chalk.cyan('║') + chalk.bold.white(' RuvNet-KB-First Dashboard ') + chalk.cyan('║'));
21
+ console.log(chalk.cyan('║') + chalk.gray(' Build intelligent applications on expert knowledge ') + chalk.cyan('║'));
22
+ console.log(chalk.cyan('╚════════════════════════════════════════════════════════════════════════╝'));
23
+ console.log('');
24
+
25
+ // Check if initialized
26
+ const configPath = join(cwd, '.ruvector', 'config.json');
27
+ if (!existsSync(configPath)) {
28
+ console.log(chalk.yellow(' ⚠ Not initialized in this directory'));
29
+ console.log('');
30
+ console.log(chalk.white(' Quick Start:'));
31
+ console.log(chalk.cyan(' ruvnet-kb-first init --kb ask_ruvnet'));
32
+ console.log('');
33
+ console.log(chalk.gray(' This will:'));
34
+ console.log(chalk.gray(' • Connect to the RuvNet Knowledge Base (230K+ entries)'));
35
+ console.log(chalk.gray(' • Set up hooks to enforce KB-first development'));
36
+ console.log(chalk.gray(' • Enable comprehensive scoring for your application'));
37
+ console.log('');
38
+ return;
39
+ }
40
+
41
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
42
+
43
+ // ═══════════════════════════════════════════════════════════════════════════
44
+ // SECTION 1: KNOWLEDGE BASE STATUS
45
+ // ═══════════════════════════════════════════════════════════════════════════
46
+ console.log(chalk.white.bold(' 📚 KNOWLEDGE BASE'));
47
+ console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
48
+
49
+ if (config.knowledgeBase?.connected) {
50
+ let kbStats = {
51
+ entries: config.knowledgeBase.entries,
52
+ isLive: false,
53
+ categories: 0,
54
+ avgEmbeddingSize: 0
55
+ };
56
+
57
+ // Try to get live stats
58
+ try {
59
+ const pg = await import('pg');
60
+ const client = new pg.default.Client({
61
+ host: config.knowledgeBase.host,
62
+ port: config.knowledgeBase.port,
63
+ database: 'postgres',
64
+ user: 'postgres',
65
+ password: 'guruKB2025'
66
+ });
67
+ await client.connect();
68
+
69
+ // Entry count
70
+ const countResult = await client.query(
71
+ `SELECT COUNT(*) as count FROM ${config.knowledgeBase.schema}.architecture_docs`
72
+ );
73
+ kbStats.entries = parseInt(countResult.rows[0].count);
74
+ kbStats.isLive = true;
75
+
76
+ // Get category count (if source column exists)
77
+ try {
78
+ const catResult = await client.query(
79
+ `SELECT COUNT(DISTINCT source) as cats FROM ${config.knowledgeBase.schema}.architecture_docs`
80
+ );
81
+ kbStats.categories = parseInt(catResult.rows[0].cats);
82
+ } catch (e) {
83
+ // Ignore if source column doesn't exist
84
+ }
85
+
86
+ await client.end();
87
+ } catch (e) {
88
+ // Use cached values
89
+ }
90
+
91
+ const statusIcon = kbStats.isLive ? chalk.green('●') : chalk.yellow('○');
92
+ const statusText = kbStats.isLive ? chalk.green('CONNECTED') : chalk.yellow('CACHED');
93
+
94
+ console.log(` ${statusIcon} Status: ${statusText}`);
95
+ console.log(` Schema: ${chalk.cyan(config.knowledgeBase.schema)}`);
96
+ console.log(` Entries: ${chalk.cyan(kbStats.entries.toLocaleString())}`);
97
+ if (kbStats.categories > 0) {
98
+ console.log(` Sources: ${chalk.cyan(kbStats.categories)}`);
99
+ }
100
+ console.log(` Host: ${chalk.gray(config.knowledgeBase.host + ':' + config.knowledgeBase.port)}`);
101
+
102
+ // KB Score (out of 100)
103
+ const kbScore = calculateKBScore(kbStats);
104
+ console.log('');
105
+ console.log(` ${chalk.white('KB Health Score:')} ${renderScoreBar(kbScore)} ${colorScore(kbScore)}/100`);
106
+
107
+ } else {
108
+ console.log(chalk.red(' ○ Status: NOT CONNECTED'));
109
+ console.log('');
110
+ console.log(chalk.yellow(' Run: ruvnet-kb-first init --kb ask_ruvnet'));
111
+ }
112
+
113
+ console.log('');
114
+
115
+ // ═══════════════════════════════════════════════════════════════════════════
116
+ // SECTION 2: APPLICATION SCORES
117
+ // ═══════════════════════════════════════════════════════════════════════════
118
+ console.log(chalk.white.bold(' 📊 APPLICATION SCORES'));
119
+ console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
120
+
121
+ const scores = await calculateAllScores(cwd, config);
122
+
123
+ // Display each score category
124
+ for (const [category, data] of Object.entries(scores)) {
125
+ const label = formatLabel(category).padEnd(18);
126
+ const bar = renderScoreBar(data.percentage);
127
+ const scoreText = colorScore(data.percentage);
128
+ console.log(` ${label} ${bar} ${scoreText.padStart(4)}/100 ${chalk.gray(data.summary)}`);
129
+ }
130
+
131
+ // Total Score
132
+ const totalScore = Math.round(
133
+ Object.values(scores).reduce((sum, s) => sum + s.percentage, 0) / Object.keys(scores).length
134
+ );
135
+
136
+ console.log('');
137
+ console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
138
+ const totalBar = renderScoreBar(totalScore);
139
+ console.log(` ${chalk.bold('OVERALL SCORE')} ${totalBar} ${colorScore(totalScore).padStart(4)}/100 ${getGrade(totalScore)}`);
140
+ console.log('');
141
+
142
+ // ═══════════════════════════════════════════════════════════════════════════
143
+ // SECTION 3: PROJECT INFO
144
+ // ═══════════════════════════════════════════════════════════════════════════
145
+ console.log(chalk.white.bold(' 📁 PROJECT'));
146
+ console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
147
+ console.log(` Name: ${chalk.cyan(projectName)}`);
148
+ console.log(` Namespace: ${chalk.cyan(config.kbFirst?.namespace || 'not set')}`);
149
+ console.log(` Phase: ${chalk.cyan(config.phases?.current || 0)}/11`);
150
+ console.log(` Hooks: ${config.hooks?.enabled ? chalk.green('Enabled') : chalk.red('Disabled')}`);
151
+ console.log('');
152
+
153
+ // ═══════════════════════════════════════════════════════════════════════════
154
+ // SECTION 4: RECOMMENDATIONS
155
+ // ═══════════════════════════════════════════════════════════════════════════
156
+ const recommendations = getRecommendations(scores, config);
157
+
158
+ if (recommendations.length > 0) {
159
+ console.log(chalk.white.bold(' 💡 RECOMMENDATIONS'));
160
+ console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
161
+ for (const rec of recommendations.slice(0, 5)) {
162
+ console.log(chalk.yellow(` → ${rec}`));
163
+ }
164
+ console.log('');
165
+ }
166
+
167
+ // ═══════════════════════════════════════════════════════════════════════════
168
+ // SECTION 5: COMMANDS
169
+ // ═══════════════════════════════════════════════════════════════════════════
170
+ console.log(chalk.white.bold(' ⌨️ COMMANDS'));
171
+ console.log(chalk.gray(' ────────────────────────────────────────────────────────────────────'));
172
+ console.log(chalk.cyan(' ruvnet-kb-first score --detailed') + chalk.gray(' Full score breakdown'));
173
+ console.log(chalk.cyan(' ruvnet-kb-first status --detailed') + chalk.gray(' Detailed project status'));
174
+ console.log(chalk.cyan(' ruvnet-kb-first verify') + chalk.gray(' Run verification checks'));
175
+ console.log(chalk.cyan(' ruvnet-kb-first phase <n>') + chalk.gray(' Execute build phase'));
176
+ console.log('');
177
+
178
+ // Final prompt
179
+ if (config.knowledgeBase?.connected) {
180
+ console.log(chalk.gray(' Ask Claude: "Review this app using RuvNet-KB-First and recommend improvements"'));
181
+ }
182
+ console.log('');
183
+ }
184
+
185
+ function calculateKBScore(stats) {
186
+ let score = 0;
187
+
188
+ // Connection (30 points)
189
+ if (stats.isLive) score += 30;
190
+ else score += 15;
191
+
192
+ // Entry count (50 points)
193
+ if (stats.entries >= 100000) score += 50;
194
+ else if (stats.entries >= 50000) score += 40;
195
+ else if (stats.entries >= 10000) score += 30;
196
+ else if (stats.entries >= 1000) score += 20;
197
+ else score += 10;
198
+
199
+ // Categories (20 points)
200
+ if (stats.categories >= 50) score += 20;
201
+ else if (stats.categories >= 20) score += 15;
202
+ else if (stats.categories >= 10) score += 10;
203
+ else score += 5;
204
+
205
+ return Math.min(score, 100);
206
+ }
207
+
208
+ async function calculateAllScores(cwd, config) {
209
+ const scores = {};
210
+
211
+ // 1. KB Coverage
212
+ const srcDir = join(cwd, 'src');
213
+ let kbCoverage = { percentage: 100, summary: 'No code yet' };
214
+ if (existsSync(srcDir)) {
215
+ const codeFiles = globSync('**/*.{ts,tsx,js,jsx,py}', { cwd: srcDir });
216
+ if (codeFiles.length > 0) {
217
+ let withCitation = 0;
218
+ for (const file of codeFiles) {
219
+ const content = readFileSync(join(srcDir, file), 'utf-8');
220
+ if (content.includes('KB-Generated:') || content.includes('Sources:')) {
221
+ withCitation++;
222
+ }
223
+ }
224
+ const pct = Math.round((withCitation / codeFiles.length) * 100);
225
+ kbCoverage = { percentage: pct, summary: `${withCitation}/${codeFiles.length} files` };
226
+ }
227
+ }
228
+ scores.kbCoverage = kbCoverage;
229
+
230
+ // 2. Phase Progress
231
+ const completed = config.phases?.completed?.length || 0;
232
+ const phasePct = Math.round((completed / 12) * 100);
233
+ scores.phaseProgress = { percentage: phasePct, summary: `${completed}/12 complete` };
234
+
235
+ // 3. Hook Compliance
236
+ let hookPct = 0;
237
+ if (config.hooks?.enabled) {
238
+ const hooksDir = join(cwd, '.ruvector', 'hooks');
239
+ let found = 0;
240
+ if (existsSync(join(hooksDir, 'pre-tool-use.py'))) found++;
241
+ if (existsSync(join(hooksDir, 'post-tool-use.py'))) found++;
242
+ hookPct = found === 2 ? 100 : (found === 1 ? 50 : 0);
243
+ }
244
+ scores.hooks = { percentage: hookPct, summary: hookPct === 100 ? 'All installed' : 'Incomplete' };
245
+
246
+ // 4. KB Gaps
247
+ const gapPath = join(cwd, '.ruvector', 'gaps.jsonl');
248
+ let gapPct = 100;
249
+ let gapSummary = 'No gaps';
250
+ if (existsSync(gapPath)) {
251
+ const content = readFileSync(gapPath, 'utf-8').trim();
252
+ if (content) {
253
+ const gaps = content.split('\n').length;
254
+ gapPct = Math.max(0, 100 - (gaps * 10));
255
+ gapSummary = `${gaps} unresolved`;
256
+ }
257
+ }
258
+ scores.kbGaps = { percentage: gapPct, summary: gapSummary };
259
+
260
+ // 5. Documentation
261
+ let docScore = 0;
262
+ if (existsSync(join(cwd, 'README.md'))) docScore += 40;
263
+ if (existsSync(join(cwd, 'docs', 'api.md'))) docScore += 30;
264
+ if (existsSync(join(cwd, 'docs', 'architecture.md'))) docScore += 30;
265
+ scores.documentation = { percentage: docScore, summary: docScore === 100 ? 'Complete' : 'Incomplete' };
266
+
267
+ // 6. Security
268
+ let secScore = 100;
269
+ const gitignore = join(cwd, '.gitignore');
270
+ if (!existsSync(gitignore)) {
271
+ secScore -= 30;
272
+ } else {
273
+ const content = readFileSync(gitignore, 'utf-8');
274
+ if (!content.includes('.env')) secScore -= 20;
275
+ }
276
+ if (!existsSync(join(cwd, 'package-lock.json')) && existsSync(join(cwd, 'package.json'))) {
277
+ secScore -= 20;
278
+ }
279
+ scores.security = { percentage: Math.max(0, secScore), summary: secScore === 100 ? 'Passed' : 'Issues found' };
280
+
281
+ return scores;
282
+ }
283
+
284
+ function renderScoreBar(percentage) {
285
+ const filled = Math.round(percentage / 5);
286
+ const empty = 20 - filled;
287
+ const color = percentage >= 80 ? chalk.green :
288
+ percentage >= 60 ? chalk.yellow :
289
+ percentage >= 40 ? chalk.hex('#FFA500') :
290
+ chalk.red;
291
+ return color('█'.repeat(filled)) + chalk.gray('░'.repeat(empty));
292
+ }
293
+
294
+ function colorScore(score) {
295
+ const color = score >= 80 ? chalk.green :
296
+ score >= 60 ? chalk.yellow :
297
+ score >= 40 ? chalk.hex('#FFA500') :
298
+ chalk.red;
299
+ return color(score.toString());
300
+ }
301
+
302
+ function formatLabel(key) {
303
+ const labels = {
304
+ kbCoverage: 'KB Coverage',
305
+ phaseProgress: 'Phase Progress',
306
+ hooks: 'Hooks',
307
+ kbGaps: 'KB Gaps',
308
+ documentation: 'Documentation',
309
+ security: 'Security'
310
+ };
311
+ return labels[key] || key;
312
+ }
313
+
314
+ function getGrade(score) {
315
+ if (score >= 95) return chalk.green.bold('A+');
316
+ if (score >= 90) return chalk.green.bold('A');
317
+ if (score >= 85) return chalk.green('A-');
318
+ if (score >= 80) return chalk.yellow.bold('B+');
319
+ if (score >= 75) return chalk.yellow('B');
320
+ if (score >= 70) return chalk.yellow('B-');
321
+ if (score >= 65) return chalk.hex('#FFA500')('C+');
322
+ if (score >= 60) return chalk.hex('#FFA500')('C');
323
+ if (score >= 55) return chalk.hex('#FFA500')('C-');
324
+ return chalk.red.bold('F');
325
+ }
326
+
327
+ function getRecommendations(scores, config) {
328
+ const recs = [];
329
+
330
+ if (!config.knowledgeBase?.connected) {
331
+ recs.push('Connect to KB: ruvnet-kb-first init --kb ask_ruvnet --force');
332
+ }
333
+
334
+ if (scores.kbCoverage.percentage < 80) {
335
+ recs.push('Add KB citations to code files (use kb_code_gen before writing)');
336
+ }
337
+
338
+ if (scores.phaseProgress.percentage < 50) {
339
+ recs.push(`Complete build phases: ruvnet-kb-first phase ${config.phases?.current || 0}`);
340
+ }
341
+
342
+ if (scores.hooks.percentage < 100) {
343
+ recs.push('Install enforcement hooks: ruvnet-kb-first hooks --install');
344
+ }
345
+
346
+ if (scores.kbGaps.percentage < 100) {
347
+ recs.push('Resolve KB gaps logged in .ruvector/gaps.jsonl');
348
+ }
349
+
350
+ if (scores.documentation.percentage < 100) {
351
+ recs.push('Add documentation: README.md, docs/api.md, docs/architecture.md');
352
+ }
353
+
354
+ if (scores.security.percentage < 100) {
355
+ recs.push('Fix security issues: ruvnet-kb-first verify --phase=9');
356
+ }
357
+
358
+ return recs;
359
+ }
@@ -14,6 +14,22 @@ import inquirer from 'inquirer';
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = dirname(__filename);
16
16
 
17
+ // Known KB schemas with descriptions
18
+ const KNOWN_KB_SCHEMAS = {
19
+ 'ask_ruvnet': {
20
+ description: 'RuvNet comprehensive knowledge base (230K+ entries)',
21
+ path: '/Users/stuartkerr/Code/Ask-Ruvnet'
22
+ },
23
+ 'retirewell': {
24
+ description: 'Retirement planning knowledge base',
25
+ path: '/Users/stuartkerr/Code/RetireWell'
26
+ },
27
+ 'presentermode': {
28
+ description: 'Presentation and public speaking knowledge',
29
+ path: null
30
+ }
31
+ };
32
+
17
33
  // Project structure template
18
34
  const PROJECT_STRUCTURE = {
19
35
  directories: [
@@ -30,12 +46,21 @@ const PROJECT_STRUCTURE = {
30
46
  files: {
31
47
  '.ruvector/config.json': {
32
48
  kbFirst: {
33
- version: '5.0.0',
49
+ version: '6.2.0',
34
50
  initialized: new Date().toISOString(),
35
51
  namespace: '',
36
52
  minConfidence: 0.5,
37
53
  gapLogging: true
38
54
  },
55
+ knowledgeBase: {
56
+ schema: null,
57
+ host: 'localhost',
58
+ port: 5435,
59
+ database: 'postgres',
60
+ table: 'architecture_docs',
61
+ entries: 0,
62
+ connected: false
63
+ },
39
64
  hooks: {
40
65
  enabled: true,
41
66
  preToolUse: true,
@@ -55,7 +80,7 @@ export async function initCommand(options) {
55
80
  const projectName = cwd.split('/').pop().toLowerCase().replace(/[^a-z0-9]/g, '_');
56
81
 
57
82
  console.log('');
58
- console.log(chalk.cyan('Initializing KB-First project structure...'));
83
+ console.log(chalk.cyan('Initializing RuvNet-KB-First project structure...'));
59
84
  console.log('');
60
85
 
61
86
  // Check if already initialized
@@ -67,14 +92,42 @@ export async function initCommand(options) {
67
92
  }
68
93
 
69
94
  // Interactive prompts if not forcing
70
- let config = { ...PROJECT_STRUCTURE.files['.ruvector/config.json'] };
95
+ let config = JSON.parse(JSON.stringify(PROJECT_STRUCTURE.files['.ruvector/config.json']));
96
+ config.kbFirst.initialized = new Date().toISOString();
97
+
98
+ // Handle --kb flag for connecting to existing KB
99
+ let kbSchema = options.kb;
100
+ const kbHost = options.kbHost || 'localhost';
101
+ const kbPort = options.kbPort || '5435';
102
+
103
+ if (!options.force && !kbSchema) {
104
+ // Show available KBs if not specified
105
+ const kbChoices = [
106
+ { name: 'None - Create new KB for this project', value: null },
107
+ ...Object.entries(KNOWN_KB_SCHEMAS).map(([schema, info]) => ({
108
+ name: `${schema} - ${info.description}`,
109
+ value: schema
110
+ })),
111
+ { name: 'Other - Enter custom schema name', value: '__custom__' }
112
+ ];
71
113
 
72
- if (!options.force) {
73
114
  const answers = await inquirer.prompt([
115
+ {
116
+ type: 'list',
117
+ name: 'kbSchema',
118
+ message: 'Connect to existing Knowledge Base?',
119
+ choices: kbChoices
120
+ },
121
+ {
122
+ type: 'input',
123
+ name: 'customSchema',
124
+ message: 'Enter KB schema name:',
125
+ when: (answers) => answers.kbSchema === '__custom__'
126
+ },
74
127
  {
75
128
  type: 'input',
76
129
  name: 'namespace',
77
- message: 'KB namespace (leave empty for project name):',
130
+ message: 'Project namespace (for new content):',
78
131
  default: projectName
79
132
  },
80
133
  {
@@ -96,12 +149,74 @@ export async function initCommand(options) {
96
149
  }
97
150
  ]);
98
151
 
152
+ kbSchema = answers.kbSchema === '__custom__' ? answers.customSchema : answers.kbSchema;
99
153
  config.kbFirst.namespace = answers.namespace || projectName;
100
154
  config.hooks.enabled = answers.installHooks;
101
155
  } else {
102
156
  config.kbFirst.namespace = projectName;
103
157
  }
104
158
 
159
+ // Connect to KB if specified
160
+ if (kbSchema) {
161
+ const spinner = ora(`Connecting to KB schema: ${kbSchema}...`).start();
162
+
163
+ try {
164
+ const pg = await import('pg');
165
+ const client = new pg.default.Client({
166
+ host: kbHost,
167
+ port: parseInt(kbPort),
168
+ database: 'postgres',
169
+ user: 'postgres',
170
+ password: 'guruKB2025'
171
+ });
172
+
173
+ await client.connect();
174
+
175
+ // Check if schema exists and count entries
176
+ const schemaCheck = await client.query(
177
+ `SELECT COUNT(*) as count FROM information_schema.schemata WHERE schema_name = $1`,
178
+ [kbSchema]
179
+ );
180
+
181
+ if (schemaCheck.rows[0].count === '0') {
182
+ spinner.fail(`KB schema '${kbSchema}' not found`);
183
+ await client.end();
184
+ console.log(chalk.yellow('\nAvailable schemas:'));
185
+ Object.entries(KNOWN_KB_SCHEMAS).forEach(([name, info]) => {
186
+ console.log(chalk.gray(` ${name} - ${info.description}`));
187
+ });
188
+ return;
189
+ }
190
+
191
+ // Count entries
192
+ const countResult = await client.query(
193
+ `SELECT COUNT(*) as count FROM ${kbSchema}.architecture_docs`
194
+ );
195
+ const entryCount = parseInt(countResult.rows[0].count);
196
+
197
+ await client.end();
198
+
199
+ // Update config with KB connection
200
+ config.knowledgeBase = {
201
+ schema: kbSchema,
202
+ host: kbHost,
203
+ port: parseInt(kbPort),
204
+ database: 'postgres',
205
+ table: 'architecture_docs',
206
+ entries: entryCount,
207
+ connected: true,
208
+ connectedAt: new Date().toISOString()
209
+ };
210
+
211
+ spinner.succeed(`Connected to KB: ${kbSchema} (${entryCount.toLocaleString()} entries)`);
212
+
213
+ } catch (err) {
214
+ spinner.fail(`Failed to connect to KB: ${err.message}`);
215
+ console.log(chalk.yellow('\nMake sure the ruvector-kb container is running:'));
216
+ console.log(chalk.gray(' docker ps | grep ruvector-kb'));
217
+ }
218
+ }
219
+
105
220
  // Create directories
106
221
  const spinner = ora('Creating directory structure...').start();
107
222
 
@@ -192,8 +307,18 @@ export async function initCommand(options) {
192
307
 
193
308
  // Summary
194
309
  console.log('');
195
- console.log(chalk.green('KB-First project initialized successfully!'));
310
+ console.log(chalk.green('RuvNet-KB-First project initialized successfully!'));
196
311
  console.log('');
312
+
313
+ // Show KB connection status
314
+ if (config.knowledgeBase.connected) {
315
+ console.log(chalk.white('Knowledge Base:'));
316
+ console.log(chalk.green(` ✓ Connected to: ${config.knowledgeBase.schema}`));
317
+ console.log(chalk.gray(` ${config.knowledgeBase.entries.toLocaleString()} entries available`));
318
+ console.log(chalk.gray(` Host: ${config.knowledgeBase.host}:${config.knowledgeBase.port}`));
319
+ console.log('');
320
+ }
321
+
197
322
  console.log(chalk.white('Project structure:'));
198
323
  console.log(chalk.gray(' .ruvector/ - KB configuration and hooks'));
199
324
  console.log(chalk.gray(' src/kb/ - Knowledge base modules'));
@@ -201,9 +326,13 @@ export async function initCommand(options) {
201
326
  console.log(chalk.gray(' scripts/ - Verification scripts'));
202
327
  console.log('');
203
328
  console.log(chalk.white('Next steps:'));
204
- console.log(chalk.cyan(' 1. Run: kb-first status'));
205
- console.log(chalk.cyan(' 2. Start Phase 0: kb-first phase 0'));
206
- console.log(chalk.cyan(' 3. Build your KB in src/kb/'));
329
+ console.log(chalk.cyan(' 1. Run: npx ruvnet-kb-first status'));
330
+ console.log(chalk.cyan(' 2. Start Phase 0: npx ruvnet-kb-first phase 0'));
331
+ if (config.knowledgeBase.connected) {
332
+ console.log(chalk.cyan(' 3. Ask Claude: "Review this app using RuvNet-KB-First and recommend improvements"'));
333
+ } else {
334
+ console.log(chalk.cyan(' 3. Build your KB in src/kb/'));
335
+ }
207
336
  console.log('');
208
337
  }
209
338
 
@@ -31,22 +31,70 @@ export async function statusCommand(options) {
31
31
 
32
32
  console.log('');
33
33
  console.log(chalk.cyan('╔═══════════════════════════════════════════════════════════════╗'));
34
- console.log(chalk.cyan('║') + chalk.bold.white(' KB-First Project Status ') + chalk.cyan('║'));
34
+ console.log(chalk.cyan('║') + chalk.bold.white(' RuvNet-KB-First Project Status ') + chalk.cyan('║'));
35
35
  console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════════╝'));
36
36
  console.log('');
37
37
 
38
38
  // Check if initialized
39
39
  const configPath = join(cwd, '.ruvector', 'config.json');
40
40
  if (!existsSync(configPath)) {
41
- console.log(chalk.red(' Not a KB-First project.'));
41
+ console.log(chalk.red(' Not a RuvNet-KB-First project.'));
42
42
  console.log('');
43
- console.log(chalk.gray(' Initialize with: kb-first init'));
43
+ console.log(chalk.gray(' Initialize with: npx ruvnet-kb-first init'));
44
+ console.log(chalk.gray(' With KB: npx ruvnet-kb-first init --kb ask_ruvnet'));
44
45
  console.log('');
45
46
  return;
46
47
  }
47
48
 
48
49
  const config = JSON.parse(readFileSync(configPath, 'utf-8'));
49
50
 
51
+ // Knowledge Base Connection (PROMINENT - this is the "Bible")
52
+ if (config.knowledgeBase?.connected) {
53
+ console.log(chalk.white(' Knowledge Base (Authority Source)'));
54
+ console.log(chalk.gray(' ─────────────────────────────────────────────────────────────'));
55
+
56
+ // Try to get live count
57
+ let liveCount = config.knowledgeBase.entries;
58
+ let isLive = false;
59
+ try {
60
+ const pg = await import('pg');
61
+ const client = new pg.default.Client({
62
+ host: config.knowledgeBase.host,
63
+ port: config.knowledgeBase.port,
64
+ database: 'postgres',
65
+ user: 'postgres',
66
+ password: 'guruKB2025'
67
+ });
68
+ await client.connect();
69
+ const result = await client.query(
70
+ `SELECT COUNT(*) as count FROM ${config.knowledgeBase.schema}.architecture_docs`
71
+ );
72
+ liveCount = parseInt(result.rows[0].count);
73
+ await client.end();
74
+ isLive = true;
75
+ } catch (e) {
76
+ // Use cached count
77
+ }
78
+
79
+ const statusIcon = isLive ? chalk.green('●') : chalk.yellow('○');
80
+ console.log(` ${statusIcon} Schema: ${chalk.green(config.knowledgeBase.schema)}`);
81
+ console.log(` Entries: ${chalk.cyan(liveCount.toLocaleString())} ${isLive ? chalk.green('(live)') : chalk.yellow('(cached)')}`);
82
+ console.log(` Host: ${chalk.gray(config.knowledgeBase.host + ':' + config.knowledgeBase.port)}`);
83
+ console.log('');
84
+ console.log(chalk.gray(' Claude will use this KB to:'));
85
+ console.log(chalk.gray(' • Verify architecture patterns are correct'));
86
+ console.log(chalk.gray(' • Ensure optimal configurations are applied'));
87
+ console.log(chalk.gray(' • Identify missing best practices'));
88
+ console.log(chalk.gray(' • Generate code with proper KB citations'));
89
+ console.log('');
90
+ } else {
91
+ console.log(chalk.white(' Knowledge Base'));
92
+ console.log(chalk.gray(' ─────────────────────────────────────────────────────────────'));
93
+ console.log(chalk.yellow(' ○ Not connected'));
94
+ console.log(chalk.gray(' Reinitialize with: npx ruvnet-kb-first init --kb ask_ruvnet'));
95
+ console.log('');
96
+ }
97
+
50
98
  // Project Info
51
99
  console.log(chalk.white(' Project Information'));
52
100
  console.log(chalk.gray(' ─────────────────────────────────────────────────────────────'));
@@ -132,12 +180,18 @@ export async function statusCommand(options) {
132
180
  console.log(chalk.white(' Next Steps'));
133
181
  console.log(chalk.gray(' ─────────────────────────────────────────────────────────────'));
134
182
 
183
+ if (config.knowledgeBase?.connected) {
184
+ console.log(chalk.cyan(' → Ask Claude: "Review this app using RuvNet-KB-First"'));
185
+ console.log(chalk.gray(' Claude will query the KB and analyze your code'));
186
+ console.log('');
187
+ }
188
+
135
189
  if (currentPhase < 11) {
136
- console.log(chalk.cyan(` → Run: kb-first phase ${currentPhase}`));
190
+ console.log(chalk.cyan(` → Run: npx ruvnet-kb-first phase ${currentPhase}`));
137
191
  console.log(chalk.gray(` Complete Phase ${currentPhase}: ${PHASES.find(p => p.num === currentPhase)?.name}`));
138
192
  } else {
139
193
  console.log(chalk.green(' → All phases complete!'));
140
- console.log(chalk.gray(' Run: kb-first score --detailed'));
194
+ console.log(chalk.gray(' Run: npx ruvnet-kb-first score --detailed'));
141
195
  }
142
196
 
143
197
  console.log('');