@ztffn/presentation-generator-plugin 1.0.7 → 1.0.9
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 +145 -121
- 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,192 +123,212 @@ 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, installDir) {
|
|
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 (!settings.enabledPlugins || typeof settings.enabledPlugins !== "object" || Array.isArray(settings.enabledPlugins)) {
|
|
174
|
+
settings.enabledPlugins = {};
|
|
175
|
+
}
|
|
166
176
|
|
|
167
|
-
|
|
168
|
-
settings.enabledPlugins.push(INSTALL_DIR);
|
|
169
|
-
}
|
|
177
|
+
settings.enabledPlugins[PLUGIN_NAME] = installDir;
|
|
170
178
|
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
|
|
180
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
181
|
+
}
|
|
173
182
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
183
|
+
function removeFromSettings(settingsPath) {
|
|
184
|
+
if (!fs.existsSync(settingsPath)) return;
|
|
185
|
+
try {
|
|
186
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
187
|
+
if (!settings.enabledPlugins || typeof settings.enabledPlugins !== "object") return;
|
|
188
|
+
if (PLUGIN_NAME in settings.enabledPlugins) {
|
|
189
|
+
delete settings.enabledPlugins[PLUGIN_NAME];
|
|
190
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
191
|
+
console.log(`Removed entry from ${settingsPath}`);
|
|
182
192
|
}
|
|
183
193
|
} catch {
|
|
184
|
-
|
|
185
|
-
console.log(JSON.stringify({ enabledPlugins: [INSTALL_DIR] }, null, 2));
|
|
186
|
-
console.log(`\nOr load for a single session:`);
|
|
187
|
-
console.log(` claude --plugin-dir "${INSTALL_DIR}"`);
|
|
194
|
+
// ignore
|
|
188
195
|
}
|
|
189
196
|
}
|
|
190
197
|
|
|
191
198
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
192
199
|
|
|
193
200
|
async function install() {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
201
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
202
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
203
|
+
|
|
204
|
+
if (globalExists || projectExists) {
|
|
205
|
+
const meta = await getLatestNpmMeta();
|
|
206
|
+
if (globalExists) {
|
|
207
|
+
const v = getInstalledVersion(GLOBAL_INSTALL_DIR);
|
|
208
|
+
if (meta && v !== meta.version) {
|
|
209
|
+
console.log(`\nGlobal install found (v${v}). v${meta.version} available — run: npx ${NPM_PACKAGE} update\n`);
|
|
210
|
+
} else {
|
|
211
|
+
console.log(`\nGlobal install already up to date (v${v || "unknown"}).\n`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (projectExists) {
|
|
215
|
+
const v = getInstalledVersion(PROJECT_INSTALL_DIR);
|
|
216
|
+
if (meta && v !== meta.version) {
|
|
217
|
+
console.log(`\nProject install found (v${v}). v${meta.version} available — run: npx ${NPM_PACKAGE} update\n`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log(`\nProject install already up to date (v${v || "unknown"}).\n`);
|
|
220
|
+
}
|
|
208
221
|
}
|
|
209
|
-
registerPlugin();
|
|
210
222
|
return;
|
|
211
223
|
}
|
|
212
224
|
|
|
213
|
-
|
|
214
|
-
|
|
225
|
+
const meta = await getLatestNpmMeta();
|
|
215
226
|
if (!meta) {
|
|
216
|
-
console.error(
|
|
217
|
-
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
218
|
-
);
|
|
227
|
+
console.error("\nCould not fetch latest version from npm registry. Check your internet connection.\n");
|
|
219
228
|
process.exit(1);
|
|
220
229
|
}
|
|
221
230
|
|
|
222
|
-
await
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
const { installDir, settingsPath, scope } = await resolveInstallTarget();
|
|
232
|
+
|
|
233
|
+
console.log(`\nInstalling presentation-generator plugin...`);
|
|
234
|
+
await downloadAndExtract(meta.version, meta.tarball, installDir);
|
|
235
|
+
|
|
236
|
+
const label = scope === "project" ? `.claude/plugins/${PLUGIN_NAME}` : installDir;
|
|
237
|
+
console.log(`\nInstalled v${meta.version} to: ${label}`);
|
|
238
|
+
|
|
239
|
+
console.log("\nRegistering plugin with Claude Code...");
|
|
240
|
+
writeSettings(settingsPath, installDir);
|
|
241
|
+
|
|
242
|
+
const settingsLabel = scope === "project" ? ".claude/settings.json" : "~/.claude/settings.json";
|
|
243
|
+
console.log(`Plugin registered in ${settingsLabel} ✓`);
|
|
225
244
|
}
|
|
226
245
|
|
|
227
246
|
async function update() {
|
|
228
|
-
|
|
247
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
248
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
249
|
+
|
|
250
|
+
if (!globalExists && !projectExists) {
|
|
229
251
|
console.log("\nPlugin not installed. Run install first:");
|
|
230
252
|
console.log(` npx ${NPM_PACKAGE} install\n`);
|
|
231
253
|
process.exit(1);
|
|
232
254
|
}
|
|
233
255
|
|
|
234
|
-
const before = getInstalledVersion();
|
|
235
256
|
const meta = await getLatestNpmMeta();
|
|
236
|
-
|
|
237
257
|
if (!meta) {
|
|
238
|
-
console.error(
|
|
239
|
-
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
240
|
-
);
|
|
258
|
+
console.error("\nCould not fetch latest version from npm registry. Check your internet connection.\n");
|
|
241
259
|
process.exit(1);
|
|
242
260
|
}
|
|
243
261
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
for (const [installDir, settingsPath, label] of [
|
|
263
|
+
[GLOBAL_INSTALL_DIR, path.join(os.homedir(), ".claude", "settings.json"), "Global"],
|
|
264
|
+
[PROJECT_INSTALL_DIR, path.join(process.cwd(), ".claude", "settings.json"), "Project"],
|
|
265
|
+
]) {
|
|
266
|
+
if (!fs.existsSync(installDir)) continue;
|
|
267
|
+
|
|
268
|
+
const before = getInstalledVersion(installDir);
|
|
269
|
+
if (before === meta.version) {
|
|
270
|
+
console.log(`\n${label}: already up to date (v${before}) ✓`);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
249
273
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
registerPlugin();
|
|
274
|
+
console.log(`\n${label}: updating v${before || "unknown"} → v${meta.version}...`);
|
|
275
|
+
await downloadAndExtract(meta.version, meta.tarball, installDir);
|
|
276
|
+
writeSettings(settingsPath, installDir);
|
|
277
|
+
console.log(`${label}: updated to v${meta.version} ✓`);
|
|
278
|
+
}
|
|
256
279
|
}
|
|
257
280
|
|
|
258
281
|
async function checkUpdate() {
|
|
259
|
-
|
|
282
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
283
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
284
|
+
|
|
285
|
+
if (!globalExists && !projectExists) {
|
|
260
286
|
console.log("\nPlugin not installed.\n");
|
|
261
287
|
return;
|
|
262
288
|
}
|
|
263
289
|
|
|
264
|
-
const installed = getInstalledVersion();
|
|
265
290
|
const meta = await getLatestNpmMeta();
|
|
266
291
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function removeFromSettings(settingsPath) {
|
|
283
|
-
if (!fs.existsSync(settingsPath)) return;
|
|
284
|
-
try {
|
|
285
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
|
|
286
|
-
if (!Array.isArray(settings.enabledPlugins)) return;
|
|
287
|
-
const before = settings.enabledPlugins.length;
|
|
288
|
-
settings.enabledPlugins = settings.enabledPlugins.filter((p) => p !== INSTALL_DIR);
|
|
289
|
-
if (settings.enabledPlugins.length !== before) {
|
|
290
|
-
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
291
|
-
console.log(`Removed from ${settingsPath}`);
|
|
292
|
+
for (const [installDir, label] of [
|
|
293
|
+
[GLOBAL_INSTALL_DIR, "Global"],
|
|
294
|
+
[PROJECT_INSTALL_DIR, "Project"],
|
|
295
|
+
]) {
|
|
296
|
+
if (!fs.existsSync(installDir)) continue;
|
|
297
|
+
const installed = getInstalledVersion(installDir);
|
|
298
|
+
|
|
299
|
+
if (!meta) {
|
|
300
|
+
console.log(`\n${label}: v${installed || "unknown"} (could not reach npm registry)`);
|
|
301
|
+
} else if (installed === meta.version) {
|
|
302
|
+
console.log(`\n${label}: up to date (v${installed}) ✓`);
|
|
303
|
+
} else {
|
|
304
|
+
console.log(`\n${label}: update available v${installed} → v${meta.version}`);
|
|
305
|
+
console.log(` Run: npx ${NPM_PACKAGE} update`);
|
|
292
306
|
}
|
|
293
|
-
} catch {
|
|
294
|
-
// ignore
|
|
295
307
|
}
|
|
308
|
+
console.log();
|
|
296
309
|
}
|
|
297
310
|
|
|
298
311
|
function uninstall() {
|
|
299
|
-
|
|
312
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
313
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
314
|
+
|
|
315
|
+
if (!globalExists && !projectExists) {
|
|
300
316
|
console.log("\nPlugin not installed.\n");
|
|
301
317
|
return;
|
|
302
318
|
}
|
|
303
|
-
fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
304
|
-
console.log(`\nRemoved: ${INSTALL_DIR}`);
|
|
305
319
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
320
|
+
if (globalExists) {
|
|
321
|
+
fs.rmSync(GLOBAL_INSTALL_DIR, { recursive: true, force: true });
|
|
322
|
+
console.log(`\nRemoved: ${GLOBAL_INSTALL_DIR}`);
|
|
323
|
+
removeFromSettings(path.join(os.homedir(), ".claude", "settings.json"));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (projectExists) {
|
|
327
|
+
fs.rmSync(PROJECT_INSTALL_DIR, { recursive: true, force: true });
|
|
328
|
+
console.log(`\nRemoved: .claude/plugins/${PLUGIN_NAME}`);
|
|
329
|
+
removeFromSettings(path.join(process.cwd(), ".claude", "settings.json"));
|
|
330
|
+
}
|
|
331
|
+
|
|
309
332
|
console.log();
|
|
310
333
|
}
|
|
311
334
|
|
|
@@ -314,15 +337,16 @@ function help() {
|
|
|
314
337
|
presentation-generator-plugin v${CURRENT_VERSION}
|
|
315
338
|
|
|
316
339
|
Commands:
|
|
317
|
-
install Download
|
|
340
|
+
install Download and install the plugin, register with Claude Code
|
|
318
341
|
update Download and install the latest version from npm
|
|
319
342
|
check-update Report whether an update is available
|
|
320
|
-
uninstall Remove plugin
|
|
343
|
+
uninstall Remove the plugin
|
|
321
344
|
|
|
322
345
|
Usage:
|
|
323
346
|
npx ${NPM_PACKAGE} install
|
|
324
347
|
npx ${NPM_PACKAGE} update
|
|
325
348
|
npx ${NPM_PACKAGE} check-update
|
|
349
|
+
npx ${NPM_PACKAGE} uninstall
|
|
326
350
|
`);
|
|
327
351
|
}
|
|
328
352
|
|