@litmers/cursorflow-orchestrator 0.1.18 → 0.1.20
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 +16 -0
- package/README.md +25 -7
- package/dist/cli/clean.js +7 -6
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/index.js +5 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +7 -6
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/logs.js +50 -42
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.js +15 -14
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/prepare.js +37 -20
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js +193 -40
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +3 -2
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/signal.js +7 -7
- package/dist/cli/signal.js.map +1 -1
- package/dist/core/orchestrator.d.ts +2 -1
- package/dist/core/orchestrator.js +48 -91
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/runner.js +55 -20
- package/dist/core/runner.js.map +1 -1
- package/dist/utils/config.js +7 -6
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/doctor.js +7 -6
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.js +14 -11
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/git.js +163 -10
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/log-formatter.d.ts +16 -0
- package/dist/utils/log-formatter.js +194 -0
- package/dist/utils/log-formatter.js.map +1 -0
- package/dist/utils/path.d.ts +19 -0
- package/dist/utils/path.js +77 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/state.d.ts +4 -1
- package/dist/utils/state.js +11 -8
- package/dist/utils/state.js.map +1 -1
- package/dist/utils/template.d.ts +14 -0
- package/dist/utils/template.js +122 -0
- package/dist/utils/template.js.map +1 -0
- package/dist/utils/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/cli/clean.ts +7 -6
- package/src/cli/index.ts +5 -1
- package/src/cli/init.ts +7 -6
- package/src/cli/logs.ts +52 -42
- package/src/cli/monitor.ts +15 -14
- package/src/cli/prepare.ts +39 -20
- package/src/cli/resume.ts +810 -626
- package/src/cli/run.ts +3 -2
- package/src/cli/signal.ts +7 -6
- package/src/core/orchestrator.ts +62 -91
- package/src/core/runner.ts +58 -20
- package/src/utils/config.ts +7 -6
- package/src/utils/doctor.ts +7 -6
- package/src/utils/enhanced-logger.ts +14 -11
- package/src/utils/git.ts +145 -11
- package/src/utils/log-formatter.ts +162 -0
- package/src/utils/path.ts +45 -0
- package/src/utils/state.ts +16 -8
- package/src/utils/template.ts +92 -0
- package/src/utils/types.ts +1 -0
- package/templates/basic.json +21 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensures that a path is safe and stays within a base directory.
|
|
3
|
+
* Prevents path traversal attacks.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isSafePath(baseDir: string, ...parts: string[]): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Safely joins path parts and ensures the result is within the base directory.
|
|
8
|
+
* Throws an error if path traversal is detected.
|
|
9
|
+
*
|
|
10
|
+
* @param baseDir The base directory that the resulting path must be within
|
|
11
|
+
* @param parts Path parts to join
|
|
12
|
+
* @returns The joined path
|
|
13
|
+
* @throws Error if the resulting path is outside the base directory
|
|
14
|
+
*/
|
|
15
|
+
export declare function safeJoin(baseDir: string, ...parts: string[]): string;
|
|
16
|
+
/**
|
|
17
|
+
* Normalizes a path and checks if it's absolute or relative to project root.
|
|
18
|
+
*/
|
|
19
|
+
export declare function normalizePath(p: string, projectRoot: string): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isSafePath = isSafePath;
|
|
37
|
+
exports.safeJoin = safeJoin;
|
|
38
|
+
exports.normalizePath = normalizePath;
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
/**
|
|
41
|
+
* Ensures that a path is safe and stays within a base directory.
|
|
42
|
+
* Prevents path traversal attacks.
|
|
43
|
+
*/
|
|
44
|
+
function isSafePath(baseDir, ...parts) {
|
|
45
|
+
const joined = path.join(baseDir, ...parts); // nosemgrep
|
|
46
|
+
const resolvedBase = path.resolve(baseDir); // nosemgrep
|
|
47
|
+
const resolvedJoined = path.resolve(joined); // nosemgrep
|
|
48
|
+
return resolvedJoined.startsWith(resolvedBase);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Safely joins path parts and ensures the result is within the base directory.
|
|
52
|
+
* Throws an error if path traversal is detected.
|
|
53
|
+
*
|
|
54
|
+
* @param baseDir The base directory that the resulting path must be within
|
|
55
|
+
* @param parts Path parts to join
|
|
56
|
+
* @returns The joined path
|
|
57
|
+
* @throws Error if the resulting path is outside the base directory
|
|
58
|
+
*/
|
|
59
|
+
function safeJoin(baseDir, ...parts) {
|
|
60
|
+
const joined = path.join(baseDir, ...parts); // nosemgrep
|
|
61
|
+
const resolvedBase = path.resolve(baseDir); // nosemgrep
|
|
62
|
+
const resolvedJoined = path.resolve(joined); // nosemgrep
|
|
63
|
+
if (!resolvedJoined.startsWith(resolvedBase)) {
|
|
64
|
+
throw new Error(`Potential path traversal detected: ${joined} is outside of ${baseDir}`);
|
|
65
|
+
}
|
|
66
|
+
return joined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Normalizes a path and checks if it's absolute or relative to project root.
|
|
70
|
+
*/
|
|
71
|
+
function normalizePath(p, projectRoot) {
|
|
72
|
+
if (path.isAbsolute(p)) {
|
|
73
|
+
return path.normalize(p);
|
|
74
|
+
}
|
|
75
|
+
return path.join(projectRoot, p); // nosemgrep
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA,gCAMC;AAWD,4BAUC;AAKD,sCAKC;AA3CD,2CAA6B;AAE7B;;;GAGG;AACH,SAAgB,UAAU,CAAC,OAAe,EAAE,GAAG,KAAe;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,YAAY;IACzD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY;IACxD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY;IAEzD,OAAO,cAAc,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjD,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,QAAQ,CAAC,OAAe,EAAE,GAAG,KAAe;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,YAAY;IACzD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY;IACxD,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY;IAEzD,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,sCAAsC,MAAM,kBAAkB,OAAO,EAAE,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,CAAS,EAAE,WAAmB;IAC1D,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY;AAChD,CAAC"}
|
package/dist/utils/state.d.ts
CHANGED
|
@@ -22,7 +22,10 @@ export declare function readLog<T = any>(logPath: string): T[];
|
|
|
22
22
|
/**
|
|
23
23
|
* Create initial lane state
|
|
24
24
|
*/
|
|
25
|
-
export declare function createLaneState(laneName: string, config: RunnerConfig
|
|
25
|
+
export declare function createLaneState(laneName: string, config: RunnerConfig, tasksFile?: string, options?: {
|
|
26
|
+
pipelineBranch?: string;
|
|
27
|
+
worktreeDir?: string;
|
|
28
|
+
}): LaneState;
|
|
26
29
|
/**
|
|
27
30
|
* Update lane state
|
|
28
31
|
*/
|
package/dist/utils/state.js
CHANGED
|
@@ -50,6 +50,7 @@ exports.listLanesInRun = listLanesInRun;
|
|
|
50
50
|
exports.getLaneStateSummary = getLaneStateSummary;
|
|
51
51
|
const fs = __importStar(require("fs"));
|
|
52
52
|
const path = __importStar(require("path"));
|
|
53
|
+
const path_1 = require("./path");
|
|
53
54
|
/**
|
|
54
55
|
* Save state to JSON file
|
|
55
56
|
*/
|
|
@@ -109,18 +110,20 @@ function readLog(logPath) {
|
|
|
109
110
|
/**
|
|
110
111
|
* Create initial lane state
|
|
111
112
|
*/
|
|
112
|
-
function createLaneState(laneName, config) {
|
|
113
|
+
function createLaneState(laneName, config, tasksFile, options = {}) {
|
|
113
114
|
return {
|
|
114
115
|
label: laneName,
|
|
115
116
|
status: 'pending',
|
|
116
117
|
currentTaskIndex: 0,
|
|
117
118
|
totalTasks: config.tasks ? config.tasks.length : 0,
|
|
118
|
-
worktreeDir: null,
|
|
119
|
-
pipelineBranch: null,
|
|
119
|
+
worktreeDir: options.worktreeDir || null,
|
|
120
|
+
pipelineBranch: options.pipelineBranch || null,
|
|
120
121
|
startTime: Date.now(),
|
|
121
122
|
endTime: null,
|
|
122
123
|
error: null,
|
|
123
124
|
dependencyRequest: null,
|
|
125
|
+
tasksFile,
|
|
126
|
+
dependsOn: config.dependsOn || [],
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
129
|
/**
|
|
@@ -174,13 +177,13 @@ function getLatestRunDir(logsDir) {
|
|
|
174
177
|
return null;
|
|
175
178
|
}
|
|
176
179
|
const runs = fs.readdirSync(logsDir)
|
|
177
|
-
.filter(f => fs.statSync(
|
|
180
|
+
.filter(f => fs.statSync((0, path_1.safeJoin)(logsDir, f)).isDirectory())
|
|
178
181
|
.sort()
|
|
179
182
|
.reverse();
|
|
180
183
|
if (runs.length === 0) {
|
|
181
184
|
return null;
|
|
182
185
|
}
|
|
183
|
-
return
|
|
186
|
+
return (0, path_1.safeJoin)(logsDir, runs[0]);
|
|
184
187
|
}
|
|
185
188
|
/**
|
|
186
189
|
* List all lanes in a run directory
|
|
@@ -190,11 +193,11 @@ function listLanesInRun(runDir) {
|
|
|
190
193
|
return [];
|
|
191
194
|
}
|
|
192
195
|
return fs.readdirSync(runDir)
|
|
193
|
-
.filter(f => fs.statSync(
|
|
196
|
+
.filter(f => fs.statSync((0, path_1.safeJoin)(runDir, f)).isDirectory())
|
|
194
197
|
.map(laneName => ({
|
|
195
198
|
name: laneName,
|
|
196
|
-
dir:
|
|
197
|
-
statePath:
|
|
199
|
+
dir: (0, path_1.safeJoin)(runDir, laneName),
|
|
200
|
+
statePath: (0, path_1.safeJoin)(runDir, laneName, 'state.json'),
|
|
198
201
|
}));
|
|
199
202
|
}
|
|
200
203
|
/**
|
package/dist/utils/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/utils/state.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/utils/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template loading utilities for CursorFlow
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Fetch remote template from URL
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchRemoteTemplate(url: string): Promise<any>;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve template from various sources:
|
|
10
|
+
* 1. URL (starts with http:// or https://)
|
|
11
|
+
* 2. Built-in template (name without .json)
|
|
12
|
+
* 3. Local file path
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveTemplate(templatePath: string): Promise<any>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Template loading utilities for CursorFlow
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.fetchRemoteTemplate = fetchRemoteTemplate;
|
|
40
|
+
exports.resolveTemplate = resolveTemplate;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const https = __importStar(require("https"));
|
|
44
|
+
const http = __importStar(require("http"));
|
|
45
|
+
const logger = __importStar(require("./logger"));
|
|
46
|
+
const path_1 = require("./path");
|
|
47
|
+
const config_1 = require("./config");
|
|
48
|
+
/**
|
|
49
|
+
* Fetch remote template from URL
|
|
50
|
+
*/
|
|
51
|
+
async function fetchRemoteTemplate(url) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
54
|
+
protocol.get(url, (res) => {
|
|
55
|
+
if (res.statusCode !== 200) {
|
|
56
|
+
reject(new Error(`Failed to fetch template from ${url}: Status ${res.statusCode}`));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let data = '';
|
|
60
|
+
res.on('data', (chunk) => {
|
|
61
|
+
data += chunk;
|
|
62
|
+
});
|
|
63
|
+
res.on('end', () => {
|
|
64
|
+
try {
|
|
65
|
+
resolve(JSON.parse(data));
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
reject(new Error(`Failed to parse template JSON from ${url}: ${e}`));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}).on('error', (err) => {
|
|
72
|
+
reject(new Error(`Network error while fetching template from ${url}: ${err.message}`));
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Resolve template from various sources:
|
|
78
|
+
* 1. URL (starts with http:// or https://)
|
|
79
|
+
* 2. Built-in template (name without .json)
|
|
80
|
+
* 3. Local file path
|
|
81
|
+
*/
|
|
82
|
+
async function resolveTemplate(templatePath) {
|
|
83
|
+
// 1. Remote URL
|
|
84
|
+
if (templatePath.startsWith('http://') || templatePath.startsWith('https://')) {
|
|
85
|
+
logger.info(`Fetching remote template: ${templatePath}`);
|
|
86
|
+
return fetchRemoteTemplate(templatePath);
|
|
87
|
+
}
|
|
88
|
+
// 2. Built-in template
|
|
89
|
+
// Search in templates/ directory of the project root
|
|
90
|
+
try {
|
|
91
|
+
const projectRoot = (0, config_1.findProjectRoot)();
|
|
92
|
+
const builtInPath = (0, path_1.safeJoin)(projectRoot, 'templates', templatePath.endsWith('.json') ? templatePath : `${templatePath}.json`);
|
|
93
|
+
if (fs.existsSync(builtInPath)) {
|
|
94
|
+
logger.info(`Using built-in template: ${templatePath}`);
|
|
95
|
+
return JSON.parse(fs.readFileSync(builtInPath, 'utf8'));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
// Ignore error if project root not found, try other methods
|
|
100
|
+
}
|
|
101
|
+
// Fallback for built-in templates relative to the module (for installed package)
|
|
102
|
+
const templatesDir = path.resolve(__dirname, '../../templates');
|
|
103
|
+
const templateFileName = templatePath.endsWith('.json') ? templatePath : `${templatePath}.json`;
|
|
104
|
+
const modulePath = (0, path_1.safeJoin)(templatesDir, templateFileName);
|
|
105
|
+
if (fs.existsSync(modulePath)) {
|
|
106
|
+
logger.info(`Using module template: ${templatePath}`);
|
|
107
|
+
return JSON.parse(fs.readFileSync(modulePath, 'utf8'));
|
|
108
|
+
}
|
|
109
|
+
// 3. Local file path
|
|
110
|
+
const localPath = (0, path_1.safeJoin)(process.cwd(), templatePath);
|
|
111
|
+
if (fs.existsSync(localPath)) {
|
|
112
|
+
logger.info(`Using local template: ${templatePath}`);
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(fs.readFileSync(localPath, 'utf8'));
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
throw new Error(`Failed to parse local template ${templatePath}: ${e}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw new Error(`Template not found: ${templatePath}. It must be a URL, a built-in template name, or a local file path.`);
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/utils/template.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/types.d.ts
CHANGED
package/package.json
CHANGED
package/src/cli/clean.ts
CHANGED
|
@@ -9,6 +9,7 @@ import * as path from 'path';
|
|
|
9
9
|
import * as logger from '../utils/logger';
|
|
10
10
|
import * as git from '../utils/git';
|
|
11
11
|
import { loadConfig, getLogsDir, getTasksDir } from '../utils/config';
|
|
12
|
+
import { safeJoin } from '../utils/path';
|
|
12
13
|
|
|
13
14
|
interface CleanOptions {
|
|
14
15
|
type?: string;
|
|
@@ -105,7 +106,7 @@ async function cleanWorktrees(config: any, repoRoot: string, options: CleanOptio
|
|
|
105
106
|
logger.info('\nChecking worktrees...');
|
|
106
107
|
const worktrees = git.listWorktrees(repoRoot);
|
|
107
108
|
|
|
108
|
-
const worktreeRoot =
|
|
109
|
+
const worktreeRoot = safeJoin(repoRoot, config.worktreeRoot || '_cursorflow/worktrees');
|
|
109
110
|
let toRemove = worktrees.filter(wt => {
|
|
110
111
|
// Skip main worktree
|
|
111
112
|
if (wt.path === repoRoot) return false;
|
|
@@ -226,9 +227,9 @@ async function cleanLogs(config: any, options: CleanOptions) {
|
|
|
226
227
|
const entries = fs.readdirSync(logsDir, { withFileTypes: true });
|
|
227
228
|
let items = entries.map(entry => ({
|
|
228
229
|
name: entry.name,
|
|
229
|
-
path:
|
|
230
|
+
path: safeJoin(logsDir, entry.name),
|
|
230
231
|
isDir: entry.isDirectory(),
|
|
231
|
-
mtime: getModTime(
|
|
232
|
+
mtime: getModTime(safeJoin(logsDir, entry.name))
|
|
232
233
|
}));
|
|
233
234
|
|
|
234
235
|
if (items.length <= 1) {
|
|
@@ -288,9 +289,9 @@ async function cleanTasks(config: any, options: CleanOptions) {
|
|
|
288
289
|
.filter(entry => entry.name !== 'example')
|
|
289
290
|
.map(entry => ({
|
|
290
291
|
name: entry.name,
|
|
291
|
-
path:
|
|
292
|
+
path: safeJoin(tasksDir, entry.name),
|
|
292
293
|
isDir: entry.isDirectory(),
|
|
293
|
-
mtime: getModTime(
|
|
294
|
+
mtime: getModTime(safeJoin(tasksDir, entry.name))
|
|
294
295
|
}));
|
|
295
296
|
|
|
296
297
|
if (items.length <= 1) {
|
|
@@ -325,7 +326,7 @@ async function cleanTasks(config: any, options: CleanOptions) {
|
|
|
325
326
|
const entries = fs.readdirSync(tasksDir, { withFileTypes: true });
|
|
326
327
|
for (const entry of entries) {
|
|
327
328
|
if (entry.name === 'example') continue;
|
|
328
|
-
const itemPath =
|
|
329
|
+
const itemPath = safeJoin(tasksDir, entry.name);
|
|
329
330
|
logger.info(` Removing task: ${entry.name}...`);
|
|
330
331
|
fs.rmSync(itemPath, { recursive: true, force: true });
|
|
331
332
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -34,10 +34,11 @@ function printHelp(): void {
|
|
|
34
34
|
|
|
35
35
|
\x1b[1mCOMMANDS\x1b[0m
|
|
36
36
|
\x1b[33minit\x1b[0m [options] Initialize CursorFlow in project
|
|
37
|
+
\x1b[33msetup\x1b[0m [options] Install Cursor IDE commands
|
|
37
38
|
\x1b[33mprepare\x1b[0m <feature> [opts] Prepare task directory and JSON files
|
|
38
39
|
\x1b[33mrun\x1b[0m <tasks-dir> [options] Run orchestration (DAG-based)
|
|
39
40
|
\x1b[33mmonitor\x1b[0m [run-dir] [options] \x1b[36mInteractive\x1b[0m lane dashboard
|
|
40
|
-
\x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs
|
|
41
|
+
\x1b[33mclean\x1b[0m <type> [options] Clean branches/worktrees/logs/tasks
|
|
41
42
|
\x1b[33mresume\x1b[0m [lane] [options] Resume lane(s) - use --all for batch resume
|
|
42
43
|
\x1b[33mdoctor\x1b[0m [options] Check environment and preflight
|
|
43
44
|
\x1b[33msignal\x1b[0m <lane> <msg> Directly intervene in a running lane
|
|
@@ -54,6 +55,9 @@ function printHelp(): void {
|
|
|
54
55
|
$ \x1b[32mcursorflow prepare NewFeature --lanes 3\x1b[0m
|
|
55
56
|
$ \x1b[32mcursorflow run _cursorflow/tasks/MyFeature/\x1b[0m
|
|
56
57
|
$ \x1b[32mcursorflow monitor latest\x1b[0m
|
|
58
|
+
$ \x1b[32mcursorflow logs --all --follow\x1b[0m
|
|
59
|
+
$ \x1b[32mcursorflow resume --all\x1b[0m
|
|
60
|
+
$ \x1b[32mcursorflow doctor\x1b[0m
|
|
57
61
|
$ \x1b[32mcursorflow models\x1b[0m
|
|
58
62
|
|
|
59
63
|
\x1b[1mDOCUMENTATION\x1b[0m
|
package/src/cli/init.ts
CHANGED
|
@@ -9,6 +9,7 @@ import * as path from 'path';
|
|
|
9
9
|
import * as logger from '../utils/logger';
|
|
10
10
|
import { findProjectRoot, createDefaultConfig, CursorFlowConfig } from '../utils/config';
|
|
11
11
|
import { setupCommands } from './setup-commands';
|
|
12
|
+
import { safeJoin } from '../utils/path';
|
|
12
13
|
|
|
13
14
|
interface InitOptions {
|
|
14
15
|
example: boolean;
|
|
@@ -84,8 +85,8 @@ Examples:
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
function createDirectories(projectRoot: string, config: CursorFlowConfig): void {
|
|
87
|
-
const tasksDir =
|
|
88
|
-
const logsDir =
|
|
88
|
+
const tasksDir = safeJoin(projectRoot, config.tasksDir);
|
|
89
|
+
const logsDir = safeJoin(projectRoot, config.logsDir);
|
|
89
90
|
|
|
90
91
|
if (!fs.existsSync(tasksDir)) {
|
|
91
92
|
fs.mkdirSync(tasksDir, { recursive: true });
|
|
@@ -103,7 +104,7 @@ function createDirectories(projectRoot: string, config: CursorFlowConfig): void
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
function createExampleTasks(projectRoot: string, config: CursorFlowConfig): void {
|
|
106
|
-
const exampleDir =
|
|
107
|
+
const exampleDir = safeJoin(projectRoot, config.tasksDir, 'example');
|
|
107
108
|
|
|
108
109
|
if (!fs.existsSync(exampleDir)) {
|
|
109
110
|
fs.mkdirSync(exampleDir, { recursive: true });
|
|
@@ -134,13 +135,13 @@ Create a simple hello.txt file with a greeting message.
|
|
|
134
135
|
]
|
|
135
136
|
};
|
|
136
137
|
|
|
137
|
-
const taskPath =
|
|
138
|
+
const taskPath = safeJoin(exampleDir, '01-hello.json');
|
|
138
139
|
fs.writeFileSync(taskPath, JSON.stringify(exampleTask, null, 2) + '\n', 'utf8');
|
|
139
140
|
|
|
140
141
|
logger.success(`Created example task: ${path.relative(projectRoot, taskPath)}`);
|
|
141
142
|
|
|
142
143
|
// Create README
|
|
143
|
-
const readmePath =
|
|
144
|
+
const readmePath = safeJoin(exampleDir, 'README.md');
|
|
144
145
|
const readme = `# Example Task
|
|
145
146
|
|
|
146
147
|
This is an example CursorFlow task to help you get started.
|
|
@@ -172,7 +173,7 @@ cursorflow run ${config.tasksDir}/example/
|
|
|
172
173
|
* Add _cursorflow to .gitignore
|
|
173
174
|
*/
|
|
174
175
|
function updateGitignore(projectRoot: string): void {
|
|
175
|
-
const gitignorePath =
|
|
176
|
+
const gitignorePath = safeJoin(projectRoot, '.gitignore');
|
|
176
177
|
const entry = '_cursorflow/';
|
|
177
178
|
|
|
178
179
|
// Try to read existing .gitignore (avoid TOCTOU by reading directly)
|