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.
@@ -0,0 +1,228 @@
1
+ import { execa } from 'execa';
2
+ import ora from 'ora';
3
+ import chalk from 'chalk';
4
+ import { logger } from './logger.js';
5
+
6
+ interface Prerequisite {
7
+ name: string;
8
+ command: string;
9
+ versionFlag: string;
10
+ installCommand?: string;
11
+ installMessage?: string;
12
+ optional?: boolean;
13
+ }
14
+
15
+ const PREREQUISITES: Prerequisite[] = [
16
+ {
17
+ name: 'Bun',
18
+ command: 'bun',
19
+ versionFlag: '--version',
20
+ installCommand: 'curl -fsSL https://bun.sh/install | bash',
21
+ installMessage: 'Installiere Bun...',
22
+ },
23
+ {
24
+ name: 'Git',
25
+ command: 'git',
26
+ versionFlag: '--version',
27
+ installCommand: 'brew install git',
28
+ installMessage: 'Installiere Git...',
29
+ },
30
+ {
31
+ name: 'GitHub CLI',
32
+ command: 'gh',
33
+ versionFlag: '--version',
34
+ installCommand: 'brew install gh',
35
+ installMessage: 'Installiere GitHub CLI...',
36
+ },
37
+ {
38
+ name: 'Claude Code',
39
+ command: 'claude',
40
+ versionFlag: '--version',
41
+ installCommand: 'brew install claude',
42
+ installMessage: 'Installiere Claude Code...',
43
+ optional: true,
44
+ },
45
+ {
46
+ name: 'Supabase CLI',
47
+ command: 'supabase',
48
+ versionFlag: '--version',
49
+ installCommand: 'brew install supabase/tap/supabase',
50
+ installMessage: 'Installiere Supabase CLI...',
51
+ optional: true,
52
+ },
53
+ ];
54
+
55
+ interface CheckResult {
56
+ name: string;
57
+ installed: boolean;
58
+ version?: string;
59
+ optional: boolean;
60
+ }
61
+
62
+ /**
63
+ * Prüft alle Voraussetzungen und installiert fehlende automatisch.
64
+ * Läuft still im Hintergrund - User merkt nichts außer bei Fehlern.
65
+ */
66
+ export async function ensurePrerequisites(options?: {
67
+ verbose?: boolean;
68
+ requiredOnly?: boolean;
69
+ autoInstall?: boolean;
70
+ }): Promise<CheckResult[]> {
71
+ const { verbose = false, requiredOnly = false, autoInstall = true } = options || {};
72
+ const results: CheckResult[] = [];
73
+
74
+ const prereqs = requiredOnly
75
+ ? PREREQUISITES.filter((p) => !p.optional)
76
+ : PREREQUISITES;
77
+
78
+ for (const prereq of prereqs) {
79
+ const result = await checkPrerequisite(prereq);
80
+ results.push(result);
81
+
82
+ // Auto-Install wenn nicht vorhanden und nicht optional
83
+ if (!result.installed && autoInstall && prereq.installCommand && !prereq.optional) {
84
+ await installPrerequisite(prereq, verbose);
85
+ // Erneut prüfen
86
+ const recheck = await checkPrerequisite(prereq);
87
+ results[results.length - 1] = recheck;
88
+ }
89
+ }
90
+
91
+ return results;
92
+ }
93
+
94
+ async function checkPrerequisite(prereq: Prerequisite): Promise<CheckResult> {
95
+ try {
96
+ const { stdout } = await execa(prereq.command, [prereq.versionFlag], {
97
+ timeout: 5000,
98
+ reject: false,
99
+ });
100
+
101
+ // Version extrahieren (erstes Match von Zahlen mit Punkten)
102
+ const versionMatch = stdout.match(/\d+\.\d+(\.\d+)?/);
103
+ const version = versionMatch ? versionMatch[0] : undefined;
104
+
105
+ return {
106
+ name: prereq.name,
107
+ installed: true,
108
+ version,
109
+ optional: prereq.optional || false,
110
+ };
111
+ } catch {
112
+ return {
113
+ name: prereq.name,
114
+ installed: false,
115
+ optional: prereq.optional || false,
116
+ };
117
+ }
118
+ }
119
+
120
+ async function installPrerequisite(prereq: Prerequisite, verbose: boolean): Promise<boolean> {
121
+ if (!prereq.installCommand) return false;
122
+
123
+ const spinner = verbose
124
+ ? ora(prereq.installMessage || `Installiere ${prereq.name}...`).start()
125
+ : null;
126
+
127
+ try {
128
+ // Führe Install-Command aus
129
+ await execa('sh', ['-c', prereq.installCommand], {
130
+ timeout: 120000, // 2 Minuten Timeout
131
+ stdio: verbose ? 'inherit' : 'pipe',
132
+ });
133
+
134
+ spinner?.succeed(`${prereq.name} installiert`);
135
+ return true;
136
+ } catch (error) {
137
+ spinner?.fail(`${prereq.name} Installation fehlgeschlagen`);
138
+ return false;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Zeigt den Status aller Voraussetzungen an (für cl doctor).
144
+ */
145
+ export async function showPrerequisiteStatus(): Promise<void> {
146
+ logger.title('System-Check');
147
+
148
+ const results = await ensurePrerequisites({ autoInstall: false });
149
+
150
+ for (const result of results) {
151
+ if (result.installed) {
152
+ const version = result.version ? chalk.dim(` v${result.version}`) : '';
153
+ console.log(chalk.green(' ✓'), result.name + version);
154
+ } else if (result.optional) {
155
+ console.log(chalk.yellow(' ○'), result.name, chalk.dim('(optional, nicht installiert)'));
156
+ } else {
157
+ console.log(chalk.red(' ✗'), result.name, chalk.dim('(nicht installiert)'));
158
+ }
159
+ }
160
+
161
+ logger.blank();
162
+
163
+ // GitHub Auth Status prüfen
164
+ const ghAuthStatus = await checkGitHubAuth();
165
+ if (ghAuthStatus.authenticated) {
166
+ console.log(chalk.green(' ✓'), `GitHub authentifiziert als ${ghAuthStatus.username}`);
167
+ } else {
168
+ console.log(chalk.red(' ✗'), 'GitHub nicht authentifiziert');
169
+ console.log(chalk.dim(' → gh auth login'));
170
+ }
171
+
172
+ // Supabase Auth Status prüfen
173
+ const supabaseAuthStatus = await checkSupabaseAuth();
174
+ if (supabaseAuthStatus) {
175
+ console.log(chalk.green(' ✓'), 'Supabase authentifiziert');
176
+ } else {
177
+ console.log(chalk.yellow(' ○'), 'Supabase nicht authentifiziert', chalk.dim('(optional)'));
178
+ }
179
+
180
+ logger.blank();
181
+ }
182
+
183
+ /**
184
+ * Prüft GitHub Authentifizierung.
185
+ */
186
+ export async function checkGitHubAuth(): Promise<{ authenticated: boolean; username?: string }> {
187
+ try {
188
+ const { stdout } = await execa('gh', ['auth', 'status'], { reject: false });
189
+ const usernameMatch = stdout.match(/Logged in to .+ account (\w+)/);
190
+ return {
191
+ authenticated: stdout.includes('Logged in'),
192
+ username: usernameMatch?.[1],
193
+ };
194
+ } catch {
195
+ return { authenticated: false };
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Prüft Supabase Authentifizierung.
201
+ */
202
+ export async function checkSupabaseAuth(): Promise<boolean> {
203
+ try {
204
+ const result = await execa('supabase', ['projects', 'list'], {
205
+ timeout: 10000,
206
+ reject: false,
207
+ });
208
+ return result.exitCode === 0;
209
+ } catch {
210
+ return false;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Stille Hintergrund-Prüfung beim CLI-Start.
216
+ * Installiert fehlende Required-Tools automatisch.
217
+ */
218
+ export async function silentPrerequisiteCheck(): Promise<void> {
219
+ try {
220
+ await ensurePrerequisites({
221
+ verbose: false,
222
+ requiredOnly: true,
223
+ autoInstall: true,
224
+ });
225
+ } catch {
226
+ // Fehler ignorieren - soll CLI nicht blockieren
227
+ }
228
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }