clevermation-cli 0.3.2
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/.github/workflows/publish.yml +46 -0
- package/CLAUDE.md +167 -0
- package/README.md +211 -0
- package/bin/cl +2 -0
- package/bin/clever +2 -0
- package/bun.lock +361 -0
- package/package.json +43 -0
- package/scripts/setup-team-member.sh +43 -0
- package/src/commands/auth.ts +302 -0
- package/src/commands/config.ts +174 -0
- package/src/commands/doctor.ts +15 -0
- package/src/commands/explain.ts +113 -0
- package/src/commands/init.ts +429 -0
- package/src/commands/open.ts +104 -0
- package/src/commands/sync.ts +181 -0
- package/src/commands/update.ts +90 -0
- package/src/index.ts +44 -0
- package/src/types/config.ts +90 -0
- package/src/utils/auto-update.ts +169 -0
- package/src/utils/config.ts +85 -0
- package/src/utils/logger.ts +49 -0
- package/src/utils/prerequisites.ts +228 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { select, confirm, input, password } from '@inquirer/prompts';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import {
|
|
8
|
+
checkGitHubAuth,
|
|
9
|
+
checkSupabaseAuth,
|
|
10
|
+
} from '../utils/prerequisites.js';
|
|
11
|
+
import { loadGlobalConfig, saveGlobalConfig } from '../utils/config.js';
|
|
12
|
+
|
|
13
|
+
type ServiceType = 'github' | 'supabase' | 'n8n' | 'elevenlabs' | 'all';
|
|
14
|
+
|
|
15
|
+
export function createAuthCommand(): Command {
|
|
16
|
+
const cmd = new Command('auth').description('Authentifizierung für Services verwalten');
|
|
17
|
+
|
|
18
|
+
cmd
|
|
19
|
+
.command('status')
|
|
20
|
+
.description('Zeige Auth-Status aller Services')
|
|
21
|
+
.action(async () => {
|
|
22
|
+
await showAuthStatus();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
cmd
|
|
26
|
+
.command('login [service]')
|
|
27
|
+
.description('Bei einem Service anmelden')
|
|
28
|
+
.action(async (service?: string) => {
|
|
29
|
+
await loginService(service as ServiceType);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
cmd
|
|
33
|
+
.command('logout [service]')
|
|
34
|
+
.description('Von einem Service abmelden')
|
|
35
|
+
.action(async (service?: string) => {
|
|
36
|
+
await logoutService(service as ServiceType);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Default action wenn nur `cl auth` aufgerufen wird
|
|
40
|
+
cmd.action(async () => {
|
|
41
|
+
await showAuthStatus();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return cmd;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function showAuthStatus(): Promise<void> {
|
|
48
|
+
logger.title('Authentifizierung');
|
|
49
|
+
|
|
50
|
+
const spinner = ora('Prüfe Auth-Status...').start();
|
|
51
|
+
|
|
52
|
+
// GitHub
|
|
53
|
+
const ghAuth = await checkGitHubAuth();
|
|
54
|
+
|
|
55
|
+
// Supabase
|
|
56
|
+
const supabaseAuth = await checkSupabaseAuth();
|
|
57
|
+
|
|
58
|
+
// N8N & ElevenLabs aus Config
|
|
59
|
+
const config = await loadGlobalConfig();
|
|
60
|
+
|
|
61
|
+
spinner.stop();
|
|
62
|
+
|
|
63
|
+
console.log(chalk.bold(' Services:\n'));
|
|
64
|
+
|
|
65
|
+
// GitHub
|
|
66
|
+
if (ghAuth.authenticated) {
|
|
67
|
+
console.log(chalk.green(' ✓'), 'GitHub', chalk.dim(`(@${ghAuth.username})`));
|
|
68
|
+
} else {
|
|
69
|
+
console.log(chalk.red(' ✗'), 'GitHub', chalk.dim('(nicht angemeldet)'));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Supabase
|
|
73
|
+
if (supabaseAuth) {
|
|
74
|
+
console.log(chalk.green(' ✓'), 'Supabase');
|
|
75
|
+
} else {
|
|
76
|
+
console.log(chalk.yellow(' ○'), 'Supabase', chalk.dim('(nicht angemeldet)'));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// N8N
|
|
80
|
+
if (config?.auth?.n8n?.apiKey) {
|
|
81
|
+
console.log(chalk.green(' ✓'), 'N8N', chalk.dim(`(${config.auth.n8n.url})`));
|
|
82
|
+
} else {
|
|
83
|
+
console.log(chalk.yellow(' ○'), 'N8N', chalk.dim('(nicht konfiguriert)'));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ElevenLabs
|
|
87
|
+
if (config?.auth?.elevenlabs?.apiKey) {
|
|
88
|
+
console.log(chalk.green(' ✓'), 'ElevenLabs');
|
|
89
|
+
} else {
|
|
90
|
+
console.log(chalk.yellow(' ○'), 'ElevenLabs', chalk.dim('(nicht konfiguriert)'));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
logger.blank();
|
|
94
|
+
console.log(chalk.dim(' Anmelden: cl auth login <service>'));
|
|
95
|
+
console.log(chalk.dim(' Services: github, supabase, n8n, elevenlabs, all'));
|
|
96
|
+
logger.blank();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function loginService(service?: ServiceType): Promise<void> {
|
|
100
|
+
// Wenn kein Service angegeben, fragen
|
|
101
|
+
const selectedService =
|
|
102
|
+
service ||
|
|
103
|
+
((await select({
|
|
104
|
+
message: 'Bei welchem Service anmelden?',
|
|
105
|
+
choices: [
|
|
106
|
+
{ name: 'Alle Services', value: 'all' },
|
|
107
|
+
{ name: 'GitHub', value: 'github' },
|
|
108
|
+
{ name: 'Supabase', value: 'supabase' },
|
|
109
|
+
{ name: 'N8N', value: 'n8n' },
|
|
110
|
+
{ name: 'ElevenLabs', value: 'elevenlabs' },
|
|
111
|
+
],
|
|
112
|
+
})) as ServiceType);
|
|
113
|
+
|
|
114
|
+
if (selectedService === 'all') {
|
|
115
|
+
await loginGitHub();
|
|
116
|
+
await loginSupabase();
|
|
117
|
+
await loginN8N();
|
|
118
|
+
await loginElevenLabs();
|
|
119
|
+
} else {
|
|
120
|
+
switch (selectedService) {
|
|
121
|
+
case 'github':
|
|
122
|
+
await loginGitHub();
|
|
123
|
+
break;
|
|
124
|
+
case 'supabase':
|
|
125
|
+
await loginSupabase();
|
|
126
|
+
break;
|
|
127
|
+
case 'n8n':
|
|
128
|
+
await loginN8N();
|
|
129
|
+
break;
|
|
130
|
+
case 'elevenlabs':
|
|
131
|
+
await loginElevenLabs();
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function loginGitHub(): Promise<void> {
|
|
138
|
+
logger.info('GitHub Login');
|
|
139
|
+
|
|
140
|
+
const ghAuth = await checkGitHubAuth();
|
|
141
|
+
if (ghAuth.authenticated) {
|
|
142
|
+
logger.success(`Bereits angemeldet als @${ghAuth.username}`);
|
|
143
|
+
|
|
144
|
+
const reauth = await confirm({
|
|
145
|
+
message: 'Erneut anmelden?',
|
|
146
|
+
default: false,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!reauth) return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
await execa('gh', ['auth', 'login', '--web'], { stdio: 'inherit' });
|
|
154
|
+
|
|
155
|
+
// Setup git für GitHub Packages
|
|
156
|
+
await execa('gh', ['auth', 'setup-git']);
|
|
157
|
+
|
|
158
|
+
logger.success('GitHub Login erfolgreich');
|
|
159
|
+
} catch {
|
|
160
|
+
logger.error('GitHub Login fehlgeschlagen');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function loginSupabase(): Promise<void> {
|
|
165
|
+
logger.info('Supabase Login');
|
|
166
|
+
|
|
167
|
+
// Prüfe ob Supabase CLI installiert
|
|
168
|
+
try {
|
|
169
|
+
await execa('supabase', ['--version']);
|
|
170
|
+
} catch {
|
|
171
|
+
logger.warning('Supabase CLI nicht installiert');
|
|
172
|
+
logger.dim(' Installieren: brew install supabase/tap/supabase');
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
await execa('supabase', ['login'], { stdio: 'inherit' });
|
|
178
|
+
logger.success('Supabase Login erfolgreich');
|
|
179
|
+
} catch {
|
|
180
|
+
logger.error('Supabase Login fehlgeschlagen');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function loginN8N(): Promise<void> {
|
|
185
|
+
logger.info('N8N Konfiguration');
|
|
186
|
+
|
|
187
|
+
const url = await input({
|
|
188
|
+
message: 'N8N Instance URL',
|
|
189
|
+
default: 'https://n8n.clevermation.com',
|
|
190
|
+
validate: (v) => v.startsWith('http') || 'Muss eine gültige URL sein',
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const apiKey = await password({
|
|
194
|
+
message: 'N8N API Key',
|
|
195
|
+
validate: (v) => v.length > 0 || 'API Key erforderlich',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Validiere Credentials
|
|
199
|
+
const spinner = ora('Validiere N8N Credentials...').start();
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
const response = await fetch(`${url}/api/v1/workflows`, {
|
|
203
|
+
headers: { 'X-N8N-API-KEY': apiKey },
|
|
204
|
+
signal: AbortSignal.timeout(10000),
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (response.ok) {
|
|
208
|
+
spinner.succeed('N8N Credentials gültig');
|
|
209
|
+
|
|
210
|
+
// Speichere in Config
|
|
211
|
+
const config = (await loadGlobalConfig()) || {
|
|
212
|
+
version: '1.0.0',
|
|
213
|
+
auth: {},
|
|
214
|
+
preferences: {},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
config.auth = config.auth || {};
|
|
218
|
+
config.auth.n8n = { url, apiKey };
|
|
219
|
+
|
|
220
|
+
await saveGlobalConfig(config);
|
|
221
|
+
logger.success('N8N konfiguriert');
|
|
222
|
+
} else {
|
|
223
|
+
spinner.fail('Ungültige N8N Credentials');
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
spinner.fail('Verbindung zu N8N fehlgeschlagen');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function loginElevenLabs(): Promise<void> {
|
|
231
|
+
logger.info('ElevenLabs Konfiguration');
|
|
232
|
+
|
|
233
|
+
const apiKey = await password({
|
|
234
|
+
message: 'ElevenLabs API Key',
|
|
235
|
+
validate: (v) => v.length > 0 || 'API Key erforderlich',
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Validiere API Key
|
|
239
|
+
const spinner = ora('Validiere ElevenLabs API Key...').start();
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const response = await fetch('https://api.elevenlabs.io/v1/user', {
|
|
243
|
+
headers: { 'xi-api-key': apiKey },
|
|
244
|
+
signal: AbortSignal.timeout(10000),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (response.ok) {
|
|
248
|
+
spinner.succeed('ElevenLabs API Key gültig');
|
|
249
|
+
|
|
250
|
+
// Speichere in Config
|
|
251
|
+
const config = (await loadGlobalConfig()) || {
|
|
252
|
+
version: '1.0.0',
|
|
253
|
+
auth: {},
|
|
254
|
+
preferences: {},
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
config.auth = config.auth || {};
|
|
258
|
+
config.auth.elevenlabs = { apiKey };
|
|
259
|
+
|
|
260
|
+
await saveGlobalConfig(config);
|
|
261
|
+
logger.success('ElevenLabs konfiguriert');
|
|
262
|
+
} else {
|
|
263
|
+
spinner.fail('Ungültiger ElevenLabs API Key');
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
spinner.fail('Validierung fehlgeschlagen');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function logoutService(service?: ServiceType): Promise<void> {
|
|
271
|
+
const selectedService =
|
|
272
|
+
service ||
|
|
273
|
+
((await select({
|
|
274
|
+
message: 'Von welchem Service abmelden?',
|
|
275
|
+
choices: [
|
|
276
|
+
{ name: 'GitHub', value: 'github' },
|
|
277
|
+
{ name: 'Supabase', value: 'supabase' },
|
|
278
|
+
{ name: 'N8N', value: 'n8n' },
|
|
279
|
+
{ name: 'ElevenLabs', value: 'elevenlabs' },
|
|
280
|
+
],
|
|
281
|
+
})) as ServiceType);
|
|
282
|
+
|
|
283
|
+
switch (selectedService) {
|
|
284
|
+
case 'github':
|
|
285
|
+
await execa('gh', ['auth', 'logout'], { stdio: 'inherit' });
|
|
286
|
+
break;
|
|
287
|
+
case 'supabase':
|
|
288
|
+
// Supabase hat kein logout command
|
|
289
|
+
logger.info('Supabase: Lösche Token manuell in ~/.supabase');
|
|
290
|
+
break;
|
|
291
|
+
case 'n8n':
|
|
292
|
+
case 'elevenlabs': {
|
|
293
|
+
const config = await loadGlobalConfig();
|
|
294
|
+
if (config?.auth) {
|
|
295
|
+
delete config.auth[selectedService];
|
|
296
|
+
await saveGlobalConfig(config);
|
|
297
|
+
logger.success(`${selectedService} Credentials entfernt`);
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { select, input, confirm } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
import {
|
|
6
|
+
loadProjectConfig,
|
|
7
|
+
loadGlobalConfig,
|
|
8
|
+
saveGlobalConfig,
|
|
9
|
+
} from '../utils/config.js';
|
|
10
|
+
|
|
11
|
+
export function createConfigCommand(): Command {
|
|
12
|
+
const cmd = new Command('config').description(
|
|
13
|
+
'Zeige und bearbeite Projektkonfiguration'
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
cmd
|
|
17
|
+
.command('show')
|
|
18
|
+
.description('Zeige aktuelle Konfiguration')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
await showConfig();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
cmd
|
|
24
|
+
.command('auth')
|
|
25
|
+
.description('Verwalte Service-Authentifizierung')
|
|
26
|
+
.action(async () => {
|
|
27
|
+
await manageAuth();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Default: show
|
|
31
|
+
cmd.action(async () => {
|
|
32
|
+
await showConfig();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return cmd;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function showConfig() {
|
|
39
|
+
const projectConfig = await loadProjectConfig();
|
|
40
|
+
const globalConfig = await loadGlobalConfig();
|
|
41
|
+
|
|
42
|
+
logger.title('Konfiguration');
|
|
43
|
+
|
|
44
|
+
if (projectConfig) {
|
|
45
|
+
logger.info('Projekt:');
|
|
46
|
+
logger.dim(` Name: ${projectConfig.project.name}`);
|
|
47
|
+
logger.dim(` Typ: ${projectConfig.project.type}`);
|
|
48
|
+
if (projectConfig.project.customer) {
|
|
49
|
+
logger.dim(` Kunde: ${projectConfig.project.customer}`);
|
|
50
|
+
}
|
|
51
|
+
logger.dim(` Repo: ${projectConfig.services.github.org}/${projectConfig.services.github.repo}`);
|
|
52
|
+
logger.blank();
|
|
53
|
+
|
|
54
|
+
logger.info('Services:');
|
|
55
|
+
logger.dim(` GitHub: ✓ aktiviert`);
|
|
56
|
+
logger.dim(
|
|
57
|
+
` Supabase: ${projectConfig.services.supabase?.enabled ? '✓ aktiviert' : '✗ deaktiviert'}`
|
|
58
|
+
);
|
|
59
|
+
logger.dim(
|
|
60
|
+
` N8N: ${projectConfig.services.n8n?.enabled ? '✓ aktiviert' : '✗ deaktiviert'}`
|
|
61
|
+
);
|
|
62
|
+
logger.dim(
|
|
63
|
+
` ElevenLabs: ${projectConfig.services.elevenlabs?.enabled ? '✓ aktiviert' : '✗ deaktiviert'}`
|
|
64
|
+
);
|
|
65
|
+
logger.blank();
|
|
66
|
+
|
|
67
|
+
logger.info('Claude Code:');
|
|
68
|
+
logger.dim(` Model: ${projectConfig.claudeCode.model}`);
|
|
69
|
+
logger.dim(` Plugins: ${projectConfig.claudeCode.plugins.join(', ')}`);
|
|
70
|
+
} else {
|
|
71
|
+
logger.warning('Kein Projekt gefunden. Führe "cl init" aus.');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
logger.blank();
|
|
75
|
+
logger.info('Global:');
|
|
76
|
+
logger.dim(` Standard Model: ${globalConfig.preferences.defaultModel}`);
|
|
77
|
+
logger.dim(` Auto-Update: ${globalConfig.preferences.autoUpdate ? 'Ja' : 'Nein'}`);
|
|
78
|
+
logger.blank();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function manageAuth() {
|
|
82
|
+
logger.title('Service Authentifizierung');
|
|
83
|
+
|
|
84
|
+
const service = await select({
|
|
85
|
+
message: 'Wähle Service zur Konfiguration',
|
|
86
|
+
choices: [
|
|
87
|
+
{ name: 'N8N', value: 'n8n' },
|
|
88
|
+
{ name: 'ElevenLabs', value: 'elevenlabs' },
|
|
89
|
+
{ name: 'Supabase (via CLI)', value: 'supabase' },
|
|
90
|
+
{ name: 'GitHub (via CLI)', value: 'github' },
|
|
91
|
+
],
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const globalConfig = await loadGlobalConfig();
|
|
95
|
+
|
|
96
|
+
switch (service) {
|
|
97
|
+
case 'n8n':
|
|
98
|
+
await authN8n(globalConfig);
|
|
99
|
+
break;
|
|
100
|
+
case 'elevenlabs':
|
|
101
|
+
await authElevenLabs(globalConfig);
|
|
102
|
+
break;
|
|
103
|
+
case 'supabase':
|
|
104
|
+
logger.info('Supabase verwendet die Supabase CLI zur Authentifizierung.');
|
|
105
|
+
logger.dim(' Führe aus: supabase login');
|
|
106
|
+
break;
|
|
107
|
+
case 'github':
|
|
108
|
+
logger.info('GitHub verwendet die GitHub CLI zur Authentifizierung.');
|
|
109
|
+
logger.dim(' Führe aus: gh auth login');
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function authN8n(globalConfig: Awaited<ReturnType<typeof loadGlobalConfig>>) {
|
|
115
|
+
const url = await input({
|
|
116
|
+
message: 'N8N Instance URL',
|
|
117
|
+
default: globalConfig.auth.n8n?.url || 'https://n8n.clevermation.com',
|
|
118
|
+
validate: (v) => v.startsWith('http') || 'Muss eine gültige URL sein',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const apiKey = await input({
|
|
122
|
+
message: 'N8N API Key',
|
|
123
|
+
validate: (v) => v.length > 0 || 'API Key erforderlich',
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// API Key validieren
|
|
127
|
+
logger.info('Validiere N8N Credentials...');
|
|
128
|
+
try {
|
|
129
|
+
const response = await fetch(`${url}/api/v1/workflows`, {
|
|
130
|
+
headers: { 'X-N8N-API-KEY': apiKey },
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
if (response.ok) {
|
|
134
|
+
logger.success('N8N Credentials gültig!');
|
|
135
|
+
|
|
136
|
+
// Speichern
|
|
137
|
+
globalConfig.auth.n8n = { url, apiKey };
|
|
138
|
+
await saveGlobalConfig(globalConfig);
|
|
139
|
+
logger.success('Credentials gespeichert.');
|
|
140
|
+
} else {
|
|
141
|
+
logger.error('Ungültige N8N Credentials.');
|
|
142
|
+
}
|
|
143
|
+
} catch {
|
|
144
|
+
logger.error('Verbindung zu N8N fehlgeschlagen.');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function authElevenLabs(globalConfig: Awaited<ReturnType<typeof loadGlobalConfig>>) {
|
|
149
|
+
const apiKey = await input({
|
|
150
|
+
message: 'ElevenLabs API Key',
|
|
151
|
+
validate: (v) => v.length > 0 || 'API Key erforderlich',
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// API Key validieren
|
|
155
|
+
logger.info('Validiere ElevenLabs API Key...');
|
|
156
|
+
try {
|
|
157
|
+
const response = await fetch('https://api.elevenlabs.io/v1/user', {
|
|
158
|
+
headers: { 'xi-api-key': apiKey },
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (response.ok) {
|
|
162
|
+
logger.success('ElevenLabs API Key gültig!');
|
|
163
|
+
|
|
164
|
+
// Speichern
|
|
165
|
+
globalConfig.auth.elevenlabs = { apiKey };
|
|
166
|
+
await saveGlobalConfig(globalConfig);
|
|
167
|
+
logger.success('API Key gespeichert.');
|
|
168
|
+
} else {
|
|
169
|
+
logger.error('Ungültiger ElevenLabs API Key.');
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
logger.error('Validierung fehlgeschlagen.');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { showPrerequisiteStatus } from '../utils/prerequisites.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
|
|
5
|
+
export function createDoctorCommand(): Command {
|
|
6
|
+
return new Command('doctor')
|
|
7
|
+
.description('Prüfe System-Voraussetzungen und zeige Probleme')
|
|
8
|
+
.action(async () => {
|
|
9
|
+
await showPrerequisiteStatus();
|
|
10
|
+
|
|
11
|
+
logger.blank();
|
|
12
|
+
logger.dim(' Bei Problemen: cl auth login');
|
|
13
|
+
logger.blank();
|
|
14
|
+
});
|
|
15
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { loadProjectConfig } from '../utils/config.js';
|
|
7
|
+
|
|
8
|
+
interface ExplainOptions {
|
|
9
|
+
verbose?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createExplainCommand(): Command {
|
|
13
|
+
return new Command('explain')
|
|
14
|
+
.description('Erkläre die aktuelle Projektstruktur')
|
|
15
|
+
.option('-v, --verbose', 'Zeige detaillierte Erklärung')
|
|
16
|
+
.action(async (options: ExplainOptions) => {
|
|
17
|
+
await explainProject(options.verbose ?? false);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function explainProject(verbose: boolean) {
|
|
22
|
+
const config = await loadProjectConfig();
|
|
23
|
+
|
|
24
|
+
logger.title('Projekt Übersicht');
|
|
25
|
+
|
|
26
|
+
if (!config) {
|
|
27
|
+
logger.error('Kein Clevermation Projekt gefunden.');
|
|
28
|
+
logger.info('Führe "cl init" aus, um ein Projekt zu erstellen.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Basis-Info
|
|
33
|
+
logger.info('Projekt:');
|
|
34
|
+
logger.dim(` Name: ${config.project.name}`);
|
|
35
|
+
logger.dim(
|
|
36
|
+
` Typ: ${config.project.type === 'customer' ? 'Kundenprojekt' : 'Internes Projekt'}`
|
|
37
|
+
);
|
|
38
|
+
if (config.project.customer) {
|
|
39
|
+
logger.dim(` Kunde: ${config.project.customer}`);
|
|
40
|
+
}
|
|
41
|
+
logger.blank();
|
|
42
|
+
|
|
43
|
+
// GitHub
|
|
44
|
+
logger.info('GitHub:');
|
|
45
|
+
logger.dim(` Repository: ${config.services.github.org}/${config.services.github.repo}`);
|
|
46
|
+
logger.dim(` Sichtbarkeit: Privat`);
|
|
47
|
+
logger.blank();
|
|
48
|
+
|
|
49
|
+
// Services
|
|
50
|
+
const enabledServices: string[] = ['GitHub'];
|
|
51
|
+
if (config.services.supabase?.enabled) enabledServices.push('Supabase');
|
|
52
|
+
if (config.services.n8n?.enabled) enabledServices.push('N8N');
|
|
53
|
+
if (config.services.elevenlabs?.enabled) enabledServices.push('ElevenLabs');
|
|
54
|
+
|
|
55
|
+
logger.info('Aktivierte Services:');
|
|
56
|
+
for (const service of enabledServices) {
|
|
57
|
+
logger.dim(` • ${service}`);
|
|
58
|
+
}
|
|
59
|
+
logger.blank();
|
|
60
|
+
|
|
61
|
+
// Claude Code
|
|
62
|
+
logger.info('Claude Code:');
|
|
63
|
+
logger.dim(` Standard Model: ${config.claudeCode.model}`);
|
|
64
|
+
logger.dim(` Marketplace: ${config.claudeCode.marketplace}`);
|
|
65
|
+
logger.blank();
|
|
66
|
+
|
|
67
|
+
logger.info('Installierte Plugins:');
|
|
68
|
+
for (const plugin of config.claudeCode.plugins) {
|
|
69
|
+
logger.dim(` • ${plugin}`);
|
|
70
|
+
}
|
|
71
|
+
logger.blank();
|
|
72
|
+
|
|
73
|
+
// Verbose: Verzeichnisstruktur
|
|
74
|
+
if (verbose) {
|
|
75
|
+
logger.info('Verzeichnisstruktur:');
|
|
76
|
+
await showDirectoryTree();
|
|
77
|
+
logger.blank();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Timestamps
|
|
81
|
+
logger.dim(`Erstellt: ${new Date(config.createdAt).toLocaleDateString('de-DE')}`);
|
|
82
|
+
logger.dim(`Aktualisiert: ${new Date(config.updatedAt).toLocaleDateString('de-DE')}`);
|
|
83
|
+
logger.blank();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function showDirectoryTree(dir: string = '.', prefix: string = ' '): Promise<void> {
|
|
87
|
+
const ignoreDirs = ['node_modules', '.git', 'dist', '.bun'];
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
91
|
+
const filteredEntries = entries.filter((e) => !ignoreDirs.includes(e.name));
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < filteredEntries.length; i++) {
|
|
94
|
+
const entry = filteredEntries[i];
|
|
95
|
+
if (!entry) continue;
|
|
96
|
+
|
|
97
|
+
const isLast = i === filteredEntries.length - 1;
|
|
98
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
99
|
+
|
|
100
|
+
if (entry.isDirectory()) {
|
|
101
|
+
console.log(chalk.dim(`${prefix}${connector}${chalk.blue(entry.name)}/`));
|
|
102
|
+
await showDirectoryTree(
|
|
103
|
+
path.join(dir, entry.name),
|
|
104
|
+
prefix + (isLast ? ' ' : '│ ')
|
|
105
|
+
);
|
|
106
|
+
} else {
|
|
107
|
+
console.log(chalk.dim(`${prefix}${connector}${entry.name}`));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
// Ignoriere Fehler
|
|
112
|
+
}
|
|
113
|
+
}
|