@elyun/bylane 1.26.0 → 1.28.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.
@@ -1,13 +1,23 @@
1
1
  ---
2
2
  name: bylane-code-agent
3
- description: issue-agent가 작성한 전략 스펙(이슈 본문)을 기반으로 브랜치를 생성하고 코드를 구현한다. issueMemory에 진행 상황을 기록하며, 실패 피드백 루프로 재시도한다.
3
+ description: issue-agent가 작성한 전략 스펙을 기반으로 코드를 구현한다. 내부적으로 분석/구현/리뷰 서브에이전트를 활용하며, 서브에이전트 피드백 루프(Ralph Loop)로 품질을 보장한다. 부모 에이전트에게는 최종 결과만 반환한다.
4
4
  ---
5
5
 
6
6
  # Code Agent
7
7
 
8
- ## 입력 읽기
8
+ > **설계 원칙**: code-agent 본체는 **오케스트레이션만** 담당한다. 실제 파일 읽기/수정/분석은 전부 서브에이전트가 수행한다. 부모(orchestrator)에게는 `changedFiles` 결과만 돌려준다.
9
+
10
+ ---
11
+
12
+ ## 실행 전 상태 기록
13
+
14
+ ```bash
15
+ npx @elyun/bylane state write code-agent '{"status":"in_progress","startedAt":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","progress":0,"currentTask":"서브에이전트 초기화","retries":0,"log":[]}'
16
+ ```
17
+
18
+ ---
9
19
 
10
- ### 1. issue-agent 상태에서 spec 로드
20
+ ## 입력 읽기
11
21
 
12
22
  `.bylane/state/issue-agent.json`에서 다음 필드를 읽는다:
13
23
 
@@ -19,73 +29,172 @@ description: issue-agent가 작성한 전략 스펙(이슈 본문)을 기반으
19
29
  | `spec.checklist` | 구현 체크리스트 |
20
30
  | `spec.figmaSpec` | Figma 컬러토큰·컴포넌트 구조 |
21
31
  | `issueType` | `new-feature` / `bug` / `improvement` / `chore` |
32
+ | `issueNumber` | GitHub 이슈 번호 |
22
33
 
23
- 없으면 GitHub 이슈 본문을 직접 로드하여 "구현 방향", "관련 파일 및 영향 범위", "구현 체크리스트" 섹션을 파싱한다.
24
-
25
- ### 2. issueMemory 로드
26
-
27
- 이슈 번호를 알고 있으면 이전 세션 컨텍스트를 확인한다:
34
+ 없으면 GitHub 이슈 본문을 직접 로드하여 파싱한다.
28
35
 
36
+ issueMemory 로드:
29
37
  ```bash
30
38
  npx @elyun/bylane memory read ISSUE_NUMBER
31
39
  ```
32
40
 
33
- 아키텍처 결정이나 트러블슈팅 기록이 있으면 구현에 반영한다.
34
-
35
41
  ---
36
42
 
37
- ## 실행 상태 기록
43
+ ## Phase 1 병렬 분석 서브에이전트 (3개 동시 실행)
38
44
 
39
- ```bash
40
- npx @elyun/bylane state write code-agent '{"status":"in_progress","startedAt":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","progress":0,"currentTask":"코드 구현 시작","retries":0,"log":[]}'
45
+ Agent 도구로 3개를 **동시에** 호출한다.
46
+ 서브에이전트는 결과만 텍스트로 반환하고, code-agent 본체는 취합한다.
47
+
48
+ ### 서브에이전트 A — 파일 현황 분석
49
+ ```
50
+ prompt: |
51
+ 다음 파일들의 현재 구현 상태를 분석해줘:
52
+ 대상: {spec.affectedFiles}
53
+ issueType: {issueType}
54
+
55
+ 각 파일에 대해:
56
+ - 현재 구현 방식 요약 (있는 경우)
57
+ - 변경이 필요한 라인/영역
58
+ - 의존하는 import 목록
59
+
60
+ 결과만 간결하게 텍스트로 반환. 파일을 직접 수정하지 마라.
61
+ ```
62
+
63
+ ### 서브에이전트 B — 코드 패턴 샘플링
64
+ ```
65
+ prompt: |
66
+ 이 프로젝트에서 유사한 구현 패턴을 찾아줘.
67
+ 구현할 기능: {spec.title}
68
+ issueType: {issueType}
69
+
70
+ 찾아야 할 것:
71
+ - 유사한 컴포넌트/함수 구현 예시 (파일경로:라인번호)
72
+ - 사용 중인 네이밍 컨벤션
73
+ - 테스트 파일 위치와 작성 패턴
74
+ - 상태 관리 / API 호출 방식
75
+
76
+ 결과만 간결하게 텍스트로 반환. 파일을 수정하지 마라.
77
+ ```
78
+
79
+ ### 서브에이전트 C — 영향 범위 확인
80
+ ```
81
+ prompt: |
82
+ 다음 파일들을 수정할 때 영향받는 코드를 파악해줘:
83
+ 대상: {spec.affectedFiles}
84
+
85
+ 찾아야 할 것:
86
+ - 이 파일들을 import/사용하는 파일 목록
87
+ - 공유 컴포넌트/훅인 경우 사이드 이펙트 위험도
88
+ - bug/improvement인 경우: 현재 문제 지점 (파일:라인)
89
+
90
+ 결과만 간결하게 텍스트로 반환. 파일을 수정하지 마라.
41
91
  ```
42
92
 
43
93
  ---
44
94
 
45
- ## 실행 흐름
95
+ ## Phase 2 — Ralph Loop: 구현 → 리뷰 → 피드백 사이클
96
+
97
+ `spec.checklist` 항목을 순서대로 처리한다.
98
+ 각 항목마다 **구현 서브에이전트 → 리뷰 서브에이전트** 쌍으로 실행하며,
99
+ 리뷰가 통과될 때까지 피드백을 실어 구현 서브에이전트를 재호출한다.
100
+
101
+ ```
102
+ for each checklistItem in spec.checklist:
103
+
104
+ feedback = null
105
+ retries = 0
46
106
 
47
- ### issueType별 접근 전략
107
+ while true:
108
+ [구현 서브에이전트 호출]
109
+ [리뷰 서브에이전트 호출]
48
110
 
49
- #### `new-feature`
50
- - `spec.affectedFiles`의 신규 파일부터 생성
51
- - 유사 구현 패턴(이슈 본문 "코드 패턴 참고" 섹션) 따르기
52
- - Figma 스펙이 있으면 컬러토큰 → CSS 변수/Tailwind config 변환
111
+ if 리뷰 통과 → break
112
+ if retries >= 2 → 상태에 경고 기록 후 break
53
113
 
54
- #### `bug`
55
- - 이슈 본문의 "문제 지점" 파일부터 열기
56
- - 원인 파악 후 최소 변경으로 수정
57
- - 재현 케이스를 테스트로 먼저 작성 (TDD)
114
+ feedback = 리뷰 결과
115
+ retries++
116
+ ```
58
117
 
59
- #### `improvement`
60
- - 기존 구현 파악 후 변경 범위 최소화
61
- - 사이드 이펙트 발생 가능 파일은 이슈 본문 "영향 범위" 기준으로 확인
118
+ ### 구현 서브에이전트 프롬프트
62
119
 
63
- #### `chore`
64
- - 기능 변경 없음 확인 후 진행
65
- - 설정 파일 변경 시 기존 동작 보존 여부 체크
120
+ ```
121
+ prompt: |
122
+ 다음 체크리스트 항목을 구현해줘.
123
+
124
+ [컨텍스트]
125
+ - 작업: {spec.title}
126
+ - issueType: {issueType}
127
+ - 구현 방향: {spec.approach}
128
+ - 현재 항목: {checklistItem}
129
+ - 관련 파일: {spec.affectedFiles에서 이 항목과 관련된 파일}
130
+
131
+ [분석 결과]
132
+ - 파일 현황: {서브에이전트 A 결과}
133
+ - 참고 패턴: {서브에이전트 B 결과}
134
+ - 영향 범위: {서브에이전트 C 결과}
135
+
136
+ {feedback이 있으면 추가:}
137
+ [이전 리뷰 피드백 — 이 내용을 반드시 반영해야 함]
138
+ {feedback}
139
+
140
+ [코딩 원칙]
141
+ - 함수형 컴포넌트 + hooks 우선
142
+ - 파일당 단일 책임
143
+ - 200줄 초과 시 분리 고려
144
+ - 불변성 패턴 유지 (객체 직접 수정 금지)
145
+ - issueType이 bug라면 최소 변경으로 수정
146
+ - issueType이 chore라면 기능 변경 없음 확인
147
+
148
+ 구현 완료 후 변경한 파일 목록만 반환해줘.
149
+ ```
66
150
 
67
- ### 구현 순서
151
+ ### 리뷰 서브에이전트 프롬프트
68
152
 
69
- 1. `spec.checklist` 항목을 순서대로 처리
70
- 2. 각 파일 구현 완료 시 상태 로그 기록:
71
- ```bash
72
- npx @elyun/bylane state append code-agent "FILENAME 구현 완료"
73
- ```
74
- 3. 코드베이스 기존 패턴과 동일한 스타일 유지 (TypeScript, 테스트 위치, import 방식 등)
153
+ ```
154
+ prompt: |
155
+ 방금 구현된 코드를 리뷰해줘.
156
+
157
+ [검토 대상]
158
+ - 항목: {checklistItem}
159
+ - 변경 파일: {구현 서브에이전트가 반환한 파일 목록}
160
+
161
+ [체크리스트]
162
+ 1. spec.approach 방향과 일치하는가?
163
+ 2. 코딩 원칙(불변성, 단일책임, 200줄) 준수했는가?
164
+ 3. issueType별 주의사항 지켰는가?
165
+ - new-feature: 신규 파일 패턴이 기존 패턴과 일치하는가
166
+ - bug: 최소 변경인가, 다른 동작 변경 없는가
167
+ - improvement: 사이드 이펙트 없는가
168
+ - chore: 기능 변경 없는가
169
+ 4. 명백한 버그나 누락 없는가?
170
+
171
+ 응답 형식:
172
+ PASS — 문제 없음 (한 줄 요약)
173
+ FAIL — {구체적인 문제점과 수정 방향}
174
+
175
+ 코드를 직접 수정하지 마라. 판정만 내려줘.
176
+ ```
75
177
 
76
- ### 코딩 원칙
178
+ ### 상태 로그 (각 항목 완료 시)
77
179
 
78
- - 함수형 컴포넌트 + hooks 우선
79
- - 파일당 단일 책임
80
- - 200줄 초과 시 분리 고려
81
- - 불변성 패턴 유지 (객체 직접 수정 금지)
180
+ ```bash
181
+ npx @elyun/bylane state append code-agent "{checklistItem} 구현 완료 (리뷰 통과)"
182
+ ```
82
183
 
83
184
  ---
84
185
 
85
- ## 완료 처리
186
+ ## Phase 3 — 완료 처리
187
+
188
+ 모든 체크리스트 항목이 완료되면:
86
189
 
87
190
  ```bash
88
- npx @elyun/bylane state write code-agent '{"status":"completed","progress":100,"currentTask":"구현 완료","retries":0,"changedFiles":CHANGED_FILES_ARRAY}'
191
+ npx @elyun/bylane state write code-agent '{
192
+ "status": "completed",
193
+ "progress": 100,
194
+ "currentTask": "구현 완료",
195
+ "retries": 0,
196
+ "changedFiles": CHANGED_FILES_ARRAY
197
+ }'
89
198
  ```
90
199
 
91
200
  ---
@@ -105,7 +214,9 @@ npx @elyun/bylane memory append ISSUE_NUMBER code-agent "구현 요약: SUMMARY
105
214
 
106
215
  ## 출력
107
216
 
108
- `.bylane/state/code-agent.json`의 `changedFiles`: 변경된 파일 경로 배열
217
+ 부모(orchestrator)에게 돌려주는 값:
218
+ - `.bylane/state/code-agent.json`의 `changedFiles` 배열
219
+ - `status: "completed"` 또는 `"failed"`
109
220
 
110
221
  ## 수동 실행
111
222
 
@@ -1,51 +1,50 @@
1
1
  ---
2
2
  name: bylane-orchestrator
3
- description: byLane 메인 오케스트레이터. 자연어 의도를 파싱하여 4가지 파이프라인(새 기능/기존 이슈/리뷰/단일 에이전트) 중 하나를 선택하고 에이전트 체인을 순차 실행한다.
3
+ description: byLane 메인 오케스트레이터. 자연어 의도를 파싱하여 4가지 파이프라인(새 기능/기존 이슈/리뷰/단일 에이전트) 중 하나를 선택하고, 단계를 **서브에이전트**로 위임하여 실행한다. 오케스트레이터 본체는 상태 파일만 읽고 판단하며, 직접 구현하지 않는다.
4
4
  ---
5
5
 
6
6
  # byLane Orchestrator
7
7
 
8
8
  ## 역할
9
9
 
10
- 사용자 의도를 파싱하고, issue-agent를 통해 전략을 수립한 에이전트 파이프라인을 실행한다.
10
+ 사용자 의도를 파싱하고, 파이프라인을 결정한 **각 에이전트를 서브에이전트로 위임**한다.
11
+ 오케스트레이터 자신은 **상태 조회와 다음 단계 결정**만 담당한다. 직접 코드를 수정하거나 이슈를 작성하지 않는다.
11
12
 
12
- ## 실행 체크
13
+ > **Hang 방지 원칙**: 오케스트레이터는 Agent 도구로 서브에이전트를 호출하고 결과만 받는다.
14
+ > 긴 작업(코드 구현, 분석, 테스트 등)을 직접 수행하면 Claude Code가 hang될 수 있다.
15
+
16
+ ---
13
17
 
14
- 1. 사전 점검:
15
- ```bash
16
- npx @elyun/bylane preflight
17
- ```
18
- 실패 시 안내 메시지 출력 후 **중단**. `.bylane/bylane.json` 없으면 즉시 `bylane-setup` 스킬 실행.
18
+ ## 실행 전 체크
19
19
 
20
- 2. `.bylane/state/` 디렉토리 확인. 없으면 생성.
20
+ ```bash
21
+ npx @elyun/bylane preflight
22
+ ```
23
+ 실패 시 안내 메시지 출력 후 **중단**. `.bylane/bylane.json` 없으면 즉시 `bylane-setup` 스킬 실행.
21
24
 
22
25
  ## 에이전트별 모델 결정
23
26
 
24
27
  ```bash
25
28
  npx @elyun/bylane models
26
29
  ```
30
+ 출력 형식: `AGENT_NAME=MODEL_ID`. 서브에이전트 호출 시 `model` 파라미터로 전달.
27
31
 
28
- 출력 형식: `AGENT_NAME=MODEL_ID` (한 줄씩). 에이전트 호출 시 `model` 파라미터로 전달.
32
+ ---
29
33
 
30
34
  ## 의도 파싱 규칙
31
35
 
32
- 오케스트레이터는 `/bylane`에서 **복합 의도** 또는 **기능 요청 자연어**가 넘어올 때 실행된다.
33
- 단일 에이전트 키워드는 `/bylane`이 직접 라우팅하므로, 오케스트레이터는 **파이프라인 실행**에 집중한다.
34
-
35
36
  ### 파이프라인 A — 새 기능 / 이슈 없는 구현 요청
36
37
 
37
38
  **감지 키워드**: `만들어`, `추가해`, `구현해`, `개발해`, `넣어줘`, `바꿔줘`, `변경해`, `개선해`, `수정해`,
38
39
  `리팩터`, `리팩토링`, `마이그레이션`, `~해줘` (기능 설명 포함), 이슈 번호 없음
39
40
 
40
- **실행 흐름**:
41
- 전략 수립 → `issue-agent` → `code-agent` → `test-agent` → `commit-agent` → `pr-agent` → `review-agent` → `notify-agent`
41
+ **실행 흐름**: `issue-agent` → `code-agent` → `test-agent` → `commit-agent` → `pr-agent` → `review-agent` → `notify-agent`
42
42
 
43
43
  ### 파이프라인 B — 기존 이슈 구현
44
44
 
45
45
  **감지 키워드**: `#N`, `이슈 #N`, `issue #N`, `#N 구현`, `#N 작업`, `#N 해줘`, `#N 처리`
46
46
 
47
- **실행 흐름**:
48
- 전략 수립(기존 이슈 분석) → `code-agent` → `test-agent` → `commit-agent` → `pr-agent` → `review-agent` → `notify-agent`
47
+ **실행 흐름**: `code-agent` → `test-agent` → `commit-agent` → `pr-agent` → `review-agent` → `notify-agent`
49
48
 
50
49
  ### 파이프라인 C — 리뷰 관련
51
50
 
@@ -53,62 +52,93 @@ npx @elyun/bylane models
53
52
  |---|---|
54
53
  | `PR #N 리뷰`, `리뷰해줘`, `코드 리뷰`, `#N 봐줘` | `review-agent`(PR번호 전달) |
55
54
  | `리뷰 반영`, `수락`, `accept`, `LGTM 반영`, `코멘트 반영` | `respond-agent`(모드=accept) |
56
- | `반박`, `rebut`, `동의 안 해`, `이유 설명`, `왜 이렇게 했냐면` | `respond-agent`(모드=rebut) |
55
+ | `반박`, `rebut`, `동의 안 해`, `이유 설명` | `respond-agent`(모드=rebut) |
57
56
 
58
- ### 파이프라인 D — 단일 에이전트 (오케스트레이터 경유 시)
57
+ ### 파이프라인 D — 단일 에이전트
59
58
 
60
59
  | 감지 키워드 | 실행 |
61
60
  |---|---|
62
- | `커밋`, `commit`, `커밋해줘`, `변경사항 저장` | `commit-agent` |
63
- | `PR`, `풀리퀘`, `PR 만들어`, `PR 올려` | `pr-agent` |
64
- | `테스트`, `test`, `검증`, `시험`, `테스트 돌려` | `test-agent` |
65
- | `분석`, `analyze`, `구조 파악`, `코드 분석` | `analyze-agent` |
66
- | `알림`, `notify`, `슬랙`, `텔레그램`, `통보` | `notify-agent` |
67
-
68
- ### 파이프라인 판단 기준
61
+ | `커밋`, `commit` | `commit-agent` |
62
+ | `PR`, `풀리퀘`, `PR 만들어` | `pr-agent` |
63
+ | `테스트`, `test`, `검증` | `test-agent` |
64
+ | `분석`, `구조 파악` | `analyze-agent` |
65
+ | `알림`, `notify` | `notify-agent` |
69
66
 
70
- 1. 이슈 번호(`#N`)가 있으면 → **파이프라인 B**
71
- 2. 기능/변경 요청 키워드가 있고 이슈 번호 없으면 → **파이프라인 A**
72
- 3. 리뷰/대응 키워드가 있으면 → **파이프라인 C**
73
- 4. 단일 에이전트 키워드만 있으면 → **파이프라인 D**
74
- 5. 복합 키워드 ("이슈 만들고 구현까지", "테스트하고 커밋") → 파이프라인 A 또는 해당 에이전트 순차 실행
75
- 6. 판단 불가 → 사용자에게 의도 확인
67
+ ### 판단 기준
76
68
 
77
- ## 전략 수립 단계 (파이프라인 A, B 필수)
69
+ 1. 이슈 번호(`#N`) 파이프라인 B
70
+ 2. 기능/변경 키워드 + 이슈 번호 없음 → 파이프라인 A
71
+ 3. 리뷰/대응 키워드 → 파이프라인 C
72
+ 4. 단일 에이전트 키워드만 → 파이프라인 D
73
+ 5. 판단 불가 → 사용자에게 의도 확인
78
74
 
79
- 파이프라인 C, D (단일 에이전트 요청)가 아닌 경우 반드시 전략 수립 후 진행.
75
+ ---
80
76
 
81
- issue-agent를 `model` 파라미터와 함께 호출한다. issue-agent 내부에서 다음을 수행:
77
+ ## 파이프라인 실행 방식 (핵심)
82
78
 
83
- 1. 코드베이스 병렬 분석 (서브에이전트)
84
- 2. 사용자 문답
85
- 3. 이슈 분류 및 작성
79
+ 오케스트레이터는 에이전트를 **Agent 도구로 서브에이전트 호출**한다.
80
+ 서브에이전트가 완료되면 `.bylane/state/{name}.json`의 `status`만 확인하고 다음 단계로 진행한다.
86
81
 
87
- 상세 로직은 `bylane-issue-agent` 참조.
82
+ ```
83
+ [오케스트레이터]
84
+ ↓ Agent 도구 호출 (bylane-issue-agent 스킬)
85
+ [issue-agent 서브에이전트] — 내부에서 3개 병렬 분석 서브에이전트 실행
86
+ ↓ 완료 → .bylane/state/issue-agent.json 에 결과 기록
87
+ [오케스트레이터] state 확인 → issueNumber, spec 읽기
88
+ ↓ Agent 도구 호출 (bylane-code-agent 스킬)
89
+ [code-agent 서브에이전트] — 내부에서 구현/리뷰 피드백 루프 실행
90
+ ↓ 완료 → .bylane/state/code-agent.json 에 결과 기록
91
+ [오케스트레이터] state 확인 → changedFiles 읽기
92
+ ↓ ... (이하 동일)
93
+ ```
88
94
 
89
- ## 에이전트 실행 방법
95
+ ### 에이전트 호출 방법
90
96
 
91
- 각 에이전트를 순서대로 Agent 도구로 호출. 이전 출력을 다음 입력으로 전달.
92
- **config에서 읽은 모델을 `model` 파라미터로 반드시 전달.**
97
+ Agent 도구에 아래 형식으로 전달한다:
93
98
 
94
- 상태 기록 (각 에이전트 시작 전):
95
- ```bash
96
- npx @elyun/bylane state write AGENT_NAME '{"status":"in_progress","startedAt":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","progress":0,"currentTask":"TASK_DESCRIPTION","retries":0,"log":[]}'
97
99
  ```
100
+ subagent_type: "general-purpose"
101
+ model: {models 명령으로 확인한 해당 에이전트 모델}
102
+ prompt: |
103
+ 다음 bylane 스킬을 실행해줘: bylane-{에이전트명}
104
+
105
+ [컨텍스트]
106
+ - 이슈 번호: {issueNumber} (있는 경우)
107
+ - 이슈 URL: {issueUrl} (있는 경우)
108
+ - 이전 단계 출력: {이전 state 파일 핵심 필드}
109
+
110
+ 스킬 완료 후 .bylane/state/{에이전트명}.json 에 결과가 기록된다.
111
+ 결과 파일의 status, 핵심 출력 필드만 응답으로 돌려줘.
112
+ ```
113
+
114
+ ---
98
115
 
99
- ## 피드백 루프
116
+ ## 피드백 루프 (오케스트레이터 레벨)
100
117
 
101
118
  test-agent가 FAIL 반환 시:
119
+
102
120
  1. `.bylane/state/test-agent.json`에서 `failureDetails` 읽기
103
- 2. `retries < config.workflow.maxRetries` → code-agent 재실행 (실패 피드백 포함, retries+1)
121
+ 2. `retries < config.workflow.maxRetries` → **code-agent 서브에이전트 재호출** (failureDetails 포함, retries+1)
104
122
  3. `retries >= maxRetries` → notify-agent에 "개입 필요" 메시지 후 중단
105
123
 
106
- respond-agent가 "수정 필요" 반환 시 동일 로직 적용.
124
+ respond-agent가 "수정 필요" 반환 시 동일 로직.
125
+
126
+ > 재호출 시 code-agent에 전달할 추가 컨텍스트:
127
+ > `"이전 테스트 실패 내용: {failureDetails}. 해당 부분을 중점적으로 수정해줘."`
128
+
129
+ ---
107
130
 
108
131
  ## 완료 처리
109
132
 
110
- 1. 각 에이전트 state를 `status: "completed"`로 업데이트
111
- 2. notify-agent 실행하여 최종 결과 전송
133
+ 각 에이전트 완료 notify-agent 서브에이전트 호출:
134
+
135
+ ```
136
+ prompt: |
137
+ bylane-notify-agent 스킬 실행.
138
+ type: completed
139
+ summary: {전체 파이프라인 요약}
140
+ url: {PR URL}
141
+ ```
112
142
 
113
143
  ## 수동 실행
114
144
 
@@ -158,7 +158,16 @@ curl -s -X POST \
158
158
  }'
159
159
  ```
160
160
 
161
- `event` 값: 지적사항이 있으면 `"REQUEST_CHANGES"`, 없으면 `"APPROVE"`, 코멘트만이면 `"COMMENT"`
161
+ `event` 결정:
162
+
163
+ | 조건 | `review.autoApprove` | event |
164
+ |------|---------------------|-------|
165
+ | 지적사항 있음 | 무관 | `"REQUEST_CHANGES"` |
166
+ | 지적사항 없음 | `true` | `"APPROVE"` |
167
+ | 지적사항 없음 | `false` (기본) | `"COMMENT"` — 사람이 직접 Approve 판단 |
168
+ | 코멘트만 | 무관 | `"COMMENT"` |
169
+
170
+ `review.autoApprove`가 `false`(기본값)이면 AI가 Approve를 내리지 않고 코멘트만 남긴다.
162
171
 
163
172
  ### 4. 전체 요약 (PR 전체 코멘트)
164
173
 
@@ -175,7 +184,7 @@ curl -s -X POST \
175
184
  ```
176
185
 
177
186
  푸터: `review.footer`의 `{model}`을 실제 모델명으로, `{date}`를 현재 날짜로 치환.
178
- 기본 푸터: `{model} · {date}` (bylane 문구 없음)
187
+ 기본 푸터: `🤖 {model} · {date}` 모델명 앞에 🤖 이모지로 AI 리뷰임을 표시
179
188
 
180
189
  ### 5. 상태 업데이트
181
190
 
@@ -83,7 +83,16 @@ Linear API Key는 환경변수명(`LINEAR_API_KEY`)으로 저장. 실제 키값
83
83
 
84
84
  `permissions.scope`에 저장.
85
85
 
86
- ## Step 5/8Loop 실행 모드
86
+ ## Step 5/9리뷰 자동 Approve
87
+
88
+ > AI 리뷰에서 지적사항이 없을 때 자동으로 Approve할까요? (y/n, 기본: n)
89
+
90
+ - `n` (기본) → `review.autoApprove = false` — AI는 코멘트만 남기고, Approve는 사람이 직접 판단
91
+ - `y` → `review.autoApprove = true` — 지적사항 없으면 AI가 자동 Approve
92
+
93
+ 대부분의 팀에서는 사람이 최종 Approve하는 것을 권장한다.
94
+
95
+ ## Step 6/9 — Loop 실행 모드
87
96
 
88
97
  > Loop 실행 모드를 선택하세요:
89
98
  > 1. tmux — tmux 세션에서 백그라운드 실행 (터미널 종료 후에도 유지, 권장)
@@ -108,7 +117,7 @@ bylane loop stop # loop 종료
108
117
  bylane loop status # 상태 확인
109
118
  ```
110
119
 
111
- ## Step 6/8 — 고급 설정
120
+ ## Step 7/9 — 고급 설정
112
121
 
113
122
  > 고급 설정을 변경하시겠습니까? (Enter = 기본값 사용)
114
123
 
@@ -117,7 +126,7 @@ bylane loop status # 상태 확인
117
126
  - `loopTimeoutMinutes` (기본: 30): 루프 타임아웃 (분)
118
127
  - Figma MCP 활성화? (y/n, 기본: n)
119
128
 
120
- ## Step 7/8 — 브랜치 네이밍
129
+ ## Step 8/9 — 브랜치 네이밍
121
130
 
122
131
  > 브랜치 네이밍 패턴을 선택하세요:
123
132
  > 1. {tracker}-{issue-number} 예) issues-32
@@ -132,7 +141,7 @@ bylane loop status # 상태 확인
132
141
  직접 입력 시 사용 가능한 토큰 목록 안내:
133
142
  `{tracker}`, `{type}`, `{issue-number}`, `{custom-id}`, `{title-slug}`, `{date}`, `{username}`
134
143
 
135
- ## Step 8/8 — 에이전트 모델 설정
144
+ ## Step 9/9 — 에이전트 모델 설정
136
145
 
137
146
  > 각 에이전트에 사용할 AI 모델을 설정하시겠습니까? (Enter = 기본값 사용)
138
147
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elyun/bylane",
3
- "version": "1.26.0",
3
+ "version": "1.28.0",
4
4
  "description": "Frontend development harness for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -223,7 +223,7 @@ if (command === 'install') {
223
223
  }
224
224
  } else if (command === 'loop') {
225
225
  const subCmd = args[1] || 'start'
226
- const { resolveLoopMode, startTmuxLoops, stopTmuxLoops, isTmuxSessionAlive } = await import('./loop-utils.js')
226
+ const { resolveLoopMode, startTmuxLoops, stopTmuxLoops, isTmuxSessionAlive, findLoopPid } = await import('./loop-utils.js')
227
227
  const { loadConfig } = await import('./config.js')
228
228
  const config = loadConfig()
229
229
  const sessionName = config.loop?.sessionName ?? 'bylane-loops'
@@ -264,42 +264,50 @@ if (command === 'install') {
264
264
  stopped = true
265
265
  }
266
266
 
267
- // 2) process 모드 PID 종료 시도 (모드 무관)
267
+ // 2) PID 기반 종료 (state → pgrep fallback)
268
268
  const { readState, writeState } = await import('./state.js')
269
269
  for (const loopName of ['review-loop', 'respond-loop']) {
270
- const state = readState(loopName)
271
- if (!state?.pid) continue
272
- const pid = Number(state.pid)
273
- let alive = false
274
- try { process.kill(pid, 0); alive = true } catch {}
275
-
276
- if (alive) {
277
- try {
278
- process.kill(pid, 'SIGTERM')
279
- console.log(` ${loopName} (PID: ${pid}) 종료`)
280
- } catch {
281
- console.log(` ${loopName} (PID: ${pid}) 종료 실패`)
270
+ const found = findLoopPid(loopName)
271
+ if (!found) {
272
+ // state가 running이면 정리
273
+ const state = readState(loopName)
274
+ if (state?.status === 'running') {
275
+ writeState(loopName, { ...state, status: 'stopped', stoppedAt: new Date().toISOString() })
276
+ console.log(` ${loopName}: 프로세스를 찾을 수 없음 — 상태 정리`)
277
+ stopped = true
282
278
  }
283
- } else {
284
- console.log(` ${loopName} (PID: ${pid}) 이미 종료됨 — 상태 정리`)
279
+ continue
280
+ }
281
+
282
+ const { pid, source } = found
283
+ try {
284
+ process.kill(pid, 'SIGTERM')
285
+ const state = readState(loopName)
286
+ if (state) writeState(loopName, { ...state, status: 'stopped', stoppedAt: new Date().toISOString() })
287
+ console.log(` ${loopName} (PID: ${pid}, 출처: ${source}) 종료`)
288
+ stopped = true
289
+ } catch (err) {
290
+ console.log(` ${loopName} (PID: ${pid}) 종료 실패: ${err.message}`)
291
+ console.log(` 수동 종료: kill -9 ${pid}`)
285
292
  }
286
- writeState(loopName, { ...state, status: 'stopped', stoppedAt: new Date().toISOString() })
287
- stopped = true
288
293
  }
289
294
 
290
295
  if (!stopped) {
291
296
  console.log(' 실행 중인 루프가 없습니다.')
292
297
  }
293
298
  } else if (subCmd === 'status') {
294
- const alive = isTmuxSessionAlive(sessionName)
299
+ const tmuxAlive = isTmuxSessionAlive(sessionName)
295
300
  const { readState } = await import('./state.js')
296
- const reviewState = readState('review-loop')
297
- const respondState = readState('respond-loop')
298
301
  const mode = resolveLoopMode()
299
302
  console.log(`\n 모드: ${mode}`)
300
- console.log(` tmux 세션 (${sessionName}): ${alive ? '실행 중' : '없음'}`)
301
- console.log(` review-loop: ${reviewState?.status ?? 'unknown'}${reviewState?.pid ? ` (PID: ${reviewState.pid})` : ''}`)
302
- console.log(` respond-loop: ${respondState?.status ?? 'unknown'}${respondState?.pid ? ` (PID: ${respondState.pid})` : ''}\n`)
303
+ console.log(` tmux 세션 (${sessionName}): ${tmuxAlive ? '실행 중' : '없음'}`)
304
+ for (const loopName of ['review-loop', 'respond-loop']) {
305
+ const state = readState(loopName)
306
+ const found = findLoopPid(loopName)
307
+ const pidInfo = found ? `PID: ${found.pid} (${found.source}, 실행 중)` : (state?.pid ? `PID: ${state.pid} (사망)` : '프로세스 없음')
308
+ console.log(` ${loopName}: ${state?.status ?? 'unknown'} — ${pidInfo}`)
309
+ }
310
+ console.log('')
303
311
  } else {
304
312
  console.error('사용법: bylane loop <start|stop|status>')
305
313
  process.exit(1)
package/src/config.js CHANGED
@@ -67,6 +67,7 @@ export const DEFAULT_CONFIG = {
67
67
  language: 'ko',
68
68
  includeModel: true,
69
69
  includeCodeExample: true,
70
+ autoApprove: false,
70
71
  templateFile: '',
71
72
  severityEmoji: {
72
73
  CRITICAL: '[CRITICAL]',
@@ -74,7 +75,7 @@ export const DEFAULT_CONFIG = {
74
75
  MEDIUM: '[MEDIUM]',
75
76
  LOW: '[LOW]'
76
77
  },
77
- footer: 'Reviewed by byLane · model: {model}'
78
+ footer: '🤖 {model} · {date}'
78
79
  }
79
80
  }
80
81
 
package/src/loop-utils.js CHANGED
@@ -47,6 +47,42 @@ export function killExistingLoop(loopName, stateDir = '.bylane/state') {
47
47
  }
48
48
  }
49
49
 
50
+ /**
51
+ * 루프 프로세스의 PID를 반환한다.
52
+ * state 파일 → pgrep fallback 순서로 탐색.
53
+ * @param {string} loopName e.g. 'review-loop'
54
+ * @param {string} stateDir
55
+ * @returns {{ pid: number, source: 'state' | 'pgrep' } | null}
56
+ */
57
+ export function findLoopPid(loopName, stateDir = '.bylane/state') {
58
+ // 1) state 파일에서 PID 확인
59
+ const state = readState(loopName, stateDir)
60
+ if (state?.pid) {
61
+ const pid = Number(state.pid)
62
+ try {
63
+ process.kill(pid, 0)
64
+ return { pid, source: 'state' }
65
+ } catch {
66
+ // PID가 있지만 이미 죽었으면 pgrep으로 재시도
67
+ }
68
+ }
69
+
70
+ // 2) pgrep으로 프로세스명 검색
71
+ const scriptFile = `${loopName}.js`
72
+ try {
73
+ const result = execSync(`pgrep -f "${scriptFile}"`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
74
+ if (result) {
75
+ // 여러 PID가 있을 수 있음 — 첫 번째 사용
76
+ const pid = Number(result.split('\n')[0])
77
+ if (pid && pid !== process.pid) return { pid, source: 'pgrep' }
78
+ }
79
+ } catch {
80
+ // pgrep 결과 없음
81
+ }
82
+
83
+ return null
84
+ }
85
+
50
86
  /**
51
87
  * tmux 세션이 살아있는지 확인
52
88
  * @param {string} sessionName