baseguard 1.0.5 → 1.0.6
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/ai/gemini-analyzer.d.ts.map +1 -1
- package/dist/ai/gemini-analyzer.js +1 -1
- package/dist/ai/gemini-analyzer.js.map +1 -1
- package/dist/ai/gemini-code-fixer.d.ts.map +1 -1
- package/dist/ai/gemini-code-fixer.js +2 -7
- package/dist/ai/gemini-code-fixer.js.map +1 -1
- package/dist/ai/jules-implementer.d.ts +8 -0
- package/dist/ai/jules-implementer.d.ts.map +1 -1
- package/dist/ai/jules-implementer.js +115 -17
- package/dist/ai/jules-implementer.js.map +1 -1
- package/package.json +1 -1
- package/src/ai/__tests__/gemini-analyzer.test.ts +0 -181
- package/src/ai/agentkit-orchestrator.ts +0 -534
- package/src/ai/fix-manager.ts +0 -362
- package/src/ai/gemini-analyzer.ts +0 -665
- package/src/ai/gemini-code-fixer.ts +0 -539
- package/src/ai/index.ts +0 -4
- package/src/ai/jules-implementer.ts +0 -504
- package/src/ai/unified-code-fixer.ts +0 -347
- package/src/commands/automation.ts +0 -344
- package/src/commands/check.ts +0 -298
- package/src/commands/config.ts +0 -584
- package/src/commands/fix.ts +0 -269
- package/src/commands/index.ts +0 -7
- package/src/commands/init.ts +0 -156
- package/src/commands/status.ts +0 -307
- package/src/core/api-key-manager.ts +0 -298
- package/src/core/baseguard.ts +0 -757
- package/src/core/baseline-checker.ts +0 -566
- package/src/core/cache-manager.ts +0 -272
- package/src/core/configuration-recovery.ts +0 -672
- package/src/core/configuration.ts +0 -596
- package/src/core/debug-logger.ts +0 -590
- package/src/core/directory-filter.ts +0 -421
- package/src/core/error-handler.ts +0 -518
- package/src/core/file-processor.ts +0 -338
- package/src/core/gitignore-manager.ts +0 -169
- package/src/core/graceful-degradation-manager.ts +0 -596
- package/src/core/index.ts +0 -17
- package/src/core/lazy-loader.ts +0 -317
- package/src/core/logger.ts +0 -0
- package/src/core/memory-manager.ts +0 -290
- package/src/core/parser-worker.ts +0 -33
- package/src/core/startup-optimizer.ts +0 -246
- package/src/core/system-error-handler.ts +0 -755
- package/src/git/automation-engine.ts +0 -361
- package/src/git/github-manager.ts +0 -190
- package/src/git/hook-manager.ts +0 -210
- package/src/git/index.ts +0 -4
- package/src/index.ts +0 -8
- package/src/parsers/feature-validator.ts +0 -559
- package/src/parsers/index.ts +0 -8
- package/src/parsers/parser-manager.ts +0 -418
- package/src/parsers/parser.ts +0 -26
- package/src/parsers/react-parser-optimized.ts +0 -161
- package/src/parsers/react-parser.ts +0 -359
- package/src/parsers/svelte-parser.ts +0 -510
- package/src/parsers/vanilla-parser.ts +0 -685
- package/src/parsers/vue-parser.ts +0 -476
- package/src/types/index.ts +0 -96
- package/src/ui/components.ts +0 -567
- package/src/ui/help.ts +0 -193
- package/src/ui/index.ts +0 -4
- package/src/ui/prompts.ts +0 -681
- package/src/ui/terminal-header.ts +0 -59
- package/tests/e2e/baseguard.e2e.test.ts +0 -516
- package/tests/e2e/cross-platform.e2e.test.ts +0 -420
- package/tests/e2e/git-integration.e2e.test.ts +0 -487
- package/tests/fixtures/react-project/package.json +0 -14
- package/tests/fixtures/react-project/src/App.css +0 -76
- package/tests/fixtures/react-project/src/App.tsx +0 -77
- package/tests/fixtures/svelte-project/package.json +0 -11
- package/tests/fixtures/svelte-project/src/App.svelte +0 -369
- package/tests/fixtures/vanilla-project/index.html +0 -76
- package/tests/fixtures/vanilla-project/script.js +0 -331
- package/tests/fixtures/vanilla-project/styles.css +0 -359
- package/tests/fixtures/vue-project/package.json +0 -12
- package/tests/fixtures/vue-project/src/App.vue +0 -216
- package/tmp-smoke/.baseguard/backups/config-2026-02-19T12-04-11-067Z-auto.json +0 -30
- package/tmp-smoke/src/bad.css +0 -3
|
@@ -1,361 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import ora from 'ora';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
|
-
import type { AutomationOptions, Violation, Analysis, Fix, Configuration } from '../types/index.js';
|
|
6
|
-
import { ConfigurationManager } from '../core/configuration.js';
|
|
7
|
-
import { BaseGuard } from '../core/baseguard.js';
|
|
8
|
-
import { UIComponents } from '../ui/components.js';
|
|
9
|
-
import { GeminiAnalyzer } from '../ai/gemini-analyzer.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Automation engine for git workflow integration
|
|
13
|
-
*/
|
|
14
|
-
export class AutomationEngine {
|
|
15
|
-
private config: Configuration;
|
|
16
|
-
private baseGuard: BaseGuard;
|
|
17
|
-
|
|
18
|
-
constructor(config?: Configuration) {
|
|
19
|
-
this.config = config || ConfigurationManager.createDefault();
|
|
20
|
-
this.baseGuard = new BaseGuard(this.config);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Run automation for git hooks
|
|
25
|
-
*/
|
|
26
|
-
async run(options: AutomationOptions): Promise<void> {
|
|
27
|
-
try {
|
|
28
|
-
// Load current configuration
|
|
29
|
-
this.config = await ConfigurationManager.load();
|
|
30
|
-
this.baseGuard = new BaseGuard(this.config);
|
|
31
|
-
|
|
32
|
-
// Check if automation is enabled
|
|
33
|
-
if (!this.config.automation.enabled) {
|
|
34
|
-
console.log(chalk.yellow('⚠️ BaseGuard automation is disabled. Run "base automation enable" to enable it.'));
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Check if this is the correct trigger
|
|
39
|
-
if (this.config.automation.trigger !== options.trigger) {
|
|
40
|
-
// Silently exit if this isn't the configured trigger
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
console.log(chalk.cyan(`🛡️ BaseGuard automation running (${options.trigger})...`));
|
|
45
|
-
|
|
46
|
-
// Step 1: Check violations in staged files
|
|
47
|
-
const violations = await this.checkViolations(options.trigger);
|
|
48
|
-
|
|
49
|
-
if (violations.length === 0) {
|
|
50
|
-
console.log(chalk.green('✅ No compatibility issues found'));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
console.log(chalk.yellow(`⚠️ Found ${violations.length} compatibility issue(s)`));
|
|
55
|
-
UIComponents.showViolations(violations);
|
|
56
|
-
|
|
57
|
-
// Step 2: Analyze violations (if enabled and API key available)
|
|
58
|
-
let analyses: Analysis[] = [];
|
|
59
|
-
if (this.config.automation.autoAnalyze && this.config.apiKeys.gemini) {
|
|
60
|
-
analyses = await this.analyzeViolations(violations);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Step 3: Auto-fix (if enabled and API keys available)
|
|
64
|
-
if (this.config.automation.autoFix && this.config.apiKeys.jules && this.config.apiKeys.gemini) {
|
|
65
|
-
const fixes = await this.generateFixes(violations, analyses);
|
|
66
|
-
if (fixes.length > 0) {
|
|
67
|
-
await this.applyFixes(fixes);
|
|
68
|
-
await this.stageChanges();
|
|
69
|
-
console.log(chalk.green('✅ All issues fixed automatically and staged'));
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Manual mode - show options
|
|
75
|
-
await this.showManualOptions(violations, analyses);
|
|
76
|
-
|
|
77
|
-
// Block commit if configured to do so
|
|
78
|
-
if (this.config.automation.blockCommit && options.strict !== false) {
|
|
79
|
-
console.log(chalk.red('❌ Commit blocked due to compatibility issues'));
|
|
80
|
-
console.log(chalk.dim('Use --no-verify to bypass this check'));
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error(chalk.red('❌ BaseGuard automation failed:'), error instanceof Error ? error.message : 'Unknown error');
|
|
86
|
-
|
|
87
|
-
// Don't block commit on automation errors unless in strict mode
|
|
88
|
-
if (options.strict) {
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Check violations in staged files (for pre-commit) or all files (for pre-push)
|
|
96
|
-
*/
|
|
97
|
-
private async checkViolations(trigger: 'pre-commit' | 'pre-push'): Promise<Violation[]> {
|
|
98
|
-
const spinner = ora('Checking for compatibility violations...').start();
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
let filesToCheck: string[] = [];
|
|
102
|
-
|
|
103
|
-
if (trigger === 'pre-commit') {
|
|
104
|
-
// Get staged files for pre-commit
|
|
105
|
-
filesToCheck = this.getStagedFiles();
|
|
106
|
-
} else {
|
|
107
|
-
// For pre-push, check all files in the repository
|
|
108
|
-
filesToCheck = this.getAllTrackedFiles();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Filter to only supported file types
|
|
112
|
-
const supportedExtensions = ['.js', '.ts', '.jsx', '.tsx', '.vue', '.svelte', '.css', '.html'];
|
|
113
|
-
const filteredFiles = filesToCheck.filter(file =>
|
|
114
|
-
supportedExtensions.some(ext => file.endsWith(ext))
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
if (filteredFiles.length === 0) {
|
|
118
|
-
spinner.succeed('No supported files to check');
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
spinner.text = `Checking ${filteredFiles.length} file(s)...`;
|
|
123
|
-
|
|
124
|
-
// Use BaseGuard to check violations
|
|
125
|
-
const violations = await this.baseGuard.checkViolations(filteredFiles);
|
|
126
|
-
|
|
127
|
-
spinner.succeed(`Checked ${filteredFiles.length} file(s)`);
|
|
128
|
-
return violations;
|
|
129
|
-
|
|
130
|
-
} catch (error) {
|
|
131
|
-
spinner.fail('Failed to check violations');
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Get staged files from git
|
|
138
|
-
*/
|
|
139
|
-
private getStagedFiles(): string[] {
|
|
140
|
-
try {
|
|
141
|
-
const output = execSync('git diff --cached --name-only', { encoding: 'utf-8' });
|
|
142
|
-
return output.trim().split('\n').filter(file => file.length > 0);
|
|
143
|
-
} catch (error) {
|
|
144
|
-
console.warn(chalk.yellow('⚠️ Could not get staged files, checking all files'));
|
|
145
|
-
return this.getAllTrackedFiles();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Get all tracked files from git
|
|
151
|
-
*/
|
|
152
|
-
private getAllTrackedFiles(): string[] {
|
|
153
|
-
try {
|
|
154
|
-
const output = execSync('git ls-files', { encoding: 'utf-8' });
|
|
155
|
-
return output.trim().split('\n').filter(file => file.length > 0);
|
|
156
|
-
} catch (error) {
|
|
157
|
-
throw new Error('Could not get tracked files from git');
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Analyze violations if auto-analyze is enabled
|
|
163
|
-
*/
|
|
164
|
-
private async analyzeViolations(violations: Violation[]): Promise<Analysis[]> {
|
|
165
|
-
if (!this.config.apiKeys.gemini) {
|
|
166
|
-
console.log(chalk.yellow('⚠️ Gemini API key not configured, skipping analysis'));
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const spinner = ora('Analyzing violations with AI...').start();
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
const analyzer = new GeminiAnalyzer(this.config.apiKeys.gemini);
|
|
174
|
-
const analyses: Analysis[] = [];
|
|
175
|
-
|
|
176
|
-
// Analyze violations in batches to avoid rate limiting
|
|
177
|
-
for (const violation of violations) {
|
|
178
|
-
try {
|
|
179
|
-
const analysis = await analyzer.analyzeViolation(violation);
|
|
180
|
-
analyses.push(analysis);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.warn(chalk.yellow(`⚠️ Could not analyze ${violation.feature}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
spinner.succeed(`Analyzed ${analyses.length} violation(s)`);
|
|
187
|
-
return analyses;
|
|
188
|
-
|
|
189
|
-
} catch (error) {
|
|
190
|
-
spinner.fail('Failed to analyze violations');
|
|
191
|
-
throw error;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Generate and apply fixes if auto-fix is enabled
|
|
197
|
-
*/
|
|
198
|
-
private async generateFixes(violations: Violation[], analyses: Analysis[]): Promise<Fix[]> {
|
|
199
|
-
if (!this.config.apiKeys.jules) {
|
|
200
|
-
console.log(chalk.yellow('⚠️ Jules API key not configured, skipping auto-fix'));
|
|
201
|
-
return [];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const spinner = ora('Generating fixes with AI...').start();
|
|
205
|
-
|
|
206
|
-
try {
|
|
207
|
-
const fixes: Fix[] = [];
|
|
208
|
-
|
|
209
|
-
// Generate fixes for violations that have analyses
|
|
210
|
-
for (const violation of violations) {
|
|
211
|
-
const analysis = analyses.find(a => a.violation.feature === violation.feature);
|
|
212
|
-
if (!analysis) continue;
|
|
213
|
-
|
|
214
|
-
try {
|
|
215
|
-
// For automation, we need a repository source - this would need to be configured
|
|
216
|
-
// For now, we'll skip Jules integration in automation mode
|
|
217
|
-
console.log(chalk.yellow('⚠️ Jules integration requires repository setup, skipping auto-fix'));
|
|
218
|
-
break;
|
|
219
|
-
} catch (error) {
|
|
220
|
-
console.warn(chalk.yellow(`⚠️ Could not generate fix for ${violation.feature}: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
spinner.succeed(`Generated ${fixes.length} fix(es)`);
|
|
225
|
-
return fixes;
|
|
226
|
-
|
|
227
|
-
} catch (error) {
|
|
228
|
-
spinner.fail('Failed to generate fixes');
|
|
229
|
-
throw error;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Apply fixes to files
|
|
235
|
-
*/
|
|
236
|
-
private async applyFixes(fixes: Fix[]): Promise<void> {
|
|
237
|
-
const spinner = ora('Applying fixes...').start();
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
// This would use the BaseGuard.applyFixes method when implemented
|
|
241
|
-
// For now, we'll just log that fixes would be applied
|
|
242
|
-
spinner.succeed(`Applied ${fixes.length} fix(es)`);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
spinner.fail('Failed to apply fixes');
|
|
245
|
-
throw error;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Stage changes after fixing
|
|
251
|
-
*/
|
|
252
|
-
private async stageChanges(): Promise<void> {
|
|
253
|
-
try {
|
|
254
|
-
execSync('git add -u', { stdio: 'ignore' });
|
|
255
|
-
} catch (error) {
|
|
256
|
-
console.warn(chalk.yellow('⚠️ Could not stage changes automatically'));
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Show manual options when auto-fix is disabled
|
|
262
|
-
*/
|
|
263
|
-
private async showManualOptions(violations: Violation[], analyses: Analysis[] = []): Promise<void> {
|
|
264
|
-
console.log(chalk.cyan('\n🔧 Manual Options:'));
|
|
265
|
-
|
|
266
|
-
const choices = [
|
|
267
|
-
{
|
|
268
|
-
name: 'Continue with commit (ignore violations)',
|
|
269
|
-
value: 'continue'
|
|
270
|
-
},
|
|
271
|
-
{
|
|
272
|
-
name: 'Fix violations manually and retry',
|
|
273
|
-
value: 'manual'
|
|
274
|
-
}
|
|
275
|
-
];
|
|
276
|
-
|
|
277
|
-
// Add AI options if API keys are available
|
|
278
|
-
if (this.config.apiKeys.gemini && analyses.length === 0) {
|
|
279
|
-
choices.unshift({
|
|
280
|
-
name: 'Analyze violations with AI first',
|
|
281
|
-
value: 'analyze'
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (this.config.apiKeys.jules && this.config.apiKeys.gemini) {
|
|
286
|
-
choices.unshift({
|
|
287
|
-
name: 'Generate and preview AI fixes',
|
|
288
|
-
value: 'fix'
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const { action } = await inquirer.prompt([
|
|
293
|
-
{
|
|
294
|
-
type: 'list',
|
|
295
|
-
name: 'action',
|
|
296
|
-
message: 'What would you like to do?',
|
|
297
|
-
choices
|
|
298
|
-
}
|
|
299
|
-
]);
|
|
300
|
-
|
|
301
|
-
switch (action) {
|
|
302
|
-
case 'continue':
|
|
303
|
-
console.log(chalk.yellow('⚠️ Continuing with violations...'));
|
|
304
|
-
break;
|
|
305
|
-
|
|
306
|
-
case 'manual':
|
|
307
|
-
console.log(chalk.blue('💡 Fix the violations manually and run git commit again'));
|
|
308
|
-
process.exit(1);
|
|
309
|
-
return;
|
|
310
|
-
|
|
311
|
-
case 'analyze':
|
|
312
|
-
console.log(chalk.blue('🔍 Run "base fix --analyze-only" to get AI analysis'));
|
|
313
|
-
process.exit(1);
|
|
314
|
-
return;
|
|
315
|
-
|
|
316
|
-
case 'fix':
|
|
317
|
-
console.log(chalk.blue('🤖 Run "base fix" to generate and preview AI fixes'));
|
|
318
|
-
process.exit(1);
|
|
319
|
-
return;
|
|
320
|
-
|
|
321
|
-
default:
|
|
322
|
-
process.exit(1);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Check if git repository has uncommitted changes
|
|
328
|
-
*/
|
|
329
|
-
private hasUncommittedChanges(): boolean {
|
|
330
|
-
try {
|
|
331
|
-
const output = execSync('git status --porcelain', { encoding: 'utf-8' });
|
|
332
|
-
return output.trim().length > 0;
|
|
333
|
-
} catch {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Get current git branch
|
|
340
|
-
*/
|
|
341
|
-
private getCurrentBranch(): string {
|
|
342
|
-
try {
|
|
343
|
-
const output = execSync('git branch --show-current', { encoding: 'utf-8' });
|
|
344
|
-
return output.trim();
|
|
345
|
-
} catch {
|
|
346
|
-
return 'unknown';
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Check if we're in a git repository
|
|
352
|
-
*/
|
|
353
|
-
private isGitRepository(): boolean {
|
|
354
|
-
try {
|
|
355
|
-
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
|
|
356
|
-
return true;
|
|
357
|
-
} catch {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* GitHub repository manager for Jules integration
|
|
7
|
-
* Note: GitHub app installation should be done on the Jules dashboard, not here
|
|
8
|
-
*/
|
|
9
|
-
export class GitHubManager {
|
|
10
|
-
private repoOwner: string | null = null;
|
|
11
|
-
private repoName: string | null = null;
|
|
12
|
-
private sourceIdentifier: string | null = null;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Check if current directory is a git repository
|
|
16
|
-
*/
|
|
17
|
-
isGitRepository(): boolean {
|
|
18
|
-
try {
|
|
19
|
-
execSync('git rev-parse --git-dir', { stdio: 'ignore' });
|
|
20
|
-
return true;
|
|
21
|
-
} catch {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Detect repository owner and name from git remote
|
|
28
|
-
*/
|
|
29
|
-
async detectRepositoryInfo(): Promise<void> {
|
|
30
|
-
try {
|
|
31
|
-
const remoteUrl = execSync('git config --get remote.origin.url', { encoding: 'utf8' }).trim();
|
|
32
|
-
|
|
33
|
-
// Parse GitHub URL (supports both HTTPS and SSH)
|
|
34
|
-
const match = remoteUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
|
|
35
|
-
|
|
36
|
-
if (match) {
|
|
37
|
-
this.repoOwner = match[1] || null;
|
|
38
|
-
this.repoName = match[2] || null;
|
|
39
|
-
|
|
40
|
-
console.log(chalk.blue(`📁 Detected repository: ${this.repoOwner}/${this.repoName}`));
|
|
41
|
-
} else {
|
|
42
|
-
throw new Error('Could not detect GitHub repository from remote URL');
|
|
43
|
-
}
|
|
44
|
-
} catch (error) {
|
|
45
|
-
throw new Error(`Failed to detect repository info: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get source identifier for Jules API
|
|
53
|
-
*/
|
|
54
|
-
async getSourceIdentifier(): Promise<string> {
|
|
55
|
-
if (!this.repoOwner || !this.repoName) {
|
|
56
|
-
await this.detectRepositoryInfo();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!this.repoOwner || !this.repoName) {
|
|
60
|
-
throw new Error('Repository information not available');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Generate source identifier in the format expected by Jules
|
|
64
|
-
this.sourceIdentifier = `sources/github/${this.repoOwner}/${this.repoName}`;
|
|
65
|
-
|
|
66
|
-
return this.sourceIdentifier;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Verify GitHub authentication and permissions
|
|
71
|
-
*/
|
|
72
|
-
async verifyGitHubConnection(): Promise<boolean> {
|
|
73
|
-
try {
|
|
74
|
-
if (!this.repoOwner || !this.repoName) {
|
|
75
|
-
await this.detectRepositoryInfo();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Check if we can access the repository
|
|
79
|
-
const response = await fetch(`https://api.github.com/repos/${this.repoOwner}/${this.repoName}`);
|
|
80
|
-
|
|
81
|
-
if (response.ok) {
|
|
82
|
-
console.log(chalk.green('✅ GitHub repository access verified'));
|
|
83
|
-
return true;
|
|
84
|
-
} else {
|
|
85
|
-
console.log(chalk.yellow('⚠️ GitHub repository access verification failed'));
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.log(chalk.red(`❌ GitHub connection error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Get current repository source identifier
|
|
96
|
-
*/
|
|
97
|
-
async getCurrentSourceIdentifier(): Promise<string> {
|
|
98
|
-
if (this.sourceIdentifier) {
|
|
99
|
-
return this.sourceIdentifier;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
await this.detectRepositoryInfo();
|
|
103
|
-
return this.getSourceIdentifier();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Check if we can detect repository information (GitHub integration is handled on Jules dashboard)
|
|
108
|
-
*/
|
|
109
|
-
async isJulesIntegrationSetup(): Promise<boolean> {
|
|
110
|
-
try {
|
|
111
|
-
if (!this.isGitRepository()) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
await this.detectRepositoryInfo();
|
|
116
|
-
return this.repoOwner !== null && this.repoName !== null;
|
|
117
|
-
} catch {
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Get repository information
|
|
124
|
-
*/
|
|
125
|
-
getRepositoryInfo(): { owner: string | null; name: string | null } {
|
|
126
|
-
return {
|
|
127
|
-
owner: this.repoOwner,
|
|
128
|
-
name: this.repoName
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Check if repository has required permissions for Jules
|
|
134
|
-
*/
|
|
135
|
-
async checkRepositoryPermissions(): Promise<{ hasAccess: boolean; permissions: string[] }> {
|
|
136
|
-
try {
|
|
137
|
-
if (!this.repoOwner || !this.repoName) {
|
|
138
|
-
await this.detectRepositoryInfo();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// In a real implementation, this would check specific permissions
|
|
142
|
-
// For now, we'll assume basic access if we can read the repo
|
|
143
|
-
const hasAccess = await this.verifyGitHubConnection();
|
|
144
|
-
|
|
145
|
-
const permissions = hasAccess ? [
|
|
146
|
-
'read',
|
|
147
|
-
'write',
|
|
148
|
-
'pull_requests'
|
|
149
|
-
] : [];
|
|
150
|
-
|
|
151
|
-
return { hasAccess, permissions };
|
|
152
|
-
} catch {
|
|
153
|
-
return { hasAccess: false, permissions: [] };
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Get current branch name
|
|
159
|
-
*/
|
|
160
|
-
getCurrentBranch(): string {
|
|
161
|
-
try {
|
|
162
|
-
return execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
163
|
-
} catch {
|
|
164
|
-
return 'main'; // fallback
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Check if there are uncommitted changes
|
|
170
|
-
*/
|
|
171
|
-
hasUncommittedChanges(): boolean {
|
|
172
|
-
try {
|
|
173
|
-
const status = execSync('git status --porcelain', { encoding: 'utf8' });
|
|
174
|
-
return status.trim().length > 0;
|
|
175
|
-
} catch {
|
|
176
|
-
return false;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Get repository URL for display
|
|
182
|
-
*/
|
|
183
|
-
getRepositoryUrl(): string | null {
|
|
184
|
-
if (!this.repoOwner || !this.repoName) {
|
|
185
|
-
return null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return `https://github.com/${this.repoOwner}/${this.repoName}`;
|
|
189
|
-
}
|
|
190
|
-
}
|