@ztffn/presentation-generator-plugin 1.3.3 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "presentation-generator",
3
- "description": "Generate complete graph-based presentations from natural language briefs and project documents. Includes a three-agent pipeline: content extraction, narrative design, and graph JSON compilation.",
4
- "version": "1.3.1",
3
+ "description": "Generate complete graph-based presentations from natural language briefs and project documents. Pipeline: content extraction, narrative design, deterministic graph generation, and visual styling.",
4
+ "version": "1.3.6",
5
5
  "author": {
6
6
  "name": "Huma"
7
7
  },
8
8
  "homepage": "https://github.com/huma/humashowcase",
9
- "keywords": ["presentation", "slides", "react-flow", "graph", "pitch deck"]
9
+ "keywords": ["presentation", "slides", "react-flow", "graph", "pitch deck"],
10
+ "hooks": "./hooks/hooks.json"
10
11
  }
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Presentation Generator Plugin
2
2
 
3
- Generates complete graph-based presentations from a natural language brief and optional project documents. Uses a three-agent pipeline: content extraction → narrative design → graph JSON compilation.
3
+ Generates complete graph-based presentations from a natural language brief and optional project documents. Uses a two-agent + script pipeline: content extraction → narrative design → deterministic graph generation → visual styling.
4
4
 
5
5
  ## What it produces
6
6
 
7
- A `_temp/presentation-draft.json` file you import directly into the graph editor at `/present/plan` via **New presentation → Import JSON**. The result is a fully navigable presentation with correct edge wiring, positioned nodes, and visual treatments applied.
7
+ A `presentations/{slug}/{slug}.json` file you import directly into the graph editor at `/present/plan` via **New presentation → Import JSON**. The result is a fully navigable presentation with correct edge wiring, positioned nodes, and visual treatments applied.
8
8
 
9
9
  ## Install
10
10
 
@@ -81,22 +81,25 @@ User Brief + Documents
81
81
 
82
82
  [3] User Approval → confirm or revise structure
83
83
 
84
- [4] Design & JSON _temp/presentation-draft.json
84
+ [4] Graph Generation presentations/{slug}/{slug}.json (script)
85
85
 
86
- [5] Delivery import instructions + media checklist
86
+ [5] Visual Styling styled JSON (agent)
87
+
88
+ [6] Delivery → import instructions + media checklist
87
89
  ```
88
90
 
89
91
  You review and approve the structure before any JSON is generated. If the structure needs changes, describe them and Claude revises before proceeding.
90
92
 
91
- ## Agents
93
+ ## Agents and scripts
92
94
 
93
- | Agent | Role |
94
- |---|---|
95
- | `presentation-content` | Reads source documents, extracts a structured content brief |
96
- | `presentation-narrative` | Selects a narrative framework, designs spine and drill-downs, writes slide content |
97
- | `presentation-design` | Translates the approved outline into valid graph JSON |
95
+ | Component | Type | Role |
96
+ |---|---|---|
97
+ | `presentation-content` | agent | Reads source documents, extracts a structured content brief |
98
+ | `presentation-narrative` | agent | Selects a narrative framework, designs spine and drill-downs, writes slide content |
99
+ | `outline_to_graph.py` | script | Deterministic converter: parses outline markdown, emits valid ReactFlow graph JSON |
100
+ | `presentation-style` | agent | Applies visual treatments (layout, backgrounds, charts, branding) to the valid JSON |
98
101
 
99
- Each agent loads only the knowledge it needs — narrative agents never see layout decisions; design agents never read source documents.
102
+ Content and narrative agents never see layout decisions; the styling agent never reads source documents. The script handles all structural correctness (node wrappers, edge wiring, positions) mechanically.
100
103
 
101
104
  ## Output files
102
105
 
@@ -105,14 +108,14 @@ Each agent loads only the knowledge it needs — narrative agents never see layo
105
108
  | `_temp/presentation-content-brief.json` | Content agent | Structured brief passed to narrative agent |
106
109
  | `_temp/presentation-outline.md` | Narrative agent | Human-readable structure for user approval |
107
110
  | `_temp/presentation-plan.md` | Orchestrator | Folder-tree view shown during approval step |
108
- | `_temp/presentation-draft.json` | Design agent | Final graph JSON for import |
111
+ | `presentations/{slug}/{slug}.json` | Script + styling agent | Final graph JSON for import |
109
112
 
110
113
  ## After import
111
114
 
112
115
  1. Open `/present/plan` in the app
113
116
  2. Click **New presentation**
114
117
  3. Choose **Import JSON**
115
- 4. Select `_temp/presentation-draft.json`
118
+ 4. Select `presentations/{slug}/{slug}.json`
116
119
 
117
120
  If any slides require video backgrounds, Claude lists them in the delivery summary with upload instructions.
118
121
 
@@ -142,7 +145,7 @@ presentation-generator-plugin/
142
145
  ├── agents/
143
146
  │ ├── presentation-content.md
144
147
  │ ├── presentation-narrative.md
145
- │ └── presentation-design.md
148
+ │ └── presentation-style.md
146
149
  ├── skills/
147
150
  │ ├── presentation-generator/
148
151
  │ │ ├── SKILL.md # Orchestrator skill
@@ -151,7 +154,14 @@ presentation-generator-plugin/
151
154
  │ ├── frameworks/SKILL.md # Narrative frameworks
152
155
  │ ├── slide-content/SKILL.md # Slide writing quality
153
156
  │ ├── graph-topology/SKILL.md # Spine/drill-down structure
154
- └── graph-json-spec/SKILL.md # Complete graph JSON spec (design agent)
155
- └── scripts/
156
- └── validate_draft.py
157
+ ├── graph-json-spec/SKILL.md # Node schema, edge wiring, positioning grid
158
+ └── slide-recipes/SKILL.md # Design decisions, visual intent, slide recipes
159
+ ├── scripts/
160
+ │ ├── outline_to_graph.py # Deterministic outline-to-graph converter
161
+ │ └── validate_draft.py # Validates generated JSON against renderer types
162
+ └── hooks/
163
+ ├── hooks.json
164
+ ├── enforce-style-schema.sh # PreToolUse: injects schema rules into styling agent
165
+ ├── pre-validate-presentation-json.sh # PreToolUse: blocks invalid JSON writes
166
+ └── validate-presentation-json.sh # PostToolUse: validates after Write/Edit
157
167
  ```
@@ -14,8 +14,7 @@ You are a content extraction specialist. Your job is to read source documents an
14
14
 
15
15
  ### Step 1 — Load your skill files
16
16
 
17
- Read this file before doing anything else:
18
- - Find and read the file matching `**/presentation-generator/skills/content-signals/SKILL.md`
17
+ Read the skill file at the explicit path provided by the orchestrator in your prompt. The path will look like `{PLUGIN_ROOT}/skills/content-signals/SKILL.md`.
19
18
 
20
19
  This file defines the content brief schema, signal priorities, validation rules, and strong/weak examples. You must follow it exactly.
21
20
 
@@ -16,10 +16,10 @@ You are a narrative design specialist. Your job is to take a structured content
16
16
 
17
17
  ### Step 1 — Load your skill files
18
18
 
19
- Read these three files before doing anything else:
20
- - Find and read the file matching `**/presentation-generator/skills/frameworks/SKILL.md`
21
- - Find and read the file matching `**/presentation-generator/skills/slide-content/SKILL.md`
22
- - Find and read the file matching `**/presentation-generator/skills/graph-topology/SKILL.md`
19
+ Read the skill files at the explicit paths provided by the orchestrator in your prompt. The paths will look like:
20
+ - `{PLUGIN_ROOT}/skills/frameworks/SKILL.md`
21
+ - `{PLUGIN_ROOT}/skills/slide-content/SKILL.md`
22
+ - `{PLUGIN_ROOT}/skills/graph-topology/SKILL.md`
23
23
 
24
24
  These files define framework selection logic, slide content quality rules, visual intent annotations, and topology patterns. You must follow them exactly.
25
25
 
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: presentation-style
3
+ description: Applies visual treatments to structurally valid presentation JSON. Rewrites content for slide readability and sets layout, centering, backgrounds, charts, and branding per slide. Edits only data fields — never touches node wrappers, edges, or positions.
4
+ tools: Read, Edit, Bash
5
+ skills:
6
+ - presentation-generator:slide-recipes
7
+ - presentation-generator:graph-json-spec
8
+ ---
9
+
10
+ # Visual Styling Agent
11
+
12
+ You are a presentation design specialist. You receive structurally valid graph JSON (correct nodes, edges, positions) and transform it into a polished, presentation-ready deck by rewriting content for slide readability and applying visual treatments slide-by-slide.
13
+
14
+ The script that produced this JSON extracted content verbatim from the outline. Your job is to make each slide *work as a slide* — concise, scannable, visually intentional.
15
+
16
+ ## Process — follow these steps in order
17
+
18
+ ### Step 1 — Load your skill files
19
+
20
+ Read the skill files at the explicit paths provided by the orchestrator in your prompt. The paths will look like:
21
+ - `{PLUGIN_ROOT}/skills/slide-recipes/SKILL.md`
22
+ - `{PLUGIN_ROOT}/skills/graph-json-spec/SKILL.md`
23
+
24
+ These define the visual intent mapping, slide recipes, allowed data fields, and content markdown features.
25
+
26
+ ### Step 2 — Read the source materials
27
+
28
+ 1. Read the presentation JSON file (path provided in your prompt)
29
+ 2. Read the outline markdown at `_temp/presentation-outline.md` for full context on each slide's purpose, audience, and narrative role
30
+
31
+ ### Step 3 — Classify each slide
32
+
33
+ Walk through every node and assign a visual intent. Use the outline's `**Slide type:**` annotation if present. Otherwise infer:
34
+
35
+ | Position / Content Signal | Visual Intent | Treatment |
36
+ |---|---|---|
37
+ | First spine node | `bookend` | Centered, brand font, branding, optional background image |
38
+ | Last spine node | `bookend` | Same — this is the call to action |
39
+ | Spine node introducing a major section | `chapter-opener` | Background image with overlay, light text, minimal bullets (max 3) |
40
+ | Spine node with a single bold claim | `impact` | Centered, no bullets, whitespace-forward |
41
+ | Spine or drill-down with 4+ bullet points | `workhorse` | Left-aligned, `centered: false`, single or two-column |
42
+ | Slide with comparison data (before/after, us/them, old/new) | `evidence` | Two-column layout, split on `---` |
43
+ | Slide with numeric data suitable for charting | `evidence` | Inline chart (`[chart:name]` in content) or full-viewport `type: "chart"` |
44
+ | Slide after 2+ consecutive bullet-heavy slides | `breathing-room` | Centered, background image, minimal text |
45
+ | Drill-down with detailed specs or numbers | `workhorse` or `evidence` | Depends on whether data is comparative or sequential |
46
+
47
+ ### Step 4 — Rewrite content for slide readability
48
+
49
+ The script extracted outline content verbatim. Outline content is written for reading, not presenting. For each slide, rewrite `data.content` to work on screen:
50
+
51
+ **Content rules:**
52
+ - Lead with a `## Heading` that states the insight or claim (not a topic label)
53
+ - Weak: `## Revenue Data` — Strong: `## Revenue grew 40% after the pricing change`
54
+ - Maximum 5-6 bullet points per single-column slide. If more, cut to the strongest or split into two-column.
55
+ - Each bullet should be one line. Remove sub-bullets where possible — flatten or promote.
56
+ - Remove transitional sentences ("Let's look at...", "Now that we understand...") — those belong in speaker notes, not on screen.
57
+ - Remove redundancy between the `## Heading` and bullets. The heading states the claim; bullets provide evidence.
58
+ - For two-column slides, ensure the content has a `---` delimiter separating left and right columns.
59
+ - For `impact` slides, content should be 1-2 sentences maximum. Let whitespace carry the message.
60
+ - For `bookend` slides, content is a single compelling sentence or tagline.
61
+
62
+ **Do NOT change:**
63
+ - `data.label` — the slide title in the graph editor
64
+ - `data.notes` — speaker notes are already correct from the outline
65
+ - `data.transitionToNext` — transition cues are already correct
66
+
67
+ ### Step 5 — Apply visual fields
68
+
69
+ For each slide, set the appropriate data fields based on visual intent:
70
+
71
+ #### Bookend (cover / CTA)
72
+ ```json
73
+ {
74
+ "centered": true,
75
+ "brandFont": true,
76
+ "showBranding": true,
77
+ "brandingText": "[company.domain from outline context]"
78
+ }
79
+ ```
80
+ Optionally add a background image for atmosphere. If so, set `backgroundImageOverlay: true` and `lightText: true`.
81
+
82
+ #### Chapter Opener
83
+ ```json
84
+ {
85
+ "centered": false,
86
+ "backgroundImage": "https://images.unsplash.com/photo-XXXXX?w=1920&q=80",
87
+ "backgroundImageFit": "cover",
88
+ "backgroundImageOverlay": true,
89
+ "lightText": true
90
+ }
91
+ ```
92
+ Choose an Unsplash image that evokes the section's theme (energy, technology, finance, nature, industry). Keep text to max 3 bullet points.
93
+
94
+ #### Impact
95
+ ```json
96
+ {
97
+ "centered": true,
98
+ "brandFont": false
99
+ }
100
+ ```
101
+ Optionally add a dark `style.backgroundColor` via the node's `style` field. If dark background, set `lightText: true`.
102
+
103
+ #### Workhorse
104
+ ```json
105
+ {
106
+ "centered": false,
107
+ "layout": "single"
108
+ }
109
+ ```
110
+ This is the default. Most content slides are workhorses. Only change from defaults when the content warrants it.
111
+
112
+ #### Evidence (two-column)
113
+ ```json
114
+ {
115
+ "centered": false,
116
+ "layout": "two-column"
117
+ }
118
+ ```
119
+ Rewrite content with `---` delimiter. Left column = context/explanation, right column = data/chart/comparison.
120
+
121
+ #### Evidence (chart)
122
+ For inline charts, add a `charts` record and reference with `[chart:name]` in content:
123
+ ```json
124
+ {
125
+ "charts": {
126
+ "comparison": {
127
+ "chartType": "bar",
128
+ "data": [...],
129
+ "config": { "xKey": "category", "yKeys": ["value1", "value2"], "showGrid": true }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+ For full-viewport charts, set `type: "chart"` and add the `chart` field.
135
+
136
+ **When to create charts from outline data:**
137
+ - The outline contains a clear data table or comparison with numbers → create a chart
138
+ - Energy density comparisons, cost comparisons, revenue projections → `bar` or `radar`
139
+ - Time-series data (quarterly, yearly) → `line` or `area`
140
+ - Market share or part-of-whole → `pie`
141
+ - Multi-axis comparison (us vs. them across categories) → `radar`
142
+
143
+ #### Breathing Room
144
+ ```json
145
+ {
146
+ "centered": true,
147
+ "backgroundImage": "https://images.unsplash.com/photo-XXXXX?w=1920&q=80",
148
+ "backgroundImageFit": "cover",
149
+ "backgroundImageOverlay": true,
150
+ "lightText": true
151
+ }
152
+ ```
153
+ Minimal text. This is a visual pause to reset audience attention.
154
+
155
+ ### Step 6 — Topic badge formatting
156
+
157
+ Ensure `data.topic` follows the convention:
158
+ - Spine nodes: `"01 / Section Name"`, `"02 / Section Name"`, etc.
159
+ - Drill-down nodes: `"02.1 / Sub-topic"`, `"02.2 / Sub-topic"`, etc.
160
+
161
+ The script sets these, but verify formatting is clean and consistent.
162
+
163
+ ### Step 7 — Run the validator
164
+
165
+ After all edits, run the validator:
166
+
167
+ ```bash
168
+ python3 "<plugin-root>/scripts/validate_draft.py" <path-to-json>
169
+ ```
170
+
171
+ The orchestrator provides the plugin root path in your prompt. Use that path for the validator.
172
+
173
+ Fix any errors and repeat until the validator prints `OK Presentation JSON is valid`.
174
+
175
+ ### Step 8 — Report to orchestrator
176
+
177
+ Your completion message must include:
178
+ - Exact validator terminal output
179
+ - Number of slides with visual treatments applied
180
+ - Summary of chart slides created (if any)
181
+ - List of any slides with background image/video placeholders requiring manual upload
182
+
183
+ ## Structural constraints — NEVER violate these
184
+
185
+ - **NEVER modify** `id`, `type` (on the node wrapper), `position`, `style`, or `measured` on any node
186
+ - **NEVER modify** any edge — edges are structurally fixed
187
+ - **NEVER add or remove** nodes or edges
188
+ - **NEVER change** `data.label` — this is the slide title shown in the graph editor
189
+ - **NEVER change** `data.notes` — speaker notes are set by the outline
190
+ - **NEVER change** `data.transitionToNext` — transition cues are set by the outline
191
+ - Only use field names from the `ALLOWED_DATA_FIELDS` set in the validator
192
+ - For background images, use Unsplash URLs with `?w=1920&q=80`
193
+ - For video, use placeholder strings: `"PLACEHOLDER: [description]"`
194
+ - When using `two-column` layout, the content MUST have a `---` delimiter
195
+ - When setting `lightText: true`, the background MUST be dark
196
+ - When setting `backgroundImageOverlay: true`, also set `lightText: true`
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+ # PreToolUse hook for Task tool — injects structural constraints
3
+ # into styling agent prompts, preventing it from breaking valid JSON.
4
+ # Scope: only fires when subagent_type contains "style".
5
+ # Primary responsibility: ensure the styling agent never touches structure.
6
+
7
+ INPUT=$(cat)
8
+ SUBAGENT=$(jq -r '.tool_input.subagent_type // empty' <<< "$INPUT")
9
+
10
+ # Only intercept styling agent calls
11
+ if [[ "$SUBAGENT" != *style* ]]; then
12
+ exit 0
13
+ fi
14
+
15
+ ORIGINAL_PROMPT=$(jq -r '.tool_input.prompt // empty' <<< "$INPUT")
16
+
17
+ # Hard-coded constraints — cannot be paraphrased away
18
+ PREAMBLE="MANDATORY CONSTRAINTS — these rules override anything else in this prompt:
19
+
20
+ STRUCTURAL FIELDS YOU MUST NEVER MODIFY:
21
+ - Node wrapper fields: id, type (\"huma\"), position, style, measured
22
+ - Any edge object — edges are structurally fixed
23
+ - Do NOT add or remove nodes or edges
24
+ - Do NOT change data.label — this is the slide title in the graph editor
25
+ - Do NOT change data.notes — speaker notes are set by the outline
26
+ - Do NOT change data.transitionToNext — transition cues are set by the outline
27
+
28
+ FIELDS YOU MAY MODIFY (inside data object only):
29
+ - content — rewrite for slide readability (concise, scannable, claim-first headings)
30
+ - centered, layout, lightText, brandFont, showBranding, brandingText
31
+ - backgroundImage, backgroundImageFit, backgroundImageOverlay
32
+ - backgroundVideo, backgroundVideoFit, backgroundVideoLoop
33
+ - type (data.type: \"content\" | \"r3f\" | \"chart\" | \"custom\")
34
+ - chart, charts (for adding chart visualizations)
35
+ - inlineVideoControls, inlineVideoAutoplay, inlineVideoLoop
36
+
37
+ FIELD NAMES in data object — use EXACTLY:
38
+ - \"label\" for slide title (NOT title, headline, heading)
39
+ - \"content\" for markdown body (NOT body, text, bullets)
40
+ - \"notes\" for speaker notes (NOT speakerNotes, speakerNote)
41
+ - \"topic\" for section badge
42
+ Valid data.layout: \"single\" | \"two-column\"
43
+
44
+ TWO-COLUMN RULE: When setting layout:\"two-column\", the content MUST contain a --- delimiter.
45
+ LIGHT TEXT RULE: When setting lightText:true, the background MUST be dark.
46
+
47
+ ---
48
+
49
+ "
50
+
51
+ SUFFIX="
52
+
53
+ ---
54
+
55
+ MANDATORY FINAL STEP: After all edits, run the validator using the plugin root path from your prompt:
56
+ python3 \"<plugin-root>/scripts/validate_draft.py\" <path-to-json>
57
+ Fix ALL errors until it prints 'OK Presentation JSON is valid'.
58
+ Your report MUST include the exact validator terminal output."
59
+
60
+ # Build the modified prompt
61
+ MODIFIED_PROMPT="${PREAMBLE}${ORIGINAL_PROMPT}${SUFFIX}"
62
+
63
+ # Return updatedInput with the augmented prompt
64
+ jq -n --arg prompt "$MODIFIED_PROMPT" '{
65
+ hookSpecificOutput: {
66
+ hookEventName: "PreToolUse",
67
+ permissionDecision: "allow",
68
+ updatedInput: { prompt: $prompt }
69
+ }
70
+ }'
@@ -0,0 +1,49 @@
1
+ {
2
+ "description": "Validates presentation JSON and enforces structural constraints on the styling agent.",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "Task",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-style-schema.sh",
11
+ "timeout": 10
12
+ }
13
+ ]
14
+ },
15
+ {
16
+ "matcher": "Write",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/pre-validate-presentation-json.sh",
21
+ "timeout": 30
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "PostToolUse": [
27
+ {
28
+ "matcher": "Write",
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate-presentation-json.sh",
33
+ "timeout": 30
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "matcher": "Edit",
39
+ "hooks": [
40
+ {
41
+ "type": "command",
42
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate-presentation-json.sh",
43
+ "timeout": 30
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ }
@@ -0,0 +1,62 @@
1
+ #!/bin/bash
2
+ # PreToolUse hook for Write — validates presentation JSON BEFORE writing.
3
+ # Denies the write if validation fails, forcing the agent to fix errors first.
4
+ # Scope: .json files in presentations/ or _temp/ directories.
5
+ # Primary responsibility: prevent invalid JSON from reaching disk.
6
+
7
+ INPUT=$(cat)
8
+ FILE=$(jq -r '.tool_input.file_path // empty' <<< "$INPUT")
9
+
10
+ # Skip non-JSON files
11
+ [[ "$FILE" != *.json ]] && exit 0
12
+
13
+ # Only validate presentation JSON
14
+ case "$FILE" in
15
+ *presentations/*.json|*_temp/presentation-draft.json|*_temp/presentation*.json) ;;
16
+ *) exit 0 ;;
17
+ esac
18
+
19
+ CONTENT=$(jq -r '.tool_input.content // empty' <<< "$INPUT")
20
+ [[ -z "$CONTENT" ]] && exit 0
21
+
22
+ # Write to temp file for validation
23
+ TMPFILE=$(mktemp /tmp/validate-pres-XXXXXX.json)
24
+ echo "$CONTENT" > "$TMPFILE"
25
+
26
+ # Find validator
27
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
28
+ PLUGIN_DIR="$(cd "$HOOK_DIR/.." && pwd)"
29
+ VALIDATE="$PLUGIN_DIR/scripts/validate_draft.py"
30
+ if [ ! -f "$VALIDATE" ]; then
31
+ VALIDATE=$(find -L .claude ~/.claude -path "*/presentation-generator*/scripts/validate_draft.py" 2>/dev/null | head -1)
32
+ fi
33
+
34
+ # If no validator found, allow the write
35
+ if [ -z "$VALIDATE" ] || [ ! -f "$VALIDATE" ]; then
36
+ rm -f "$TMPFILE"
37
+ exit 0
38
+ fi
39
+
40
+ RESULT=$(python3 "$VALIDATE" "$TMPFILE" 2>&1)
41
+ EXIT_CODE=$?
42
+ rm -f "$TMPFILE"
43
+
44
+ if [ $EXIT_CODE -ne 0 ]; then
45
+ REASON="Presentation JSON validation FAILED. Fix ALL errors before writing.
46
+
47
+ ${RESULT}
48
+
49
+ Reminders: label not title, content not body, notes not speakerNotes, s-right not right, t-left not left. All fields inside data object."
50
+
51
+ jq -n --arg reason "$REASON" '{
52
+ hookSpecificOutput: {
53
+ hookEventName: "PreToolUse",
54
+ permissionDecision: "deny",
55
+ permissionDecisionReason: $reason
56
+ }
57
+ }'
58
+ exit 0
59
+ fi
60
+
61
+ # Valid — allow the write
62
+ exit 0
@@ -0,0 +1,64 @@
1
+ #!/bin/bash
2
+ # validate-presentation-json.sh — PostToolUse hook for Write/Edit
3
+ # Runs validate_draft.py against presentation JSON after every write.
4
+ # Returns validation errors as additionalContext so the agent must fix them.
5
+ # Only triggers on .json files in presentations/ or _temp/ directories.
6
+
7
+ INPUT=$(cat)
8
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
9
+
10
+ # Skip if no file path or not a JSON file
11
+ if [ -z "$FILE" ] || [[ "$FILE" != *.json ]]; then
12
+ exit 0
13
+ fi
14
+
15
+ # Only validate presentation JSON files
16
+ case "$FILE" in
17
+ *presentations/*.json|*_temp/presentation-draft.json|*_temp/presentation*.json)
18
+ ;;
19
+ *)
20
+ exit 0
21
+ ;;
22
+ esac
23
+
24
+ # Find the validator script — try plugin-relative first, then search
25
+ HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
26
+ PLUGIN_DIR="$(cd "$HOOK_DIR/.." && pwd)"
27
+ VALIDATE="$PLUGIN_DIR/scripts/validate_draft.py"
28
+
29
+ if [ ! -f "$VALIDATE" ]; then
30
+ VALIDATE=$(find -L .claude ~/.claude -path "*/presentation-generator*/scripts/validate_draft.py" 2>/dev/null | head -1)
31
+ fi
32
+
33
+ if [ -z "$VALIDATE" ] || [ ! -f "$VALIDATE" ]; then
34
+ exit 0
35
+ fi
36
+
37
+ # Run validator
38
+ RESULT=$(python3 "$VALIDATE" "$FILE" 2>&1)
39
+ EXIT_CODE=$?
40
+
41
+ if [ $EXIT_CODE -ne 0 ]; then
42
+ CONTEXT="VALIDATION FAILED for $FILE. You MUST fix every error and rewrite the file. The hook will re-validate automatically.
43
+
44
+ Validator output:
45
+ $RESULT
46
+
47
+ Reminders: use 'label' not 'title', 'content' not 'body'/'text', 'notes' not 'speakerNotes', 's-right' not 'right', 't-left' not 'left', 240px horizontal spacing, 150px vertical spacing, style and measured must be {width:180,height:70}."
48
+
49
+ # Properly escape for JSON using python
50
+ ESCAPED_CONTEXT=$(echo "$CONTEXT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
51
+
52
+ cat <<EOF
53
+ {
54
+ "decision": "block",
55
+ "reason": "Presentation JSON validation failed. Fix all errors and rewrite the file.",
56
+ "hookSpecificOutput": {
57
+ "hookEventName": "PostToolUse",
58
+ "additionalContext": $ESCAPED_CONTEXT
59
+ }
60
+ }
61
+ EOF
62
+ else
63
+ exit 0
64
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ztffn/presentation-generator-plugin",
3
- "version": "1.3.3",
3
+ "version": "1.3.6",
4
4
  "description": "Claude Code plugin for generating graph-based presentations",
5
5
  "bin": {
6
6
  "presentation-generator-plugin": "bin/index.js"