edsger 0.4.8 → 0.4.10
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/dist/cli/index.js +2 -30
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +77 -4
- package/dist/commands/code-review/index.d.ts +2 -0
- package/dist/commands/code-review/index.js +39 -0
- package/dist/commands/code-review/reviewer.d.ts +3 -0
- package/dist/commands/code-review/reviewer.js +192 -0
- package/dist/commands/refactor/refactor.d.ts +2 -0
- package/dist/commands/refactor/refactor.js +123 -0
- package/dist/commands/workflow/index.d.ts +2 -0
- package/dist/commands/workflow/index.js +34 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/phases/code-refine-verification/verifier.js +102 -19
- package/dist/types/index.d.ts +0 -4
- package/dist/utils/validation.d.ts +25 -0
- package/dist/utils/validation.js +58 -0
- package/dist/utils/workflow-utils.d.ts +21 -0
- package/dist/utils/workflow-utils.js +47 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -6,12 +6,8 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import { config as dotenvConfig } from 'dotenv';
|
|
7
7
|
import { logError } from '../utils/logger.js';
|
|
8
8
|
import { runWorkflow } from './commands/workflow-command.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { runCodeImplementation } from './commands/code-implementation-command.js';
|
|
12
|
-
import { runFunctionalTestingCommand } from './commands/functional-testing-command.js';
|
|
13
|
-
import { runCodeReview } from './commands/code-review-command.js';
|
|
14
|
-
import { runRefactor } from './commands/refactor-command.js';
|
|
9
|
+
import { runCodeReview } from '../commands/code-review/index.js';
|
|
10
|
+
import { runRefactor } from '../commands/refactor/refactor.js';
|
|
15
11
|
// Get package.json version dynamically
|
|
16
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
17
13
|
const __dirname = dirname(__filename);
|
|
@@ -30,10 +26,6 @@ program
|
|
|
30
26
|
.option('-s, --staged', 'Review only staged changes')
|
|
31
27
|
.option('-f, --files <patterns...>', 'Review specific file patterns')
|
|
32
28
|
.option('--refactor', 'Refactor code in current directory')
|
|
33
|
-
.option('--feature-analysis <featureId>', 'Analyze feature and generate user stories/test cases')
|
|
34
|
-
.option('--technical-design <featureId>', 'Generate technical design for a feature')
|
|
35
|
-
.option('--implement <featureId>', 'Implement code for a feature based on its specifications')
|
|
36
|
-
.option('--test <featureId>', 'Run functional tests for a feature using Playwright')
|
|
37
29
|
.option('--workflow', 'Run continuous workflow processor to handle ready_for_dev features')
|
|
38
30
|
.option('-c, --config <path>', 'Path to config file')
|
|
39
31
|
.option('-v, --verbose', 'Verbose output');
|
|
@@ -52,26 +44,6 @@ export const runEdsger = async (options) => {
|
|
|
52
44
|
await runWorkflow(options);
|
|
53
45
|
return;
|
|
54
46
|
}
|
|
55
|
-
// Handle feature analysis mode
|
|
56
|
-
if (options.featureAnalysis) {
|
|
57
|
-
await runFeatureAnalysis(options);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
// Handle technical design mode
|
|
61
|
-
if (options.technicalDesign) {
|
|
62
|
-
await runTechnicalDesign(options);
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
// Handle code implementation mode
|
|
66
|
-
if (options.implement) {
|
|
67
|
-
await runCodeImplementation(options);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Handle functional testing mode
|
|
71
|
-
if (options.test) {
|
|
72
|
-
await runFunctionalTestingCommand(options);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
47
|
// Handle refactor mode
|
|
76
48
|
if (options.refactor) {
|
|
77
49
|
await runRefactor(options);
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { CliOptions } from './types/index.js';
|
|
3
|
+
export declare const runEdsger: (options: CliOptions) => Promise<void>;
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import '
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { readFileSync } from 'fs';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { config as dotenvConfig } from 'dotenv';
|
|
7
|
+
import { logError } from './utils/logger.js';
|
|
8
|
+
import { runWorkflow } from './commands/workflow/index.js';
|
|
9
|
+
import { runCodeReview } from './commands/code-review/index.js';
|
|
10
|
+
import { runRefactor } from './commands/refactor/refactor.js';
|
|
11
|
+
// Get package.json version dynamically
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));
|
|
15
|
+
const version = packageJson.version;
|
|
16
|
+
// Load environment variables from .env file
|
|
17
|
+
// Load from current working directory (where the command is run)
|
|
18
|
+
dotenvConfig({ path: join(process.cwd(), '.env') });
|
|
19
|
+
const program = new Command();
|
|
20
|
+
program
|
|
21
|
+
.name('edsger')
|
|
22
|
+
.description('AI-powered code review CLI tool using Claude Code SDK')
|
|
23
|
+
.version(version);
|
|
24
|
+
program
|
|
25
|
+
.option('-r, --review', 'Review code changes')
|
|
26
|
+
.option('-s, --staged', 'Review only staged changes')
|
|
27
|
+
.option('-f, --files <patterns...>', 'Review specific file patterns')
|
|
28
|
+
.option('--refactor', 'Refactor code in current directory')
|
|
29
|
+
.option('--workflow', 'Run continuous workflow processor to handle ready_for_dev features')
|
|
30
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
31
|
+
.option('-v, --verbose', 'Verbose output');
|
|
32
|
+
program.action(async (options) => {
|
|
33
|
+
try {
|
|
34
|
+
await runEdsger(options);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
export const runEdsger = async (options) => {
|
|
42
|
+
// Handle workflow mode
|
|
43
|
+
if (options.workflow) {
|
|
44
|
+
await runWorkflow(options);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Handle refactor mode
|
|
48
|
+
if (options.refactor) {
|
|
49
|
+
await runRefactor(options);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Handle code review mode (explicit --review flag or as fallback)
|
|
53
|
+
if (options.review || options.staged || options.files) {
|
|
54
|
+
await runCodeReview(options);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Default to workflow mode if environment is configured, otherwise code review
|
|
58
|
+
const productId = process.env.EDSGER_PRODUCT_ID;
|
|
59
|
+
const mcpToken = process.env.EDSGER_MCP_TOKEN;
|
|
60
|
+
if (productId && mcpToken) {
|
|
61
|
+
// Environment is configured for workflow, use that as default
|
|
62
|
+
options.workflow = true;
|
|
63
|
+
await runWorkflow(options);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Default to code review mode
|
|
67
|
+
await runCodeReview(options);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
// Only parse when this file is run directly (not imported as module)
|
|
71
|
+
// Check if this is the main module being executed
|
|
72
|
+
const isMainModule = import.meta.url === `file://${process.argv[1]}` ||
|
|
73
|
+
process.argv[1]?.endsWith('/edsger') ||
|
|
74
|
+
process.argv[1]?.endsWith('\\edsger') ||
|
|
75
|
+
process.argv[1]?.endsWith('cli.js');
|
|
76
|
+
if (isMainModule) {
|
|
77
|
+
program.parse();
|
|
78
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { reviewWithClaudeSDK, checkClaudeCodeSDK } from './reviewer.js';
|
|
2
|
+
import { logInfo, logError, logSuccess, logResults, } from '../../utils/logger.js';
|
|
3
|
+
import { validateConfiguration, validateRequirements, } from '../../utils/validation.js';
|
|
4
|
+
export const runCodeReview = async (options) => {
|
|
5
|
+
// Check Claude Code SDK availability
|
|
6
|
+
await validateRequirements(checkClaudeCodeSDK, 'Claude Code SDK not found. Install with: npm install @anthropic-ai/claude-code');
|
|
7
|
+
// Load and validate configuration
|
|
8
|
+
const config = validateConfiguration(options);
|
|
9
|
+
if (options.verbose) {
|
|
10
|
+
logInfo(`Using Claude Code SDK for ${options.staged ? 'staged' : 'all'} changes`);
|
|
11
|
+
}
|
|
12
|
+
logInfo('Starting Claude Code review...');
|
|
13
|
+
try {
|
|
14
|
+
// Use Claude Code SDK to review changes
|
|
15
|
+
const results = await reviewWithClaudeSDK(config, options.staged || false, options.files);
|
|
16
|
+
// Display results
|
|
17
|
+
logResults(results, options.verbose);
|
|
18
|
+
// Determine exit code
|
|
19
|
+
const hasBlockingIssues = results.some((r) => r.status === 'BLOCK');
|
|
20
|
+
const hasWarnings = results.some((r) => r.status === 'WARN');
|
|
21
|
+
const exitCode = hasBlockingIssues || (hasWarnings && config.severity === 'error') ? 1 : 0;
|
|
22
|
+
if (exitCode === 0) {
|
|
23
|
+
logSuccess('Review completed successfully');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
if (hasBlockingIssues) {
|
|
27
|
+
logError('Review failed with blocking issues');
|
|
28
|
+
}
|
|
29
|
+
else if (hasWarnings) {
|
|
30
|
+
logError('Review failed with warnings');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
process.exit(exitCode);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
logError(`Review failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { query } from '@anthropic-ai/claude-code';
|
|
2
|
+
export const reviewWithClaudeSDK = async (config, staged = false, filePatterns) => {
|
|
3
|
+
const systemPrompt = createSystemPrompt(config);
|
|
4
|
+
const reviewPrompt = createReviewPrompt(staged, filePatterns);
|
|
5
|
+
try {
|
|
6
|
+
const results = [];
|
|
7
|
+
for await (const message of query({
|
|
8
|
+
prompt: reviewPrompt,
|
|
9
|
+
options: {
|
|
10
|
+
appendSystemPrompt: systemPrompt,
|
|
11
|
+
model: config.claude.model || 'sonnet',
|
|
12
|
+
},
|
|
13
|
+
})) {
|
|
14
|
+
// Stream the review process
|
|
15
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
16
|
+
for (const content of message.message.content) {
|
|
17
|
+
if (content.type === 'text') {
|
|
18
|
+
console.log(`\n🤖 ${content.text}`);
|
|
19
|
+
}
|
|
20
|
+
else if (content.type === 'tool_use') {
|
|
21
|
+
console.log(`\n🔧 ${content.name}: ${content.input.description || content.input.command || 'Running...'}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (message.type === 'result') {
|
|
26
|
+
if (message.subtype === 'success') {
|
|
27
|
+
console.log('\n📊 Review completed, parsing results...');
|
|
28
|
+
const parsedResults = parseSDKResponse(message.result);
|
|
29
|
+
results.push(...parsedResults);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log(`\n⚠️ Review incomplete: ${message.subtype}`);
|
|
33
|
+
if (message.subtype === 'error_max_turns') {
|
|
34
|
+
console.log('💡 Try reducing maxFiles in config or increase timeout');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return results.length > 0
|
|
40
|
+
? results
|
|
41
|
+
: [
|
|
42
|
+
{
|
|
43
|
+
file: 'No Changes',
|
|
44
|
+
status: 'OK',
|
|
45
|
+
message: 'No files to review or no changes detected',
|
|
46
|
+
details: undefined,
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new Error(`Claude Code SDK review failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const createSystemPrompt = (config) => {
|
|
55
|
+
return `You are an expert code reviewer using Claude Code. Your task is to review git repository changes.
|
|
56
|
+
|
|
57
|
+
IMPORTANT INSTRUCTIONS:
|
|
58
|
+
1. Use the Bash tool to check git status and find changed files
|
|
59
|
+
2. Use the Read tool to examine file contents
|
|
60
|
+
3. Use the Grep tool to search for patterns or related code
|
|
61
|
+
4. Analyze code quality, bugs, security issues, and best practices
|
|
62
|
+
|
|
63
|
+
REVIEW CRITERIA:
|
|
64
|
+
- Severity level: ${config.severity}
|
|
65
|
+
- Max files to review: ${config.maxFiles}
|
|
66
|
+
- Include patterns: ${config.patterns.join(', ')}
|
|
67
|
+
- Exclude patterns: ${config.exclude.join(', ')}
|
|
68
|
+
|
|
69
|
+
RESPONSE FORMAT:
|
|
70
|
+
You MUST end your response with a JSON object containing the review results. Use this EXACT format:
|
|
71
|
+
|
|
72
|
+
{
|
|
73
|
+
"reviews": [
|
|
74
|
+
{
|
|
75
|
+
"file": "path/to/file.js",
|
|
76
|
+
"status": "OK|WARN|BLOCK",
|
|
77
|
+
"message": "Brief description",
|
|
78
|
+
"details": "Detailed explanation if needed"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
STATUS GUIDELINES:
|
|
84
|
+
- OK: Code looks good, no issues
|
|
85
|
+
- WARN: Minor issues, suggestions for improvement
|
|
86
|
+
- BLOCK: Serious issues that should prevent commit (use only when severity is "error")
|
|
87
|
+
|
|
88
|
+
IMPORTANT: After your analysis, you MUST provide the JSON results at the end of your response. Start by examining the repository state and then review the appropriate files.`;
|
|
89
|
+
};
|
|
90
|
+
const createReviewPrompt = (staged, filePatterns) => {
|
|
91
|
+
const changeType = staged ? 'staged changes' : 'uncommitted changes';
|
|
92
|
+
const patternsText = filePatterns?.length
|
|
93
|
+
? ` matching patterns: ${filePatterns.join(', ')}`
|
|
94
|
+
: '';
|
|
95
|
+
const gitCommand = staged
|
|
96
|
+
? 'git diff --cached --name-only'
|
|
97
|
+
: 'git status --porcelain';
|
|
98
|
+
return `Please review the ${changeType} in this git repository${patternsText}.
|
|
99
|
+
|
|
100
|
+
IMPORTANT: Start by running "${gitCommand}" to see what files have changed. Then read and analyze each changed file.
|
|
101
|
+
|
|
102
|
+
Steps to follow:
|
|
103
|
+
1. Run "${gitCommand}" to identify changed files
|
|
104
|
+
2. For each changed file, use the Read tool to examine its contents
|
|
105
|
+
3. Analyze code quality, bugs, security issues, and best practices
|
|
106
|
+
4. Provide structured feedback using the required format
|
|
107
|
+
|
|
108
|
+
If you find files that have changed, review them. If no files are found, clearly state "No changes detected".
|
|
109
|
+
|
|
110
|
+
Focus on:
|
|
111
|
+
- Code quality and maintainability
|
|
112
|
+
- Potential bugs or security vulnerabilities
|
|
113
|
+
- Performance issues
|
|
114
|
+
- Adherence to best practices
|
|
115
|
+
- Breaking changes
|
|
116
|
+
- Missing error handling
|
|
117
|
+
|
|
118
|
+
Please start by running the git command to check for changes.`;
|
|
119
|
+
};
|
|
120
|
+
const parseSDKResponse = (response) => {
|
|
121
|
+
console.log('Parsing JSON response...');
|
|
122
|
+
try {
|
|
123
|
+
// Try to find JSON in the response
|
|
124
|
+
const jsonMatch = response.match(/```json\s*([\s\S]*?)\s*```/) ||
|
|
125
|
+
response.match(/\{[\s\S]*"reviews"[\s\S]*\}/);
|
|
126
|
+
if (jsonMatch) {
|
|
127
|
+
const jsonStr = jsonMatch[1] || jsonMatch[0];
|
|
128
|
+
console.log('Found JSON:', jsonStr.substring(0, 200) + '...');
|
|
129
|
+
const parsed = JSON.parse(jsonStr);
|
|
130
|
+
if (parsed.reviews && Array.isArray(parsed.reviews)) {
|
|
131
|
+
const results = parsed.reviews.map((review) => ({
|
|
132
|
+
file: review.file || 'Unknown file',
|
|
133
|
+
status: review.status || 'OK',
|
|
134
|
+
message: review.message || 'No message provided',
|
|
135
|
+
details: review.details || undefined,
|
|
136
|
+
}));
|
|
137
|
+
console.log(`Parsed ${results.length} results from JSON`);
|
|
138
|
+
results.forEach((r) => console.log(`- ${r.file}: ${r.status}`));
|
|
139
|
+
return results;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Fallback: look for JSON without markdown
|
|
143
|
+
const lines = response.split('\n');
|
|
144
|
+
for (let i = 0; i < lines.length; i++) {
|
|
145
|
+
const line = lines[i].trim();
|
|
146
|
+
if (line.startsWith('{') && line.includes('"reviews"')) {
|
|
147
|
+
// Try to parse from this line to the end
|
|
148
|
+
const potentialJson = lines.slice(i).join('\n').trim();
|
|
149
|
+
try {
|
|
150
|
+
const parsed = JSON.parse(potentialJson);
|
|
151
|
+
if (parsed.reviews) {
|
|
152
|
+
const results = parsed.reviews.map((review) => ({
|
|
153
|
+
file: review.file || 'Unknown file',
|
|
154
|
+
status: review.status || 'OK',
|
|
155
|
+
message: review.message || 'No message provided',
|
|
156
|
+
details: review.details || undefined,
|
|
157
|
+
}));
|
|
158
|
+
console.log(`Parsed ${results.length} results from fallback JSON`);
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
console.log('JSON parsing failed:', error);
|
|
170
|
+
}
|
|
171
|
+
// Ultimate fallback: return a generic result
|
|
172
|
+
console.log('Using fallback parser due to JSON parse failure');
|
|
173
|
+
return [
|
|
174
|
+
{
|
|
175
|
+
file: 'Parse Error',
|
|
176
|
+
status: 'WARN',
|
|
177
|
+
message: 'Failed to parse review results from Claude response',
|
|
178
|
+
details: 'The response was not in the expected JSON format',
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
};
|
|
182
|
+
export const checkClaudeCodeSDK = async () => {
|
|
183
|
+
try {
|
|
184
|
+
// Try to import and use the SDK
|
|
185
|
+
const claudeCode = await import('@anthropic-ai/claude-code');
|
|
186
|
+
return claudeCode && typeof claudeCode.query === 'function';
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
console.log('SDK check failed:', error instanceof Error ? error.message : error);
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { query } from '@anthropic-ai/claude-code';
|
|
2
|
+
import { logInfo, logError, logSuccess } from '../../utils/logger.js';
|
|
3
|
+
import { validateConfiguration } from '../../utils/validation.js';
|
|
4
|
+
export const runRefactor = async (options) => {
|
|
5
|
+
// Load and validate configuration
|
|
6
|
+
const config = validateConfiguration(options);
|
|
7
|
+
if (options.verbose) {
|
|
8
|
+
logInfo('Starting automatic code refactoring for current directory...');
|
|
9
|
+
}
|
|
10
|
+
logInfo('Analyzing and refactoring code...');
|
|
11
|
+
try {
|
|
12
|
+
const refactorPrompt = createRefactorPrompt(config);
|
|
13
|
+
const systemPrompt = createSystemPrompt();
|
|
14
|
+
let hasCompleted = false;
|
|
15
|
+
for await (const message of query({
|
|
16
|
+
prompt: refactorPrompt,
|
|
17
|
+
options: {
|
|
18
|
+
appendSystemPrompt: systemPrompt,
|
|
19
|
+
model: config.claude.model || 'sonnet',
|
|
20
|
+
permissionMode: 'bypassPermissions',
|
|
21
|
+
},
|
|
22
|
+
})) {
|
|
23
|
+
// Stream the refactoring process
|
|
24
|
+
if (message.type === 'assistant' && message.message?.content) {
|
|
25
|
+
for (const content of message.message.content) {
|
|
26
|
+
if (content.type === 'text') {
|
|
27
|
+
console.log(`\n🤖 ${content.text}`);
|
|
28
|
+
}
|
|
29
|
+
else if (content.type === 'tool_use') {
|
|
30
|
+
console.log(`\n🔧 ${content.name}: ${content.input.description || content.input.command || 'Running...'}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (message.type === 'result') {
|
|
35
|
+
hasCompleted = true;
|
|
36
|
+
if (message.subtype === 'success') {
|
|
37
|
+
logSuccess('Code refactoring completed successfully');
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
logError(`Refactoring incomplete: ${message.subtype}`);
|
|
42
|
+
if (message.subtype === 'error_max_turns') {
|
|
43
|
+
console.log('💡 Try simplifying the codebase or increase timeout');
|
|
44
|
+
}
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!hasCompleted) {
|
|
50
|
+
logError('Refactoring process did not complete');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logError(`Refactoring failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const createSystemPrompt = () => {
|
|
60
|
+
return `You are an expert code refactoring assistant using Claude Code. Your task is to analyze and AUTOMATICALLY REFACTOR code in the current directory to improve quality and maintainability.
|
|
61
|
+
|
|
62
|
+
IMPORTANT INSTRUCTIONS:
|
|
63
|
+
1. Use the Bash tool to explore the project structure (ls, find, etc.)
|
|
64
|
+
2. Use the Read tool to examine file contents
|
|
65
|
+
3. Use the Grep tool to search for patterns, code smells, or duplications
|
|
66
|
+
4. Use the Edit tool to DIRECTLY apply refactoring changes
|
|
67
|
+
5. You have FULL PERMISSION to make refactoring changes without asking for confirmation
|
|
68
|
+
|
|
69
|
+
REFACTORING FOCUS AREAS (prioritized):
|
|
70
|
+
1. Code duplication and DRY violations (HIGHEST PRIORITY)
|
|
71
|
+
2. Complex functions that need simplification
|
|
72
|
+
3. Poor naming conventions
|
|
73
|
+
4. Missing type annotations (TypeScript)
|
|
74
|
+
5. Inefficient algorithms or data structures
|
|
75
|
+
6. Code organization and structure
|
|
76
|
+
7. Missing or inadequate error handling
|
|
77
|
+
8. Opportunities for better abstraction
|
|
78
|
+
9. Performance improvements
|
|
79
|
+
|
|
80
|
+
WORKFLOW:
|
|
81
|
+
1. First, explore the project structure to understand the codebase
|
|
82
|
+
2. Identify files that would benefit from refactoring
|
|
83
|
+
3. Analyze specific issues and patterns (focus on high-impact issues first)
|
|
84
|
+
4. For each issue found:
|
|
85
|
+
- Explain what you're refactoring and why
|
|
86
|
+
- Apply the refactoring using the Edit tool
|
|
87
|
+
- Verify the change is correct
|
|
88
|
+
5. Continue until all high-priority refactorings are complete or you run out of turns
|
|
89
|
+
|
|
90
|
+
SAFETY GUIDELINES:
|
|
91
|
+
- Start with safest refactorings (extract utilities, remove duplication)
|
|
92
|
+
- Preserve existing functionality - only improve structure/quality
|
|
93
|
+
- Focus on non-breaking changes
|
|
94
|
+
- Make atomic commits for each logical refactoring
|
|
95
|
+
|
|
96
|
+
IMPORTANT: You should DIRECTLY perform refactorings without asking for permission. The user has granted you full bypass authority to improve the code.`;
|
|
97
|
+
};
|
|
98
|
+
const createRefactorPrompt = (config) => {
|
|
99
|
+
return `Please analyze and REFACTOR the code in the current directory. You have full permission to make improvements directly.
|
|
100
|
+
|
|
101
|
+
TASK: Automatically identify and fix code quality issues
|
|
102
|
+
|
|
103
|
+
Steps to follow:
|
|
104
|
+
1. Start by running "ls -la" to see the project structure
|
|
105
|
+
2. Identify key source files (focus on patterns: ${config.patterns.join(', ')})
|
|
106
|
+
3. Read and analyze relevant source files
|
|
107
|
+
4. Look for HIGH-PRIORITY issues:
|
|
108
|
+
- Code duplication (MOST IMPORTANT - fix first)
|
|
109
|
+
- Complex or long functions
|
|
110
|
+
- Poor naming
|
|
111
|
+
- Missing types or documentation
|
|
112
|
+
- Error handling issues
|
|
113
|
+
- Performance bottlenecks
|
|
114
|
+
5. For EACH issue found, IMMEDIATELY apply the fix:
|
|
115
|
+
- Use the Edit tool to make the change
|
|
116
|
+
- Explain what you changed and why
|
|
117
|
+
- Move to the next issue
|
|
118
|
+
6. Continue refactoring until all high-priority issues are addressed
|
|
119
|
+
|
|
120
|
+
IMPORTANT: Do NOT ask for permission. You have full bypass authority to improve the code directly. Start refactoring immediately after analysis.
|
|
121
|
+
|
|
122
|
+
Start by exploring the current directory structure and begin refactoring.`;
|
|
123
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { startWorkflowProcessor } from '../../workflow-runner/workflow-processor.js';
|
|
2
|
+
import { logInfo, logError, logSuccess } from '../../utils/logger.js';
|
|
3
|
+
import { setupWorkflowProcessor } from '../../utils/workflow-utils.js';
|
|
4
|
+
export const runWorkflow = async (options) => {
|
|
5
|
+
const { config, params } = setupWorkflowProcessor(options);
|
|
6
|
+
try {
|
|
7
|
+
// Start workflow processor using functional composition
|
|
8
|
+
const startProcessor = startWorkflowProcessor(params, config);
|
|
9
|
+
const processor = await startProcessor();
|
|
10
|
+
// Handle graceful shutdown
|
|
11
|
+
const cleanup = () => {
|
|
12
|
+
logInfo('\n⏹️ Received shutdown signal, stopping workflow processor...');
|
|
13
|
+
processor.stop();
|
|
14
|
+
// Show final stats
|
|
15
|
+
const stats = processor.getStats();
|
|
16
|
+
logInfo('📊 Final Statistics:');
|
|
17
|
+
logInfo(` Total processed: ${stats.totalProcessed}`);
|
|
18
|
+
logInfo(` Completed: ${stats.completed}`);
|
|
19
|
+
logInfo(` Failed: ${stats.failed}`);
|
|
20
|
+
logInfo(` Processing: ${stats.processing}`);
|
|
21
|
+
logSuccess('Workflow processor stopped gracefully');
|
|
22
|
+
process.exit(0);
|
|
23
|
+
};
|
|
24
|
+
// Set up signal handlers for graceful shutdown
|
|
25
|
+
process.on('SIGINT', cleanup);
|
|
26
|
+
process.on('SIGTERM', cleanup);
|
|
27
|
+
// Keep the process running
|
|
28
|
+
process.stdin.resume();
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
logError(`Workflow processor failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { runEdsger } from './cli
|
|
1
|
+
export { runEdsger } from './cli.js';
|
|
2
2
|
export { loadConfig, validateConfig } from './config.js';
|
|
3
|
-
export { reviewWithClaudeSDK, checkClaudeCodeSDK, } from './
|
|
3
|
+
export { reviewWithClaudeSDK, checkClaudeCodeSDK, } from './commands/code-review/reviewer.js';
|
|
4
4
|
export type { EdsgerConfig, ReviewResult, CliOptions } from './types/index.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { runEdsger } from './cli
|
|
1
|
+
export { runEdsger } from './cli.js';
|
|
2
2
|
export { loadConfig, validateConfig } from './config.js';
|
|
3
|
-
export { reviewWithClaudeSDK, checkClaudeCodeSDK, } from './
|
|
3
|
+
export { reviewWithClaudeSDK, checkClaudeCodeSDK, } from './commands/code-review/reviewer.js';
|
|
@@ -9,29 +9,76 @@ import { logInfo, logError } from '../../utils/logger.js';
|
|
|
9
9
|
import { parsePullRequestUrl, fetchPRReviews, } from '../code-refine/context-fetcher.js';
|
|
10
10
|
import { getFeature } from '../../api/features/get-feature.js';
|
|
11
11
|
/**
|
|
12
|
-
* Fetch
|
|
12
|
+
* Fetch complete file content from a specific ref (branch/commit)
|
|
13
|
+
*/
|
|
14
|
+
async function fetchFileContent(octokit, owner, repo, ref, path, verbose) {
|
|
15
|
+
try {
|
|
16
|
+
const { data } = await octokit.repos.getContent({
|
|
17
|
+
owner,
|
|
18
|
+
repo,
|
|
19
|
+
ref,
|
|
20
|
+
path,
|
|
21
|
+
});
|
|
22
|
+
// Handle file content (not directory)
|
|
23
|
+
if ('content' in data && !Array.isArray(data)) {
|
|
24
|
+
// Content is base64 encoded
|
|
25
|
+
return Buffer.from(data.content, 'base64').toString('utf-8');
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (verbose) {
|
|
31
|
+
logError(`Failed to fetch content for ${path} at ref ${ref}: ${error}`);
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fetch PR file changes (diff information and full content)
|
|
13
38
|
*/
|
|
14
39
|
async function fetchPRFileChanges(octokit, owner, repo, prNumber, verbose) {
|
|
15
40
|
try {
|
|
16
41
|
if (verbose) {
|
|
17
|
-
logInfo('📂 Fetching PR file changes...');
|
|
42
|
+
logInfo('📂 Fetching PR file changes and content...');
|
|
18
43
|
}
|
|
44
|
+
// Get PR details to get the head ref
|
|
45
|
+
const { data: pr } = await octokit.pulls.get({
|
|
46
|
+
owner,
|
|
47
|
+
repo,
|
|
48
|
+
pull_number: prNumber,
|
|
49
|
+
});
|
|
50
|
+
const headRef = pr.head.sha;
|
|
51
|
+
// Get list of changed files
|
|
19
52
|
const { data: files } = await octokit.pulls.listFiles({
|
|
20
53
|
owner,
|
|
21
54
|
repo,
|
|
22
55
|
pull_number: prNumber,
|
|
23
56
|
per_page: 100,
|
|
24
57
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
58
|
+
if (verbose) {
|
|
59
|
+
logInfo(`✅ Found ${files.length} changed files`);
|
|
60
|
+
logInfo(`📥 Fetching full content for changed files from ${headRef.substring(0, 7)}...`);
|
|
61
|
+
}
|
|
62
|
+
// Fetch full content for each file in parallel
|
|
63
|
+
const fileChanges = await Promise.all(files.map(async (file) => {
|
|
64
|
+
let fullContent;
|
|
65
|
+
// Only fetch content for files that were not deleted
|
|
66
|
+
if (file.status !== 'removed') {
|
|
67
|
+
fullContent = await fetchFileContent(octokit, owner, repo, headRef, file.filename, verbose);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
filename: file.filename,
|
|
71
|
+
status: file.status,
|
|
72
|
+
additions: file.additions,
|
|
73
|
+
deletions: file.deletions,
|
|
74
|
+
changes: file.changes,
|
|
75
|
+
patch: file.patch,
|
|
76
|
+
fullContent,
|
|
77
|
+
};
|
|
32
78
|
}));
|
|
33
79
|
if (verbose) {
|
|
34
|
-
|
|
80
|
+
const filesWithContent = fileChanges.filter((f) => f.fullContent).length;
|
|
81
|
+
logInfo(`✅ Retrieved full content for ${filesWithContent}/${fileChanges.length} files`);
|
|
35
82
|
}
|
|
36
83
|
return fileChanges;
|
|
37
84
|
}
|
|
@@ -221,6 +268,35 @@ function createThreadAnalysisPrompt(thread, fileChange, firstComment) {
|
|
|
221
268
|
const allComments = thread.comments.nodes
|
|
222
269
|
.map((c, idx) => `Comment ${idx + 1} by @${c.author.login}:\n${c.body}`)
|
|
223
270
|
.join('\n\n');
|
|
271
|
+
// Build the code context section
|
|
272
|
+
let codeContext = '';
|
|
273
|
+
// Include diff (what changed)
|
|
274
|
+
if (fileChange.patch) {
|
|
275
|
+
codeContext += `**Code Changes (Diff):**
|
|
276
|
+
\`\`\`diff
|
|
277
|
+
${fileChange.patch}
|
|
278
|
+
\`\`\`
|
|
279
|
+
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
282
|
+
// Include full file content for better context
|
|
283
|
+
if (fileChange.fullContent) {
|
|
284
|
+
// Truncate very large files to avoid context limits
|
|
285
|
+
const maxLength = 10000;
|
|
286
|
+
const content = fileChange.fullContent.length > maxLength
|
|
287
|
+
? fileChange.fullContent.substring(0, maxLength) +
|
|
288
|
+
'\n\n... (file truncated, showing first 10000 characters)'
|
|
289
|
+
: fileChange.fullContent;
|
|
290
|
+
codeContext += `**Complete File Content (After Changes):**
|
|
291
|
+
\`\`\`
|
|
292
|
+
${content}
|
|
293
|
+
\`\`\`
|
|
294
|
+
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
297
|
+
if (!codeContext) {
|
|
298
|
+
codeContext = '(No code information available)\n\n';
|
|
299
|
+
}
|
|
224
300
|
return `You are analyzing whether a code review comment has been addressed by subsequent code changes.
|
|
225
301
|
|
|
226
302
|
**Review Thread Information:**
|
|
@@ -231,18 +307,24 @@ function createThreadAnalysisPrompt(thread, fileChange, firstComment) {
|
|
|
231
307
|
**Review Comments:**
|
|
232
308
|
${allComments}
|
|
233
309
|
|
|
234
|
-
|
|
235
|
-
\`\`\`diff
|
|
236
|
-
${fileChange.patch || '(No patch available)'}
|
|
237
|
-
\`\`\`
|
|
310
|
+
${codeContext}
|
|
238
311
|
|
|
239
312
|
**Your Task:**
|
|
240
|
-
Analyze whether the code changes adequately address the feedback in the review comments.
|
|
313
|
+
Analyze whether the code changes adequately address the feedback in the review comments. You have access to:
|
|
314
|
+
1. **The diff** showing what was changed
|
|
315
|
+
2. **The complete file content** after changes for full context
|
|
316
|
+
|
|
317
|
+
Consider:
|
|
241
318
|
1. Does the feedback request a specific code change?
|
|
242
319
|
2. Have those changes (or equivalent changes) been made?
|
|
243
|
-
3. If the feedback points to a specific line,
|
|
320
|
+
3. If the feedback points to a specific line, use the full file content to understand the broader context
|
|
321
|
+
4. Check if related areas of code were modified to address the concern
|
|
322
|
+
5. Use the complete file to verify the fix is properly integrated
|
|
244
323
|
|
|
245
|
-
**Important:**
|
|
324
|
+
**Important:**
|
|
325
|
+
- The comment may point to a specific line, but the fix might be in nearby code
|
|
326
|
+
- Focus on whether the **underlying issue** was addressed, not just whether that exact line was modified
|
|
327
|
+
- Use the full file content to understand the complete context and verify the implementation
|
|
246
328
|
|
|
247
329
|
Return your analysis in this JSON format:
|
|
248
330
|
\`\`\`json
|
|
@@ -253,9 +335,10 @@ Return your analysis in this JSON format:
|
|
|
253
335
|
\`\`\`
|
|
254
336
|
|
|
255
337
|
Example responses:
|
|
256
|
-
- If a comment says "add null check" and the
|
|
257
|
-
- If a comment suggests a refactor but no relevant changes are visible: {"isAddressed": false, "reason": "No refactoring changes visible in diff"}
|
|
338
|
+
- If a comment says "add null check" and the code shows a null check was added: {"isAddressed": true, "reason": "Null check added in the updated code"}
|
|
339
|
+
- If a comment suggests a refactor but no relevant changes are visible: {"isAddressed": false, "reason": "No refactoring changes visible in diff or file content"}
|
|
258
340
|
- If code was modified in related areas addressing the concern: {"isAddressed": true, "reason": "Related code modified to address the concern"}
|
|
341
|
+
- If the full context shows the issue is resolved differently: {"isAddressed": true, "reason": "Issue resolved through alternative implementation visible in full file"}
|
|
259
342
|
`;
|
|
260
343
|
}
|
|
261
344
|
/**
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CliOptions } from '../types/index.js';
|
|
2
|
+
export interface ValidationResult {
|
|
3
|
+
config: any;
|
|
4
|
+
mcpServerUrl: string;
|
|
5
|
+
mcpToken: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Common configuration validation for all CLI commands
|
|
9
|
+
*/
|
|
10
|
+
export declare const validateConfiguration: (options: CliOptions) => any;
|
|
11
|
+
/**
|
|
12
|
+
* Validate MCP environment variables and URL format
|
|
13
|
+
*/
|
|
14
|
+
export declare const validateMCPEnvironment: () => {
|
|
15
|
+
mcpServerUrl: string;
|
|
16
|
+
mcpToken: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Combined validation for commands that need both config and MCP setup
|
|
20
|
+
*/
|
|
21
|
+
export declare const validateCommandEnvironment: (options: CliOptions) => ValidationResult;
|
|
22
|
+
/**
|
|
23
|
+
* Check if required dependencies are installed
|
|
24
|
+
*/
|
|
25
|
+
export declare const validateRequirements: (checkFunction: () => Promise<boolean>, errorMessage: string) => Promise<void>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { loadConfig, validateConfig } from '../config.js';
|
|
2
|
+
import { logInfo } from './logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Common configuration validation for all CLI commands
|
|
5
|
+
*/
|
|
6
|
+
export const validateConfiguration = (options) => {
|
|
7
|
+
const config = loadConfig(options.config);
|
|
8
|
+
const configErrors = validateConfig(config);
|
|
9
|
+
if (configErrors.length > 0) {
|
|
10
|
+
throw new Error(`Configuration errors:\n${configErrors.map((e) => ` - ${e}`).join('\n')}`);
|
|
11
|
+
}
|
|
12
|
+
if (options.verbose) {
|
|
13
|
+
logInfo('Configuration loaded successfully');
|
|
14
|
+
}
|
|
15
|
+
return config;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Validate MCP environment variables and URL format
|
|
19
|
+
*/
|
|
20
|
+
export const validateMCPEnvironment = () => {
|
|
21
|
+
const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
|
|
22
|
+
const mcpToken = process.env.EDSGER_MCP_TOKEN;
|
|
23
|
+
if (!mcpServerUrl) {
|
|
24
|
+
throw new Error('MCP server URL is required. Set EDSGER_MCP_SERVER_URL environment variable.');
|
|
25
|
+
}
|
|
26
|
+
if (!mcpToken) {
|
|
27
|
+
throw new Error('MCP token is required. Set EDSGER_MCP_TOKEN environment variable.');
|
|
28
|
+
}
|
|
29
|
+
// Validate MCP server URL format
|
|
30
|
+
try {
|
|
31
|
+
new URL(mcpServerUrl);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error('Invalid MCP server URL format. Use http:// or https://');
|
|
35
|
+
}
|
|
36
|
+
return { mcpServerUrl, mcpToken };
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Combined validation for commands that need both config and MCP setup
|
|
40
|
+
*/
|
|
41
|
+
export const validateCommandEnvironment = (options) => {
|
|
42
|
+
const config = validateConfiguration(options);
|
|
43
|
+
const { mcpServerUrl, mcpToken } = validateMCPEnvironment();
|
|
44
|
+
return {
|
|
45
|
+
config,
|
|
46
|
+
mcpServerUrl,
|
|
47
|
+
mcpToken,
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Check if required dependencies are installed
|
|
52
|
+
*/
|
|
53
|
+
export const validateRequirements = async (checkFunction, errorMessage) => {
|
|
54
|
+
const hasRequirements = await checkFunction();
|
|
55
|
+
if (!hasRequirements) {
|
|
56
|
+
throw new Error(errorMessage);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CliOptions } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validate workflow-specific environment variables
|
|
4
|
+
*/
|
|
5
|
+
export declare const validateWorkflowEnvironment: () => {
|
|
6
|
+
productId: string;
|
|
7
|
+
mcpServerUrl: string;
|
|
8
|
+
mcpToken: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Setup workflow processor parameters
|
|
12
|
+
*/
|
|
13
|
+
export declare const setupWorkflowProcessor: (options: CliOptions) => {
|
|
14
|
+
config: any;
|
|
15
|
+
params: {
|
|
16
|
+
productId: string;
|
|
17
|
+
mcpServerUrl: string;
|
|
18
|
+
mcpToken: string;
|
|
19
|
+
verbose?: boolean;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { validateConfiguration } from './validation.js';
|
|
2
|
+
import { logInfo } from './logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Validate workflow-specific environment variables
|
|
5
|
+
*/
|
|
6
|
+
export const validateWorkflowEnvironment = () => {
|
|
7
|
+
const productId = process.env.EDSGER_PRODUCT_ID;
|
|
8
|
+
const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL;
|
|
9
|
+
const mcpToken = process.env.EDSGER_MCP_TOKEN;
|
|
10
|
+
if (!productId) {
|
|
11
|
+
throw new Error('Product ID is required. Set EDSGER_PRODUCT_ID environment variable.');
|
|
12
|
+
}
|
|
13
|
+
if (!mcpServerUrl) {
|
|
14
|
+
throw new Error('MCP server URL is required. Set EDSGER_MCP_SERVER_URL environment variable.');
|
|
15
|
+
}
|
|
16
|
+
if (!mcpToken) {
|
|
17
|
+
throw new Error('MCP token is required. Set EDSGER_MCP_TOKEN environment variable.');
|
|
18
|
+
}
|
|
19
|
+
// Validate MCP server URL format
|
|
20
|
+
try {
|
|
21
|
+
new URL(mcpServerUrl);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error('Invalid MCP server URL format. Use http:// or https://');
|
|
25
|
+
}
|
|
26
|
+
return { productId, mcpServerUrl, mcpToken };
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Setup workflow processor parameters
|
|
30
|
+
*/
|
|
31
|
+
export const setupWorkflowProcessor = (options) => {
|
|
32
|
+
const config = validateConfiguration(options);
|
|
33
|
+
if (options.verbose) {
|
|
34
|
+
logInfo('Starting workflow processor mode...');
|
|
35
|
+
}
|
|
36
|
+
const { productId, mcpServerUrl, mcpToken } = validateWorkflowEnvironment();
|
|
37
|
+
logInfo('🚀 Starting workflow processor...');
|
|
38
|
+
return {
|
|
39
|
+
config,
|
|
40
|
+
params: {
|
|
41
|
+
productId,
|
|
42
|
+
mcpServerUrl,
|
|
43
|
+
mcpToken,
|
|
44
|
+
verbose: options.verbose,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
};
|