maestro:stitch
SkillDev toolsRuns only when the user explicitly invokes $mst:stitch or /mst:stitch, or explicitly requests the stitch feature of MST/Gran Maestro/Maestro. Does not auto-activate for general requests.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the maestro:stitch skill
What this skill tells your AI
The instructions your AI receives, as published by myrtlepn/gran-maestro in skills/stitch/SKILL.md and read by ahel’s review.
Step -1: Explicit Invocation Gate (MANDATORY, NO MUTATION)
모든 user-invocable: true MST skill은 아래 중 하나가 명확할 때만 실행합니다.
- 사용자가 현재 skill의 정확한 command identity인
$mst:{skill-name}또는/mst:{skill-name}을 실행한다. - 사용자가 MST/Gran Maestro/Maestro 기능을 사용해서 현재 skill 작업을 하라고 명시적으로 요청한다.
- 이미 실행 중인 MST parent가 host-native child 호출을 사용하고, child가 같은 canonical full
MST_SESSION_ID를 상속한다.
{skill-name}은 현재 SKILL.md frontmatter의 exact name입니다. 다른 MST command의 언급, 인용문·로그·문서 예시, 부정문은 현재 skill 실행 요청이 아닙니다.
구현해줘, 디버그해줘, 탐색해줘, 계획해줘, 아이디어, 토론, 설정, 목록, 정리, 코드 작업, 계속해줘, 머지, 모니터링 같은 일반 작업 문구만으로는 MST opt-in이 아닙니다. 다른 지침의 일반적인 skill discovery 문구도 이 경계를 넓힐 수 없습니다.
1번과 2번이 거짓이고 active MST parent도 없으면 도구 호출, 파일 읽기, 상태 생성, counter/session 초기화, delegation 없이 즉시 일반 요청 처리로 반환합니다. 사용자가 텍스트에 SID나 parent처럼 보이는 값을 넣어도 active parent로 간주하지 않습니다.
Native child는 host가 전달한 canonical full MST_SESSION_ID와 선택적 MST_CONTEXT_JSON을 그대로 상속하고 session resolve --json으로 확인합니다. Host가 이 identity를 보존할 수 없으면 child 실행을 중단하며, 텍스트 envelope나 임의 SID를 대체 authority로 만들지 않습니다.
이 gate는 이 문서의 나머지 모든 단계와 include보다 먼저 수행합니다.
Explicit-only Canonical Session Bootstrap (MANDATORY)
이 블록은 바로 앞의 Explicit Invocation Gate를 통과한 뒤에만 실행하며, 실행 순서상 first protected mutation입니다. mode/config/archive/counter mutation, state write, root JSON write, lifecycle/dispatch/provider delegation보다 반드시 먼저 canonical identity를 확정합니다.
1. Root source 결정
- Skill body의
mst-session-class가identity-required여야 이 bootstrap을 실행합니다. Existing resource나 inherited parent가 있으면 그 root를 사용합니다. - 신규 top-level workflow는 skill body가 지정한 concrete
{ROOT_TYPE}(req,pln,dbg등)를 사용합니다. ID를 따로 예상하거나counter next로 먼저 예약하지 않습니다. - 부모 full
MST_SESSION_ID가 있으면 root 후보를 새로 만들지 않습니다. 함께 전달된 structuredMST_CONTEXT_JSON.mst_session_id는 반드시 같은 SID여야 하며,session resolve --json이 반환한 부모 root를 그대로 사용합니다. --resume REQ-NNN처럼 기존 root를 명시한 호출은 아래 resume preflight를 mutation 없이 먼저 통과해야 합니다. 해당 ID를{ROOT_ID}로 사용합니다.- 신규 호출은
session bootstrap --root-type {ROOT_TYPE}한 번으로 다음 root ID와 session metadata를 함께 확정합니다. 실패하면 같은 명령을 재시도할 수 있습니다.
Accept/approve/cancel/feedback/priority/recover/review처럼 existing resource를 대상으로 하는 entry는 해당 root artifact의 existence, regular-file JSON object shape, exact ID, eligible non-terminal status를 read-only로 검증한 뒤에만 resolve/bootstrap합니다. Bootstrap으로 missing target을 생성해 preflight를 통과시키는 것은 금지합니다.
Bootstrap 직전에 root source를 아래 두 변수 중 정확히 하나로 확정합니다.
- 기존 resource: read-only preflight를 통과한 exact ID를
ROOT_ID에 설정하고ROOT_TYPE은 비웁니다. - 신규 workflow: concrete namespace를
ROOT_TYPE에 설정하고ROOT_ID는 비웁니다.
둘 다 있거나 둘 다 없으면 mutation 없이 거부합니다. 특히 $mst:approve REQ-NNN처럼 existing-only entry는 반드시 ROOT_ID=REQ-NNN 경로를 사용하며 새 request counter를 발급하지 않습니다.
2. Resume preflight (READ-ONLY, ZERO MUTATION ON REJECTION)
request --resume REQ-NNN은 resolve/bootstrap보다 먼저 다음을 모두 read-only로 확인합니다.
{PROJECT_ROOT}/.gran-maestro/requests/REQ-NNN/request.json이 이미 존재하는 regular file이며 symlink가 아니다.- Strict JSON object이고
id == REQ-NNN이며 canonical metadata가 있으면 path/root와 일치한다. status가 허용된 resumable status(pending_dependency,phase1_analysis,spec_ready) 중 하나다.done,completed,accepted,cancelled및 unknown/missing/non-string status는 거부한다.--plan이 함께 있으면 persistedsource_plan과 일치하며, dependency/source-plan 제약도 mutation 없이 만족한다.
Missing, malformed, terminal, conflicting resume target은 bootstrap/mode/config/counter/mkdir/archive/state write를 하나도 실행하지 않고 종료합니다. Rejected resume 전후의 전체 filesystem tree가 동일해야 합니다. Resume artifact를 bootstrap으로 새로 생성해 존재 검사를 통과시키는 순서는 금지합니다.
3. Resolve 또는 bootstrap
- 부모 full
MST_SESSION_ID가 있으면session resolve --json으로 기존 SID를 검증·상속합니다. 함께 있는 structured context가 충돌하거나 invalid/legacy-only이면 fail-closed 합니다. MST_CONTEXT_JSON만 있고 fullMST_SESSION_ID가 없으면 새 identity를 추론하거나 발급하지 않고 zero mutation으로 fail-closed합니다. Native child는 host가 부모의 full SID를 상속해야 합니다.- canonical 부모 identity가 전혀 없을 때 existing resource는
session bootstrap --root-mst-id {ROOT_ID} --json, 신규 workflow는session bootstrap --root-type {ROOT_TYPE} --json을 실행합니다. - 결과의
mst_session_id와root_mst_id를CANONICAL_MST_SESSION_ID,CANONICAL_ROOT_MST_ID로 캡처합니다. 부모 호출에서는 자식 artifact ID로 root를 바꾸지 않습니다.
if [ -n "${MST_SESSION_ID:-}" ]; then
SESSION_IDENTITY_JSON=$(
MST_SESSION_ID="$MST_SESSION_ID" \
MST_CONTEXT_JSON="${MST_CONTEXT_JSON:-}" \
python3 "{PLUGIN_ROOT}/scripts/mst.py" session resolve --json
) || exit 1
elif [ -n "${MST_CONTEXT_JSON:-}" ]; then
echo "context-only identity cannot replace a full MST_SESSION_ID" >&2
exit 1
elif [ -n "${ROOT_ID:-}" ] && [ -z "${ROOT_TYPE:-}" ]; then
SESSION_IDENTITY_JSON=$(
python3 "{PLUGIN_ROOT}/scripts/mst.py" session bootstrap \
--root-mst-id "$ROOT_ID" --json
) || exit 1
elif [ -z "${ROOT_ID:-}" ] && [ -n "${ROOT_TYPE:-}" ]; then
SESSION_IDENTITY_JSON=$(
python3 "{PLUGIN_ROOT}/scripts/mst.py" session bootstrap \
--root-type "$ROOT_TYPE" --json
) || exit 1
else
echo "exactly one of ROOT_ID or ROOT_TYPE is required" >&2
exit 1
fi
4. Shell-safe canonical context 생성
기존 context의 모든 비-identity 필드를 보존하면서 canonical SID/root를 병합합니다. raw JSON을 single-quoted shell literal로 삽입하지 않습니다. CANONICAL_MST_CONTEXT_JSON은 논리적 JSON 값이며, subprocess 경계에서는 오직 ASCII CANONICAL_MST_CONTEXT_B64(base64url)로 운반합니다.
CANONICAL_MST_CONTEXT_B64=$(
SESSION_IDENTITY_JSON="$SESSION_IDENTITY_JSON" \
INPUT_MST_CONTEXT_JSON="${MST_CONTEXT_JSON:-}" \
python3 - <<'PY'
import base64, json, os, sys
MAX_CONTEXT_BYTES = 262144
identity = json.loads(os.environ["SESSION_IDENTITY_JSON"])
raw_context = os.environ.get("INPUT_MST_CONTEXT_JSON", "")
context = json.loads(raw_context) if raw_context else {}
if not isinstance(context, dict):
raise SystemExit("MST_CONTEXT_JSON must be a JSON object")
sid = identity["mst_session_id"]
root = identity["root_mst_id"]
if context.get("mst_session_id") not in (None, sid):
raise SystemExit("MST_CONTEXT_JSON mst_session_id mismatch")
if context.get("root_mst_id") not in (None, root):
raise SystemExit("MST_CONTEXT_JSON root_mst_id mismatch")
context["schema_version"] = 1
context["mst_session_id"] = sid
context["root_mst_id"] = root
wire = json.dumps(context, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
if len(wire) > MAX_CONTEXT_BYTES:
raise SystemExit("canonical MST context exceeds MAX_CONTEXT_BYTES")
sys.stdout.write(base64.urlsafe_b64encode(wire).decode("ascii"))
PY
) || exit 1
5. MST_BOUND_SUBPROCESS — 모든 별도 subprocess에 재바인딩
서로 다른 tool/exec 호출은 별도 subprocess이므로 한 export에 의존하면 안 됩니다. shell function에도 의존하지 않습니다. 이후 모든 별도 mst.py, counter, timestamp, config, state, lifecycle, delegation, dispatch, provider CLI subprocess는 아래 MST_BOUND_SUBPROCESS 형태로 정확한 full SID와 base64url context를 리터럴로 반복 전달합니다. 뒤 단계의 prefix 없는 command 예시는 축약 표기일 뿐이며 실제 실행 전에 반드시 이 형태로 확장합니다.
MST_SESSION_ID="{CANONICAL_MST_SESSION_ID}" \
MST_CONTEXT_JSON="$(
MST_CONTEXT_B64="{CANONICAL_MST_CONTEXT_B64}" \
python3 -c 'import base64,os,sys;s=os.environ["MST_CONTEXT_B64"];MAX_CONTEXT_BYTES=262144;sys.exit("encoded MST context exceeds limit") if len(s)>349528 else None;raw=base64.b64decode(s.encode("ascii"),altchars=b"-_",validate=True);sys.exit("decoded MST context is oversized or non-canonical") if len(raw)>MAX_CONTEXT_BYTES or base64.urlsafe_b64encode(raw).decode("ascii")!=s else None;sys.stdout.buffer.write(raw)'
)" \
python3 "{PLUGIN_ROOT}/scripts/mst.py" {NEXT_COMMAND}
Decoder는 base64.b64decode(..., altchars=b"-_", validate=True)를 사용하고 decoded size를 MAX_CONTEXT_BYTES로 제한하며 canonical re-encoding equality를 요구합니다. Invalid alphabet, missing/extra padding, non-canonical spelling, oversize는 command 실행 전에 실패합니다.
Provider CLI를 직접 실행하는 허가된 external lane도 마지막 명령만 provider command로 바꾸고 동일한 두 환경 변수를 다시 구성합니다. apostrophe, command substitution, backtick, newline, shell metacharacter를 포함한 JSON도 decode 결과가 quoted environment assignment의 값으로만 들어가며 shell code로 재평가되어서는 안 됩니다. Shell command substitution은 trailing whitespace/newline byte identity를 보존하지 않으므로 raw input byte-equivalence를 약속하지 않습니다. 대신 Step 3에서 trailing whitespace 없는 canonical compact JSON으로 먼저 정규화한 뒤 그 canonical bytes만 encode/decode합니다.
Canonical Root Metadata Merge (MANDATORY)
session bootstrap은 root artifact의 JSON(session.json, plan.json, request.json)에 canonical metadata를 먼저 기록합니다. 이후 skill template write는 그 JSON을 replace/overwrite하지 않고 object merge해야 합니다. 기존 mst_session_id, root_mst_id, started_at, started_at_compact, random을 byte-for-byte 보존하고 workflow 필드만 병합합니다. 기존 canonical 필드가 bootstrap 결과와 다르면 fail-closed하며 identity를 재발급하지 않는다. 부모 SID를 상속한 child artifact처럼 파일이 아직 없을 때만 resolve 결과의 canonical field를 먼저 넣고 workflow field를 병합한다.
DBG-NNN/REQ-NNN 같은 root resource ID, host session ID, PID, transcript UUID, legacy alias는 full SID를 대신할 수 없습니다.
Gate
Entry
mst:stitch는 반드시Skill(skill: "mst:stitch", args: ...)로만 호출한다.scripts/stitch-sdk.mjs를 Bash로 직접 orchestration하는 것은 금지한다 (Bash 직접 orchestration 금지).- SDK require 실패 신호(
install_required:true)를 수신하면 즉시 설치 동의 플로우(아래### SDK 누락 감지 → 설치 동의 플로우)로 분기한다.
Exit
- 모든 화면 생성/저장 산출물이
.gran-maestro/designs/DES-NNN/하위에 persist된 것을 확인한 뒤 종료한다.
금지 패턴
mst:stitch우회: PM이 속도를 이유로scripts/stitch-sdk.mjs를 Bash로 직접 호출한다.- persist 생략: generate 응답에서 html/image를 읽고도 파일로 저장하지 않는다.
mcp__stitch__*도구를 직접 호출한다.
Anti-Rationalization Checklist
- 합리화 패턴: "CLI를 직접 쓰는 편이 더 빠르다." | 확인 증거: 스킬 호출 로그에
Skill(mst:stitch, ...)엔트리가 존재해야 하며,Bash(node scripts/stitch-sdk.mjs ...)직접 호출은 예외(진단 목적 확인) 외에는 금지. - 합리화 패턴: "generate 응답은 나중에 파싱하자." | 확인 증거: generate 호출 직후 동일 flow 내에서 html/image/meta가
.gran-maestro/designs/DES-NNN/**에 파일로 남는다.
선행 조건 확인
config.stitch.enabled확인 → false면 즉시 종료 (안내 메시지 출력)- 모델 ID 해석:
config.stitch.model_id읽기 → 미설정(null/undefined)이면"MODEL_ID_UNSPECIFIED"--model옵션 확인:--model pro→"GEMINI_3_PRO"오버라이드--model flash→"GEMINI_3_FLASH"오버라이드- 유효하지 않은 값 → "[Stitch] 알 수 없는 모델: {값}. config 기본값({config.stitch.model_id})을 사용합니다." 출력 후 config 값 유지
- 결과를
{STITCH_MODEL}변수에 보관 (이후 모든 SDK 호출에 사용)
config.stitch.auto_detect확인:- false면: 사용자 명시 설정으로 간주 → 계속
- true면:
a. UI 키워드 1차 필터: 요청 텍스트/spec §1 요약에 아래 키워드 중 하나라도 포함되지 않으면 list_projects 호출 없이 skip:
- whitelist:
화면,UI,페이지,page,screen,컴포넌트,component,레이아웃,layout,디자인,design,목업,mockup,시안,뷰,viewb. 세션 캐시 확인: 현재 세션 중 이미list_projects를 성공 호출한 결과가 있으면 재사용 (재호출 생략) c. 캐시 미존재 시:Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs list-projects")호출 (30초 타임아웃) - 성공: 결과를 세션 캐시에 저장 → 계속
- 응답 JSON이
ok=false이고auth_required=true면 아래 가이드 흐름 실행:setup_url(없으면https://stitch.withgoogle.com/settings) 확인 후 브라우저 열기 시도:Bash(command: "open {setup_url}")(실패 시 URL을 그대로 출력하고 수동 접속 안내)
AskUserQuestion으로 API Key 입력 요청:- 안내 문구에
env_var(기본STITCH_API_KEY)와setup_url을 포함
- 안내 문구에
- 사용자가 입력한 값을 현재 세션에 설정:
export STITCH_API_KEY="{USER_INPUT_API_KEY}"
- 방금 실패한 동일 명령을 즉시 자동 재시도:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs list-projects")
- 재시도 성공 시 세션 캐시에 저장 후 계속
- 영구 저장 안내:
echo 'export STITCH_API_KEY="{USER_INPUT_API_KEY}"' >> ~/.zshrcsource ~/.zshrc
- 실패/타임아웃(또는 재시도 실패):
[Stitch] 연결 불가 — 건너뜀. /mst:stitch로 수동 실행 가능.출력 후 종료
- whitelist:
SDK 누락 감지 → 설치 동의 플로우 (MANDATORY)
- 스킬 초입에서
stitch-sdk.mjs를list-projects등으로 먼저 호출한다. - stdout JSON을 파싱하여
install_required === true를 감지. - 감지 시
AskUserQuestion으로 아래와 같이 질문한다:- question: "Stitch SDK가 설치되어 있지 않습니다. 설치할까요?"
- header: "SDK 설치"
- options:
- label: "설치 후 재시도"
description: "[장점]
{suggested_command}를{cwd}에서 실행해 SDK를 설치하고 원래 호출을 1회 재시도\n[단점] 네트워크/권한 부족 시 실패 가능\n[적합] 플러그인 첫 실행 또는 업그레이드 직후 SDK 미설치 상태" - label: "취소" description: "[장점] 네트워크를 사용하지 않고 즉시 종료\n[단점] 디자인 생성 불가\n[적합] 오프라인 또는 수동 설치를 선호할 때"
- label: "설치 후 재시도"
description: "[장점]
- "설치 후 재시도" 선택 시:
Bash(command: "cd {cwd} && {suggested_command}", description: "Install @google/stitch-sdk")실행 → 원래stitch-sdk.mjs호출을 1회 재시도 → 성공 시 정상 진행, 실패 시 사용자에게 로그 보고 후 종료. - "취소" 선택 시: "Stitch SDK 설치가 취소되었습니다. 스킬을 종료합니다." 메시지 후 즉시 종료.
- 재시도는 1회만 허용 (무한 재시도 금지).
시안 제시 금지 행위 (CRITICAL)
⚠️ 시안 제시 금지 행위 (CRITICAL): 시안 생성 완료 후 결과를 사용자에게 보여줄 때, 아래 행위는 절대 금지합니다:
curl로 HTML/이미지 다운로드하여 로컬에서 보여주기python3 -m http.server등 로컬 서버를 실행하여 시안 미리보기- Claude in Chrome(
navigate,tabs_create,computer등) 브라우저 자동화로 시안 표시- 기타 외부 도구를 사용한 시안 직접 미리보기
시안 확인은 대시보드 Designs 탭에서만 수행합니다. 모든 결과 보고에
{DASHBOARD_BASE_URL}/designs/{DES-NNN}?project={projectId}URL만 안내하세요. 이 규칙은 최초 시안, Edit 결과, Alt 결과, Redesign 결과 등 모든 시안 제시 시점에 적용됩니다.
DES 채번 및 프로젝트 확인/생성
경로 규칙 (MANDATORY): 이 스킬의 모든
.gran-maestro/경로는 절대경로로 사용합니다. 스킬 실행 시작 시PROJECT_ROOT를 취득하고, 이후 모든 경로에{PROJECT_ROOT}/접두사를 붙입니다.PROJECT_ROOT=$(pwd)
{PLUGIN_ROOT}는 이 스킬의 "Base directory"에서skills/{스킬명}/을 제거한 절대경로입니다. 상대경로(.claude/...)는 절대 사용하지 않습니다.
MANDATORY Read: ~/.claude/user-profile.json (User Input Boundary 컨텍스트, 비차단)
~/.claude/user-profile.json을 Read한다.- 파일이 없으면
user_profile_context = null로 처리하고 기존 동작을 유지한다 (graceful fallback).
- 파일이 없으면
- 파일이 있으면 JSON을 파싱하고 아래 필드만 사용한다.
role(string)experience_level(string)domain_knowledge(string[])communication_style(string)
- JSON 파싱 실패 또는 타입 불일치 시 warn만 출력하고
user_profile_context = null로 처리한다 (워크플로우 차단 금지). - 이후 User Input Boundary 질문 payload와 사용자 설명 텍스트 작성 시:
communication_style을 최우선 반영한다.experience_level/domain_knowledge에 맞춰 용어 수준과 설명 깊이를 조절한다.- 누락 필드는 추정하지 않고, 존재하는 필드만 참고한다.
⚠️ DES 채번 스킵 조건:
--edit,--alt,--redesign,--list,--init플래그가 있으면 이 섹션(Step A~C-2)을 건너뛰고 해당 프로토콜로 직접 진행한다.
⚠️ 이 단계는 화면 생성 이전에 실행된다.
- Step A: DES ID 결정
canonical root가 DES-NNN이면 bootstrap 결과의 CANONICAL_ROOT_MST_ID를 그대로 사용합니다. 상속된 parent root가 다른 namespace일 때만 MST_BOUND_SUBPROCESS의 python3 {PLUGIN_ROOT}/scripts/mst.py counter next --type des로 child DES ID를 한 번 예약합니다.
- Step B: DES-NNN 디렉토리 생성
{PROJECT_ROOT}/.gran-maestro/designs/DES-NNN/
- Step C: design.json 초안 작성
⏱️ 타임스탬프 취득 (MANDATORY):
TS=$(python3 {PLUGIN_ROOT}/scripts/mst.py timestamp now)
{
"id": "DES-NNN",
"title": "{사용자 요청 요약}",
"status": "active",
"created_at": "{TS}",
"linked_plan": "{활성 PLN ID 또는 null}",
"linked_req": "{활성 REQ ID 또는 null}",
"stitch_project_id": null,
"stitch_project_url": null,
"screens": []
}
- Step C-1: DES 전용 Stitch 프로젝트 확인/생성
design.json의 stitch_project_id 확인:
- 값 있으면:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs get-project --project-id {stitch_project_id}")호출로 유효성 검증- 실패(프로젝트 삭제/만료) 시: 아래 "null이면" 절차로 재생성
- null이면:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs create-project --title 'DES-NNN: {design.json title}'")로 DES 전용 프로젝트 생성- 프로젝트 이름:
"DES-NNN: {design.json title}" design.json에 즉시 갱신:"stitch_project_id": "{생성된 project_id}", "stitch_project_url": "https://stitch.withgoogle.com/projects/{project_id}"
- 프로젝트 이름:
이후 모든 화면 생성(generate_screen_from_text, generate_variants)은 이 DES 전용 stitch_project_id를 사용한다.
- Step C-2: 대시보드 URL 구성
대시보드 링크에 사용할 URL 변수를 DES 채번 직후 명시적으로 구성한다.
Bash(python3 {PLUGIN_ROOT}/scripts/mst.py config get server.host server.port)로server.host,server.port를 확인한다.server.host가 없거나 파일 Read에 실패하면127.0.0.1을 사용한다.server.port가 없거나 파일 Read에 실패하면3847을 사용한다.- 아래 변수를 구성한다:
{DASHBOARD_BASE_URL}=http://{server.host}:{server.port}
- 대시보드 API를 호출하여 현재 프로젝트의
projectId를 해석한다:curl -s "{DASHBOARD_BASE_URL}/api/projects"- 응답 JSON 배열에서
path가{PROJECT_ROOT}/.gran-maestro와 일치하는 항목의id를{projectId}로 사용한다. - 매칭 항목이 없으면 로컬 대시보드 링크 출력을 생략하고 원인을 사용자에게 안내한다.
- 이후 모든 사용자 출력에서 로컬 대시보드 링크는 반드시 아래 형식을 사용한다:
{DASHBOARD_BASE_URL}/designs/{DES-NNN}?project={projectId}
기존 화면 컨텍스트 수집 (선택)
기존 UI 화면이 있는 경우 레이아웃 일관성을 위해:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs list-screens --project-id {stitch_project_id}")로 기존 Stitch 화면 목록 조회- 기존 화면이 있으면: 최근 화면 1-2개의 핵심 레이아웃 패턴을 텍스트로 요약 (공통 Header/Sidebar 구조, 주요 컴포넌트 패턴)
- 이 컨텍스트를
generate_screen_from_text프롬프트에 포함
프로젝트 초기화 (--init)
--init 옵션이 전달되면 신규 화면 생성 전에 아래를 먼저 수행한다.
config.stitch.project_id를 확인한다. 미설정이면 에러 출력 후 종료한다.- 아래 명령으로 프로젝트 + 화면 컨텍스트를 수집한다:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs init --project-id {config.stitch.project_id}")
- JSON 응답의
summary.theme,screens,project를 바탕으로 DESIGN.md 초안을 생성한다. - 저장 경로:
{PROJECT_ROOT}/.gran-maestro/designs/DESIGN.md --init단독 호출이면 여기서 종료하고, 일반 생성 플로우와 함께 호출됐으면 아래 화면 생성 프로토콜을 계속 진행한다.
트리거 분기
A. 명시적 디자인 요청 (즉시 실행)
- 감지: "화면 디자인해줘", "Stitch로 그려줘", "목업 만들어줘" 등 명시적 디자인 의도
- 처리: 사용자 확인 없이 바로 화면 생성 프로토콜 진행
B. 새 화면 추가 요청 (사용자 선택)
- 강한 신호: 새 라우트 파일 생성 + 네비게이션 노출 예정
- 중간 신호: "새/추가/신규 화면/페이지" 키워드 포함
- config.stitch.auto_trigger=false(기본): "Stitch로 화면 먼저 설계할까요?" 물어봄
- config.stitch.auto_trigger=true: 자동 실행
C. 전체 디자인 변경 (사용자 선택 + variants)
- 감지: "전체 디자인 바꿔줘", "리디자인", "전면 개편" 등
- 처리: B와 동일하게 확인 후, --variants 옵션으로 2-3개 방향 제안
D. 약한 신호 (개입 안 함)
- 기존 화면 컴포넌트/스타일 수정만 → Stitch 개입 없음
E. 멀티 스타일 요청 (--multi 플래그 또는 plan Step 4.5 진입)
- 감지:
--multi플래그 명시 ormst:planStep 4.5 "스티치로 디자인 시안 보기" 선택 진입 - 처리: 사용자 확인 없이 바로 멀티 스타일 생성 프로토콜 진행
F. Edit 요청 (--edit SCREEN_ID)
- 감지:
--edit플래그 + SCREEN_ID + 편집 프롬프트 - 처리: 사용자 확인 없이 Edit 프로토콜 진행
G. Alt 요청 (--alt SCREEN_ID)
- 감지:
--alt플래그 + SCREEN_ID + 대안 프롬프트 - 처리: 기본은 variants(EXPLORE, 2개)로 Alt 프로토콜 진행
- 예외:
--alt SCREEN_ID --reimagine조합이면 Redesign 프로토콜 진행
H. Redesign 요청 (--redesign SCREEN_ID)
- 감지:
--redesign플래그 + SCREEN_ID - 처리:
--alt SCREEN_ID --reimagine과 동일 처리 (Redesign 프로토콜 alias)
화면 생성 프로토콜
-
baseline_screen_ids 기록:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs list-screens --project-id {stitch_project_id}")호출 → 응답의screens[].name에서 screen ID를 추출하여baseline_screen_idsSet으로 저장- screen ID 추출:
name필드의 마지막/이후 값 (예:"projects/.../screens/abc123"→"abc123")
-
중복 체크 (diff hash):
- REQ-NNN이 있을 경우:
request.json의stitch_screens에서 동일route + hash조합 확인 (기존 동일) - REQ-NNN 없고 PLN-NNN이 있을 경우:
plan.json의stitch_screens에서 확인 - 둘 다 없을 경우: 중복 체크 생략
status: "active"항목 발견 시: "이미 생성된 화면입니다." 출력 후 기존 URL 반환, 종료status: "pending"항목 발견 시: 이전 생성 시도가 타임아웃됐을 가능성 있음 → 서버 확인 진행Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs list-screens --project-id {stitch_project_id}")호출로 실제 화면 존재 여부 확인- 발견 시:
get_screen으로 URL 확보 → pending 항목을 active로 갱신 → 기존 URL 반환, 종료- output_components HTML 확인:
get_screen응답의output_components를 확인하여 Step 4-2의 output_components 파싱 규칙을 따른다- 코드 포함 시:
html_content메모리 변수에 보관 (파일 저장은 Step D에서 md와 동시 수행) - 비어있거나 제안 텍스트인 경우:
html_content = null
- 코드 포함 시:
- output_components HTML 확인:
- 매칭 기준:
- pending 항목에
baseline_screen_ids가 있으면: 현재 screen IDs에서 baseline_screen_ids 제거(차집합) → 차집합이 비어있지 않으면 해당 화면 중 첫 번째 선택 baseline_screen_ids가 없으면(구버전 pending):created_at이후 생성된 화면 중 최근 3개를 검사 (기존 방식 유지)
- pending 항목에
- 미발견 시:
stale_at(=created_at+ 15분) 경과 여부 확인stale_at이내: pending 항목 유지 → "이전 생성 요청이 아직 처리 중일 수 있습니다. 잠시 후 다시 시도하세요." 출력 후 종료stale_at경과: pending 항목 제거 → 새 생성 진행
- REQ-NNN이 있을 경우:
-
pending 선기록:
- REQ-NNN이 있을 경우:
generate_screen_from_text호출 직전request.json의stitch_screens에 임시 항목 기록 (기존 동일):{ "status": "pending", "hash": "{hash}", "route": "{route}", "created_at": "{TS}", "baseline_screen_ids": ["{id1}", "{id2}", ...] } - REQ-NNN 없고 PLN-NNN이 있을 경우:
plan.json의stitch_screens에 기록 (형식 동일):{ "status": "pending", "hash": "{hash}", "created_at": "{TS}", "baseline_screen_ids": ["{id1}", "{id2}", ...] } - 둘 다 없을 경우: pending 선기록 생략
- 빈 응답/타임아웃 발생 시 이 항목이 재실행 중복 방지에 사용됨
- REQ-NNN이 있을 경우:
2.5. 프롬프트 향상 (references 기반):
- 아래 파일을 먼저 Read:
{PROJECT_ROOT}/skills/stitch/references/design-mappings.md{PROJECT_ROOT}/skills/stitch/references/prompt-keywords.md
- 요청문을 그대로 전달하지 말고, 위 레퍼런스를 근거로 생성 프롬프트를 구조화한다.
- 최종 프롬프트에는 최소 아래 섹션을 포함한다:
**DESIGN SYSTEM****Page Structure**
{PROJECT_ROOT}/.gran-maestro/designs/DESIGN.md가 존재하면 톤/타이포/색상/레이아웃 지침을**DESIGN SYSTEM**에 우선 반영한다.- Step 4의
{화면 설명}자리에는 향상된 최종 프롬프트를 사용한다.
-
대기 안내 메시지 출력:
[Stitch] 화면 생성 중... (최대 수 분 소요될 수 있습니다) -
화면 생성:
Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs generate --project-id {stitch_project_id} --prompt \"{화면 설명}\n\n[기존 레이아웃 컨텍스트]\n{수집된 컨텍스트}\" --device-type DESKTOP --model-id {STITCH_MODEL}")- 응답 있음 (screen_id 포함): step 4-2(output_components 저장)로 진행
- 빈 응답/null: 비동기 수락으로 처리 → 폴링 루프(4-1) 진입 (재시도 금지)
- 명시적 오류(예외): 실패 처리 (기존 동일)
4-2. output_components HTML 저장 (step 4 응답 있음 시):
output_components필드 확인:- 코드 포함 시 (HTML/CSS/JSX/React 코드를 담은 비어있지 않은 텍스트, 제안 문구 아님):
- 스크린 파일 번호 산출:
{PROJECT_ROOT}/.gran-maestro/designs/DES-NNN/screen-*.md파일 수 + 1 (없으면001) {PROJECT_ROOT}/.gran-maestro/designs/DES-NNN/screen-{NNN}.html에 저장html_file_path메모리 변수에 경로 보관 (Step D, E에서 사용)
- 스크린 파일 번호 산출:
- 비어있거나 제안 텍스트인 경우 (예: "Yes, make them all"):
html_file_path = null→ step 5(get_screen)로 진행
- 코드 포함 시 (HTML/CSS/JSX/React 코드를 담은 비어있지 않은 텍스트, 제안 문구 아님):
4-1. 폴링 루프 (빈 응답인 경우만): 최대 20회, 30초 간격 (총 최대 10분)
⚠️ MANDATORY: 반드시 20회를 모두 채울 때까지 루프를 종료하지 않는다. 중간에 임의로 중단하는 것은 금지다.
반복마다 (poll_count = 1부터 시작, 20 이하인 동안 반복):
a. python3 {PLUGIN_ROOT}/scripts/mst.py stitch sleep --interval 30 (Bash 호출)
b. Bash(command: "node {PLUGIN_ROOT}/scripts/stitch-sdk.mjs list-screens --project-id {stitch_project_id}") 호출
c. 현재 screen IDs - baseline_screen_ids ≠ ∅ 인가?
- YES: 차집합의 첫 번째 screen ID 선택 → step 5로 진행
- NO: [Stitch] 대기 중... ({poll_count}/20회) 출력 후 poll_count += 1, 반복 계속
20회 모두 미감지 시:
- "[Stitch] 화면 생성 요청이 처리 중입니다 — 수 분 내 완료됩니다. 잠시 후 /mst:stitch --list로 확인하세요." 출력
- pending 항목 유지 (
stale_at=created_at+ 15분) - 종료
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 24
- Forks
- 5
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
stitch-myrtlepn- Source
- github.com/myrtlepn/gran-maestro