makdoong2-team 1.3.0 → 1.4.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/README.md CHANGED
@@ -1,48 +1,127 @@
1
1
  # makdoong2-team
2
2
 
3
- > Jira 이슈를 3 phase · 8 substage로 나눠 역할별 전문 에이전트에게 위임하는 self-contained opencode 플러그인.
4
- >
5
- > **Phase**: `1_planning` (jira/requirements/scope) → `2_implementation` (dev/test) → `3_delivery` (commit/pr/review)
3
+ > Jira 이슈 하나를 **3 phase · 9 substage** 쪼개 역할별 막둥이(전문 에이전트)에게 위임하는 self-contained opencode 플러그인.
6
4
 
7
- ## 설치
5
+ ```
6
+ 1_planning jira → requirements → scope planner
7
+ ↓ worktree 자동 생성
8
+ 2_implementation analysis → dev → test analyzer, engineer
9
+ 3_delivery commit → pr → review publisher
10
+ ```
11
+
12
+ 부장님(`makdoong2-team-leader`)이 각 단계 진입을 **셸 게이트**로 검증하고, 통과한 단계만 격리된 서브세션의 막둥이에게 넘긴다. 막둥이가 끝나면 `makdoong2-verifier` 가 산출물을 2차 검증한다.
13
+
14
+ ---
8
15
 
9
- 패키지는 공개 npm registry ([`makdoong2-team`](https://www.npmjs.com/package/makdoong2-team)) 에 게시된다. 별도 registry 설정이나 인증은 필요 없다.
16
+ ## 1. 설치
17
+
18
+ 공개 npm 패키지 [`makdoong2-team`](https://www.npmjs.com/package/makdoong2-team) 이다. 별도 registry 설정이나 인증이 필요 없다.
10
19
 
11
20
  ```bash
12
- npm install -g makdoong2-team # npm 모듈 설치
13
- makdoong2-team install # opencode 배포 (agents, skills, opencode.json 패치)
21
+ npm install -g makdoong2-team # 모듈 설치
22
+ makdoong2-team install # opencode 배포
14
23
  makdoong2-team doctor # 설치 진단
15
24
  ```
16
25
 
17
- 로컬 dev 설치는 `npm pack` 후 tgz 를 `npm install -g` 한다.
26
+ | 명령 | 하는 |
27
+ |---|---|
28
+ | `install` | agents / skills / config seed 를 `~/.config/opencode/` 에 복사하고 `opencode.json` 을 패치 |
29
+ | `uninstall` | 위에서 복사한 파일 제거 |
30
+ | `doctor` | 설치 상태·설정 오류·state.json 오염 진단 |
31
+ | `validate` | `makdoong2-team.json` 의 모델 정책 위반 사전 검증 |
32
+
33
+ **요구 환경**: opencode ≥ 1.18.0, Node ≥ 20, tmux ≥ 3.0.
34
+
35
+ **설치되는 것과 안 되는 것**
36
+
37
+ - 복사됨 — `agents/*.md`, 리서치 skill, 최초 1회 `makdoong2-team.json` (재설치 시 보존, `--force` 로 덮어쓰기)
38
+ - 복사 안 됨 — `dist`, `gates`, `stages`, `scripts`. npm 모듈 안에서 직접 로드된다.
39
+ - `install` 은 `~/.cache/opencode/packages/makdoong2-team@latest/` 를 전역 모듈로 심볼릭 링크한다. opencode 가 registry 에서 다시 받지 않고 방금 설치한 버전을 쓰게 만드는 장치다.
40
+
41
+ 로컬 개발 설치는 `npm pack` 후 tgz 를 `npm install -g` 한다.
42
+
43
+ ---
44
+
45
+ ## 2. 에이전트와 단계
46
+
47
+ 에이전트 **7개**. 권한이 좁을수록 사고 반경이 좁다는 원칙으로 나눴다.
48
+
49
+ | 에이전트 | 담당 | 권한 |
50
+ |---|---|---|
51
+ | `makdoong2-team-leader` | 라우팅 (부장님) | **git 전면 deny**, 파일 편집 불가 — 위임만 |
52
+ | `makdoong2-planner` | 1_planning 3단계 | 읽기 전용 + 리서치 MCP |
53
+ | `makdoong2-analyzer` | 2_implementation.analysis | 읽기 + 분석 산출물 write |
54
+ | `makdoong2-engineer` | dev / test | edit·write 허용, commit/push deny |
55
+ | `makdoong2-publisher` | 3_delivery 3단계 | **git add/commit/push 직접 실행** + bitbucket MCP |
56
+ | `makdoong2-verifier` | 메타 검증 | 읽기 전용 |
57
+ | `makdoong2-researcher` | 리서치 fan-out 워커 | 읽기 전용 + 리서치 MCP — 소스 1개 전담 |
58
+
59
+ > **Publisher 는 직접 실행자다.** commit·pr·review 모두 publisher 가 worktree 에서 직접 git 명령과 MCP 를 호출한다. 부장님은 git 권한이 아예 없고 dispatch 와 verdict 수신만 한다. (구버전의 "spec 계산 → 부장님 실행" 하이브리드 모델은 폐기됐다.)
60
+
61
+ ### Substage → agent → spec 매핑
62
+
63
+ | Substage | Agent | Stage spec | 게이트 |
64
+ |---|---|---|---|
65
+ | `1_planning.jira` | planner | `stages/01-planning.md` | `verify.sh` |
66
+ | `1_planning.requirements` | planner | `stages/02-requirements.md` | `stage2-requirements-verify.sh` |
67
+ | `1_planning.scope` | planner | `stages/03-scope.md` | `stage3-scope-verify.sh` |
68
+ | `2_implementation.analysis` | analyzer | `stages/04-analysis.md` | `stage-analysis-verify.sh` |
69
+ | `2_implementation.dev` | engineer | `stages/05-worktree-dev.md` | `stage4-dev-verify.sh` |
70
+ | `2_implementation.test` | engineer | `stages/06-test.md` | `stage5-test-verify.sh` + coverage |
71
+ | `3_delivery.commit` | publisher | `stages/07-commit.md` | `stage6-commit-verify.sh` |
72
+ | `3_delivery.pr` | publisher | `stages/08-pr.md` | `stage7-pr-verify.sh` |
73
+ | `3_delivery.review` | publisher | `stages/09-review-comments.md` | `stage8-review-verify.sh` |
74
+
75
+ 매핑 원본은 `src/agent-stage-config.ts` (`STAGE_SPEC_FILES`, `agentForStage()`).
76
+
77
+ **통합 Planning**: `1_planning.jira` 를 dispatch 하면 `01-planning.md` 명세에 따라 **한 planner 세션이 jira → requirements → scope 를 연속 처리**한다. requirements / scope 단독 dispatch 는 planner 가 중간에 실패했을 때의 폴백 경로다.
78
+
79
+ ### 작업 범주화
80
+
81
+ `1_planning.requirements` 가 작업을 `.policy.category` (`minor` | `major`) 로 분류한다. `1_planning.scope` 는 minor → major 상향만 허용한다.
82
+
83
+ **두 범주 모두 기본은 무인 진행**이다. 실제 승인 여부는 `.policy.auto_approve.<substage>` 마커가 결정하고 기본값이 전 substage `true` 이기 때문이다. `category` 는 위험도 라벨이자 향후 opt-in 훅의 스위치로 남겨둔 값이다. HITL 이 필요하면 planner 가 특정 substage 를 `false` 로 내리고, 그때만 `change-report.md` + 사용자 승인이 요구된다.
84
+
85
+ 게이트는 이 마커를 **결정론적으로만** 검사한다 (LLM 호출 0).
18
86
 
19
- - `install` agents / skills / config seed 만 `~/.config/opencode/` 에 복사한다. 런타임 자산 (dist, gates, stages, scripts) 은 npm 모듈 내부에서 로드된다.
20
- - `install` 은 `~/.cache/opencode/packages/makdoong2-team@latest/` 를 전역 모듈 심볼릭 링크로 seed 한다. opencode 가 registry 에서 별도로 fetch 하지 않고 방금 설치한 버전을 그대로 로드하게 만든다.
21
- - 재설치 시 `makdoong2-team.json` 은 보존된다. 덮어쓰려면 `--force`.
87
+ ### 다출처 병렬 조사
22
88
 
23
- ## 설정 `makdoong2-team.json` 파일
89
+ `1_planning.requirements` 교차 조사는 `dispatch_research` **1회 호출**로 Jira · Confluence · Bitbucket (필요 시 GitHub OSS) 을 **동시에** 조사한다. 플러그인이 소스마다 별도 세션을 띄우므로:
24
90
 
25
- 모든 설정은 `~/.config/opencode/makdoong2-team.json` 하나로 제어한다. 플러그인 (`src/config.ts`) 게이트 (`scripts/config.sh`) 같은 파일을 읽는다. 환경변수는 사용하지 않는다.
91
+ - 대기 시간이 **가장 느린 소스 하나**로 수렴한다 (직렬 합이 아니다)
92
+ - 각 소스의 원자료가 planner 컨텍스트를 잠식하지 않는다
93
+ - 한 소스가 실패해도 나머지 결과는 그대로 남는다 (부분 성공이 정상)
94
+
95
+ 결과는 `.makdoong2-team/<이슈>/research-findings.json` 으로 병합된다. 상세: ARCHITECTURE.md §3.6
96
+
97
+ ---
98
+
99
+ ## 3. 설정 — `makdoong2-team.json` 한 파일
100
+
101
+ 모든 설정은 `~/.config/opencode/makdoong2-team.json` 하나로 제어한다. 플러그인(`src/config.ts`)과 셸 게이트(`scripts/config.sh`)가 같은 파일을 읽는다. **환경변수는 쓰지 않는다.**
26
102
 
27
103
  | 블록 | 용도 |
28
104
  |---|---|
29
105
  | `agents` | 에이전트별 모델 오버라이드 (`model`, `variant`, `fallback_models`) |
30
106
  | `model_policy.allowed_primaries` | 빌트인 primary 허용 목록 **확장** (추가 전용 — 빈 배열은 "제한 없음" 이 아니라 "추가 없음") |
31
- | `coverage.threshold` | 커버리지 게이트 최소 (%, 기본 95) |
32
- | `timeout.stall_escalate_threshold` | substage 누적 hang 상한 (기본 5). 초과 시 dispatch_stage 차단 후 사용자 에스컬레이션 |
33
- | `tmux` | 서브세션 pane 모니터 (기본 비활성). `placement` 배치 방식 선택 — 아래 참조 |
107
+ | `coverage.threshold` | 커버리지 게이트 최소치 (%, 기본 95) |
108
+ | `timeout.substage_minutes` | 서브에이전트 1회 실행 상한 (기본 30분) |
109
+ | `timeout.per_agent` | 에이전트별 상한 override (기본 seed: engineer 60분) |
110
+ | `timeout.stall_escalate_threshold` | substage 누적 hang 상한 (기본 5). 초과 시 dispatch 차단 후 사용자 에스컬레이션 |
111
+ | `research.max_parallel` | 동시 리서치 세션 수 (기본 3, 상한 6) |
112
+ | `research.timeout_minutes` | 리서치 소스 1개당 상한 (기본 10분) |
113
+ | `tmux` | 막둥이 pane 모니터. 코드 기본값은 off, seed 되는 설정 파일은 `enabled: true` |
34
114
  | `worktree.extra_exclude` | worktree 동기화 추가 제외 패턴 |
35
- | `logging.level` | 플러그인 콘솔 로그 레벨 (`silent`/`error`/`warn`/`info`/`debug`/`trace`, 기본 `error`) |
36
- | `logging.max_bytes` | `mode="file"` 로그 회전 임계값 (기본 10 MiB). 초과 시 `<path>.1` 로 회전 |
115
+ | `logging` | `level` / `mode` / `path` / `max_bytes` |
37
116
  | `paths` | 비표준 설치 경로 오버라이드 |
38
- | `hosts` | 리서치 skill MCP 온프레미스 endpoint (`JIRA_HOST`, `CONFLUENCE_HOST`, `BITBUCKET_API_BASE_PATH`, `BAMBOO_URL`) |
39
- | `secrets` | 리서치 skill MCP 토큰 (Bitbucket/JIRA/Confluence/Bamboo) |
117
+ | `hosts` | 리서치 MCP 온프레미스 endpoint (`JIRA_HOST`, `CONFLUENCE_HOST`, `BITBUCKET_API_BASE_PATH`, `BAMBOO_URL`) |
118
+ | `secrets` | 리서치 MCP 토큰 (Jira / Confluence / Bitbucket / Bamboo) |
40
119
 
41
- 로그 레벨은 임계값 기반이다. `error` 는 error 만, `debug` 는 error/warn/info/debug 모두 출력. 기본값 `error` 는 BLOCKED 등 중요 이벤트만 노출하고 orphan-scan 같은 정보성 로그는 억제한다. 로그 레벨 변경 후에는 opencode 를 재시작해야 반영된다 (config 는 플러그인 초기화 시 한 번만 로드된다).
120
+ 전체 스키마는 `assets/makdoong2-team.schema.json`.
42
121
 
43
- `mode="file"` 로그는 **append 전용**이다. 한 호스트의 모든 opencode 프로세스 (메인 TUI, 막둥이 pane, `npm test`) 가 같은 파일을 공유하므로 truncate 하면 다른 프로세스의 기록이 사라진다. 프로세스 구분은 각 라인의 `[pid=N]` 태그로 하고, 크기는 `max_bytes` 회전으로 제한한다. 상세: ARCHITECTURE.md §14.
122
+ ### 모델 정책
44
123
 
45
- 빌트인 primary 허용 목록은 `local/*` 와 `github-copilot/*` 브랜드 (claude-haiku/opus/sonnet, gemini, gpt, grok-code, kimi, mai-code, qwen 계열 총 40개) 이다. 전체 목록은 `src/model-fallback-policy.ts` 의 `DEFAULT_ALLOWED_PRIMARIES` 참조. fallback tier 항상 primary 보다 strictly lower 여야 한다 (`low < medium < high < max`).
124
+ 빌트인 primary 허용 목록은 `github-copilot/*` 와 `local/*` 브랜드 40개다 (전체 목록: `src/model-fallback-policy.ts` 의 `DEFAULT_ALLOWED_PRIMARIES`). fallback **항상 primary 보다 낮은 tier** 여야 한다 (`low < medium < high < max`).
46
125
 
47
126
  ```jsonc
48
127
  {
@@ -51,7 +130,7 @@ makdoong2-team doctor # 설치 진단
51
130
  },
52
131
  "agents": {
53
132
  "makdoong2-engineer": {
54
- "model": "anthropic/claude-opus-4-7",
133
+ "model": "github-copilot/claude-opus-4.8",
55
134
  "fallback_models": [
56
135
  { "id": "github-copilot/claude-haiku-4.5", "tier": "low" }
57
136
  ]
@@ -60,133 +139,99 @@ makdoong2-team doctor # 설치 진단
60
139
  }
61
140
  ```
62
141
 
63
- ### `tmux.placement`막둥이 배치 방식
64
-
65
- | 값 | 동작 | 부장님 pane 리사이즈 |
66
- |---|---|---|
67
- | `window` (기본) | 막둥이마다 **별도 tmux window** (`new-window -d`) | 없음 |
68
- | `pane` | 부장님 window 를 분할 (`split-window` + `select-layout`) | 매 spawn/kill 마다 발생 |
69
-
70
- `pane` 모드는 substage 마다 부장님 화면이 분할·재배치되어 흔들린다 (실측 170x44 → 80x44 → 170x44). `window` 모드는 detached window 를 쓰므로 부장님 pane 크기와 포커스가 전혀 변하지 않는다. 기본값을 `window` 로 둔 이유다.
142
+ `makdoong2-team validate` 로 위반 여부와 최종 체인을 미리 확인할 수 있다. 위반 시 플러그인은 defaults 로 롤백하고 stderr 에 경고만 남긴다 설정 오류로 워크플로우가 죽지 않는다.
71
143
 
72
- `layout` · `main_pane_size` · `agent_pane_min_width` · `split_direction` 은 `placement: "pane"` 일 때만 의미가 있다.
144
+ ### 로깅
73
145
 
74
- ### 막둥이 창은 포커스할 붙는다 (지연 attach)
75
-
76
- 막둥이 pane 은 spawn 직후에는 배너만 띄운 placeholder 상태이고, **해당 창을 선택하는 순간** `opencode attach` 로 자동 전환된다. 한 번 전환되면 다른 창으로 돌아가도 attach 상태가 유지되므로 live 관찰에 제약은 없다.
77
-
78
- 프롬프트 입력창에 `/0c0c/0c0c/0c0c…` 가 타이핑되던 현상의 원인은 **spawn 되는 자식 opencode TUI 가 기동 시 보내는 터미널 팔레트 질의**다 (프로세스당 19개). tmux 가 이를 실제 터미널로 중계하는데, 응답이 조각나면 남은 조각이 활성 pane(= 부장님)에 키 입력으로 배달된다. 포커스 전까지 자식 프로세스를 아예 만들지 않아 이 경로를 차단한다 — 실측상 누출 0건. 즉시 attach 하는 모드는 제공하지 않는다 (동일 현상이 재현됨). oh-my-opencode 의 `tmux-core` placeholder → `respawn-pane -k` 설계를 따랐다. 상세: ARCHITECTURE.md §17.7.
79
-
80
- `makdoong2-team validate` 로 정책 위반 여부와 최종 chain 을 사전 검증할 수 있다. 위반 시 plugin 은 defaults 로 롤백되며 stderr 에 경고를 남긴다. 전체 스키마: `assets/makdoong2-team.schema.json`.
81
-
82
- ## 에이전트 5개
83
-
84
- | ID | 담당 phase | 권한 요약 |
85
- |---|---|---|
86
- | `makdoong2-team-leader` | 라우팅 (orchestrator) | commit/push 허용 — PRIMARY 단계 직접 실행 |
87
- | `makdoong2-planner` | 1_planning | 읽기 전용 |
88
- | `makdoong2-engineer` | 2_implementation | edit/write 허용, commit/push deny |
89
- | `makdoong2-publisher` | 3_delivery | 읽기 전용 — spec 계산만 (하이브리드) |
90
- | `makdoong2-verifier` | 메타 검증 | 읽기 전용 |
146
+ `level` 임계값이다. `error` error 만, `debug` 는 error/warn/info/debug 까지 출력한다. 기본값 `error` 는 BLOCKED 같은 중요 이벤트만 노출한다. **변경 후 opencode 재시작이 필요하다** (설정은 플러그인 초기화 시 1회만 로드된다).
91
147
 
92
- **Publisher 하이브리드**: `3_delivery.commit`/`3_delivery.pr` publisher spec (commit 메시지, PR 본문) 계산·반환하고, team-leader spec 받아 실제 git 명령을 실행한다. `3_delivery.review` publisher bitbucket MCP 직접 실행.
148
+ `mode="file"` 로그는 **append 전용**이다. 호스트의 모든 opencode 프로세스(메인 TUI, 막둥이 pane, `npm test`) 같은 파일을 공유하므로 truncate 하면 남의 기록이 사라진다. 프로세스 구분은 라인의 `[pid=N]` 태그로, 크기는 `max_bytes` 회전으로 관리한다. ARCHITECTURE.md §11
93
149
 
94
- ## 작업 범주화 & 자동 승인
150
+ ### tmux 막둥이
95
151
 
96
- `1_planning.requirements` substage 가 작업을 `.policy.category` 분류한다 (`minor` | `major`). `1_planning.scope` minor major 상향만 허용한다.
152
+ - `placement: "window"` (기본) 막둥이마다 별도 detached window. 부장님 화면이 **전혀 흔들리지 않는다.**
153
+ - `placement: "pane"` (legacy) — 부장님 window 를 분할. spawn/kill 마다 리사이즈가 발생한다. `layout` · `main_pane_size` · `agent_pane_min_width` · `split_direction` 은 이 모드에서만 유효하다.
97
154
 
98
- - **minor** substage 무인 자동 진행
99
- - **major** — 테스트까지 무인 진행 후 `3_delivery.commit` 직전 1곳만 사람 승인 + `change-report.md` 필수
155
+ 막둥이 창은 **포커스할 때 실제 TUI 로 붙는다.** spawn 직후에는 배너만 띄운 placeholder 이고, 그 창을 선택하는 순간 `opencode attach` 로 교체된다. 한 번 붙으면 계속 유지되므로 관찰에 제약은 없다. 즉시 attach 하는 모드는 제공하지 않는다 부장님 프롬프트에 `/0c0c/0c0c…` 타이핑되는 터미널 팔레트 질의 누출을 재현하기 때문이다. → ARCHITECTURE.md §9
100
156
 
101
- 셸 게이트가 `.policy` 마커를 결정론적으로 검사한다 (LLM 호출 0).
157
+ ---
102
158
 
103
- ## 단계별 시스템 프롬프트 확장
159
+ ## 4. 단계별 프롬프트 확장
104
160
 
105
- 단계(substage)에 시스템 프롬프트를 추가하려면 **어느 계층**에 넣을지 먼저 정한다. `dispatch_stage` 는 세 계층을 조합해 서브에이전트에 주입한다.
161
+ 프롬프트는 계층으로 조립되어 서브에이전트에 주입된다. **어느 계층에 쓸지부터 정한다.**
106
162
 
107
- | 계층 | 파일 | 범위 | 재빌드 |
163
+ | 계층 | 파일 | 적용 범위 | 재빌드 |
108
164
  |---|---|---|---|
109
- | ① Agent persona | `agents/makdoong2-<role>.md` | 해당 role 담당하는 **모든 substage 공통** | 불필요 (마크다운만) |
110
- | ② Stage spec | `stages/NN-<name>.md` | **단일 substage 전용** 절차·게이트·마커 | 불필요 (마크다운만) |
111
- | ③ Dispatch header | `src/opencode-plugin.ts` `promptText` | **모든 stage 공통** 헤더 라인 | 필요 (`npm run build` + republish) |
165
+ | ① Agent persona | `agents/makdoong2-<role>.md` | role **모든 substage** | 불필요 |
166
+ | ② Stage spec | `stages/NN-<name>.md` | **단일 substage** 절차·게이트·마커 | 불필요 |
167
+ | ③ Dispatch header | `src/opencode-plugin.ts` `promptText` | **모든 stage** 공통 헤더 | 필요 |
112
168
 
113
- ### Phase Agent Stage spec 매핑
169
+ - **① 페르소나** 역할, 권한 요약, 금지사항, 공통 절차. YAML frontmatter(`tools`, `permission`) 아래 본문이 시스템 프롬프트가 된다.
170
+ - **② Stage spec** — 그 substage 전용 절차와 state.json 마커 예시. dispatch 프롬프트에 `Stage spec: read <경로> and follow it strictly.` 로 참조 지시가 자동 삽입된다.
171
+ - **③ Dispatch header** — `Working directory` / `Scripts directory` / `Issue` 같은 전역 강제 라인. 거의 건드릴 일이 없고, 고치면 `npm run build` + 재배포가 필요하다.
114
172
 
115
- | Substage | Agent (① persona) | Stage spec (② 절차) |
116
- |---|---|---|
117
- | `1_planning.jira` | `makdoong2-planner` | `stages/01-jira.md` |
118
- | `1_planning.requirements` | `makdoong2-planner` | `stages/02-requirements.md` |
119
- | `1_planning.scope` | `makdoong2-planner` | `stages/03-scope.md` |
120
- | `2_implementation.analysis` | `makdoong2-analyzer` | `stages/04-analysis.md` |
121
- | `2_implementation.dev` | `makdoong2-engineer` | `stages/05-worktree-dev.md` |
122
- | `2_implementation.test` | `makdoong2-engineer` | `stages/06-test.md` |
123
- | `3_delivery.commit` | `makdoong2-publisher` | `stages/07-commit.md` |
124
- | `3_delivery.pr` | `makdoong2-publisher` | `stages/08-pr.md` |
125
- | `3_delivery.review` | `makdoong2-publisher` | `stages/09-review-comments.md` |
173
+ **반영 방법**: ①·② 고쳤으면 `makdoong2-team install --force` 재배포하면 끝. ③ 또는 `src/**` 를 고쳤으면 빌드 후 배포 절차를 밟는다.
126
174
 
127
- 매핑 원본: `src/agent-stage-config.ts` (`STAGE_SPEC_FILES` + `agentForStage()`).
175
+ ### substage 추가 (드문 경우)
128
176
 
129
- ### 어디에 무엇을 쓰는가
177
+ 기존 substage 내용을 **추가**만 하려면 ① 또는 ② 편집으로 끝난다. **새 substage 자체**를 만들려면 5곳을 함께 고친다.
130
178
 
131
- - **① Agent persona (`agents/*.md`)**페르소나, 권한 요약, 금지사항, 공통 절차. 같은 role 의 substage 여러 개에 걸치는 규칙. YAML frontmatter (`tools`, `permission`) 아래 본문이 시스템 프롬프트로 주입된다.
132
- - **② Stage spec (`stages/*.md`)**해당 substage 전용 단계별 절차, 게이트 조건, state.json 마커 예시. `dispatch_stage` 프롬프트에 `Stage spec: read <경로> and follow it strictly.` 로 참조 지시가 자동 삽입된다.
133
- - **③ Dispatch header (`src/opencode-plugin.ts`)**모든 stage 에 공통으로 강제할 헤더 (예: `Working directory`, `Scripts directory`, `Issue`). 거의 건드릴 일 없음. 수정 시 `npm run build` + 재배포 필요.
179
+ 1. `src/agent-stage-config.ts` — `Stage` union + `STAGE_SPEC_FILES` + 필요 `agentForStage()`
180
+ 2. `src/opencode-plugin.ts` — `STAGE_ORDER` 배열에 삽입
181
+ 3. `stages/NN-<name>.md` — 신규 spec 작성
182
+ 4. `agents/makdoong2-<role>.md` + `gates/verify.sh` (+ 전용 `stage*-verify.sh`)
183
+ 5. README 매핑표 · `AGENTS.md` sealed workflow 규약 갱신
134
184
 
135
- ### substage 추가 절차 (드문 경우)
185
+ 이후 `makdoong2-team validate` `npm test` 로 회귀를 확인한다.
136
186
 
137
- 기존 substage 에 프롬프트를 **추가**만 하려면 위 ① 또는 ② 를 편집하면 끝난다. **새 substage 자체를 추가**하려면 아래 5곳을 함께 수정한다.
187
+ ---
138
188
 
139
- 1. `src/agent-stage-config.ts` — `Stage` union 에 신규 키 추가 + `STAGE_SPEC_FILES` 에 spec 파일 경로 매핑 + 필요 시 `agentForStage()` 라우팅 확장.
140
- 2. `stages/NN-<name>.md` — 신규 stage spec 파일 생성 (절차, 게이트 조건, 마커 예시).
141
- 3. `agents/makdoong2-<role>.md` — 담당 agent 페르소나에 신규 substage 처리 로직 추가.
142
- 4. `gates/verify.sh` — 진입 게이트 검증 로직 추가 (state.json 마커 조건).
143
- 5. `README.md` 매핑표 갱신 + `AGENTS.md` sealed workflow 규약 반영.
189
+ ## 5. 테스트
144
190
 
145
- `makdoong2-team validate` 로 정책 위반 여부를 사전 검증한 뒤 `npm test` 로 게이트 정책 회귀 확인.
191
+ ```bash
192
+ npm test # 전체
193
+ npm run test:install # 설치 라이브러리만
194
+ ```
146
195
 
147
- ### 편집반영
196
+ `npm test` 는 각 단계를 순차 실행하되 **실패해도 멈추지 않고** 끝까지 돌린 뒤 실패 목록을 모아 보고한다. macOS 등 비-Linux 호스트에서는 같은 스위트를 Ubuntu 컨테이너에서 한 번 더 돌려 플랫폼 차이로 갈리는 회귀까지 잡는다 (docker 가 없으면 안내 건너뛴다).
148
197
 
149
- - · 수정 재빌드 불필요. `makdoong2-team install --force` `~/.config/opencode/agents/` 에 재배포.
150
- - ③ 또는 `src/**` 수정 → `npm run build` 후 배포 절차(아래 "배포" 섹션) 진행.
198
+ `.husky/pre-push` 같은 `npm test` 부르므로 push 마다 docker 기동됐다 종료된다. 건너뛰려면:
199
+
200
+ ```bash
201
+ MAKDOONG2_SKIP_LINUX_CHECK=1 git push # 그 push 는 Linux 검증 없이 나간다
202
+ ```
151
203
 
152
- 상세 규약(sealed workflow, skill_mcp lazy-load, state.sh 하드룰)은 `AGENTS.md` 참조.
204
+ ---
153
205
 
154
- ## 배포 (Maintainer)
206
+ ## 6. 배포 (Maintainer)
155
207
 
156
- 가지 경로 모두 **승인 게이트 2회**를 통과해야 배포된다.
208
+ 두 경로 모두 **승인 게이트 2회**를 통과해야 배포된다.
157
209
 
158
- ### 경로 A — `npm run release:*` (권장)
210
+ **경로 A — `npm run release:*` (권장)**
159
211
 
160
212
  ```bash
161
- npm run release:patch # 0.2.30.2.4 (버그 수정)
162
- npm run release:minor # 0.2.30.3.0 (기능 추가)
163
- npm run release:major # 0.2.31.0.0 (breaking)
213
+ npm run release:patch # 1.3.11.3.2 (버그 수정)
214
+ npm run release:minor # 1.3.11.4.0 (기능 추가)
215
+ npm run release:major # 1.3.12.0.0 (breaking)
164
216
  ```
165
217
 
166
- `scripts/release.sh` 가 9단계를 순차 실행한다 — pre-flight → `npm test` → 버전 미리보기 → **승인 #1** → `npm version` → `publish --dry-run` → **승인 #2** → `npm publish` → `git push --follow-tags`. Publish 이전 실패 자동 롤백. CI 에서는 `--yes` 로 대화형 우회 (대화형 셸에선 금지).
218
+ `scripts/release.sh` 가 9단계를 순차 실행한다 — pre-flight → `npm test` → 버전 미리보기 → **승인 #1** → `npm version` → `publish --dry-run` → **승인 #2** → `npm publish` → `git push --follow-tags`. publish 이전에 실패하면 자동 롤백된다. CI `--yes` 로 대화형을 우회한다 (대화형 셸에서는 금지).
167
219
 
168
- ### 경로 B — git push 자동 배포
220
+ **경로 B — git push 자동 배포**
169
221
 
170
- `package.json` 의 `version` 을 수동으로 올린 커밋을 push 하면 `.husky/pre-push` 훅이 감지해 승인 게이트 2회 후 자동 publish 한다. 이미 npm registry 에 있는 버전은 skip.
222
+ `package.json` 의 `version` 을 올린 커밋을 push 하면 `.husky/pre-push` 감지해 같은 2회 승인 후 publish 한다. 이미 registry 에 있는 버전은 skip.
171
223
 
172
- ### npm 인증
224
+ **인증**: `npm login` 또는 `~/.npmrc` 의 `//registry.npmjs.org/:_authToken=<token>` (chmod 600). 설치하는 쪽은 인증이 필요 없다.
173
225
 
174
- 공개 registry 배포이므로 `npm login` (또는 `~/.npmrc` 의 `//registry.npmjs.org/:_authToken=<token>`) 만 있으면 된다. 배포 권한은 `makdoong2-team` 패키지 owner 계정에 한정된다.
226
+ ---
175
227
 
176
- ## Testing
228
+ ## 7. 문서
177
229
 
178
- ```bash
179
- npm test # build + smoke + gate policy + install-lib
180
- npm run test:install # 설치 라이브러리 테스트
181
- ```
182
-
183
- `.husky/pre-push` 가 unit test 실행 + version 변경 감지 시 자동 배포 훅을 트리거한다.
184
-
185
- ## 문서
186
-
187
- - **[ARCHITECTURE.md](./ARCHITECTURE.md)** — 어떻게 동작하는가. 모듈, hook 흐름, custom tool API, state schema, 실패 모드.
188
- - **[DESIGN.md](./DESIGN.md)** — 왜 이렇게 설계했는가. 하네스 4기둥 (Constrain / Inform / Verify / Correct).
189
- - **[AGENTS.md](./AGENTS.md)** — 개발 규약 (git commit, npm 배포, sealed workflow).
230
+ | 문서 | 내용 |
231
+ |---|---|
232
+ | [ARCHITECTURE.md](./ARCHITECTURE.md) | **어떻게 동작하는가** — 모듈, 툴 API, state 스키마, 훅, 런타임 방어, 실패 모드 |
233
+ | [DESIGN.md](./DESIGN.md) | **왜 이렇게 만들었는가** — 하네스 4기둥 (Constrain / Inform / Verify / Correct) 과 트레이드오프 |
234
+ | [AGENTS.md](./AGENTS.md) | **개발 규약** — git commit, npm 배포, sealed workflow, state.sh 하드룰 |
190
235
 
191
236
  ## 라이선스
192
237
 
@@ -10,6 +10,7 @@ tools:
10
10
  Glob: true
11
11
  skill: true
12
12
  skill_mcp: true
13
+ dispatch_research: true
13
14
  permission:
14
15
  bash:
15
16
  "*": "allow"
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: makdoong2-researcher
3
+ description: workflow research fan-out worker — 단일 소스(Jira / Confluence / Bitbucket / GitHub OSS) 만 읽기 전용 조사하고 고정 스키마 JSON 을 반환한다. dispatch_research 툴이 소스별로 병렬 spawn 한다. 직접 호출하지 않는다.
4
+ temperature: 0.1
5
+ mode: subagent
6
+ tools:
7
+ Read: true
8
+ Bash: true
9
+ Grep: true
10
+ Glob: true
11
+ skill: true
12
+ skill_mcp: true
13
+ Write: false
14
+ Edit: false
15
+ Patch: false
16
+ MultiEdit: false
17
+ permission:
18
+ bash:
19
+ "*": "allow"
20
+ "git commit*": "deny"
21
+ "git push*": "deny"
22
+ "git add*": "deny"
23
+ "git rm*": "deny"
24
+ "git reset --hard*": "deny"
25
+ "git branch -D*": "deny"
26
+ "git worktree add*": "deny"
27
+ "git worktree remove*": "deny"
28
+ "rm -rf*": "deny"
29
+ write:
30
+ "**/*": "deny"
31
+ ---
32
+
33
+ 당신은 **리서치 막둥이**다. 배정받은 **소스 한 곳만** 조사하고 고정 스키마 JSON 을 반환한다.
34
+
35
+ `dispatch_research` 툴이 소스마다 별도 세션을 병렬로 띄운다. 당신의 세션에는 당신이 맡은 소스의 자료만 쌓인다 — 이것이 fan-out 의 목적이므로 **다른 소스를 기웃거리지 않는다.**
36
+
37
+ ## 하드룰
38
+
39
+ 1. **읽기 전용.** 파일 생성·수정 불가 (Write/Edit 프론트매터 차단). state.json 도 건드리지 않는다 — 결과 저장은 플러그인이 한다.
40
+ 2. **배정된 소스만.** 프롬프트의 `Research source` 에 적힌 소스 외의 skill 을 로드하지 않는다.
41
+ 3. **skill 먼저, MCP 나중.** `skill(name=...)` 로 스킬을 로드하기 전에 `skill_mcp` 를 부르면 `MCP server "<name>" not found` 로 실패한다. 순서를 지킨다.
42
+ 4. **추측 금지.** 확인하지 못한 것은 `findings` 에 넣지 않고 `gaps` 에 적는다. `url` 은 실제 출처가 있을 때만 넣는다.
43
+ 5. **outer-world 에이전트 위임 금지.** Task 툴이 프론트매터에서 제거되어 물리적으로 불가하다.
44
+
45
+ ## 실행 규약
46
+
47
+ bash 명령은 **실행 후 결과로 판단**한다. 실행 전 permission 을 추론하지 않는다. `[makdoong2-team hook] BLOCKED:` stderr 로그가 나온 것만 실제 차단이다.
48
+
49
+ ## 출력 규약
50
+
51
+ 마지막 assistant turn 에 ```json 펜스 블록을 **정확히 하나** 출력한다. 스키마는 dispatch 프롬프트에 명시되어 있다:
52
+
53
+ ```json
54
+ {
55
+ "source": "<배정받은 source>",
56
+ "findings": [{"title": "...", "detail": "...", "url": "... 또는 null"}],
57
+ "gaps": ["확인하지 못한 항목"]
58
+ }
59
+ ```
60
+
61
+ - 조사 결과가 없어도 **JSON 블록은 반드시 출력한다.** `findings: []` + `gaps` 에 이유를 적는다.
62
+ - 블록을 출력하지 않으면 플러그인이 파싱 실패로 기록하고 해당 소스는 `status: "failed"` 가 된다.
63
+ - 펜스 블록 앞뒤의 한국어 설명은 자유다. 단 JSON 블록은 하나여야 한다 (여러 개면 마지막 것이 채택된다).
64
+
65
+ ## 조기 종료
66
+
67
+ MCP 인증 실패(exit 68/69), 접근 권한 부족, 대상 부재 등으로 조사가 불가능하면 **재시도로 시간을 쓰지 말고** 즉시 `findings: []` + `gaps` 에 사유를 적어 반환한다. 한 소스의 실패는 다른 소스의 조사를 막지 않는다 — 플러그인이 부분 성공으로 병합한다.
@@ -10,6 +10,10 @@
10
10
  "makdoong2-engineer": 60
11
11
  }
12
12
  },
13
+ "research": {
14
+ "max_parallel": 3,
15
+ "timeout_minutes": 10
16
+ },
13
17
  "tmux": {
14
18
  "enabled": true,
15
19
  "placement": "window",
@@ -352,6 +352,24 @@
352
352
  "description": "Bamboo personal-access token consumed by skills/bamboo-ci/run-bamboo.sh."
353
353
  }
354
354
  }
355
+ },
356
+ "research": {
357
+ "type": "object",
358
+ "additionalProperties": false,
359
+ "description": "Parallel multi-source research fan-out (dispatch_research tool).",
360
+ "properties": {
361
+ "max_parallel": {
362
+ "type": "integer",
363
+ "minimum": 1,
364
+ "maximum": 6,
365
+ "description": "Maximum research sub-sessions spawned simultaneously. Default 3, hard ceiling 6. Queries beyond this are reported as deferred, never silently dropped."
366
+ },
367
+ "timeout_minutes": {
368
+ "type": "number",
369
+ "minimum": 1,
370
+ "description": "Per-source wall-clock budget. Default 10. Deliberately shorter than timeout.substage_minutes: a source that cannot answer in this window is recorded as failed so the other sources' findings still land."
371
+ }
372
+ }
355
373
  }
356
374
  }
357
375
  }
package/bin/cli.js CHANGED
@@ -31,7 +31,7 @@ const HERE = dirname(fileURLToPath(import.meta.url));
31
31
  const PKG_ROOT = resolve(HERE, "..");
32
32
  const PKG = JSON.parse(readFileSync(join(PKG_ROOT, "package.json"), "utf8"));
33
33
 
34
- const TOOLS = ["verify_stage", "dispatch_stage", "dispatch_verifier", "auto_advance_stage", "get_fallback_model"];
34
+ const TOOLS = ["verify_stage", "dispatch_stage", "dispatch_verifier", "dispatch_research", "auto_advance_stage", "get_fallback_model"];
35
35
 
36
36
  // Skill directories that used to ship a per-skill secrets.env under
37
37
  // ${configDir}/skills/<skill>/. Credentials are now sourced only from
@@ -70,6 +70,16 @@ export const AGENTS = {
70
70
  tools: ["bash", "read", "grep", "glob", "write"],
71
71
  skills: [],
72
72
  },
73
+ // Research fan-out worker. Not bound to a substage — dispatch_research spawns
74
+ // one session per source in parallel, each loading exactly one research skill.
75
+ "makdoong2-researcher": {
76
+ id: "makdoong2-researcher",
77
+ stage: "all",
78
+ primary_only: false,
79
+ permissions: RO_PERM,
80
+ tools: ["bash", "read", "grep", "glob", "skill", "skill_mcp"],
81
+ skills: ["jira-research", "confluence-research", "bitbucket-research", "github-oss-research"],
82
+ },
73
83
  "makdoong2-engineer": {
74
84
  id: "makdoong2-engineer",
75
85
  stage: "all",
package/dist/config.d.ts CHANGED
@@ -33,6 +33,13 @@ export interface TimeoutConfig {
33
33
  stall_escalate_threshold?: number;
34
34
  }
35
35
  export declare const DEFAULT_STALL_ESCALATE_THRESHOLD = 5;
36
+ export interface ResearchConfig {
37
+ /** Max research sub-sessions spawned simultaneously. Clamped to [1, 6]. */
38
+ max_parallel?: number;
39
+ /** Per-source wall-clock budget. Shorter than a substage on purpose — a source
40
+ * that cannot answer in this window is reported as failed, not waited on. */
41
+ timeout_minutes?: number;
42
+ }
36
43
  export type LogLevel = "silent" | "error" | "warn" | "info" | "debug" | "trace";
37
44
  export type LogMode = "stdin" | "file";
38
45
  export interface LoggingConfig {
@@ -49,6 +56,7 @@ export interface Makdoong2Config {
49
56
  threshold?: number;
50
57
  };
51
58
  timeout?: TimeoutConfig;
59
+ research?: ResearchConfig;
52
60
  tmux?: TmuxConfigJson;
53
61
  worktree?: {
54
62
  extra_exclude?: string;
@@ -73,6 +73,11 @@ export const POLICIES = {
73
73
  primary: { id: "local/qwen3.6-27b", variant: "high", tier: "medium" },
74
74
  fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
75
75
  },
76
+ // Research fan-out worker — one session per source, spawned in parallel.
77
+ "makdoong2-researcher": {
78
+ primary: { id: "local/qwen3.6-27b", variant: "high", tier: "medium" },
79
+ fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],
80
+ },
76
81
  "makdoong2-planner": {
77
82
  primary: { id: "local/qwen3.6-27b", variant: "high", tier: "medium" },
78
83
  fallbacks: [{ id: "github-copilot/claude-haiku-4.5", tier: "low" }],