From agent4ppt
**English:** End-to-end skill that orchestrates a 4-stage pipeline: (1) deep-interview spec gathering, (2) template parsing, (3) content generation with validation, and (4) PPTX generation. Produces a complete presentation from a topic string.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent4ppt:research-and-presentThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**English:** End-to-end skill that orchestrates a 4-stage pipeline: (1) deep-interview spec gathering, (2) template parsing, (3) content generation with validation, and (4) PPTX generation. Produces a complete presentation from a topic string.
English: End-to-end skill that orchestrates a 4-stage pipeline: (1) deep-interview spec gathering, (2) template parsing, (3) content generation with validation, and (4) PPTX generation. Produces a complete presentation from a topic string.
한국어: 4단계 파이프라인을 오케스트레이션하는 엔드투엔드 스킬: (1) 딥 인터뷰 스펙 수집, (2) 템플릿 파싱, (3) 콘텐츠 생성 및 검증, (4) PPTX 생성. 주제 문자열로부터 완전한 프레젠테이션을 생성합니다.
/research-and-present <topic> [--template <template.pptx>] [--output <output.pptx>] [--lang ko|en]
Executed via: python skills/research-and-present/research_and_present.py
| Parameter | Required | Default | Description |
|---|---|---|---|
topic | ✅ | — | Presentation topic (free text) |
--template, -T | ❌ | Auto-detect | Path to a PPTX template file |
--output, -o | ❌ | <topic_slug>.pptx | Output PPTX file path |
--lang, -l | ❌ | Auto-detect from topic or $LANG | Language for messages (ko | en) |
--skip-menu | ❌ | false | Skip interactive execution menu — bypass confirmation prompts and proceed automatically through all pipeline stages. Use when invoked programmatically from another skill or automation. |
When /research-and-present is invoked programmatically (e.g., delegated from another
skill, a script, or an automated pipeline), pass --skip-menu to bypass all interactive
confirmation menus and proceed directly through each stage without user prompts.
Behavior when --skip-menu is active:
| Stage | Normal (interactive) | With --skip-menu |
|---|---|---|
| Pre-flight (OMC detection) | Prints status, may prompt | Prints status silently, no prompt |
| Stage 1 (Spec gathering) | Fallback interview asks user questions | Skips fallback interview; uses defaults or pre-supplied spec via stdin |
| Stage 2 (Template parsing) | Confirmation before parsing | Proceeds immediately |
| Stage 3 (Content validation) | May ask user to review on failure | Auto-retries up to 3 times silently |
| Stage 4 (PPTX generation) | Confirmation before generation | Proceeds immediately |
Example programmatic invocation:
# From another skill or automation — skip all menus
python skills/research-and-present/research_and_present.py save-spec \
--topic "AI Trends 2026" --output ./artifacts/ --skip-menu
# Pipe pre-built spec to avoid fallback interview questions
echo '<yaml_spec>' | python skills/research-and-present/research_and_present.py save-spec \
--topic "AI Trends 2026" --output ./artifacts/ --skip-menu
LLM directive: When the /research-and-present pipeline is invoked as a
sub-delegation from another skill (e.g., another orchestrator calls this pipeline),
the calling LLM must include --skip-menu in all subprocess invocations to
prevent blocking on interactive prompts.
The Python utility exposes a helper to check the flag:
python -c "
import sys; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import is_skip_menu
print(is_skip_menu()) # True if --skip-menu was passed or AGENT4PPT_SKIP_MENU=1
"
When --output is not supplied, the output filename is derived automatically:
topic string is slugified — lowercased, special characters removed, spaces/hyphens collapsed to underscores..pptx extension is appended.--output if it is a directory).Examples:
| Topic | Default output |
|---|---|
"Deep Learning for NLP" | deep_learning_for_nlp.pptx |
"AI & Robotics: 2026 Trends!!" | ai_robotics_2026_trends.pptx |
"한국어 주제 테스트" | 한국어_주제_테스트.pptx |
"Machine-Learning 101" | machine_learning_101.pptx |
presentationIMPORTANT: This detection step MUST run BEFORE Stage 1. It determines whether the pipeline uses OMC's
/deep-interviewor the built-in fallback interview for spec gathering.
oh-my-claudecode (OMC) provides a /deep-interview skill that implements
Socratic questioning with mathematical ambiguity scoring. When available,
it produces higher-quality specs. When unavailable, the pipeline gracefully
falls back to a simpler built-in interview — the downstream stages work
identically either way.
When /research-and-present is invoked, perform detection in this order:
Run the OMC detection helper:
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import detect_omc
print(json.dumps(detect_omc()))
"
This returns JSON with the following fields:
| Field | Type | Description |
|---|---|---|
available | bool | true if the omc binary is found on PATH |
version | str | null | OMC version string (e.g. "4.7.5") |
has_deep_interview | bool | true if deep-interview SKILL.md found in OMC plugin dirs |
detection_method | str | "binary_found" or "binary_not_found" |
skill_path | str | null | Absolute path to deep-interview SKILL.md if found |
The Python helper checks these filesystem locations:
~/.claude/plugins/oh-my-claudecode/skills/deep-interview/SKILL.md~/.claude/plugins/marketplaces/omc/skills/deep-interview/SKILL.md~/.claude/plugins/cache/omc/oh-my-claudecode/<version>/skills/deep-interview/SKILL.mdIf Step 1 returns available: false or has_deep_interview: false,
perform a secondary check by examining the LLM's currently loaded skill list:
<system-reminder> blocks in the current conversation for any
mention of deep-interview in the available skills listdeep-interview appears as an available skill, override the Python
result and set has_deep_interview = trueThis secondary check handles edge cases where:
omc is not on PATHPersist the detection result as an intermediate artifact.
Pass llm_has_deep_interview=True if Step 2 found deep-interview in
the <system-reminder> skill list, otherwise False:
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import detect_omc_with_llm_override
result = detect_omc_with_llm_override(llm_has_deep_interview=<True|False>)
with open('<slug>_omc_detection.json', 'w') as f:
json.dump(result, f, indent=2)
"
Print the detection result for user transparency.
The llm_has_deep_interview parameter carries forward the Step 2 finding:
python -c "
import sys; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import print_omc_status
print_omc_status('<lang>', llm_has_deep_interview=<True|False>)
"
available | has_deep_interview | LLM skill-list has it | → Stage 1 Mode |
|---|---|---|---|
| ✅ | ✅ | — | OMC mode — invoke /deep-interview |
| ✅ | ❌ | ✅ | OMC mode — invoke /deep-interview |
| ✅ | ❌ | ❌ | Fallback mode — built-in interview |
| ❌ | — | ✅ | OMC mode — invoke /deep-interview |
| ❌ | — | ❌ | Fallback mode — built-in interview |
Rule: If has_deep_interview is true from any detection method, use OMC mode.
OMC mode:
[agent4ppt] OMC detected (version: 4.7.5)
[agent4ppt] deep-interview skill available ✓
Fallback mode (OMC installed, skill missing):
[agent4ppt] OMC detected (version: 4.7.5)
[agent4ppt] deep-interview skill not found — falling back to built-in interview
Fallback mode (OMC not installed):
[agent4ppt] OMC not installed — falling back to built-in interview
IMPORTANT: This detection step MUST run alongside OMC detection in pre-flight. It determines whether the pipeline is invoked via
/research-and-presentslash command (installed) or via direct Python subprocess (first-run).
When research-and-present is registered in the root SKILL.md frontmatter
skills: array, the LLM can delegate to it via the /research-and-present slash
command — the same way /parse-ppt-template and /generate-ppt are invoked.
When it is not registered (first-run, or manual execution), the pipeline must
be invoked via direct Python subprocess calls (python skills/research-and-present/research_and_present.py ...).
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import detect_installed_state
print(json.dumps(detect_installed_state()))
"
This returns JSON with:
| Field | Type | Description |
|---|---|---|
installed | bool | true if skill is in root SKILL.md skills: array |
skill_name | str | "research-and-present" |
trigger | str | null | Trigger string (e.g. "/research-and-present") if registered |
entry | str | null | Entry script path if registered |
registered_skills | list[str] | All skill names in the registry |
detection_method | str | "frontmatter_parsed", "file_not_found", or "parse_error" |
python -c "
import sys; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import print_installed_status
print_installed_status('<lang>')
"
Based on the detected state, get the appropriate invocation method:
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import get_invocation_command
cmd = get_invocation_command('<topic>', lang='<lang>')
print(json.dumps(cmd, indent=2))
"
installed | Invocation method | Example |
|---|---|---|
| ✅ | Slash command: /research-and-present <topic> | LLM calls skill via trigger |
| ❌ | Subprocess: python skills/research-and-present/research_and_present.py save-spec --topic <topic> | LLM orchestrates stages directly |
Installed (registered):
[agent4ppt] research-and-present skill registered ✓ (trigger: /research-and-present)
First-run (not registered):
[agent4ppt] research-and-present skill not registered — using direct subprocess invocation
[agent4ppt] Registered skills: parse-ppt-template, generate-ppt, revise-ppt
LLM directive: When the installed-state is false, the LLM must orchestrate
each pipeline stage by calling the Python subprocess commands documented in the
Pipeline Stages section below. When true, the LLM may delegate to the
/research-and-present slash command which handles orchestration internally.
Gather user intent via the method determined by OMC detection above.
When OMC's /deep-interview is detected, delegate spec gathering to it.
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import build_deep_interview_params
params = build_deep_interview_params('<topic>', lang='<lang>', output_dir='<output_dir>')
print(json.dumps(params, indent=2))
"
This returns a JSON dict with:
invocation: The formatted /deep-interview <topic> string to invokespec_path: Where to save the resulting spec artifactslug: Slugified topic for artifact naming/deep-interviewUse the invocation string from Step 1a to call the deep-interview skill:
/deep-interview <topic>
The deep-interview skill conducts Socratic questioning with mathematical ambiguity scoring. Wait for it to complete and capture the full output.
Parse and normalise the captured output into the canonical spec format:
python -c "
import sys, json, yaml; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import format_deep_interview_spec
spec = format_deep_interview_spec(
raw_output='''<captured_deep_interview_output>''',
topic='<topic>',
lang='<lang>',
)
print(yaml.dump(spec, default_flow_style=False, allow_unicode=True))
"
The format_deep_interview_spec function:
source: "omc_deep_interview" for provenance trackingtopic and slug from the original request take precedencePipe the normalised spec into the save-spec subcommand:
echo '<yaml_spec>' | python skills/research-and-present/research_and_present.py save-spec \
--topic "<topic>" --output "<output_dir>" --lang "<lang>"
This persists <slug>_spec.yaml as an intermediate artifact.
When OMC is not available, conduct a simpler built-in interview.
--skip-menuoverride: When the pipeline is running with--skip-menu(programmatic invocation), skip the interactive interview entirely. Instead, callgenerate_fallback_specwith empty answers{}— it will fill sensible defaults for all fields. Check with:python -c " import sys; sys.path.insert(0, 'skills/research-and-present') from research_and_present import should_skip_interview print(should_skip_interview()) "If
True, jump directly to step 3 below withanswers={}.
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import get_fallback_questions
qs = get_fallback_questions('<lang>')
for q in qs:
print(f'{q[\"key\"]}: {q[\"question\"]}')
"
Ask the user each question (one at a time):
audience: What is the target audience?key_points: What are the key points or sections to cover?slide_count: How many slides approximately?tone: What tone/style? (formal, casual, technical)extras: Any specific data or examples to include?Synthesize answers into a deep-interview-format spec via the Python helper:
python -c "
import sys, json, yaml; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import generate_fallback_spec, validate_fallback_spec
spec = generate_fallback_spec(
topic='<topic>',
answers={
'audience': '<answer1>',
'key_points': '<answer2>',
'slide_count': '<answer3>',
'tone': '<answer4>',
'extras': '<answer5>',
},
lang='<lang>',
output_dir='<output_dir>',
)
errors = validate_fallback_spec(spec)
if errors:
print('Spec validation errors:', errors, file=sys.stderr)
sys.exit(1)
print(yaml.dump(spec, default_flow_style=False, allow_unicode=True))
"
The spec YAML format (deep-interview format) is identical for both modes:
topic: "Deep Learning for NLP"
slug: "deep_learning_for_nlp"
lang: "en"
output: "deep_learning_for_nlp.pptx"
audience: "ML engineers and researchers"
key_points:
- "Transformer architecture"
- "BERT and GPT models"
- "Fine-tuning strategies"
slide_count: 10
tone: "technical"
extras: "Include benchmark comparisons"
source: "fallback_interview" # or "deep_interview" when OMC is used
Required fields: topic, slug, audience, key_points, slide_count, tone
python -c "
import sys, json, yaml; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import validate_fallback_spec
spec = yaml.safe_load(open('<spec_path>'))
errors = validate_fallback_spec(spec)
if errors:
for e in errors: print(f'ERROR: {e}', file=sys.stderr)
sys.exit(1)
print('Spec is valid')
"
python skills/research-and-present/research_and_present.py save-spec \
--topic "<topic>" --output "<output_dir>" --lang "<lang>"
Produces: <topic_slug>_spec.yaml
python skills/research-and-present/research_and_present.py save-spec \
--topic "Deep Learning for NLP" \
--output ./artifacts/
Parse the PPTX template to extract layout/placeholder structure. Failure halts immediately — no retry.
python skills/parse-ppt-template/parse_template.py <template.pptx> \
--output template_parsed.md
After parsing, the LLM must classify each layout in the parsed template as either content or structural. This classification drives Stage 3 content generation — the LLM generates body text only for content layouts, while structural layouts receive minimal/formulaic text.
Run the classifier on the parsed template output:
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import classify_layouts
result = classify_layouts('<parsed_template.md>')
print(json.dumps(result, indent=2))
"
| Criterion | → Classification | Examples |
|---|---|---|
| Layout name contains "Title Slide" (case-insensitive) | structural | Opening/closing title slides |
| Layout name contains "Section Header" (case-insensitive) | structural | Section divider slides |
| Layout name contains "Blank" (case-insensitive) | structural | Empty/spacer slides |
| Layout has no content placeholders (only auto: footer/date/slide#) | structural | Decoration-only layouts |
| Layout has body, object, picture, chart, table, or diagram placeholders | content | Main content slides |
| Layout name contains "Content" or "Comparison" (case-insensitive) | content | "Title and Content", "Two Content", "Comparison" |
| Default (none of the above match) | content | Unknown layouts treated as content |
title, center_title, or subtitle placeholders (no body/object)
are classified as structural — they carry headings but no authored content.{
"layouts": [
{
"idx": 0,
"name": "Title Slide",
"classification": "structural",
"reason": "name_match:Title Slide",
"content_placeholder_count": 2,
"has_body": false
},
{
"idx": 1,
"name": "Title and Content",
"classification": "content",
"reason": "name_match:Content",
"content_placeholder_count": 2,
"has_body": true
}
],
"summary": {
"total": 11,
"content": 6,
"structural": 5
}
}
Generate markdown content matching the template structure. Validate with retry (max 3 attempts).
Use the Python helper to generate an initial content markdown skeleton that maps spec fields to template placeholders:
python -c "
import sys, json; sys.path.insert(0, 'skills/research-and-present')
from research_and_present import map_spec_to_content
import yaml
spec = yaml.safe_load(open('<spec_path>'))
md = map_spec_to_content(
spec=spec,
template_path='<template.pptx>',
parsed_template_path='<parsed_template.md>',
)
print(md)
"
Mapping rules:
| Spec field | → Content markdown target |
|---|---|
topic (Goal) | # <topic> in the title placeholder (<!-- ph:N type:center_title --> or <!-- ph:N type:title -->) on the title slide |
key_points[i] (sections) | # <key_point> in the title placeholder + bullet points in the body placeholder (<!-- ph:N type:body -->) on content slides |
audience | Informs bullet point content (e.g. "Relevance for ") |
tone | Guides content style (formal, casual, technical) |
extras | Additional context woven into relevant sections |
The generated markdown follows the exact format expected by /generate-ppt:
template: field----separated slide sections<!-- ph:N type:TYPE --> placeholder annotationsThe LLM takes the skeleton from Step 3a and enriches each slide's bullet points with researched, substantive content. The skeleton provides structural guidance (which layout, which placeholders) while the LLM fills in actual presentation text.
python skills/research-and-present/research_and_present.py validate-content \
--spec artifacts/deep_learning_for_nlp_spec.yaml \
--content artifacts/deep_learning_for_nlp_content.md
If validation fails, retry content generation (Steps 3a-3c) up to 3 times.
Generate the final PPTX file. Output path defaults to <topic_slug>.pptx.
python skills/generate-ppt/generate_ppt.py content.md \
--output deep_learning_for_nlp.pptx
The research_and_present.py utility has exactly 3 subcommands:
| Subcommand | Purpose |
|---|---|
save-spec | Persist the presentation spec YAML to disk |
save-content | Persist the generated markdown content to disk |
validate-content | Validate markdown content against the spec |
All intermediate artifacts are preserved for debugging and resumption:
| Artifact | Path pattern | Stage |
|---|---|---|
| OMC detection result | <topic_slug>_omc_detection.json | Pre-flight |
| Spec YAML | <topic_slug>_spec.yaml | 1 |
| Parsed template | <topic_slug>_template.md | 2 |
| Content markdown | <topic_slug>_content.md | 3 |
| Final PPTX | <topic_slug>.pptx | 4 |
| Condition | Behaviour |
|---|---|
| Stage 2 (template parse) failure | Halt immediately, no retry |
| Stage 3 (content) validation failure | Retry up to 3 times |
| OMC unavailable | Graceful fallback to direct LLM questioning |
| Empty topic slug | Falls back to presentation.pptx |
| Code | Meaning |
|---|---|
0 | Success |
1 | Error (missing file, validation failure, etc.) |
pyyaml >= 6.0parse-ppt-template and generate-pptoh-my-claudecode for deep-interview stageCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub jinwangmok/agent4ppt --plugin agent4ppt