@litmers/cursorflow-orchestrator 0.1.3 → 0.1.6
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/CHANGELOG.md +17 -7
- package/README.md +33 -2
- package/commands/cursorflow-doctor.md +24 -0
- package/commands/cursorflow-signal.md +19 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +57 -0
- package/dist/cli/clean.js.map +1 -0
- package/dist/cli/doctor.d.ts +15 -0
- package/dist/cli/doctor.js +139 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +125 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +302 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/monitor.d.ts +8 -0
- package/dist/cli/monitor.js +210 -0
- package/dist/cli/monitor.js.map +1 -0
- package/dist/cli/resume.d.ts +5 -0
- package/dist/cli/resume.js +128 -0
- package/dist/cli/resume.js.map +1 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.js +128 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/setup-commands.d.ts +23 -0
- package/dist/cli/setup-commands.js +234 -0
- package/dist/cli/setup-commands.js.map +1 -0
- package/dist/cli/signal.d.ts +7 -0
- package/dist/cli/signal.js +99 -0
- package/dist/cli/signal.js.map +1 -0
- package/dist/core/orchestrator.d.ts +47 -0
- package/dist/core/orchestrator.js +192 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/reviewer.d.ts +60 -0
- package/dist/core/reviewer.js +239 -0
- package/dist/core/reviewer.js.map +1 -0
- package/dist/core/runner.d.ts +51 -0
- package/dist/core/runner.js +499 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/utils/config.d.ts +31 -0
- package/dist/utils/config.js +198 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/cursor-agent.d.ts +61 -0
- package/dist/utils/cursor-agent.js +263 -0
- package/dist/utils/cursor-agent.js.map +1 -0
- package/dist/utils/doctor.d.ts +63 -0
- package/dist/utils/doctor.js +280 -0
- package/dist/utils/doctor.js.map +1 -0
- package/dist/utils/git.d.ts +131 -0
- package/dist/utils/git.js +272 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +68 -0
- package/dist/utils/logger.js +158 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/state.d.ts +65 -0
- package/dist/utils/state.js +216 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/types.d.ts +118 -0
- package/dist/utils/types.js +6 -0
- package/dist/utils/types.js.map +1 -0
- package/examples/README.md +155 -0
- package/examples/demo-project/README.md +262 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/01-create-utils.json +18 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/02-add-tests.json +18 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/README.md +109 -0
- package/package.json +71 -61
- package/scripts/ai-security-check.js +11 -4
- package/scripts/local-security-gate.sh +76 -0
- package/src/cli/{clean.js → clean.ts} +11 -5
- package/src/cli/doctor.ts +127 -0
- package/src/cli/{index.js → index.ts} +27 -16
- package/src/cli/{init.js → init.ts} +26 -18
- package/src/cli/{monitor.js → monitor.ts} +57 -44
- package/src/cli/resume.ts +119 -0
- package/src/cli/run.ts +109 -0
- package/src/cli/{setup-commands.js → setup-commands.ts} +38 -18
- package/src/cli/signal.ts +89 -0
- package/src/core/{orchestrator.js → orchestrator.ts} +44 -26
- package/src/core/{reviewer.js → reviewer.ts} +36 -29
- package/src/core/{runner.js → runner.ts} +125 -76
- package/src/utils/{config.js → config.ts} +17 -25
- package/src/utils/{cursor-agent.js → cursor-agent.ts} +38 -47
- package/src/utils/doctor.ts +312 -0
- package/src/utils/{git.js → git.ts} +70 -56
- package/src/utils/{logger.js → logger.ts} +170 -178
- package/src/utils/{state.js → state.ts} +30 -38
- package/src/utils/types.ts +134 -0
- package/src/cli/resume.js +0 -31
- package/src/cli/run.js +0 -51
|
@@ -1,24 +1,59 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Git utilities for CursorFlow
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import { execSync, spawnSync } from 'child_process';
|
|
6
|
+
|
|
7
|
+
export interface GitRunOptions {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
silent?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface GitResult {
|
|
13
|
+
exitCode: number;
|
|
14
|
+
stdout: string;
|
|
15
|
+
stderr: string;
|
|
16
|
+
success: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface WorktreeInfo {
|
|
20
|
+
path: string;
|
|
21
|
+
branch?: string;
|
|
22
|
+
head?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ChangedFile {
|
|
26
|
+
status: string;
|
|
27
|
+
file: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CommitInfo {
|
|
31
|
+
hash: string;
|
|
32
|
+
shortHash: string;
|
|
33
|
+
author: string;
|
|
34
|
+
authorEmail: string;
|
|
35
|
+
timestamp: number;
|
|
36
|
+
subject: string;
|
|
37
|
+
}
|
|
8
38
|
|
|
9
39
|
/**
|
|
10
40
|
* Run git command and return output
|
|
11
41
|
*/
|
|
12
|
-
function runGit(args, options = {}) {
|
|
42
|
+
export function runGit(args: string[], options: GitRunOptions = {}): string {
|
|
13
43
|
const { cwd, silent = false } = options;
|
|
14
44
|
|
|
15
45
|
try {
|
|
16
|
-
const result =
|
|
46
|
+
const result = spawnSync('git', args, {
|
|
17
47
|
cwd: cwd || process.cwd(),
|
|
18
48
|
encoding: 'utf8',
|
|
19
49
|
stdio: silent ? 'pipe' : 'inherit',
|
|
20
50
|
});
|
|
21
|
-
|
|
51
|
+
|
|
52
|
+
if (result.status !== 0 && !silent) {
|
|
53
|
+
throw new Error(`Git command failed: git ${args.join(' ')}\n${result.stderr || ''}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result.stdout ? result.stdout.trim() : '';
|
|
22
57
|
} catch (error) {
|
|
23
58
|
if (silent) {
|
|
24
59
|
return '';
|
|
@@ -30,7 +65,7 @@ function runGit(args, options = {}) {
|
|
|
30
65
|
/**
|
|
31
66
|
* Run git command and return result object
|
|
32
67
|
*/
|
|
33
|
-
function runGitResult(args, options = {}) {
|
|
68
|
+
export function runGitResult(args: string[], options: GitRunOptions = {}): GitResult {
|
|
34
69
|
const { cwd } = options;
|
|
35
70
|
|
|
36
71
|
const result = spawnSync('git', args, {
|
|
@@ -41,8 +76,8 @@ function runGitResult(args, options = {}) {
|
|
|
41
76
|
|
|
42
77
|
return {
|
|
43
78
|
exitCode: result.status ?? 1,
|
|
44
|
-
stdout: (result.stdout || '').trim(),
|
|
45
|
-
stderr: (result.stderr || '').trim(),
|
|
79
|
+
stdout: (result.stdout || '').toString().trim(),
|
|
80
|
+
stderr: (result.stderr || '').toString().trim(),
|
|
46
81
|
success: result.status === 0,
|
|
47
82
|
};
|
|
48
83
|
}
|
|
@@ -50,21 +85,21 @@ function runGitResult(args, options = {}) {
|
|
|
50
85
|
/**
|
|
51
86
|
* Get current branch name
|
|
52
87
|
*/
|
|
53
|
-
function getCurrentBranch(cwd) {
|
|
88
|
+
export function getCurrentBranch(cwd?: string): string {
|
|
54
89
|
return runGit(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd, silent: true });
|
|
55
90
|
}
|
|
56
91
|
|
|
57
92
|
/**
|
|
58
93
|
* Get repository root directory
|
|
59
94
|
*/
|
|
60
|
-
function getRepoRoot(cwd) {
|
|
95
|
+
export function getRepoRoot(cwd?: string): string {
|
|
61
96
|
return runGit(['rev-parse', '--show-toplevel'], { cwd, silent: true });
|
|
62
97
|
}
|
|
63
98
|
|
|
64
99
|
/**
|
|
65
100
|
* Check if directory is a git repository
|
|
66
101
|
*/
|
|
67
|
-
function isGitRepo(cwd) {
|
|
102
|
+
export function isGitRepo(cwd?: string): boolean {
|
|
68
103
|
const result = runGitResult(['rev-parse', '--git-dir'], { cwd });
|
|
69
104
|
return result.success;
|
|
70
105
|
}
|
|
@@ -72,7 +107,7 @@ function isGitRepo(cwd) {
|
|
|
72
107
|
/**
|
|
73
108
|
* Check if worktree exists
|
|
74
109
|
*/
|
|
75
|
-
function worktreeExists(worktreePath, cwd) {
|
|
110
|
+
export function worktreeExists(worktreePath: string, cwd?: string): boolean {
|
|
76
111
|
const result = runGitResult(['worktree', 'list'], { cwd });
|
|
77
112
|
if (!result.success) return false;
|
|
78
113
|
|
|
@@ -82,7 +117,7 @@ function worktreeExists(worktreePath, cwd) {
|
|
|
82
117
|
/**
|
|
83
118
|
* Create worktree
|
|
84
119
|
*/
|
|
85
|
-
function createWorktree(worktreePath, branchName, options = {}) {
|
|
120
|
+
export function createWorktree(worktreePath: string, branchName: string, options: { cwd?: string; baseBranch?: string } = {}): string {
|
|
86
121
|
const { cwd, baseBranch = 'main' } = options;
|
|
87
122
|
|
|
88
123
|
// Check if branch already exists
|
|
@@ -102,7 +137,7 @@ function createWorktree(worktreePath, branchName, options = {}) {
|
|
|
102
137
|
/**
|
|
103
138
|
* Remove worktree
|
|
104
139
|
*/
|
|
105
|
-
function removeWorktree(worktreePath, options = {}) {
|
|
140
|
+
export function removeWorktree(worktreePath: string, options: { cwd?: string; force?: boolean } = {}): void {
|
|
106
141
|
const { cwd, force = false } = options;
|
|
107
142
|
|
|
108
143
|
const args = ['worktree', 'remove', worktreePath];
|
|
@@ -116,18 +151,18 @@ function removeWorktree(worktreePath, options = {}) {
|
|
|
116
151
|
/**
|
|
117
152
|
* List all worktrees
|
|
118
153
|
*/
|
|
119
|
-
function listWorktrees(cwd) {
|
|
154
|
+
export function listWorktrees(cwd?: string): WorktreeInfo[] {
|
|
120
155
|
const result = runGitResult(['worktree', 'list', '--porcelain'], { cwd });
|
|
121
156
|
if (!result.success) return [];
|
|
122
157
|
|
|
123
|
-
const worktrees = [];
|
|
158
|
+
const worktrees: WorktreeInfo[] = [];
|
|
124
159
|
const lines = result.stdout.split('\n');
|
|
125
|
-
let current = {};
|
|
160
|
+
let current: Partial<WorktreeInfo> = {};
|
|
126
161
|
|
|
127
162
|
for (const line of lines) {
|
|
128
163
|
if (line.startsWith('worktree ')) {
|
|
129
164
|
if (current.path) {
|
|
130
|
-
worktrees.push(current);
|
|
165
|
+
worktrees.push(current as WorktreeInfo);
|
|
131
166
|
}
|
|
132
167
|
current = { path: line.slice(9) };
|
|
133
168
|
} else if (line.startsWith('branch ')) {
|
|
@@ -138,7 +173,7 @@ function listWorktrees(cwd) {
|
|
|
138
173
|
}
|
|
139
174
|
|
|
140
175
|
if (current.path) {
|
|
141
|
-
worktrees.push(current);
|
|
176
|
+
worktrees.push(current as WorktreeInfo);
|
|
142
177
|
}
|
|
143
178
|
|
|
144
179
|
return worktrees;
|
|
@@ -147,7 +182,7 @@ function listWorktrees(cwd) {
|
|
|
147
182
|
/**
|
|
148
183
|
* Check if there are uncommitted changes
|
|
149
184
|
*/
|
|
150
|
-
function hasUncommittedChanges(cwd) {
|
|
185
|
+
export function hasUncommittedChanges(cwd?: string): boolean {
|
|
151
186
|
const result = runGitResult(['status', '--porcelain'], { cwd });
|
|
152
187
|
return result.success && result.stdout.length > 0;
|
|
153
188
|
}
|
|
@@ -155,7 +190,7 @@ function hasUncommittedChanges(cwd) {
|
|
|
155
190
|
/**
|
|
156
191
|
* Get list of changed files
|
|
157
192
|
*/
|
|
158
|
-
function getChangedFiles(cwd) {
|
|
193
|
+
export function getChangedFiles(cwd?: string): ChangedFile[] {
|
|
159
194
|
const result = runGitResult(['status', '--porcelain'], { cwd });
|
|
160
195
|
if (!result.success) return [];
|
|
161
196
|
|
|
@@ -172,7 +207,7 @@ function getChangedFiles(cwd) {
|
|
|
172
207
|
/**
|
|
173
208
|
* Create commit
|
|
174
209
|
*/
|
|
175
|
-
function commit(message, options = {}) {
|
|
210
|
+
export function commit(message: string, options: { cwd?: string; addAll?: boolean } = {}): void {
|
|
176
211
|
const { cwd, addAll = true } = options;
|
|
177
212
|
|
|
178
213
|
if (addAll) {
|
|
@@ -185,7 +220,7 @@ function commit(message, options = {}) {
|
|
|
185
220
|
/**
|
|
186
221
|
* Push to remote
|
|
187
222
|
*/
|
|
188
|
-
function push(branchName, options = {}) {
|
|
223
|
+
export function push(branchName: string, options: { cwd?: string; force?: boolean; setUpstream?: boolean } = {}): void {
|
|
189
224
|
const { cwd, force = false, setUpstream = false } = options;
|
|
190
225
|
|
|
191
226
|
const args = ['push'];
|
|
@@ -206,7 +241,7 @@ function push(branchName, options = {}) {
|
|
|
206
241
|
/**
|
|
207
242
|
* Fetch from remote
|
|
208
243
|
*/
|
|
209
|
-
function fetch(options = {}) {
|
|
244
|
+
export function fetch(options: { cwd?: string; prune?: boolean } = {}): void {
|
|
210
245
|
const { cwd, prune = true } = options;
|
|
211
246
|
|
|
212
247
|
const args = ['fetch', 'origin'];
|
|
@@ -220,7 +255,7 @@ function fetch(options = {}) {
|
|
|
220
255
|
/**
|
|
221
256
|
* Check if branch exists (local or remote)
|
|
222
257
|
*/
|
|
223
|
-
function branchExists(branchName, options = {}) {
|
|
258
|
+
export function branchExists(branchName: string, options: { cwd?: string; remote?: boolean } = {}): boolean {
|
|
224
259
|
const { cwd, remote = false } = options;
|
|
225
260
|
|
|
226
261
|
if (remote) {
|
|
@@ -235,7 +270,7 @@ function branchExists(branchName, options = {}) {
|
|
|
235
270
|
/**
|
|
236
271
|
* Delete branch
|
|
237
272
|
*/
|
|
238
|
-
function deleteBranch(branchName, options = {}) {
|
|
273
|
+
export function deleteBranch(branchName: string, options: { cwd?: string; force?: boolean; remote?: boolean } = {}): void {
|
|
239
274
|
const { cwd, force = false, remote = false } = options;
|
|
240
275
|
|
|
241
276
|
if (remote) {
|
|
@@ -249,7 +284,7 @@ function deleteBranch(branchName, options = {}) {
|
|
|
249
284
|
/**
|
|
250
285
|
* Merge branch
|
|
251
286
|
*/
|
|
252
|
-
function merge(branchName, options = {}) {
|
|
287
|
+
export function merge(branchName: string, options: { cwd?: string; noFf?: boolean; message?: string | null } = {}): void {
|
|
253
288
|
const { cwd, noFf = false, message = null } = options;
|
|
254
289
|
|
|
255
290
|
const args = ['merge'];
|
|
@@ -270,7 +305,7 @@ function merge(branchName, options = {}) {
|
|
|
270
305
|
/**
|
|
271
306
|
* Get commit info
|
|
272
307
|
*/
|
|
273
|
-
function getCommitInfo(commitHash, options = {}) {
|
|
308
|
+
export function getCommitInfo(commitHash: string, options: { cwd?: string } = {}): CommitInfo | null {
|
|
274
309
|
const { cwd } = options;
|
|
275
310
|
|
|
276
311
|
const format = '--format=%H%n%h%n%an%n%ae%n%at%n%s';
|
|
@@ -280,32 +315,11 @@ function getCommitInfo(commitHash, options = {}) {
|
|
|
280
315
|
|
|
281
316
|
const lines = result.stdout.split('\n');
|
|
282
317
|
return {
|
|
283
|
-
hash: lines[0],
|
|
284
|
-
shortHash: lines[1],
|
|
285
|
-
author: lines[2],
|
|
286
|
-
authorEmail: lines[3],
|
|
287
|
-
timestamp: parseInt(lines[4]),
|
|
288
|
-
subject: lines[5],
|
|
318
|
+
hash: lines[0] || '',
|
|
319
|
+
shortHash: lines[1] || '',
|
|
320
|
+
author: lines[2] || '',
|
|
321
|
+
authorEmail: lines[3] || '',
|
|
322
|
+
timestamp: parseInt(lines[4] || '0'),
|
|
323
|
+
subject: lines[5] || '',
|
|
289
324
|
};
|
|
290
325
|
}
|
|
291
|
-
|
|
292
|
-
module.exports = {
|
|
293
|
-
runGit,
|
|
294
|
-
runGitResult,
|
|
295
|
-
getCurrentBranch,
|
|
296
|
-
getRepoRoot,
|
|
297
|
-
isGitRepo,
|
|
298
|
-
worktreeExists,
|
|
299
|
-
createWorktree,
|
|
300
|
-
removeWorktree,
|
|
301
|
-
listWorktrees,
|
|
302
|
-
hasUncommittedChanges,
|
|
303
|
-
getChangedFiles,
|
|
304
|
-
commit,
|
|
305
|
-
push,
|
|
306
|
-
fetch,
|
|
307
|
-
branchExists,
|
|
308
|
-
deleteBranch,
|
|
309
|
-
merge,
|
|
310
|
-
getCommitInfo,
|
|
311
|
-
};
|
|
@@ -1,178 +1,170 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
console.log(
|
|
104
|
-
console.log(`${COLORS.cyan}
|
|
105
|
-
console.log(`${COLORS.cyan}
|
|
106
|
-
console.log(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
progress,
|
|
172
|
-
section,
|
|
173
|
-
log,
|
|
174
|
-
json,
|
|
175
|
-
createSpinner,
|
|
176
|
-
COLORS,
|
|
177
|
-
LOG_LEVELS,
|
|
178
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Logging utilities for CursorFlow
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export enum LogLevel {
|
|
6
|
+
error = 0,
|
|
7
|
+
warn = 1,
|
|
8
|
+
info = 2,
|
|
9
|
+
debug = 3,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const COLORS = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
red: '\x1b[31m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
green: '\x1b[32m',
|
|
17
|
+
blue: '\x1b[34m',
|
|
18
|
+
cyan: '\x1b[36m',
|
|
19
|
+
gray: '\x1b[90m',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let currentLogLevel: number = LogLevel.info;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Set log level
|
|
26
|
+
*/
|
|
27
|
+
export function setLogLevel(level: string | number): void {
|
|
28
|
+
if (typeof level === 'string') {
|
|
29
|
+
currentLogLevel = (LogLevel as any)[level] ?? LogLevel.info;
|
|
30
|
+
} else {
|
|
31
|
+
currentLogLevel = level;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Format message with timestamp
|
|
37
|
+
*/
|
|
38
|
+
function formatMessage(level: string, message: string, emoji = ''): string {
|
|
39
|
+
const timestamp = new Date().toISOString();
|
|
40
|
+
const prefix = emoji ? `${emoji} ` : '';
|
|
41
|
+
return `[${timestamp}] [${level.toUpperCase()}] ${prefix}${message}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Log with color
|
|
46
|
+
*/
|
|
47
|
+
function logWithColor(color: string, level: keyof typeof LogLevel, message: string, emoji = ''): void {
|
|
48
|
+
if (LogLevel[level] > currentLogLevel) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const formatted = formatMessage(level, message, emoji);
|
|
53
|
+
console.log(`${color}${formatted}${COLORS.reset}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Error log
|
|
58
|
+
*/
|
|
59
|
+
export function error(message: string, emoji = '❌'): void {
|
|
60
|
+
logWithColor(COLORS.red, 'error', message, emoji);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Warning log
|
|
65
|
+
*/
|
|
66
|
+
export function warn(message: string, emoji = '⚠️'): void {
|
|
67
|
+
logWithColor(COLORS.yellow, 'warn', message, emoji);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Info log
|
|
72
|
+
*/
|
|
73
|
+
export function info(message: string, emoji = 'ℹ️'): void {
|
|
74
|
+
logWithColor(COLORS.cyan, 'info', message, emoji);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Success log
|
|
79
|
+
*/
|
|
80
|
+
export function success(message: string, emoji = '✅'): void {
|
|
81
|
+
logWithColor(COLORS.green, 'info', message, emoji);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Debug log
|
|
86
|
+
*/
|
|
87
|
+
export function debug(message: string, emoji = '🔍'): void {
|
|
88
|
+
logWithColor(COLORS.gray, 'debug', message, emoji);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Progress log
|
|
93
|
+
*/
|
|
94
|
+
export function progress(message: string, emoji = '🔄'): void {
|
|
95
|
+
logWithColor(COLORS.blue, 'info', message, emoji);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Section header
|
|
100
|
+
*/
|
|
101
|
+
export function section(message: string): void {
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
|
|
104
|
+
console.log(`${COLORS.cyan} ${message}${COLORS.reset}`);
|
|
105
|
+
console.log(`${COLORS.cyan}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
|
|
106
|
+
console.log('');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Simple log without formatting
|
|
111
|
+
*/
|
|
112
|
+
export function log(message: string | any): void {
|
|
113
|
+
console.log(message);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Log JSON data (pretty print in debug mode)
|
|
118
|
+
*/
|
|
119
|
+
export function json(data: any): void {
|
|
120
|
+
if (currentLogLevel >= LogLevel.debug) {
|
|
121
|
+
console.log(JSON.stringify(data, null, 2));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface Spinner {
|
|
126
|
+
start(): void;
|
|
127
|
+
stop(finalMessage?: string | null): void;
|
|
128
|
+
succeed(message: string): void;
|
|
129
|
+
fail(message: string): void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create spinner (simple implementation)
|
|
134
|
+
*/
|
|
135
|
+
export function createSpinner(message: string): Spinner {
|
|
136
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
137
|
+
let i = 0;
|
|
138
|
+
let interval: NodeJS.Timeout | null = null;
|
|
139
|
+
|
|
140
|
+
const spinner: Spinner = {
|
|
141
|
+
start() {
|
|
142
|
+
process.stdout.write(`${message} ${frames[0]}`);
|
|
143
|
+
interval = setInterval(() => {
|
|
144
|
+
i = (i + 1) % frames.length;
|
|
145
|
+
process.stdout.write(`\r${message} ${frames[i]}`);
|
|
146
|
+
}, 80);
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
stop(finalMessage: string | null = null) {
|
|
150
|
+
if (interval) {
|
|
151
|
+
clearInterval(interval);
|
|
152
|
+
interval = null;
|
|
153
|
+
}
|
|
154
|
+
process.stdout.write('\r\x1b[K'); // Clear line
|
|
155
|
+
if (finalMessage) {
|
|
156
|
+
console.log(finalMessage);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
succeed(message: string) {
|
|
161
|
+
this.stop(`${COLORS.green}✓${COLORS.reset} ${message}`);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
fail(message: string) {
|
|
165
|
+
this.stop(`${COLORS.red}✗${COLORS.reset} ${message}`);
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
return spinner;
|
|
170
|
+
}
|