@ztffn/presentation-generator-plugin 1.0.3 → 1.0.5

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 +177 -75
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -1,144 +1,237 @@
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 getLatestNpmMeta() {
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
+ if (!data.version || !data.dist?.tarball) return null;
72
+ return { version: data.version, tarball: data.dist.tarball };
42
73
  } catch {
43
74
  return null;
44
75
  }
45
76
  }
46
77
 
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;
78
+ async function downloadAndExtract(version, tarballUrl) {
79
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "claude-plugin-"));
80
+ const tarball = path.join(tmpDir, "plugin.tgz");
81
+
82
+ console.log(`Downloading v${version} from npm...`);
83
+ await downloadFile(tarballUrl, tarball);
84
+
85
+ console.log("Extracting...");
86
+ execSync(`tar -xzf "${tarball}" -C "${tmpDir}"`);
87
+
88
+ // npm tarballs always extract to a "package/" subdirectory
89
+ const extracted = path.join(tmpDir, "package");
90
+ if (!fs.existsSync(extracted)) {
91
+ fs.rmSync(tmpDir, { recursive: true, force: true });
92
+ throw new Error("Extraction failed: no package/ directory in tarball");
93
+ }
94
+
95
+ if (fs.existsSync(INSTALL_DIR)) {
96
+ fs.rmSync(INSTALL_DIR, { recursive: true, force: true });
97
+ }
98
+ fs.mkdirSync(path.dirname(INSTALL_DIR), { recursive: true });
99
+ fs.renameSync(extracted, INSTALL_DIR);
100
+ fs.rmSync(tmpDir, { recursive: true, force: true });
52
101
  }
53
102
 
54
103
  function registerPlugin() {
55
104
  console.log("\nRegistering plugin with Claude Code...");
56
105
 
57
- const result = spawnSync(
58
- "claude",
59
- ["plugin", "install", INSTALL_DIR, "--scope", "project"],
60
- { encoding: "utf8", stdio: "inherit" }
61
- );
106
+ // Prefer project-level settings if .claude/ exists in cwd, else user-level
107
+ const projectDir = path.join(process.cwd(), ".claude");
108
+ const settingsPath = fs.existsSync(projectDir)
109
+ ? path.join(projectDir, "settings.json")
110
+ : path.join(os.homedir(), ".claude", "settings.json");
62
111
 
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:`);
112
+ try {
113
+ let settings = {};
114
+ if (fs.existsSync(settingsPath)) {
115
+ settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
116
+ }
117
+
118
+ if (!Array.isArray(settings.enabledPlugins)) {
119
+ settings.enabledPlugins = [];
120
+ }
121
+
122
+ if (!settings.enabledPlugins.includes(INSTALL_DIR)) {
123
+ settings.enabledPlugins.push(INSTALL_DIR);
124
+ }
125
+
126
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
127
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
128
+
129
+ const label = settingsPath.startsWith(process.cwd())
130
+ ? ".claude/settings.json"
131
+ : settingsPath;
132
+ console.log(`Plugin registered in ${label} ✓`);
133
+
134
+ if (settingsPath.startsWith(process.cwd())) {
135
+ console.log("Commit that file to share the plugin with your team.");
136
+ }
137
+ } catch {
138
+ console.log(
139
+ "\nCould not write settings. Add manually to .claude/settings.json:"
140
+ );
141
+ console.log(JSON.stringify({ enabledPlugins: [INSTALL_DIR] }, null, 2));
142
+ console.log(`\nOr load for a single session:`);
70
143
  console.log(` claude --plugin-dir "${INSTALL_DIR}"`);
71
144
  }
72
145
  }
73
146
 
74
147
  // ── Commands ──────────────────────────────────────────────────────────────────
75
148
 
76
- function install() {
77
- checkGitAuth();
149
+ async function install() {
150
+ const meta = await getLatestNpmMeta();
78
151
 
79
152
  if (fs.existsSync(INSTALL_DIR)) {
80
153
  const installed = getInstalledVersion();
81
- const latest = getLatestRemoteVersion();
82
154
 
83
- if (latest && installed && installed !== latest) {
155
+ if (meta && installed && installed !== meta.version) {
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${meta.version} 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
+ if (!meta) {
172
+ console.error(
173
+ "\nCould not fetch latest version from npm registry. Check your internet connection.\n"
174
+ );
175
+ process.exit(1);
176
+ }
177
+
178
+ await downloadAndExtract(meta.version, meta.tarball);
179
+ console.log(`\nInstalled v${meta.version} to: ${INSTALL_DIR}`);
97
180
  registerPlugin();
98
181
  }
99
182
 
100
- function update() {
101
- checkGitAuth();
102
-
183
+ async function update() {
103
184
  if (!fs.existsSync(INSTALL_DIR)) {
104
185
  console.log("\nPlugin not installed. Run install first:");
105
- console.log(" npx @ztffn/presentation-generator-plugin install\n");
186
+ console.log(` npx ${NPM_PACKAGE} install\n`);
106
187
  process.exit(1);
107
188
  }
108
189
 
109
190
  const before = getInstalledVersion();
110
- console.log(`\nUpdating presentation-generator plugin (current: v${before || "unknown"})...`);
111
- run(`git -C "${INSTALL_DIR}" pull`);
191
+ const meta = await getLatestNpmMeta();
112
192
 
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}) ✓`);
193
+ if (!meta) {
194
+ console.error(
195
+ "\nCould not fetch latest version from npm registry. Check your internet connection.\n"
196
+ );
197
+ process.exit(1);
198
+ }
199
+
200
+ if (before === meta.version) {
201
+ console.log(`\nAlready up to date (v${before}) ✓`);
202
+ registerPlugin();
203
+ return;
118
204
  }
119
205
 
206
+ console.log(
207
+ `\nUpdating presentation-generator plugin (v${before || "unknown"} → v${meta.version})...`
208
+ );
209
+ await downloadAndExtract(meta.version, meta.tarball);
210
+ console.log(`\nUpdated to v${meta.version} ✓`);
120
211
  registerPlugin();
121
212
  }
122
213
 
123
- function checkUpdate() {
214
+ async function checkUpdate() {
124
215
  if (!fs.existsSync(INSTALL_DIR)) {
125
- console.log("\nPlugin not installed.");
216
+ console.log("\nPlugin not installed.\n");
126
217
  return;
127
218
  }
128
219
 
129
220
  const installed = getInstalledVersion();
130
- const latest = getLatestRemoteVersion();
221
+ const meta = await getLatestNpmMeta();
131
222
 
132
- if (!latest) {
133
- console.log(`\nInstalled: v${installed || "unknown"} (could not reach remote)\n`);
223
+ if (!meta) {
224
+ console.log(
225
+ `\nInstalled: v${installed || "unknown"} (could not reach npm registry)\n`
226
+ );
134
227
  return;
135
228
  }
136
229
 
137
- if (installed === latest) {
230
+ if (installed === meta.version) {
138
231
  console.log(`\nUp to date: v${installed} ✓\n`);
139
232
  } else {
140
- console.log(`\nUpdate available: v${installed} → v${latest}`);
141
- console.log(`Run: npx @ztffn/presentation-generator-plugin update\n`);
233
+ console.log(`\nUpdate available: v${installed} → v${meta.version}`);
234
+ console.log(`Run: npx ${NPM_PACKAGE} update\n`);
142
235
  }
143
236
  }
144
237
 
@@ -148,8 +241,7 @@ function uninstall() {
148
241
  return;
149
242
  }
150
243
  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");
244
+ console.log(`\nRemoved: ${INSTALL_DIR}\n`);
153
245
  }
154
246
 
155
247
  function help() {
@@ -157,21 +249,28 @@ function help() {
157
249
  presentation-generator-plugin v${CURRENT_VERSION}
158
250
 
159
251
  Commands:
160
- install Clone plugin to ~/.claude/plugins/ and register with Claude Code
161
- update Pull latest changes from GitHub
252
+ install Download plugin from npm and register with Claude Code
253
+ update Download and install the latest version from npm
162
254
  check-update Report whether an update is available
163
255
  uninstall Remove plugin from ~/.claude/plugins/
164
256
 
165
257
  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)
258
+ npx ${NPM_PACKAGE} install
259
+ npx ${NPM_PACKAGE} update
260
+ npx ${NPM_PACKAGE} check-update
171
261
  `);
172
262
  }
173
263
 
174
- const commands = { install, update, "check-update": checkUpdate, uninstall, help };
264
+ // ── Dispatch ──────────────────────────────────────────────────────────────────
265
+
266
+ const commands = {
267
+ install,
268
+ update,
269
+ "check-update": checkUpdate,
270
+ uninstall,
271
+ help,
272
+ };
273
+
175
274
  const fn = commands[command];
176
275
 
177
276
  if (!fn) {
@@ -180,4 +279,7 @@ if (!fn) {
180
279
  process.exit(1);
181
280
  }
182
281
 
183
- fn();
282
+ Promise.resolve(fn()).catch((err) => {
283
+ console.error("\nError:", err.message, "\n");
284
+ process.exit(1);
285
+ });
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.5",
4
4
  "description": "Claude Code plugin for generating graph-based presentations",
5
5
  "bin": {
6
6
  "presentation-generator-plugin": "bin/index.js"