@ztffn/presentation-generator-plugin 1.0.8 → 1.1.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/bin/index.js +148 -117
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -9,15 +9,16 @@ const readline = require("readline");
|
|
|
9
9
|
|
|
10
10
|
const NPM_PACKAGE = "@ztffn/presentation-generator-plugin";
|
|
11
11
|
const PLUGIN_NAME = "presentation-generator";
|
|
12
|
-
const
|
|
12
|
+
const GLOBAL_INSTALL_DIR = path.join(os.homedir(), ".claude", "plugins", PLUGIN_NAME);
|
|
13
|
+
const PROJECT_INSTALL_DIR = path.join(process.cwd(), ".claude", "plugins", PLUGIN_NAME);
|
|
13
14
|
const CURRENT_VERSION = require("../package.json").version;
|
|
14
15
|
|
|
15
16
|
const command = process.argv[2] || "help";
|
|
16
17
|
|
|
17
18
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
18
19
|
|
|
19
|
-
function getInstalledVersion() {
|
|
20
|
-
const pkgPath = path.join(
|
|
20
|
+
function getInstalledVersion(installDir) {
|
|
21
|
+
const pkgPath = path.join(installDir, "package.json");
|
|
21
22
|
if (!fs.existsSync(pkgPath)) return null;
|
|
22
23
|
try {
|
|
23
24
|
return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version;
|
|
@@ -76,7 +77,7 @@ async function getLatestNpmMeta() {
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
async function downloadAndExtract(version, tarballUrl) {
|
|
80
|
+
async function downloadAndExtract(version, tarballUrl, installDir) {
|
|
80
81
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-plugin-"));
|
|
81
82
|
const tarball = path.join(tmpDir, "plugin.tgz");
|
|
82
83
|
|
|
@@ -86,18 +87,17 @@ async function downloadAndExtract(version, tarballUrl) {
|
|
|
86
87
|
console.log("Extracting...");
|
|
87
88
|
execSync(`tar -xzf "${tarball}" -C "${tmpDir}"`);
|
|
88
89
|
|
|
89
|
-
// npm tarballs always extract to a "package/" subdirectory
|
|
90
90
|
const extracted = path.join(tmpDir, "package");
|
|
91
91
|
if (!fs.existsSync(extracted)) {
|
|
92
92
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
93
93
|
throw new Error("Extraction failed: no package/ directory in tarball");
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
if (fs.existsSync(
|
|
97
|
-
fs.rmSync(
|
|
96
|
+
if (fs.existsSync(installDir)) {
|
|
97
|
+
fs.rmSync(installDir, { recursive: true, force: true });
|
|
98
98
|
}
|
|
99
|
-
fs.mkdirSync(path.dirname(
|
|
100
|
-
fs.renameSync(extracted,
|
|
99
|
+
fs.mkdirSync(path.dirname(installDir), { recursive: true });
|
|
100
|
+
fs.renameSync(extracted, installDir);
|
|
101
101
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -111,7 +111,10 @@ function promptChoice(title, choices) {
|
|
|
111
111
|
});
|
|
112
112
|
console.log("│");
|
|
113
113
|
|
|
114
|
-
const rl = readline.createInterface({
|
|
114
|
+
const rl = readline.createInterface({
|
|
115
|
+
input: process.stdin,
|
|
116
|
+
output: process.stdout,
|
|
117
|
+
});
|
|
115
118
|
rl.question(`└ Enter choice [1-${choices.length}]: `, (answer) => {
|
|
116
119
|
rl.close();
|
|
117
120
|
const idx = parseInt(answer.trim(), 10) - 1;
|
|
@@ -120,188 +123,215 @@ function promptChoice(title, choices) {
|
|
|
120
123
|
});
|
|
121
124
|
}
|
|
122
125
|
|
|
123
|
-
async function
|
|
126
|
+
async function resolveInstallTarget() {
|
|
124
127
|
const projectClaudeDir = path.join(process.cwd(), ".claude");
|
|
125
|
-
const userSettingsPath = path.join(os.homedir(), ".claude", "settings.json");
|
|
126
128
|
|
|
127
129
|
if (!fs.existsSync(projectClaudeDir)) {
|
|
128
|
-
return {
|
|
130
|
+
return {
|
|
131
|
+
installDir: GLOBAL_INSTALL_DIR,
|
|
132
|
+
settingsPath: path.join(os.homedir(), ".claude", "settings.json"),
|
|
133
|
+
scope: "global",
|
|
134
|
+
};
|
|
129
135
|
}
|
|
130
136
|
|
|
131
137
|
const idx = await promptChoice("Installation scope", [
|
|
132
138
|
{
|
|
133
139
|
label: "Global",
|
|
134
|
-
hint: "
|
|
140
|
+
hint: "~/.claude/plugins/ — available in all your Claude sessions",
|
|
135
141
|
},
|
|
136
142
|
{
|
|
137
143
|
label: "Project",
|
|
138
|
-
hint: "
|
|
144
|
+
hint: ".claude/plugins/ — lives in this directory, commit to share with your team",
|
|
139
145
|
},
|
|
140
146
|
]);
|
|
141
147
|
|
|
142
148
|
if (idx === 1) {
|
|
143
149
|
return {
|
|
150
|
+
installDir: PROJECT_INSTALL_DIR,
|
|
144
151
|
settingsPath: path.join(projectClaudeDir, "settings.json"),
|
|
145
152
|
scope: "project",
|
|
146
153
|
};
|
|
147
154
|
}
|
|
148
155
|
|
|
149
|
-
return {
|
|
156
|
+
return {
|
|
157
|
+
installDir: GLOBAL_INSTALL_DIR,
|
|
158
|
+
settingsPath: path.join(os.homedir(), ".claude", "settings.json"),
|
|
159
|
+
scope: "global",
|
|
160
|
+
};
|
|
150
161
|
}
|
|
151
162
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
let settings = {};
|
|
159
|
-
if (fs.existsSync(settingsPath)) {
|
|
163
|
+
function writeSettings(settingsPath) {
|
|
164
|
+
let settings = {};
|
|
165
|
+
if (fs.existsSync(settingsPath)) {
|
|
166
|
+
try {
|
|
160
167
|
settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
168
|
+
} catch {
|
|
169
|
+
settings = {};
|
|
161
170
|
}
|
|
171
|
+
}
|
|
162
172
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
173
|
+
if (!Array.isArray(settings.enabledPlugins)) {
|
|
174
|
+
settings.enabledPlugins = [];
|
|
175
|
+
}
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
177
|
+
if (!settings.enabledPlugins.includes(PLUGIN_NAME)) {
|
|
178
|
+
settings.enabledPlugins.push(PLUGIN_NAME);
|
|
179
|
+
}
|
|
170
180
|
|
|
171
|
-
|
|
172
|
-
|
|
181
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
182
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
183
|
+
}
|
|
173
184
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
185
|
+
function removeFromSettings(settingsPath) {
|
|
186
|
+
if (!fs.existsSync(settingsPath)) return;
|
|
187
|
+
try {
|
|
188
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
189
|
+
if (!Array.isArray(settings.enabledPlugins)) return;
|
|
190
|
+
const filtered = settings.enabledPlugins.filter((p) => p !== PLUGIN_NAME);
|
|
191
|
+
if (filtered.length !== settings.enabledPlugins.length) {
|
|
192
|
+
settings.enabledPlugins = filtered;
|
|
193
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
194
|
+
console.log(`Removed entry from ${settingsPath}`);
|
|
178
195
|
}
|
|
179
196
|
} catch {
|
|
180
|
-
|
|
181
|
-
console.log(JSON.stringify({ enabledPlugins: [INSTALL_DIR] }, null, 2));
|
|
182
|
-
console.log(`\nOr load for a single session:`);
|
|
183
|
-
console.log(` claude --plugin-dir "${INSTALL_DIR}"`);
|
|
197
|
+
// ignore
|
|
184
198
|
}
|
|
185
199
|
}
|
|
186
200
|
|
|
187
201
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
188
202
|
|
|
189
203
|
async function install() {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
205
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
206
|
+
|
|
207
|
+
if (globalExists || projectExists) {
|
|
208
|
+
const meta = await getLatestNpmMeta();
|
|
209
|
+
if (globalExists) {
|
|
210
|
+
const v = getInstalledVersion(GLOBAL_INSTALL_DIR);
|
|
211
|
+
if (meta && v !== meta.version) {
|
|
212
|
+
console.log(`\nGlobal install found (v${v}). v${meta.version} available — run: npx ${NPM_PACKAGE} update\n`);
|
|
213
|
+
} else {
|
|
214
|
+
console.log(`\nGlobal install already up to date (v${v || "unknown"}).\n`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (projectExists) {
|
|
218
|
+
const v = getInstalledVersion(PROJECT_INSTALL_DIR);
|
|
219
|
+
if (meta && v !== meta.version) {
|
|
220
|
+
console.log(`\nProject install found (v${v}). v${meta.version} available — run: npx ${NPM_PACKAGE} update\n`);
|
|
221
|
+
} else {
|
|
222
|
+
console.log(`\nProject install already up to date (v${v || "unknown"}).\n`);
|
|
223
|
+
}
|
|
204
224
|
}
|
|
205
|
-
registerPlugin();
|
|
206
225
|
return;
|
|
207
226
|
}
|
|
208
227
|
|
|
209
|
-
|
|
210
|
-
|
|
228
|
+
const meta = await getLatestNpmMeta();
|
|
211
229
|
if (!meta) {
|
|
212
|
-
console.error(
|
|
213
|
-
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
214
|
-
);
|
|
230
|
+
console.error("\nCould not fetch latest version from npm registry. Check your internet connection.\n");
|
|
215
231
|
process.exit(1);
|
|
216
232
|
}
|
|
217
233
|
|
|
218
|
-
await
|
|
219
|
-
|
|
220
|
-
|
|
234
|
+
const { installDir, settingsPath, scope } = await resolveInstallTarget();
|
|
235
|
+
|
|
236
|
+
console.log(`\nInstalling presentation-generator plugin...`);
|
|
237
|
+
await downloadAndExtract(meta.version, meta.tarball, installDir);
|
|
238
|
+
|
|
239
|
+
const label = scope === "project" ? `.claude/plugins/${PLUGIN_NAME}` : installDir;
|
|
240
|
+
console.log(`\nInstalled v${meta.version} to: ${label}`);
|
|
241
|
+
|
|
242
|
+
console.log("\nRegistering plugin with Claude Code...");
|
|
243
|
+
writeSettings(settingsPath);
|
|
244
|
+
|
|
245
|
+
const settingsLabel = scope === "project" ? ".claude/settings.json" : "~/.claude/settings.json";
|
|
246
|
+
console.log(`Plugin registered in ${settingsLabel} ✓`);
|
|
221
247
|
}
|
|
222
248
|
|
|
223
249
|
async function update() {
|
|
224
|
-
|
|
250
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
251
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
252
|
+
|
|
253
|
+
if (!globalExists && !projectExists) {
|
|
225
254
|
console.log("\nPlugin not installed. Run install first:");
|
|
226
255
|
console.log(` npx ${NPM_PACKAGE} install\n`);
|
|
227
256
|
process.exit(1);
|
|
228
257
|
}
|
|
229
258
|
|
|
230
|
-
const before = getInstalledVersion();
|
|
231
259
|
const meta = await getLatestNpmMeta();
|
|
232
|
-
|
|
233
260
|
if (!meta) {
|
|
234
|
-
console.error(
|
|
235
|
-
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
236
|
-
);
|
|
261
|
+
console.error("\nCould not fetch latest version from npm registry. Check your internet connection.\n");
|
|
237
262
|
process.exit(1);
|
|
238
263
|
}
|
|
239
264
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
265
|
+
for (const [installDir, settingsPath, label] of [
|
|
266
|
+
[GLOBAL_INSTALL_DIR, path.join(os.homedir(), ".claude", "settings.json"), "Global"],
|
|
267
|
+
[PROJECT_INSTALL_DIR, path.join(process.cwd(), ".claude", "settings.json"), "Project"],
|
|
268
|
+
]) {
|
|
269
|
+
if (!fs.existsSync(installDir)) continue;
|
|
270
|
+
|
|
271
|
+
const before = getInstalledVersion(installDir);
|
|
272
|
+
if (before === meta.version) {
|
|
273
|
+
console.log(`\n${label}: already up to date (v${before}) ✓`);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
245
276
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
registerPlugin();
|
|
277
|
+
console.log(`\n${label}: updating v${before || "unknown"} → v${meta.version}...`);
|
|
278
|
+
await downloadAndExtract(meta.version, meta.tarball, installDir);
|
|
279
|
+
writeSettings(settingsPath);
|
|
280
|
+
console.log(`${label}: updated to v${meta.version} ✓`);
|
|
281
|
+
}
|
|
252
282
|
}
|
|
253
283
|
|
|
254
284
|
async function checkUpdate() {
|
|
255
|
-
|
|
285
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
286
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
287
|
+
|
|
288
|
+
if (!globalExists && !projectExists) {
|
|
256
289
|
console.log("\nPlugin not installed.\n");
|
|
257
290
|
return;
|
|
258
291
|
}
|
|
259
292
|
|
|
260
|
-
const installed = getInstalledVersion();
|
|
261
293
|
const meta = await getLatestNpmMeta();
|
|
262
294
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
function removeFromSettings(settingsPath) {
|
|
279
|
-
if (!fs.existsSync(settingsPath)) return;
|
|
280
|
-
try {
|
|
281
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
282
|
-
if (!Array.isArray(settings.enabledPlugins)) return;
|
|
283
|
-
const before = settings.enabledPlugins.length;
|
|
284
|
-
settings.enabledPlugins = settings.enabledPlugins.filter((p) => p !== INSTALL_DIR);
|
|
285
|
-
if (settings.enabledPlugins.length !== before) {
|
|
286
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
287
|
-
console.log(`Removed from ${settingsPath}`);
|
|
295
|
+
for (const [installDir, label] of [
|
|
296
|
+
[GLOBAL_INSTALL_DIR, "Global"],
|
|
297
|
+
[PROJECT_INSTALL_DIR, "Project"],
|
|
298
|
+
]) {
|
|
299
|
+
if (!fs.existsSync(installDir)) continue;
|
|
300
|
+
const installed = getInstalledVersion(installDir);
|
|
301
|
+
|
|
302
|
+
if (!meta) {
|
|
303
|
+
console.log(`\n${label}: v${installed || "unknown"} (could not reach npm registry)`);
|
|
304
|
+
} else if (installed === meta.version) {
|
|
305
|
+
console.log(`\n${label}: up to date (v${installed}) ✓`);
|
|
306
|
+
} else {
|
|
307
|
+
console.log(`\n${label}: update available v${installed} → v${meta.version}`);
|
|
308
|
+
console.log(` Run: npx ${NPM_PACKAGE} update`);
|
|
288
309
|
}
|
|
289
|
-
} catch {
|
|
290
|
-
// ignore
|
|
291
310
|
}
|
|
311
|
+
console.log();
|
|
292
312
|
}
|
|
293
313
|
|
|
294
314
|
function uninstall() {
|
|
295
|
-
|
|
315
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
316
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
317
|
+
|
|
318
|
+
if (!globalExists && !projectExists) {
|
|
296
319
|
console.log("\nPlugin not installed.\n");
|
|
297
320
|
return;
|
|
298
321
|
}
|
|
299
|
-
fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
300
|
-
console.log(`\nRemoved: ${INSTALL_DIR}`);
|
|
301
322
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
323
|
+
if (globalExists) {
|
|
324
|
+
fs.rmSync(GLOBAL_INSTALL_DIR, { recursive: true, force: true });
|
|
325
|
+
console.log(`\nRemoved: ${GLOBAL_INSTALL_DIR}`);
|
|
326
|
+
removeFromSettings(path.join(os.homedir(), ".claude", "settings.json"));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (projectExists) {
|
|
330
|
+
fs.rmSync(PROJECT_INSTALL_DIR, { recursive: true, force: true });
|
|
331
|
+
console.log(`\nRemoved: .claude/plugins/${PLUGIN_NAME}`);
|
|
332
|
+
removeFromSettings(path.join(process.cwd(), ".claude", "settings.json"));
|
|
333
|
+
}
|
|
334
|
+
|
|
305
335
|
console.log();
|
|
306
336
|
}
|
|
307
337
|
|
|
@@ -310,15 +340,16 @@ function help() {
|
|
|
310
340
|
presentation-generator-plugin v${CURRENT_VERSION}
|
|
311
341
|
|
|
312
342
|
Commands:
|
|
313
|
-
install Download
|
|
343
|
+
install Download and install the plugin, register with Claude Code
|
|
314
344
|
update Download and install the latest version from npm
|
|
315
345
|
check-update Report whether an update is available
|
|
316
|
-
uninstall Remove plugin
|
|
346
|
+
uninstall Remove the plugin
|
|
317
347
|
|
|
318
348
|
Usage:
|
|
319
349
|
npx ${NPM_PACKAGE} install
|
|
320
350
|
npx ${NPM_PACKAGE} update
|
|
321
351
|
npx ${NPM_PACKAGE} check-update
|
|
352
|
+
npx ${NPM_PACKAGE} uninstall
|
|
322
353
|
`);
|
|
323
354
|
}
|
|
324
355
|
|