ruvnet-kb-first 5.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +674 -0
  3. package/SKILL.md +740 -0
  4. package/bin/kb-first.js +123 -0
  5. package/install/init-project.sh +435 -0
  6. package/install/install-global.sh +257 -0
  7. package/install/kb-first-autodetect.sh +108 -0
  8. package/install/kb-first-command.md +80 -0
  9. package/install/kb-first-skill.md +262 -0
  10. package/package.json +87 -0
  11. package/phases/00-assessment.md +529 -0
  12. package/phases/01-storage.md +194 -0
  13. package/phases/01.5-hooks-setup.md +521 -0
  14. package/phases/02-kb-creation.md +413 -0
  15. package/phases/03-persistence.md +125 -0
  16. package/phases/04-visualization.md +170 -0
  17. package/phases/05-integration.md +114 -0
  18. package/phases/06-scaffold.md +130 -0
  19. package/phases/07-build.md +493 -0
  20. package/phases/08-verification.md +597 -0
  21. package/phases/09-security.md +512 -0
  22. package/phases/10-documentation.md +613 -0
  23. package/phases/11-deployment.md +670 -0
  24. package/phases/testing.md +713 -0
  25. package/scripts/1.5-hooks-verify.sh +252 -0
  26. package/scripts/8.1-code-scan.sh +58 -0
  27. package/scripts/8.2-import-check.sh +42 -0
  28. package/scripts/8.3-source-returns.sh +52 -0
  29. package/scripts/8.4-startup-verify.sh +65 -0
  30. package/scripts/8.5-fallback-check.sh +63 -0
  31. package/scripts/8.6-attribution.sh +56 -0
  32. package/scripts/8.7-confidence.sh +56 -0
  33. package/scripts/8.8-gap-logging.sh +70 -0
  34. package/scripts/9-security-audit.sh +202 -0
  35. package/scripts/init-project.sh +395 -0
  36. package/scripts/verify-enforcement.sh +167 -0
  37. package/src/commands/hooks.js +361 -0
  38. package/src/commands/init.js +315 -0
  39. package/src/commands/phase.js +372 -0
  40. package/src/commands/score.js +380 -0
  41. package/src/commands/status.js +193 -0
  42. package/src/commands/verify.js +286 -0
  43. package/src/index.js +56 -0
  44. package/src/mcp-server.js +412 -0
  45. package/templates/attention-router.ts +534 -0
  46. package/templates/code-analysis.ts +683 -0
  47. package/templates/federated-kb-learner.ts +649 -0
  48. package/templates/gnn-engine.ts +1091 -0
  49. package/templates/intentions.md +277 -0
  50. package/templates/kb-client.ts +905 -0
  51. package/templates/schema.sql +303 -0
  52. package/templates/sona-config.ts +312 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * KB-First Init Command
3
+ *
4
+ * Initializes KB-First structure in the current project.
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import ora from 'ora';
9
+ import { existsSync, mkdirSync, writeFileSync, copyFileSync, readFileSync } from 'fs';
10
+ import { join, dirname } from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import inquirer from 'inquirer';
13
+
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ // Project structure template
18
+ const PROJECT_STRUCTURE = {
19
+ directories: [
20
+ '.ruvector',
21
+ '.ruvector/hooks',
22
+ '.ruvector/domain',
23
+ 'src',
24
+ 'src/kb',
25
+ 'docs',
26
+ 'phases',
27
+ 'scripts',
28
+ 'templates'
29
+ ],
30
+ files: {
31
+ '.ruvector/config.json': {
32
+ kbFirst: {
33
+ version: '5.0.0',
34
+ initialized: new Date().toISOString(),
35
+ namespace: '',
36
+ minConfidence: 0.5,
37
+ gapLogging: true
38
+ },
39
+ hooks: {
40
+ enabled: true,
41
+ preToolUse: true,
42
+ postToolUse: true,
43
+ sessionEnd: true
44
+ },
45
+ phases: {
46
+ current: 0,
47
+ completed: []
48
+ }
49
+ }
50
+ }
51
+ };
52
+
53
+ export async function initCommand(options) {
54
+ const cwd = process.cwd();
55
+ const projectName = cwd.split('/').pop().toLowerCase().replace(/[^a-z0-9]/g, '_');
56
+
57
+ console.log('');
58
+ console.log(chalk.cyan('Initializing KB-First project structure...'));
59
+ console.log('');
60
+
61
+ // Check if already initialized
62
+ const configPath = join(cwd, '.ruvector', 'config.json');
63
+ if (existsSync(configPath) && !options.force) {
64
+ console.log(chalk.yellow('Project already initialized.'));
65
+ console.log(chalk.gray('Use --force to reinitialize.'));
66
+ return;
67
+ }
68
+
69
+ // Interactive prompts if not forcing
70
+ let config = { ...PROJECT_STRUCTURE.files['.ruvector/config.json'] };
71
+
72
+ if (!options.force) {
73
+ const answers = await inquirer.prompt([
74
+ {
75
+ type: 'input',
76
+ name: 'namespace',
77
+ message: 'KB namespace (leave empty for project name):',
78
+ default: projectName
79
+ },
80
+ {
81
+ type: 'confirm',
82
+ name: 'installHooks',
83
+ message: 'Install KB-First hooks?',
84
+ default: options.hooks !== false
85
+ },
86
+ {
87
+ type: 'list',
88
+ name: 'template',
89
+ message: 'Project template:',
90
+ choices: [
91
+ { name: 'Basic - Simple KB structure', value: 'basic' },
92
+ { name: 'API - REST API with KB search', value: 'api' },
93
+ { name: 'Fullstack - Complete web application', value: 'fullstack' }
94
+ ],
95
+ default: options.template || 'basic'
96
+ }
97
+ ]);
98
+
99
+ config.kbFirst.namespace = answers.namespace || projectName;
100
+ config.hooks.enabled = answers.installHooks;
101
+ } else {
102
+ config.kbFirst.namespace = projectName;
103
+ }
104
+
105
+ // Create directories
106
+ const spinner = ora('Creating directory structure...').start();
107
+
108
+ for (const dir of PROJECT_STRUCTURE.directories) {
109
+ const dirPath = join(cwd, dir);
110
+ if (!existsSync(dirPath)) {
111
+ mkdirSync(dirPath, { recursive: true });
112
+ }
113
+ }
114
+
115
+ spinner.succeed('Directory structure created');
116
+
117
+ // Write config
118
+ spinner.start('Writing configuration...');
119
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
120
+ spinner.succeed('Configuration written');
121
+
122
+ // Copy phase documentation
123
+ spinner.start('Copying phase documentation...');
124
+ const phasesSource = join(__dirname, '..', '..', 'phases');
125
+ const phasesTarget = join(cwd, 'phases');
126
+
127
+ if (existsSync(phasesSource)) {
128
+ const { globSync } = await import('glob');
129
+ const phaseFiles = globSync('*.md', { cwd: phasesSource });
130
+
131
+ for (const file of phaseFiles) {
132
+ const source = join(phasesSource, file);
133
+ const target = join(phasesTarget, file);
134
+ if (!existsSync(target)) {
135
+ copyFileSync(source, target);
136
+ }
137
+ }
138
+ spinner.succeed(`Copied ${phaseFiles.length} phase documents`);
139
+ } else {
140
+ spinner.warn('Phase documentation not found - skipping');
141
+ }
142
+
143
+ // Copy verification scripts
144
+ spinner.start('Copying verification scripts...');
145
+ const scriptsSource = join(__dirname, '..', '..', 'scripts');
146
+ const scriptsTarget = join(cwd, 'scripts');
147
+
148
+ if (existsSync(scriptsSource)) {
149
+ const { globSync } = await import('glob');
150
+ const scriptFiles = globSync('*.sh', { cwd: scriptsSource });
151
+
152
+ for (const file of scriptFiles) {
153
+ const source = join(scriptsSource, file);
154
+ const target = join(scriptsTarget, file);
155
+ if (!existsSync(target)) {
156
+ copyFileSync(source, target);
157
+ }
158
+ }
159
+ spinner.succeed(`Copied ${scriptFiles.length} verification scripts`);
160
+ } else {
161
+ spinner.warn('Verification scripts not found - skipping');
162
+ }
163
+
164
+ // Install hooks if requested
165
+ if (config.hooks.enabled && options.hooks !== false) {
166
+ spinner.start('Installing KB-First hooks...');
167
+ await installHooks(cwd);
168
+ spinner.succeed('Hooks installed');
169
+ }
170
+
171
+ // Create .gitignore entries
172
+ spinner.start('Updating .gitignore...');
173
+ const gitignorePath = join(cwd, '.gitignore');
174
+ const gitignoreEntries = [
175
+ '',
176
+ '# KB-First',
177
+ '.ruvector/domain/',
178
+ '.ruvector/cache/',
179
+ '*.embedding',
180
+ ''
181
+ ].join('\n');
182
+
183
+ if (existsSync(gitignorePath)) {
184
+ const content = readFileSync(gitignorePath, 'utf-8');
185
+ if (!content.includes('# KB-First')) {
186
+ writeFileSync(gitignorePath, content + gitignoreEntries);
187
+ }
188
+ } else {
189
+ writeFileSync(gitignorePath, gitignoreEntries);
190
+ }
191
+ spinner.succeed('.gitignore updated');
192
+
193
+ // Summary
194
+ console.log('');
195
+ console.log(chalk.green('KB-First project initialized successfully!'));
196
+ console.log('');
197
+ console.log(chalk.white('Project structure:'));
198
+ console.log(chalk.gray(' .ruvector/ - KB configuration and hooks'));
199
+ console.log(chalk.gray(' src/kb/ - Knowledge base modules'));
200
+ console.log(chalk.gray(' phases/ - Build phase documentation'));
201
+ console.log(chalk.gray(' scripts/ - Verification scripts'));
202
+ console.log('');
203
+ 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/'));
207
+ console.log('');
208
+ }
209
+
210
+ async function installHooks(projectDir) {
211
+ const hooksDir = join(projectDir, '.ruvector', 'hooks');
212
+
213
+ // PreToolUse hook
214
+ const preToolUseHook = `#!/usr/bin/env python3
215
+ """
216
+ KB-First PreToolUse Hook
217
+ Intercepts Write operations to enforce KB-First patterns.
218
+ """
219
+
220
+ import json
221
+ import sys
222
+ import os
223
+
224
+ def check_kb_first(tool_input):
225
+ """Check if Write operation follows KB-First principles."""
226
+ if tool_input.get('tool_name') != 'Write':
227
+ return {'decision': 'approve'}
228
+
229
+ file_path = tool_input.get('tool_input', {}).get('file_path', '')
230
+ content = tool_input.get('tool_input', {}).get('content', '')
231
+
232
+ # Check if it's a code file
233
+ code_extensions = ['.ts', '.tsx', '.js', '.jsx', '.py', '.go', '.rs', '.java']
234
+ is_code_file = any(file_path.endswith(ext) for ext in code_extensions)
235
+
236
+ if not is_code_file:
237
+ return {'decision': 'approve'}
238
+
239
+ # Check for KB citation header
240
+ has_kb_citation = 'KB-Generated:' in content or 'Sources:' in content
241
+
242
+ if not has_kb_citation:
243
+ return {
244
+ 'decision': 'block',
245
+ 'message': 'KB-First Violation: Code files must include KB citation header. Use kb_code_gen first.'
246
+ }
247
+
248
+ return {'decision': 'approve'}
249
+
250
+ if __name__ == '__main__':
251
+ try:
252
+ input_data = json.loads(sys.stdin.read())
253
+ result = check_kb_first(input_data)
254
+ print(json.dumps(result))
255
+ except Exception as e:
256
+ print(json.dumps({'decision': 'approve', 'error': str(e)}))
257
+ `;
258
+
259
+ // PostToolUse hook
260
+ const postToolUseHook = `#!/usr/bin/env python3
261
+ """
262
+ KB-First PostToolUse Hook
263
+ Tracks KB usage and logs gaps.
264
+ """
265
+
266
+ import json
267
+ import sys
268
+ import os
269
+ from datetime import datetime
270
+
271
+ def track_kb_usage(tool_result):
272
+ """Track KB search results and log gaps."""
273
+ tool_name = tool_result.get('tool_name', '')
274
+
275
+ # Track KB searches
276
+ if 'kb_search' in tool_name or 'kb_code_gen' in tool_name:
277
+ result = tool_result.get('tool_result', {})
278
+ coverage = result.get('kb_coverage', 'unknown')
279
+
280
+ if coverage == 'gap':
281
+ log_gap(tool_result)
282
+
283
+ return {'status': 'ok'}
284
+
285
+ def log_gap(tool_result):
286
+ """Log KB gap for future improvement."""
287
+ gap_log = os.path.join(os.getcwd(), '.ruvector', 'gaps.jsonl')
288
+
289
+ gap_entry = {
290
+ 'timestamp': datetime.now().isoformat(),
291
+ 'query': tool_result.get('tool_input', {}).get('query', ''),
292
+ 'description': tool_result.get('tool_input', {}).get('description', ''),
293
+ 'file_path': tool_result.get('tool_input', {}).get('file_path', '')
294
+ }
295
+
296
+ with open(gap_log, 'a') as f:
297
+ f.write(json.dumps(gap_entry) + '\\n')
298
+
299
+ if __name__ == '__main__':
300
+ try:
301
+ input_data = json.loads(sys.stdin.read())
302
+ result = track_kb_usage(input_data)
303
+ print(json.dumps(result))
304
+ except Exception as e:
305
+ print(json.dumps({'status': 'error', 'error': str(e)}))
306
+ `;
307
+
308
+ writeFileSync(join(hooksDir, 'pre-tool-use.py'), preToolUseHook);
309
+ writeFileSync(join(hooksDir, 'post-tool-use.py'), postToolUseHook);
310
+
311
+ // Make executable
312
+ const { chmodSync } = await import('fs');
313
+ chmodSync(join(hooksDir, 'pre-tool-use.py'), 0o755);
314
+ chmodSync(join(hooksDir, 'post-tool-use.py'), 0o755);
315
+ }
@@ -0,0 +1,372 @@
1
+ /**
2
+ * KB-First Phase Command
3
+ *
4
+ * Runs or shows information about a specific build phase.
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import ora from 'ora';
9
+ import { existsSync, readFileSync, writeFileSync, chmodSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { execFileSync } from 'child_process';
12
+
13
+ const PHASES = {
14
+ 0: {
15
+ name: 'Assessment',
16
+ description: 'Evaluate if KB-First is appropriate for your project',
17
+ subphases: [
18
+ '0.1 Project Analysis',
19
+ '0.2 Domain Complexity',
20
+ '0.3 KB-First Suitability',
21
+ '0.4 Resource Estimation',
22
+ '0.5 Go/No-Go Decision'
23
+ ],
24
+ gate: 'Decision documented and approved'
25
+ },
26
+ 1: {
27
+ name: 'KB Design',
28
+ description: 'Design the knowledge base structure',
29
+ subphases: [
30
+ '1.1 Domain Mapping',
31
+ '1.2 Taxonomy Design',
32
+ '1.3 Relationship Model',
33
+ '1.4 Query Patterns',
34
+ '1.5 Design Review'
35
+ ],
36
+ gate: 'KB schema designed and documented'
37
+ },
38
+ 1.5: {
39
+ name: 'Hooks Setup',
40
+ description: 'Install and configure KB-First enforcement hooks',
41
+ subphases: [
42
+ '1.5.1 Hook Installation',
43
+ '1.5.2 Hook Configuration',
44
+ '1.5.3 Pattern Training',
45
+ '1.5.4 Hook Verification'
46
+ ],
47
+ gate: 'All hooks passing verification',
48
+ script: '1.5-hooks-verify.sh'
49
+ },
50
+ 2: {
51
+ name: 'Schema Definition',
52
+ description: 'Define the database schema for KB storage',
53
+ subphases: [
54
+ '2.1 Table Design',
55
+ '2.2 Vector Columns',
56
+ '2.3 Index Strategy',
57
+ '2.4 Migration Scripts'
58
+ ],
59
+ gate: 'Schema created and migrations applied'
60
+ },
61
+ 3: {
62
+ name: 'KB Population',
63
+ description: 'Populate the knowledge base with content',
64
+ subphases: [
65
+ '3.1 Content Collection',
66
+ '3.2 Content Processing',
67
+ '3.3 Embedding Generation',
68
+ '3.4 Import Validation',
69
+ '3.5 Initial Testing'
70
+ ],
71
+ gate: 'KB populated with initial content'
72
+ },
73
+ 4: {
74
+ name: 'Scoring & Gaps',
75
+ description: 'Score KB quality and identify gaps',
76
+ subphases: [
77
+ '4.1 Coverage Analysis',
78
+ '4.2 Quality Scoring',
79
+ '4.3 Gap Identification',
80
+ '4.4 Priority Ranking',
81
+ '4.5 Remediation Plan'
82
+ ],
83
+ gate: 'KB score >= 80, gaps documented'
84
+ },
85
+ 5: {
86
+ name: 'Integration',
87
+ description: 'Integrate KB into application code',
88
+ subphases: [
89
+ '5.1 Search API',
90
+ '5.2 Code Generation',
91
+ '5.3 Citation System',
92
+ '5.4 Gap Logging'
93
+ ],
94
+ gate: 'KB integrated and accessible'
95
+ },
96
+ 6: {
97
+ name: 'Testing',
98
+ description: 'Test KB functionality and accuracy',
99
+ subphases: [
100
+ '6.1 Unit Tests',
101
+ '6.2 Integration Tests',
102
+ '6.3 Accuracy Tests',
103
+ '6.4 Performance Tests',
104
+ '6.5 Edge Cases'
105
+ ],
106
+ gate: 'All tests passing'
107
+ },
108
+ 7: {
109
+ name: 'Optimization',
110
+ description: 'Optimize KB performance',
111
+ subphases: [
112
+ '7.1 Query Optimization',
113
+ '7.2 Index Tuning',
114
+ '7.3 Caching Strategy',
115
+ '7.4 Benchmark Validation'
116
+ ],
117
+ gate: 'Performance targets met'
118
+ },
119
+ 8: {
120
+ name: 'Verification',
121
+ description: 'Comprehensive KB-First verification',
122
+ subphases: [
123
+ '8.1 Code Scan',
124
+ '8.2 Import Check',
125
+ '8.3 Source Returns',
126
+ '8.4 Startup Verify',
127
+ '8.5 Fallback Check',
128
+ '8.6 Attribution',
129
+ '8.7 Confidence',
130
+ '8.8 Gap Logging'
131
+ ],
132
+ gate: 'All 8 verification scripts pass',
133
+ scripts: [
134
+ '8.1-code-scan.sh',
135
+ '8.2-import-check.sh',
136
+ '8.3-source-returns.sh',
137
+ '8.4-startup-verify.sh',
138
+ '8.5-fallback-check.sh',
139
+ '8.6-attribution.sh',
140
+ '8.7-confidence.sh',
141
+ '8.8-gap-logging.sh'
142
+ ]
143
+ },
144
+ 9: {
145
+ name: 'Security',
146
+ description: 'Security audit and hardening',
147
+ subphases: [
148
+ '9.1 Dependency Audit',
149
+ '9.2 OWASP Top 10',
150
+ '9.3 SQL Injection',
151
+ '9.4 Authentication',
152
+ '9.5 Secrets Management',
153
+ '9.6 API Security'
154
+ ],
155
+ gate: 'Security audit passed',
156
+ script: '9-security-audit.sh'
157
+ },
158
+ 10: {
159
+ name: 'Documentation',
160
+ description: 'Complete project documentation',
161
+ subphases: [
162
+ '10.1 README',
163
+ '10.2 API Documentation',
164
+ '10.3 KB Schema Docs',
165
+ '10.4 Architecture Docs',
166
+ '10.5 Operator Guide',
167
+ '10.6 Versioning'
168
+ ],
169
+ gate: 'All documentation complete'
170
+ },
171
+ 11: {
172
+ name: 'Deployment',
173
+ description: 'Deploy to production',
174
+ subphases: [
175
+ '11.1 Infrastructure',
176
+ '11.2 Environment Config',
177
+ '11.3 CI/CD Pipeline',
178
+ '11.4 Database Migration',
179
+ '11.5 Monitoring',
180
+ '11.6 Go-Live'
181
+ ],
182
+ gate: 'Application deployed and accessible'
183
+ }
184
+ };
185
+
186
+ export async function phaseCommand(phaseNum, options) {
187
+ const cwd = process.cwd();
188
+
189
+ // Parse phase number
190
+ const phase = parseFloat(phaseNum);
191
+ const phaseInfo = PHASES[phase];
192
+
193
+ if (!phaseInfo) {
194
+ console.log(chalk.red(`Unknown phase: ${phaseNum}`));
195
+ console.log(chalk.gray('Valid phases: 0, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11'));
196
+ return;
197
+ }
198
+
199
+ console.log('');
200
+ console.log(chalk.cyan('╔═══════════════════════════════════════════════════════════════╗'));
201
+ console.log(chalk.cyan('║') + chalk.bold.white(` Phase ${phase}: ${phaseInfo.name}`.padEnd(62)) + chalk.cyan('║'));
202
+ console.log(chalk.cyan('╚═══════════════════════════════════════════════════════════════╝'));
203
+ console.log('');
204
+ console.log(chalk.gray(` ${phaseInfo.description}`));
205
+ console.log('');
206
+
207
+ // Show subphases
208
+ console.log(chalk.white(' Sub-phases:'));
209
+ for (const sub of phaseInfo.subphases) {
210
+ console.log(chalk.gray(` • ${sub}`));
211
+ }
212
+ console.log('');
213
+
214
+ // Show quality gate
215
+ console.log(chalk.white(' Quality Gate:'));
216
+ console.log(chalk.yellow(` ${phaseInfo.gate}`));
217
+ console.log('');
218
+
219
+ // Run specific sub-phase if specified
220
+ if (options.sub) {
221
+ await runSubphase(cwd, phase, options.sub);
222
+ return;
223
+ }
224
+
225
+ // Run verification scripts if available
226
+ if (phaseInfo.scripts || phaseInfo.script) {
227
+ const scripts = phaseInfo.scripts || [phaseInfo.script];
228
+ console.log(chalk.white(' Running verification scripts...'));
229
+ console.log('');
230
+
231
+ await runPhaseScripts(cwd, scripts, options.skipGate);
232
+ } else {
233
+ // Show phase documentation
234
+ const phaseDocPath = join(cwd, 'phases', `${String(phase).replace('.', '')}-${phaseInfo.name.toLowerCase().replace(/\s+/g, '-')}.md`);
235
+ const altPhaseDocPath = join(cwd, 'phases', `0${String(phase).replace('.', '')}-${phaseInfo.name.toLowerCase().replace(/\s+/g, '-')}.md`);
236
+
237
+ if (existsSync(phaseDocPath)) {
238
+ console.log(chalk.gray(` Documentation: phases/${phaseDocPath.split('/').pop()}`));
239
+ } else if (existsSync(altPhaseDocPath)) {
240
+ console.log(chalk.gray(` Documentation: phases/${altPhaseDocPath.split('/').pop()}`));
241
+ }
242
+
243
+ console.log('');
244
+ console.log(chalk.white(' Manual Phase'));
245
+ console.log(chalk.gray(' This phase requires manual completion. Review the documentation'));
246
+ console.log(chalk.gray(' and complete all sub-phases before marking as complete.'));
247
+ console.log('');
248
+ console.log(chalk.cyan(' Mark complete: kb-first phase ' + phase + ' --complete'));
249
+ }
250
+
251
+ // Show next step
252
+ console.log('');
253
+ console.log(chalk.gray('─────────────────────────────────────────────────────────────────'));
254
+ const nextPhase = getNextPhase(phase);
255
+ if (nextPhase !== null) {
256
+ console.log(chalk.gray(` Next: kb-first phase ${nextPhase}`));
257
+ } else {
258
+ console.log(chalk.green(' This is the final phase!'));
259
+ }
260
+ }
261
+
262
+ async function runPhaseScripts(cwd, scripts, skipGate) {
263
+ const scriptsDir = join(cwd, 'scripts');
264
+ let passed = 0;
265
+ let failed = 0;
266
+
267
+ for (const script of scripts) {
268
+ const scriptPath = join(scriptsDir, script);
269
+
270
+ if (!existsSync(scriptPath)) {
271
+ console.log(chalk.yellow(` Skip: ${script} (not found)`));
272
+ continue;
273
+ }
274
+
275
+ const spinner = ora(` Running ${script}...`).start();
276
+
277
+ try {
278
+ chmodSync(scriptPath, 0o755);
279
+ execFileSync('/bin/bash', [scriptPath], {
280
+ cwd,
281
+ stdio: 'pipe',
282
+ timeout: 120000,
283
+ encoding: 'utf-8'
284
+ });
285
+
286
+ spinner.succeed(` ${script}`);
287
+ passed++;
288
+ } catch (error) {
289
+ spinner.fail(` ${script}`);
290
+ failed++;
291
+ }
292
+ }
293
+
294
+ console.log('');
295
+
296
+ if (failed === 0) {
297
+ console.log(chalk.green(' ✅ All verification scripts passed'));
298
+
299
+ // Update phase in config
300
+ await markPhaseComplete(cwd);
301
+ } else {
302
+ console.log(chalk.red(` ❌ ${failed} script(s) failed`));
303
+
304
+ if (!skipGate) {
305
+ console.log(chalk.yellow(' Quality gate NOT passed. Fix failures before proceeding.'));
306
+ } else {
307
+ console.log(chalk.yellow(' Warning: --skip-gate used. Proceeding despite failures.'));
308
+ }
309
+ }
310
+ }
311
+
312
+ async function runSubphase(cwd, phase, subNum) {
313
+ const phaseInfo = PHASES[phase];
314
+ const subIndex = parseInt(subNum) - 1;
315
+
316
+ if (subIndex < 0 || subIndex >= phaseInfo.subphases.length) {
317
+ console.log(chalk.red(`Invalid sub-phase: ${subNum}`));
318
+ console.log(chalk.gray(`Valid sub-phases: 1-${phaseInfo.subphases.length}`));
319
+ return;
320
+ }
321
+
322
+ const subphase = phaseInfo.subphases[subIndex];
323
+ console.log(chalk.white(`Running: ${subphase}`));
324
+ console.log('');
325
+
326
+ // Check for specific script
327
+ const scripts = phaseInfo.scripts || (phaseInfo.script ? [phaseInfo.script] : []);
328
+ const matchingScript = scripts.find(s => s.startsWith(`${phase}.${subNum}`));
329
+
330
+ if (matchingScript) {
331
+ await runPhaseScripts(cwd, [matchingScript], false);
332
+ } else {
333
+ console.log(chalk.gray(' No automated script for this sub-phase.'));
334
+ console.log(chalk.gray(' Complete manually and proceed to next sub-phase.'));
335
+ }
336
+ }
337
+
338
+ async function markPhaseComplete(cwd) {
339
+ const configPath = join(cwd, '.ruvector', 'config.json');
340
+
341
+ if (!existsSync(configPath)) return;
342
+
343
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
344
+ const currentPhase = config.phases?.current || 0;
345
+
346
+ if (!config.phases) {
347
+ config.phases = { current: 0, completed: [] };
348
+ }
349
+
350
+ if (!config.phases.completed.includes(currentPhase)) {
351
+ config.phases.completed.push(currentPhase);
352
+ }
353
+
354
+ // Move to next phase
355
+ const nextPhase = getNextPhase(currentPhase);
356
+ if (nextPhase !== null) {
357
+ config.phases.current = nextPhase;
358
+ }
359
+
360
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
361
+ }
362
+
363
+ function getNextPhase(current) {
364
+ const phases = [0, 1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
365
+ const currentIndex = phases.indexOf(current);
366
+
367
+ if (currentIndex === -1 || currentIndex === phases.length - 1) {
368
+ return null;
369
+ }
370
+
371
+ return phases[currentIndex + 1];
372
+ }