@ztffn/presentation-generator-plugin 1.0.8 → 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 -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,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
|
-
|
|
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}`);
|
|
178
192
|
}
|
|
179
193
|
} 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}"`);
|
|
194
|
+
// ignore
|
|
184
195
|
}
|
|
185
196
|
}
|
|
186
197
|
|
|
187
198
|
// ── Commands ──────────────────────────────────────────────────────────────────
|
|
188
199
|
|
|
189
200
|
async function install() {
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
+
}
|
|
204
221
|
}
|
|
205
|
-
registerPlugin();
|
|
206
222
|
return;
|
|
207
223
|
}
|
|
208
224
|
|
|
209
|
-
|
|
210
|
-
|
|
225
|
+
const meta = await getLatestNpmMeta();
|
|
211
226
|
if (!meta) {
|
|
212
|
-
console.error(
|
|
213
|
-
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
214
|
-
);
|
|
227
|
+
console.error("\nCould not fetch latest version from npm registry. Check your internet connection.\n");
|
|
215
228
|
process.exit(1);
|
|
216
229
|
}
|
|
217
230
|
|
|
218
|
-
await
|
|
219
|
-
|
|
220
|
-
|
|
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} ✓`);
|
|
221
244
|
}
|
|
222
245
|
|
|
223
246
|
async function update() {
|
|
224
|
-
|
|
247
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
248
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
249
|
+
|
|
250
|
+
if (!globalExists && !projectExists) {
|
|
225
251
|
console.log("\nPlugin not installed. Run install first:");
|
|
226
252
|
console.log(` npx ${NPM_PACKAGE} install\n`);
|
|
227
253
|
process.exit(1);
|
|
228
254
|
}
|
|
229
255
|
|
|
230
|
-
const before = getInstalledVersion();
|
|
231
256
|
const meta = await getLatestNpmMeta();
|
|
232
|
-
|
|
233
257
|
if (!meta) {
|
|
234
|
-
console.error(
|
|
235
|
-
"\nCould not fetch latest version from npm registry. Check your internet connection.\n"
|
|
236
|
-
);
|
|
258
|
+
console.error("\nCould not fetch latest version from npm registry. Check your internet connection.\n");
|
|
237
259
|
process.exit(1);
|
|
238
260
|
}
|
|
239
261
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
+
}
|
|
245
273
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
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
|
+
}
|
|
252
279
|
}
|
|
253
280
|
|
|
254
281
|
async function checkUpdate() {
|
|
255
|
-
|
|
282
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
283
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
284
|
+
|
|
285
|
+
if (!globalExists && !projectExists) {
|
|
256
286
|
console.log("\nPlugin not installed.\n");
|
|
257
287
|
return;
|
|
258
288
|
}
|
|
259
289
|
|
|
260
|
-
const installed = getInstalledVersion();
|
|
261
290
|
const meta = await getLatestNpmMeta();
|
|
262
291
|
|
|
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}`);
|
|
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`);
|
|
288
306
|
}
|
|
289
|
-
} catch {
|
|
290
|
-
// ignore
|
|
291
307
|
}
|
|
308
|
+
console.log();
|
|
292
309
|
}
|
|
293
310
|
|
|
294
311
|
function uninstall() {
|
|
295
|
-
|
|
312
|
+
const globalExists = fs.existsSync(GLOBAL_INSTALL_DIR);
|
|
313
|
+
const projectExists = fs.existsSync(PROJECT_INSTALL_DIR);
|
|
314
|
+
|
|
315
|
+
if (!globalExists && !projectExists) {
|
|
296
316
|
console.log("\nPlugin not installed.\n");
|
|
297
317
|
return;
|
|
298
318
|
}
|
|
299
|
-
fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
|
|
300
|
-
console.log(`\nRemoved: ${INSTALL_DIR}`);
|
|
301
319
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
+
|
|
305
332
|
console.log();
|
|
306
333
|
}
|
|
307
334
|
|
|
@@ -310,15 +337,16 @@ function help() {
|
|
|
310
337
|
presentation-generator-plugin v${CURRENT_VERSION}
|
|
311
338
|
|
|
312
339
|
Commands:
|
|
313
|
-
install Download
|
|
340
|
+
install Download and install the plugin, register with Claude Code
|
|
314
341
|
update Download and install the latest version from npm
|
|
315
342
|
check-update Report whether an update is available
|
|
316
|
-
uninstall Remove plugin
|
|
343
|
+
uninstall Remove the plugin
|
|
317
344
|
|
|
318
345
|
Usage:
|
|
319
346
|
npx ${NPM_PACKAGE} install
|
|
320
347
|
npx ${NPM_PACKAGE} update
|
|
321
348
|
npx ${NPM_PACKAGE} check-update
|
|
349
|
+
npx ${NPM_PACKAGE} uninstall
|
|
322
350
|
`);
|
|
323
351
|
}
|
|
324
352
|
|