clitrigger 0.1.0 → 0.1.3
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/.env.example +1 -1
- package/README.md +34 -1
- package/bin/clitrigger.js +146 -12
- package/bin/postuninstall.js +19 -0
- package/dist/client/assets/index-B4peRpUi.js +134 -0
- package/dist/client/assets/index-Bjyoy6lB.css +1 -0
- package/dist/client/index.html +3 -2
- package/dist/client/logo-icon.svg +15 -0
- package/dist/client/logo.svg +17 -0
- package/dist/server/db/queries.d.ts +7 -3
- package/dist/server/db/queries.d.ts.map +1 -1
- package/dist/server/db/queries.js +15 -3
- package/dist/server/db/queries.js.map +1 -1
- package/dist/server/db/schema.d.ts.map +1 -1
- package/dist/server/db/schema.js +4 -0
- package/dist/server/db/schema.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +43 -11
- package/dist/server/index.js.map +1 -1
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/auth.js +0 -4
- package/dist/server/middleware/auth.js.map +1 -1
- package/dist/server/routes/auth.d.ts.map +1 -1
- package/dist/server/routes/auth.js +1 -2
- package/dist/server/routes/auth.js.map +1 -1
- package/dist/server/routes/execution.d.ts.map +1 -1
- package/dist/server/routes/execution.js +65 -5
- package/dist/server/routes/execution.js.map +1 -1
- package/dist/server/routes/projects.d.ts.map +1 -1
- package/dist/server/routes/projects.js +82 -5
- package/dist/server/routes/projects.js.map +1 -1
- package/dist/server/services/claude-manager.d.ts +1 -1
- package/dist/server/services/claude-manager.d.ts.map +1 -1
- package/dist/server/services/claude-manager.js +46 -17
- package/dist/server/services/claude-manager.js.map +1 -1
- package/dist/server/services/cli-adapters.d.ts +4 -0
- package/dist/server/services/cli-adapters.d.ts.map +1 -1
- package/dist/server/services/cli-adapters.js +12 -3
- package/dist/server/services/cli-adapters.js.map +1 -1
- package/dist/server/services/debug-logger.d.ts +1 -0
- package/dist/server/services/debug-logger.d.ts.map +1 -1
- package/dist/server/services/debug-logger.js +15 -0
- package/dist/server/services/debug-logger.js.map +1 -1
- package/dist/server/services/log-streamer.d.ts +7 -0
- package/dist/server/services/log-streamer.d.ts.map +1 -1
- package/dist/server/services/log-streamer.js +26 -13
- package/dist/server/services/log-streamer.js.map +1 -1
- package/dist/server/services/orchestrator.d.ts +8 -0
- package/dist/server/services/orchestrator.d.ts.map +1 -1
- package/dist/server/services/orchestrator.js +142 -39
- package/dist/server/services/orchestrator.js.map +1 -1
- package/dist/server/services/pty-output-filter.d.ts +18 -0
- package/dist/server/services/pty-output-filter.d.ts.map +1 -0
- package/dist/server/services/pty-output-filter.js +125 -0
- package/dist/server/services/pty-output-filter.js.map +1 -0
- package/dist/server/services/task-intent.d.ts +6 -0
- package/dist/server/services/task-intent.d.ts.map +1 -0
- package/dist/server/services/task-intent.js +4 -0
- package/dist/server/services/task-intent.js.map +1 -0
- package/dist/server/services/tunnel-manager.d.ts +2 -2
- package/dist/server/services/tunnel-manager.d.ts.map +1 -1
- package/dist/server/services/tunnel-manager.js +20 -7
- package/dist/server/services/tunnel-manager.js.map +1 -1
- package/dist/server/services/worktree-manager.d.ts +5 -0
- package/dist/server/services/worktree-manager.d.ts.map +1 -1
- package/dist/server/services/worktree-manager.js +40 -13
- package/dist/server/services/worktree-manager.js.map +1 -1
- package/dist/server/websocket/index.d.ts.map +1 -1
- package/dist/server/websocket/index.js +17 -6
- package/dist/server/websocket/index.js.map +1 -1
- package/package.json +4 -2
- package/dist/client/assets/index-BkOCv65b.css +0 -1
- package/dist/client/assets/index-Fbf16Lh1.js +0 -129
package/.env.example
CHANGED
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="src/client/public/logo.svg">
|
|
5
|
+
<source media="(prefers-color-scheme: light)" srcset="src/client/public/logo.svg">
|
|
6
|
+
<img alt="CLITrigger" src="src/client/public/logo.svg" width="360">
|
|
7
|
+
</picture>
|
|
4
8
|
|
|
5
9
|
**AI-Powered Parallel Worktree Automation**
|
|
6
10
|
|
|
@@ -25,6 +29,11 @@
|
|
|
25
29
|
> 하지만 **이해 없는 바이브 코딩**은 결국 한계에 부딪힌다.
|
|
26
30
|
> CLITrigger는 AI를 병렬로 돌리면서도, 개발자가 맥락을 잃지 않도록 설계되었다.
|
|
27
31
|
|
|
32
|
+
<div align="center">
|
|
33
|
+
<img src="docs/images/screenshot-tasks.png" alt="Tasks — 병렬 워크트리 실행" width="800">
|
|
34
|
+
<p><em>병렬 워크트리에서 AI CLI가 동시에 작업을 처리하는 모습</em></p>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
28
37
|
---
|
|
29
38
|
|
|
30
39
|
## 왜 CLITrigger인가?
|
|
@@ -67,9 +76,19 @@ TODO를 작성하면 각 작업마다 격리된 git worktree가 자동 생성된
|
|
|
67
76
|
### 다중 AI 토론 (Discussion)
|
|
68
77
|
아키텍트, 개발자, 리뷰어 등 역할이 다른 AI 에이전트들이 라운드 방식으로 토론한 뒤, 합의된 내용을 바탕으로 자동 구현까지 이어진다. 단일 AI의 판단보다 훨씬 검증된 설계 결과물이 나온다.
|
|
69
78
|
|
|
79
|
+
<div align="center">
|
|
80
|
+
<img src="docs/images/screenshot-discussions.png" alt="Discussions — 다중 AI 토론" width="800">
|
|
81
|
+
<p><em>여러 AI 에이전트가 역할별로 토론하는 Discussion 화면</em></p>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
70
84
|
### 예약 실행 (Scheduler)
|
|
71
85
|
토큰 한도를 피해 새벽이나 특정 시각에 작업을 예약 실행할 수 있다. cron 기반 반복 스케줄과 일회성 예약 모두 지원한다.
|
|
72
86
|
|
|
87
|
+
<div align="center">
|
|
88
|
+
<img src="docs/images/screenshot-schedules.png" alt="Schedules — 예약 실행" width="800">
|
|
89
|
+
<p><em>cron 기반 반복·일회성 예약 실행 설정 화면</em></p>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
73
92
|
### 파이프라인 (Pipeline)
|
|
74
93
|
여러 작업을 순차 또는 병렬로 묶어 다단계 실행 흐름을 구성한다. 복잡한 릴리스 절차도 자동화할 수 있다.
|
|
75
94
|
|
|
@@ -118,6 +137,9 @@ clitrigger config password # 비밀번호 변경
|
|
|
118
137
|
```
|
|
119
138
|
|
|
120
139
|
> **사전 요구사항**: Node.js 20+, Git, 사용할 AI CLI (Claude / Gemini / Codex 중 하나 이상)
|
|
140
|
+
>
|
|
141
|
+
> **지원 플랫폼**: Windows · macOS · Linux — 모든 핵심 코드가 크로스 플랫폼 대응되어 있다.
|
|
142
|
+
> macOS에서는 네이티브 모듈 빌드를 위해 `xcode-select --install`이 필요할 수 있다.
|
|
121
143
|
|
|
122
144
|
### 소스에서 직접 실행 (개발용)
|
|
123
145
|
|
|
@@ -154,6 +176,17 @@ npm run dev
|
|
|
154
176
|
| `start-tunnel.bat` | 터널 모드 실행 |
|
|
155
177
|
| `test.bat` | 전체 테스트 |
|
|
156
178
|
|
|
179
|
+
#### macOS / Linux
|
|
180
|
+
|
|
181
|
+
`npm run` 명령어가 모든 플랫폼에서 동일하게 동작한다. `.bat` 스크립트 대신 터미널에서 직접 실행하면 된다.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
npm run dev # 개발 모드
|
|
185
|
+
npm run build # 빌드
|
|
186
|
+
npm run start # 프로덕션 실행
|
|
187
|
+
npm test # 테스트
|
|
188
|
+
```
|
|
189
|
+
|
|
157
190
|
</details>
|
|
158
191
|
|
|
159
192
|
### 외부 접속 (Cloudflare Tunnel)
|
package/bin/clitrigger.js
CHANGED
|
@@ -4,6 +4,7 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import os from 'os';
|
|
6
6
|
import { createInterface } from 'readline/promises';
|
|
7
|
+
import { execSync, spawn } from 'child_process';
|
|
7
8
|
|
|
8
9
|
const CONFIG_DIR = path.join(os.homedir(), '.clitrigger');
|
|
9
10
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
@@ -15,7 +16,10 @@ if (args[0] === 'config') {
|
|
|
15
16
|
} else if (args[0] === '--help' || args[0] === '-h') {
|
|
16
17
|
printHelp();
|
|
17
18
|
} else {
|
|
18
|
-
await
|
|
19
|
+
const updated = await checkAutoUpdate();
|
|
20
|
+
if (!updated) {
|
|
21
|
+
await startServer();
|
|
22
|
+
}
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
async function startServer() {
|
|
@@ -26,29 +30,69 @@ async function startServer() {
|
|
|
26
30
|
console.log('Welcome to CLITrigger!\n');
|
|
27
31
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
28
32
|
|
|
29
|
-
const setPassword = await rl.question('비밀번호를 설정하시겠습니까? (y/N): ');
|
|
30
33
|
let password = '';
|
|
31
|
-
|
|
32
|
-
password = await rl.question('
|
|
34
|
+
while (!password) {
|
|
35
|
+
password = await rl.question('비밀번호를 설정해주세요: ');
|
|
36
|
+
if (!password) console.log('비밀번호는 필수입니다.');
|
|
33
37
|
}
|
|
34
38
|
rl.close();
|
|
35
39
|
|
|
36
|
-
const config = { port: 3000, password };
|
|
40
|
+
const config = { port: 3000, password, tunnel: true };
|
|
37
41
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
38
42
|
console.log(`\n✅ 설정 완료! (${CONFIG_FILE})`);
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
// config 읽고 env 설정
|
|
42
46
|
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
47
|
+
|
|
48
|
+
// 기존 config에 비밀번호가 없으면 설정 강제
|
|
49
|
+
if (!config.password) {
|
|
50
|
+
console.log('⚠️ 비밀번호가 설정되지 않았습니다.\n');
|
|
51
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
52
|
+
let password = '';
|
|
53
|
+
while (!password) {
|
|
54
|
+
password = await rl.question('비밀번호를 설정해주세요: ');
|
|
55
|
+
if (!password) console.log('비밀번호는 필수입니다.');
|
|
56
|
+
}
|
|
57
|
+
rl.close();
|
|
58
|
+
config.password = password;
|
|
59
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
60
|
+
console.log('✅ 비밀번호가 설정되었습니다.\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
43
63
|
process.env.PORT = String(config.port || 3000);
|
|
44
|
-
|
|
64
|
+
process.env.AUTH_PASSWORD = config.password;
|
|
45
65
|
process.env.DB_PATH = path.join(CONFIG_DIR, 'clitrigger.db');
|
|
66
|
+
// tunnel defaults to true (auto-enable for new and existing users)
|
|
67
|
+
if (config.tunnel !== false) {
|
|
68
|
+
process.env.TUNNEL_ENABLED = 'true';
|
|
69
|
+
}
|
|
70
|
+
if (config.tunnelName) {
|
|
71
|
+
process.env.TUNNEL_NAME = config.tunnelName;
|
|
72
|
+
}
|
|
46
73
|
|
|
47
74
|
// 서버 시작
|
|
48
75
|
await import('../dist/server/index.js');
|
|
49
76
|
}
|
|
50
77
|
|
|
51
78
|
async function handleConfig(args) {
|
|
79
|
+
if (args[0] === 'clear') {
|
|
80
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
81
|
+
console.log('삭제할 설정이 없습니다.');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
85
|
+
const answer = await rl.question(`⚠️ ${CONFIG_DIR} 의 모든 설정과 데이터가 삭제됩니다. 계속하시겠습니까? (y/N) `);
|
|
86
|
+
rl.close();
|
|
87
|
+
if (answer.toLowerCase() === 'y') {
|
|
88
|
+
fs.rmSync(CONFIG_DIR, { recursive: true, force: true });
|
|
89
|
+
console.log('✅ 설정 및 데이터가 삭제되었습니다.');
|
|
90
|
+
} else {
|
|
91
|
+
console.log('취소되었습니다.');
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
52
96
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
53
97
|
|
|
54
98
|
if (!fs.existsSync(CONFIG_FILE)) {
|
|
@@ -73,21 +117,107 @@ async function handleConfig(args) {
|
|
|
73
117
|
console.log(`✅ 포트가 ${port}으로 변경되었습니다.`);
|
|
74
118
|
} else if (args[0] === 'password') {
|
|
75
119
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
76
|
-
|
|
120
|
+
let password = '';
|
|
121
|
+
while (!password) {
|
|
122
|
+
password = await rl.question('새 비밀번호: ');
|
|
123
|
+
if (!password) console.log('비밀번호는 필수입니다.');
|
|
124
|
+
}
|
|
77
125
|
rl.close();
|
|
78
126
|
config.password = password;
|
|
79
127
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
80
|
-
|
|
81
|
-
console.log('✅ 비밀번호가 변경되었습니다.');
|
|
82
|
-
} else {
|
|
83
|
-
console.log('✅ 비밀번호가 해제되었습니다.');
|
|
84
|
-
}
|
|
128
|
+
console.log('✅ 비밀번호가 변경되었습니다.');
|
|
85
129
|
} else if (args[0] === 'path') {
|
|
86
130
|
console.log(CONFIG_DIR);
|
|
131
|
+
} else if (args[0] === 'tunnel') {
|
|
132
|
+
if (!args[1]) {
|
|
133
|
+
console.log(`터널: ${config.tunnel ? '활성화' : '비활성화'}${config.tunnelName ? ` (이름: ${config.tunnelName})` : ''}`);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (args[1] === 'on') {
|
|
137
|
+
config.tunnel = true;
|
|
138
|
+
if (args[2]) config.tunnelName = args[2];
|
|
139
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
140
|
+
console.log(`✅ 터널이 활성화되었습니다.${config.tunnelName ? ` (이름: ${config.tunnelName})` : ''}`);
|
|
141
|
+
} else if (args[1] === 'off') {
|
|
142
|
+
config.tunnel = false;
|
|
143
|
+
delete config.tunnelName;
|
|
144
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
145
|
+
console.log('✅ 터널이 비활성화되었습니다.');
|
|
146
|
+
} else {
|
|
147
|
+
console.log('사용법: clitrigger config tunnel [on [이름] | off]');
|
|
148
|
+
}
|
|
87
149
|
} else {
|
|
88
150
|
console.log(`현재 설정 (${CONFIG_FILE}):`);
|
|
89
151
|
console.log(` 포트: ${config.port || 3000}`);
|
|
90
152
|
console.log(` 비밀번호: ${config.password ? '설정됨' : '없음'}`);
|
|
153
|
+
console.log(` 터널: ${config.tunnel ? '활성화' : '비활성화'}${config.tunnelName ? ` (이름: ${config.tunnelName})` : ''}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function isNewerVersion(latest, current) {
|
|
158
|
+
const a = latest.split('.').map(Number);
|
|
159
|
+
const b = current.split('.').map(Number);
|
|
160
|
+
for (let i = 0; i < 3; i++) {
|
|
161
|
+
if ((a[i] || 0) > (b[i] || 0)) return true;
|
|
162
|
+
if ((a[i] || 0) < (b[i] || 0)) return false;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function checkAutoUpdate() {
|
|
168
|
+
// 업데이트 직후 재시작된 프로세스면 스킵
|
|
169
|
+
if (process.env.CLITRIGGER_UPDATED === '1') {
|
|
170
|
+
delete process.env.CLITRIGGER_UPDATED;
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
if (!fs.existsSync(CONFIG_FILE)) return false;
|
|
176
|
+
|
|
177
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
178
|
+
|
|
179
|
+
// 24시간 이내에 체크했으면 스킵
|
|
180
|
+
const now = Date.now();
|
|
181
|
+
const lastCheck = config.lastUpdateCheck || 0;
|
|
182
|
+
if (now - lastCheck < 24 * 60 * 60 * 1000) return false;
|
|
183
|
+
|
|
184
|
+
// 체크 시간 저장 (네트워크 실패 시에도 반복 체크 방지)
|
|
185
|
+
config.lastUpdateCheck = now;
|
|
186
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
187
|
+
|
|
188
|
+
// 현재 버전 읽기
|
|
189
|
+
const pkgPath = new URL('../package.json', import.meta.url);
|
|
190
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
191
|
+
const currentVersion = pkg.version;
|
|
192
|
+
|
|
193
|
+
// npm registry에서 최신 버전 조회 (5초 타임아웃)
|
|
194
|
+
const controller = new AbortController();
|
|
195
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
196
|
+
const res = await fetch('https://registry.npmjs.org/clitrigger/latest', {
|
|
197
|
+
signal: controller.signal,
|
|
198
|
+
});
|
|
199
|
+
clearTimeout(timeout);
|
|
200
|
+
|
|
201
|
+
if (!res.ok) return false;
|
|
202
|
+
const data = await res.json();
|
|
203
|
+
const latestVersion = data.version;
|
|
204
|
+
|
|
205
|
+
if (!isNewerVersion(latestVersion, currentVersion)) return false;
|
|
206
|
+
|
|
207
|
+
console.log(`\n🔄 새 버전 발견: v${currentVersion} → v${latestVersion}, 업데이트 중...`);
|
|
208
|
+
execSync('npm i -g clitrigger@latest', { stdio: 'inherit' });
|
|
209
|
+
console.log(`✅ v${latestVersion} 업데이트 완료! 재시작합니다...\n`);
|
|
210
|
+
|
|
211
|
+
// 업데이트된 코드로 재시작
|
|
212
|
+
const child = spawn(process.argv[0], process.argv.slice(1), {
|
|
213
|
+
stdio: 'inherit',
|
|
214
|
+
env: { ...process.env, CLITRIGGER_UPDATED: '1' },
|
|
215
|
+
});
|
|
216
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
217
|
+
return true;
|
|
218
|
+
} catch {
|
|
219
|
+
// 네트워크 오류, 타임아웃 등 — 무시하고 현재 버전으로 계속
|
|
220
|
+
return false;
|
|
91
221
|
}
|
|
92
222
|
}
|
|
93
223
|
|
|
@@ -100,7 +230,11 @@ Usage:
|
|
|
100
230
|
clitrigger config 현재 설정 보기
|
|
101
231
|
clitrigger config port <n> 포트 변경
|
|
102
232
|
clitrigger config password 비밀번호 변경
|
|
233
|
+
clitrigger config tunnel on Cloudflare 터널 활성화
|
|
234
|
+
clitrigger config tunnel on <name> Named 터널 활성화
|
|
235
|
+
clitrigger config tunnel off 터널 비활성화
|
|
103
236
|
clitrigger config path 설정 디렉토리 경로 출력
|
|
237
|
+
clitrigger config clear 설정 및 데이터 완전 삭제
|
|
104
238
|
clitrigger --help 이 도움말 표시
|
|
105
239
|
`.trim());
|
|
106
240
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
|
|
7
|
+
const CONFIG_DIR = path.join(os.homedir(), '.clitrigger');
|
|
8
|
+
|
|
9
|
+
if (fs.existsSync(CONFIG_DIR)) {
|
|
10
|
+
console.log(`
|
|
11
|
+
CLITrigger가 제거되었습니다.
|
|
12
|
+
|
|
13
|
+
설정 및 데이터가 아래 경로에 남아 있습니다:
|
|
14
|
+
${CONFIG_DIR}
|
|
15
|
+
|
|
16
|
+
완전히 제거하려면 해당 폴더를 수동으로 삭제해주세요:
|
|
17
|
+
rm -rf ${CONFIG_DIR}
|
|
18
|
+
`.trim());
|
|
19
|
+
}
|