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,90 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { confirm } from '@inquirer/prompts';
|
|
3
|
+
import { execa } from 'execa';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
|
|
7
|
+
interface UpdateOptions {
|
|
8
|
+
cliOnly?: boolean;
|
|
9
|
+
pluginsOnly?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createUpdateCommand(): Command {
|
|
13
|
+
return new Command('update')
|
|
14
|
+
.description('Aktualisiere CLI und Plugins')
|
|
15
|
+
.option('--cli-only', 'Nur CLI aktualisieren')
|
|
16
|
+
.option('--plugins-only', 'Nur Plugins aktualisieren')
|
|
17
|
+
.action(async (options: UpdateOptions) => {
|
|
18
|
+
await runUpdate(options);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function runUpdate(options: UpdateOptions) {
|
|
23
|
+
logger.title('Update');
|
|
24
|
+
|
|
25
|
+
// CLI Update
|
|
26
|
+
if (!options.pluginsOnly) {
|
|
27
|
+
const spinner = ora('Prüfe auf CLI Updates...').start();
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Aktuelle Version ermitteln
|
|
31
|
+
const packageJson = await import('../../package.json', {
|
|
32
|
+
assert: { type: 'json' },
|
|
33
|
+
});
|
|
34
|
+
const currentVersion = packageJson.default.version;
|
|
35
|
+
|
|
36
|
+
spinner.text = 'Aktualisiere CLI...';
|
|
37
|
+
|
|
38
|
+
// Update durchführen
|
|
39
|
+
await execa('bun', ['update', 'clevermation-cli']);
|
|
40
|
+
|
|
41
|
+
spinner.succeed(`CLI aktualisiert (v${currentVersion})`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
spinner.info('CLI Update übersprungen (lokal installiert)');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Plugin Update
|
|
48
|
+
if (!options.cliOnly) {
|
|
49
|
+
const spinner = ora('Aktualisiere Claude Code Plugins...').start();
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Prüfe ob Marketplace installiert ist
|
|
53
|
+
const marketplacePath = await getMarketplacePath();
|
|
54
|
+
|
|
55
|
+
if (marketplacePath) {
|
|
56
|
+
await execa('git', ['-C', marketplacePath, 'pull', 'origin', 'main']);
|
|
57
|
+
spinner.succeed('Plugins aktualisiert!');
|
|
58
|
+
} else {
|
|
59
|
+
spinner.info('Kein Plugin Marketplace gefunden.');
|
|
60
|
+
logger.dim(' Führe "cl init" aus, um Plugins zu installieren.');
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
spinner.fail('Plugin Update fehlgeschlagen');
|
|
64
|
+
logger.dim(' Prüfe deine Internetverbindung.');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
logger.blank();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function getMarketplacePath(): Promise<string | null> {
|
|
72
|
+
// Versuche verschiedene Pfade
|
|
73
|
+
const possiblePaths = [
|
|
74
|
+
'.claude/plugins/clevermation-claude-plugins',
|
|
75
|
+
`${process.env.HOME}/.claude/plugins/clevermation-claude-plugins`,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const p of possiblePaths) {
|
|
79
|
+
try {
|
|
80
|
+
const fs = await import('fs-extra');
|
|
81
|
+
if (await fs.default.pathExists(p)) {
|
|
82
|
+
return p;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return null;
|
|
90
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createInitCommand } from './commands/init.js';
|
|
3
|
+
import { createSyncCommand } from './commands/sync.js';
|
|
4
|
+
import { createConfigCommand } from './commands/config.js';
|
|
5
|
+
import { createExplainCommand } from './commands/explain.js';
|
|
6
|
+
import { createUpdateCommand } from './commands/update.js';
|
|
7
|
+
import { createAuthCommand } from './commands/auth.js';
|
|
8
|
+
import { createDoctorCommand } from './commands/doctor.js';
|
|
9
|
+
import { createOpenCommand } from './commands/open.js';
|
|
10
|
+
import { handleError } from './utils/logger.js';
|
|
11
|
+
import { checkForUpdates } from './utils/auto-update.js';
|
|
12
|
+
import { silentPrerequisiteCheck } from './utils/prerequisites.js';
|
|
13
|
+
|
|
14
|
+
const VERSION = '0.3.2';
|
|
15
|
+
|
|
16
|
+
const program = new Command();
|
|
17
|
+
|
|
18
|
+
program
|
|
19
|
+
.name('cl')
|
|
20
|
+
.description('Clevermation CLI - Internes Tool für Claude Code Projekte')
|
|
21
|
+
.version(VERSION);
|
|
22
|
+
|
|
23
|
+
// Commands registrieren
|
|
24
|
+
program.addCommand(createInitCommand());
|
|
25
|
+
program.addCommand(createSyncCommand());
|
|
26
|
+
program.addCommand(createConfigCommand());
|
|
27
|
+
program.addCommand(createAuthCommand());
|
|
28
|
+
program.addCommand(createDoctorCommand());
|
|
29
|
+
program.addCommand(createOpenCommand());
|
|
30
|
+
program.addCommand(createExplainCommand());
|
|
31
|
+
program.addCommand(createUpdateCommand());
|
|
32
|
+
|
|
33
|
+
// Hintergrund-Tasks (non-blocking)
|
|
34
|
+
Promise.all([
|
|
35
|
+
// Auto-Update Check
|
|
36
|
+
checkForUpdates(VERSION),
|
|
37
|
+
// Prerequisite Check & Auto-Install
|
|
38
|
+
silentPrerequisiteCheck(),
|
|
39
|
+
]).catch(() => {
|
|
40
|
+
// Fehler ignorieren - soll CLI nicht blockieren
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Ausführen
|
|
44
|
+
program.parseAsync(process.argv).catch(handleError);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// Globale CLI Konfiguration (~/.clevermation/config.json)
|
|
4
|
+
export const GlobalConfigSchema = z.object({
|
|
5
|
+
version: z.string(),
|
|
6
|
+
defaultOrg: z.string().default('Clevermation'),
|
|
7
|
+
auth: z.object({
|
|
8
|
+
github: z
|
|
9
|
+
.object({
|
|
10
|
+
authenticated: z.boolean(),
|
|
11
|
+
username: z.string().optional(),
|
|
12
|
+
})
|
|
13
|
+
.optional(),
|
|
14
|
+
supabase: z
|
|
15
|
+
.object({
|
|
16
|
+
authenticated: z.boolean(),
|
|
17
|
+
accessToken: z.string().optional(),
|
|
18
|
+
})
|
|
19
|
+
.optional(),
|
|
20
|
+
n8n: z
|
|
21
|
+
.object({
|
|
22
|
+
url: z.string().optional(),
|
|
23
|
+
apiKey: z.string().optional(),
|
|
24
|
+
})
|
|
25
|
+
.optional(),
|
|
26
|
+
elevenlabs: z
|
|
27
|
+
.object({
|
|
28
|
+
apiKey: z.string().optional(),
|
|
29
|
+
})
|
|
30
|
+
.optional(),
|
|
31
|
+
}),
|
|
32
|
+
preferences: z.object({
|
|
33
|
+
defaultModel: z.enum(['opus', 'sonnet', 'haiku']).default('sonnet'),
|
|
34
|
+
defaultIDE: z.enum(['code', 'cursor', 'webstorm', 'zed']).default('code'),
|
|
35
|
+
autoUpdate: z.boolean().default(true),
|
|
36
|
+
optimaleEinstellungen: z.boolean().default(true),
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export type GlobalConfig = z.infer<typeof GlobalConfigSchema>;
|
|
41
|
+
|
|
42
|
+
// Projekt Konfiguration (.clevermation/config.json)
|
|
43
|
+
export const ProjectConfigSchema = z.object({
|
|
44
|
+
version: z.string(),
|
|
45
|
+
project: z.object({
|
|
46
|
+
name: z.string(),
|
|
47
|
+
type: z.enum(['customer', 'internal']),
|
|
48
|
+
customer: z.string().optional(),
|
|
49
|
+
description: z.string().optional(),
|
|
50
|
+
}),
|
|
51
|
+
services: z.object({
|
|
52
|
+
github: z.object({
|
|
53
|
+
enabled: z.literal(true),
|
|
54
|
+
repo: z.string(),
|
|
55
|
+
org: z.string().default('Clevermation'),
|
|
56
|
+
}),
|
|
57
|
+
supabase: z
|
|
58
|
+
.object({
|
|
59
|
+
enabled: z.boolean(),
|
|
60
|
+
projectRef: z.string().optional(),
|
|
61
|
+
url: z.string().optional(),
|
|
62
|
+
})
|
|
63
|
+
.optional(),
|
|
64
|
+
n8n: z
|
|
65
|
+
.object({
|
|
66
|
+
enabled: z.boolean(),
|
|
67
|
+
instanceUrl: z.string().optional(),
|
|
68
|
+
})
|
|
69
|
+
.optional(),
|
|
70
|
+
elevenlabs: z
|
|
71
|
+
.object({
|
|
72
|
+
enabled: z.boolean(),
|
|
73
|
+
})
|
|
74
|
+
.optional(),
|
|
75
|
+
}),
|
|
76
|
+
claudeCode: z.object({
|
|
77
|
+
plugins: z.array(z.string()),
|
|
78
|
+
marketplace: z.string().default('Clevermation/clevermation-claude-plugins'),
|
|
79
|
+
model: z.enum(['opus', 'sonnet', 'haiku']).default('sonnet'),
|
|
80
|
+
}),
|
|
81
|
+
createdAt: z.string(),
|
|
82
|
+
updatedAt: z.string(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
|
|
86
|
+
|
|
87
|
+
// Service-Typen für Init
|
|
88
|
+
export type ServiceType = 'supabase' | 'n8n' | 'elevenlabs';
|
|
89
|
+
export type ProjectType = 'customer' | 'internal';
|
|
90
|
+
export type ModelType = 'opus' | 'sonnet' | 'haiku';
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { execa } from 'execa';
|
|
6
|
+
import semver from 'semver';
|
|
7
|
+
|
|
8
|
+
const UPDATE_CHECK_FILE = path.join(os.homedir(), '.clevermation', 'last-update-check.json');
|
|
9
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 Stunden
|
|
10
|
+
const PACKAGE_NAME = 'clevermation-cli';
|
|
11
|
+
|
|
12
|
+
interface UpdateCheckData {
|
|
13
|
+
lastCheck: number;
|
|
14
|
+
latestVersion: string | null;
|
|
15
|
+
notifiedVersion: string | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Prüft auf Updates und zeigt eine Benachrichtigung an.
|
|
20
|
+
* Läuft non-blocking im Hintergrund.
|
|
21
|
+
*/
|
|
22
|
+
export async function checkForUpdates(currentVersion: string): Promise<void> {
|
|
23
|
+
try {
|
|
24
|
+
// Prüfe ob wir kürzlich gecheckt haben
|
|
25
|
+
const checkData = await loadCheckData();
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
|
|
28
|
+
if (checkData.lastCheck && now - checkData.lastCheck < CHECK_INTERVAL_MS) {
|
|
29
|
+
// Kürzlich gecheckt - zeige cached Ergebnis wenn Update verfügbar
|
|
30
|
+
if (checkData.latestVersion &&
|
|
31
|
+
semver.gt(checkData.latestVersion, currentVersion) &&
|
|
32
|
+
checkData.notifiedVersion !== checkData.latestVersion) {
|
|
33
|
+
showUpdateNotification(currentVersion, checkData.latestVersion);
|
|
34
|
+
await saveCheckData({ ...checkData, notifiedVersion: checkData.latestVersion });
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Neuer Check (non-blocking)
|
|
40
|
+
checkForUpdatesAsync(currentVersion, checkData);
|
|
41
|
+
} catch {
|
|
42
|
+
// Fehler ignorieren - Update-Check soll CLI nicht blockieren
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function checkForUpdatesAsync(
|
|
47
|
+
currentVersion: string,
|
|
48
|
+
checkData: UpdateCheckData
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
try {
|
|
51
|
+
// Versuche Version von npm/GitHub Packages zu holen
|
|
52
|
+
const latestVersion = await fetchLatestVersion();
|
|
53
|
+
|
|
54
|
+
if (latestVersion) {
|
|
55
|
+
const newCheckData: UpdateCheckData = {
|
|
56
|
+
lastCheck: Date.now(),
|
|
57
|
+
latestVersion,
|
|
58
|
+
notifiedVersion: checkData.notifiedVersion,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
await saveCheckData(newCheckData);
|
|
62
|
+
|
|
63
|
+
if (semver.gt(latestVersion, currentVersion) &&
|
|
64
|
+
checkData.notifiedVersion !== latestVersion) {
|
|
65
|
+
showUpdateNotification(currentVersion, latestVersion);
|
|
66
|
+
await saveCheckData({ ...newCheckData, notifiedVersion: latestVersion });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// Fehler ignorieren
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function fetchLatestVersion(): Promise<string | null> {
|
|
75
|
+
try {
|
|
76
|
+
// Versuche über npm view
|
|
77
|
+
const result = await execa('npm', ['view', PACKAGE_NAME, 'version'], {
|
|
78
|
+
timeout: 5000,
|
|
79
|
+
reject: false,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (result.exitCode === 0 && result.stdout) {
|
|
83
|
+
return result.stdout.trim();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Fallback: GitHub API für Releases
|
|
87
|
+
const response = await fetch(
|
|
88
|
+
'https://api.github.com/repos/Clevermation/clevermation-cli/releases/latest',
|
|
89
|
+
{ signal: AbortSignal.timeout(5000) }
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
if (response.ok) {
|
|
93
|
+
const data = await response.json() as { tag_name: string };
|
|
94
|
+
return data.tag_name.replace(/^v/, '');
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// Fehler ignorieren
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function loadCheckData(): Promise<UpdateCheckData> {
|
|
104
|
+
try {
|
|
105
|
+
if (await fs.pathExists(UPDATE_CHECK_FILE)) {
|
|
106
|
+
return await fs.readJson(UPDATE_CHECK_FILE);
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
// Fehler ignorieren
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
lastCheck: 0,
|
|
114
|
+
latestVersion: null,
|
|
115
|
+
notifiedVersion: null,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function saveCheckData(data: UpdateCheckData): Promise<void> {
|
|
120
|
+
try {
|
|
121
|
+
await fs.ensureDir(path.dirname(UPDATE_CHECK_FILE));
|
|
122
|
+
await fs.writeJson(UPDATE_CHECK_FILE, data);
|
|
123
|
+
} catch {
|
|
124
|
+
// Fehler ignorieren
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function showUpdateNotification(currentVersion: string, latestVersion: string): void {
|
|
129
|
+
const boxWidth = 50;
|
|
130
|
+
const line = '─'.repeat(boxWidth);
|
|
131
|
+
|
|
132
|
+
console.log();
|
|
133
|
+
console.log(chalk.yellow(` ╭${line}╮`));
|
|
134
|
+
console.log(chalk.yellow(` │${' '.repeat(boxWidth)}│`));
|
|
135
|
+
console.log(chalk.yellow(` │${centerText(`Update verfügbar: ${currentVersion} → ${chalk.green(latestVersion)}`, boxWidth)}│`));
|
|
136
|
+
console.log(chalk.yellow(` │${' '.repeat(boxWidth)}│`));
|
|
137
|
+
console.log(chalk.yellow(` │${centerText('Aktualisiere mit:', boxWidth)}│`));
|
|
138
|
+
console.log(chalk.yellow(` │${centerText(chalk.cyan('cl update'), boxWidth)}│`));
|
|
139
|
+
console.log(chalk.yellow(` │${' '.repeat(boxWidth)}│`));
|
|
140
|
+
console.log(chalk.yellow(` ╰${line}╯`));
|
|
141
|
+
console.log();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function centerText(text: string, width: number): string {
|
|
145
|
+
// Entferne ANSI Codes für Längenberechnung
|
|
146
|
+
const plainText = text.replace(/\x1b\[[0-9;]*m/g, '');
|
|
147
|
+
const padding = Math.max(0, Math.floor((width - plainText.length) / 2));
|
|
148
|
+
const rightPadding = width - plainText.length - padding;
|
|
149
|
+
return ' '.repeat(padding) + text + ' '.repeat(Math.max(0, rightPadding));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Führt ein automatisches Update durch wenn gewünscht.
|
|
154
|
+
*/
|
|
155
|
+
export async function performAutoUpdate(): Promise<boolean> {
|
|
156
|
+
try {
|
|
157
|
+
console.log(chalk.blue(' Aktualisiere CLI...'));
|
|
158
|
+
|
|
159
|
+
await execa('bun', ['update', PACKAGE_NAME], { stdio: 'inherit' });
|
|
160
|
+
|
|
161
|
+
console.log(chalk.green(' ✓ CLI erfolgreich aktualisiert!'));
|
|
162
|
+
console.log(chalk.dim(' Starte die CLI neu um die neue Version zu nutzen.'));
|
|
163
|
+
|
|
164
|
+
return true;
|
|
165
|
+
} catch {
|
|
166
|
+
console.log(chalk.red(' ✗ Update fehlgeschlagen'));
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import {
|
|
5
|
+
GlobalConfigSchema,
|
|
6
|
+
ProjectConfigSchema,
|
|
7
|
+
type GlobalConfig,
|
|
8
|
+
type ProjectConfig,
|
|
9
|
+
} from '../types/config.js';
|
|
10
|
+
|
|
11
|
+
// Pfade
|
|
12
|
+
const GLOBAL_CONFIG_DIR = path.join(os.homedir(), '.clevermation');
|
|
13
|
+
const GLOBAL_CONFIG_PATH = path.join(GLOBAL_CONFIG_DIR, 'config.json');
|
|
14
|
+
const PROJECT_CONFIG_DIR = '.clevermation';
|
|
15
|
+
const PROJECT_CONFIG_PATH = path.join(PROJECT_CONFIG_DIR, 'config.json');
|
|
16
|
+
|
|
17
|
+
// Default Configs
|
|
18
|
+
const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
|
|
19
|
+
version: '1.0.0',
|
|
20
|
+
defaultOrg: 'Clevermation',
|
|
21
|
+
auth: {},
|
|
22
|
+
preferences: {
|
|
23
|
+
defaultModel: 'sonnet',
|
|
24
|
+
defaultIDE: 'code',
|
|
25
|
+
autoUpdate: true,
|
|
26
|
+
optimaleEinstellungen: true,
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Globale Config
|
|
31
|
+
export async function loadGlobalConfig(): Promise<GlobalConfig> {
|
|
32
|
+
try {
|
|
33
|
+
if (await fs.pathExists(GLOBAL_CONFIG_PATH)) {
|
|
34
|
+
const data = await fs.readJson(GLOBAL_CONFIG_PATH);
|
|
35
|
+
return GlobalConfigSchema.parse(data);
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Falls parsing fehlschlägt, default zurückgeben
|
|
39
|
+
}
|
|
40
|
+
return DEFAULT_GLOBAL_CONFIG;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function saveGlobalConfig(config: GlobalConfig): Promise<void> {
|
|
44
|
+
await fs.ensureDir(GLOBAL_CONFIG_DIR);
|
|
45
|
+
await fs.writeJson(GLOBAL_CONFIG_PATH, config, { spaces: 2 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function updateGlobalConfig(
|
|
49
|
+
updates: Partial<GlobalConfig>
|
|
50
|
+
): Promise<GlobalConfig> {
|
|
51
|
+
const current = await loadGlobalConfig();
|
|
52
|
+
const updated = { ...current, ...updates };
|
|
53
|
+
await saveGlobalConfig(updated);
|
|
54
|
+
return updated;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Projekt Config
|
|
58
|
+
export async function loadProjectConfig(): Promise<ProjectConfig | null> {
|
|
59
|
+
try {
|
|
60
|
+
if (await fs.pathExists(PROJECT_CONFIG_PATH)) {
|
|
61
|
+
const data = await fs.readJson(PROJECT_CONFIG_PATH);
|
|
62
|
+
return ProjectConfigSchema.parse(data);
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
// Falls parsing fehlschlägt
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function saveProjectConfig(config: ProjectConfig): Promise<void> {
|
|
71
|
+
await fs.ensureDir(PROJECT_CONFIG_DIR);
|
|
72
|
+
await fs.writeJson(PROJECT_CONFIG_PATH, config, { spaces: 2 });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function hasProjectConfig(): Promise<boolean> {
|
|
76
|
+
return fs.pathExists(PROJECT_CONFIG_PATH);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getProjectConfigDir(): string {
|
|
80
|
+
return PROJECT_CONFIG_DIR;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getGlobalConfigDir(): string {
|
|
84
|
+
return GLOBAL_CONFIG_DIR;
|
|
85
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export const logger = {
|
|
4
|
+
info: (msg: string) => console.log(chalk.blue(' info'), msg),
|
|
5
|
+
success: (msg: string) => console.log(chalk.green(' ✓'), msg),
|
|
6
|
+
warning: (msg: string) => console.log(chalk.yellow(' ⚠'), msg),
|
|
7
|
+
error: (msg: string) => console.log(chalk.red(' ✗'), msg),
|
|
8
|
+
|
|
9
|
+
step: (num: number, total: number, msg: string) => {
|
|
10
|
+
console.log(chalk.dim(` [${num}/${total}]`), msg);
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
title: (msg: string) => {
|
|
14
|
+
console.log(chalk.bold.blue(`\n ${msg}\n`));
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
dim: (msg: string) => console.log(chalk.dim(` ${msg}`)),
|
|
18
|
+
|
|
19
|
+
blank: () => console.log(''),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export class ClevermationError extends Error {
|
|
23
|
+
constructor(
|
|
24
|
+
message: string,
|
|
25
|
+
public hint?: string,
|
|
26
|
+
public code?: string
|
|
27
|
+
) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.name = 'ClevermationError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function handleError(error: unknown): never {
|
|
34
|
+
if (error instanceof ClevermationError) {
|
|
35
|
+
logger.error(error.message);
|
|
36
|
+
if (error.hint) {
|
|
37
|
+
logger.info(error.hint);
|
|
38
|
+
}
|
|
39
|
+
} else if (error instanceof Error) {
|
|
40
|
+
logger.error(error.message);
|
|
41
|
+
if (process.env.DEBUG) {
|
|
42
|
+
console.error(error.stack);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
logger.error('Ein unerwarteter Fehler ist aufgetreten');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|