@ztffn/presentation-generator-plugin 1.0.0 → 1.0.4
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/.github/workflows/publish.yml +3 -1
- package/README.md +5 -7
- package/bin/index.js +174 -117
- package/package.json +1 -1
|
@@ -20,7 +20,9 @@ jobs:
|
|
|
20
20
|
node-version: "20"
|
|
21
21
|
registry-url: "https://registry.npmjs.org"
|
|
22
22
|
|
|
23
|
-
- run: npm publish --
|
|
23
|
+
- run: npm publish --access public
|
|
24
|
+
env:
|
|
25
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
24
26
|
|
|
25
27
|
release-zip:
|
|
26
28
|
name: Create GitHub Release with zip
|
package/README.md
CHANGED
|
@@ -20,16 +20,14 @@ Requires Claude Code 1.0.33 or later.
|
|
|
20
20
|
claude --plugin-dir /path/to/presentation-generator-plugin
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
To always have it available in a project,
|
|
23
|
+
To always have it available in a project, register it with Claude Code:
|
|
24
24
|
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
"plugins": [
|
|
28
|
-
{ "path": "/path/to/presentation-generator-plugin" }
|
|
29
|
-
]
|
|
30
|
-
}
|
|
25
|
+
```bash
|
|
26
|
+
claude plugin install /path/to/presentation-generator-plugin --scope project
|
|
31
27
|
```
|
|
32
28
|
|
|
29
|
+
This writes the correct entry to `.claude/settings.json`. Commit that file to share the plugin with your team.
|
|
30
|
+
|
|
33
31
|
To update: download the new zip from Releases, replace the old folder.
|
|
34
32
|
|
|
35
33
|
### Option B — install via npx (requires Node.js 18+ and GitHub auth)
|
package/bin/index.js
CHANGED
|
@@ -1,182 +1,230 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const https = require("https");
|
|
4
|
+
const { execSync } = require("child_process");
|
|
4
5
|
const fs = require("fs");
|
|
5
6
|
const path = require("path");
|
|
6
7
|
const os = require("os");
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
"https://github.com/ztffn/presentation-generator-plugin.git";
|
|
9
|
+
const NPM_PACKAGE = "@ztffn/presentation-generator-plugin";
|
|
10
10
|
const PLUGIN_NAME = "presentation-generator";
|
|
11
11
|
const INSTALL_DIR = path.join(os.homedir(), ".claude", "plugins", PLUGIN_NAME);
|
|
12
12
|
const CURRENT_VERSION = require("../package.json").version;
|
|
13
13
|
|
|
14
14
|
const command = process.argv[2] || "help";
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
return execSync(cmd, { stdio: "inherit", ...opts });
|
|
18
|
-
}
|
|
16
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
19
17
|
|
|
20
|
-
function
|
|
18
|
+
function getInstalledVersion() {
|
|
19
|
+
const pkgPath = path.join(INSTALL_DIR, "package.json");
|
|
20
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
21
21
|
try {
|
|
22
|
-
return
|
|
22
|
+
return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version;
|
|
23
23
|
} catch {
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
function fetchJson(url) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
https
|
|
31
|
+
.get(url, { headers: { Accept: "application/json" } }, (res) => {
|
|
32
|
+
let data = "";
|
|
33
|
+
res.on("data", (chunk) => (data += chunk));
|
|
34
|
+
res.on("end", () => {
|
|
35
|
+
try {
|
|
36
|
+
resolve(JSON.parse(data));
|
|
37
|
+
} catch (e) {
|
|
38
|
+
reject(e);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
})
|
|
42
|
+
.on("error", reject);
|
|
43
|
+
});
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
function
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
function downloadFile(url, dest) {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
const follow = (u) => {
|
|
49
|
+
https
|
|
50
|
+
.get(u, (res) => {
|
|
51
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
52
|
+
follow(res.headers.location);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const file = fs.createWriteStream(dest);
|
|
56
|
+
res.pipe(file);
|
|
57
|
+
file.on("finish", () => file.close(resolve));
|
|
58
|
+
file.on("error", reject);
|
|
59
|
+
})
|
|
60
|
+
.on("error", reject);
|
|
61
|
+
};
|
|
62
|
+
follow(url);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function getLatestNpmVersion() {
|
|
40
67
|
try {
|
|
41
|
-
|
|
68
|
+
const data = await fetchJson(
|
|
69
|
+
`https://registry.npmjs.org/${NPM_PACKAGE}/latest`
|
|
70
|
+
);
|
|
71
|
+
return data.version || null;
|
|
42
72
|
} catch {
|
|
43
73
|
return null;
|
|
44
74
|
}
|
|
45
75
|
}
|
|
46
76
|
|
|
47
|
-
function
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
}
|
|
77
|
+
async function downloadAndExtract(version) {
|
|
78
|
+
const pkgSlug = NPM_PACKAGE.replace("@", "").replace("/", "-");
|
|
79
|
+
const tarballUrl = `https://registry.npmjs.org/${NPM_PACKAGE}/-/${pkgSlug}-${version}.tgz`;
|
|
80
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-plugin-"));
|
|
81
|
+
const tarball = path.join(tmpDir, "plugin.tgz");
|
|
53
82
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const settingsExists = fs.existsSync(settingsPath);
|
|
83
|
+
console.log(`Downloading v${version} from npm...`);
|
|
84
|
+
await downloadFile(tarballUrl, tarball);
|
|
57
85
|
|
|
58
|
-
console.log("
|
|
59
|
-
|
|
60
|
-
` File: ${path.join(process.cwd(), ".claude", "settings.json")}`
|
|
61
|
-
);
|
|
86
|
+
console.log("Extracting...");
|
|
87
|
+
execSync(`tar -xzf "${tarball}" -C "${tmpDir}"`);
|
|
62
88
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
}
|
|
89
|
+
// npm tarballs always extract to a "package/" subdirectory
|
|
90
|
+
const extracted = path.join(tmpDir, "package");
|
|
91
|
+
if (!fs.existsSync(extracted)) {
|
|
92
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
93
|
+
throw new Error("Extraction failed: no package/ directory in tarball");
|
|
76
94
|
}
|
|
77
95
|
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
96
|
+
if (fs.existsSync(INSTALL_DIR)) {
|
|
97
|
+
fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
99
98
|
}
|
|
99
|
+
fs.mkdirSync(path.dirname(INSTALL_DIR), { recursive: true });
|
|
100
|
+
fs.renameSync(extracted, INSTALL_DIR);
|
|
101
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
104
|
+
function registerPlugin() {
|
|
105
|
+
console.log("\nRegistering plugin with Claude Code...");
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
// Prefer project-level settings if .claude/ exists in cwd, else user-level
|
|
108
|
+
const projectDir = path.join(process.cwd(), ".claude");
|
|
109
|
+
const settingsPath = fs.existsSync(projectDir)
|
|
110
|
+
? path.join(projectDir, "settings.json")
|
|
111
|
+
: path.join(os.homedir(), ".claude", "settings.json");
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
let settings = {};
|
|
115
|
+
if (fs.existsSync(settingsPath)) {
|
|
109
116
|
settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
117
|
+
}
|
|
112
118
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
if (!Array.isArray(settings.enabledPlugins)) {
|
|
120
|
+
settings.enabledPlugins = [];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!settings.enabledPlugins.includes(INSTALL_DIR)) {
|
|
124
|
+
settings.enabledPlugins.push(INSTALL_DIR);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
117
128
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
118
|
-
|
|
129
|
+
|
|
130
|
+
const label = settingsPath.startsWith(process.cwd())
|
|
131
|
+
? ".claude/settings.json"
|
|
132
|
+
: settingsPath;
|
|
133
|
+
console.log(`Plugin registered in ${label} ✓`);
|
|
134
|
+
|
|
135
|
+
if (settingsPath.startsWith(process.cwd())) {
|
|
136
|
+
console.log("Commit that file to share the plugin with your team.");
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
console.log(
|
|
140
|
+
"\nCould not write settings. Add manually to .claude/settings.json:"
|
|
141
|
+
);
|
|
142
|
+
console.log(JSON.stringify({ enabledPlugins: [INSTALL_DIR] }, null, 2));
|
|
143
|
+
console.log(`\nOr load for a single session:`);
|
|
144
|
+
console.log(` claude --plugin-dir "${INSTALL_DIR}"`);
|
|
119
145
|
}
|
|
120
146
|
}
|
|
121
147
|
|
|
122
148
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
123
149
|
|
|
124
|
-
function install() {
|
|
125
|
-
checkGitAuth();
|
|
126
|
-
|
|
150
|
+
async function install() {
|
|
127
151
|
if (fs.existsSync(INSTALL_DIR)) {
|
|
128
152
|
const installed = getInstalledVersion();
|
|
129
|
-
const latest =
|
|
153
|
+
const latest = await getLatestNpmVersion();
|
|
130
154
|
|
|
131
155
|
if (latest && installed && installed !== latest) {
|
|
132
156
|
console.log(`\nPlugin already installed (v${installed}).`);
|
|
133
|
-
console.log(
|
|
157
|
+
console.log(
|
|
158
|
+
`v${latest} is available — run: npx ${NPM_PACKAGE} update\n`
|
|
159
|
+
);
|
|
134
160
|
} else {
|
|
135
|
-
console.log(
|
|
161
|
+
console.log(
|
|
162
|
+
`\nPlugin already installed (v${installed || "unknown"}). Nothing to do.\n`
|
|
163
|
+
);
|
|
136
164
|
}
|
|
137
|
-
|
|
165
|
+
registerPlugin();
|
|
138
166
|
return;
|
|
139
167
|
}
|
|
140
168
|
|
|
141
169
|
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
170
|
|
|
148
|
-
|
|
149
|
-
|
|
171
|
+
const version = await getLatestNpmVersion();
|
|
172
|
+
if (!version) {
|
|
173
|
+
console.error(
|
|
174
|
+
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
175
|
+
);
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await downloadAndExtract(version);
|
|
180
|
+
console.log(`\nInstalled v${version} to: ${INSTALL_DIR}`);
|
|
181
|
+
registerPlugin();
|
|
182
|
+
}
|
|
150
183
|
|
|
184
|
+
async function update() {
|
|
151
185
|
if (!fs.existsSync(INSTALL_DIR)) {
|
|
152
186
|
console.log("\nPlugin not installed. Run install first:");
|
|
153
|
-
console.log(
|
|
187
|
+
console.log(` npx ${NPM_PACKAGE} install\n`);
|
|
154
188
|
process.exit(1);
|
|
155
189
|
}
|
|
156
190
|
|
|
157
191
|
const before = getInstalledVersion();
|
|
158
|
-
|
|
159
|
-
run(`git -C "${INSTALL_DIR}" pull`);
|
|
192
|
+
const latest = await getLatestNpmVersion();
|
|
160
193
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
194
|
+
if (!latest) {
|
|
195
|
+
console.error(
|
|
196
|
+
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
197
|
+
);
|
|
198
|
+
process.exit(1);
|
|
166
199
|
}
|
|
200
|
+
|
|
201
|
+
if (before === latest) {
|
|
202
|
+
console.log(`\nAlready up to date (v${before}) ✓`);
|
|
203
|
+
registerPlugin();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
console.log(
|
|
208
|
+
`\nUpdating presentation-generator plugin (v${before || "unknown"} → v${latest})...`
|
|
209
|
+
);
|
|
210
|
+
await downloadAndExtract(latest);
|
|
211
|
+
console.log(`\nUpdated to v${latest} ✓`);
|
|
212
|
+
registerPlugin();
|
|
167
213
|
}
|
|
168
214
|
|
|
169
|
-
function checkUpdate() {
|
|
215
|
+
async function checkUpdate() {
|
|
170
216
|
if (!fs.existsSync(INSTALL_DIR)) {
|
|
171
|
-
console.log("\nPlugin not installed
|
|
217
|
+
console.log("\nPlugin not installed.\n");
|
|
172
218
|
return;
|
|
173
219
|
}
|
|
174
220
|
|
|
175
221
|
const installed = getInstalledVersion();
|
|
176
|
-
const latest =
|
|
222
|
+
const latest = await getLatestNpmVersion();
|
|
177
223
|
|
|
178
224
|
if (!latest) {
|
|
179
|
-
console.log(
|
|
225
|
+
console.log(
|
|
226
|
+
`\nInstalled: v${installed || "unknown"} (could not reach npm registry)\n`
|
|
227
|
+
);
|
|
180
228
|
return;
|
|
181
229
|
}
|
|
182
230
|
|
|
@@ -184,7 +232,7 @@ function checkUpdate() {
|
|
|
184
232
|
console.log(`\nUp to date: v${installed} ✓\n`);
|
|
185
233
|
} else {
|
|
186
234
|
console.log(`\nUpdate available: v${installed} → v${latest}`);
|
|
187
|
-
console.log(`Run: npx
|
|
235
|
+
console.log(`Run: npx ${NPM_PACKAGE} update\n`);
|
|
188
236
|
}
|
|
189
237
|
}
|
|
190
238
|
|
|
@@ -194,8 +242,7 @@ function uninstall() {
|
|
|
194
242
|
return;
|
|
195
243
|
}
|
|
196
244
|
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");
|
|
245
|
+
console.log(`\nRemoved: ${INSTALL_DIR}\n`);
|
|
199
246
|
}
|
|
200
247
|
|
|
201
248
|
function help() {
|
|
@@ -203,21 +250,28 @@ function help() {
|
|
|
203
250
|
presentation-generator-plugin v${CURRENT_VERSION}
|
|
204
251
|
|
|
205
252
|
Commands:
|
|
206
|
-
install
|
|
207
|
-
update
|
|
253
|
+
install Download plugin from npm and register with Claude Code
|
|
254
|
+
update Download and install the latest version from npm
|
|
208
255
|
check-update Report whether an update is available
|
|
209
256
|
uninstall Remove plugin from ~/.claude/plugins/
|
|
210
257
|
|
|
211
258
|
Usage:
|
|
212
|
-
npx
|
|
213
|
-
npx
|
|
214
|
-
npx
|
|
215
|
-
|
|
216
|
-
Requires: gh auth login (GitHub authentication)
|
|
259
|
+
npx ${NPM_PACKAGE} install
|
|
260
|
+
npx ${NPM_PACKAGE} update
|
|
261
|
+
npx ${NPM_PACKAGE} check-update
|
|
217
262
|
`);
|
|
218
263
|
}
|
|
219
264
|
|
|
220
|
-
|
|
265
|
+
// ── Dispatch ──────────────────────────────────────────────────────────────────
|
|
266
|
+
|
|
267
|
+
const commands = {
|
|
268
|
+
install,
|
|
269
|
+
update,
|
|
270
|
+
"check-update": checkUpdate,
|
|
271
|
+
uninstall,
|
|
272
|
+
help,
|
|
273
|
+
};
|
|
274
|
+
|
|
221
275
|
const fn = commands[command];
|
|
222
276
|
|
|
223
277
|
if (!fn) {
|
|
@@ -226,4 +280,7 @@ if (!fn) {
|
|
|
226
280
|
process.exit(1);
|
|
227
281
|
}
|
|
228
282
|
|
|
229
|
-
fn()
|
|
283
|
+
Promise.resolve(fn()).catch((err) => {
|
|
284
|
+
console.error("\nError:", err.message, "\n");
|
|
285
|
+
process.exit(1);
|
|
286
|
+
});
|