@litmers/cursorflow-orchestrator 0.1.2 → 0.1.5
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 +7 -6
- 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/index.d.ts +6 -0
- package/dist/cli/index.js +120 -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 +58 -0
- package/dist/cli/resume.js.map +1 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.js +74 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/setup-commands.d.ts +19 -0
- package/dist/cli/setup-commands.js +218 -0
- package/dist/cli/setup-commands.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 +49 -0
- package/dist/core/runner.js +475 -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/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 +117 -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/{index.js → index.ts} +22 -16
- package/src/cli/{init.js → init.ts} +26 -18
- package/src/cli/{monitor.js → monitor.ts} +57 -44
- package/src/cli/{resume.js → resume.ts} +11 -5
- package/src/cli/run.ts +54 -0
- package/src/cli/{setup-commands.js → setup-commands.ts} +19 -18
- 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} +78 -56
- package/src/utils/{config.js → config.ts} +17 -25
- package/src/utils/{cursor-agent.js → cursor-agent.ts} +38 -47
- 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 +133 -0
- package/src/cli/run.js +0 -51
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Configuration loader for CursorFlow
|
|
4
3
|
*
|
|
5
4
|
* Finds project root and loads user configuration with defaults
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import { CursorFlowConfig } from './types';
|
|
10
|
+
export { CursorFlowConfig };
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Find project root by looking for package.json
|
|
13
14
|
*/
|
|
14
|
-
function findProjectRoot(cwd = process.cwd()) {
|
|
15
|
+
export function findProjectRoot(cwd = process.cwd()): string {
|
|
15
16
|
let current = cwd;
|
|
16
17
|
|
|
17
18
|
while (current !== path.parse(current).root) {
|
|
@@ -28,7 +29,7 @@ function findProjectRoot(cwd = process.cwd()) {
|
|
|
28
29
|
/**
|
|
29
30
|
* Load configuration with defaults
|
|
30
31
|
*/
|
|
31
|
-
function loadConfig(projectRoot = null) {
|
|
32
|
+
export function loadConfig(projectRoot: string | null = null): CursorFlowConfig {
|
|
32
33
|
if (!projectRoot) {
|
|
33
34
|
projectRoot = findProjectRoot();
|
|
34
35
|
}
|
|
@@ -36,7 +37,7 @@ function loadConfig(projectRoot = null) {
|
|
|
36
37
|
const configPath = path.join(projectRoot, 'cursorflow.config.js');
|
|
37
38
|
|
|
38
39
|
// Default configuration
|
|
39
|
-
const defaults = {
|
|
40
|
+
const defaults: CursorFlowConfig = {
|
|
40
41
|
// Directories
|
|
41
42
|
tasksDir: '_cursorflow/tasks',
|
|
42
43
|
logsDir: '_cursorflow/logs',
|
|
@@ -46,8 +47,8 @@ function loadConfig(projectRoot = null) {
|
|
|
46
47
|
branchPrefix: 'feature/',
|
|
47
48
|
|
|
48
49
|
// Execution
|
|
49
|
-
executor: 'cursor-agent',
|
|
50
|
-
pollInterval: 60,
|
|
50
|
+
executor: 'cursor-agent',
|
|
51
|
+
pollInterval: 60,
|
|
51
52
|
|
|
52
53
|
// Dependencies
|
|
53
54
|
allowDependencyChange: false,
|
|
@@ -60,12 +61,12 @@ function loadConfig(projectRoot = null) {
|
|
|
60
61
|
|
|
61
62
|
// Lane defaults
|
|
62
63
|
defaultLaneConfig: {
|
|
63
|
-
devPort: 3001,
|
|
64
|
+
devPort: 3001,
|
|
64
65
|
autoCreatePr: false,
|
|
65
66
|
},
|
|
66
67
|
|
|
67
68
|
// Logging
|
|
68
|
-
logLevel: 'info',
|
|
69
|
+
logLevel: 'info',
|
|
69
70
|
verboseGit: false,
|
|
70
71
|
|
|
71
72
|
// Advanced
|
|
@@ -81,7 +82,7 @@ function loadConfig(projectRoot = null) {
|
|
|
81
82
|
try {
|
|
82
83
|
const userConfig = require(configPath);
|
|
83
84
|
return { ...defaults, ...userConfig, projectRoot };
|
|
84
|
-
} catch (error) {
|
|
85
|
+
} catch (error: any) {
|
|
85
86
|
console.warn(`Warning: Failed to load config from ${configPath}: ${error.message}`);
|
|
86
87
|
console.warn('Using default configuration...');
|
|
87
88
|
}
|
|
@@ -93,22 +94,22 @@ function loadConfig(projectRoot = null) {
|
|
|
93
94
|
/**
|
|
94
95
|
* Get absolute path for tasks directory
|
|
95
96
|
*/
|
|
96
|
-
function getTasksDir(config) {
|
|
97
|
+
export function getTasksDir(config: CursorFlowConfig): string {
|
|
97
98
|
return path.join(config.projectRoot, config.tasksDir);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
/**
|
|
101
102
|
* Get absolute path for logs directory
|
|
102
103
|
*/
|
|
103
|
-
function getLogsDir(config) {
|
|
104
|
+
export function getLogsDir(config: CursorFlowConfig): string {
|
|
104
105
|
return path.join(config.projectRoot, config.logsDir);
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
/**
|
|
108
109
|
* Validate configuration
|
|
109
110
|
*/
|
|
110
|
-
function validateConfig(config) {
|
|
111
|
-
const errors = [];
|
|
111
|
+
export function validateConfig(config: CursorFlowConfig): boolean {
|
|
112
|
+
const errors: string[] = [];
|
|
112
113
|
|
|
113
114
|
if (!config.tasksDir) {
|
|
114
115
|
errors.push('tasksDir is required');
|
|
@@ -136,7 +137,7 @@ function validateConfig(config) {
|
|
|
136
137
|
/**
|
|
137
138
|
* Create default config file
|
|
138
139
|
*/
|
|
139
|
-
function createDefaultConfig(projectRoot, force = false) {
|
|
140
|
+
export function createDefaultConfig(projectRoot: string, force = false): string {
|
|
140
141
|
const configPath = path.join(projectRoot, 'cursorflow.config.js');
|
|
141
142
|
|
|
142
143
|
if (fs.existsSync(configPath) && !force) {
|
|
@@ -184,12 +185,3 @@ function createDefaultConfig(projectRoot, force = false) {
|
|
|
184
185
|
fs.writeFileSync(configPath, template, 'utf8');
|
|
185
186
|
return configPath;
|
|
186
187
|
}
|
|
187
|
-
|
|
188
|
-
module.exports = {
|
|
189
|
-
findProjectRoot,
|
|
190
|
-
loadConfig,
|
|
191
|
-
getTasksDir,
|
|
192
|
-
getLogsDir,
|
|
193
|
-
validateConfig,
|
|
194
|
-
createDefaultConfig,
|
|
195
|
-
};
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* Cursor Agent CLI wrapper and utilities
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import { execSync, spawnSync } from 'child_process';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Check if cursor-agent CLI is installed
|
|
10
9
|
*/
|
|
11
|
-
function checkCursorAgentInstalled() {
|
|
10
|
+
export function checkCursorAgentInstalled(): boolean {
|
|
12
11
|
try {
|
|
13
|
-
|
|
14
|
-
return
|
|
12
|
+
const result = spawnSync('cursor-agent', ['--version'], { stdio: 'pipe' });
|
|
13
|
+
return result.status === 0;
|
|
15
14
|
} catch {
|
|
16
15
|
return false;
|
|
17
16
|
}
|
|
@@ -20,13 +19,13 @@ function checkCursorAgentInstalled() {
|
|
|
20
19
|
/**
|
|
21
20
|
* Get cursor-agent version
|
|
22
21
|
*/
|
|
23
|
-
function getCursorAgentVersion() {
|
|
22
|
+
export function getCursorAgentVersion(): string | null {
|
|
24
23
|
try {
|
|
25
|
-
const
|
|
24
|
+
const result = spawnSync('cursor-agent', ['--version'], {
|
|
26
25
|
encoding: 'utf8',
|
|
27
26
|
stdio: 'pipe',
|
|
28
|
-
})
|
|
29
|
-
return
|
|
27
|
+
});
|
|
28
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
30
29
|
} catch {
|
|
31
30
|
return null;
|
|
32
31
|
}
|
|
@@ -35,7 +34,7 @@ function getCursorAgentVersion() {
|
|
|
35
34
|
/**
|
|
36
35
|
* Ensure cursor-agent is installed, exit with error message if not
|
|
37
36
|
*/
|
|
38
|
-
function ensureCursorAgent() {
|
|
37
|
+
export function ensureCursorAgent(): void {
|
|
39
38
|
if (!checkCursorAgentInstalled()) {
|
|
40
39
|
console.error(`
|
|
41
40
|
❌ cursor-agent CLI is not installed
|
|
@@ -56,7 +55,7 @@ More info: https://docs.cursor.com/agent
|
|
|
56
55
|
/**
|
|
57
56
|
* Print installation guide
|
|
58
57
|
*/
|
|
59
|
-
function printInstallationGuide() {
|
|
58
|
+
export function printInstallationGuide(): void {
|
|
60
59
|
console.log(`
|
|
61
60
|
📦 cursor-agent CLI Installation Guide
|
|
62
61
|
|
|
@@ -83,15 +82,15 @@ After installation, run your command again.
|
|
|
83
82
|
/**
|
|
84
83
|
* Check if CURSOR_API_KEY is set (for cloud execution)
|
|
85
84
|
*/
|
|
86
|
-
function checkCursorApiKey() {
|
|
87
|
-
return !!process.env
|
|
85
|
+
export function checkCursorApiKey(): boolean {
|
|
86
|
+
return !!process.env['CURSOR_API_KEY'];
|
|
88
87
|
}
|
|
89
88
|
|
|
90
89
|
/**
|
|
91
90
|
* Validate cursor-agent setup for given executor type
|
|
92
91
|
*/
|
|
93
|
-
function validateSetup(executor = 'cursor-agent') {
|
|
94
|
-
const errors = [];
|
|
92
|
+
export function validateSetup(executor = 'cursor-agent'): { valid: boolean; errors: string[] } {
|
|
93
|
+
const errors: string[] = [];
|
|
95
94
|
|
|
96
95
|
if (executor === 'cursor-agent') {
|
|
97
96
|
if (!checkCursorAgentInstalled()) {
|
|
@@ -114,20 +113,18 @@ function validateSetup(executor = 'cursor-agent') {
|
|
|
114
113
|
/**
|
|
115
114
|
* Get available models (if cursor-agent supports it)
|
|
116
115
|
*/
|
|
117
|
-
function getAvailableModels() {
|
|
116
|
+
export function getAvailableModels(): string[] {
|
|
118
117
|
try {
|
|
119
118
|
// This is a placeholder - actual implementation depends on cursor-agent API
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
119
|
+
// execSync('cursor-agent --model invalid "test"', {
|
|
120
|
+
// encoding: 'utf8',
|
|
121
|
+
// stdio: 'pipe',
|
|
122
|
+
// });
|
|
124
123
|
|
|
125
|
-
// Parse models from error message
|
|
126
|
-
// This is an example - actual parsing depends on cursor-agent output
|
|
127
124
|
return [];
|
|
128
|
-
} catch (error) {
|
|
125
|
+
} catch (error: any) {
|
|
129
126
|
// Parse from error message
|
|
130
|
-
const output = error.stderr || error.stdout || '';
|
|
127
|
+
const output = (error.stderr || error.stdout || '').toString();
|
|
131
128
|
// Extract model names from output
|
|
132
129
|
return parseModelsFromOutput(output);
|
|
133
130
|
}
|
|
@@ -136,10 +133,10 @@ function getAvailableModels() {
|
|
|
136
133
|
/**
|
|
137
134
|
* Parse model names from cursor-agent output
|
|
138
135
|
*/
|
|
139
|
-
function parseModelsFromOutput(output) {
|
|
136
|
+
export function parseModelsFromOutput(output: string): string[] {
|
|
140
137
|
// This is a placeholder implementation
|
|
141
138
|
// Actual parsing depends on cursor-agent CLI output format
|
|
142
|
-
const models = [];
|
|
139
|
+
const models: string[] = [];
|
|
143
140
|
|
|
144
141
|
// Example parsing logic
|
|
145
142
|
const lines = output.split('\n');
|
|
@@ -147,7 +144,7 @@ function parseModelsFromOutput(output) {
|
|
|
147
144
|
if (line.includes('sonnet') || line.includes('opus') || line.includes('gpt')) {
|
|
148
145
|
const match = line.match(/['"]([^'"]+)['"]/);
|
|
149
146
|
if (match) {
|
|
150
|
-
models.push(match[1]);
|
|
147
|
+
models.push(match[1]!);
|
|
151
148
|
}
|
|
152
149
|
}
|
|
153
150
|
}
|
|
@@ -158,7 +155,7 @@ function parseModelsFromOutput(output) {
|
|
|
158
155
|
/**
|
|
159
156
|
* Test cursor-agent with a simple command
|
|
160
157
|
*/
|
|
161
|
-
function testCursorAgent() {
|
|
158
|
+
export function testCursorAgent(): { success: boolean; output?: string; error?: string } {
|
|
162
159
|
try {
|
|
163
160
|
const result = spawnSync('cursor-agent', ['--help'], {
|
|
164
161
|
encoding: 'utf8',
|
|
@@ -170,7 +167,7 @@ function testCursorAgent() {
|
|
|
170
167
|
output: result.stdout,
|
|
171
168
|
error: result.stderr,
|
|
172
169
|
};
|
|
173
|
-
} catch (error) {
|
|
170
|
+
} catch (error: any) {
|
|
174
171
|
return {
|
|
175
172
|
success: false,
|
|
176
173
|
error: error.message,
|
|
@@ -178,10 +175,18 @@ function testCursorAgent() {
|
|
|
178
175
|
}
|
|
179
176
|
}
|
|
180
177
|
|
|
178
|
+
export interface AuthCheckResult {
|
|
179
|
+
authenticated: boolean;
|
|
180
|
+
message: string;
|
|
181
|
+
details?: string;
|
|
182
|
+
help?: string;
|
|
183
|
+
error?: string;
|
|
184
|
+
}
|
|
185
|
+
|
|
181
186
|
/**
|
|
182
187
|
* Check cursor-agent authentication
|
|
183
188
|
*/
|
|
184
|
-
function checkCursorAuth() {
|
|
189
|
+
export function checkCursorAuth(): AuthCheckResult {
|
|
185
190
|
try {
|
|
186
191
|
const result = spawnSync('cursor-agent', ['create-chat'], {
|
|
187
192
|
encoding: 'utf8',
|
|
@@ -196,7 +201,7 @@ function checkCursorAuth() {
|
|
|
196
201
|
};
|
|
197
202
|
}
|
|
198
203
|
|
|
199
|
-
const errorMsg = result.stderr?.trim() || result.stdout?.trim() || '';
|
|
204
|
+
const errorMsg = (result.stderr?.trim() || result.stdout?.trim() || '').toString();
|
|
200
205
|
|
|
201
206
|
// Check for authentication errors
|
|
202
207
|
if (errorMsg.includes('not authenticated') ||
|
|
@@ -227,7 +232,7 @@ function checkCursorAuth() {
|
|
|
227
232
|
message: 'Unknown error',
|
|
228
233
|
details: errorMsg,
|
|
229
234
|
};
|
|
230
|
-
} catch (error) {
|
|
235
|
+
} catch (error: any) {
|
|
231
236
|
if (error.code === 'ETIMEDOUT') {
|
|
232
237
|
return {
|
|
233
238
|
authenticated: false,
|
|
@@ -247,7 +252,7 @@ function checkCursorAuth() {
|
|
|
247
252
|
/**
|
|
248
253
|
* Print authentication help
|
|
249
254
|
*/
|
|
250
|
-
function printAuthHelp() {
|
|
255
|
+
export function printAuthHelp(): void {
|
|
251
256
|
console.log(`
|
|
252
257
|
🔐 Cursor Authentication Required
|
|
253
258
|
|
|
@@ -268,19 +273,5 @@ Common issues:
|
|
|
268
273
|
• VPN or firewall blocking Cursor API
|
|
269
274
|
|
|
270
275
|
For more help, visit: https://docs.cursor.com
|
|
271
|
-
|
|
272
276
|
`);
|
|
273
277
|
}
|
|
274
|
-
|
|
275
|
-
module.exports = {
|
|
276
|
-
checkCursorAgentInstalled,
|
|
277
|
-
getCursorAgentVersion,
|
|
278
|
-
ensureCursorAgent,
|
|
279
|
-
printInstallationGuide,
|
|
280
|
-
checkCursorApiKey,
|
|
281
|
-
validateSetup,
|
|
282
|
-
getAvailableModels,
|
|
283
|
-
testCursorAgent,
|
|
284
|
-
checkCursorAuth,
|
|
285
|
-
printAuthHelp,
|
|
286
|
-
};
|
|
@@ -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
|
-
};
|