@litmers/cursorflow-orchestrator 0.1.14 → 0.1.18
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 +9 -0
- package/README.md +1 -0
- package/commands/cursorflow-run.md +2 -0
- package/commands/cursorflow-triggers.md +250 -0
- package/dist/cli/clean.js +1 -1
- package/dist/cli/clean.js.map +1 -1
- package/dist/cli/init.js +13 -8
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/logs.js +66 -44
- package/dist/cli/logs.js.map +1 -1
- package/dist/cli/monitor.js +12 -3
- package/dist/cli/monitor.js.map +1 -1
- package/dist/cli/prepare.js +36 -13
- package/dist/cli/prepare.js.map +1 -1
- package/dist/cli/resume.js.map +1 -1
- package/dist/cli/run.js +7 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/core/orchestrator.d.ts +3 -1
- package/dist/core/orchestrator.js +154 -11
- package/dist/core/orchestrator.js.map +1 -1
- package/dist/core/reviewer.d.ts +8 -4
- package/dist/core/reviewer.js +11 -7
- package/dist/core/reviewer.js.map +1 -1
- package/dist/core/runner.d.ts +17 -3
- package/dist/core/runner.js +326 -69
- package/dist/core/runner.js.map +1 -1
- package/dist/utils/config.js +17 -5
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/doctor.js +28 -1
- package/dist/utils/doctor.js.map +1 -1
- package/dist/utils/enhanced-logger.d.ts +5 -4
- package/dist/utils/enhanced-logger.js +178 -43
- package/dist/utils/enhanced-logger.js.map +1 -1
- package/dist/utils/git.d.ts +6 -0
- package/dist/utils/git.js +15 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +4 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/repro-thinking-logs.d.ts +1 -0
- package/dist/utils/repro-thinking-logs.js +80 -0
- package/dist/utils/repro-thinking-logs.js.map +1 -0
- package/dist/utils/types.d.ts +22 -0
- package/dist/utils/webhook.js +3 -0
- package/dist/utils/webhook.js.map +1 -1
- package/package.json +4 -1
- package/scripts/ai-security-check.js +3 -0
- package/scripts/local-security-gate.sh +9 -1
- package/scripts/patches/test-cursor-agent.js +1 -1
- package/scripts/verify-and-fix.sh +37 -0
- package/src/cli/clean.ts +1 -1
- package/src/cli/init.ts +12 -9
- package/src/cli/logs.ts +68 -43
- package/src/cli/monitor.ts +13 -4
- package/src/cli/prepare.ts +36 -15
- package/src/cli/resume.ts +1 -1
- package/src/cli/run.ts +8 -0
- package/src/core/orchestrator.ts +171 -11
- package/src/core/reviewer.ts +30 -11
- package/src/core/runner.ts +346 -71
- package/src/utils/config.ts +17 -6
- package/src/utils/doctor.ts +31 -1
- package/src/utils/enhanced-logger.ts +182 -48
- package/src/utils/git.ts +15 -0
- package/src/utils/logger.ts +4 -1
- package/src/utils/repro-thinking-logs.ts +54 -0
- package/src/utils/types.ts +22 -0
- package/src/utils/webhook.ts +3 -0
- package/scripts/simple-logging-test.sh +0 -97
- package/scripts/test-real-logging.sh +0 -289
- package/scripts/test-streaming-multi-task.sh +0 -247
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const enhanced_logger_1 = require("./enhanced-logger");
|
|
39
|
+
async function testThinkingLogs() {
|
|
40
|
+
const testDir = path.join(process.cwd(), '_test_thinking_logs');
|
|
41
|
+
if (fs.existsSync(testDir)) {
|
|
42
|
+
fs.rmSync(testDir, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
fs.mkdirSync(testDir, { recursive: true });
|
|
45
|
+
console.log('--- Initializing Log Manager ---');
|
|
46
|
+
const manager = (0, enhanced_logger_1.createLogManager)(testDir, 'test-lane-thinking', {
|
|
47
|
+
writeJsonLog: true,
|
|
48
|
+
keepRawLogs: true
|
|
49
|
+
});
|
|
50
|
+
manager.setTask('repro-thinking-task', 'sonnet-4.5-thinking');
|
|
51
|
+
const logLines = [
|
|
52
|
+
'{"type":"tool_call","subtype":"started","call_id":"0_tool_54a8fcc9-6981-4f59-aeb6-3ab6d37b2","tool_call":{"readToolCall":{"args":{"path":"/home/eugene/workbench/workbench-os-eungjin/_cursorflow/worktrees/cursorflow/run-mjfxp57i/agent_output.txt"}}}}',
|
|
53
|
+
'{"type":"thinking","subtype":"delta","text":"**Defining Installation Strategy**\\n\\nI\'ve considered the `package.json` file as the central point for installation in automated environments. Thinking now about how that impacts the user\'s ultimate goal, given this is how the process begins in these environments.\\n\\n\\n"}',
|
|
54
|
+
'{"type":"thinking","subtype":"delta","text":"**Clarifying Execution Context**\\n\\nI\'m focused on the user\'s explicit command: `pnpm add @convex-dev/agent ai @ai-sdk/google zod`. My inability to directly execute this is a key constraint. I\'m exploring ways to inform the user about this. I\'ve considered that, without the tools required to run the original command, any attempt to run a command like `grep` or `date` is pointless and will fail.\\n\\n\\n"}',
|
|
55
|
+
'{"type":"tool_call","subtype":"started","call_id":"0_tool_d8f826c8-9d8f-4cab-9ff8-1c47d1ac1","tool_call":{"shellToolCall":{"args":{"command":"date"}}}}'
|
|
56
|
+
];
|
|
57
|
+
console.log('\n--- Feeding Log Lines to Manager ---');
|
|
58
|
+
for (const line of logLines) {
|
|
59
|
+
console.log('Processing:', line.substring(0, 100) + '...');
|
|
60
|
+
manager.writeStdout(line + '\n');
|
|
61
|
+
}
|
|
62
|
+
manager.close();
|
|
63
|
+
console.log('\n--- Verifying terminal-readable.log ---');
|
|
64
|
+
const readableLog = fs.readFileSync(path.join(testDir, 'terminal-readable.log'), 'utf8');
|
|
65
|
+
console.log(readableLog);
|
|
66
|
+
console.log('\n--- Verifying terminal.jsonl (last 3 entries) ---');
|
|
67
|
+
const jsonlLog = fs.readFileSync(path.join(testDir, 'terminal.jsonl'), 'utf8');
|
|
68
|
+
const lines = jsonlLog.trim().split('\n');
|
|
69
|
+
for (const line of lines.slice(-3)) {
|
|
70
|
+
const parsed = JSON.parse(line);
|
|
71
|
+
console.log(JSON.stringify({
|
|
72
|
+
level: parsed.level,
|
|
73
|
+
message: parsed.message.substring(0, 50) + '...',
|
|
74
|
+
hasMetadata: !!parsed.metadata,
|
|
75
|
+
metadataType: parsed.metadata?.type
|
|
76
|
+
}, null, 2));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
testThinkingLogs().catch(console.error);
|
|
80
|
+
//# sourceMappingURL=repro-thinking-logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repro-thinking-logs.js","sourceRoot":"","sources":["../../src/utils/repro-thinking-logs.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAC7B,uDAAqD;AAErD,KAAK,UAAU,gBAAgB;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,qBAAqB,CAAC,CAAC;IAChE,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,CAAC;IACD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,IAAA,kCAAgB,EAAC,OAAO,EAAE,oBAAoB,EAAE;QAC9D,YAAY,EAAE,IAAI;QAClB,WAAW,EAAE,IAAI;KAClB,CAAC,CAAC;IAEH,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,CAAC;IAE9D,MAAM,QAAQ,GAAG;QACf,2PAA2P;QAC3P,sUAAsU;QACtU,6cAA6c;QAC7c,yJAAyJ;KAC1J,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;QAC3D,OAAO,CAAC,WAAW,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC,EAAE,MAAM,CAAC,CAAC;IACzF,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEzB,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;YACzB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK;YAChD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ;YAC9B,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI;SACpC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACf,CAAC;AACH,CAAC;AAED,gBAAgB,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC"}
|
package/dist/utils/types.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface CursorFlowConfig {
|
|
|
16
16
|
lockfileReadOnly: boolean;
|
|
17
17
|
enableReview: boolean;
|
|
18
18
|
reviewModel: string;
|
|
19
|
+
reviewAllTasks?: boolean;
|
|
19
20
|
maxReviewIterations: number;
|
|
20
21
|
defaultLaneConfig: LaneConfig;
|
|
21
22
|
logLevel: string;
|
|
@@ -23,6 +24,8 @@ export interface CursorFlowConfig {
|
|
|
23
24
|
worktreePrefix: string;
|
|
24
25
|
maxConcurrentLanes: number;
|
|
25
26
|
projectRoot: string;
|
|
27
|
+
/** Output format for cursor-agent (default: 'stream-json') */
|
|
28
|
+
agentOutputFormat: 'stream-json' | 'json' | 'plain';
|
|
26
29
|
webhooks?: WebhookConfig[];
|
|
27
30
|
/** Enhanced logging configuration */
|
|
28
31
|
enhancedLogging?: Partial<EnhancedLogConfig>;
|
|
@@ -155,6 +158,10 @@ export interface Task {
|
|
|
155
158
|
model?: string;
|
|
156
159
|
/** Acceptance criteria for the AI reviewer to validate */
|
|
157
160
|
acceptanceCriteria?: string[];
|
|
161
|
+
/** Task-level dependencies (format: "lane:task") */
|
|
162
|
+
dependsOn?: string[];
|
|
163
|
+
/** Task execution timeout in milliseconds. Overrides lane-level timeout. */
|
|
164
|
+
timeout?: number;
|
|
158
165
|
}
|
|
159
166
|
export interface RunnerConfig {
|
|
160
167
|
tasks: Task[];
|
|
@@ -165,7 +172,11 @@ export interface RunnerConfig {
|
|
|
165
172
|
baseBranch?: string;
|
|
166
173
|
model?: string;
|
|
167
174
|
dependencyPolicy: DependencyPolicy;
|
|
175
|
+
enableReview?: boolean;
|
|
176
|
+
/** Output format for cursor-agent (default: 'stream-json') */
|
|
177
|
+
agentOutputFormat?: 'stream-json' | 'json' | 'plain';
|
|
168
178
|
reviewModel?: string;
|
|
179
|
+
reviewAllTasks?: boolean;
|
|
169
180
|
maxReviewIterations?: number;
|
|
170
181
|
acceptanceCriteria?: string[];
|
|
171
182
|
/** Task execution timeout in milliseconds. Default: 600000 (10 minutes) */
|
|
@@ -176,6 +187,12 @@ export interface RunnerConfig {
|
|
|
176
187
|
* Default: false
|
|
177
188
|
*/
|
|
178
189
|
enableIntervention?: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* Disable Git operations (worktree, branch, push, commit).
|
|
192
|
+
* Useful for testing or environments without Git remote.
|
|
193
|
+
* Default: false
|
|
194
|
+
*/
|
|
195
|
+
noGit?: boolean;
|
|
179
196
|
}
|
|
180
197
|
export interface DependencyRequestPlan {
|
|
181
198
|
reason: string;
|
|
@@ -214,6 +231,7 @@ export interface ReviewResult {
|
|
|
214
231
|
export interface TaskResult {
|
|
215
232
|
taskName: string;
|
|
216
233
|
taskBranch: string;
|
|
234
|
+
acceptanceCriteria?: string[];
|
|
217
235
|
[key: string]: any;
|
|
218
236
|
}
|
|
219
237
|
export interface LaneState {
|
|
@@ -231,6 +249,10 @@ export interface LaneState {
|
|
|
231
249
|
tasksFile?: string;
|
|
232
250
|
dependsOn?: string[];
|
|
233
251
|
pid?: number;
|
|
252
|
+
/** List of completed task names in this lane */
|
|
253
|
+
completedTasks?: string[];
|
|
254
|
+
/** Task-level dependencies currently being waited for (format: "lane:task") */
|
|
255
|
+
waitingFor?: string[];
|
|
234
256
|
}
|
|
235
257
|
export interface ConversationEntry {
|
|
236
258
|
timestamp: string;
|
package/dist/utils/webhook.js
CHANGED
|
@@ -84,6 +84,9 @@ async function sendWebhook(config, event) {
|
|
|
84
84
|
try {
|
|
85
85
|
const controller = new AbortController();
|
|
86
86
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
87
|
+
// SECURITY NOTE: Intentionally sending event data to configured webhook URLs.
|
|
88
|
+
// This is the expected behavior - users explicitly configure webhook endpoints
|
|
89
|
+
// to receive CursorFlow events. The data is JSON-serialized event metadata.
|
|
87
90
|
const response = await fetch(config.url, {
|
|
88
91
|
method: 'POST',
|
|
89
92
|
headers,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/utils/webhook.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/utils/webhook.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,4CAkBC;AA1BD,+CAAiC;AACjC,qCAAkC;AAElC,iDAAmC;AAEnC;;GAEG;AACH,SAAgB,gBAAgB,CAAC,OAAwB;IACvD,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO;IAEhD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK;YAAE,SAAS;QAEvC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAExC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,eAAM,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;gBACjC,IAAI,CAAC;oBACH,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;gBAAC,OAAO,KAAU,EAAE,CAAC;oBACpB,MAAM,CAAC,KAAK,CAAC,sBAAsB,MAAM,CAAC,GAAG,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CAAC,MAAqB,EAAE,KAAsB;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;QAClC,YAAY,EAAE,yBAAyB;QACvC,GAAG,MAAM,CAAC,OAAO;KAClB,CAAC;IAEF,2CAA2C;IAC3C,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,SAAS,GAAG,MAAM;aACrB,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;aACnC,MAAM,CAAC,OAAO,CAAC;aACf,MAAM,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,CAAC,wBAAwB,CAAC,GAAG,UAAU,SAAS,EAAE,CAAC;IAC5D,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC;IAE5C,IAAI,SAAc,CAAC;IAEnB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;YAElE,8EAA8E;YAC9E,+EAA+E;YAC/E,4EAA4E;YAC5E,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;gBACvC,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YAEH,YAAY,CAAC,SAAS,CAAC,CAAC;YAExB,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,OAAO;YACT,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACrE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC;YAElB,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,sBAAsB;gBACjE,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,CAAC;AAClB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@litmers/cursorflow-orchestrator",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Git worktree-based parallel AI agent orchestration system for Cursor",
|
|
5
5
|
"main": "dist/cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"security:audit": "npm audit",
|
|
23
23
|
"security:audit:fix": "npm audit fix",
|
|
24
24
|
"security:setup": "scripts/setup-security.sh",
|
|
25
|
+
"verify": "scripts/verify-and-fix.sh",
|
|
26
|
+
"test:lifecycle": "tests/scripts/test-real-cursor-lifecycle.sh",
|
|
27
|
+
"test:comprehensive": "tests/scripts/test-comprehensive-lifecycle.sh",
|
|
25
28
|
"prepare": "husky"
|
|
26
29
|
},
|
|
27
30
|
"keywords": [
|
|
@@ -101,6 +101,9 @@ async function analyzeCodeWithAI(code, filename) {
|
|
|
101
101
|
const prompt = createSecurityPrompt(code, filename);
|
|
102
102
|
|
|
103
103
|
try {
|
|
104
|
+
// SECURITY NOTE: Intentionally sending code to OpenAI API for security analysis.
|
|
105
|
+
// This is the expected behavior - the script's purpose is AI-powered code review.
|
|
106
|
+
// Code is sent over HTTPS to OpenAI's secure API endpoint.
|
|
104
107
|
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
105
108
|
method: 'POST',
|
|
106
109
|
headers: {
|
|
@@ -45,7 +45,15 @@ echo -e "\n${BLUE}[3/4] Checking for hardcoded secrets...${NC}"
|
|
|
45
45
|
# .cursorignore나 .gitignore에 있는 파일은 제외
|
|
46
46
|
# .github, *.md, scripts/setup-security.sh 등은 제외
|
|
47
47
|
# 변수 선언이나 에러 메시지에 포함된 키워드는 제외하도록 필터 강화
|
|
48
|
-
|
|
48
|
+
RAW_SECRETS=$(git grep -Ei "api[_-]?key|secret|password|token|bearer|private[_-]?key" -- ":!package-lock.json" ":!*.md" ":!scripts/setup-security.sh" ":!scripts/ai-security-check.js" ":!.github/*" ":!scripts/local-security-gate.sh" | grep -v "process.env" | grep -v "example" | grep -v "\${{" | grep -vE "stderr\.includes|checkCursorApiKey|CURSOR_API_KEY|api key|API_KEY" || true)
|
|
49
|
+
|
|
50
|
+
# .secretsignore 파일이 있으면 해당 패턴을 제외
|
|
51
|
+
if [ -f .secretsignore ] && [ -n "$RAW_SECRETS" ]; then
|
|
52
|
+
# grep -v -f를 사용하여 .secretsignore에 있는 패턴이 포함된 줄을 제외
|
|
53
|
+
SECRETS_FOUND=$(echo "$RAW_SECRETS" | grep -v -f .secretsignore || true)
|
|
54
|
+
else
|
|
55
|
+
SECRETS_FOUND="$RAW_SECRETS"
|
|
56
|
+
fi
|
|
49
57
|
|
|
50
58
|
if [ -z "$SECRETS_FOUND" ]; then
|
|
51
59
|
echo -e "${GREEN}✅ No obvious secrets found in tracked files.${NC}"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 작업 후 품질 검증 및 자동 수정 스크립트
|
|
4
|
+
# Husky pre-push 훅과 유사하지만, 자동 수정(fix)을 시도합니다.
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
echo "🛠️ Starting Post-Work Verification & Fix..."
|
|
9
|
+
|
|
10
|
+
# 1. 의존성 취약점 확인 및 수정
|
|
11
|
+
echo -e "\n📦 Checking dependencies (npm audit)..."
|
|
12
|
+
if ! npm audit --audit-level=high; then
|
|
13
|
+
echo "⚠️ High severity issues found. Attempting 'npm audit fix'..."
|
|
14
|
+
npm audit fix
|
|
15
|
+
# 다시 확인
|
|
16
|
+
if ! npm audit --audit-level=high; then
|
|
17
|
+
echo "❌ Vulnerabilities still exist after fix. Please check manually."
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
else
|
|
21
|
+
echo "✅ Dependencies are clean."
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# 2. 테스트 실행
|
|
25
|
+
echo -e "\n🧪 Running tests..."
|
|
26
|
+
npm test
|
|
27
|
+
|
|
28
|
+
# 3. 보안 게이트 실행 (새로 추가한 .secretsignore 적용됨)
|
|
29
|
+
echo -e "\n🔒 Running Local Security Gate..."
|
|
30
|
+
./scripts/local-security-gate.sh
|
|
31
|
+
|
|
32
|
+
# 4. 패키지 유효성 검사
|
|
33
|
+
echo -e "\n📦 Validating package..."
|
|
34
|
+
npm run validate
|
|
35
|
+
|
|
36
|
+
echo -e "\n✅ All checks passed! Ready to push."
|
|
37
|
+
|
package/src/cli/clean.ts
CHANGED
|
@@ -178,7 +178,7 @@ async function cleanBranches(config: any, repoRoot: string, options: CleanOption
|
|
|
178
178
|
|
|
179
179
|
const branches = result.stdout
|
|
180
180
|
.split('\n')
|
|
181
|
-
.map(b => b.replace(
|
|
181
|
+
.map(b => b.replace(/\*/g, '').trim())
|
|
182
182
|
.filter(b => b && b !== 'main' && b !== 'master');
|
|
183
183
|
|
|
184
184
|
const prefix = config.branchPrefix || 'feature/';
|
package/src/cli/init.ts
CHANGED
|
@@ -175,17 +175,20 @@ function updateGitignore(projectRoot: string): void {
|
|
|
175
175
|
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
176
176
|
const entry = '_cursorflow/';
|
|
177
177
|
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
fs.
|
|
182
|
-
|
|
183
|
-
|
|
178
|
+
// Try to read existing .gitignore (avoid TOCTOU by reading directly)
|
|
179
|
+
let content: string;
|
|
180
|
+
try {
|
|
181
|
+
content = fs.readFileSync(gitignorePath, 'utf8');
|
|
182
|
+
} catch (err: any) {
|
|
183
|
+
if (err.code === 'ENOENT') {
|
|
184
|
+
// File doesn't exist - create new .gitignore
|
|
185
|
+
fs.writeFileSync(gitignorePath, `# CursorFlow\n${entry}\n`, 'utf8');
|
|
186
|
+
logger.success('Created .gitignore with _cursorflow/');
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
throw err;
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
// Read existing .gitignore
|
|
187
|
-
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
188
|
-
|
|
189
192
|
// Check if already included
|
|
190
193
|
const lines = content.split('\n');
|
|
191
194
|
const hasEntry = lines.some(line => {
|
package/src/cli/logs.ts
CHANGED
|
@@ -29,6 +29,13 @@ interface LogsOptions {
|
|
|
29
29
|
help: boolean;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Escape special regex characters to prevent regex injection
|
|
34
|
+
*/
|
|
35
|
+
function escapeRegex(str: string): string {
|
|
36
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
function printHelp(): void {
|
|
33
40
|
console.log(`
|
|
34
41
|
Usage: cursorflow logs [run-dir] [options]
|
|
@@ -45,15 +52,15 @@ Options:
|
|
|
45
52
|
--follow, -f Follow log output in real-time
|
|
46
53
|
--filter <pattern> Filter entries by regex pattern
|
|
47
54
|
--level <level> Filter by log level: stdout, stderr, info, error, debug
|
|
48
|
-
--
|
|
49
|
-
--
|
|
50
|
-
--
|
|
55
|
+
--readable, -r Show readable log (parsed AI output) (default)
|
|
56
|
+
--clean Show clean terminal logs without ANSI codes
|
|
57
|
+
--raw Show raw terminal logs with ANSI codes
|
|
51
58
|
--help, -h Show help
|
|
52
59
|
|
|
53
60
|
Examples:
|
|
54
61
|
cursorflow logs # View latest run logs summary
|
|
55
|
-
cursorflow logs --lane api-setup # View
|
|
56
|
-
cursorflow logs --lane api-setup --
|
|
62
|
+
cursorflow logs --lane api-setup # View readable parsed log (default)
|
|
63
|
+
cursorflow logs --lane api-setup --clean # View clean terminal logs
|
|
57
64
|
cursorflow logs --all # View all lanes merged by time
|
|
58
65
|
cursorflow logs --all --follow # Follow all lanes in real-time
|
|
59
66
|
cursorflow logs --all --format json # Export all lanes as JSON
|
|
@@ -81,6 +88,10 @@ function parseArgs(args: string[]): LogsOptions {
|
|
|
81
88
|
return true;
|
|
82
89
|
});
|
|
83
90
|
|
|
91
|
+
const raw = args.includes('--raw');
|
|
92
|
+
const clean = args.includes('--clean');
|
|
93
|
+
const readable = args.includes('--readable') || args.includes('-r');
|
|
94
|
+
|
|
84
95
|
return {
|
|
85
96
|
runDir,
|
|
86
97
|
lane: laneIdx >= 0 ? args[laneIdx + 1] : undefined,
|
|
@@ -91,9 +102,10 @@ function parseArgs(args: string[]): LogsOptions {
|
|
|
91
102
|
follow: args.includes('--follow') || args.includes('-f'),
|
|
92
103
|
filter: filterIdx >= 0 ? args[filterIdx + 1] : undefined,
|
|
93
104
|
level: levelIdx >= 0 ? args[levelIdx + 1] : undefined,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
raw,
|
|
106
|
+
clean,
|
|
107
|
+
// Default to readable if no other format is specified
|
|
108
|
+
readable: readable || (!raw && !clean),
|
|
97
109
|
help: args.includes('--help') || args.includes('-h'),
|
|
98
110
|
};
|
|
99
111
|
}
|
|
@@ -136,29 +148,32 @@ function displayTextLogs(
|
|
|
136
148
|
options: LogsOptions
|
|
137
149
|
): void {
|
|
138
150
|
let logFile: string;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
const readableLog = path.join(laneDir, 'terminal-readable.log');
|
|
152
|
+
const rawLog = path.join(laneDir, 'terminal-raw.log');
|
|
153
|
+
const cleanLog = path.join(laneDir, 'terminal.log');
|
|
154
|
+
|
|
155
|
+
if (options.raw) {
|
|
156
|
+
logFile = rawLog;
|
|
157
|
+
} else if (options.clean) {
|
|
158
|
+
logFile = cleanLog;
|
|
159
|
+
} else if (options.readable && fs.existsSync(readableLog)) {
|
|
160
|
+
logFile = readableLog;
|
|
143
161
|
} else {
|
|
144
|
-
|
|
162
|
+
// Default or fallback to clean log
|
|
163
|
+
logFile = cleanLog;
|
|
145
164
|
}
|
|
146
165
|
|
|
147
166
|
if (!fs.existsSync(logFile)) {
|
|
148
|
-
|
|
149
|
-
console.log('Readable log not found. This log is only available for runs with streaming output enabled.');
|
|
150
|
-
} else {
|
|
151
|
-
console.log('No log file found.');
|
|
152
|
-
}
|
|
167
|
+
console.log('No log file found.');
|
|
153
168
|
return;
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
let content = fs.readFileSync(logFile, 'utf8');
|
|
157
172
|
let lines = content.split('\n');
|
|
158
173
|
|
|
159
|
-
// Apply filter
|
|
174
|
+
// Apply filter (escape to prevent regex injection)
|
|
160
175
|
if (options.filter) {
|
|
161
|
-
const regex = new RegExp(options.filter, 'i');
|
|
176
|
+
const regex = new RegExp(escapeRegex(options.filter), 'i');
|
|
162
177
|
lines = lines.filter(line => regex.test(line));
|
|
163
178
|
}
|
|
164
179
|
|
|
@@ -167,8 +182,8 @@ function displayTextLogs(
|
|
|
167
182
|
lines = lines.slice(-options.tail);
|
|
168
183
|
}
|
|
169
184
|
|
|
170
|
-
// Clean ANSI if needed (for clean mode)
|
|
171
|
-
if (
|
|
185
|
+
// Clean ANSI if needed (for clean mode or default fallback)
|
|
186
|
+
if (!options.raw) {
|
|
172
187
|
lines = lines.map(line => stripAnsi(line));
|
|
173
188
|
}
|
|
174
189
|
|
|
@@ -196,9 +211,9 @@ function displayJsonLogs(
|
|
|
196
211
|
entries = entries.filter(e => e.level === options.level);
|
|
197
212
|
}
|
|
198
213
|
|
|
199
|
-
// Apply regex filter
|
|
214
|
+
// Apply regex filter (escape to prevent regex injection)
|
|
200
215
|
if (options.filter) {
|
|
201
|
-
const regex = new RegExp(options.filter, 'i');
|
|
216
|
+
const regex = new RegExp(escapeRegex(options.filter), 'i');
|
|
202
217
|
entries = entries.filter(e => regex.test(e.message) || regex.test(e.task || ''));
|
|
203
218
|
}
|
|
204
219
|
|
|
@@ -318,9 +333,9 @@ function displayMergedLogs(runDir: string, options: LogsOptions): void {
|
|
|
318
333
|
entries = entries.filter(e => e.level === options.level);
|
|
319
334
|
}
|
|
320
335
|
|
|
321
|
-
// Apply regex filter
|
|
336
|
+
// Apply regex filter (escape to prevent regex injection)
|
|
322
337
|
if (options.filter) {
|
|
323
|
-
const regex = new RegExp(options.filter, 'i');
|
|
338
|
+
const regex = new RegExp(escapeRegex(options.filter), 'i');
|
|
324
339
|
entries = entries.filter(e =>
|
|
325
340
|
regex.test(e.message) ||
|
|
326
341
|
regex.test(e.task || '') ||
|
|
@@ -414,9 +429,8 @@ function followAllLogs(runDir: string, options: LogsOptions): void {
|
|
|
414
429
|
const laneDir = path.join(runDir, 'lanes', lane);
|
|
415
430
|
const jsonLogPath = path.join(laneDir, 'terminal.jsonl');
|
|
416
431
|
|
|
417
|
-
if (!fs.existsSync(jsonLogPath)) continue;
|
|
418
|
-
|
|
419
432
|
try {
|
|
433
|
+
// Use statSync directly to avoid TOCTOU race condition
|
|
420
434
|
const stats = fs.statSync(jsonLogPath);
|
|
421
435
|
if (stats.size > lastPositions[lane]!) {
|
|
422
436
|
const fd = fs.openSync(jsonLogPath, 'r');
|
|
@@ -459,9 +473,9 @@ function followAllLogs(runDir: string, options: LogsOptions): void {
|
|
|
459
473
|
// Apply level filter
|
|
460
474
|
if (options.level && entry.level !== options.level) continue;
|
|
461
475
|
|
|
462
|
-
// Apply regex filter
|
|
476
|
+
// Apply regex filter (escape to prevent regex injection)
|
|
463
477
|
if (options.filter) {
|
|
464
|
-
const regex = new RegExp(options.filter, 'i');
|
|
478
|
+
const regex = new RegExp(escapeRegex(options.filter), 'i');
|
|
465
479
|
if (!regex.test(entry.message) && !regex.test(entry.task || '') && !regex.test(entry.laneName)) {
|
|
466
480
|
continue;
|
|
467
481
|
}
|
|
@@ -620,7 +634,9 @@ function escapeHtml(text: string): string {
|
|
|
620
634
|
.replace(/</g, '<')
|
|
621
635
|
.replace(/>/g, '>')
|
|
622
636
|
.replace(/"/g, '"')
|
|
623
|
-
.replace(/'/g, ''')
|
|
637
|
+
.replace(/'/g, ''')
|
|
638
|
+
.replace(/`/g, '`')
|
|
639
|
+
.replace(/\//g, '/');
|
|
624
640
|
}
|
|
625
641
|
|
|
626
642
|
/**
|
|
@@ -628,12 +644,19 @@ function escapeHtml(text: string): string {
|
|
|
628
644
|
*/
|
|
629
645
|
function followLogs(laneDir: string, options: LogsOptions): void {
|
|
630
646
|
let logFile: string;
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
647
|
+
const readableLog = path.join(laneDir, 'terminal-readable.log');
|
|
648
|
+
const rawLog = path.join(laneDir, 'terminal-raw.log');
|
|
649
|
+
const cleanLog = path.join(laneDir, 'terminal.log');
|
|
650
|
+
|
|
651
|
+
if (options.raw) {
|
|
652
|
+
logFile = rawLog;
|
|
653
|
+
} else if (options.clean) {
|
|
654
|
+
logFile = cleanLog;
|
|
655
|
+
} else if (options.readable && fs.existsSync(readableLog)) {
|
|
656
|
+
logFile = readableLog;
|
|
635
657
|
} else {
|
|
636
|
-
|
|
658
|
+
// Default or fallback to clean log
|
|
659
|
+
logFile = cleanLog;
|
|
637
660
|
}
|
|
638
661
|
|
|
639
662
|
if (!fs.existsSync(logFile)) {
|
|
@@ -642,9 +665,11 @@ function followLogs(laneDir: string, options: LogsOptions): void {
|
|
|
642
665
|
|
|
643
666
|
let lastSize = 0;
|
|
644
667
|
try {
|
|
645
|
-
|
|
668
|
+
// Use statSync directly to avoid TOCTOU race condition
|
|
669
|
+
lastSize = fs.statSync(logFile).size;
|
|
646
670
|
} catch {
|
|
647
|
-
//
|
|
671
|
+
// File doesn't exist yet or other error - start from 0
|
|
672
|
+
lastSize = 0;
|
|
648
673
|
}
|
|
649
674
|
|
|
650
675
|
console.log(`${logger.COLORS.cyan}Following ${logFile}... (Ctrl+C to stop)${logger.COLORS.reset}\n`);
|
|
@@ -662,15 +687,15 @@ function followLogs(laneDir: string, options: LogsOptions): void {
|
|
|
662
687
|
|
|
663
688
|
let content = buffer.toString();
|
|
664
689
|
|
|
665
|
-
// Apply filter
|
|
690
|
+
// Apply filter (escape to prevent regex injection)
|
|
666
691
|
if (options.filter) {
|
|
667
|
-
const regex = new RegExp(options.filter, 'i');
|
|
692
|
+
const regex = new RegExp(escapeRegex(options.filter), 'i');
|
|
668
693
|
const lines = content.split('\n');
|
|
669
694
|
content = lines.filter(line => regex.test(line)).join('\n');
|
|
670
695
|
}
|
|
671
696
|
|
|
672
|
-
// Clean ANSI if needed
|
|
673
|
-
if (
|
|
697
|
+
// Clean ANSI if needed (unless raw mode)
|
|
698
|
+
if (!options.raw) {
|
|
674
699
|
content = stripAnsi(content);
|
|
675
700
|
}
|
|
676
701
|
|
|
@@ -809,7 +834,7 @@ async function logs(args: string[]): Promise<void> {
|
|
|
809
834
|
// If no lane specified, show summary
|
|
810
835
|
if (!options.lane) {
|
|
811
836
|
displaySummary(runDir);
|
|
812
|
-
console.log(`${logger.COLORS.gray}Use --lane <name> to view logs, --
|
|
837
|
+
console.log(`${logger.COLORS.gray}Use --lane <name> to view logs (default: readable), --clean for terminal logs, or --all to view all lanes merged${logger.COLORS.reset}`);
|
|
813
838
|
return;
|
|
814
839
|
}
|
|
815
840
|
|
package/src/cli/monitor.ts
CHANGED
|
@@ -505,8 +505,12 @@ class InteractiveMonitor {
|
|
|
505
505
|
nextAction = '🏁 Done';
|
|
506
506
|
}
|
|
507
507
|
} else if (status.status === 'waiting') {
|
|
508
|
-
|
|
509
|
-
|
|
508
|
+
if (status.waitingFor && status.waitingFor.length > 0) {
|
|
509
|
+
nextAction = `Wait for task: ${status.waitingFor.join(', ')}`;
|
|
510
|
+
} else {
|
|
511
|
+
const missingDeps = status.dependsOn.filter((d: string) => laneStatuses[d] && laneStatuses[d].status !== 'completed');
|
|
512
|
+
nextAction = `Wait for lane: ${missingDeps.join(', ')}`;
|
|
513
|
+
}
|
|
510
514
|
} else if (status.status === 'running') {
|
|
511
515
|
nextAction = '🚀 Working...';
|
|
512
516
|
}
|
|
@@ -553,6 +557,10 @@ class InteractiveMonitor {
|
|
|
553
557
|
process.stdout.write(` Chat ID: ${status.chatId}\n`);
|
|
554
558
|
process.stdout.write(` Depends: ${status.dependsOn.join(', ') || 'None'}\n`);
|
|
555
559
|
|
|
560
|
+
if (status.waitingFor && status.waitingFor.length > 0) {
|
|
561
|
+
process.stdout.write(`\x1b[33m Wait For: ${status.waitingFor.join(', ')}\x1b[0m\n`);
|
|
562
|
+
}
|
|
563
|
+
|
|
556
564
|
if (status.error) {
|
|
557
565
|
process.stdout.write(`\x1b[31m Error: ${status.error}\x1b[0m\n`);
|
|
558
566
|
}
|
|
@@ -815,9 +823,10 @@ class InteractiveMonitor {
|
|
|
815
823
|
dependsOn,
|
|
816
824
|
duration,
|
|
817
825
|
error: state.error,
|
|
818
|
-
pid: state.pid
|
|
826
|
+
pid: state.pid,
|
|
827
|
+
waitingFor: state.waitingFor || [],
|
|
819
828
|
};
|
|
820
|
-
|
|
829
|
+
}
|
|
821
830
|
|
|
822
831
|
private formatDuration(ms: number): string {
|
|
823
832
|
if (ms <= 0) return '-';
|