codeflow-hook 1.4.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -399
- package/bin/codeflow-hook.js +183 -3
- package/lib/cli-integration/dist/index.d.ts +128 -0
- package/lib/cli-integration/dist/index.js +585 -0
- package/lib/cli-integration/dist/pipelineConfigs.d.ts +60 -0
- package/lib/cli-integration/dist/pipelineConfigs.js +549 -0
- package/lib/cli-integration/dist/simulationEngine.d.ts +86 -0
- package/lib/cli-integration/dist/simulationEngine.js +475 -0
- package/lib/cli-integration/dist/types.d.ts +156 -0
- package/lib/cli-integration/dist/types.js +15 -0
- package/lib/cli-integration/src/index.ts +748 -0
- package/lib/cli-integration/src/pipelineConfigs.ts +579 -0
- package/lib/cli-integration/src/simulationEngine.ts +622 -0
- package/lib/cli-integration/src/types.ts +175 -0
- package/package.json +13 -5
package/bin/codeflow-hook.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
@@ -8,11 +8,10 @@ import fs from 'fs';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import os from 'os'; // Make sure os is imported
|
|
10
10
|
import readline from 'readline';
|
|
11
|
-
import { indexProject } from './rag.js';
|
|
12
11
|
import { orchestrateReview } from './agents.js';
|
|
13
12
|
|
|
14
13
|
// Import CLI integration service
|
|
15
|
-
import { indexProject, analyzeDiff } from '../
|
|
14
|
+
import { indexProject, analyzeDiff } from '../lib/cli-integration/dist/index.js';
|
|
16
15
|
|
|
17
16
|
// Export for use in agents module
|
|
18
17
|
export { callAIProvider };
|
|
@@ -344,6 +343,92 @@ program
|
|
|
344
343
|
}
|
|
345
344
|
});
|
|
346
345
|
|
|
346
|
+
// Run pipeline simulation (Enhanced Frontend Integration)
|
|
347
|
+
program
|
|
348
|
+
.command('simulate')
|
|
349
|
+
.description('Run configurable CI/CD pipeline simulation')
|
|
350
|
+
.argument('[template]', 'Pipeline template ID (nodejs-basic, enterprise-advanced, fast-dev, chaotic-test, microservices-parallel)')
|
|
351
|
+
.option('-c, --config <file>', 'Custom pipeline configuration JSON file')
|
|
352
|
+
.option('-o, --output <file>', 'Output results to JSON file')
|
|
353
|
+
.option('-m, --mode <mode>', 'Simulation mode (REALISTIC, FAST, DETERMINISTIC, CHAOTIC)', 'REALISTIC')
|
|
354
|
+
.option('--commit-message <message>', 'Custom commit message for simulation')
|
|
355
|
+
.option('--json', 'Output results as JSON only')
|
|
356
|
+
.action(async (template, options) => {
|
|
357
|
+
try {
|
|
358
|
+
let pipelineConfig;
|
|
359
|
+
|
|
360
|
+
// Load configuration from file or template
|
|
361
|
+
if (options.config) {
|
|
362
|
+
console.log(chalk.blue(`📄 Loading pipeline config from: ${options.config}`));
|
|
363
|
+
const fs = await import('fs/promises');
|
|
364
|
+
const configData = await fs.readFile(options.config, 'utf8');
|
|
365
|
+
pipelineConfig = JSON.parse(configData);
|
|
366
|
+
} else {
|
|
367
|
+
const templateId = template || 'nodejs-basic';
|
|
368
|
+
console.log(chalk.blue(`🎯 Using pipeline template: ${templateId}`));
|
|
369
|
+
|
|
370
|
+
// Import simulation engine and config manager dynamically
|
|
371
|
+
const { PipelineConfigManager } = await import('../lib/cli-integration/dist/pipelineConfigs.js');
|
|
372
|
+
pipelineConfig = PipelineConfigManager.getPipelineById(templateId);
|
|
373
|
+
|
|
374
|
+
if (!pipelineConfig) {
|
|
375
|
+
console.log(chalk.red(`❌ Unknown pipeline template: ${templateId}`));
|
|
376
|
+
console.log(chalk.gray('Available templates:'));
|
|
377
|
+
const templates = PipelineConfigManager.getAvailableTemplates();
|
|
378
|
+
templates.forEach(t => {
|
|
379
|
+
console.log(chalk.gray(` • ${t.id}: ${t.description}`));
|
|
380
|
+
});
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Apply customizations
|
|
386
|
+
if (options.mode) {
|
|
387
|
+
pipelineConfig.settings.mode = options.mode.toUpperCase();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (options.commitMessage) {
|
|
391
|
+
pipelineConfig.environment = {
|
|
392
|
+
...pipelineConfig.environment,
|
|
393
|
+
COMMIT_MESSAGE: options.commitMessage
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
console.log(chalk.blue(`🚀 Starting pipeline simulation: ${pipelineConfig.name}`));
|
|
398
|
+
console.log(chalk.gray(` Mode: ${pipelineConfig.settings.mode}`));
|
|
399
|
+
console.log(chalk.gray(` Stages: ${pipelineConfig.stages.length}`));
|
|
400
|
+
|
|
401
|
+
const spinner = ora('Running pipeline simulation...').start();
|
|
402
|
+
|
|
403
|
+
// Import and run simulation engine
|
|
404
|
+
const { simulationEngine } = await import('../lib/cli-integration/dist/simulationEngine.js');
|
|
405
|
+
const result = await simulationEngine.executePipeline(pipelineConfig);
|
|
406
|
+
|
|
407
|
+
spinner.succeed('Simulation completed');
|
|
408
|
+
|
|
409
|
+
// Display results
|
|
410
|
+
if (options.json) {
|
|
411
|
+
console.log(JSON.stringify(result, null, 2));
|
|
412
|
+
} else {
|
|
413
|
+
displaySimulationResults(result);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Save to file if requested
|
|
417
|
+
if (options.output) {
|
|
418
|
+
const fs = await import('fs/promises');
|
|
419
|
+
await fs.writeFile(options.output, JSON.stringify(result, null, 2), 'utf8');
|
|
420
|
+
console.log(chalk.green(`💾 Results saved to: ${options.output}`));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Exit with appropriate code
|
|
424
|
+
process.exit(result.status === 'success' ? 0 : 1);
|
|
425
|
+
|
|
426
|
+
} catch (error) {
|
|
427
|
+
console.log(chalk.red(`❌ Simulation failed: ${error.message}`));
|
|
428
|
+
process.exit(1);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
347
432
|
// Show status
|
|
348
433
|
program
|
|
349
434
|
.command('status')
|
|
@@ -763,5 +848,100 @@ function displayEKGAnalysisResults(analysis) {
|
|
|
763
848
|
}
|
|
764
849
|
}
|
|
765
850
|
|
|
851
|
+
// Display pipeline simulation results
|
|
852
|
+
function displaySimulationResults(result) {
|
|
853
|
+
if (!result) {
|
|
854
|
+
console.log(chalk.yellow('⚠️ No simulation results available'));
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// Overall status
|
|
859
|
+
const statusIcon = result.status === 'success' ? '✅' :
|
|
860
|
+
result.status === 'failed' ? '❌' :
|
|
861
|
+
result.status === 'partial' ? '⚠️' : '⏸️';
|
|
862
|
+
const statusColor = result.status === 'success' ? chalk.green :
|
|
863
|
+
result.status === 'failed' ? chalk.red :
|
|
864
|
+
result.status === 'partial' ? chalk.yellow : chalk.gray;
|
|
865
|
+
|
|
866
|
+
console.log(statusColor(`${statusIcon} Pipeline ${result.status.toUpperCase()}`));
|
|
867
|
+
console.log(chalk.blue(`📋 Execution ID: ${result.executionId}`));
|
|
868
|
+
console.log(chalk.gray(`⏱️ Duration: ${(result.metrics.totalDuration / 1000).toFixed(2)}s`));
|
|
869
|
+
console.log();
|
|
870
|
+
|
|
871
|
+
// Pipeline metrics
|
|
872
|
+
console.log(chalk.blue('📊 Pipeline Metrics:'));
|
|
873
|
+
console.log(` 📊 Stages: ${result.metrics.successCount}/${result.metrics.stageCount} passed`);
|
|
874
|
+
if (result.metrics.failureCount > 0) {
|
|
875
|
+
console.log(chalk.red(` ❌ Failed: ${result.metrics.failureCount}`));
|
|
876
|
+
}
|
|
877
|
+
if (result.metrics.skippedCount > 0) {
|
|
878
|
+
console.log(chalk.yellow(` ⏭️ Skipped: ${result.metrics.skippedCount}`));
|
|
879
|
+
}
|
|
880
|
+
console.log(` 📈 Avg Stage Duration: ${(result.metrics.averageStageDuration / 1000).toFixed(2)}s`);
|
|
881
|
+
if (result.metrics.bottleneckStage) {
|
|
882
|
+
console.log(chalk.yellow(` 🐌 Bottleneck: ${result.metrics.bottleneckStage}`));
|
|
883
|
+
}
|
|
884
|
+
console.log();
|
|
885
|
+
|
|
886
|
+
// Resource utilization
|
|
887
|
+
console.log(chalk.blue('💻 Resource Utilization:'));
|
|
888
|
+
console.log(` 🖥️ Avg CPU: ${result.metrics.resourceUtilization.avgCpu}%`);
|
|
889
|
+
console.log(` 🧠 Avg Memory: ${result.metrics.resourceUtilization.avgMemory}MB`);
|
|
890
|
+
console.log(` 🔥 Peak CPU: ${result.metrics.resourceUtilization.peakCpu}%`);
|
|
891
|
+
console.log(` 💾 Peak Memory: ${result.metrics.resourceUtilization.peakMemory}MB`);
|
|
892
|
+
console.log();
|
|
893
|
+
|
|
894
|
+
// Stage details
|
|
895
|
+
console.log(chalk.blue('🔧 Stage Results:'));
|
|
896
|
+
result.stages.forEach(stage => {
|
|
897
|
+
const stageIcon = stage.status === 'SUCCESS' ? '✅' :
|
|
898
|
+
stage.status === 'FAILED' ? '❌' :
|
|
899
|
+
stage.status === 'SKIPPED' ? '⏭️' : '⏳';
|
|
900
|
+
const stageColor = stage.status === 'SUCCESS' ? chalk.green :
|
|
901
|
+
stage.status === 'FAILED' ? chalk.red :
|
|
902
|
+
stage.status === 'SKIPPED' ? chalk.yellow : chalk.gray;
|
|
903
|
+
|
|
904
|
+
const duration = stage.duration ? `${(stage.duration / 1000).toFixed(2)}s` : 'N/A';
|
|
905
|
+
console.log(`${stageColor(` ${stageIcon} ${stage.id}: ${duration}`)}`);
|
|
906
|
+
|
|
907
|
+
// Show metrics if available
|
|
908
|
+
if (stage.metrics) {
|
|
909
|
+
console.log(chalk.gray(` CPU: ${stage.metrics.cpuUsage}%, Mem: ${stage.metrics.memoryUsage}MB`));
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Show errors if any
|
|
913
|
+
if (stage.errors && stage.errors.length > 0) {
|
|
914
|
+
stage.errors.forEach(error => {
|
|
915
|
+
console.log(chalk.red(` 💥 ${error.message}`));
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
console.log();
|
|
920
|
+
|
|
921
|
+
// Artifacts
|
|
922
|
+
if (result.artifacts && result.artifacts.length > 0) {
|
|
923
|
+
console.log(chalk.blue('📦 Generated Artifacts:'));
|
|
924
|
+
result.artifacts.forEach(artifact => {
|
|
925
|
+
console.log(` 📄 ${artifact.name} (${(artifact.size / 1024 / 1024).toFixed(2)}MB)`);
|
|
926
|
+
console.log(chalk.gray(` ${artifact.path}`));
|
|
927
|
+
});
|
|
928
|
+
console.log();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// Execution logs summary
|
|
932
|
+
if (result.logs && result.logs.length > 0) {
|
|
933
|
+
console.log(chalk.blue('📝 Execution Summary:'));
|
|
934
|
+
// Show last few log lines
|
|
935
|
+
const recentLogs = result.logs.slice(-5);
|
|
936
|
+
recentLogs.forEach(log => {
|
|
937
|
+
console.log(chalk.gray(` ${log}`));
|
|
938
|
+
});
|
|
939
|
+
if (result.logs.length > 5) {
|
|
940
|
+
console.log(chalk.gray(` ... and ${result.logs.length - 5} more log entries`));
|
|
941
|
+
}
|
|
942
|
+
console.log();
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
766
946
|
// Make sure the final line uses parseAsync
|
|
767
947
|
program.parseAsync(process.argv);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI Integration Service - Phase 4
|
|
4
|
+
*
|
|
5
|
+
* Bridges the local CLI commands with EKG backend services.
|
|
6
|
+
* Transforms CLI operations from local processing to backend-driven workflows.
|
|
7
|
+
*
|
|
8
|
+
* Key transformations:
|
|
9
|
+
* - `codeflow index` → EKG Ingestion Service webhook simulation
|
|
10
|
+
* - `codeflow analyze-diff` → EKG Query Service context-enhanced analysis
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* CLI Integration Service
|
|
14
|
+
* Provides methods that CLI commands can call to interact with EKG backend
|
|
15
|
+
*/
|
|
16
|
+
export declare class CLIIntegrationService {
|
|
17
|
+
private config;
|
|
18
|
+
private git;
|
|
19
|
+
constructor();
|
|
20
|
+
/**
|
|
21
|
+
* Index repository for EKG - equivalent to `codeflow index`
|
|
22
|
+
*
|
|
23
|
+
* Sends repository URL to EKG Ingestion Service for analysis and graph population
|
|
24
|
+
*/
|
|
25
|
+
indexRepository(options?: {
|
|
26
|
+
repositoryUrl?: string;
|
|
27
|
+
dryRun?: boolean;
|
|
28
|
+
}): Promise<{
|
|
29
|
+
success: boolean;
|
|
30
|
+
repositoryId?: string;
|
|
31
|
+
message: string;
|
|
32
|
+
stats?: {
|
|
33
|
+
indexedFiles: number;
|
|
34
|
+
analysisTime: number;
|
|
35
|
+
webhookAccepted: boolean;
|
|
36
|
+
};
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Analyze code diff with EKG context enhancement
|
|
40
|
+
*
|
|
41
|
+
* Sends diff to Query Service for EKG-enhanced analysis instead of local RAG
|
|
42
|
+
*/
|
|
43
|
+
analyzeDiff(diffContent: string, options?: {
|
|
44
|
+
legacy?: boolean;
|
|
45
|
+
outputFormat?: 'console' | 'json';
|
|
46
|
+
}): Promise<{
|
|
47
|
+
success: boolean;
|
|
48
|
+
analysis: any;
|
|
49
|
+
message: string;
|
|
50
|
+
stats?: {
|
|
51
|
+
ekg_queries: number;
|
|
52
|
+
similar_repos_found: number;
|
|
53
|
+
analysis_time: number;
|
|
54
|
+
};
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Analyze diff content and extract structured information
|
|
58
|
+
*/
|
|
59
|
+
private analyzeDiffContent;
|
|
60
|
+
/**
|
|
61
|
+
* Query EKG for context on affected files
|
|
62
|
+
*/
|
|
63
|
+
private getEKGContext;
|
|
64
|
+
/**
|
|
65
|
+
* Generate enhanced analysis using EKG context
|
|
66
|
+
*/
|
|
67
|
+
private generateEKGEnhancedAnalysis;
|
|
68
|
+
/**
|
|
69
|
+
* Get current repository information
|
|
70
|
+
*/
|
|
71
|
+
private getRepositoryInfo;
|
|
72
|
+
/**
|
|
73
|
+
* Get list of files that would be indexed
|
|
74
|
+
*/
|
|
75
|
+
private getIndexableFiles;
|
|
76
|
+
/**
|
|
77
|
+
* Make HTTP request to backend service with retry logic
|
|
78
|
+
*/
|
|
79
|
+
private makeBackendRequest;
|
|
80
|
+
/**
|
|
81
|
+
* Make GraphQL request to Query Service
|
|
82
|
+
*/
|
|
83
|
+
private makeGraphQLRequest;
|
|
84
|
+
/**
|
|
85
|
+
* Generate repository ID (similar to ingestion service)
|
|
86
|
+
*/
|
|
87
|
+
private generateRepositoryId;
|
|
88
|
+
/**
|
|
89
|
+
* Get current user information
|
|
90
|
+
*/
|
|
91
|
+
private getCurrentUser;
|
|
92
|
+
/**
|
|
93
|
+
* Detect language from file extension
|
|
94
|
+
*/
|
|
95
|
+
private detectLanguage;
|
|
96
|
+
/**
|
|
97
|
+
* Format error for logging and display
|
|
98
|
+
*/
|
|
99
|
+
private formatError;
|
|
100
|
+
}
|
|
101
|
+
export declare const cliIntegrationService: CLIIntegrationService;
|
|
102
|
+
export declare const indexProject: (options?: {
|
|
103
|
+
repositoryUrl?: string;
|
|
104
|
+
dryRun?: boolean;
|
|
105
|
+
}) => Promise<{
|
|
106
|
+
success: boolean;
|
|
107
|
+
repositoryId?: string;
|
|
108
|
+
message: string;
|
|
109
|
+
stats?: {
|
|
110
|
+
indexedFiles: number;
|
|
111
|
+
analysisTime: number;
|
|
112
|
+
webhookAccepted: boolean;
|
|
113
|
+
};
|
|
114
|
+
}>;
|
|
115
|
+
export declare const analyzeDiff: (diffContent: string, options?: {
|
|
116
|
+
legacy?: boolean;
|
|
117
|
+
outputFormat?: 'console' | 'json';
|
|
118
|
+
}) => Promise<{
|
|
119
|
+
success: boolean;
|
|
120
|
+
analysis: any;
|
|
121
|
+
message: string;
|
|
122
|
+
stats?: {
|
|
123
|
+
ekg_queries: number;
|
|
124
|
+
similar_repos_found: number;
|
|
125
|
+
analysis_time: number;
|
|
126
|
+
};
|
|
127
|
+
}>;
|
|
128
|
+
export default cliIntegrationService;
|