一键导入
speaker-notes-writer
Generate timed, transition-cued speaker notes calibrated to talk duration and audience profile, with lightweight preference gathering.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Generate timed, transition-cued speaker notes calibrated to talk duration and audience profile, with lightweight preference gathering.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Produce a labeled figure with PERFECT text — render a label-free image locally (or take an external image) then overlay leader lines and typeset labels programmatically from vision-derived anchor coordinates. Text is correct by construction; only pointer placement is reviewed.
Ask which image models fit a set of tasks — get evidence-based recommendations with per-image costs, which external services to pay for, and which local models to install (with disk/RAM/licence guidance). Reads the live model catalog and probes what is actually available on this machine.
Check mflux (MLX) availability, per-family runtime presence, cached weights, and report readiness status.
Top-level image orchestrator. Routes all slide image generation to the appropriate skill (jack-tar-ollama:image, jack-tar-ollama:icon, jack-tar-ollama:pattern, jack-tar-ollama:diagram, jack-tar-cloud:image, jack-tar-cloud:icon, render_chart). Produces ImageManifest and ChartManifest. Also reads strategy-map.json to determine per-slide rendering approach (full_render, backdrop_render, composed).
Meta-verify — discover all jack-tar engine plugins, call each verify, report aggregate pipeline capability and discipline-hook readiness.
Generate an image locally on Apple Silicon via the mflux CLI (MLX). Flag-compatible with jack-tar-ollama's /image — a $0 local tier, no API keys needed.
| name | speaker-notes-writer |
| description | Generate timed, transition-cued speaker notes calibrated to talk duration and audience profile, with lightweight preference gathering. |
| argument-hint | ["--deck-dir PATH"] |
| allowed-tools | Bash(python *), Read, Glob, Write |
Generate per-slide speaker notes with timing markers, cumulative time marks, and interaction cues.
./tmp/deck/talk-brief.json must exist./tmp/deck/outline.json must exist (produced by narrative-architect)Invoked by the Deck Conductor after narrative-architect. Can also be invoked directly:
/speaker-notes-writer
/speaker-notes-writer --deck-dir ./tmp/deck
Run this first — the steps below reference $PLUGIN_ROOT.
PLUGIN_ROOT=$(python3 -c "
from pathlib import Path
import sys, os
if os.environ.get('JACK_TAR_DECKHAND_ROOT'):
print(os.environ['JACK_TAR_DECKHAND_ROOT']); sys.exit()
home = Path.home()
for base in [home / '.claude' / 'plugins' / 'cache']:
for p in base.rglob('jack-tar-deckhand/.claude-plugin/plugin.json'):
print(str(p.parent.parent)); sys.exit()
dev = Path.cwd() / 'plugins' / 'jack-tar-deckhand'
if dev.exists():
print(str(dev)); sys.exit()
print('NOT_FOUND')
" 2>/dev/null)
if [ -z "$PLUGIN_ROOT" ] || [ "$PLUGIN_ROOT" = "NOT_FOUND" ]; then echo "ERROR: jack-tar-deckhand not found" && exit 1; fi
Before gathering preferences, check if the TalkBrief provides external notes:
PYTHONPATH="$PLUGIN_ROOT" python3 -c "
import json
brief = json.load(open('./tmp/deck/talk-brief.json'))
path = brief.get('preferences', {}).get('speaker_notes_path')
include = brief.get('preferences', {}).get('include_speaker_notes', True)
if not include:
print('SKIP: include_speaker_notes is false')
elif path:
print(f'IMPORT: {path}')
else:
print('GENERATE')
"
If SKIP: Write an empty notes file and exit:
echo '{"notes": []}' > ./tmp/deck/speaker-notes.json
If IMPORT: Run the import/enrich flow (see Step 1a below). Do NOT gather preferences — they don't apply to imported notes.
If GENERATE: Proceed to Step 1 (preferences gathering) as normal.
When the TalkBrief provides preferences.speaker_notes_path:
PYTHONPATH="$PLUGIN_ROOT" python3 -c "
import json
from src.notes_parser import parse_notes_file, match_notes_to_outline, build_timing_markers
brief = json.load(open('./tmp/deck/talk-brief.json'))
outline = json.load(open('./tmp/deck/outline.json'))
path = brief['preferences']['speaker_notes_path']
blocks = parse_notes_file(path)
matched, warnings = match_notes_to_outline(blocks, outline)
markers = build_timing_markers(matched)
print(f'Matched {len(matched)} slides, {len(warnings)} warnings')
for w in warnings:
print(f' WARNING: {w}')
print('MATCHED_SLIDES:', json.dumps(list(matched.keys())))
# Save intermediate results for enrichment
json.dump({'matched': {str(k): v for k, v in matched.items()}, 'markers': {str(k): v for k, v in markers.items()}}, open('./tmp/deck/_imported_notes.json', 'w'))
"
Report any warnings to the Speaker.
Enrich the matched notes: For each matched slide, you have the speaker's text and computed timing. Now generate contextual cues (transition, pause, emphasis, audience_interaction) using the same reasoning you would for generated notes. The speaker's text is the input — do not modify it, only add cues.
Fill gaps: Check which slides in the outline have no imported notes. For those slides, generate notes from scratch using the same approach as Step 2 (autonomous generation), using default preferences (bullet points, moderate interaction, comfortable detail).
Assemble the complete SpeakerNotes contract (imported + generated gap-fills) and proceed to Step 3 (validate and write).
Before generating notes, ask the Speaker three quick questions (if the answers aren't obvious from the TalkBrief):
"Before I write your speaker notes, three quick questions:
- Note format: Do you prefer bullet points/keywords or fuller sentences?
- Audience interaction: Any preference? (show of hands, rhetorical questions, polls, none)
- Your experience level: First-time presenter, comfortable, or seasoned?
(Or just say 'defaults' and I'll use: bullet points, match-to-tone interaction, moderate detail)"
Defaults if Speaker says 'defaults' or skips:
Read ./tmp/deck/talk-brief.json and ./tmp/deck/outline.json.
For each slide in the outline, produce a note with:
text -- The speaking content for this slide:
estimated_seconds -- Time allocation for this slide:
target_pace_wpm (default 130) and word countduration_minutes from TalkBrieftiming_marker -- Cumulative time mark (e.g., "~5:30", "~12:00"):
cues -- Interaction cues appropriate to the slide:
transition: How to bridge from the previous slide ("Building on that insight...")pause: Deliberate silence for impact ("[pause 2 seconds for the number to land]")audience_interaction: Engage the audience, matching the Speaker's preferenceemphasis: Key point to stress ("This is the number your CFO cares about")demo: Live demonstration beat ("Switch to terminal, run the command")build_animation: Timed reveal ("Click to reveal the second column")What you decide autonomously:
Validate the notes before writing:
PYTHONPATH="$PLUGIN_ROOT" python3 -c "
import json
from src.content_validation import validate_notes_schema, check_timing_total, check_notes_slide_references
notes = json.load(open('./tmp/deck/speaker-notes.json'))
outline = json.load(open('./tmp/deck/outline.json'))
brief = json.load(open('./tmp/deck/talk-brief.json'))
schema_errors = validate_notes_schema(notes)
timing_issues = check_timing_total(notes, brief['duration_minutes'])
ref_issues = check_notes_slide_references(notes, outline)
for e in schema_errors: print(f'SCHEMA: {e}')
for e in timing_issues: print(f'TIMING: {e}')
for e in ref_issues: print(f'REF: {e}')
if not schema_errors and not timing_issues and not ref_issues:
print('SpeakerNotes validates successfully')
"
Write the validated SpeakerNotes to ./tmp/deck/speaker-notes.json.
./tmp/deck/speaker-notes.json (SpeakerNotes contract)