npx claudepluginhub myrtlepn/gran-maestro --plugin mstThis skill uses the workspace's default tool permissions.
설정된 AI 팀원들의 의견을 병렬 수집하고 PM이 종합하여 인터랙티브 토론을 진행합니다. Maestro 모드 활성 여부에 관계없이 사용 가능. REQ 워크플로우와 독립적으로 실행됩니다.
Orchestrates iterative discussions among AI team members (architect, UX, security) until consensus on topics. Invoked via keywords like '토론' or /mst:discussion; outputs consensus.md after rounds.
Runs structured brainstorming with explorer/challenger agent pairs for product ideas, features, strategies, and milestones. Produces debate-tested proposals over 2-3 rounds.
Explores design alternatives with parallel agents for brainstorming ideas, solutions, or comparisons in software projects.
Share bugs, ideas, or general feedback.
설정된 AI 팀원들의 의견을 병렬 수집하고 PM이 종합하여 인터랙티브 토론을 진행합니다. Maestro 모드 활성 여부에 관계없이 사용 가능. REQ 워크플로우와 독립적으로 실행됩니다.
경로 규칙 (MANDATORY): 이 스킬의 모든
.gran-maestro/경로는 절대경로로 사용합니다. 스킬 실행 시작 시PROJECT_ROOT를 취득하고, 이후 모든 경로에{PROJECT_ROOT}/접두사를 붙입니다.PROJECT_ROOT=$(pwd)
{PLUGIN_ROOT}는 이 스킬의 "Base directory"에서skills/{스킬명}/을 제거한 절대경로입니다. 상대경로(.claude/...)는 절대 사용하지 않습니다.
{PROJECT_ROOT}/.gran-maestro/ideation/ 디렉토리 존재 확인, 없으면 생성python3 {PLUGIN_ROOT}/scripts/mst.py counter next --type idn → 출력 ID 사용{PROJECT_ROOT}/.gran-maestro/ideation/counter.json 파일 Readnext_id = last_id + 1{PROJECT_ROOT}/.gran-maestro/ideation/ 하위의 기존 IDN-* 디렉토리 스캔
b. {PROJECT_ROOT}/.gran-maestro/archive/ 내 ideation-* tar.gz 파일명에서 ID 범위 추출 (예: ideation-IDN001-IDN005-*.tar.gz → max 5)
c. 모든 소스에서 최대 번호 결정 → counter.json 생성: { "last_id": {max_number} }
d. next_id = last_id + 1counter.json 업데이트: { "last_id": {next_id} }{PROJECT_ROOT}/.gran-maestro/ideation/IDN-NNN/ 디렉토리 생성 (NNN은 3자리 zero-padded)session.json 작성:⏱️ 타임스탬프 취득 (MANDATORY):
TS=$(python3 {PLUGIN_ROOT}/scripts/mst.py timestamp now)위 명령 실패 시 폴백:python3 -c "from datetime import datetime, timezone; print(datetime.now(timezone.utc).isoformat())"출력값을created_at필드에 기입한다. 날짜만 기입 금지.
{
"id": "IDN-NNN",
"topic": "{사용자 주제}",
"focus": "{focus 옵션 또는 null}",
"status": "analyzing",
"created_at": "{TS — mst.py timestamp now 출력값}",
"dispatch_started_at": null,
"participants": [
{
"key": "architect(codex)",
"role": "architect",
"perspective": "",
"type": "opinion",
"status": "pending",
"provider": "codex",
"started_at": null,
"completed_at": null
},
{
"key": "ux-strategist(gemini)",
"role": "ux-strategist",
"perspective": "",
"type": "opinion",
"status": "pending",
"provider": "gemini",
"started_at": null,
"completed_at": null
},
{
"key": "risk-analyst(claude)",
"role": "risk-analyst",
"perspective": "",
"type": "opinion",
"status": "pending",
"provider": "claude",
"started_at": null,
"completed_at": null
}
],
"critics": {
"claude": { "status": "pending", "provider": "claude" }
},
"critic_count": 1,
"participant_config": { "codex": 3, "gemini": 2, "claude": 1 }
}
participants는 config의 ideation.agents를 읽어 생성합니다 (discussion과 독립 운영).
{role}(provider) 형식{participant.key} 형태로 key 구성provider 필드 기록participants 키 없으면 기본값 { codex:1, gemini:1, claude:1 }.
예시 (ideation.agents.codex=3, ideation.agents.gemini=2, ideation.agents.claude=1):
{
"participants": [
{ "key": "architect(codex)", "role": "architect", "perspective": "", "type": "opinion", "status": "pending", "provider": "codex", "started_at": null, "completed_at": null },
{ "key": "ux(codex)", "role": "ux", "perspective": "", "type": "opinion", "status": "pending", "provider": "codex", "started_at": null, "completed_at": null },
{ "key": "security(codex)", "role": "security", "perspective": "", "type": "opinion", "status": "pending", "provider": "codex", "started_at": null, "completed_at": null },
{ "key": "architecture(gemini)", "role": "architecture", "perspective": "", "type": "opinion", "status": "pending", "provider": "gemini", "started_at": null, "completed_at": null },
{ "key": "cost(gemini)", "role": "cost", "perspective": "", "type": "opinion", "status": "pending", "provider": "gemini", "started_at": null, "completed_at": null },
{ "key": "risk(claude)", "role": "risk", "perspective": "", "type": "opinion", "status": "pending", "provider": "claude", "started_at": null, "completed_at": null }
]
}
PM이 주제와 focus를 분석하여 participants 수만큼 관점을 배정합니다.
session.json 업데이트: participants[].perspective, critics 키, participant_config, critic_count, status: "collecting"이 스킬의 Step 1~3은 사용자 입력 없이 자율적으로 진행합니다.
- 백그라운드 작업(Codex/Gemini/Claude)이 완료될 때, 사용자에게 "계속할까요?" "진행할까요?" 등을 절대 묻지 마세요.
- 개별 백그라운드 작업 완료 알림에는 간단히 확인만 하고 모든 작업이 완료될 때까지 대기하세요.
- 모든 작업이 완료되면 즉시 다음 Step으로 진행하세요 (Step 2 (participants + critics 동시 dispatch) → 2.5 (완료 대기 + 진행 상황 출력) → 2.7 (critic 완료 확인) → 3 → 사용자 보고).
- 사용자 상호작용은 Step 4(인터랙티브 토론)에서만 발생합니다.
- 이 원칙은 mst-loop/ultrawork 모드가 아니어도 항상 적용됩니다.
context.md는 단독 Write, 프롬프트 파일은 단일 combined 파일 Write → 스크립트 split 패턴을 사용합니다:
session.json 업데이트, context.md 작성은 기존대로 단일 응답 내 Write 처리prompts/combined-prompts.txt 1개에 ===SPLIT: {filename}=== 구분기호로 모두 포함python3 {PLUGIN_ROOT}/scripts/mst.py session split-prompts --dir {absolute_path}/prompts 실행Task(run_in_background: true) 호출을 단일 응답에 포함합니다.단일 응답에서 아래를 동시 Write한 뒤, 모든 Task()를 단일 응답 내에서 동시 발송합니다.
Dispatch 프롬프트 조립 — feature flag 분기
config 확인:
python3 {PLUGIN_ROOT}/scripts/mst.py config get prompt_builder.enabled prompt_builder.fallback_on_error
context.md 본문을 .gran-maestro/tmp/ctx-{session_id}.md로 Write (기존 context.md와 동일 내용, tmp 복사본)dispatch-input.json을 아래 스키마로 Write:
{
"format": "mst.dispatch",
"schema_version": 1,
"common": {
"topic": "{IDN-NNN 주제}",
"constraints": ["..."],
"reference_context_file": ".gran-maestro/tmp/ctx-{session_id}.md"
},
"tasks": [
{"role": "{participant.key}", "angle": "{perspective}", "ask": "핵심 질문 1~3개 ≤200자"},
{"role": "{participant.key}", "angle": "{perspective}", "ask_file": ".gran-maestro/tmp/task-{role}-ask.md"}
]
}
format: "mst.dispatch", schema_version: 1common: topic, constraints[], reference_context_file: ".gran-maestro/tmp/ctx-{session_id}.md"tasks[]: 각 participant/critic마다 {role: "{participant.key}", angle: "{perspective}", ask: "...≤200자"} 또는 ask_file: "..."}.gran-maestro/tmp/task-{role}-ask.md로 Write 후 ask_file 경로 참조python3 {PLUGIN_ROOT}/scripts/mst.py prompt build \
--input {absolute_path}/dispatch-input.json \
--out-dir {absolute_path}/prompts \
--sid {session_id}
(metrics는 자동 기본 경로로 적재됨)prompts/combined-prompts.txt를 기존대로 session split-prompts로 분할 (기존 3단계 스크립트 호출 유지):
python3 {PLUGIN_ROOT}/scripts/mst.py session split-prompts --dir {absolute_path}/prompts
context.md — 공통 배경 컨텍스트 (주제 상세, 코드베이스 현황, 핵심 제약)prompts/combined-prompts.txt — N+M개 프롬프트를 ===SPLIT: {filename}=== 구분기호로 구분하여 1개 파일에 모두 포함
(participant N개 + critic M개, 아래 포맷 그대로 적용)combined-prompts.txt Write 완료 직후:
python3 {PLUGIN_ROOT}/scripts/mst.py session split-prompts --dir {absolute_path}/prompts
→ prompts/{participant.key}-prompt.md × N, prompts/critique-{criticKey}-prompt.md × M 자동 생성
(a) 경로 실행 중 mst.py prompt build가 exit 2/3 등 실패 반환 시:
config.prompt_builder.fallback_on_error=false이면 repair 실패 시 워크플로우를 중단하고 사용자 에스컬레이션참고: mst.py prompt build는 오류 반환만 담당하며, repair 1회/fallback 전환은 본 스킬(ideation)의 책임이다.
이후 2번(participant Task 발송)부터는 기존 내용 그대로 진행.
개별 프롬프트 포맷:
# {Role} 관점 의견 요청
<!-- @include _shared/skill-execution-marker.md -->
## 스킬 실행 마커 (MANDATORY)
- 모든 응답의 첫 줄 또는 각 Step 시작 줄에 아래 마커를 출력한다.
- 기본 마커 포맷: `[MST skill={name} step={N}/{M} return_to={parent_skill/step | null}]`
- 필드 규칙:
- `skill`: 현재 실행 중인 스킬 이름
- `step`: 현재 단계(`N/M`) 또는 서브스킬 종료 시 `returned`
- `return_to`: 최상위 스킬이면 `null`, 서브스킬이면 `{parent_skill}/{step_number}`
- 서브스킬 종료 마커: `[MST skill={subskill} step=returned return_to={parent/step}]`
- C/D 분리 마커 규칙을 추가로 사용하지 않는다. 반드시 단일 MST 마커만 사용한다.
- 예시:
- `[MST skill={name} step=1/3 return_to=null]`
- `[MST skill={subskill} step=returned return_to={parent_skill}/{step_number}]`
<!-- @end-include -->
## 공유 컨텍스트
{absolute_path}/context.md 파일을 Read하세요.
## 당신의 역할
{perspective} 관점에서 분석합니다.
## 질문
{역할별 핵심 질문 1~3개}
## 출력 요구사항
- {absolute_path}/opinion-{participant.key}.md에 저장
- {opinion_char_limit}자 이내
Critic 프롬프트 템플릿:
# Critic 평가 요청 — {session_id}
## 대기 지시
다음 명령을 실행하고 결과를 기다리세요:
python3 {PLUGIN_ROOT}/scripts/mst.py wait-files {participants 순회 → {absolute_path}/opinion-{participant.key}.md 절대 경로 목록}
마지막 줄이 ALL_READY면 다음 단계를 수행합니다.
TIMEOUT이면 완료된 파일들만으로 진행합니다.
## 역할
비판적 시각에서 모든 의견의 허점, 엣지 케이스, 반론을 식별합니다.
## 출력 요구사항
- {absolute_path}/critique-{criticKey}.md에 저장
- {critique_char_limit}자 이내
도구 사용 원칙 (CRITICAL)
- 모든 호출은
Task(run_in_background: true)래핑으로 병렬 실행- 각 응답은 파일로 직접 쓰기, 프롬프트도 파일로 저장 후
--prompt-file사용- agent는 프롬프트 파일 실행 전 반드시 공유 컨텍스트 파일을 Read해야 합니다
모델 결정:
Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get ideation.agents.claude.tier models.providers.claude.default_tier)로 tier를 구한 뒤models.providers.claude[{tier}]로 resolve (opus / sonnet)
participants 동적 순회):provider: "codex":
Bash(
run_in_background: true,
command: "codex exec --full-auto -m $(python3 {PLUGIN_ROOT}/scripts/mst.py resolve-model codex ideation 2>/dev/null || echo \"gpt-5.3-codex\") -C $(pwd) \"$(cat {absolute_path}/prompts/{participant.key}-prompt.md)\" > {absolute_path}/opinion-{participant.key}.md < /dev/null 2>&1; EC=$?; echo \"EXIT_CODE:$EC\" >> {absolute_path}/opinion-{participant.key}.md; exit $EC"
)
provider: "gemini":
Bash(
run_in_background: true,
command: "gemini -p \"$(cat {absolute_path}/prompts/{participant.key}-prompt.md)\" --model {config.models.providers.gemini[ideation.agents.gemini.tier || default_tier]} --approval-mode yolo --sandbox=false > {absolute_path}/opinion-{participant.key}.md < /dev/null 2>&1; EC=$?; echo \"EXIT_CODE:$EC\" >> {absolute_path}/opinion-{participant.key}.md; exit $EC"
)
provider: "claude":
Task(
subagent_type: "general-purpose",
model: "{config.models.providers.claude[ideation.agents.claude.tier || default_tier]}",
run_in_background: true,
prompt: "{absolute_path}/prompts/{participant.key}-prompt.md 파일을 Read하고 지시에 따라 분석. 결과를 opinion-{participant.key}.md에 Write. 완료 후 '완료'"
)
critics 동적 순회, participant Task()와 동일 응답):provider: "codex":
Bash(
run_in_background: true,
command: "codex exec --full-auto -m $(python3 {PLUGIN_ROOT}/scripts/mst.py resolve-model codex ideation 2>/dev/null || echo \"gpt-5.3-codex\") -C $(pwd) \"$(cat {absolute_path}/prompts/critique-{criticKey}-prompt.md)\" > {absolute_path}/critique-{criticKey}.md < /dev/null 2>&1; EC=$?; echo \"EXIT_CODE:$EC\" >> {absolute_path}/critique-{criticKey}.md; exit $EC"
)
provider: "gemini":
Bash(
run_in_background: true,
command: "gemini -p \"$(cat {absolute_path}/prompts/critique-{criticKey}-prompt.md)\" --model {config.models.providers.gemini[ideation.agents.gemini.tier || default_tier]} --approval-mode yolo --sandbox=false > {absolute_path}/critique-{criticKey}.md < /dev/null 2>&1; EC=$?; echo \"EXIT_CODE:$EC\" >> {absolute_path}/critique-{criticKey}.md; exit $EC"
)
provider: "claude":
Task(
subagent_type: "general-purpose",
model: "{config.models.providers.claude[ideation.agents.claude.tier || default_tier]}",
run_in_background: true,
prompt: "prompts/critique-{criticKey}-prompt.md 파일을 Read하고 비판 관점에서 분석. 결과를 critique-{criticKey}.md에 Write. 완료 후 '완료'"
)
의견 수집 중 ({session_id})
─────────────────────────────
[→] {participant.role} ({participant.provider}) ← participants 배열 동적 순회
...
── 비평 ──
[→] critic: {criticKey} ({critic.provider}) ← critics 객체 동적 순회
─────────────────────────────
완료 알림을 기다리는 중...
── 비평 ── 섹션 전체 생략participants 배열, critics 객체를 각각 동적 순회 (고정 인원 표기 금지)결과 확인: participants 순회 → opinion-{participant.key}.md 존재 여부로 성공/실패 판단.
각 백그라운드 태스크 완료 알림 도착 시 아래 형식으로 출력:
[✓] {role} ({provider}) 완료 [{n}/{participants_total + critics_total}]
모든 participants + critics가 완료되면:
의견 수집 완료 (참여자 {P}/{P}, 비평 {C}/{C})
→ synthesis 시작...
participants 순회 → 파일 존재 + 비어있지 않음: "done", 아니면: "failed". 세션 상태 일괄 업데이트 후 다음 Step 진행.
critics 키 순회 → critique-{criticKey}.md 존재 + 비어있지 않음: "done", 아니면: "failed".
실패 시 에러 처리는 기존 에러 처리 섹션 준수.
의견 파일 목록은 participants 항목 순회로 동적 생성:
opinion-{participant.key}.md + 관점: {participant.perspective}critique-{criticKey}.md 순회Synthesis prompt는 템플릿 templates/ideation-synthesis.md 사용.
세션 정보 또한 고정 인원 표기가 아닌 participants 동적 나열 형식으로 구성.
Step 4 진입 시 컨텍스트 판별 (최우선):
/mst:request가 ideation을 서브 호출할 때는 호출 인자에 --from-start 플래그가 포함됨.
이 플래그 존재 여부로 분기한다.
[경로 A] /mst:request 서브 호출 (--from-start 포함):
synthesis.md를 호출자(/mst:request)에게 반환session.json의 status를 즉시 "completed"로 갱신[경로 B] 독립 실행 (flags 없음):
synthesis.md 표시discussion.mdsession.json의 status를 "discussing" → 완료 시 "completed"로 갱신참여자 수 대비 처리:
--focus {architecture|ux|performance|security|cost}: 분석 범위를 특정 분야로 제한.gran-maestro/ideation/IDN-NNN/
├── session.json
├── context.md # 공통 배경 컨텍스트 (Step 2 병렬 Write)
├── prompts/
│ ├── {participant.key}-prompt.md # 경량 프롬프트 (context.md Read 지시 포함)
│ ├── critique-{criticKey}-prompt.md
│ └── synthesis-prompt.md
├── opinion-{participant.key}.md
├── critique-{criticKey}.md
└── synthesis.md
/mst:ideation "마이크로서비스 vs 모놀리식 아키텍처"