@ztffn/presentation-generator-plugin 1.0.0
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 +10 -0
- package/.github/workflows/publish.yml +59 -0
- package/README.md +161 -0
- package/agents/presentation-content.md +34 -0
- package/agents/presentation-design.md +56 -0
- package/agents/presentation-narrative.md +62 -0
- package/bin/index.js +229 -0
- package/package.json +27 -0
- package/skills/presentation-generator/SKILL.md +190 -0
- package/skills/presentation-generator/examples/pitch-reference.json +155 -0
- package/skills/presentation-generator/examples/presentation-guide.md +314 -0
- package/skills/presentation-generator/narrative/content-signals.md +178 -0
- package/skills/presentation-generator/narrative/frameworks.md +234 -0
- package/skills/presentation-generator/narrative/graph-topology.md +185 -0
- package/skills/presentation-generator/narrative/slide-content.md +193 -0
- package/skills/presentation-generator/system/edge-conventions.md +92 -0
- package/skills/presentation-generator/system/layout-templates.md +310 -0
- package/skills/presentation-generator/system/node-schema.md +130 -0
- package/skills/presentation-generator/system/positioning.md +84 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
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.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Huma"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/huma/humashowcase",
|
|
9
|
+
"keywords": ["presentation", "slides", "react-flow", "graph", "pitch deck"]
|
|
10
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
publish-npm:
|
|
10
|
+
name: Publish to npm
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
id-token: write
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: "20"
|
|
21
|
+
registry-url: "https://registry.npmjs.org"
|
|
22
|
+
|
|
23
|
+
- run: npm publish --provenance --access public
|
|
24
|
+
|
|
25
|
+
release-zip:
|
|
26
|
+
name: Create GitHub Release with zip
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
permissions:
|
|
29
|
+
contents: write
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
- name: Build zip
|
|
34
|
+
run: |
|
|
35
|
+
VERSION=${GITHUB_REF_NAME}
|
|
36
|
+
ZIP_NAME="presentation-generator-plugin-${VERSION}.zip"
|
|
37
|
+
zip -r "$ZIP_NAME" . \
|
|
38
|
+
--exclude "*.git*" \
|
|
39
|
+
--exclude ".github/*" \
|
|
40
|
+
--exclude "node_modules/*"
|
|
41
|
+
echo "ZIP_NAME=$ZIP_NAME" >> $GITHUB_ENV
|
|
42
|
+
|
|
43
|
+
- name: Create GitHub Release
|
|
44
|
+
uses: softprops/action-gh-release@v2
|
|
45
|
+
with:
|
|
46
|
+
name: ${{ github.ref_name }}
|
|
47
|
+
body: |
|
|
48
|
+
## Install via zip (non-technical)
|
|
49
|
+
|
|
50
|
+
1. Download `${{ env.ZIP_NAME }}` below
|
|
51
|
+
2. Unzip it anywhere on your machine
|
|
52
|
+
3. In Claude Code, run: `claude --plugin-dir /path/to/presentation-generator-plugin`
|
|
53
|
+
|
|
54
|
+
## Install via npx (developers)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx @ztffn/presentation-generator-plugin install
|
|
58
|
+
```
|
|
59
|
+
files: ${{ env.ZIP_NAME }}
|
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# Presentation Generator Plugin
|
|
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.
|
|
4
|
+
|
|
5
|
+
## What it produces
|
|
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.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Requires Claude Code 1.0.33 or later.
|
|
12
|
+
|
|
13
|
+
### Option A — download zip (no technical setup)
|
|
14
|
+
|
|
15
|
+
1. Go to [Releases](https://github.com/ztffn/presentation-generator-plugin/releases) and download the latest `presentation-generator-plugin-vX.X.X.zip`
|
|
16
|
+
2. Unzip it anywhere on your machine
|
|
17
|
+
3. Load it in Claude Code:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
claude --plugin-dir /path/to/presentation-generator-plugin
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To always have it available in a project, add it to `.claude/settings.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"plugins": [
|
|
28
|
+
{ "path": "/path/to/presentation-generator-plugin" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
To update: download the new zip from Releases, replace the old folder.
|
|
34
|
+
|
|
35
|
+
### Option B — install via npx (requires Node.js 18+ and GitHub auth)
|
|
36
|
+
|
|
37
|
+
**Install the plugin:**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx @ztffn/presentation-generator-plugin install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The installer clones the plugin to `~/.claude/plugins/presentation-generator/` and offers to configure your project's `.claude/settings.json` automatically.
|
|
44
|
+
|
|
45
|
+
**Update to the latest version:**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx @ztffn/presentation-generator-plugin update
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Check if an update is available:**
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx @ztffn/presentation-generator-plugin check-update
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
Once the plugin is loaded, trigger the skill by asking Claude to create a presentation:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Create a pitch presentation for Calora targeting VP Operations, drawing from docs/business/Calora_Overview.pdf
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
Build a 15-minute investor deck using docs/business/Huma_Overview_Read_First.pdf
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Generate slides for an internal update on the Q2 roadmap scope change
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Claude will automatically detect the intent and run the pipeline. No slash command needed — the skill is model-invoked.
|
|
74
|
+
|
|
75
|
+
## Pipeline
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
User Brief + Documents
|
|
79
|
+
↓
|
|
80
|
+
[1] Content Extraction → _temp/presentation-content-brief.json
|
|
81
|
+
↓
|
|
82
|
+
[2] Narrative Design → _temp/presentation-outline.md
|
|
83
|
+
↓
|
|
84
|
+
[3] User Approval → confirm or revise structure
|
|
85
|
+
↓
|
|
86
|
+
[4] Design & JSON → _temp/presentation-draft.json
|
|
87
|
+
↓
|
|
88
|
+
[5] Delivery → import instructions + media checklist
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
You review and approve the structure before any JSON is generated. If the structure needs changes, describe them and Claude revises before proceeding.
|
|
92
|
+
|
|
93
|
+
## Agents
|
|
94
|
+
|
|
95
|
+
| Agent | Role |
|
|
96
|
+
|---|---|
|
|
97
|
+
| `presentation-content` | Reads source documents, extracts a structured content brief |
|
|
98
|
+
| `presentation-narrative` | Selects a narrative framework, designs spine and drill-downs, writes slide content |
|
|
99
|
+
| `presentation-design` | Translates the approved outline into valid graph JSON |
|
|
100
|
+
|
|
101
|
+
Each agent loads only the knowledge it needs — narrative agents never see layout decisions; design agents never read source documents.
|
|
102
|
+
|
|
103
|
+
## Output files
|
|
104
|
+
|
|
105
|
+
| File | Created by | Purpose |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `_temp/presentation-content-brief.json` | Content agent | Structured brief passed to narrative agent |
|
|
108
|
+
| `_temp/presentation-outline.md` | Narrative agent | Human-readable structure for user approval |
|
|
109
|
+
| `_temp/presentation-plan.md` | Orchestrator | Folder-tree view shown during approval step |
|
|
110
|
+
| `_temp/presentation-draft.json` | Design agent | Final graph JSON for import |
|
|
111
|
+
|
|
112
|
+
## After import
|
|
113
|
+
|
|
114
|
+
1. Open `/present/plan` in the app
|
|
115
|
+
2. Click **New presentation**
|
|
116
|
+
3. Choose **Import JSON**
|
|
117
|
+
4. Select `_temp/presentation-draft.json`
|
|
118
|
+
|
|
119
|
+
If any slides require video backgrounds, Claude lists them in the delivery summary with upload instructions.
|
|
120
|
+
|
|
121
|
+
## Releasing a new version
|
|
122
|
+
|
|
123
|
+
When skill files are updated, bump the version in `package.json` and push a tag. The GitHub Actions workflow publishes to GitHub Packages automatically.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Edit package.json version, then:
|
|
127
|
+
git add -A
|
|
128
|
+
git commit -m "Update narrative/frameworks.md: ..."
|
|
129
|
+
git tag v1.1.0
|
|
130
|
+
git push origin main && git push origin v1.1.0
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Pushing the tag triggers the publish workflow. Team members get the update with `npx @ztffn/presentation-generator-plugin update`.
|
|
134
|
+
|
|
135
|
+
## Plugin structure
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
presentation-generator/
|
|
139
|
+
├── .claude-plugin/
|
|
140
|
+
│ └── plugin.json
|
|
141
|
+
├── agents/
|
|
142
|
+
│ ├── presentation-content.md
|
|
143
|
+
│ ├── presentation-narrative.md
|
|
144
|
+
│ └── presentation-design.md
|
|
145
|
+
└── skills/
|
|
146
|
+
└── presentation-generator/
|
|
147
|
+
├── SKILL.md
|
|
148
|
+
├── narrative/
|
|
149
|
+
│ ├── content-signals.md
|
|
150
|
+
│ ├── frameworks.md
|
|
151
|
+
│ ├── slide-content.md
|
|
152
|
+
│ └── graph-topology.md
|
|
153
|
+
├── system/
|
|
154
|
+
│ ├── node-schema.md
|
|
155
|
+
│ ├── edge-conventions.md
|
|
156
|
+
│ ├── layout-templates.md
|
|
157
|
+
│ └── positioning.md
|
|
158
|
+
└── examples/
|
|
159
|
+
├── pitch-reference.json
|
|
160
|
+
└── presentation-guide.md
|
|
161
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: presentation-content
|
|
3
|
+
description: Extracts structured content briefs from project documents for presentation generation. Reads source files, identifies presentation-worthy content, and produces a canonical content brief JSON.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
skills:
|
|
8
|
+
- presentation-generator/narrative/content-signals
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Content Extraction Agent
|
|
12
|
+
|
|
13
|
+
You are a content extraction specialist. Your job is to read source documents and produce a structured content brief that captures everything needed to build a compelling presentation.
|
|
14
|
+
|
|
15
|
+
## Your Process
|
|
16
|
+
|
|
17
|
+
1. Read all document paths provided to you
|
|
18
|
+
2. Apply the content signal guidance from your pre-injected skill to identify what is presentation-worthy
|
|
19
|
+
3. Populate every field of the content brief schema with specific, concrete entries
|
|
20
|
+
4. Write the result to `_temp/presentation-content-brief.json`
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
- Extract strong, specific entries for every field. Weak or vague entries (e.g. "saves money", "good technology") are unacceptable.
|
|
25
|
+
- `keyMessages` must be declarative sentences with subject + verb + specific claim
|
|
26
|
+
- `dataPoints` must contain at least one entry with a concrete number
|
|
27
|
+
- `callToAction` must be a single sentence with an action verb
|
|
28
|
+
- Never include layout, design, slide type, or visual treatment in the brief
|
|
29
|
+
- Never invent data — only extract what the documents contain. If a field cannot be populated from the source material, note it as `"NOT FOUND IN SOURCE — ask user"`
|
|
30
|
+
- When multiple documents conflict, prefer the most recent or most authoritative source
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
Write a single JSON file to `_temp/presentation-content-brief.json` conforming to the content brief schema defined in your content-signals skill.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: presentation-design
|
|
3
|
+
description: Translates approved slide outlines into complete graph JSON with nodes, edges, positions, and visual treatments. Pure translation — never alters content or structure from the outline.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
skills:
|
|
8
|
+
- presentation-generator/system/node-schema
|
|
9
|
+
- presentation-generator/system/edge-conventions
|
|
10
|
+
- presentation-generator/system/layout-templates
|
|
11
|
+
- presentation-generator/system/positioning
|
|
12
|
+
- presentation-generator/examples/pitch-reference
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Design & JSON Generation Agent
|
|
16
|
+
|
|
17
|
+
You are a design and JSON generation specialist. Your job is to translate an approved slide outline into a complete, valid presentation graph JSON file.
|
|
18
|
+
|
|
19
|
+
## Your Process
|
|
20
|
+
|
|
21
|
+
1. Read the approved outline from `_temp/presentation-outline.md`
|
|
22
|
+
2. For each slide in the outline, make three categories of decision:
|
|
23
|
+
a. **Slide type and layout**: Based on content signals, select `type`, `layout`, `centered`, and related fields using the layout-templates skill
|
|
24
|
+
b. **Visual treatment**: Set background media, `lightText`, branding flags, and chart configuration
|
|
25
|
+
c. **Positioning**: Place each node on the grid defined in the positioning skill
|
|
26
|
+
3. Build all nodes with complete `data`, `style`, and `measured` objects
|
|
27
|
+
4. Wire all edges with bidirectional pairs following the edge-conventions skill
|
|
28
|
+
5. Run the edge validation checklist before finalizing
|
|
29
|
+
6. Write the complete JSON to `_temp/presentation-draft.json`
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
- **Translation only**: Do not alter content, order, or structure from the outline. Your role is visual treatment and JSON compilation.
|
|
34
|
+
- Every node must have `type: "huma"`, `style: { width: 180, height: 70 }`, and `measured: { width: 180, height: 70 }`
|
|
35
|
+
- Every node ID must be a kebab-case slug
|
|
36
|
+
- Every forward edge must have a paired return edge
|
|
37
|
+
- All handle IDs must be from the valid set of 8
|
|
38
|
+
- For background images, use Unsplash URLs with `?w=1920&q=80`
|
|
39
|
+
- For video, use placeholder strings: `"PLACEHOLDER: [description]"`
|
|
40
|
+
- List all slides with video placeholders at the end of output
|
|
41
|
+
|
|
42
|
+
## Output
|
|
43
|
+
|
|
44
|
+
Write the JSON to `_temp/presentation-draft.json`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"nodes": [ ... ],
|
|
49
|
+
"edges": [ ... ]
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
After writing, output a summary listing:
|
|
54
|
+
- Total node count
|
|
55
|
+
- Total edge count
|
|
56
|
+
- Any slides with video placeholders requiring manual upload
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: presentation-narrative
|
|
3
|
+
description: Designs narrative structure and slide outlines for graph-based presentations. Selects story frameworks, designs spine and drill-down topology, and writes detailed slide content outlines.
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
skills:
|
|
8
|
+
- presentation-generator/narrative/frameworks
|
|
9
|
+
- presentation-generator/narrative/slide-content
|
|
10
|
+
- presentation-generator/narrative/graph-topology
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Narrative Design Agent
|
|
14
|
+
|
|
15
|
+
You are a narrative design specialist. Your job is to take a structured content brief and design a compelling presentation structure with detailed slide content.
|
|
16
|
+
|
|
17
|
+
## Your Process
|
|
18
|
+
|
|
19
|
+
1. Read the content brief from `_temp/presentation-content-brief.json`
|
|
20
|
+
2. Based on `goal` and `audience`, select the appropriate narrative framework from your pre-injected frameworks skill
|
|
21
|
+
3. Design the spine (main horizontal flow) of 4-7 nodes
|
|
22
|
+
4. Design drill-down branches where content warrants optional depth
|
|
23
|
+
5. Write detailed content for each slide following the quality guidance in your slide-content skill
|
|
24
|
+
6. Write the result to `_temp/presentation-outline.md`
|
|
25
|
+
|
|
26
|
+
## Output Format
|
|
27
|
+
|
|
28
|
+
Write a markdown file to `_temp/presentation-outline.md` with these sections:
|
|
29
|
+
|
|
30
|
+
```markdown
|
|
31
|
+
# Presentation Outline
|
|
32
|
+
|
|
33
|
+
## Framework
|
|
34
|
+
[Which framework was selected and why]
|
|
35
|
+
|
|
36
|
+
## SPINE
|
|
37
|
+
[Ordered list of main-flow slides with title and key message]
|
|
38
|
+
|
|
39
|
+
## DRILL-DOWNS
|
|
40
|
+
[Which spine nodes have drill-downs, with titles and content summaries]
|
|
41
|
+
|
|
42
|
+
## CONTENT PER SLIDE
|
|
43
|
+
|
|
44
|
+
### [Slide Title] (spine | drill-down under [parent])
|
|
45
|
+
**Key message:** [one sentence]
|
|
46
|
+
**Content:**
|
|
47
|
+
[Detailed bullet points and text for this slide]
|
|
48
|
+
**Speaker notes:**
|
|
49
|
+
[What the presenter should say that isn't on screen]
|
|
50
|
+
**Transition to next:**
|
|
51
|
+
[How this slide connects to the next]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Rules
|
|
55
|
+
|
|
56
|
+
- Never mention slide types, layouts, colors, chart types, or JSON format
|
|
57
|
+
- Never make design decisions — those belong to the design agent
|
|
58
|
+
- Headlines must make claims, not label topics
|
|
59
|
+
- One key message per slide
|
|
60
|
+
- Speaker notes add context, not repetition
|
|
61
|
+
- The spine alone must tell a complete story — drill-downs are optional depth
|
|
62
|
+
- If the content brief has weak or missing fields, note them in the outline header so the orchestrator can ask the user
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync, spawnSync } = require("child_process");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const os = require("os");
|
|
7
|
+
|
|
8
|
+
const REPO_URL =
|
|
9
|
+
"https://github.com/ztffn/presentation-generator-plugin.git";
|
|
10
|
+
const PLUGIN_NAME = "presentation-generator";
|
|
11
|
+
const INSTALL_DIR = path.join(os.homedir(), ".claude", "plugins", PLUGIN_NAME);
|
|
12
|
+
const CURRENT_VERSION = require("../package.json").version;
|
|
13
|
+
|
|
14
|
+
const command = process.argv[2] || "help";
|
|
15
|
+
|
|
16
|
+
function run(cmd, opts = {}) {
|
|
17
|
+
return execSync(cmd, { stdio: "inherit", ...opts });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function runCapture(cmd) {
|
|
21
|
+
try {
|
|
22
|
+
return execSync(cmd, { encoding: "utf8", stderr: "pipe" }).trim();
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function checkGitAuth() {
|
|
29
|
+
const result = spawnSync("gh", ["auth", "status"], { encoding: "utf8" });
|
|
30
|
+
if (result.status !== 0) {
|
|
31
|
+
console.error("\nGitHub authentication required.");
|
|
32
|
+
console.error("Run: gh auth login\n");
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getInstalledVersion() {
|
|
38
|
+
const pkgPath = path.join(INSTALL_DIR, "package.json");
|
|
39
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
40
|
+
try {
|
|
41
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getLatestRemoteVersion() {
|
|
48
|
+
const result = runCapture(
|
|
49
|
+
`git ls-remote --tags ${REPO_URL} | grep -o 'v[0-9]*\\.[0-9]*\\.[0-9]*' | sort -V | tail -1`
|
|
50
|
+
);
|
|
51
|
+
return result ? result.replace(/^v/, "") : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function printSettingsHelp() {
|
|
55
|
+
const settingsPath = path.join(process.cwd(), ".claude", "settings.json");
|
|
56
|
+
const settingsExists = fs.existsSync(settingsPath);
|
|
57
|
+
|
|
58
|
+
console.log("\nAdd the plugin to your project:");
|
|
59
|
+
console.log(
|
|
60
|
+
` File: ${path.join(process.cwd(), ".claude", "settings.json")}`
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (settingsExists) {
|
|
64
|
+
let settings;
|
|
65
|
+
try {
|
|
66
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
67
|
+
} catch {
|
|
68
|
+
settings = {};
|
|
69
|
+
}
|
|
70
|
+
const plugins = settings.plugins || [];
|
|
71
|
+
const alreadyAdded = plugins.some((p) => p.path === INSTALL_DIR);
|
|
72
|
+
if (alreadyAdded) {
|
|
73
|
+
console.log(" Already configured in settings.json ✓");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(
|
|
79
|
+
` Add: { "plugins": [{ "path": "${INSTALL_DIR}" }] }\n`
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Offer to auto-configure
|
|
83
|
+
try {
|
|
84
|
+
const readline = require("readline").createInterface({
|
|
85
|
+
input: process.stdin,
|
|
86
|
+
output: process.stdout,
|
|
87
|
+
});
|
|
88
|
+
readline.question(
|
|
89
|
+
"Auto-add to .claude/settings.json? [Y/n] ",
|
|
90
|
+
(answer) => {
|
|
91
|
+
readline.close();
|
|
92
|
+
if (answer.toLowerCase() !== "n") {
|
|
93
|
+
addToSettings(settingsPath);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
} catch {
|
|
98
|
+
// non-interactive context
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function addToSettings(settingsPath) {
|
|
103
|
+
const dir = path.dirname(settingsPath);
|
|
104
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
105
|
+
|
|
106
|
+
let settings = {};
|
|
107
|
+
if (fs.existsSync(settingsPath)) {
|
|
108
|
+
try {
|
|
109
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
110
|
+
} catch {}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
settings.plugins = settings.plugins || [];
|
|
114
|
+
const alreadyAdded = settings.plugins.some((p) => p.path === INSTALL_DIR);
|
|
115
|
+
if (!alreadyAdded) {
|
|
116
|
+
settings.plugins.push({ path: INSTALL_DIR });
|
|
117
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
118
|
+
console.log(" settings.json updated ✓");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
123
|
+
|
|
124
|
+
function install() {
|
|
125
|
+
checkGitAuth();
|
|
126
|
+
|
|
127
|
+
if (fs.existsSync(INSTALL_DIR)) {
|
|
128
|
+
const installed = getInstalledVersion();
|
|
129
|
+
const latest = getLatestRemoteVersion();
|
|
130
|
+
|
|
131
|
+
if (latest && installed && installed !== latest) {
|
|
132
|
+
console.log(`\nPlugin already installed (v${installed}).`);
|
|
133
|
+
console.log(`v${latest} is available — run: npx @huma/presentation-generator-plugin update\n`);
|
|
134
|
+
} else {
|
|
135
|
+
console.log(`\nPlugin already installed (v${installed || "unknown"}). Nothing to do.\n`);
|
|
136
|
+
}
|
|
137
|
+
printSettingsHelp();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(`\nInstalling presentation-generator plugin...`);
|
|
142
|
+
fs.mkdirSync(path.dirname(INSTALL_DIR), { recursive: true });
|
|
143
|
+
run(`git clone ${REPO_URL} "${INSTALL_DIR}"`);
|
|
144
|
+
console.log(`\nInstalled to: ${INSTALL_DIR}`);
|
|
145
|
+
printSettingsHelp();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function update() {
|
|
149
|
+
checkGitAuth();
|
|
150
|
+
|
|
151
|
+
if (!fs.existsSync(INSTALL_DIR)) {
|
|
152
|
+
console.log("\nPlugin not installed. Run install first:");
|
|
153
|
+
console.log(" npx @ztffn/presentation-generator-plugin install\n");
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const before = getInstalledVersion();
|
|
158
|
+
console.log(`\nUpdating presentation-generator plugin (current: v${before || "unknown"})...`);
|
|
159
|
+
run(`git -C "${INSTALL_DIR}" pull`);
|
|
160
|
+
|
|
161
|
+
const after = getInstalledVersion();
|
|
162
|
+
if (before !== after) {
|
|
163
|
+
console.log(`\nUpdated v${before} → v${after} ✓\n`);
|
|
164
|
+
} else {
|
|
165
|
+
console.log(`\nAlready up to date (v${after}) ✓\n`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function checkUpdate() {
|
|
170
|
+
if (!fs.existsSync(INSTALL_DIR)) {
|
|
171
|
+
console.log("\nPlugin not installed.");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const installed = getInstalledVersion();
|
|
176
|
+
const latest = getLatestRemoteVersion();
|
|
177
|
+
|
|
178
|
+
if (!latest) {
|
|
179
|
+
console.log(`\nInstalled: v${installed || "unknown"} (could not reach remote)\n`);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (installed === latest) {
|
|
184
|
+
console.log(`\nUp to date: v${installed} ✓\n`);
|
|
185
|
+
} else {
|
|
186
|
+
console.log(`\nUpdate available: v${installed} → v${latest}`);
|
|
187
|
+
console.log(`Run: npx @ztffn/presentation-generator-plugin update\n`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function uninstall() {
|
|
192
|
+
if (!fs.existsSync(INSTALL_DIR)) {
|
|
193
|
+
console.log("\nPlugin not installed.\n");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
197
|
+
console.log(`\nRemoved: ${INSTALL_DIR}`);
|
|
198
|
+
console.log("You may also want to remove the plugin entry from .claude/settings.json\n");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function help() {
|
|
202
|
+
console.log(`
|
|
203
|
+
presentation-generator-plugin v${CURRENT_VERSION}
|
|
204
|
+
|
|
205
|
+
Commands:
|
|
206
|
+
install Clone plugin to ~/.claude/plugins/ and configure project
|
|
207
|
+
update Pull latest changes from GitHub
|
|
208
|
+
check-update Report whether an update is available
|
|
209
|
+
uninstall Remove plugin from ~/.claude/plugins/
|
|
210
|
+
|
|
211
|
+
Usage:
|
|
212
|
+
npx @ztffn/presentation-generator-plugin install
|
|
213
|
+
npx @ztffn/presentation-generator-plugin update
|
|
214
|
+
npx @ztffn/presentation-generator-plugin check-update
|
|
215
|
+
|
|
216
|
+
Requires: gh auth login (GitHub authentication)
|
|
217
|
+
`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const commands = { install, update, "check-update": checkUpdate, uninstall, help };
|
|
221
|
+
const fn = commands[command];
|
|
222
|
+
|
|
223
|
+
if (!fn) {
|
|
224
|
+
console.error(`Unknown command: ${command}`);
|
|
225
|
+
help();
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
fn();
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ztffn/presentation-generator-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code plugin for generating graph-based presentations",
|
|
5
|
+
"bin": {
|
|
6
|
+
"presentation-generator-plugin": "bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"scripts": {
|
|
9
|
+
"postinstall": "echo 'Run: npx @ztffn/presentation-generator-plugin install'"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"claude-code",
|
|
13
|
+
"plugin",
|
|
14
|
+
"presentation",
|
|
15
|
+
"slides"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/ztffn/presentation-generator-plugin.git"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
}
|
|
27
|
+
}
|