sleepcode 1.0.0 → 1.2.0
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/.claude/settings.local.json +17 -0
- package/README.md +225 -178
- package/bin/index.js +503 -331
- package/package.json +17 -17
- package/templates/common/README.md +100 -97
- package/templates/common/ai_worker.ps1 +20 -0
- package/templates/common/ai_worker.sh +23 -23
- package/templates/common/log_filter.py +63 -63
- package/templates/common/run_forever.ps1 +59 -0
- package/templates/common/run_forever.sh +59 -59
- package/templates/rules/custom.md +69 -69
- package/templates/rules/nextjs.md +77 -77
- package/templates/rules/react-native.md +76 -76
- package/templates/rules/spring-boot.md +78 -78
- package/templates/settings/custom.json +19 -19
- package/templates/settings/nextjs.json +30 -30
- package/templates/settings/react-native.json +30 -30
- package/templates/settings/spring-boot.json +24 -24
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "sleepcode",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "AI codes while you sleep — Claude AI 야간 자동화 세팅 CLI",
|
|
5
|
-
"bin": {
|
|
6
|
-
"sleepcode": "bin/index.js"
|
|
7
|
-
},
|
|
8
|
-
"keywords": [
|
|
9
|
-
"claude",
|
|
10
|
-
"ai",
|
|
11
|
-
"automation",
|
|
12
|
-
"coding",
|
|
13
|
-
"nightshift",
|
|
14
|
-
"sleepcode"
|
|
15
|
-
],
|
|
16
|
-
"license": "MIT"
|
|
17
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "sleepcode",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "AI codes while you sleep — Claude AI 야간 자동화 세팅 CLI",
|
|
5
|
+
"bin": {
|
|
6
|
+
"sleepcode": "./bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"claude",
|
|
10
|
+
"ai",
|
|
11
|
+
"automation",
|
|
12
|
+
"coding",
|
|
13
|
+
"nightshift",
|
|
14
|
+
"sleepcode"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT"
|
|
17
|
+
}
|
|
@@ -1,97 +1,100 @@
|
|
|
1
|
-
# SleepCode
|
|
2
|
-
|
|
3
|
-
AI codes while you sleep — 밤새 개발 작업을 자동화하는 시스템입니다.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## 폴더 구조
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
.
|
|
11
|
-
rules.md
|
|
12
|
-
tasks.md
|
|
13
|
-
docs/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
# SleepCode
|
|
2
|
+
|
|
3
|
+
AI codes while you sleep — 밤새 개발 작업을 자동화하는 시스템입니다.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 폴더 구조
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
.sleepcode/
|
|
11
|
+
rules.md # ✏️ AI 역할 + 작업 규칙 (수정하세요)
|
|
12
|
+
tasks.md # ✏️ 오늘 진행할 작업 목록 (수정하세요)
|
|
13
|
+
docs/ # ✏️ 개발 참고 자료 (피그마 스크린샷, 기획서 등)
|
|
14
|
+
scripts/ # ⚙️ 시스템 스크립트 (수정하지 마세요)
|
|
15
|
+
ai_worker.* # 1회 실행 스크립트
|
|
16
|
+
run_forever.* # 무한 루프 감시자 스크립트
|
|
17
|
+
log_filter.py # 로그 필터 (핵심 메시지만 추출)
|
|
18
|
+
logs/ # 실행 로그 (자동 생성)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 작동 원리
|
|
24
|
+
|
|
25
|
+
1. `claude -p` 로 비대화형 모드 실행
|
|
26
|
+
2. `rules.md` + `tasks.md` 를 합쳐서 프롬프트로 전달
|
|
27
|
+
3. AI가 코드 작성 → 빌드/테스트 → 오류 수정 → git commit
|
|
28
|
+
4. 대기 후 다시 반복
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 실행 방법
|
|
33
|
+
|
|
34
|
+
### 1. (최초 1회) --dangerously-skip-permissions 수락
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
claude --dangerously-skip-permissions
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
동의 프롬프트가 뜨면 수락 후 `Ctrl + C`로 나옵니다.
|
|
41
|
+
|
|
42
|
+
### 2. 실행
|
|
43
|
+
|
|
44
|
+
**macOS / Linux:**
|
|
45
|
+
```bash
|
|
46
|
+
# 권한 부여
|
|
47
|
+
chmod +x .sleepcode/scripts/*.sh
|
|
48
|
+
|
|
49
|
+
# 1회 실행
|
|
50
|
+
./.sleepcode/scripts/ai_worker.sh
|
|
51
|
+
|
|
52
|
+
# 무한 루프 (tmux)
|
|
53
|
+
tmux new -s ai './.sleepcode/scripts/run_forever.sh'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Windows (PowerShell):**
|
|
57
|
+
```powershell
|
|
58
|
+
# 1회 실행
|
|
59
|
+
powershell -File .\.sleepcode\scripts\ai_worker.ps1
|
|
60
|
+
|
|
61
|
+
# 무한 루프
|
|
62
|
+
powershell -File .\.sleepcode\scripts\run_forever.ps1
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. tmux 분리 (백그라운드 전환, macOS/Linux)
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
Ctrl + B → D
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 관리 명령어
|
|
74
|
+
|
|
75
|
+
| 동작 | 명령어 |
|
|
76
|
+
|------|--------|
|
|
77
|
+
| 세션 재접속 | `tmux attach -t ai` |
|
|
78
|
+
| 실시간 로그 | `tail -f .sleepcode/logs/worker_*.log` |
|
|
79
|
+
| 종료 | `tmux attach -t ai` → `Ctrl + C` |
|
|
80
|
+
| 세션 삭제 | `tmux kill-session -t ai` |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 아침 확인
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# 밤 동안의 커밋 확인
|
|
88
|
+
git log --oneline --since="12 hours ago"
|
|
89
|
+
|
|
90
|
+
# 로그 확인
|
|
91
|
+
tail -100 .sleepcode/logs/worker_*.log
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 커스터마이징
|
|
97
|
+
|
|
98
|
+
- **역할/규칙 변경**: `.sleepcode/rules.md` 수정
|
|
99
|
+
- **태스크 변경**: `.sleepcode/tasks.md` 수정
|
|
100
|
+
- **참고 자료 추가**: `.sleepcode/docs/` 에 파일 추가
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# AI Worker - 1회 실행 스크립트 (Windows PowerShell)
|
|
2
|
+
# run_forever.ps1 (무한 루프) 대신 수동으로 1회만 돌릴 때 사용
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = "Stop"
|
|
5
|
+
Set-Location (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent)
|
|
6
|
+
|
|
7
|
+
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
8
|
+
Write-Host "[$timestamp] AI 단일 실행 시작"
|
|
9
|
+
|
|
10
|
+
$rules = Get-Content .sleepcode/rules.md -Raw -Encoding UTF8
|
|
11
|
+
$tasks = Get-Content .sleepcode/tasks.md -Raw -Encoding UTF8
|
|
12
|
+
|
|
13
|
+
$prompt = "$rules`n`n---`n`n$tasks"
|
|
14
|
+
|
|
15
|
+
# stream-json + verbose: 토큰 단위 실시간 출력
|
|
16
|
+
$prompt | claude -p --dangerously-skip-permissions --output-format stream-json --verbose 2>&1 |
|
|
17
|
+
python .sleepcode/scripts/log_filter.py
|
|
18
|
+
|
|
19
|
+
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
20
|
+
Write-Host "[$timestamp] AI 단일 실행 종료"
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# AI Worker - 1회 실행 스크립트
|
|
4
|
-
# run_forever.sh (무한 루프) 대신 수동으로 1회만 돌릴 때 사용
|
|
5
|
-
|
|
6
|
-
cd "$(dirname "$0")
|
|
7
|
-
|
|
8
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] AI 단일 실행 시작"
|
|
9
|
-
|
|
10
|
-
RULES=$(cat .
|
|
11
|
-
TASKS=$(cat .
|
|
12
|
-
|
|
13
|
-
PROMPT="${RULES}
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
${TASKS}"
|
|
18
|
-
|
|
19
|
-
# stream-json + verbose: 토큰 단위 실시간 출력
|
|
20
|
-
claude -p "$PROMPT" --dangerously-skip-permissions --output-format stream-json --verbose 2>&1 \
|
|
21
|
-
| python3 .
|
|
22
|
-
|
|
23
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] AI 단일 실행 종료"
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# AI Worker - 1회 실행 스크립트
|
|
4
|
+
# run_forever.sh (무한 루프) 대신 수동으로 1회만 돌릴 때 사용
|
|
5
|
+
|
|
6
|
+
cd "$(dirname "$0")/../.." || exit 1
|
|
7
|
+
|
|
8
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] AI 단일 실행 시작"
|
|
9
|
+
|
|
10
|
+
RULES=$(cat .sleepcode/rules.md)
|
|
11
|
+
TASKS=$(cat .sleepcode/tasks.md)
|
|
12
|
+
|
|
13
|
+
PROMPT="${RULES}
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
${TASKS}"
|
|
18
|
+
|
|
19
|
+
# stream-json + verbose: 토큰 단위 실시간 출력
|
|
20
|
+
claude -p "$PROMPT" --dangerously-skip-permissions --output-format stream-json --verbose 2>&1 \
|
|
21
|
+
| python3 .sleepcode/scripts/log_filter.py
|
|
22
|
+
|
|
23
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] AI 단일 실행 종료"
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
stream-json 출력에서 핵심 메시지만 추출하는 필터.
|
|
4
|
-
Usage: claude ... --output-format stream-json | python3 .
|
|
5
|
-
"""
|
|
6
|
-
import sys
|
|
7
|
-
import json
|
|
8
|
-
|
|
9
|
-
for line in sys.stdin:
|
|
10
|
-
line = line.strip()
|
|
11
|
-
if not line:
|
|
12
|
-
continue
|
|
13
|
-
try:
|
|
14
|
-
obj = json.loads(line)
|
|
15
|
-
except json.JSONDecodeError:
|
|
16
|
-
continue
|
|
17
|
-
|
|
18
|
-
msg_type = obj.get("type")
|
|
19
|
-
|
|
20
|
-
# assistant 메시지만 처리
|
|
21
|
-
if msg_type == "assistant":
|
|
22
|
-
message = obj.get("message", {})
|
|
23
|
-
contents = message.get("content", [])
|
|
24
|
-
for c in contents:
|
|
25
|
-
ctype = c.get("type")
|
|
26
|
-
if ctype == "text":
|
|
27
|
-
text = c.get("text", "").strip()
|
|
28
|
-
if text:
|
|
29
|
-
print(f"[TEXT] {text}", flush=True)
|
|
30
|
-
elif ctype == "tool_use":
|
|
31
|
-
name = c.get("name", "?")
|
|
32
|
-
inp = c.get("input", {})
|
|
33
|
-
# 도구별 핵심 파라미터만 요약
|
|
34
|
-
if name in ("Read", "Write", "Edit"):
|
|
35
|
-
param = inp.get("file_path", "")
|
|
36
|
-
print(f"[TOOL] {name}: {param}", flush=True)
|
|
37
|
-
elif name == "Bash":
|
|
38
|
-
cmd = inp.get("command", "")
|
|
39
|
-
if len(cmd) > 120:
|
|
40
|
-
cmd = cmd[:120] + "..."
|
|
41
|
-
print(f"[TOOL] Bash: {cmd}", flush=True)
|
|
42
|
-
elif name == "Glob":
|
|
43
|
-
print(f"[TOOL] Glob: {inp.get('pattern', '')}", flush=True)
|
|
44
|
-
elif name == "Grep":
|
|
45
|
-
print(f"[TOOL] Grep: {inp.get('pattern', '')}", flush=True)
|
|
46
|
-
elif name == "TodoWrite":
|
|
47
|
-
todos = inp.get("todos", [])
|
|
48
|
-
active = [t for t in todos if t.get("status") == "in_progress"]
|
|
49
|
-
if active:
|
|
50
|
-
print(f"[TODO] {active[0].get('activeForm', '')}", flush=True)
|
|
51
|
-
else:
|
|
52
|
-
print(f"[TOOL] {name}", flush=True)
|
|
53
|
-
|
|
54
|
-
# 최종 결과
|
|
55
|
-
elif msg_type == "result":
|
|
56
|
-
message = obj.get("message", "")
|
|
57
|
-
if isinstance(message, str) and message:
|
|
58
|
-
short = message[:200] + "..." if len(message) > 200 else message
|
|
59
|
-
print(f"[DONE] {short}", flush=True)
|
|
60
|
-
cost = obj.get("cost_usd")
|
|
61
|
-
duration = obj.get("duration_ms")
|
|
62
|
-
if cost is not None:
|
|
63
|
-
print(f"[COST] ${cost:.4f} | {(duration or 0) / 1000:.0f}s", flush=True)
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
stream-json 출력에서 핵심 메시지만 추출하는 필터.
|
|
4
|
+
Usage: claude ... --output-format stream-json | python3 .sleepcode/log_filter.py
|
|
5
|
+
"""
|
|
6
|
+
import sys
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
for line in sys.stdin:
|
|
10
|
+
line = line.strip()
|
|
11
|
+
if not line:
|
|
12
|
+
continue
|
|
13
|
+
try:
|
|
14
|
+
obj = json.loads(line)
|
|
15
|
+
except json.JSONDecodeError:
|
|
16
|
+
continue
|
|
17
|
+
|
|
18
|
+
msg_type = obj.get("type")
|
|
19
|
+
|
|
20
|
+
# assistant 메시지만 처리
|
|
21
|
+
if msg_type == "assistant":
|
|
22
|
+
message = obj.get("message", {})
|
|
23
|
+
contents = message.get("content", [])
|
|
24
|
+
for c in contents:
|
|
25
|
+
ctype = c.get("type")
|
|
26
|
+
if ctype == "text":
|
|
27
|
+
text = c.get("text", "").strip()
|
|
28
|
+
if text:
|
|
29
|
+
print(f"[TEXT] {text}", flush=True)
|
|
30
|
+
elif ctype == "tool_use":
|
|
31
|
+
name = c.get("name", "?")
|
|
32
|
+
inp = c.get("input", {})
|
|
33
|
+
# 도구별 핵심 파라미터만 요약
|
|
34
|
+
if name in ("Read", "Write", "Edit"):
|
|
35
|
+
param = inp.get("file_path", "")
|
|
36
|
+
print(f"[TOOL] {name}: {param}", flush=True)
|
|
37
|
+
elif name == "Bash":
|
|
38
|
+
cmd = inp.get("command", "")
|
|
39
|
+
if len(cmd) > 120:
|
|
40
|
+
cmd = cmd[:120] + "..."
|
|
41
|
+
print(f"[TOOL] Bash: {cmd}", flush=True)
|
|
42
|
+
elif name == "Glob":
|
|
43
|
+
print(f"[TOOL] Glob: {inp.get('pattern', '')}", flush=True)
|
|
44
|
+
elif name == "Grep":
|
|
45
|
+
print(f"[TOOL] Grep: {inp.get('pattern', '')}", flush=True)
|
|
46
|
+
elif name == "TodoWrite":
|
|
47
|
+
todos = inp.get("todos", [])
|
|
48
|
+
active = [t for t in todos if t.get("status") == "in_progress"]
|
|
49
|
+
if active:
|
|
50
|
+
print(f"[TODO] {active[0].get('activeForm', '')}", flush=True)
|
|
51
|
+
else:
|
|
52
|
+
print(f"[TOOL] {name}", flush=True)
|
|
53
|
+
|
|
54
|
+
# 최종 결과
|
|
55
|
+
elif msg_type == "result":
|
|
56
|
+
message = obj.get("message", "")
|
|
57
|
+
if isinstance(message, str) and message:
|
|
58
|
+
short = message[:200] + "..." if len(message) > 200 else message
|
|
59
|
+
print(f"[DONE] {short}", flush=True)
|
|
60
|
+
cost = obj.get("cost_usd")
|
|
61
|
+
duration = obj.get("duration_ms")
|
|
62
|
+
if cost is not None:
|
|
63
|
+
print(f"[COST] ${cost:.4f} | {(duration or 0) / 1000:.0f}s", flush=True)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# AI Night Worker - 감시자 스크립트 (Windows PowerShell)
|
|
2
|
+
# 사용법: powershell -File .\.sleepcode\scripts\run_forever.ps1
|
|
3
|
+
|
|
4
|
+
$ErrorActionPreference = "Continue"
|
|
5
|
+
Set-Location (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent)
|
|
6
|
+
|
|
7
|
+
$logDir = ".sleepcode/logs"
|
|
8
|
+
if (!(Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
|
|
9
|
+
$logFile = "$logDir/worker_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
|
|
10
|
+
|
|
11
|
+
function Log($msg) {
|
|
12
|
+
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $msg"
|
|
13
|
+
Write-Host $line
|
|
14
|
+
Add-Content -Path $logFile -Value $line -Encoding UTF8
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Log "=== AI Night Worker 시작 ==="
|
|
18
|
+
Log "로그 파일: $logFile"
|
|
19
|
+
|
|
20
|
+
$iteration = 0
|
|
21
|
+
|
|
22
|
+
while ($true) {
|
|
23
|
+
$iteration++
|
|
24
|
+
Log "--- 반복 #$iteration 시작 ---"
|
|
25
|
+
|
|
26
|
+
# 미완료 태스크 확인
|
|
27
|
+
$remaining = 0
|
|
28
|
+
if (Test-Path .sleepcode/tasks.md) {
|
|
29
|
+
$remaining = (Select-String -Pattern '\[ \]' -Path .sleepcode/tasks.md -SimpleMatch).Count
|
|
30
|
+
}
|
|
31
|
+
Log "남은 태스크: ${remaining}개"
|
|
32
|
+
|
|
33
|
+
if ($remaining -eq 0) {
|
|
34
|
+
Log "=== 모든 태스크 완료. 종료합니다. ==="
|
|
35
|
+
exit 0
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# rules.md + tasks.md 를 합쳐서 프롬프트 구성
|
|
39
|
+
$rules = Get-Content .sleepcode/rules.md -Raw -Encoding UTF8
|
|
40
|
+
$tasks = Get-Content .sleepcode/tasks.md -Raw -Encoding UTF8
|
|
41
|
+
$prompt = "$rules`n`n---`n`n$tasks"
|
|
42
|
+
|
|
43
|
+
Log "claude 실행 중..."
|
|
44
|
+
# stream-json -> log_filter.py 로 핵심 메시지만 추출
|
|
45
|
+
$prompt | claude -p --dangerously-skip-permissions --output-format stream-json --verbose 2>&1 |
|
|
46
|
+
python .sleepcode/scripts/log_filter.py |
|
|
47
|
+
Tee-Object -Append $logFile
|
|
48
|
+
$exitCode = $LASTEXITCODE
|
|
49
|
+
Log "claude 종료 (exit code: $exitCode)"
|
|
50
|
+
|
|
51
|
+
# 미커밋 변경사항 체크
|
|
52
|
+
$porcelain = git status --porcelain
|
|
53
|
+
if ($porcelain) {
|
|
54
|
+
Log "경고: 커밋되지 않은 변경사항 감지"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Log "--- 반복 #$iteration 종료, {{SLEEP_INTERVAL}}초 대기 ---"
|
|
58
|
+
Start-Sleep -Seconds {{SLEEP_INTERVAL}}
|
|
59
|
+
}
|
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# AI Night Worker - 감시자 스크립트
|
|
4
|
-
# 사용법: tmux new -s ai './.
|
|
5
|
-
|
|
6
|
-
cd "$(dirname "$0")
|
|
7
|
-
|
|
8
|
-
LOG_DIR=".
|
|
9
|
-
mkdir -p "$LOG_DIR"
|
|
10
|
-
LOG_FILE="$LOG_DIR/worker_$(date +%Y%m%d_%H%M%S).log"
|
|
11
|
-
|
|
12
|
-
log() {
|
|
13
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
log "=== AI Night Worker 시작 ==="
|
|
17
|
-
log "로그 파일: $LOG_FILE"
|
|
18
|
-
|
|
19
|
-
ITERATION=0
|
|
20
|
-
|
|
21
|
-
while true; do
|
|
22
|
-
ITERATION=$((ITERATION + 1))
|
|
23
|
-
log "--- 반복 #${ITERATION} 시작 ---"
|
|
24
|
-
|
|
25
|
-
# 미완료 태스크가 있는지 확인
|
|
26
|
-
REMAINING=$(grep -c '\[ \]' .
|
|
27
|
-
log "남은 태스크: ${REMAINING}개"
|
|
28
|
-
|
|
29
|
-
if [ "$REMAINING" -eq 0 ]; then
|
|
30
|
-
log "=== 모든 태스크 완료. 종료합니다. ==="
|
|
31
|
-
exit 0
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# rules.md + tasks.md 를 합쳐서 프롬프트 구성
|
|
35
|
-
RULES=$(cat .
|
|
36
|
-
TASKS=$(cat .
|
|
37
|
-
|
|
38
|
-
PROMPT="${RULES}
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
${TASKS}"
|
|
43
|
-
|
|
44
|
-
log "claude 실행 중..."
|
|
45
|
-
# stream-json → log_filter.py 로 핵심 메시지만 추출
|
|
46
|
-
claude -p "$PROMPT" --dangerously-skip-permissions --output-format stream-json --verbose 2>&1 \
|
|
47
|
-
| python3 .
|
|
48
|
-
| tee -a "$LOG_FILE"
|
|
49
|
-
EXIT_CODE=${PIPESTATUS[0]}
|
|
50
|
-
log "claude 종료 (exit code: $EXIT_CODE)"
|
|
51
|
-
|
|
52
|
-
# 미커밋 변경사항 체크
|
|
53
|
-
if [[ -n $(git status --porcelain) ]]; then
|
|
54
|
-
log "경고: 커밋되지 않은 변경사항 감지"
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
log "--- 반복 #${ITERATION} 종료, {{SLEEP_INTERVAL}}초 대기 ---"
|
|
58
|
-
sleep {{SLEEP_INTERVAL}}
|
|
59
|
-
done
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# AI Night Worker - 감시자 스크립트
|
|
4
|
+
# 사용법: tmux new -s ai './.sleepcode/scripts/run_forever.sh'
|
|
5
|
+
|
|
6
|
+
cd "$(dirname "$0")/../.." || exit 1
|
|
7
|
+
|
|
8
|
+
LOG_DIR=".sleepcode/logs"
|
|
9
|
+
mkdir -p "$LOG_DIR"
|
|
10
|
+
LOG_FILE="$LOG_DIR/worker_$(date +%Y%m%d_%H%M%S).log"
|
|
11
|
+
|
|
12
|
+
log() {
|
|
13
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
log "=== AI Night Worker 시작 ==="
|
|
17
|
+
log "로그 파일: $LOG_FILE"
|
|
18
|
+
|
|
19
|
+
ITERATION=0
|
|
20
|
+
|
|
21
|
+
while true; do
|
|
22
|
+
ITERATION=$((ITERATION + 1))
|
|
23
|
+
log "--- 반복 #${ITERATION} 시작 ---"
|
|
24
|
+
|
|
25
|
+
# 미완료 태스크가 있는지 확인
|
|
26
|
+
REMAINING=$(grep -c '\[ \]' .sleepcode/tasks.md 2>/dev/null || echo "0")
|
|
27
|
+
log "남은 태스크: ${REMAINING}개"
|
|
28
|
+
|
|
29
|
+
if [ "$REMAINING" -eq 0 ]; then
|
|
30
|
+
log "=== 모든 태스크 완료. 종료합니다. ==="
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# rules.md + tasks.md 를 합쳐서 프롬프트 구성
|
|
35
|
+
RULES=$(cat .sleepcode/rules.md)
|
|
36
|
+
TASKS=$(cat .sleepcode/tasks.md)
|
|
37
|
+
|
|
38
|
+
PROMPT="${RULES}
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
${TASKS}"
|
|
43
|
+
|
|
44
|
+
log "claude 실행 중..."
|
|
45
|
+
# stream-json → log_filter.py 로 핵심 메시지만 추출
|
|
46
|
+
claude -p "$PROMPT" --dangerously-skip-permissions --output-format stream-json --verbose 2>&1 \
|
|
47
|
+
| python3 .sleepcode/scripts/log_filter.py \
|
|
48
|
+
| tee -a "$LOG_FILE"
|
|
49
|
+
EXIT_CODE=${PIPESTATUS[0]}
|
|
50
|
+
log "claude 종료 (exit code: $EXIT_CODE)"
|
|
51
|
+
|
|
52
|
+
# 미커밋 변경사항 체크
|
|
53
|
+
if [[ -n $(git status --porcelain) ]]; then
|
|
54
|
+
log "경고: 커밋되지 않은 변경사항 감지"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
log "--- 반복 #${ITERATION} 종료, {{SLEEP_INTERVAL}}초 대기 ---"
|
|
58
|
+
sleep {{SLEEP_INTERVAL}}
|
|
59
|
+
done
|