wiggum-cli 0.1.0 → 0.2.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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +92 -13
- package/dist/commands/init.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/init.ts +104 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAGL,KAAK,UAAU,EAEhB,MAAM,gBAAgB,CAAC;AAOxB,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgNrE"}
|
package/dist/commands/init.js
CHANGED
|
@@ -6,8 +6,11 @@ import { logger } from '../utils/logger.js';
|
|
|
6
6
|
import { Scanner, formatScanResult } from '../scanner/index.js';
|
|
7
7
|
import { Generator, formatGenerationResult } from '../generator/index.js';
|
|
8
8
|
import { AIEnhancer, formatAIAnalysis, } from '../ai/index.js';
|
|
9
|
+
import { hasApiKey, getApiKeyEnvVar, getAvailableProvider } from '../ai/providers.js';
|
|
9
10
|
import * as prompts from '@clack/prompts';
|
|
10
11
|
import pc from 'picocolors';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
11
14
|
/**
|
|
12
15
|
* Initialize Ralph in the current project
|
|
13
16
|
* Scans the project and generates configuration
|
|
@@ -36,23 +39,99 @@ export async function initCommand(options) {
|
|
|
36
39
|
console.log(pc.cyan('--- Scan Results ---'));
|
|
37
40
|
console.log(formatScanResult(scanResult));
|
|
38
41
|
console.log('');
|
|
39
|
-
// Step 2: AI Enhancement
|
|
42
|
+
// Step 2: AI Enhancement
|
|
40
43
|
let enhancedResult;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
let useAI = options.ai;
|
|
45
|
+
let provider = options.provider || 'anthropic';
|
|
46
|
+
// Ask about AI enhancement if not specified via --ai flag and not in --yes mode
|
|
47
|
+
if (!options.ai && !options.yes) {
|
|
48
|
+
const wantAI = await prompts.confirm({
|
|
49
|
+
message: 'Enable AI-enhanced analysis?',
|
|
50
|
+
initialValue: true,
|
|
47
51
|
});
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
if (prompts.isCancel(wantAI)) {
|
|
53
|
+
logger.info('Initialization cancelled');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
useAI = wantAI;
|
|
57
|
+
}
|
|
58
|
+
if (useAI) {
|
|
59
|
+
// Check if we have an API key for the selected provider or any provider
|
|
60
|
+
let hasKey = options.provider ? hasApiKey(options.provider) : !!getAvailableProvider();
|
|
61
|
+
// If no API key found, prompt for one
|
|
62
|
+
if (!hasKey) {
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(pc.yellow('No API key found for AI enhancement.'));
|
|
53
65
|
console.log('');
|
|
66
|
+
// Ask which provider to use
|
|
67
|
+
const providerChoice = await prompts.select({
|
|
68
|
+
message: 'Which AI provider would you like to use?',
|
|
69
|
+
options: [
|
|
70
|
+
{ value: 'anthropic', label: 'Anthropic (Claude)', hint: 'recommended' },
|
|
71
|
+
{ value: 'openai', label: 'OpenAI (GPT-4)' },
|
|
72
|
+
{ value: 'openrouter', label: 'OpenRouter' },
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
if (prompts.isCancel(providerChoice)) {
|
|
76
|
+
logger.info('Initialization cancelled');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
provider = providerChoice;
|
|
80
|
+
const envVar = getApiKeyEnvVar(provider);
|
|
81
|
+
// Prompt for API key
|
|
82
|
+
const apiKeyInput = await prompts.password({
|
|
83
|
+
message: `Enter your ${envVar}:`,
|
|
84
|
+
});
|
|
85
|
+
if (prompts.isCancel(apiKeyInput) || !apiKeyInput) {
|
|
86
|
+
logger.warn('No API key provided, skipping AI enhancement');
|
|
87
|
+
useAI = false;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// Set the API key in process.env for current session
|
|
91
|
+
process.env[envVar] = apiKeyInput;
|
|
92
|
+
hasKey = true;
|
|
93
|
+
// Offer to save to .env.local
|
|
94
|
+
const saveKey = await prompts.confirm({
|
|
95
|
+
message: 'Save API key to .env.local?',
|
|
96
|
+
initialValue: true,
|
|
97
|
+
});
|
|
98
|
+
if (!prompts.isCancel(saveKey) && saveKey) {
|
|
99
|
+
const envLocalPath = path.join(projectRoot, '.env.local');
|
|
100
|
+
let envContent = '';
|
|
101
|
+
// Read existing content if file exists
|
|
102
|
+
if (fs.existsSync(envLocalPath)) {
|
|
103
|
+
envContent = fs.readFileSync(envLocalPath, 'utf-8');
|
|
104
|
+
// Check if key already exists
|
|
105
|
+
const keyRegex = new RegExp(`^${envVar}=.*$`, 'm');
|
|
106
|
+
if (keyRegex.test(envContent)) {
|
|
107
|
+
// Replace existing key
|
|
108
|
+
envContent = envContent.replace(keyRegex, `${envVar}=${apiKeyInput}`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Append new key
|
|
112
|
+
envContent = envContent.trimEnd() + '\n' + `${envVar}=${apiKeyInput}\n`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
envContent = `${envVar}=${apiKeyInput}\n`;
|
|
117
|
+
}
|
|
118
|
+
fs.writeFileSync(envLocalPath, envContent);
|
|
119
|
+
logger.success(`API key saved to .env.local`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
54
122
|
}
|
|
55
|
-
else {
|
|
123
|
+
else if (!options.provider) {
|
|
124
|
+
// If we have a key but no provider specified, use the available one
|
|
125
|
+
provider = getAvailableProvider() || 'anthropic';
|
|
126
|
+
}
|
|
127
|
+
// Run AI enhancement if we have a key
|
|
128
|
+
if (useAI && hasKey) {
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(pc.cyan(`--- AI Enhancement (${provider}) ---`));
|
|
131
|
+
const aiEnhancer = new AIEnhancer({
|
|
132
|
+
provider,
|
|
133
|
+
verbose: true,
|
|
134
|
+
});
|
|
56
135
|
spinner.start('Running AI analysis...');
|
|
57
136
|
try {
|
|
58
137
|
enhancedResult = await aiEnhancer.enhance(scanResult);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAmB,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EACL,UAAU,EACV,gBAAgB,GAGjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -12,8 +12,11 @@ import {
|
|
|
12
12
|
type AIProvider,
|
|
13
13
|
type EnhancedScanResult,
|
|
14
14
|
} from '../ai/index.js';
|
|
15
|
+
import { hasApiKey, getApiKeyEnvVar, getAvailableProvider } from '../ai/providers.js';
|
|
15
16
|
import * as prompts from '@clack/prompts';
|
|
16
17
|
import pc from 'picocolors';
|
|
18
|
+
import fs from 'fs';
|
|
19
|
+
import path from 'path';
|
|
17
20
|
|
|
18
21
|
export interface InitOptions {
|
|
19
22
|
ai?: boolean;
|
|
@@ -54,25 +57,112 @@ export async function initCommand(options: InitOptions): Promise<void> {
|
|
|
54
57
|
console.log(formatScanResult(scanResult));
|
|
55
58
|
console.log('');
|
|
56
59
|
|
|
57
|
-
// Step 2: AI Enhancement
|
|
60
|
+
// Step 2: AI Enhancement
|
|
58
61
|
let enhancedResult: EnhancedScanResult | undefined;
|
|
62
|
+
let useAI = options.ai;
|
|
63
|
+
let provider: AIProvider = options.provider || 'anthropic';
|
|
59
64
|
|
|
60
|
-
if
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
provider,
|
|
66
|
-
verbose: true,
|
|
65
|
+
// Ask about AI enhancement if not specified via --ai flag and not in --yes mode
|
|
66
|
+
if (!options.ai && !options.yes) {
|
|
67
|
+
const wantAI = await prompts.confirm({
|
|
68
|
+
message: 'Enable AI-enhanced analysis?',
|
|
69
|
+
initialValue: true,
|
|
67
70
|
});
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
if (prompts.isCancel(wantAI)) {
|
|
73
|
+
logger.info('Initialization cancelled');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
useAI = wantAI;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (useAI) {
|
|
81
|
+
// Check if we have an API key for the selected provider or any provider
|
|
82
|
+
let hasKey = options.provider ? hasApiKey(options.provider) : !!getAvailableProvider();
|
|
83
|
+
|
|
84
|
+
// If no API key found, prompt for one
|
|
85
|
+
if (!hasKey) {
|
|
74
86
|
console.log('');
|
|
75
|
-
|
|
87
|
+
console.log(pc.yellow('No API key found for AI enhancement.'));
|
|
88
|
+
console.log('');
|
|
89
|
+
|
|
90
|
+
// Ask which provider to use
|
|
91
|
+
const providerChoice = await prompts.select({
|
|
92
|
+
message: 'Which AI provider would you like to use?',
|
|
93
|
+
options: [
|
|
94
|
+
{ value: 'anthropic', label: 'Anthropic (Claude)', hint: 'recommended' },
|
|
95
|
+
{ value: 'openai', label: 'OpenAI (GPT-4)' },
|
|
96
|
+
{ value: 'openrouter', label: 'OpenRouter' },
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (prompts.isCancel(providerChoice)) {
|
|
101
|
+
logger.info('Initialization cancelled');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
provider = providerChoice as AIProvider;
|
|
106
|
+
const envVar = getApiKeyEnvVar(provider);
|
|
107
|
+
|
|
108
|
+
// Prompt for API key
|
|
109
|
+
const apiKeyInput = await prompts.password({
|
|
110
|
+
message: `Enter your ${envVar}:`,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (prompts.isCancel(apiKeyInput) || !apiKeyInput) {
|
|
114
|
+
logger.warn('No API key provided, skipping AI enhancement');
|
|
115
|
+
useAI = false;
|
|
116
|
+
} else {
|
|
117
|
+
// Set the API key in process.env for current session
|
|
118
|
+
process.env[envVar] = apiKeyInput;
|
|
119
|
+
hasKey = true;
|
|
120
|
+
|
|
121
|
+
// Offer to save to .env.local
|
|
122
|
+
const saveKey = await prompts.confirm({
|
|
123
|
+
message: 'Save API key to .env.local?',
|
|
124
|
+
initialValue: true,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (!prompts.isCancel(saveKey) && saveKey) {
|
|
128
|
+
const envLocalPath = path.join(projectRoot, '.env.local');
|
|
129
|
+
let envContent = '';
|
|
130
|
+
|
|
131
|
+
// Read existing content if file exists
|
|
132
|
+
if (fs.existsSync(envLocalPath)) {
|
|
133
|
+
envContent = fs.readFileSync(envLocalPath, 'utf-8');
|
|
134
|
+
// Check if key already exists
|
|
135
|
+
const keyRegex = new RegExp(`^${envVar}=.*$`, 'm');
|
|
136
|
+
if (keyRegex.test(envContent)) {
|
|
137
|
+
// Replace existing key
|
|
138
|
+
envContent = envContent.replace(keyRegex, `${envVar}=${apiKeyInput}`);
|
|
139
|
+
} else {
|
|
140
|
+
// Append new key
|
|
141
|
+
envContent = envContent.trimEnd() + '\n' + `${envVar}=${apiKeyInput}\n`;
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
envContent = `${envVar}=${apiKeyInput}\n`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fs.writeFileSync(envLocalPath, envContent);
|
|
148
|
+
logger.success(`API key saved to .env.local`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} else if (!options.provider) {
|
|
152
|
+
// If we have a key but no provider specified, use the available one
|
|
153
|
+
provider = getAvailableProvider() || 'anthropic';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Run AI enhancement if we have a key
|
|
157
|
+
if (useAI && hasKey) {
|
|
158
|
+
console.log('');
|
|
159
|
+
console.log(pc.cyan(`--- AI Enhancement (${provider}) ---`));
|
|
160
|
+
|
|
161
|
+
const aiEnhancer = new AIEnhancer({
|
|
162
|
+
provider,
|
|
163
|
+
verbose: true,
|
|
164
|
+
});
|
|
165
|
+
|
|
76
166
|
spinner.start('Running AI analysis...');
|
|
77
167
|
|
|
78
168
|
try {
|