@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.
- package/.claude-plugin/plugin.json +4 -3
- package/README.md +27 -17
- package/agents/presentation-content.md +1 -2
- package/agents/presentation-narrative.md +4 -4
- package/agents/presentation-style.md +196 -0
- package/hooks/enforce-style-schema.sh +70 -0
- package/hooks/hooks.json +49 -0
- package/hooks/pre-validate-presentation-json.sh +62 -0
- package/hooks/validate-presentation-json.sh +64 -0
- package/package.json +1 -1
- package/scripts/outline_to_graph.py +413 -0
- package/scripts/validate_draft.py +2 -1
- package/skills/graph-json-spec/SKILL.md +73 -620
- package/skills/presentation-generator/SKILL.md +82 -35
- package/skills/presentation-generator/presentation-guide.md +2 -2
- package/skills/slide-recipes/SKILL.md +326 -0
- package/agents/presentation-design.md +0 -187
|
@@ -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.
|
|
4
|
-
"version": "1.3.
|
|
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
|
|
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 `
|
|
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]
|
|
84
|
+
[4] Graph Generation → presentations/{slug}/{slug}.json (script)
|
|
85
85
|
↓
|
|
86
|
-
[5]
|
|
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
|
-
|
|
|
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
|
-
| `
|
|
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
|
-
|
|
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
|
-
| `
|
|
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 `
|
|
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-
|
|
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
|
-
│
|
|
155
|
-
└──
|
|
156
|
-
|
|
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
|
|
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
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
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
|
+
}'
|
package/hooks/hooks.json
ADDED
|
@@ -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
|