MUST USE for creating 오오티비랩 proposal decks from RFPs. Takes an RFP / 제안요청서 / 과업지시서 / 공고 (PDF or text), finds 3 similar past proposals via proposal-supabase-sync, synthesizes outline.yaml, then delegates to anthropic-skills:pptx for .pptx rendering (using bundled brand_tokens.json + brand_design.md as the OOTB design reference). ALWAYS use when user says: '제안서 만들어줘', '제안서 초안 짜줘', 'PPT 초안 뽑아줘', 'PPT 로 만들어줘', '슬라이드 초안', '피치덱', '과업지시서 로 초안', 'RFP 로 초안', '이번 과제로 제안서', '비슷한 거 참고해서', '같은 포맷으로'. Requires proposal-supabase-sync + Supabase MCP + anthropic-skills:pptx.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ootb-proposal-automation:rfp-to-proposal-pipelineThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill is a thin **orchestrator**. It doesn't reimplement DB or PPT logic — it chains:
This skill is a thin orchestrator. It doesn't reimplement DB or PPT logic — it chains:
proposal-supabase-sync (via Supabase MCP) — search past proposals, fetch content.anthropic-skills:pptx — build the final .pptx from outline.yaml using OOTB brand reference (references/brand_tokens.json + references/brand_design.md).Claude is the synthesis engine in the middle. prep_rfp.py handles PDF text extraction only — structured extraction and embedding are handled by Claude + gemini_embed_vault() in DB. PPT 렌더링은 Claude 가 anthropic-skills:pptx 의 pptxgenjs 가이드 + 본 스킬의 brand reference를 합쳐 직접 코드 생성·실행.
mcp__supabase__execute_sql availability).proposals table exists and has ≥ 1 ingested past proposal (ask: "DB에 과거 제안서가 몇 건 있나요?" → run select count(*) from proposals;).embed 가 배포되어 있고 환경변수 GEMINI_API_KEY 가 등록되어 있음 (Project Settings → Functions → Secrets). 로컬 GEMINI_API_KEY 불필요.anthropic-skills:pptx 가 Claude Cowork에서 사용 가능 (Anthropic 제공 기본 스킬).If none of the above, stop and direct the user to the relevant skill first.
Paths below assume the sibling skill lives next to this one:
오오티비랩/
├── proposal-supabase-sync/
└── rfp-to-proposal-pipeline/ ← you are here
(PPT 렌더링은 anthropic-skills:pptx 사용)
1-a. 텍스트 추출 (Python — Gemini 불필요):
cd rfp-to-proposal-pipeline/scripts
python prep_rfp.py /abs/path/to/RFP.pdf -o /tmp/rfp.json
출력: {rfp_full_text, page_count, file_name}
1-b. 구조화 추출 (Claude): /tmp/rfp.json의 rfp_full_text를 읽고 아래 필드 추출:
project_title 사업명
issuing_org 발주기관
industry 분야
scope_of_work 과업범위 5~12개
target_audience 주요 타깃
budget_krw 예산(원) 또는 null
duration 사업기간
deadline 제안 마감 YYYY-MM-DD
evaluation_criteria 평가항목/배점
required_deliverables 필수 산출물
keywords 검색 키워드 6~15개
summary 3~5 문장 요약
1-c. query_text 구성 (Claude): 키워드 나열 대신 한 문단 서술문으로:
"{project_title} — {issuing_org}이 발주한 {industry} 분야 사업. {summary} 주요 과업: {scope_of_work 상위 5개}. 타깃: {target_audience}."
→ 이 query_text가 Step 2의 gemini_embed_vault() 입력으로 사용됨.
Read /tmp/rfp.json. gemini_embed_vault() DB 함수가 Gemini 임베딩을 DB 내부에서 직접 생성하므로, 로컬 Python 실행 없이 MCP SQL 한 번으로 처리:
mcp__supabase__execute_sql(
project_id = "<ref>",
query = f"""
select id, title, client_name, project_year, industry, tags,
hybrid_score, vec_score, kw_score, abstract, signed_url
from match_proposals_with_url(
query_text => $q$ {rfp.query_text} $q$,
query_embedding => gemini_embed_vault($q$ {rfp.query_text} $q$),
match_count => 3,
vec_weight => 0.8,
kw_weight => 0.2,
url_expires_seconds => 3600
);
"""
)
($q$ ... $q$ 는 Postgres dollar-quoting — RFP 본문에 작은따옴표가 있어도 안전.)
vec_weight=0.8, kw_weight=0.2 이유: search_tsv가 simple config를 사용해 한국어 형태소 분리 불가 → kw_score 항상 ~0 → 벡터에 더 높은 가중치.
match_proposals_with_url이 없으면 match_proposals(...) 를 같은 방식으로 호출 (signed_url 생략).
If fewer than 3 rows return, continue with what we have; tell the user "유사 사례 N건 발견".
ids = [r["id"] for r in step2_result]
mcp__supabase__execute_sql(
project_id = "<ref>",
query = f"""
select id, title, project_year, client_name, industry, tags,
abstract, key_points, objectives, strategy, deliverables,
service_category, budget_krw
from proposals
where id in ({",".join(str(i) for i in ids)})
order by array_position(array[{",".join(str(i) for i in ids)}], id);
"""
)
You now have, in memory:
/tmp/rfp.json).outline.yaml (you, Claude, write this)Read references/synthesis_guide.md for how to compose the YAML. High-level:
cover → section_divider × N → (content | hero_takeaway) × M → closing. 5 types only — toc, hero, content_image (v1) 사용 금지.content slide MUST specify pattern: "A"~"F" matching the content shape (numbers→A, compare→B, diagram→C, process→D, quote→E, stacked narrative→F). See synthesis_guide.md Pattern Rubric + NG conditions.(ref: past#<id>) if a phrase was materially drawn from one of the three past proposals. 각 슬라이드에 source 필드도 명시 (footer 우측).Write the outline to /tmp/outline_<YYYYMMDD>.yaml (or a user-specified path).
유사 제안서의 시각 스타일(지배 색상)까지 새 덱에 반영하고 싶으면 analyze_reference.py 로 PDF(로컬 경로 또는 Step 2의 signed URL)를 분석해 reference_palette.json 을 만듭니다.
python scripts/analyze_reference.py \
--url "<signed_url_A>" "<signed_url_B>" \
-o /tmp/reference_palette.json \
-t /tmp/reference_thumbs/
출력은 per_deck(덱마다 추출된 역할별 색) + consensus(덱 간 공통), 그리고 Claude 가 시각 검토할 수 있는 슬라이드 썸네일 JPG들. 이 JSON 의 consensus 값을 Step 6 에서 Claude 가 직접 읽어 references/brand_tokens.json 의 palette 위에 비-null 값만 덮어쓴 뒤 pptxgenjs 코드에 반영합니다.
PPT 렌더링은 Claude 의 기본 anthropic-skills:pptx 스킬 에 위임합니다. 별도 빌드 스크립트 없이 Claude 가 pptxgenjs 코드를 생성·실행해 .pptx 를 만듭니다.
Claude 가 해야 할 일:
anthropic-skills:pptx/pptxgenjs.md 를 읽어 pptxgenjs API 파악references/brand_tokens.json — 색상 / 폰트 / 사이즈 / 레이아웃 토큰 (Brandlogy v2)references/brand_design.md — 5-zone locked skeleton + 6 body patterns + Visualization-First Rulereference_palette.json 이 있으면 brand_tokens.json.palette 위에 비-null 값으로 덮어쓰기/tmp/outline_<YYYYMMDD>.yaml 의 각 슬라이드를 brand_design.md 의 5-zone + 6 pattern 규칙에 따라 pptxgenjs 코드로 렌더링/abs/path/to/<project>_초안.pptx 로 저장핵심 제약 (brand_design.md 의 §0 참조):
생성된 .pptx 를 LibreOffice/PowerPoint 에서 열어 시각 검수:
문제가 보이면 outline.yaml 수정 후 Step 6 재실행.
SKILL.md — this file.scripts/prep_rfp.py — RFP PDF → JSON (text extraction).scripts/analyze_reference.py — reference PDFs → dominant color palette + thumbnails.scripts/requirements.txt — Python deps (pdfplumber, python-dotenv, plus Pillow, scikit-learn, numpy, requests for analyze_reference.py).references/workflow.md — compact step-by-step playbook (for Claude to re-read mid-run if context gets fuzzy).references/synthesis_guide.md — heuristics for composing v2 outline.yaml from the RFP + 3 past proposals (Pattern A–F rubric + NG conditions + diversity budget + v2 template).references/brand_design.md — OOTB v2 design system (5-zone locked skeleton + 6 body patterns + Visualization-First Rule). Read in Step 6.references/brand_tokens.json — OOTB brand tokens (palette / fonts / sizes / layout). Read in Step 6.references/brand_design.md (5-zone skeleton + 6 patterns) as the structural guide. Tell the user the output is a zero-shot draft — weaker than the full pipeline.rfp.query_text in Step 2's gemini_embed_vault() call. Write the structured fields yourself from what the user pasted, then jump to Step 2.match_count => 5 or 10. More references make synthesis richer but diminishing returns past ~5.A real RFP response depends on accurate 예산, 일정, KPI, 사례. The pipeline will happily synthesize confident-sounding numbers if you let it. Don't.
budget_krw is provided AND the new RFP's scope is comparable. Otherwise leave [금액 확인 필요].(ref: 2025_복지로) so the user knows to verify it's OK to quote.Searches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.
npx claudepluginhub hellogpc/ootb-proposal-plugin --plugin ootb-proposal-automation