@litmers/cursorflow-orchestrator 0.1.3 → 0.1.6
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 +17 -7
- package/README.md +33 -2
- package/commands/cursorflow-doctor.md +24 -0
- package/commands/cursorflow-signal.md +19 -0
- package/dist/cli/clean.d.ts +5 -0
- package/dist/cli/clean.js +57 -0
- package/dist/cli/clean.js.map +1 -0
- package/dist/cli/doctor.d.ts +15 -0
- package/dist/cli/doctor.js +139 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +125 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.js +302 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/monitor.d.ts +8 -0
- package/dist/cli/monitor.js +210 -0
- package/dist/cli/monitor.js.map +1 -0
- package/dist/cli/resume.d.ts +5 -0
- package/dist/cli/resume.js +128 -0
- package/dist/cli/resume.js.map +1 -0
- package/dist/cli/run.d.ts +5 -0
- package/dist/cli/run.js +128 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/setup-commands.d.ts +23 -0
- package/dist/cli/setup-commands.js +234 -0
- package/dist/cli/setup-commands.js.map +1 -0
- package/dist/cli/signal.d.ts +7 -0
- package/dist/cli/signal.js +99 -0
- package/dist/cli/signal.js.map +1 -0
- package/dist/core/orchestrator.d.ts +47 -0
- package/dist/core/orchestrator.js +192 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/reviewer.d.ts +60 -0
- package/dist/core/reviewer.js +239 -0
- package/dist/core/reviewer.js.map +1 -0
- package/dist/core/runner.d.ts +51 -0
- package/dist/core/runner.js +499 -0
- package/dist/core/runner.js.map +1 -0
- package/dist/utils/config.d.ts +31 -0
- package/dist/utils/config.js +198 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/cursor-agent.d.ts +61 -0
- package/dist/utils/cursor-agent.js +263 -0
- package/dist/utils/cursor-agent.js.map +1 -0
- package/dist/utils/doctor.d.ts +63 -0
- package/dist/utils/doctor.js +280 -0
- package/dist/utils/doctor.js.map +1 -0
- package/dist/utils/git.d.ts +131 -0
- package/dist/utils/git.js +272 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +68 -0
- package/dist/utils/logger.js +158 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/state.d.ts +65 -0
- package/dist/utils/state.js +216 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/types.d.ts +118 -0
- package/dist/utils/types.js +6 -0
- package/dist/utils/types.js.map +1 -0
- package/examples/README.md +155 -0
- package/examples/demo-project/README.md +262 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/01-create-utils.json +18 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/02-add-tests.json +18 -0
- package/examples/demo-project/_cursorflow/tasks/demo-test/README.md +109 -0
- package/package.json +71 -61
- package/scripts/ai-security-check.js +11 -4
- package/scripts/local-security-gate.sh +76 -0
- package/src/cli/{clean.js → clean.ts} +11 -5
- package/src/cli/doctor.ts +127 -0
- package/src/cli/{index.js → index.ts} +27 -16
- package/src/cli/{init.js → init.ts} +26 -18
- package/src/cli/{monitor.js → monitor.ts} +57 -44
- package/src/cli/resume.ts +119 -0
- package/src/cli/run.ts +109 -0
- package/src/cli/{setup-commands.js → setup-commands.ts} +38 -18
- package/src/cli/signal.ts +89 -0
- package/src/core/{orchestrator.js → orchestrator.ts} +44 -26
- package/src/core/{reviewer.js → reviewer.ts} +36 -29
- package/src/core/{runner.js → runner.ts} +125 -76
- package/src/utils/{config.js → config.ts} +17 -25
- package/src/utils/{cursor-agent.js → cursor-agent.ts} +38 -47
- package/src/utils/doctor.ts +312 -0
- package/src/utils/{git.js → git.ts} +70 -56
- package/src/utils/{logger.js → logger.ts} +170 -178
- package/src/utils/{state.js → state.ts} +30 -38
- package/src/utils/types.ts +134 -0
- package/src/cli/resume.js +0 -31
- package/src/cli/run.js +0 -51
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const fs = require('fs');
|
|
9
9
|
const path = require('path');
|
|
10
|
-
const {
|
|
10
|
+
const { spawnSync } = require('child_process');
|
|
11
11
|
|
|
12
12
|
// 색상 정의
|
|
13
13
|
const colors = {
|
|
@@ -29,8 +29,10 @@ if (!OPENAI_API_KEY) {
|
|
|
29
29
|
function getChangedFiles() {
|
|
30
30
|
try {
|
|
31
31
|
const baseBranch = process.env.GITHUB_BASE_REF || 'main';
|
|
32
|
-
const
|
|
33
|
-
|
|
32
|
+
const result = spawnSync('git', ['diff', '--name-only', `origin/${baseBranch}...HEAD`], { encoding: 'utf-8' });
|
|
33
|
+
if (result.status !== 0) throw new Error(result.stderr);
|
|
34
|
+
|
|
35
|
+
const files = result.stdout
|
|
34
36
|
.split('\n')
|
|
35
37
|
.filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx'))
|
|
36
38
|
.filter(f => f && fs.existsSync(f));
|
|
@@ -38,7 +40,10 @@ function getChangedFiles() {
|
|
|
38
40
|
} catch (error) {
|
|
39
41
|
// PR이 아닌 경우 최근 커밋의 파일들
|
|
40
42
|
try {
|
|
41
|
-
const
|
|
43
|
+
const result = spawnSync('git', ['diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD'], { encoding: 'utf-8' });
|
|
44
|
+
if (result.status !== 0) return [];
|
|
45
|
+
|
|
46
|
+
const files = result.stdout
|
|
42
47
|
.split('\n')
|
|
43
48
|
.filter(f => f.endsWith('.js') || f.endsWith('.ts') || f.endsWith('.jsx') || f.endsWith('.tsx'))
|
|
44
49
|
.filter(f => f && fs.existsSync(f));
|
|
@@ -71,6 +76,8 @@ Please analyze for:
|
|
|
71
76
|
8. **Rate limiting or DoS vulnerabilities**
|
|
72
77
|
9. **CSRF/SSRF vulnerabilities**
|
|
73
78
|
10. **Any OWASP Top 10 issues**
|
|
79
|
+
11. **CodeQL-specific patterns** (tainted data flow, improper input validation, dangerous sinks like eval or child_process.exec)
|
|
80
|
+
12. **Code quality issues** that might trigger CodeQL's "Security and Quality" queries
|
|
74
81
|
|
|
75
82
|
Respond in JSON format:
|
|
76
83
|
{
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 로컬 보안 게이트 (Local Security Gate)
|
|
4
|
+
# 커밋 또는 푸시 전에 보안 취약점을 미리 검사합니다.
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# 색상 정의
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
BLUE='\033[0;34m'
|
|
13
|
+
NC='\033[0m'
|
|
14
|
+
|
|
15
|
+
echo -e "${BLUE}🔍 Starting Local Security Gate...${NC}"
|
|
16
|
+
|
|
17
|
+
# 1. 의존성 취약점 검사 (npm audit)
|
|
18
|
+
echo -e "\n${BLUE}[1/4] Checking dependencies (npm audit)...${NC}"
|
|
19
|
+
if npm audit --audit-level=high; then
|
|
20
|
+
echo -e "${GREEN}✅ No high-severity dependency issues found.${NC}"
|
|
21
|
+
else
|
|
22
|
+
echo -e "${RED}❌ High-severity dependency issues found!${NC}"
|
|
23
|
+
echo -e "${YELLOW}Please run 'npm audit fix' to resolve them.${NC}"
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# 2. 정적 코드 분석 (Semgrep - CodeQL 대안)
|
|
28
|
+
echo -e "\n${BLUE}[2/4] Running static analysis (Semgrep)...${NC}"
|
|
29
|
+
if command -v semgrep &> /dev/null; then
|
|
30
|
+
if semgrep --config=auto --error .; then
|
|
31
|
+
echo -e "${GREEN}✅ Semgrep analysis passed.${NC}"
|
|
32
|
+
else
|
|
33
|
+
echo -e "${RED}❌ Semgrep found potential security issues!${NC}"
|
|
34
|
+
echo -e "${YELLOW}CodeQL과 유사한 보안 이슈들입니다. 위 리포트를 확인하고 수정하세요.${NC}"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
else
|
|
38
|
+
echo -e "${YELLOW}⚠️ Semgrep not installed. Skipping static analysis.${NC}"
|
|
39
|
+
echo -e "Install it to catch CodeQL-like issues: pip install semgrep"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 3. 민감 정보 노출 검사 (Simple Secret Scan)
|
|
43
|
+
echo -e "\n${BLUE}[3/4] Checking for hardcoded secrets...${NC}"
|
|
44
|
+
# git에 추적되는 파일들 중 API 키나 비밀번호 패턴 검색
|
|
45
|
+
# .cursorignore나 .gitignore에 있는 파일은 제외
|
|
46
|
+
# .github, *.md, scripts/setup-security.sh 등은 제외
|
|
47
|
+
# 변수 선언이나 에러 메시지에 포함된 키워드는 제외하도록 필터 강화
|
|
48
|
+
SECRETS_FOUND=$(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
|
+
if [ -z "$SECRETS_FOUND" ]; then
|
|
51
|
+
echo -e "${GREEN}✅ No obvious secrets found in tracked files.${NC}"
|
|
52
|
+
else
|
|
53
|
+
echo -e "${YELLOW}⚠️ Possible secrets found:${NC}"
|
|
54
|
+
echo "$SECRETS_FOUND"
|
|
55
|
+
echo -e "\n${RED}❌ Please remove secrets or move them to .env file before pushing!${NC}"
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# 4. AI 기반 코드 보안 분석 (선택적)
|
|
60
|
+
echo -e "\n${BLUE}[4/4] AI-based security analysis...${NC}"
|
|
61
|
+
if [ -z "$OPENAI_API_KEY" ]; then
|
|
62
|
+
echo -e "${YELLOW}⚠️ OPENAI_API_KEY not set. Skipping AI security check.${NC}"
|
|
63
|
+
echo -e "Set the environment variable to enable this step: export OPENAI_API_KEY=your-key"
|
|
64
|
+
else
|
|
65
|
+
if node scripts/ai-security-check.js; then
|
|
66
|
+
echo -e "${GREEN}✅ AI security check passed.${NC}"
|
|
67
|
+
else
|
|
68
|
+
echo -e "${RED}❌ AI security check failed!${NC}"
|
|
69
|
+
exit 1
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo -e "\n${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
74
|
+
echo -e "${GREEN}✅ All local security checks passed!${NC}"
|
|
75
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
|
|
76
|
+
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow clean command (stub)
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import * as logger from '../utils/logger';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
interface CleanOptions {
|
|
8
|
+
type?: string;
|
|
9
|
+
pattern: string | null;
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
force: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function parseArgs(args: string[]): CleanOptions {
|
|
9
15
|
return {
|
|
10
16
|
type: args[0], // branches | worktrees | logs | all
|
|
11
17
|
pattern: null,
|
|
@@ -14,7 +20,7 @@ function parseArgs(args) {
|
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
22
|
|
|
17
|
-
async function clean(args) {
|
|
23
|
+
async function clean(args: string[]): Promise<void> {
|
|
18
24
|
logger.section('🧹 Cleaning CursorFlow Resources');
|
|
19
25
|
|
|
20
26
|
const options = parseArgs(args);
|
|
@@ -27,4 +33,4 @@ async function clean(args) {
|
|
|
27
33
|
logger.info('This will clean branches, worktrees, and logs');
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
export = clean;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CursorFlow doctor command
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* cursorflow doctor [options]
|
|
6
|
+
*
|
|
7
|
+
* Options:
|
|
8
|
+
* --json Output machine-readable JSON
|
|
9
|
+
* --tasks-dir <path> Also validate lane files (run preflight)
|
|
10
|
+
* --executor <type> cursor-agent | cloud
|
|
11
|
+
* --no-cursor Skip Cursor Agent install/auth checks
|
|
12
|
+
* --help, -h Show help
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as logger from '../utils/logger';
|
|
16
|
+
import { runDoctor } from '../utils/doctor';
|
|
17
|
+
|
|
18
|
+
interface DoctorCliOptions {
|
|
19
|
+
json: boolean;
|
|
20
|
+
tasksDir: string | null;
|
|
21
|
+
executor: string | null;
|
|
22
|
+
includeCursorAgentChecks: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function printHelp(): void {
|
|
26
|
+
console.log(`
|
|
27
|
+
Usage: cursorflow doctor [options]
|
|
28
|
+
|
|
29
|
+
Verify your environment is ready for CursorFlow runs.
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
--json Output machine-readable JSON
|
|
33
|
+
--tasks-dir <path> Also validate lane files (run preflight)
|
|
34
|
+
--executor <type> cursor-agent | cloud
|
|
35
|
+
--no-cursor Skip Cursor Agent install/auth checks
|
|
36
|
+
--help, -h Show help
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
cursorflow doctor
|
|
40
|
+
cursorflow doctor --tasks-dir _cursorflow/tasks/demo-test/
|
|
41
|
+
cursorflow doctor --json
|
|
42
|
+
`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function parseArgs(args: string[]): DoctorCliOptions {
|
|
46
|
+
const tasksDirIdx = args.indexOf('--tasks-dir');
|
|
47
|
+
const executorIdx = args.indexOf('--executor');
|
|
48
|
+
|
|
49
|
+
const options: DoctorCliOptions = {
|
|
50
|
+
json: args.includes('--json'),
|
|
51
|
+
tasksDir: tasksDirIdx >= 0 ? (args[tasksDirIdx + 1] || null) : null,
|
|
52
|
+
executor: executorIdx >= 0 ? (args[executorIdx + 1] || null) : null,
|
|
53
|
+
includeCursorAgentChecks: !args.includes('--no-cursor'),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
57
|
+
printHelp();
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return options;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function printHumanReport(report: ReturnType<typeof runDoctor>): void {
|
|
65
|
+
logger.section('🩺 CursorFlow Doctor');
|
|
66
|
+
logger.info(`cwd: ${report.context.cwd}`);
|
|
67
|
+
if (report.context.repoRoot) logger.info(`repo: ${report.context.repoRoot}`);
|
|
68
|
+
if (report.context.tasksDir) logger.info(`tasks: ${report.context.tasksDir}`);
|
|
69
|
+
|
|
70
|
+
if (report.issues.length === 0) {
|
|
71
|
+
logger.success('All checks passed');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const issue of report.issues) {
|
|
76
|
+
const header = `${issue.title} (${issue.id})`;
|
|
77
|
+
if (issue.severity === 'error') {
|
|
78
|
+
logger.error(header, '❌');
|
|
79
|
+
} else {
|
|
80
|
+
logger.warn(header, '⚠️');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(` ${issue.message}`);
|
|
84
|
+
|
|
85
|
+
if (issue.details) {
|
|
86
|
+
console.log(` Details: ${issue.details}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (issue.fixes && issue.fixes.length > 0) {
|
|
90
|
+
console.log(' Fix:');
|
|
91
|
+
for (const fix of issue.fixes) {
|
|
92
|
+
console.log(` - ${fix}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
console.log('');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (report.ok) {
|
|
100
|
+
logger.success('Doctor completed with warnings');
|
|
101
|
+
} else {
|
|
102
|
+
logger.error('Doctor found blocking issues');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function doctor(args: string[]): Promise<void> {
|
|
107
|
+
const options = parseArgs(args);
|
|
108
|
+
|
|
109
|
+
const report = runDoctor({
|
|
110
|
+
cwd: process.cwd(),
|
|
111
|
+
tasksDir: options.tasksDir || undefined,
|
|
112
|
+
executor: options.executor || undefined,
|
|
113
|
+
includeCursorAgentChecks: options.includeCursorAgentChecks,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (options.json) {
|
|
117
|
+
console.log(JSON.stringify(report, null, 2));
|
|
118
|
+
} else {
|
|
119
|
+
printHumanReport(report);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
process.exit(report.ok ? 0 : 1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export = doctor;
|
|
126
|
+
|
|
127
|
+
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow CLI - Main entry point
|
|
4
3
|
*/
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import * as logger from '../utils/logger';
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
// Command functions signature
|
|
8
|
+
type CommandFn = (args: string[]) => Promise<void>;
|
|
9
|
+
|
|
10
|
+
// Lazy load commands to speed up help/version output
|
|
11
|
+
const COMMANDS: Record<string, CommandFn> = {
|
|
9
12
|
init: require('./init'),
|
|
10
13
|
run: require('./run'),
|
|
11
14
|
monitor: require('./monitor'),
|
|
12
15
|
clean: require('./clean'),
|
|
13
16
|
resume: require('./resume'),
|
|
17
|
+
doctor: require('./doctor'),
|
|
18
|
+
signal: require('./signal'),
|
|
14
19
|
};
|
|
15
20
|
|
|
16
|
-
function printHelp() {
|
|
21
|
+
function printHelp(): void {
|
|
17
22
|
console.log(`
|
|
18
23
|
CursorFlow - Git worktree-based parallel AI agent orchestration
|
|
19
24
|
|
|
@@ -25,6 +30,8 @@ Commands:
|
|
|
25
30
|
monitor [run-dir] [options] Monitor lane execution
|
|
26
31
|
clean <type> [options] Clean branches/worktrees/logs
|
|
27
32
|
resume <lane> [options] Resume interrupted lane
|
|
33
|
+
doctor [options] Check environment and preflight
|
|
34
|
+
signal <lane> <msg> Directly intervene in a running lane
|
|
28
35
|
|
|
29
36
|
Global Options:
|
|
30
37
|
--config <path> Config file path
|
|
@@ -36,44 +43,47 @@ Examples:
|
|
|
36
43
|
cursorflow run _cursorflow/tasks/MyFeature/
|
|
37
44
|
cursorflow monitor --watch
|
|
38
45
|
cursorflow clean branches --all
|
|
46
|
+
cursorflow doctor
|
|
39
47
|
|
|
40
48
|
Documentation:
|
|
41
49
|
https://github.com/eungjin-cigro/cursorflow#readme
|
|
42
50
|
`);
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
function printVersion() {
|
|
53
|
+
function printVersion(): void {
|
|
46
54
|
const pkg = require('../../package.json');
|
|
47
55
|
console.log(`CursorFlow v${pkg.version}`);
|
|
48
56
|
}
|
|
49
57
|
|
|
50
|
-
async function main() {
|
|
58
|
+
async function main(): Promise<void> {
|
|
51
59
|
const args = process.argv.slice(2);
|
|
52
60
|
|
|
53
61
|
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
54
62
|
printHelp();
|
|
55
|
-
|
|
63
|
+
return;
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
if (args.includes('--version') || args.includes('-v')) {
|
|
59
67
|
printVersion();
|
|
60
|
-
|
|
68
|
+
return;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
|
-
const
|
|
71
|
+
const commandName = args[0]!;
|
|
64
72
|
const commandArgs = args.slice(1);
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
const command = COMMANDS[commandName];
|
|
75
|
+
|
|
76
|
+
if (!command) {
|
|
77
|
+
logger.error(`Unknown command: ${commandName}`);
|
|
68
78
|
console.log('\nRun "cursorflow --help" for usage information.');
|
|
69
79
|
process.exit(1);
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
try {
|
|
73
|
-
await
|
|
74
|
-
} catch (error) {
|
|
83
|
+
await command(commandArgs);
|
|
84
|
+
} catch (error: any) {
|
|
75
85
|
logger.error(error.message);
|
|
76
|
-
if (process.env
|
|
86
|
+
if (process.env['DEBUG']) {
|
|
77
87
|
console.error(error.stack);
|
|
78
88
|
}
|
|
79
89
|
process.exit(1);
|
|
@@ -83,11 +93,12 @@ async function main() {
|
|
|
83
93
|
if (require.main === module) {
|
|
84
94
|
main().catch(error => {
|
|
85
95
|
logger.error(`Fatal error: ${error.message}`);
|
|
86
|
-
if (process.env
|
|
96
|
+
if (process.env['DEBUG']) {
|
|
87
97
|
console.error(error.stack);
|
|
88
98
|
}
|
|
89
99
|
process.exit(1);
|
|
90
100
|
});
|
|
91
101
|
}
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
export default main;
|
|
104
|
+
export { main };
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
2
|
* CursorFlow init command
|
|
4
3
|
*
|
|
5
4
|
* Initialize CursorFlow in a project
|
|
6
5
|
*/
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as logger from '../utils/logger';
|
|
10
|
+
import { findProjectRoot, createDefaultConfig, CursorFlowConfig } from '../utils/config';
|
|
11
|
+
import { setupCommands } from './setup-commands';
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
interface InitOptions {
|
|
14
|
+
example: boolean;
|
|
15
|
+
withCommands: boolean;
|
|
16
|
+
configOnly: boolean;
|
|
17
|
+
force: boolean;
|
|
18
|
+
gitignore: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function parseArgs(args: string[]): InitOptions {
|
|
22
|
+
const options: InitOptions = {
|
|
16
23
|
example: false,
|
|
17
24
|
withCommands: true,
|
|
18
25
|
configOnly: false,
|
|
@@ -53,7 +60,7 @@ function parseArgs(args) {
|
|
|
53
60
|
return options;
|
|
54
61
|
}
|
|
55
62
|
|
|
56
|
-
function printHelp() {
|
|
63
|
+
function printHelp(): void {
|
|
57
64
|
console.log(`
|
|
58
65
|
Usage: cursorflow init [options]
|
|
59
66
|
|
|
@@ -76,7 +83,7 @@ Examples:
|
|
|
76
83
|
`);
|
|
77
84
|
}
|
|
78
85
|
|
|
79
|
-
function createDirectories(projectRoot, config) {
|
|
86
|
+
function createDirectories(projectRoot: string, config: CursorFlowConfig): void {
|
|
80
87
|
const tasksDir = path.join(projectRoot, config.tasksDir);
|
|
81
88
|
const logsDir = path.join(projectRoot, config.logsDir);
|
|
82
89
|
|
|
@@ -95,7 +102,7 @@ function createDirectories(projectRoot, config) {
|
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
104
|
|
|
98
|
-
function createExampleTasks(projectRoot, config) {
|
|
105
|
+
function createExampleTasks(projectRoot: string, config: CursorFlowConfig): void {
|
|
99
106
|
const exampleDir = path.join(projectRoot, config.tasksDir, 'example');
|
|
100
107
|
|
|
101
108
|
if (!fs.existsSync(exampleDir)) {
|
|
@@ -164,7 +171,7 @@ cursorflow run ${config.tasksDir}/example/
|
|
|
164
171
|
/**
|
|
165
172
|
* Add _cursorflow to .gitignore
|
|
166
173
|
*/
|
|
167
|
-
function updateGitignore(projectRoot) {
|
|
174
|
+
function updateGitignore(projectRoot: string): void {
|
|
168
175
|
const gitignorePath = path.join(projectRoot, '.gitignore');
|
|
169
176
|
const entry = '_cursorflow/';
|
|
170
177
|
|
|
@@ -209,7 +216,7 @@ function updateGitignore(projectRoot) {
|
|
|
209
216
|
logger.success('Added _cursorflow/ to .gitignore');
|
|
210
217
|
}
|
|
211
218
|
|
|
212
|
-
async function init(args) {
|
|
219
|
+
async function init(args: string[]): Promise<void> {
|
|
213
220
|
logger.section('🚀 Initializing CursorFlow');
|
|
214
221
|
|
|
215
222
|
const options = parseArgs(args);
|
|
@@ -228,7 +235,7 @@ async function init(args) {
|
|
|
228
235
|
try {
|
|
229
236
|
createDefaultConfig(projectRoot, options.force);
|
|
230
237
|
logger.success(`Created config file: cursorflow.config.js`);
|
|
231
|
-
} catch (error) {
|
|
238
|
+
} catch (error: any) {
|
|
232
239
|
if (error.message.includes('already exists') && !options.force) {
|
|
233
240
|
logger.warn(error.message);
|
|
234
241
|
} else {
|
|
@@ -237,7 +244,8 @@ async function init(args) {
|
|
|
237
244
|
}
|
|
238
245
|
}
|
|
239
246
|
|
|
240
|
-
|
|
247
|
+
// We need to require the config file after it might have been created
|
|
248
|
+
const config: CursorFlowConfig = require(configPath);
|
|
241
249
|
|
|
242
250
|
if (options.configOnly) {
|
|
243
251
|
logger.section('✅ Configuration initialized');
|
|
@@ -256,7 +264,7 @@ async function init(args) {
|
|
|
256
264
|
logger.info('\n📝 Updating .gitignore...');
|
|
257
265
|
try {
|
|
258
266
|
updateGitignore(projectRoot);
|
|
259
|
-
} catch (error) {
|
|
267
|
+
} catch (error: any) {
|
|
260
268
|
logger.warn(`Failed to update .gitignore: ${error.message}`);
|
|
261
269
|
logger.info('You can manually add "_cursorflow/" to your .gitignore');
|
|
262
270
|
}
|
|
@@ -267,7 +275,7 @@ async function init(args) {
|
|
|
267
275
|
logger.info('\n📋 Installing Cursor commands...');
|
|
268
276
|
try {
|
|
269
277
|
await setupCommands({ force: options.force, silent: false });
|
|
270
|
-
} catch (error) {
|
|
278
|
+
} catch (error: any) {
|
|
271
279
|
logger.warn(`Failed to install Cursor commands: ${error.message}`);
|
|
272
280
|
logger.info('You can install them later with: npx cursorflow-setup');
|
|
273
281
|
}
|
|
@@ -297,4 +305,4 @@ async function init(args) {
|
|
|
297
305
|
console.log('');
|
|
298
306
|
}
|
|
299
307
|
|
|
300
|
-
|
|
308
|
+
export = init;
|