@ztffn/presentation-generator-plugin 1.0.4 → 1.0.6

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.
Files changed (2) hide show
  1. package/bin/index.js +86 -39
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -5,6 +5,7 @@ const { execSync } = require("child_process");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
7
  const os = require("os");
8
+ const readline = require("readline");
8
9
 
9
10
  const NPM_PACKAGE = "@ztffn/presentation-generator-plugin";
10
11
  const PLUGIN_NAME = "presentation-generator";
@@ -63,20 +64,19 @@ function downloadFile(url, dest) {
63
64
  });
64
65
  }
65
66
 
66
- async function getLatestNpmVersion() {
67
+ async function getLatestNpmMeta() {
67
68
  try {
68
69
  const data = await fetchJson(
69
70
  `https://registry.npmjs.org/${NPM_PACKAGE}/latest`
70
71
  );
71
- return data.version || null;
72
+ if (!data.version || !data.dist?.tarball) return null;
73
+ return { version: data.version, tarball: data.dist.tarball };
72
74
  } catch {
73
75
  return null;
74
76
  }
75
77
  }
76
78
 
77
- async function downloadAndExtract(version) {
78
- const pkgSlug = NPM_PACKAGE.replace("@", "").replace("/", "-");
79
- const tarballUrl = `https://registry.npmjs.org/${NPM_PACKAGE}/-/${pkgSlug}-${version}.tgz`;
79
+ async function downloadAndExtract(version, tarballUrl) {
80
80
  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-plugin-"));
81
81
  const tarball = path.join(tmpDir, "plugin.tgz");
82
82
 
@@ -101,14 +101,43 @@ async function downloadAndExtract(version) {
101
101
  fs.rmSync(tmpDir, { recursive: true, force: true });
102
102
  }
103
103
 
104
- function registerPlugin() {
104
+ function prompt(question) {
105
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
106
+ return new Promise((resolve) => {
107
+ rl.question(question, (answer) => {
108
+ rl.close();
109
+ resolve(answer.trim().toLowerCase());
110
+ });
111
+ });
112
+ }
113
+
114
+ async function resolveSettingsPath() {
115
+ const projectClaudeDir = path.join(process.cwd(), ".claude");
116
+ const userSettingsPath = path.join(os.homedir(), ".claude", "settings.json");
117
+
118
+ if (!fs.existsSync(projectClaudeDir)) {
119
+ return { settingsPath: userSettingsPath, scope: "user" };
120
+ }
121
+
122
+ console.log("\nDetected a .claude/ folder in the current directory.");
123
+ const answer = await prompt(
124
+ "Install for (u) user — available everywhere, or (p) project — this directory only? [u/p]: "
125
+ );
126
+
127
+ if (answer === "p") {
128
+ return {
129
+ settingsPath: path.join(projectClaudeDir, "settings.json"),
130
+ scope: "project",
131
+ };
132
+ }
133
+
134
+ return { settingsPath: userSettingsPath, scope: "user" };
135
+ }
136
+
137
+ async function registerPlugin() {
105
138
  console.log("\nRegistering plugin with Claude Code...");
106
139
 
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");
140
+ const { settingsPath, scope } = await resolveSettingsPath();
112
141
 
113
142
  try {
114
143
  let settings = {};
@@ -127,18 +156,15 @@ function registerPlugin() {
127
156
  fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
128
157
  fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
129
158
 
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.");
159
+ if (scope === "project") {
160
+ console.log(`Plugin registered in .claude/settings.json ✓`);
161
+ console.log("Commit that file to enable the plugin for your whole team.");
162
+ } else {
163
+ console.log(`Plugin registered in ~/.claude/settings.json ✓`);
164
+ console.log("Available in all your Claude Code sessions.");
137
165
  }
138
166
  } catch {
139
- console.log(
140
- "\nCould not write settings. Add manually to .claude/settings.json:"
141
- );
167
+ console.log("\nCould not write settings. Add manually to ~/.claude/settings.json:");
142
168
  console.log(JSON.stringify({ enabledPlugins: [INSTALL_DIR] }, null, 2));
143
169
  console.log(`\nOr load for a single session:`);
144
170
  console.log(` claude --plugin-dir "${INSTALL_DIR}"`);
@@ -148,14 +174,15 @@ function registerPlugin() {
148
174
  // ── Commands ──────────────────────────────────────────────────────────────────
149
175
 
150
176
  async function install() {
177
+ const meta = await getLatestNpmMeta();
178
+
151
179
  if (fs.existsSync(INSTALL_DIR)) {
152
180
  const installed = getInstalledVersion();
153
- const latest = await getLatestNpmVersion();
154
181
 
155
- if (latest && installed && installed !== latest) {
182
+ if (meta && installed && installed !== meta.version) {
156
183
  console.log(`\nPlugin already installed (v${installed}).`);
157
184
  console.log(
158
- `v${latest} is available — run: npx ${NPM_PACKAGE} update\n`
185
+ `v${meta.version} is available — run: npx ${NPM_PACKAGE} update\n`
159
186
  );
160
187
  } else {
161
188
  console.log(
@@ -168,16 +195,15 @@ async function install() {
168
195
 
169
196
  console.log(`\nInstalling presentation-generator plugin...`);
170
197
 
171
- const version = await getLatestNpmVersion();
172
- if (!version) {
198
+ if (!meta) {
173
199
  console.error(
174
200
  "\nCould not fetch latest version from npm registry. Check your internet connection.\n"
175
201
  );
176
202
  process.exit(1);
177
203
  }
178
204
 
179
- await downloadAndExtract(version);
180
- console.log(`\nInstalled v${version} to: ${INSTALL_DIR}`);
205
+ await downloadAndExtract(meta.version, meta.tarball);
206
+ console.log(`\nInstalled v${meta.version} to: ${INSTALL_DIR}`);
181
207
  registerPlugin();
182
208
  }
183
209
 
@@ -189,26 +215,26 @@ async function update() {
189
215
  }
190
216
 
191
217
  const before = getInstalledVersion();
192
- const latest = await getLatestNpmVersion();
218
+ const meta = await getLatestNpmMeta();
193
219
 
194
- if (!latest) {
220
+ if (!meta) {
195
221
  console.error(
196
222
  "\nCould not fetch latest version from npm registry. Check your internet connection.\n"
197
223
  );
198
224
  process.exit(1);
199
225
  }
200
226
 
201
- if (before === latest) {
227
+ if (before === meta.version) {
202
228
  console.log(`\nAlready up to date (v${before}) ✓`);
203
229
  registerPlugin();
204
230
  return;
205
231
  }
206
232
 
207
233
  console.log(
208
- `\nUpdating presentation-generator plugin (v${before || "unknown"} → v${latest})...`
234
+ `\nUpdating presentation-generator plugin (v${before || "unknown"} → v${meta.version})...`
209
235
  );
210
- await downloadAndExtract(latest);
211
- console.log(`\nUpdated to v${latest} ✓`);
236
+ await downloadAndExtract(meta.version, meta.tarball);
237
+ console.log(`\nUpdated to v${meta.version} ✓`);
212
238
  registerPlugin();
213
239
  }
214
240
 
@@ -219,30 +245,51 @@ async function checkUpdate() {
219
245
  }
220
246
 
221
247
  const installed = getInstalledVersion();
222
- const latest = await getLatestNpmVersion();
248
+ const meta = await getLatestNpmMeta();
223
249
 
224
- if (!latest) {
250
+ if (!meta) {
225
251
  console.log(
226
252
  `\nInstalled: v${installed || "unknown"} (could not reach npm registry)\n`
227
253
  );
228
254
  return;
229
255
  }
230
256
 
231
- if (installed === latest) {
257
+ if (installed === meta.version) {
232
258
  console.log(`\nUp to date: v${installed} ✓\n`);
233
259
  } else {
234
- console.log(`\nUpdate available: v${installed} → v${latest}`);
260
+ console.log(`\nUpdate available: v${installed} → v${meta.version}`);
235
261
  console.log(`Run: npx ${NPM_PACKAGE} update\n`);
236
262
  }
237
263
  }
238
264
 
265
+ function removeFromSettings(settingsPath) {
266
+ if (!fs.existsSync(settingsPath)) return;
267
+ try {
268
+ const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
269
+ if (!Array.isArray(settings.enabledPlugins)) return;
270
+ const before = settings.enabledPlugins.length;
271
+ settings.enabledPlugins = settings.enabledPlugins.filter((p) => p !== INSTALL_DIR);
272
+ if (settings.enabledPlugins.length !== before) {
273
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
274
+ console.log(`Removed from ${settingsPath}`);
275
+ }
276
+ } catch {
277
+ // ignore
278
+ }
279
+ }
280
+
239
281
  function uninstall() {
240
282
  if (!fs.existsSync(INSTALL_DIR)) {
241
283
  console.log("\nPlugin not installed.\n");
242
284
  return;
243
285
  }
244
286
  fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
245
- console.log(`\nRemoved: ${INSTALL_DIR}\n`);
287
+ console.log(`\nRemoved: ${INSTALL_DIR}`);
288
+
289
+ // Clean up both possible settings locations
290
+ removeFromSettings(path.join(os.homedir(), ".claude", "settings.json"));
291
+ removeFromSettings(path.join(process.cwd(), ".claude", "settings.json"));
292
+ console.log();
246
293
  }
247
294
 
248
295
  function help() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ztffn/presentation-generator-plugin",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Claude Code plugin for generating graph-based presentations",
5
5
  "bin": {
6
6
  "presentation-generator-plugin": "bin/index.js"