@ztffn/presentation-generator-plugin 1.0.3 → 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.
Files changed (2) hide show
  1. package/bin/index.js +175 -72
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -1,136 +1,230 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const { execSync, spawnSync } = require("child_process");
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 REPO_URL =
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
- function run(cmd, opts = {}) {
17
- return execSync(cmd, { stdio: "inherit", ...opts });
18
- }
16
+ // ── Helpers ───────────────────────────────────────────────────────────────────
19
17
 
20
- function runCapture(cmd) {
18
+ function getInstalledVersion() {
19
+ const pkgPath = path.join(INSTALL_DIR, "package.json");
20
+ if (!fs.existsSync(pkgPath)) return null;
21
21
  try {
22
- return execSync(cmd, { encoding: "utf8", stderr: "pipe" }).trim();
22
+ return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version;
23
23
  } catch {
24
24
  return null;
25
25
  }
26
26
  }
27
27
 
28
- function checkGitAuth() {
29
- const result = spawnSync("gh", ["auth", "status"], { encoding: "utf8" });
30
- if (result.status !== 0) {
31
- console.error("\nGitHub authentication required.");
32
- console.error("Run: gh auth login\n");
33
- process.exit(1);
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 getInstalledVersion() {
38
- const pkgPath = path.join(INSTALL_DIR, "package.json");
39
- if (!fs.existsSync(pkgPath)) return null;
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
- return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version;
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 getLatestRemoteVersion() {
48
- const result = runCapture(
49
- `git ls-remote --tags ${REPO_URL} | grep -o 'v[0-9]*\\.[0-9]*\\.[0-9]*' | sort -V | tail -1`
50
- );
51
- return result ? result.replace(/^v/, "") : null;
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");
82
+
83
+ console.log(`Downloading v${version} from npm...`);
84
+ await downloadFile(tarballUrl, tarball);
85
+
86
+ console.log("Extracting...");
87
+ execSync(`tar -xzf "${tarball}" -C "${tmpDir}"`);
88
+
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");
94
+ }
95
+
96
+ if (fs.existsSync(INSTALL_DIR)) {
97
+ fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
98
+ }
99
+ fs.mkdirSync(path.dirname(INSTALL_DIR), { recursive: true });
100
+ fs.renameSync(extracted, INSTALL_DIR);
101
+ fs.rmSync(tmpDir, { recursive: true, force: true });
52
102
  }
53
103
 
54
104
  function registerPlugin() {
55
105
  console.log("\nRegistering plugin with Claude Code...");
56
106
 
57
- const result = spawnSync(
58
- "claude",
59
- ["plugin", "install", INSTALL_DIR, "--scope", "project"],
60
- { encoding: "utf8", stdio: "inherit" }
61
- );
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");
62
112
 
63
- if (result.status === 0) {
64
- console.log("Plugin registered in .claude/settings.json ✓");
65
- console.log("Commit that file to share the plugin with your team.");
66
- } else {
67
- console.log("\nCould not auto-register. Add manually:");
68
- console.log(` claude plugin install "${INSTALL_DIR}" --scope project`);
69
- console.log(` Or load for a single session:`);
113
+ try {
114
+ let settings = {};
115
+ if (fs.existsSync(settingsPath)) {
116
+ settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
117
+ }
118
+
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 });
128
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
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:`);
70
144
  console.log(` claude --plugin-dir "${INSTALL_DIR}"`);
71
145
  }
72
146
  }
73
147
 
74
148
  // ── Commands ──────────────────────────────────────────────────────────────────
75
149
 
76
- function install() {
77
- checkGitAuth();
78
-
150
+ async function install() {
79
151
  if (fs.existsSync(INSTALL_DIR)) {
80
152
  const installed = getInstalledVersion();
81
- const latest = getLatestRemoteVersion();
153
+ const latest = await getLatestNpmVersion();
82
154
 
83
155
  if (latest && installed && installed !== latest) {
84
156
  console.log(`\nPlugin already installed (v${installed}).`);
85
- console.log(`v${latest} is available — run: npx @ztffn/presentation-generator-plugin update\n`);
157
+ console.log(
158
+ `v${latest} is available — run: npx ${NPM_PACKAGE} update\n`
159
+ );
86
160
  } else {
87
- console.log(`\nPlugin already installed (v${installed || "unknown"}). Nothing to do.\n`);
161
+ console.log(
162
+ `\nPlugin already installed (v${installed || "unknown"}). Nothing to do.\n`
163
+ );
88
164
  }
89
165
  registerPlugin();
90
166
  return;
91
167
  }
92
168
 
93
169
  console.log(`\nInstalling presentation-generator plugin...`);
94
- fs.mkdirSync(path.dirname(INSTALL_DIR), { recursive: true });
95
- run(`git clone ${REPO_URL} "${INSTALL_DIR}"`);
96
- console.log(`\nInstalled to: ${INSTALL_DIR}`);
170
+
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}`);
97
181
  registerPlugin();
98
182
  }
99
183
 
100
- function update() {
101
- checkGitAuth();
102
-
184
+ async function update() {
103
185
  if (!fs.existsSync(INSTALL_DIR)) {
104
186
  console.log("\nPlugin not installed. Run install first:");
105
- console.log(" npx @ztffn/presentation-generator-plugin install\n");
187
+ console.log(` npx ${NPM_PACKAGE} install\n`);
106
188
  process.exit(1);
107
189
  }
108
190
 
109
191
  const before = getInstalledVersion();
110
- console.log(`\nUpdating presentation-generator plugin (current: v${before || "unknown"})...`);
111
- run(`git -C "${INSTALL_DIR}" pull`);
192
+ const latest = await getLatestNpmVersion();
112
193
 
113
- const after = getInstalledVersion();
114
- if (before !== after) {
115
- console.log(`\nUpdated v${before} v${after} ✓`);
116
- } else {
117
- console.log(`\nAlready up to date (v${after}) ✓`);
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);
199
+ }
200
+
201
+ if (before === latest) {
202
+ console.log(`\nAlready up to date (v${before}) ✓`);
203
+ registerPlugin();
204
+ return;
118
205
  }
119
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} ✓`);
120
212
  registerPlugin();
121
213
  }
122
214
 
123
- function checkUpdate() {
215
+ async function checkUpdate() {
124
216
  if (!fs.existsSync(INSTALL_DIR)) {
125
- console.log("\nPlugin not installed.");
217
+ console.log("\nPlugin not installed.\n");
126
218
  return;
127
219
  }
128
220
 
129
221
  const installed = getInstalledVersion();
130
- const latest = getLatestRemoteVersion();
222
+ const latest = await getLatestNpmVersion();
131
223
 
132
224
  if (!latest) {
133
- console.log(`\nInstalled: v${installed || "unknown"} (could not reach remote)\n`);
225
+ console.log(
226
+ `\nInstalled: v${installed || "unknown"} (could not reach npm registry)\n`
227
+ );
134
228
  return;
135
229
  }
136
230
 
@@ -138,7 +232,7 @@ function checkUpdate() {
138
232
  console.log(`\nUp to date: v${installed} ✓\n`);
139
233
  } else {
140
234
  console.log(`\nUpdate available: v${installed} → v${latest}`);
141
- console.log(`Run: npx @ztffn/presentation-generator-plugin update\n`);
235
+ console.log(`Run: npx ${NPM_PACKAGE} update\n`);
142
236
  }
143
237
  }
144
238
 
@@ -148,8 +242,7 @@ function uninstall() {
148
242
  return;
149
243
  }
150
244
  fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
151
- console.log(`\nRemoved: ${INSTALL_DIR}`);
152
- console.log("You may also want to run: claude plugin uninstall presentation-generator\n");
245
+ console.log(`\nRemoved: ${INSTALL_DIR}\n`);
153
246
  }
154
247
 
155
248
  function help() {
@@ -157,21 +250,28 @@ function help() {
157
250
  presentation-generator-plugin v${CURRENT_VERSION}
158
251
 
159
252
  Commands:
160
- install Clone plugin to ~/.claude/plugins/ and register with Claude Code
161
- update Pull latest changes from GitHub
253
+ install Download plugin from npm and register with Claude Code
254
+ update Download and install the latest version from npm
162
255
  check-update Report whether an update is available
163
256
  uninstall Remove plugin from ~/.claude/plugins/
164
257
 
165
258
  Usage:
166
- npx @ztffn/presentation-generator-plugin install
167
- npx @ztffn/presentation-generator-plugin update
168
- npx @ztffn/presentation-generator-plugin check-update
169
-
170
- Requires: gh auth login (GitHub authentication)
259
+ npx ${NPM_PACKAGE} install
260
+ npx ${NPM_PACKAGE} update
261
+ npx ${NPM_PACKAGE} check-update
171
262
  `);
172
263
  }
173
264
 
174
- const commands = { install, update, "check-update": checkUpdate, uninstall, help };
265
+ // ── Dispatch ──────────────────────────────────────────────────────────────────
266
+
267
+ const commands = {
268
+ install,
269
+ update,
270
+ "check-update": checkUpdate,
271
+ uninstall,
272
+ help,
273
+ };
274
+
175
275
  const fn = commands[command];
176
276
 
177
277
  if (!fn) {
@@ -180,4 +280,7 @@ if (!fn) {
180
280
  process.exit(1);
181
281
  }
182
282
 
183
- fn();
283
+ Promise.resolve(fn()).catch((err) => {
284
+ console.error("\nError:", err.message, "\n");
285
+ process.exit(1);
286
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ztffn/presentation-generator-plugin",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Claude Code plugin for generating graph-based presentations",
5
5
  "bin": {
6
6
  "presentation-generator-plugin": "bin/index.js"