baseguard 1.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/.eslintrc.json +25 -0
- package/.prettierrc +8 -0
- package/README.md +94 -0
- package/bin/base.js +494 -0
- package/dist/ai/fix-manager.d.ts +67 -0
- package/dist/ai/fix-manager.d.ts.map +1 -0
- package/dist/ai/fix-manager.js +326 -0
- package/dist/ai/fix-manager.js.map +1 -0
- package/dist/ai/gemini-analyzer.d.ts +116 -0
- package/dist/ai/gemini-analyzer.d.ts.map +1 -0
- package/dist/ai/gemini-analyzer.js +572 -0
- package/dist/ai/gemini-analyzer.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +5 -0
- package/dist/ai/index.js.map +1 -0
- package/dist/ai/jules-implementer.d.ts +115 -0
- package/dist/ai/jules-implementer.d.ts.map +1 -0
- package/dist/ai/jules-implementer.js +387 -0
- package/dist/ai/jules-implementer.js.map +1 -0
- package/dist/commands/automation.d.ts +5 -0
- package/dist/commands/automation.d.ts.map +1 -0
- package/dist/commands/automation.js +305 -0
- package/dist/commands/automation.js.map +1 -0
- package/dist/commands/check.d.ts +9 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +113 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/config.d.ts +11 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +324 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/fix.d.ts +9 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +207 -0
- package/dist/commands/fix.js.map +1 -0
- package/dist/commands/index.d.ts +6 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +125 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/core/api-key-manager.d.ts +83 -0
- package/dist/core/api-key-manager.d.ts.map +1 -0
- package/dist/core/api-key-manager.js +244 -0
- package/dist/core/api-key-manager.js.map +1 -0
- package/dist/core/baseguard.d.ts +46 -0
- package/dist/core/baseguard.d.ts.map +1 -0
- package/dist/core/baseguard.js +132 -0
- package/dist/core/baseguard.js.map +1 -0
- package/dist/core/baseline-checker.d.ts +63 -0
- package/dist/core/baseline-checker.d.ts.map +1 -0
- package/dist/core/baseline-checker.js +502 -0
- package/dist/core/baseline-checker.js.map +1 -0
- package/dist/core/cache-manager.d.ts +88 -0
- package/dist/core/cache-manager.d.ts.map +1 -0
- package/dist/core/cache-manager.js +213 -0
- package/dist/core/cache-manager.js.map +1 -0
- package/dist/core/configuration.d.ts +140 -0
- package/dist/core/configuration.d.ts.map +1 -0
- package/dist/core/configuration.js +474 -0
- package/dist/core/configuration.js.map +1 -0
- package/dist/core/directory-filter.d.ts +90 -0
- package/dist/core/directory-filter.d.ts.map +1 -0
- package/dist/core/directory-filter.js +319 -0
- package/dist/core/directory-filter.js.map +1 -0
- package/dist/core/error-handler.d.ts +110 -0
- package/dist/core/error-handler.d.ts.map +1 -0
- package/dist/core/error-handler.js +392 -0
- package/dist/core/error-handler.js.map +1 -0
- package/dist/core/file-processor.d.ts +80 -0
- package/dist/core/file-processor.d.ts.map +1 -0
- package/dist/core/file-processor.js +259 -0
- package/dist/core/file-processor.js.map +1 -0
- package/dist/core/gitignore-manager.d.ts +44 -0
- package/dist/core/gitignore-manager.d.ts.map +1 -0
- package/dist/core/gitignore-manager.js +147 -0
- package/dist/core/gitignore-manager.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/lazy-loader.d.ts +68 -0
- package/dist/core/lazy-loader.d.ts.map +1 -0
- package/dist/core/lazy-loader.js +260 -0
- package/dist/core/lazy-loader.js.map +1 -0
- package/dist/core/memory-manager.d.ts +1 -0
- package/dist/core/memory-manager.d.ts.map +1 -0
- package/dist/core/memory-manager.js +2 -0
- package/dist/core/memory-manager.js.map +1 -0
- package/dist/core/startup-optimizer.d.ts +45 -0
- package/dist/core/startup-optimizer.d.ts.map +1 -0
- package/dist/core/startup-optimizer.js +140 -0
- package/dist/core/startup-optimizer.js.map +1 -0
- package/dist/git/automation-engine.d.ts +58 -0
- package/dist/git/automation-engine.d.ts.map +1 -0
- package/dist/git/automation-engine.js +318 -0
- package/dist/git/automation-engine.js.map +1 -0
- package/dist/git/github-manager.d.ts +71 -0
- package/dist/git/github-manager.d.ts.map +1 -0
- package/dist/git/github-manager.js +226 -0
- package/dist/git/github-manager.js.map +1 -0
- package/dist/git/hook-manager.d.ts +43 -0
- package/dist/git/hook-manager.d.ts.map +1 -0
- package/dist/git/hook-manager.js +191 -0
- package/dist/git/hook-manager.js.map +1 -0
- package/dist/git/index.d.ts +4 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +5 -0
- package/dist/git/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/feature-validator.d.ts +60 -0
- package/dist/parsers/feature-validator.d.ts.map +1 -0
- package/dist/parsers/feature-validator.js +483 -0
- package/dist/parsers/feature-validator.js.map +1 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +9 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/parser-manager.d.ts +103 -0
- package/dist/parsers/parser-manager.d.ts.map +1 -0
- package/dist/parsers/parser-manager.js +321 -0
- package/dist/parsers/parser-manager.js.map +1 -0
- package/dist/parsers/parser.d.ts +23 -0
- package/dist/parsers/parser.d.ts.map +1 -0
- package/dist/parsers/parser.js +6 -0
- package/dist/parsers/parser.js.map +1 -0
- package/dist/parsers/react-parser.d.ts +22 -0
- package/dist/parsers/react-parser.d.ts.map +1 -0
- package/dist/parsers/react-parser.js +307 -0
- package/dist/parsers/react-parser.js.map +1 -0
- package/dist/parsers/svelte-parser.d.ts +33 -0
- package/dist/parsers/svelte-parser.d.ts.map +1 -0
- package/dist/parsers/svelte-parser.js +408 -0
- package/dist/parsers/svelte-parser.js.map +1 -0
- package/dist/parsers/vanilla-parser.d.ts +31 -0
- package/dist/parsers/vanilla-parser.d.ts.map +1 -0
- package/dist/parsers/vanilla-parser.js +590 -0
- package/dist/parsers/vanilla-parser.js.map +1 -0
- package/dist/parsers/vue-parser.d.ts +9 -0
- package/dist/parsers/vue-parser.d.ts.map +1 -0
- package/dist/parsers/vue-parser.js +16 -0
- package/dist/parsers/vue-parser.js.map +1 -0
- package/dist/terminal-header.d.ts +12 -0
- package/dist/terminal-header.js +45 -0
- package/dist/types/index.d.ts +83 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/ui/components.d.ts +133 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js +482 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/help.d.ts +11 -0
- package/dist/ui/help.d.ts.map +1 -0
- package/dist/ui/help.js +161 -0
- package/dist/ui/help.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +5 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/prompts.d.ts +63 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +611 -0
- package/dist/ui/prompts.js.map +1 -0
- package/dist/ui/terminal-header.d.ts +13 -0
- package/dist/ui/terminal-header.d.ts.map +1 -0
- package/dist/ui/terminal-header.js +46 -0
- package/dist/ui/terminal-header.js.map +1 -0
- package/package.json +80 -0
- package/src/ai/__tests__/gemini-analyzer.test.ts +181 -0
- package/src/ai/fix-manager.ts +362 -0
- package/src/ai/gemini-analyzer.ts +671 -0
- package/src/ai/index.ts +4 -0
- package/src/ai/jules-implementer.ts +459 -0
- package/src/commands/automation.ts +344 -0
- package/src/commands/check.ts +299 -0
- package/src/commands/config.ts +365 -0
- package/src/commands/fix.ts +234 -0
- package/src/commands/index.ts +6 -0
- package/src/commands/init.ts +142 -0
- package/src/commands/status.ts +0 -0
- package/src/core/api-key-manager.ts +298 -0
- package/src/core/baseguard.ts +742 -0
- package/src/core/baseline-checker.ts +563 -0
- package/src/core/cache-manager.ts +270 -0
- package/src/core/configuration-recovery.ts +676 -0
- package/src/core/configuration.ts +559 -0
- package/src/core/debug-logger.ts +590 -0
- package/src/core/directory-filter.ts +421 -0
- package/src/core/error-handler.ts +517 -0
- package/src/core/file-processor.ts +331 -0
- package/src/core/gitignore-manager.ts +169 -0
- package/src/core/graceful-degradation-manager.ts +596 -0
- package/src/core/index.ts +13 -0
- package/src/core/lazy-loader.ts +307 -0
- package/src/core/logger.ts +0 -0
- package/src/core/memory-manager.ts +294 -0
- package/src/core/startup-optimizer.ts +173 -0
- package/src/core/system-error-handler.ts +746 -0
- package/src/git/automation-engine.ts +361 -0
- package/src/git/github-manager.ts +260 -0
- package/src/git/hook-manager.ts +210 -0
- package/src/git/index.ts +4 -0
- package/src/index.ts +8 -0
- package/src/parsers/feature-validator.ts +559 -0
- package/src/parsers/index.ts +8 -0
- package/src/parsers/parser-manager.ts +419 -0
- package/src/parsers/parser.ts +26 -0
- package/src/parsers/react-parser-optimized.ts +161 -0
- package/src/parsers/react-parser.ts +359 -0
- package/src/parsers/svelte-parser.ts +506 -0
- package/src/parsers/vanilla-parser.ts +682 -0
- package/src/parsers/vue-parser.ts +472 -0
- package/src/types/index.ts +92 -0
- package/src/ui/components.ts +567 -0
- package/src/ui/help.ts +193 -0
- package/src/ui/index.ts +4 -0
- package/src/ui/prompts.ts +688 -0
- package/src/ui/terminal-header.ts +59 -0
- package/test-config-commands.js +56 -0
- package/test-header-simple.js +33 -0
- package/test-terminal-header.js +12 -0
- package/test-ui.js +29 -0
- package/tests/e2e/baseguard.e2e.test.ts +516 -0
- package/tests/e2e/cross-platform.e2e.test.ts +420 -0
- package/tests/e2e/git-integration.e2e.test.ts +487 -0
- package/tests/fixtures/react-project/package.json +14 -0
- package/tests/fixtures/react-project/src/App.css +76 -0
- package/tests/fixtures/react-project/src/App.tsx +77 -0
- package/tests/fixtures/svelte-project/package.json +11 -0
- package/tests/fixtures/svelte-project/src/App.svelte +369 -0
- package/tests/fixtures/vanilla-project/index.html +76 -0
- package/tests/fixtures/vanilla-project/script.js +331 -0
- package/tests/fixtures/vanilla-project/styles.css +359 -0
- package/tests/fixtures/vue-project/package.json +12 -0
- package/tests/fixtures/vue-project/src/App.vue +216 -0
- package/tsconfig.json +36 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,688 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import open from 'open';
|
|
3
|
+
import { Colors, UIComponents } from './components.js';
|
|
4
|
+
import { ApiKeyManager } from '../core/api-key-manager.js';
|
|
5
|
+
import type { BrowserTarget } from '../types/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Interactive prompts for user input
|
|
9
|
+
*/
|
|
10
|
+
export class Prompts {
|
|
11
|
+
/**
|
|
12
|
+
* Prompt for initial BaseGuard setup
|
|
13
|
+
*/
|
|
14
|
+
static async setupWizard(): Promise<{
|
|
15
|
+
targets: BrowserTarget[];
|
|
16
|
+
installHooks: boolean;
|
|
17
|
+
hookTrigger: 'pre-commit' | 'pre-push';
|
|
18
|
+
setupApiKeys: boolean;
|
|
19
|
+
}> {
|
|
20
|
+
UIComponents.showHeader();
|
|
21
|
+
UIComponents.showSectionHeader('Welcome to BaseGuard Setup');
|
|
22
|
+
|
|
23
|
+
console.log(Colors.muted('Let\'s configure BaseGuard for your project.\n'));
|
|
24
|
+
|
|
25
|
+
const answers = await inquirer.prompt([
|
|
26
|
+
{
|
|
27
|
+
type: 'list',
|
|
28
|
+
name: 'targetPreset',
|
|
29
|
+
message: 'Which browser compatibility target would you like to use?',
|
|
30
|
+
choices: [
|
|
31
|
+
{
|
|
32
|
+
name: 'Baseline Widely (30+ months of support) - Recommended',
|
|
33
|
+
value: 'baseline-widely'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'Baseline Newly (Recently available features)',
|
|
37
|
+
value: 'baseline-newly'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'Last 2 years (Modern browsers)',
|
|
41
|
+
value: 'last-2-years'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'Custom configuration',
|
|
45
|
+
value: 'custom'
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
default: 'baseline-widely'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'confirm',
|
|
52
|
+
name: 'installHooks',
|
|
53
|
+
message: 'Would you like to install git hooks for automatic checking?',
|
|
54
|
+
default: true
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'list',
|
|
58
|
+
name: 'hookTrigger',
|
|
59
|
+
message: 'When should BaseGuard check your code?',
|
|
60
|
+
choices: [
|
|
61
|
+
{
|
|
62
|
+
name: 'Before each commit (pre-commit) - Recommended',
|
|
63
|
+
value: 'pre-commit'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'Before each push (pre-push)',
|
|
67
|
+
value: 'pre-push'
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
default: 'pre-commit',
|
|
71
|
+
when: (answers) => answers.installHooks
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'confirm',
|
|
75
|
+
name: 'setupApiKeys',
|
|
76
|
+
message: 'Would you like to set up AI analysis and fixing now?',
|
|
77
|
+
default: true
|
|
78
|
+
}
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
let targets: BrowserTarget[] = [];
|
|
82
|
+
|
|
83
|
+
if (answers.targetPreset === 'custom') {
|
|
84
|
+
targets = await this.promptCustomTargets();
|
|
85
|
+
} else {
|
|
86
|
+
targets = this.getPresetTargets(answers.targetPreset);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
targets,
|
|
91
|
+
installHooks: answers.installHooks,
|
|
92
|
+
hookTrigger: answers.hookTrigger || 'pre-commit',
|
|
93
|
+
setupApiKeys: answers.setupApiKeys
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Prompt for custom browser targets
|
|
99
|
+
*/
|
|
100
|
+
static async promptCustomTargets(): Promise<BrowserTarget[]> {
|
|
101
|
+
const targets: BrowserTarget[] = [];
|
|
102
|
+
let addMore = true;
|
|
103
|
+
|
|
104
|
+
while (addMore) {
|
|
105
|
+
const target = await inquirer.prompt([
|
|
106
|
+
{
|
|
107
|
+
type: 'list',
|
|
108
|
+
name: 'browser',
|
|
109
|
+
message: 'Select a browser:',
|
|
110
|
+
choices: [
|
|
111
|
+
'chrome',
|
|
112
|
+
'firefox',
|
|
113
|
+
'safari',
|
|
114
|
+
'edge',
|
|
115
|
+
'opera',
|
|
116
|
+
'samsung_android',
|
|
117
|
+
'webview_android'
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: 'input',
|
|
122
|
+
name: 'version',
|
|
123
|
+
message: 'Minimum version (or "baseline" for baseline support):',
|
|
124
|
+
default: 'baseline',
|
|
125
|
+
validate: (input: string) => {
|
|
126
|
+
if (input === 'baseline' || input === 'baseline-newly') return true;
|
|
127
|
+
if (/^\d+(\.\d+)*$/.test(input)) return true;
|
|
128
|
+
return 'Please enter a valid version number or "baseline"';
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
targets.push({
|
|
134
|
+
browser: target.browser,
|
|
135
|
+
minVersion: target.version
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const continuePrompt = await inquirer.prompt([
|
|
139
|
+
{
|
|
140
|
+
type: 'confirm',
|
|
141
|
+
name: 'addAnother',
|
|
142
|
+
message: 'Add another browser target?',
|
|
143
|
+
default: false
|
|
144
|
+
}
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
addMore = continuePrompt.addAnother;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return targets;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get preset browser targets
|
|
155
|
+
*/
|
|
156
|
+
private static getPresetTargets(preset: string): BrowserTarget[] {
|
|
157
|
+
switch (preset) {
|
|
158
|
+
case 'baseline-widely':
|
|
159
|
+
return [
|
|
160
|
+
{ browser: 'chrome', minVersion: 'baseline' },
|
|
161
|
+
{ browser: 'firefox', minVersion: 'baseline' },
|
|
162
|
+
{ browser: 'safari', minVersion: 'baseline' },
|
|
163
|
+
{ browser: 'edge', minVersion: 'baseline' }
|
|
164
|
+
];
|
|
165
|
+
case 'baseline-newly':
|
|
166
|
+
return [
|
|
167
|
+
{ browser: 'chrome', minVersion: 'baseline-newly' },
|
|
168
|
+
{ browser: 'firefox', minVersion: 'baseline-newly' },
|
|
169
|
+
{ browser: 'safari', minVersion: 'baseline-newly' },
|
|
170
|
+
{ browser: 'edge', minVersion: 'baseline-newly' }
|
|
171
|
+
];
|
|
172
|
+
case 'last-2-years':
|
|
173
|
+
return [
|
|
174
|
+
{ browser: 'chrome', minVersion: '109' },
|
|
175
|
+
{ browser: 'firefox', minVersion: '109' },
|
|
176
|
+
{ browser: 'safari', minVersion: '16' },
|
|
177
|
+
{ browser: 'edge', minVersion: '109' }
|
|
178
|
+
];
|
|
179
|
+
default:
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Guided API key setup with browser integration
|
|
186
|
+
*/
|
|
187
|
+
static async setupApiKeys(): Promise<{
|
|
188
|
+
julesApiKey?: string;
|
|
189
|
+
geminiApiKey?: string;
|
|
190
|
+
}> {
|
|
191
|
+
UIComponents.showSectionHeader('API Key Setup');
|
|
192
|
+
|
|
193
|
+
console.log(Colors.muted('BaseGuard uses AI services for analysis and fixing:'));
|
|
194
|
+
console.log(Colors.muted('• Jules API for autonomous code fixing'));
|
|
195
|
+
console.log(Colors.muted('• Gemini API for compatibility analysis\n'));
|
|
196
|
+
|
|
197
|
+
const setupChoice = await inquirer.prompt([
|
|
198
|
+
{
|
|
199
|
+
type: 'list',
|
|
200
|
+
name: 'choice',
|
|
201
|
+
message: 'How would you like to proceed?',
|
|
202
|
+
choices: [
|
|
203
|
+
{
|
|
204
|
+
name: 'Set up both APIs now (Recommended)',
|
|
205
|
+
value: 'both'
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: 'Set up Jules API only (for fixing)',
|
|
209
|
+
value: 'jules'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'Set up Gemini API only (for analysis)',
|
|
213
|
+
value: 'gemini'
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: 'Skip for now (baseline checking only)',
|
|
217
|
+
value: 'skip'
|
|
218
|
+
}
|
|
219
|
+
],
|
|
220
|
+
default: 'both'
|
|
221
|
+
}
|
|
222
|
+
]);
|
|
223
|
+
|
|
224
|
+
if (setupChoice.choice === 'skip') {
|
|
225
|
+
UIComponents.showWarningBox('Skipping API setup. You can configure APIs later with: base config api-keys');
|
|
226
|
+
return {};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const result: { julesApiKey?: string; geminiApiKey?: string } = {};
|
|
230
|
+
|
|
231
|
+
if (setupChoice.choice === 'both' || setupChoice.choice === 'jules') {
|
|
232
|
+
result.julesApiKey = await this.setupJulesApiKey();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (setupChoice.choice === 'both' || setupChoice.choice === 'gemini') {
|
|
236
|
+
result.geminiApiKey = await this.setupGeminiApiKey();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Set up Jules API key with browser integration
|
|
244
|
+
*/
|
|
245
|
+
static async setupJulesApiKey(): Promise<string | undefined> {
|
|
246
|
+
console.log(Colors.info('\n🔧 Setting up Jules API for autonomous code fixing'));
|
|
247
|
+
|
|
248
|
+
const openBrowser = await inquirer.prompt([
|
|
249
|
+
{
|
|
250
|
+
type: 'confirm',
|
|
251
|
+
name: 'open',
|
|
252
|
+
message: 'Open jules.google.com to get your API key?',
|
|
253
|
+
default: true
|
|
254
|
+
}
|
|
255
|
+
]);
|
|
256
|
+
|
|
257
|
+
if (openBrowser.open) {
|
|
258
|
+
const spinner = UIComponents.createSpinner('Opening Jules website...');
|
|
259
|
+
spinner.start();
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
await open('https://jules.google.com');
|
|
263
|
+
spinner.succeed('Opened jules.google.com');
|
|
264
|
+
} catch (error) {
|
|
265
|
+
spinner.fail('Failed to open browser. Please visit https://jules.google.com manually');
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
console.log(Colors.muted('\nSteps to get your Jules API key:'));
|
|
270
|
+
UIComponents.showList([
|
|
271
|
+
'Sign in to jules.google.com',
|
|
272
|
+
'Navigate to API settings or developer console',
|
|
273
|
+
'Generate a new API key',
|
|
274
|
+
'Copy the key and paste it below'
|
|
275
|
+
]);
|
|
276
|
+
|
|
277
|
+
let attempts = 0;
|
|
278
|
+
const maxAttempts = 3;
|
|
279
|
+
|
|
280
|
+
while (attempts < maxAttempts) {
|
|
281
|
+
const keyPrompt = await inquirer.prompt([
|
|
282
|
+
{
|
|
283
|
+
type: 'password',
|
|
284
|
+
name: 'apiKey',
|
|
285
|
+
message: 'Enter your Jules API key:',
|
|
286
|
+
mask: '*',
|
|
287
|
+
validate: (input: string) => {
|
|
288
|
+
const validation = ApiKeyManager.validateJulesApiKey(input);
|
|
289
|
+
return validation.valid || validation.error!;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
]);
|
|
293
|
+
|
|
294
|
+
// Test the API key
|
|
295
|
+
const spinner = UIComponents.createSpinner('Testing Jules API key...');
|
|
296
|
+
spinner.start();
|
|
297
|
+
|
|
298
|
+
const testResult = await ApiKeyManager.testJulesApiKey(keyPrompt.apiKey);
|
|
299
|
+
|
|
300
|
+
if (testResult.success) {
|
|
301
|
+
spinner.succeed('Jules API key validated successfully');
|
|
302
|
+
|
|
303
|
+
// Guide through GitHub app installation
|
|
304
|
+
await this.setupJulesGitHubIntegration();
|
|
305
|
+
|
|
306
|
+
return keyPrompt.apiKey;
|
|
307
|
+
} else {
|
|
308
|
+
spinner.fail(`Failed to validate Jules API key: ${testResult.error}`);
|
|
309
|
+
attempts++;
|
|
310
|
+
|
|
311
|
+
if (attempts < maxAttempts) {
|
|
312
|
+
const retry = await inquirer.prompt([
|
|
313
|
+
{
|
|
314
|
+
type: 'confirm',
|
|
315
|
+
name: 'retry',
|
|
316
|
+
message: `Would you like to try again? (${maxAttempts - attempts} attempts remaining)`,
|
|
317
|
+
default: true
|
|
318
|
+
}
|
|
319
|
+
]);
|
|
320
|
+
|
|
321
|
+
if (!retry.retry) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
UIComponents.showWarningBox('Jules API key setup incomplete. You can configure it later with: base config api-keys');
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Guide user through Jules GitHub app installation
|
|
334
|
+
*/
|
|
335
|
+
static async setupJulesGitHubIntegration(): Promise<void> {
|
|
336
|
+
console.log(Colors.info('\n🔗 Setting up Jules GitHub integration'));
|
|
337
|
+
|
|
338
|
+
const setupGitHub = await inquirer.prompt([
|
|
339
|
+
{
|
|
340
|
+
type: 'confirm',
|
|
341
|
+
name: 'setup',
|
|
342
|
+
message: 'Jules needs access to your GitHub repository for code fixing. Set up now?',
|
|
343
|
+
default: true
|
|
344
|
+
}
|
|
345
|
+
]);
|
|
346
|
+
|
|
347
|
+
if (!setupGitHub.setup) {
|
|
348
|
+
UIComponents.showWarningBox('GitHub integration skipped. Jules will have limited functionality without repository access.');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const openGitHub = await inquirer.prompt([
|
|
353
|
+
{
|
|
354
|
+
type: 'confirm',
|
|
355
|
+
name: 'open',
|
|
356
|
+
message: 'Open GitHub to install the Jules app?',
|
|
357
|
+
default: true
|
|
358
|
+
}
|
|
359
|
+
]);
|
|
360
|
+
|
|
361
|
+
if (openGitHub.open) {
|
|
362
|
+
const spinner = UIComponents.createSpinner('Opening GitHub Apps...');
|
|
363
|
+
spinner.start();
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
await open('https://github.com/apps/jules-ai');
|
|
367
|
+
spinner.succeed('Opened Jules GitHub App page');
|
|
368
|
+
} catch (error) {
|
|
369
|
+
spinner.fail('Failed to open browser. Please visit https://github.com/apps/jules-ai manually');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
console.log(Colors.muted('\nSteps to install Jules GitHub app:'));
|
|
374
|
+
UIComponents.showList([
|
|
375
|
+
'Click "Install" on the Jules GitHub app page',
|
|
376
|
+
'Select the repositories you want Jules to access',
|
|
377
|
+
'Complete the installation process',
|
|
378
|
+
'Return here when done'
|
|
379
|
+
]);
|
|
380
|
+
|
|
381
|
+
await inquirer.prompt([
|
|
382
|
+
{
|
|
383
|
+
type: 'confirm',
|
|
384
|
+
name: 'completed',
|
|
385
|
+
message: 'Have you completed the GitHub app installation?',
|
|
386
|
+
default: false
|
|
387
|
+
}
|
|
388
|
+
]);
|
|
389
|
+
|
|
390
|
+
UIComponents.showSuccessBox('Jules GitHub integration setup complete!');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Set up Gemini API key with browser integration
|
|
395
|
+
*/
|
|
396
|
+
static async setupGeminiApiKey(): Promise<string | undefined> {
|
|
397
|
+
console.log(Colors.info('\n🧠 Setting up Gemini API for compatibility analysis'));
|
|
398
|
+
|
|
399
|
+
const openBrowser = await inquirer.prompt([
|
|
400
|
+
{
|
|
401
|
+
type: 'confirm',
|
|
402
|
+
name: 'open',
|
|
403
|
+
message: 'Open aistudio.google.com to get your API key?',
|
|
404
|
+
default: true
|
|
405
|
+
}
|
|
406
|
+
]);
|
|
407
|
+
|
|
408
|
+
if (openBrowser.open) {
|
|
409
|
+
const spinner = UIComponents.createSpinner('Opening AI Studio...');
|
|
410
|
+
spinner.start();
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
await open('https://aistudio.google.com/app/apikey');
|
|
414
|
+
spinner.succeed('Opened AI Studio');
|
|
415
|
+
} catch (error) {
|
|
416
|
+
spinner.fail('Failed to open browser. Please visit https://aistudio.google.com/app/apikey manually');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
console.log(Colors.muted('\nSteps to get your Gemini API key:'));
|
|
421
|
+
UIComponents.showList([
|
|
422
|
+
'Sign in to aistudio.google.com',
|
|
423
|
+
'Click "Get API key" or "Create API key"',
|
|
424
|
+
'Copy the generated key',
|
|
425
|
+
'Paste it below'
|
|
426
|
+
]);
|
|
427
|
+
|
|
428
|
+
let attempts = 0;
|
|
429
|
+
const maxAttempts = 3;
|
|
430
|
+
|
|
431
|
+
while (attempts < maxAttempts) {
|
|
432
|
+
const keyPrompt = await inquirer.prompt([
|
|
433
|
+
{
|
|
434
|
+
type: 'password',
|
|
435
|
+
name: 'apiKey',
|
|
436
|
+
message: 'Enter your Gemini API key:',
|
|
437
|
+
mask: '*',
|
|
438
|
+
validate: (input: string) => {
|
|
439
|
+
const validation = ApiKeyManager.validateGeminiApiKey(input);
|
|
440
|
+
return validation.valid || validation.error!;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
]);
|
|
444
|
+
|
|
445
|
+
// Test the API key
|
|
446
|
+
const spinner = UIComponents.createSpinner('Testing Gemini API key...');
|
|
447
|
+
spinner.start();
|
|
448
|
+
|
|
449
|
+
const testResult = await ApiKeyManager.testGeminiApiKey(keyPrompt.apiKey);
|
|
450
|
+
|
|
451
|
+
if (testResult.success) {
|
|
452
|
+
spinner.succeed('Gemini API key validated successfully');
|
|
453
|
+
return keyPrompt.apiKey;
|
|
454
|
+
} else {
|
|
455
|
+
spinner.fail(`Failed to validate Gemini API key: ${testResult.error}`);
|
|
456
|
+
attempts++;
|
|
457
|
+
|
|
458
|
+
if (attempts < maxAttempts) {
|
|
459
|
+
const retry = await inquirer.prompt([
|
|
460
|
+
{
|
|
461
|
+
type: 'confirm',
|
|
462
|
+
name: 'retry',
|
|
463
|
+
message: `Would you like to try again? (${maxAttempts - attempts} attempts remaining)`,
|
|
464
|
+
default: true
|
|
465
|
+
}
|
|
466
|
+
]);
|
|
467
|
+
|
|
468
|
+
if (!retry.retry) {
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
UIComponents.showWarningBox('Gemini API key setup incomplete. You can configure it later with: base config api-keys');
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Prompt for fix approval with preview
|
|
481
|
+
*/
|
|
482
|
+
static async confirmFix(fixPreview: string, fileName: string): Promise<boolean> {
|
|
483
|
+
console.log(Colors.highlight(`\n📝 Proposed fix for ${fileName}:`));
|
|
484
|
+
console.log(Colors.muted('─'.repeat(50)));
|
|
485
|
+
console.log(fixPreview);
|
|
486
|
+
console.log(Colors.muted('─'.repeat(50)));
|
|
487
|
+
|
|
488
|
+
const answer = await inquirer.prompt([
|
|
489
|
+
{
|
|
490
|
+
type: 'list',
|
|
491
|
+
name: 'action',
|
|
492
|
+
message: 'What would you like to do?',
|
|
493
|
+
choices: [
|
|
494
|
+
{
|
|
495
|
+
name: '✅ Apply this fix',
|
|
496
|
+
value: 'apply'
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
name: '❌ Skip this fix',
|
|
500
|
+
value: 'skip'
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
name: '👀 Show more details',
|
|
504
|
+
value: 'details'
|
|
505
|
+
}
|
|
506
|
+
],
|
|
507
|
+
default: 'apply'
|
|
508
|
+
}
|
|
509
|
+
]);
|
|
510
|
+
|
|
511
|
+
if (answer.action === 'details') {
|
|
512
|
+
// Show more details and ask again
|
|
513
|
+
console.log(Colors.info('\nFix details:'));
|
|
514
|
+
UIComponents.showList([
|
|
515
|
+
'This fix adds progressive enhancement',
|
|
516
|
+
'Original functionality is preserved',
|
|
517
|
+
'Fallbacks are added for older browsers',
|
|
518
|
+
'Code follows best practices'
|
|
519
|
+
]);
|
|
520
|
+
|
|
521
|
+
return this.confirmFix(fixPreview, fileName);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return answer.action === 'apply';
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Show progress for batch operations
|
|
529
|
+
*/
|
|
530
|
+
static async showBatchProgress<T>(
|
|
531
|
+
items: T[],
|
|
532
|
+
operation: (item: T, index: number) => Promise<void>,
|
|
533
|
+
operationName: string
|
|
534
|
+
): Promise<void> {
|
|
535
|
+
console.log(Colors.primary(`\n${operationName}...`));
|
|
536
|
+
|
|
537
|
+
for (let i = 0; i < items.length; i++) {
|
|
538
|
+
UIComponents.showProgress(i, items.length, `Processing ${i + 1}/${items.length}`);
|
|
539
|
+
await operation(items[i]!, i);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
UIComponents.showProgress(items.length, items.length, 'Complete');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Handle graceful error display with next steps
|
|
547
|
+
*/
|
|
548
|
+
static async handleError(error: Error, context: string): Promise<void> {
|
|
549
|
+
UIComponents.showErrorBox(`${context}: ${error.message}`);
|
|
550
|
+
|
|
551
|
+
console.log(Colors.muted('\nPossible solutions:'));
|
|
552
|
+
|
|
553
|
+
if (error.message.includes('API key')) {
|
|
554
|
+
UIComponents.showList([
|
|
555
|
+
'Check your API key configuration: base config show',
|
|
556
|
+
'Reconfigure API keys: base config api-keys',
|
|
557
|
+
'Verify API key permissions and quotas'
|
|
558
|
+
]);
|
|
559
|
+
} else if (error.message.includes('network') || error.message.includes('fetch')) {
|
|
560
|
+
UIComponents.showList([
|
|
561
|
+
'Check your internet connection',
|
|
562
|
+
'Verify firewall settings',
|
|
563
|
+
'Try again in a few moments'
|
|
564
|
+
]);
|
|
565
|
+
} else if (error.message.includes('file') || error.message.includes('permission')) {
|
|
566
|
+
UIComponents.showList([
|
|
567
|
+
'Check file permissions',
|
|
568
|
+
'Ensure the file exists and is readable',
|
|
569
|
+
'Try running with appropriate permissions'
|
|
570
|
+
]);
|
|
571
|
+
} else {
|
|
572
|
+
UIComponents.showList([
|
|
573
|
+
'Check the BaseGuard documentation',
|
|
574
|
+
'Report this issue on GitHub',
|
|
575
|
+
'Try running with --verbose for more details'
|
|
576
|
+
]);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
const continuePrompt = await inquirer.prompt([
|
|
580
|
+
{
|
|
581
|
+
type: 'confirm',
|
|
582
|
+
name: 'continue',
|
|
583
|
+
message: 'Would you like to continue anyway?',
|
|
584
|
+
default: false
|
|
585
|
+
}
|
|
586
|
+
]);
|
|
587
|
+
|
|
588
|
+
if (!continuePrompt.continue) {
|
|
589
|
+
process.exit(1);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Prompt for configuration updates
|
|
595
|
+
*/
|
|
596
|
+
static async promptConfigUpdate(currentConfig: any): Promise<any> {
|
|
597
|
+
UIComponents.showSectionHeader('Update Configuration');
|
|
598
|
+
|
|
599
|
+
const choices = [
|
|
600
|
+
{
|
|
601
|
+
name: 'Browser targets',
|
|
602
|
+
value: 'targets'
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
name: 'API keys',
|
|
606
|
+
value: 'apiKeys'
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: 'Git automation settings',
|
|
610
|
+
value: 'automation'
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
name: 'View current configuration',
|
|
614
|
+
value: 'view'
|
|
615
|
+
}
|
|
616
|
+
];
|
|
617
|
+
|
|
618
|
+
const answer = await inquirer.prompt([
|
|
619
|
+
{
|
|
620
|
+
type: 'list',
|
|
621
|
+
name: 'section',
|
|
622
|
+
message: 'What would you like to update?',
|
|
623
|
+
choices
|
|
624
|
+
}
|
|
625
|
+
]);
|
|
626
|
+
|
|
627
|
+
switch (answer.section) {
|
|
628
|
+
case 'targets':
|
|
629
|
+
return { targets: await this.promptCustomTargets() };
|
|
630
|
+
case 'apiKeys':
|
|
631
|
+
return { apiKeys: await this.setupApiKeys() };
|
|
632
|
+
case 'automation':
|
|
633
|
+
return { automation: await this.promptAutomationSettings() };
|
|
634
|
+
case 'view':
|
|
635
|
+
console.log(Colors.info('\nCurrent configuration:'));
|
|
636
|
+
console.log(JSON.stringify(currentConfig, null, 2));
|
|
637
|
+
return {};
|
|
638
|
+
default:
|
|
639
|
+
return {};
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Prompt for automation settings
|
|
645
|
+
*/
|
|
646
|
+
static async promptAutomationSettings(): Promise<any> {
|
|
647
|
+
return await inquirer.prompt([
|
|
648
|
+
{
|
|
649
|
+
type: 'confirm',
|
|
650
|
+
name: 'enabled',
|
|
651
|
+
message: 'Enable git automation?',
|
|
652
|
+
default: true
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
type: 'list',
|
|
656
|
+
name: 'trigger',
|
|
657
|
+
message: 'When should BaseGuard run?',
|
|
658
|
+
choices: [
|
|
659
|
+
{ name: 'Before each commit (pre-commit)', value: 'pre-commit' },
|
|
660
|
+
{ name: 'Before each push (pre-push)', value: 'pre-push' }
|
|
661
|
+
],
|
|
662
|
+
default: 'pre-commit',
|
|
663
|
+
when: (answers) => answers.enabled
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
type: 'confirm',
|
|
667
|
+
name: 'autoAnalyze',
|
|
668
|
+
message: 'Automatically analyze violations with AI?',
|
|
669
|
+
default: true,
|
|
670
|
+
when: (answers) => answers.enabled
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
type: 'confirm',
|
|
674
|
+
name: 'autoFix',
|
|
675
|
+
message: 'Automatically fix violations when possible?',
|
|
676
|
+
default: false,
|
|
677
|
+
when: (answers) => answers.enabled
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
type: 'confirm',
|
|
681
|
+
name: 'blockCommit',
|
|
682
|
+
message: 'Block commits when violations are found?',
|
|
683
|
+
default: true,
|
|
684
|
+
when: (answers) => answers.enabled
|
|
685
|
+
}
|
|
686
|
+
]);
|
|
687
|
+
}
|
|
688
|
+
}
|