@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +7 -6
  2. package/dist/cli/clean.d.ts +5 -0
  3. package/dist/cli/clean.js +57 -0
  4. package/dist/cli/clean.js.map +1 -0
  5. package/dist/cli/index.d.ts +6 -0
  6. package/dist/cli/index.js +120 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/cli/init.d.ts +7 -0
  9. package/dist/cli/init.js +302 -0
  10. package/dist/cli/init.js.map +1 -0
  11. package/dist/cli/monitor.d.ts +8 -0
  12. package/dist/cli/monitor.js +210 -0
  13. package/dist/cli/monitor.js.map +1 -0
  14. package/dist/cli/resume.d.ts +5 -0
  15. package/dist/cli/resume.js +58 -0
  16. package/dist/cli/resume.js.map +1 -0
  17. package/dist/cli/run.d.ts +5 -0
  18. package/dist/cli/run.js +74 -0
  19. package/dist/cli/run.js.map +1 -0
  20. package/dist/cli/setup-commands.d.ts +19 -0
  21. package/dist/cli/setup-commands.js +218 -0
  22. package/dist/cli/setup-commands.js.map +1 -0
  23. package/dist/core/orchestrator.d.ts +47 -0
  24. package/dist/core/orchestrator.js +192 -0
  25. package/dist/core/orchestrator.js.map +1 -0
  26. package/dist/core/reviewer.d.ts +60 -0
  27. package/dist/core/reviewer.js +239 -0
  28. package/dist/core/reviewer.js.map +1 -0
  29. package/dist/core/runner.d.ts +49 -0
  30. package/dist/core/runner.js +475 -0
  31. package/dist/core/runner.js.map +1 -0
  32. package/dist/utils/config.d.ts +31 -0
  33. package/dist/utils/config.js +198 -0
  34. package/dist/utils/config.js.map +1 -0
  35. package/dist/utils/cursor-agent.d.ts +61 -0
  36. package/dist/utils/cursor-agent.js +263 -0
  37. package/dist/utils/cursor-agent.js.map +1 -0
  38. package/dist/utils/git.d.ts +131 -0
  39. package/dist/utils/git.js +272 -0
  40. package/dist/utils/git.js.map +1 -0
  41. package/dist/utils/logger.d.ts +68 -0
  42. package/dist/utils/logger.js +158 -0
  43. package/dist/utils/logger.js.map +1 -0
  44. package/dist/utils/state.d.ts +65 -0
  45. package/dist/utils/state.js +216 -0
  46. package/dist/utils/state.js.map +1 -0
  47. package/dist/utils/types.d.ts +117 -0
  48. package/dist/utils/types.js +6 -0
  49. package/dist/utils/types.js.map +1 -0
  50. package/examples/README.md +155 -0
  51. package/examples/demo-project/README.md +262 -0
  52. package/examples/demo-project/_cursorflow/tasks/demo-test/01-create-utils.json +18 -0
  53. package/examples/demo-project/_cursorflow/tasks/demo-test/02-add-tests.json +18 -0
  54. package/examples/demo-project/_cursorflow/tasks/demo-test/README.md +109 -0
  55. package/package.json +71 -61
  56. package/scripts/ai-security-check.js +11 -4
  57. package/scripts/local-security-gate.sh +76 -0
  58. package/src/cli/{clean.js → clean.ts} +11 -5
  59. package/src/cli/{index.js → index.ts} +22 -16
  60. package/src/cli/{init.js → init.ts} +26 -18
  61. package/src/cli/{monitor.js → monitor.ts} +57 -44
  62. package/src/cli/{resume.js → resume.ts} +11 -5
  63. package/src/cli/run.ts +54 -0
  64. package/src/cli/{setup-commands.js → setup-commands.ts} +19 -18
  65. package/src/core/{orchestrator.js → orchestrator.ts} +44 -26
  66. package/src/core/{reviewer.js → reviewer.ts} +36 -29
  67. package/src/core/{runner.js → runner.ts} +78 -56
  68. package/src/utils/{config.js → config.ts} +17 -25
  69. package/src/utils/{cursor-agent.js → cursor-agent.ts} +38 -47
  70. package/src/utils/{git.js → git.ts} +70 -56
  71. package/src/utils/{logger.js → logger.ts} +170 -178
  72. package/src/utils/{state.js → state.ts} +30 -38
  73. package/src/utils/types.ts +133 -0
  74. 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
- const path = require('path');
9
- const fs = require('fs');
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', // 'cursor-agent' | 'cloud'
50
- pollInterval: 60, // seconds
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, // 3000 + laneNumber
64
+ devPort: 3001,
64
65
  autoCreatePr: false,
65
66
  },
66
67
 
67
68
  // Logging
68
- logLevel: 'info', // 'error' | 'warn' | 'info' | 'debug'
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
- const { execSync, spawnSync } = require('child_process');
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
- execSync('cursor-agent --version', { stdio: 'pipe' });
14
- return true;
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 version = execSync('cursor-agent --version', {
24
+ const result = spawnSync('cursor-agent', ['--version'], {
26
25
  encoding: 'utf8',
27
26
  stdio: 'pipe',
28
- }).trim();
29
- return version;
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.CURSOR_API_KEY;
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
- const result = execSync('cursor-agent --model invalid "test"', {
121
- encoding: 'utf8',
122
- stdio: 'pipe',
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
- const { execSync, spawnSync } = require('child_process');
7
- const path = require('path');
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 = execSync(`git ${args.join(' ')}`, {
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
- return result ? result.trim() : '';
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
- };