@litmers/cursorflow-orchestrator 0.1.20 → 0.1.28
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 +20 -0
- package/commands/cursorflow-clean.md +19 -0
- package/commands/cursorflow-runs.md +59 -0
- package/commands/cursorflow-stop.md +55 -0
- package/dist/cli/clean.js +171 -0
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +1 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/logs.js +83 -42
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.d.ts +7 -0
- package/dist/cli/monitor.js +1007 -189
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/prepare.js +87 -3
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js +188 -236
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +125 -3
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/runs.d.ts +5 -0
- package/dist/cli/runs.js +214 -0
- package/dist/cli/runs.js.map +1 -0
- package/dist/cli/setup-commands.js +0 -0
- package/dist/cli/signal.js +1 -1
- package/dist/cli/signal.js.map +1 -1
- package/dist/cli/stop.d.ts +5 -0
- package/dist/cli/stop.js +215 -0
- package/dist/cli/stop.js.map +1 -0
- package/dist/cli/tasks.d.ts +10 -0
- package/dist/cli/tasks.js +165 -0
- package/dist/cli/tasks.js.map +1 -0
- package/dist/core/auto-recovery.d.ts +212 -0
- package/dist/core/auto-recovery.js +737 -0
- package/dist/core/auto-recovery.js.map +1 -0
- package/dist/core/failure-policy.d.ts +156 -0
- package/dist/core/failure-policy.js +488 -0
- package/dist/core/failure-policy.js.map +1 -0
- package/dist/core/orchestrator.d.ts +15 -2
- package/dist/core/orchestrator.js +397 -15
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/reviewer.d.ts +2 -0
- package/dist/core/reviewer.js +2 -0
- package/dist/core/reviewer.js.map +1 -1
- package/dist/core/runner.d.ts +33 -10
- package/dist/core/runner.js +321 -146
- package/dist/core/runner.js.map +1 -1
- package/dist/services/logging/buffer.d.ts +67 -0
- package/dist/services/logging/buffer.js +309 -0
- package/dist/services/logging/buffer.js.map +1 -0
- package/dist/services/logging/console.d.ts +89 -0
- package/dist/services/logging/console.js +169 -0
- package/dist/services/logging/console.js.map +1 -0
- package/dist/services/logging/file-writer.d.ts +71 -0
- package/dist/services/logging/file-writer.js +516 -0
- package/dist/services/logging/file-writer.js.map +1 -0
- package/dist/services/logging/formatter.d.ts +39 -0
- package/dist/services/logging/formatter.js +227 -0
- package/dist/services/logging/formatter.js.map +1 -0
- package/dist/services/logging/index.d.ts +11 -0
- package/dist/services/logging/index.js +30 -0
- package/dist/services/logging/index.js.map +1 -0
- package/dist/services/logging/parser.d.ts +31 -0
- package/dist/services/logging/parser.js +222 -0
- package/dist/services/logging/parser.js.map +1 -0
- package/dist/services/process/index.d.ts +59 -0
- package/dist/services/process/index.js +257 -0
- package/dist/services/process/index.js.map +1 -0
- package/dist/types/agent.d.ts +20 -0
- package/dist/types/agent.js +6 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/config.d.ts +65 -0
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/events.d.ts +125 -0
- package/dist/types/events.js +6 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.js +37 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/lane.d.ts +43 -0
- package/dist/types/lane.js +6 -0
- package/dist/types/lane.js.map +1 -0
- package/dist/types/logging.d.ts +71 -0
- package/dist/types/logging.js +16 -0
- package/dist/types/logging.js.map +1 -0
- package/dist/types/review.d.ts +17 -0
- package/dist/types/review.js +6 -0
- package/dist/types/review.js.map +1 -0
- package/dist/types/run.d.ts +32 -0
- package/dist/types/run.js +6 -0
- package/dist/types/run.js.map +1 -0
- package/dist/types/task.d.ts +71 -0
- package/dist/types/task.js +6 -0
- package/dist/types/task.js.map +1 -0
- package/dist/ui/components.d.ts +134 -0
- package/dist/ui/components.js +389 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/log-viewer.d.ts +49 -0
- package/dist/ui/log-viewer.js +449 -0
- package/dist/ui/log-viewer.js.map +1 -0
- package/dist/utils/checkpoint.d.ts +87 -0
- package/dist/utils/checkpoint.js +317 -0
- package/dist/utils/checkpoint.js.map +1 -0
- package/dist/utils/config.d.ts +4 -0
- package/dist/utils/config.js +11 -2
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/cursor-agent.js.map +1 -1
- package/dist/utils/dependency.d.ts +74 -0
- package/dist/utils/dependency.js +420 -0
- package/dist/utils/dependency.js.map +1 -0
- package/dist/utils/doctor.js +10 -5
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +10 -33
- package/dist/utils/enhanced-logger.js +94 -9
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/git.d.ts +121 -0
- package/dist/utils/git.js +322 -2
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/health.d.ts +91 -0
- package/dist/utils/health.js +556 -0
- package/dist/utils/health.js.map +1 -0
- package/dist/utils/lock.d.ts +95 -0
- package/dist/utils/lock.js +332 -0
- package/dist/utils/lock.js.map +1 -0
- package/dist/utils/log-buffer.d.ts +17 -0
- package/dist/utils/log-buffer.js +14 -0
- package/dist/utils/log-buffer.js.map +1 -0
- package/dist/utils/log-constants.d.ts +23 -0
- package/dist/utils/log-constants.js +28 -0
- package/dist/utils/log-constants.js.map +1 -0
- package/dist/utils/log-formatter.d.ts +9 -0
- package/dist/utils/log-formatter.js +113 -70
- package/dist/utils/log-formatter.js.map +1 -1
- package/dist/utils/log-service.d.ts +19 -0
- package/dist/utils/log-service.js +47 -0
- package/dist/utils/log-service.js.map +1 -0
- package/dist/utils/logger.d.ts +46 -27
- package/dist/utils/logger.js +82 -60
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/process-manager.d.ts +21 -0
- package/dist/utils/process-manager.js +138 -0
- package/dist/utils/process-manager.js.map +1 -0
- package/dist/utils/retry.d.ts +121 -0
- package/dist/utils/retry.js +374 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/run-service.d.ts +88 -0
- package/dist/utils/run-service.js +412 -0
- package/dist/utils/run-service.js.map +1 -0
- package/dist/utils/state.d.ts +58 -2
- package/dist/utils/state.js +306 -3
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/task-service.d.ts +82 -0
- package/dist/utils/task-service.js +348 -0
- package/dist/utils/task-service.js.map +1 -0
- package/dist/utils/types.d.ts +2 -272
- package/dist/utils/types.js +16 -0
- package/dist/utils/types.js.map +1 -1
- package/package.json +38 -23
- package/scripts/ai-security-check.js +0 -1
- package/scripts/local-security-gate.sh +0 -0
- package/scripts/monitor-lanes.sh +94 -0
- package/scripts/patches/test-cursor-agent.js +0 -1
- package/scripts/release.sh +0 -0
- package/scripts/setup-security.sh +0 -0
- package/scripts/stream-logs.sh +72 -0
- package/scripts/verify-and-fix.sh +0 -0
- package/src/cli/clean.ts +180 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/init.ts +1 -1
- package/src/cli/logs.ts +79 -42
- package/src/cli/monitor.ts +1815 -899
- package/src/cli/prepare.ts +97 -3
- package/src/cli/resume.ts +220 -277
- package/src/cli/run.ts +154 -3
- package/src/cli/runs.ts +212 -0
- package/src/cli/setup-commands.ts +0 -0
- package/src/cli/signal.ts +1 -1
- package/src/cli/stop.ts +209 -0
- package/src/cli/tasks.ts +154 -0
- package/src/core/auto-recovery.ts +909 -0
- package/src/core/failure-policy.ts +592 -0
- package/src/core/orchestrator.ts +1136 -675
- package/src/core/reviewer.ts +4 -0
- package/src/core/runner.ts +1443 -1217
- package/src/services/logging/buffer.ts +326 -0
- package/src/services/logging/console.ts +193 -0
- package/src/services/logging/file-writer.ts +526 -0
- package/src/services/logging/formatter.ts +268 -0
- package/src/services/logging/index.ts +16 -0
- package/src/services/logging/parser.ts +232 -0
- package/src/services/process/index.ts +261 -0
- package/src/types/agent.ts +24 -0
- package/src/types/config.ts +79 -0
- package/src/types/events.ts +156 -0
- package/src/types/index.ts +29 -0
- package/src/types/lane.ts +56 -0
- package/src/types/logging.ts +96 -0
- package/src/types/review.ts +20 -0
- package/src/types/run.ts +37 -0
- package/src/types/task.ts +79 -0
- package/src/ui/components.ts +430 -0
- package/src/ui/log-viewer.ts +485 -0
- package/src/utils/checkpoint.ts +374 -0
- package/src/utils/config.ts +11 -2
- package/src/utils/cursor-agent.ts +1 -1
- package/src/utils/dependency.ts +482 -0
- package/src/utils/doctor.ts +11 -5
- package/src/utils/enhanced-logger.ts +108 -49
- package/src/utils/git.ts +871 -499
- package/src/utils/health.ts +596 -0
- package/src/utils/lock.ts +346 -0
- package/src/utils/log-buffer.ts +28 -0
- package/src/utils/log-constants.ts +26 -0
- package/src/utils/log-formatter.ts +120 -37
- package/src/utils/log-service.ts +49 -0
- package/src/utils/logger.ts +100 -51
- package/src/utils/process-manager.ts +100 -0
- package/src/utils/retry.ts +413 -0
- package/src/utils/run-service.ts +433 -0
- package/src/utils/state.ts +369 -3
- package/src/utils/task-service.ts +370 -0
- package/src/utils/types.ts +2 -315
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint and recovery system for CursorFlow
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { safeJoin } from './path';
|
|
8
|
+
import * as git from './git';
|
|
9
|
+
import { LaneState } from './types';
|
|
10
|
+
import { loadState, saveState } from './state';
|
|
11
|
+
import * as logger from './logger';
|
|
12
|
+
|
|
13
|
+
export interface GitState {
|
|
14
|
+
branch: string | null;
|
|
15
|
+
commit: string | null;
|
|
16
|
+
uncommittedChanges: boolean;
|
|
17
|
+
changedFiles: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Checkpoint {
|
|
21
|
+
id: string;
|
|
22
|
+
timestamp: number;
|
|
23
|
+
laneName: string;
|
|
24
|
+
laneState: LaneState;
|
|
25
|
+
gitState: GitState | null;
|
|
26
|
+
taskIndex: number;
|
|
27
|
+
description?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface CheckpointOptions {
|
|
31
|
+
/** Directory to store checkpoints */
|
|
32
|
+
checkpointDir?: string;
|
|
33
|
+
/** Maximum number of checkpoints to keep per lane */
|
|
34
|
+
maxCheckpoints?: number;
|
|
35
|
+
/** Description of the checkpoint */
|
|
36
|
+
description?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_MAX_CHECKPOINTS = 10;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get checkpoint directory for a lane
|
|
43
|
+
*/
|
|
44
|
+
export function getCheckpointDir(laneDir: string): string {
|
|
45
|
+
return safeJoin(laneDir, 'checkpoints');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate checkpoint ID
|
|
50
|
+
*/
|
|
51
|
+
function generateCheckpointId(): string {
|
|
52
|
+
return `cp-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a checkpoint for a lane
|
|
57
|
+
*/
|
|
58
|
+
export async function createCheckpoint(
|
|
59
|
+
laneName: string,
|
|
60
|
+
laneDir: string,
|
|
61
|
+
worktreeDir: string | null,
|
|
62
|
+
options: CheckpointOptions = {}
|
|
63
|
+
): Promise<Checkpoint> {
|
|
64
|
+
const checkpointDir = options.checkpointDir || getCheckpointDir(laneDir);
|
|
65
|
+
const maxCheckpoints = options.maxCheckpoints || DEFAULT_MAX_CHECKPOINTS;
|
|
66
|
+
|
|
67
|
+
// Ensure checkpoint directory exists
|
|
68
|
+
if (!fs.existsSync(checkpointDir)) {
|
|
69
|
+
fs.mkdirSync(checkpointDir, { recursive: true });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Load current lane state
|
|
73
|
+
const statePath = safeJoin(laneDir, 'state.json');
|
|
74
|
+
const laneState = loadState<LaneState>(statePath);
|
|
75
|
+
|
|
76
|
+
if (!laneState) {
|
|
77
|
+
throw new Error(`Cannot create checkpoint: Lane state not found at ${statePath}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get Git state if worktree exists
|
|
81
|
+
let gitState: GitState | null = null;
|
|
82
|
+
if (worktreeDir && fs.existsSync(worktreeDir)) {
|
|
83
|
+
try {
|
|
84
|
+
const branch = git.getCurrentBranch(worktreeDir);
|
|
85
|
+
const commit = git.runGit(['rev-parse', 'HEAD'], { cwd: worktreeDir, silent: true });
|
|
86
|
+
const changedFiles = git.getChangedFiles(worktreeDir);
|
|
87
|
+
|
|
88
|
+
gitState = {
|
|
89
|
+
branch,
|
|
90
|
+
commit,
|
|
91
|
+
uncommittedChanges: changedFiles.length > 0,
|
|
92
|
+
changedFiles: changedFiles.map(f => f.file),
|
|
93
|
+
};
|
|
94
|
+
} catch (e) {
|
|
95
|
+
logger.warn(`Failed to capture Git state for checkpoint: ${e}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Create checkpoint
|
|
100
|
+
const checkpoint: Checkpoint = {
|
|
101
|
+
id: generateCheckpointId(),
|
|
102
|
+
timestamp: Date.now(),
|
|
103
|
+
laneName,
|
|
104
|
+
laneState: { ...laneState },
|
|
105
|
+
gitState,
|
|
106
|
+
taskIndex: laneState.currentTaskIndex,
|
|
107
|
+
description: options.description,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Save checkpoint
|
|
111
|
+
const checkpointPath = safeJoin(checkpointDir, `${checkpoint.id}.json`);
|
|
112
|
+
fs.writeFileSync(checkpointPath, JSON.stringify(checkpoint, null, 2), 'utf8');
|
|
113
|
+
|
|
114
|
+
// Cleanup old checkpoints
|
|
115
|
+
await cleanupOldCheckpoints(checkpointDir, maxCheckpoints);
|
|
116
|
+
|
|
117
|
+
logger.info(`Created checkpoint: ${checkpoint.id} (task ${checkpoint.taskIndex})`);
|
|
118
|
+
|
|
119
|
+
return checkpoint;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* List all checkpoints for a lane
|
|
124
|
+
*/
|
|
125
|
+
export function listCheckpoints(laneDir: string): Checkpoint[] {
|
|
126
|
+
const checkpointDir = getCheckpointDir(laneDir);
|
|
127
|
+
|
|
128
|
+
if (!fs.existsSync(checkpointDir)) {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const files = fs.readdirSync(checkpointDir)
|
|
133
|
+
.filter(f => f.startsWith('cp-') && f.endsWith('.json'))
|
|
134
|
+
.sort()
|
|
135
|
+
.reverse(); // Most recent first
|
|
136
|
+
|
|
137
|
+
return files.map(f => {
|
|
138
|
+
try {
|
|
139
|
+
const content = fs.readFileSync(safeJoin(checkpointDir, f), 'utf8');
|
|
140
|
+
return JSON.parse(content) as Checkpoint;
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}).filter((cp): cp is Checkpoint => cp !== null);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get a specific checkpoint
|
|
149
|
+
*/
|
|
150
|
+
export function getCheckpoint(laneDir: string, checkpointId: string): Checkpoint | null {
|
|
151
|
+
const checkpointDir = getCheckpointDir(laneDir);
|
|
152
|
+
const checkpointPath = safeJoin(checkpointDir, `${checkpointId}.json`);
|
|
153
|
+
|
|
154
|
+
if (!fs.existsSync(checkpointPath)) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const content = fs.readFileSync(checkpointPath, 'utf8');
|
|
160
|
+
return JSON.parse(content) as Checkpoint;
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get the latest checkpoint for a lane
|
|
168
|
+
*/
|
|
169
|
+
export function getLatestCheckpoint(laneDir: string): Checkpoint | null {
|
|
170
|
+
const checkpoints = listCheckpoints(laneDir);
|
|
171
|
+
return checkpoints.length > 0 ? checkpoints[0]! : null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Restore lane state from a checkpoint
|
|
176
|
+
*/
|
|
177
|
+
export async function restoreFromCheckpoint(
|
|
178
|
+
checkpoint: Checkpoint,
|
|
179
|
+
laneDir: string,
|
|
180
|
+
options: {
|
|
181
|
+
restoreGitState?: boolean;
|
|
182
|
+
worktreeDir?: string;
|
|
183
|
+
} = {}
|
|
184
|
+
): Promise<{ success: boolean; warnings: string[] }> {
|
|
185
|
+
const warnings: string[] = [];
|
|
186
|
+
|
|
187
|
+
// Restore lane state
|
|
188
|
+
const statePath = safeJoin(laneDir, 'state.json');
|
|
189
|
+
const restoredState: LaneState = {
|
|
190
|
+
...checkpoint.laneState,
|
|
191
|
+
status: 'pending', // Reset status for resume
|
|
192
|
+
error: null,
|
|
193
|
+
updatedAt: Date.now(),
|
|
194
|
+
};
|
|
195
|
+
saveState(statePath, restoredState);
|
|
196
|
+
|
|
197
|
+
logger.info(`Restored lane state from checkpoint ${checkpoint.id}`);
|
|
198
|
+
|
|
199
|
+
// Restore Git state if requested
|
|
200
|
+
if (options.restoreGitState && checkpoint.gitState && options.worktreeDir) {
|
|
201
|
+
const worktreeDir = options.worktreeDir;
|
|
202
|
+
|
|
203
|
+
if (!fs.existsSync(worktreeDir)) {
|
|
204
|
+
warnings.push(`Worktree not found: ${worktreeDir}`);
|
|
205
|
+
} else {
|
|
206
|
+
try {
|
|
207
|
+
// Check for uncommitted changes
|
|
208
|
+
if (git.hasUncommittedChanges(worktreeDir)) {
|
|
209
|
+
warnings.push('Worktree has uncommitted changes. Stashing...');
|
|
210
|
+
git.runGit(['stash', 'push', '-m', `Pre-restore checkpoint ${checkpoint.id}`], { cwd: worktreeDir });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Checkout the checkpoint commit if available
|
|
214
|
+
if (checkpoint.gitState.commit) {
|
|
215
|
+
git.runGit(['checkout', checkpoint.gitState.commit], { cwd: worktreeDir, silent: true });
|
|
216
|
+
logger.info(`Restored Git state to commit ${checkpoint.gitState.commit.substring(0, 7)}`);
|
|
217
|
+
}
|
|
218
|
+
} catch (e: any) {
|
|
219
|
+
warnings.push(`Failed to restore Git state: ${e.message}`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return { success: true, warnings };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Delete a checkpoint
|
|
229
|
+
*/
|
|
230
|
+
export function deleteCheckpoint(laneDir: string, checkpointId: string): boolean {
|
|
231
|
+
const checkpointDir = getCheckpointDir(laneDir);
|
|
232
|
+
const checkpointPath = safeJoin(checkpointDir, `${checkpointId}.json`);
|
|
233
|
+
|
|
234
|
+
if (!fs.existsSync(checkpointPath)) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
fs.unlinkSync(checkpointPath);
|
|
240
|
+
return true;
|
|
241
|
+
} catch {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Clean up old checkpoints, keeping only the most recent ones
|
|
248
|
+
*/
|
|
249
|
+
async function cleanupOldCheckpoints(checkpointDir: string, maxCheckpoints: number): Promise<number> {
|
|
250
|
+
if (!fs.existsSync(checkpointDir)) {
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const files = fs.readdirSync(checkpointDir)
|
|
255
|
+
.filter(f => f.startsWith('cp-') && f.endsWith('.json'))
|
|
256
|
+
.sort()
|
|
257
|
+
.reverse();
|
|
258
|
+
|
|
259
|
+
let deleted = 0;
|
|
260
|
+
for (let i = maxCheckpoints; i < files.length; i++) {
|
|
261
|
+
try {
|
|
262
|
+
fs.unlinkSync(safeJoin(checkpointDir, files[i]!));
|
|
263
|
+
deleted++;
|
|
264
|
+
} catch {
|
|
265
|
+
// Ignore
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return deleted;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Auto-checkpoint decorator - creates checkpoints before critical operations
|
|
274
|
+
*/
|
|
275
|
+
export function withAutoCheckpoint<T extends (...args: any[]) => Promise<any>>(
|
|
276
|
+
fn: T,
|
|
277
|
+
options: {
|
|
278
|
+
getLaneDir: (...args: Parameters<T>) => string;
|
|
279
|
+
getLaneName: (...args: Parameters<T>) => string;
|
|
280
|
+
getWorktreeDir?: (...args: Parameters<T>) => string | null;
|
|
281
|
+
description?: string;
|
|
282
|
+
}
|
|
283
|
+
): T {
|
|
284
|
+
return (async (...args: Parameters<T>): Promise<ReturnType<T>> => {
|
|
285
|
+
const laneDir = options.getLaneDir(...args);
|
|
286
|
+
const laneName = options.getLaneName(...args);
|
|
287
|
+
const worktreeDir = options.getWorktreeDir?.(...args) || null;
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
await createCheckpoint(laneName, laneDir, worktreeDir, {
|
|
291
|
+
description: options.description || `Before ${fn.name}`,
|
|
292
|
+
});
|
|
293
|
+
} catch (e) {
|
|
294
|
+
logger.warn(`Auto-checkpoint failed: ${e}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return fn(...args);
|
|
298
|
+
}) as T;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Find the best checkpoint to recover from after a failure
|
|
303
|
+
*/
|
|
304
|
+
export function findRecoveryCheckpoint(
|
|
305
|
+
laneDir: string,
|
|
306
|
+
targetTaskIndex?: number
|
|
307
|
+
): Checkpoint | null {
|
|
308
|
+
const checkpoints = listCheckpoints(laneDir);
|
|
309
|
+
|
|
310
|
+
if (checkpoints.length === 0) {
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// If target task index is specified, find the checkpoint just before it
|
|
315
|
+
if (targetTaskIndex !== undefined) {
|
|
316
|
+
for (const cp of checkpoints) {
|
|
317
|
+
if (cp.taskIndex <= targetTaskIndex) {
|
|
318
|
+
return cp;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Return the latest checkpoint
|
|
324
|
+
return checkpoints[0]!;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Checkpoint statistics for monitoring
|
|
329
|
+
*/
|
|
330
|
+
export interface CheckpointStats {
|
|
331
|
+
totalCheckpoints: number;
|
|
332
|
+
oldestTimestamp: number | null;
|
|
333
|
+
newestTimestamp: number | null;
|
|
334
|
+
totalSizeBytes: number;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Get checkpoint statistics for a lane
|
|
339
|
+
*/
|
|
340
|
+
export function getCheckpointStats(laneDir: string): CheckpointStats {
|
|
341
|
+
const checkpointDir = getCheckpointDir(laneDir);
|
|
342
|
+
|
|
343
|
+
if (!fs.existsSync(checkpointDir)) {
|
|
344
|
+
return {
|
|
345
|
+
totalCheckpoints: 0,
|
|
346
|
+
oldestTimestamp: null,
|
|
347
|
+
newestTimestamp: null,
|
|
348
|
+
totalSizeBytes: 0,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const checkpoints = listCheckpoints(laneDir);
|
|
353
|
+
let totalSize = 0;
|
|
354
|
+
|
|
355
|
+
const files = fs.readdirSync(checkpointDir)
|
|
356
|
+
.filter(f => f.startsWith('cp-') && f.endsWith('.json'));
|
|
357
|
+
|
|
358
|
+
for (const f of files) {
|
|
359
|
+
try {
|
|
360
|
+
const stat = fs.statSync(safeJoin(checkpointDir, f));
|
|
361
|
+
totalSize += stat.size;
|
|
362
|
+
} catch {
|
|
363
|
+
// Ignore
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
totalCheckpoints: checkpoints.length,
|
|
369
|
+
oldestTimestamp: checkpoints.length > 0 ? checkpoints[checkpoints.length - 1]!.timestamp : null,
|
|
370
|
+
newestTimestamp: checkpoints.length > 0 ? checkpoints[0]!.timestamp : null,
|
|
371
|
+
totalSizeBytes: totalSize,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
package/src/utils/config.ts
CHANGED
|
@@ -44,9 +44,10 @@ export function loadConfig(projectRoot: string | null = null): CursorFlowConfig
|
|
|
44
44
|
// Directories
|
|
45
45
|
tasksDir: '_cursorflow/tasks',
|
|
46
46
|
logsDir: '_cursorflow/logs',
|
|
47
|
+
pofDir: '_cursorflow/pof',
|
|
47
48
|
|
|
48
49
|
// Git
|
|
49
|
-
baseBranch
|
|
50
|
+
// baseBranch is auto-detected from current branch at runtime
|
|
50
51
|
branchPrefix: 'feature/',
|
|
51
52
|
|
|
52
53
|
// Execution
|
|
@@ -125,6 +126,13 @@ export function getLogsDir(config: CursorFlowConfig): string {
|
|
|
125
126
|
return safeJoin(config.projectRoot, config.logsDir);
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Get absolute path for POF directory
|
|
131
|
+
*/
|
|
132
|
+
export function getPofDir(config: CursorFlowConfig): string {
|
|
133
|
+
return safeJoin(config.projectRoot, config.pofDir);
|
|
134
|
+
}
|
|
135
|
+
|
|
128
136
|
/**
|
|
129
137
|
* Validate configuration
|
|
130
138
|
*/
|
|
@@ -164,9 +172,10 @@ export function createDefaultConfig(projectRoot: string, force = false): string
|
|
|
164
172
|
// Directory configuration
|
|
165
173
|
tasksDir: '_cursorflow/tasks',
|
|
166
174
|
logsDir: '_cursorflow/logs',
|
|
175
|
+
pofDir: '_cursorflow/pof',
|
|
167
176
|
|
|
168
177
|
// Git configuration
|
|
169
|
-
baseBranch: 'main',
|
|
178
|
+
baseBranch: git.getCurrentBranch() || 'main',
|
|
170
179
|
branchPrefix: 'feature/',
|
|
171
180
|
|
|
172
181
|
// Execution configuration
|