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.
- package/LICENSE +21 -0
- package/README.md +674 -0
- package/SKILL.md +740 -0
- package/bin/kb-first.js +123 -0
- package/install/init-project.sh +435 -0
- package/install/install-global.sh +257 -0
- package/install/kb-first-autodetect.sh +108 -0
- package/install/kb-first-command.md +80 -0
- package/install/kb-first-skill.md +262 -0
- package/package.json +87 -0
- package/phases/00-assessment.md +529 -0
- package/phases/01-storage.md +194 -0
- package/phases/01.5-hooks-setup.md +521 -0
- package/phases/02-kb-creation.md +413 -0
- package/phases/03-persistence.md +125 -0
- package/phases/04-visualization.md +170 -0
- package/phases/05-integration.md +114 -0
- package/phases/06-scaffold.md +130 -0
- package/phases/07-build.md +493 -0
- package/phases/08-verification.md +597 -0
- package/phases/09-security.md +512 -0
- package/phases/10-documentation.md +613 -0
- package/phases/11-deployment.md +670 -0
- package/phases/testing.md +713 -0
- package/scripts/1.5-hooks-verify.sh +252 -0
- package/scripts/8.1-code-scan.sh +58 -0
- package/scripts/8.2-import-check.sh +42 -0
- package/scripts/8.3-source-returns.sh +52 -0
- package/scripts/8.4-startup-verify.sh +65 -0
- package/scripts/8.5-fallback-check.sh +63 -0
- package/scripts/8.6-attribution.sh +56 -0
- package/scripts/8.7-confidence.sh +56 -0
- package/scripts/8.8-gap-logging.sh +70 -0
- package/scripts/9-security-audit.sh +202 -0
- package/scripts/init-project.sh +395 -0
- package/scripts/verify-enforcement.sh +167 -0
- package/src/commands/hooks.js +361 -0
- package/src/commands/init.js +315 -0
- package/src/commands/phase.js +372 -0
- package/src/commands/score.js +380 -0
- package/src/commands/status.js +193 -0
- package/src/commands/verify.js +286 -0
- package/src/index.js +56 -0
- package/src/mcp-server.js +412 -0
- package/templates/attention-router.ts +534 -0
- package/templates/code-analysis.ts +683 -0
- package/templates/federated-kb-learner.ts +649 -0
- package/templates/gnn-engine.ts +1091 -0
- package/templates/intentions.md +277 -0
- package/templates/kb-client.ts +905 -0
- package/templates/schema.sql +303 -0
- 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
|
+
}
|