slidemind 0.0.27

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.
@@ -0,0 +1,603 @@
1
+ #!/usr/bin/env node
2
+ // slidemind — single CLI entry point for the slidemind npm package.
3
+ //
4
+ // Subcommands:
5
+ // slidemind init [--for <target>] set up slidemind in a coding harness
6
+ // (register the MCP server + skill);
7
+ // targets: claude | codex | opencode | all
8
+ // (`install` is a back-compat alias)
9
+ // slidemind new <name> scaffold ./decks/<name>/{plan.md,slides/}
10
+ // slidemind build <deck> build self-contained HTML deck
11
+ // slidemind build-pptx <deck> render the deck as native .pptx (via the add-in)
12
+ // slidemind uninstall --for <target>
13
+ // slidemind update [--for <target>] [--ref <version>]
14
+ // npm install -g slidemind@latest + redeploy
15
+ // slidemind --help | -h | --version
16
+ //
17
+ // All deck paths are resolved against the user's current working directory.
18
+ // The brand-agnostic skill (skills/create-slides/) is found relative to this
19
+ // script; the design itself comes from the add-in's bundle over the bridge.
20
+
21
+ import { spawn } from "node:child_process";
22
+ import fs from "node:fs";
23
+ import path from "node:path";
24
+ import { fileURLToPath, pathToFileURL } from "node:url";
25
+
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const __dirname = path.dirname(__filename);
28
+ const PACKAGE_ROOT = path.resolve(__dirname, "..");
29
+ const SRC_DIR = path.join(PACKAGE_ROOT, "src");
30
+ const BUILDERS_DIR = path.join(SRC_DIR, "builders");
31
+ const INSTALLERS_DIR = path.join(SRC_DIR, "installers");
32
+
33
+ function readPackageJson() {
34
+ return JSON.parse(
35
+ fs.readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"),
36
+ );
37
+ }
38
+
39
+ function printHelp() {
40
+ const pkg = readPackageJson();
41
+ process.stdout.write(
42
+ `slidemind ${pkg.version} — drive the slidemind PowerPoint add-in to build native, on-brand decks\n` +
43
+ `\n` +
44
+ `Usage:\n` +
45
+ ` slidemind new <name> Scaffold ./decks/<name>/\n` +
46
+ ` slidemind build <deck> Build self-contained HTML\n` +
47
+ ` slidemind build-pptx <deck> Export native .pptx via the add-in (requires an open host)\n` +
48
+ ` --to <presId> target a specific window (required if >1 open)\n` +
49
+ ` --out <path> output path (default <deck>/<deck>.pptx)\n` +
50
+ ` slidemind search "<query>" Search the brand engine for icons/images (requires an open host)\n` +
51
+ ` --engine <id> engine to search (default: first image engine)\n` +
52
+ ` --to <presId> target a specific window (required if >1 open)\n` +
53
+ ` slidemind download-bundle [deck] Materialize the active corporate design's bundle locally\n` +
54
+ ` --to <presId> target a specific window\n` +
55
+ ` --force re-download even if the cached bundle is current\n` +
56
+ ` slidemind presentations List PowerPoint windows registered with the add-in\n` +
57
+ ` slidemind push <deck> [opts] Build deck and push slides into a running PowerPoint\n` +
58
+ ` --to <presId> target a specific window (required if >1 open)\n` +
59
+ ` --at <index> insert after slide N (0-based); default = append\n` +
60
+ ` --replace delete existing slides first (destructive)\n` +
61
+ ` --slides <ids> comma-separated source slide ids to push\n` +
62
+ ` slidemind init --for <t> Set up slidemind in a harness (registers the MCP server + skill)\n` +
63
+ ` slidemind uninstall --for <t> Remove a previously-installed skill\n` +
64
+ ` slidemind update [--for <t>] Fetch the latest release and redeploy\n` +
65
+ ` --for <t> harness to redeploy into (default: all)\n` +
66
+ ` --ref <ver> pin to a release version (e.g. v0.1.0)\n` +
67
+ ` slidemind --help Show this help\n` +
68
+ ` slidemind --version Print version\n` +
69
+ `\n` +
70
+ `Install targets: claude | codex | opencode | all\n` +
71
+ `Decks live in ./decks/ relative to your current directory.\n` +
72
+ `Override with env: SLIDEMIND_DECKS=/abs/path/to/decks\n`,
73
+ );
74
+ }
75
+
76
+ function fail(msg, code = 1) {
77
+ process.stderr.write(`slidemind: ${msg}\n`);
78
+ process.exit(code);
79
+ }
80
+
81
+ function resolveDecksDir() {
82
+ if (process.env.SLIDEMIND_DECKS) {
83
+ return path.resolve(process.env.SLIDEMIND_DECKS);
84
+ }
85
+ return path.resolve(process.cwd(), "decks");
86
+ }
87
+
88
+ function resolveDeckArg(arg) {
89
+ // A user can pass either "decks/foo" (relative to CWD), "/abs/path/foo",
90
+ // or just "foo" — in which case we look it up under the resolved decks dir.
91
+ if (!arg) return null;
92
+ if (path.isAbsolute(arg)) return arg;
93
+ if (fs.existsSync(path.resolve(process.cwd(), arg))) {
94
+ return path.resolve(process.cwd(), arg);
95
+ }
96
+ const decksDir = resolveDecksDir();
97
+ return path.join(decksDir, arg);
98
+ }
99
+
100
+ function runNode(script, args) {
101
+ return new Promise((resolve, reject) => {
102
+ const child = spawn(process.execPath, [script, ...args], {
103
+ stdio: "inherit",
104
+ env: process.env,
105
+ });
106
+ child.on("error", reject);
107
+ child.on("exit", (code) => {
108
+ if (code === 0) resolve();
109
+ else reject(new Error(`${path.basename(script)} exited with code ${code}`));
110
+ });
111
+ });
112
+ }
113
+
114
+ // ---------- subcommands ----------
115
+
116
+ function splitDeckArg(rest, cmd) {
117
+ // The first non-flag positional is the deck. Everything else passes
118
+ // through to the underlying script verbatim.
119
+ const passthrough = [...rest];
120
+ let deck = null;
121
+ for (let i = 0; i < passthrough.length; i++) {
122
+ if (!passthrough[i].startsWith("-")) {
123
+ deck = passthrough.splice(i, 1)[0];
124
+ break;
125
+ }
126
+ }
127
+ if (!deck) fail(`usage: slidemind ${cmd} <deck>`);
128
+ return { deck, passthrough };
129
+ }
130
+
131
+ async function cmdBuild(rest) {
132
+ const { deck, passthrough } = splitDeckArg(rest, "build");
133
+ await runNode(path.join(BUILDERS_DIR, "build.mjs"), [
134
+ "--deck",
135
+ resolveDeckArg(deck),
136
+ ...passthrough,
137
+ ]);
138
+ }
139
+
140
+ async function cmdBuildPptx(rest) {
141
+ const { deck, passthrough } = splitDeckArg(rest, "build-pptx");
142
+ let to = null;
143
+ let outPath = null;
144
+ for (let i = 0; i < passthrough.length; i++) {
145
+ const a = passthrough[i];
146
+ if (a === "--to") {
147
+ to = passthrough[++i];
148
+ if (!to) fail("--to expects a presId");
149
+ } else if (a === "--out" || a === "-o") {
150
+ outPath = passthrough[++i];
151
+ if (!outPath) fail("--out expects a path");
152
+ } else if (a.startsWith("-")) {
153
+ fail(`unknown flag: ${a}`);
154
+ } else {
155
+ fail(`unexpected argument: ${a}`);
156
+ }
157
+ }
158
+ const deckDir = resolveDeckArg(deck);
159
+ if (!fs.existsSync(path.join(deckDir, "slides"))) {
160
+ fail(`expected 'slides/' folder in ${deckDir}`);
161
+ }
162
+ // The .pptx is rendered server-side by the add-in (one exporter, one truth);
163
+ // this requires an open PowerPoint host with an active corporate design.
164
+ const mod = await import(
165
+ pathToFileURL(path.join(SRC_DIR, "bridge", "build-and-push.mjs")).href
166
+ );
167
+ const result = await mod.buildPptxFile({
168
+ deckDir,
169
+ presentationId: to,
170
+ outPath,
171
+ });
172
+ process.stdout.write(
173
+ `Wrote ${result.outPath} (${result.slideCount ?? "?"} slides) via the slidemind add-in\n`,
174
+ );
175
+ for (const w of result.warnings ?? []) {
176
+ process.stderr.write(` warning: ${w}\n`);
177
+ }
178
+ for (const s of result.skippedSlides ?? []) {
179
+ process.stderr.write(` skipped ${s.stem}: ${s.reason}\n`);
180
+ }
181
+ }
182
+
183
+ async function cmdSearch(rest) {
184
+ let asJson = false;
185
+ let to = null;
186
+ let engineId = null;
187
+ const queryParts = [];
188
+ for (let i = 0; i < rest.length; i++) {
189
+ const a = rest[i];
190
+ if (a === "--json") {
191
+ asJson = true;
192
+ } else if (a === "--to") {
193
+ to = rest[++i];
194
+ if (!to) fail("--to expects a presId");
195
+ } else if (a === "--engine" || a === "-e") {
196
+ engineId = rest[++i];
197
+ if (!engineId) fail("--engine expects an engineId");
198
+ } else if (a.startsWith("-")) {
199
+ fail(`unknown flag: ${a}`);
200
+ } else {
201
+ queryParts.push(a);
202
+ }
203
+ }
204
+ const query = queryParts.join(" ").trim();
205
+ if (!query) {
206
+ fail(
207
+ `usage: slidemind search "<query>" [--engine <id>] [--to <presId>] [--json]`,
208
+ );
209
+ }
210
+
211
+ // Icon/image search now runs against the add-in's live brand engine over the
212
+ // bridge — an open host is required, and the engine comes from the active
213
+ // corporate design's bundle.
214
+ const client = await import(
215
+ pathToFileURL(path.join(SRC_DIR, "bridge", "client.mjs")).href
216
+ );
217
+ const bundle = await import(
218
+ pathToFileURL(path.join(SRC_DIR, "bridge", "bundle.mjs")).href
219
+ );
220
+ const registry = await client.requireHost();
221
+ const presId = client.pickPresentation(registry, to);
222
+
223
+ if (!engineId) {
224
+ const mat = await bundle.materializeBundle(presId);
225
+ const pick = bundle.pickImageEngine(bundle.readEnginesFromDir(mat.dir));
226
+ if (!pick) {
227
+ fail(
228
+ "This corporate design has no brand engines to search. Ask the add-in owner to wire an image/icon engine.",
229
+ );
230
+ }
231
+ engineId = pick.id;
232
+ }
233
+
234
+ const res = await client.engineSearch({ presId, engineId, query });
235
+ if (asJson) {
236
+ process.stdout.write(`${JSON.stringify(res, null, 2)}\n`);
237
+ return;
238
+ }
239
+ if (!res?.ok) {
240
+ process.stderr.write(`search failed: ${res?.error ?? "unknown error"}\n`);
241
+ return;
242
+ }
243
+ const count = res.resultCount ?? (res.thumbnails?.length ?? 0);
244
+ process.stdout.write(
245
+ `searchId ${res.searchId} — ${count} result(s) from engine ${engineId}\n`,
246
+ );
247
+ (res.thumbnails ?? []).forEach((t, i) => {
248
+ process.stdout.write(` ${i + 1}. ${t.title ?? "(untitled)"}\n`);
249
+ });
250
+ process.stdout.write(
251
+ `Download a result by selection number via the agent (slidemind_download_result).\n`,
252
+ );
253
+ }
254
+
255
+ async function cmdDownloadBundle(rest) {
256
+ let to = null;
257
+ let force = false;
258
+ const positional = [];
259
+ for (let i = 0; i < rest.length; i++) {
260
+ const a = rest[i];
261
+ if (a === "--to") {
262
+ to = rest[++i];
263
+ if (!to) fail("--to expects a presId");
264
+ } else if (a === "--force") {
265
+ force = true;
266
+ } else if (a.startsWith("-")) {
267
+ fail(`unknown flag: ${a}`);
268
+ } else {
269
+ positional.push(a);
270
+ }
271
+ }
272
+ const client = await import(
273
+ pathToFileURL(path.join(SRC_DIR, "bridge", "client.mjs")).href
274
+ );
275
+ const bundle = await import(
276
+ pathToFileURL(path.join(SRC_DIR, "bridge", "bundle.mjs")).href
277
+ );
278
+ const registry = await client.requireHost();
279
+ const presId = client.pickPresentation(registry, to);
280
+ const result = await bundle.materializeBundle(presId, { force });
281
+
282
+ // Optionally bind a deck to this bundle so build-pptx/push pass the right
283
+ // etag and the local HTML/editor read the right design files.
284
+ const deckArg = positional[0];
285
+ if (deckArg) {
286
+ const deckDir = resolveDeckArg(deckArg);
287
+ bundle.writeDeckBundleRef(deckDir, {
288
+ presFP: result.presFP,
289
+ etag: result.etag,
290
+ });
291
+ }
292
+ process.stdout.write(
293
+ `${result.cached ? "Bundle already current" : "Materialized bundle"} for ${result.presFP} (etag ${result.etag ?? "?"})\n ${result.dir}\n`,
294
+ );
295
+ // Asset downloads are best-effort (a signed URL may have expired); surface any
296
+ // failures so the user knows the local preview bundle is incomplete.
297
+ for (const w of result.warnings ?? []) {
298
+ process.stderr.write(` warning: ${w}\n`);
299
+ }
300
+ }
301
+
302
+ async function cmdPresentations(rest) {
303
+ const asJson = rest.includes("--json");
304
+ const mod = await import(
305
+ pathToFileURL(path.join(SRC_DIR, "bridge", "client.mjs")).href
306
+ );
307
+ const list = await mod.listOpenPresentations();
308
+ if (asJson) {
309
+ process.stdout.write(JSON.stringify(list, null, 2) + "\n");
310
+ return;
311
+ }
312
+ if (list.length === 0) {
313
+ process.stderr.write(
314
+ "No PowerPoint windows registered with the slidemind add-in.\n",
315
+ );
316
+ return;
317
+ }
318
+ for (const p of list) {
319
+ const label = p.title || p.fileName || "(untitled)";
320
+ process.stdout.write(`${p.presId}\t${label}\n`);
321
+ }
322
+ }
323
+
324
+ async function cmdPush(rest) {
325
+ const { deck, passthrough } = splitDeckArg(rest, "push");
326
+ let to = null;
327
+ let at = null;
328
+ let replace = false;
329
+ let slides = null;
330
+ for (let i = 0; i < passthrough.length; i++) {
331
+ const a = passthrough[i];
332
+ if (a === "--to") {
333
+ to = passthrough[++i];
334
+ if (!to) fail("--to expects a presId");
335
+ } else if (a === "--at") {
336
+ const next = passthrough[++i];
337
+ const n = parseInt(next, 10);
338
+ if (!Number.isFinite(n) || n < 0) {
339
+ fail(`--at expects a non-negative integer, got '${next}'`);
340
+ }
341
+ at = n;
342
+ } else if (a === "--replace") {
343
+ replace = true;
344
+ } else if (a === "--slides") {
345
+ const next = passthrough[++i];
346
+ if (!next) fail("--slides expects a comma-separated list of slide ids");
347
+ slides = next.split(",").map((s) => s.trim()).filter(Boolean);
348
+ } else if (a.startsWith("-")) {
349
+ fail(`unknown flag: ${a}`);
350
+ } else {
351
+ fail(`unexpected argument: ${a}`);
352
+ }
353
+ }
354
+ if (replace && (at !== null || slides)) {
355
+ fail("--replace cannot be combined with --at or --slides");
356
+ }
357
+
358
+ const deckDir = resolveDeckArg(deck);
359
+ if (!fs.existsSync(path.join(deckDir, "slides"))) {
360
+ fail(`expected 'slides/' folder in ${deckDir}`);
361
+ }
362
+
363
+ const mod = await import(
364
+ pathToFileURL(path.join(SRC_DIR, "bridge", "build-and-push.mjs")).href
365
+ );
366
+ const result = await mod.buildAndPush({
367
+ deckDir,
368
+ presentationId: to,
369
+ insertAt: at,
370
+ replace,
371
+ slideIds: slides,
372
+ });
373
+ if (result.paneSuspended) {
374
+ process.stdout.write(
375
+ `PowerPoint is backgrounded — the deck rendered but couldn't be pushed.\n` +
376
+ `Click the slidemind task pane once to bring PowerPoint to the foreground, ` +
377
+ `then re-run: slidemind push ${deck}\n` +
378
+ `The rendered deck was saved to ${path.relative(process.cwd(), result.fallbackFile)} in the meantime.\n`,
379
+ );
380
+ return;
381
+ }
382
+ process.stdout.write(
383
+ `Pushed ${result.bytes} bytes (${result.mode}) to presentation ${result.presId}\n`,
384
+ );
385
+ }
386
+
387
+ async function cmdNew(rest) {
388
+ const positional = rest.filter((a) => !a.startsWith("--"));
389
+ const name = positional[0];
390
+ if (!name) fail("usage: slidemind new <name>");
391
+ if (!/^[a-z0-9][a-z0-9-]*$/i.test(name)) {
392
+ fail(`deck name must be kebab-case alphanumerics, got '${name}'`);
393
+ }
394
+ const decksDir = resolveDecksDir();
395
+ const deckDir = path.join(decksDir, name);
396
+ if (fs.existsSync(deckDir)) {
397
+ fail(`deck already exists: ${deckDir}`);
398
+ }
399
+ fs.mkdirSync(path.join(deckDir, "slides"), { recursive: true });
400
+ fs.writeFileSync(
401
+ path.join(deckDir, "plan.md"),
402
+ `# ${name}\n\n` +
403
+ `Ask your agent (Claude Code / Codex / opencode) to plan and author this deck —\n` +
404
+ `the slidemind-create-slides skill fills this in, then writes slides under ./slides/.\n`,
405
+ );
406
+ process.stdout.write(`Created ${path.relative(process.cwd(), deckDir)}\n`);
407
+ process.stdout.write(
408
+ `Next: ask your agent to author "${name}", then \`slidemind build ${name}\`\n`,
409
+ );
410
+ }
411
+
412
+ function parseTargetFlag(rest) {
413
+ const idx = rest.findIndex((a) => a === "--for" || a === "-f");
414
+ if (idx < 0 || !rest[idx + 1]) {
415
+ fail("missing --for <claude|codex|opencode|all>");
416
+ }
417
+ const target = rest[idx + 1];
418
+ if (!["claude", "codex", "opencode", "all"].includes(target)) {
419
+ fail(`unknown install target: ${target}`);
420
+ }
421
+ return target;
422
+ }
423
+
424
+ async function runInstaller(target, action, rest) {
425
+ const targets = target === "all" ? ["claude", "codex", "opencode"] : [target];
426
+ for (const t of targets) {
427
+ const mod = await import(
428
+ pathToFileURL(path.join(INSTALLERS_DIR, `${t}.mjs`)).href
429
+ );
430
+ const fn = mod[action];
431
+ if (typeof fn !== "function") {
432
+ fail(`installer for '${t}' does not implement '${action}'`);
433
+ }
434
+ await fn({
435
+ packageRoot: PACKAGE_ROOT,
436
+ flags: rest,
437
+ });
438
+ }
439
+ }
440
+
441
+ async function loadInstallerUi() {
442
+ const mod = await import(
443
+ pathToFileURL(path.join(INSTALLERS_DIR, "lib.mjs")).href
444
+ );
445
+ return mod.ui;
446
+ }
447
+
448
+ async function cmdInstall(rest) {
449
+ const target = parseTargetFlag(rest);
450
+ const ui = await loadInstallerUi();
451
+ ui.banner(readPackageJson().version, "init");
452
+ await runInstaller(target, "install", rest);
453
+ ui.done(
454
+ target === "all"
455
+ ? "ready — ask your agent to plan, create, or edit a deck"
456
+ : `ready — ${target} can now plan, create, or edit decks`,
457
+ );
458
+ }
459
+
460
+ async function cmdUninstall(rest) {
461
+ const target = parseTargetFlag(rest);
462
+ const ui = await loadInstallerUi();
463
+ ui.banner(readPackageJson().version, "uninstall");
464
+ await runInstaller(target, "uninstall", rest);
465
+ ui.done(target === "all" ? "removed from all harnesses" : `removed from ${target}`);
466
+ }
467
+
468
+ function spawnInherit(file, args) {
469
+ return new Promise((resolve, reject) => {
470
+ const child = spawn(file, args, {
471
+ stdio: "inherit",
472
+ env: process.env,
473
+ shell: process.platform === "win32",
474
+ });
475
+ child.on("error", reject);
476
+ child.on("exit", (code) => {
477
+ if (code === 0) resolve();
478
+ else reject(new Error(`${file} ${args.join(" ")} exited with code ${code}`));
479
+ });
480
+ });
481
+ }
482
+
483
+ // ---------- distribution (public npm) ----------
484
+ //
485
+ // slidemind is published to public npm as `slidemind`. First install is
486
+ // `npm install -g slidemind` (then `slidemind init` to register the MCP
487
+ // server + skill; `install` stays as a back-compat alias); `slidemind update`
488
+ // is a thin wrapper over `npm install -g slidemind@latest`.
489
+
490
+ // Resolve the `npm` that sits next to the node running this process, so an
491
+ // update lands the global into the same prefix slidemind was launched from.
492
+ function npmBin() {
493
+ const dir = path.dirname(process.execPath);
494
+ const candidates =
495
+ process.platform === "win32"
496
+ ? [path.join(dir, "npm.cmd"), path.join(dir, "npm")]
497
+ : [path.join(dir, "npm")];
498
+ for (const c of candidates) {
499
+ if (fs.existsSync(c)) return c;
500
+ }
501
+ return "npm"; // PATH fallback
502
+ }
503
+
504
+ async function cmdUpdate(rest) {
505
+ let target = "all";
506
+ let ref = null;
507
+ for (let i = 0; i < rest.length; i++) {
508
+ const a = rest[i];
509
+ if (a === "--for" || a === "-f") {
510
+ target = rest[++i];
511
+ if (!target) fail("--for expects a target");
512
+ if (!["claude", "codex", "opencode", "all", "none"].includes(target)) {
513
+ fail(`unknown install target: ${target}`);
514
+ }
515
+ } else if (a === "--ref") {
516
+ ref = rest[++i];
517
+ if (!ref) fail("--ref expects a version tag (e.g. v0.1.0)");
518
+ } else {
519
+ fail(`unknown flag: ${a}`);
520
+ }
521
+ }
522
+
523
+ const pkg = readPackageJson();
524
+ const ui = await loadInstallerUi();
525
+ ui.banner(pkg.version, "update");
526
+
527
+ // Upgrade the global install straight from public npm. `@latest` (or a pinned
528
+ // version) lets npm resolve + verify the tarball — no SAS, no checksum dance.
529
+ const spec = ref
530
+ ? `slidemind@${ref.replace(/^v/, "")}`
531
+ : "slidemind@latest";
532
+ await ui.section("release", ref ? `pinned ${spec}` : "latest");
533
+ ui.step({ label: "install", value: spec, note: "global (npm)" });
534
+ await spawnInherit(npmBin(), ["install", "-g", "--loglevel=error", spec]);
535
+
536
+ if (target !== "none") {
537
+ await spawnInherit("slidemind", ["init", "--for", target]);
538
+ }
539
+
540
+ ui.done(
541
+ target === "none"
542
+ ? "updated — skill files unchanged (run 'slidemind init --for all' when ready)"
543
+ : `updated and redeployed into ${target === "all" ? "all harnesses" : target}`,
544
+ );
545
+ }
546
+
547
+ // ---------- main ----------
548
+
549
+ async function main(argv) {
550
+ const [cmd, ...rest] = argv;
551
+ if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
552
+ printHelp();
553
+ return 0;
554
+ }
555
+ if (cmd === "--version" || cmd === "-v") {
556
+ process.stdout.write(`${readPackageJson().version}\n`);
557
+ return 0;
558
+ }
559
+ switch (cmd) {
560
+ case "build":
561
+ await cmdBuild(rest);
562
+ return 0;
563
+ case "build-pptx":
564
+ await cmdBuildPptx(rest);
565
+ return 0;
566
+ case "search":
567
+ await cmdSearch(rest);
568
+ return 0;
569
+ case "download-bundle":
570
+ await cmdDownloadBundle(rest);
571
+ return 0;
572
+ case "new":
573
+ await cmdNew(rest);
574
+ return 0;
575
+ case "presentations":
576
+ await cmdPresentations(rest);
577
+ return 0;
578
+ case "push":
579
+ await cmdPush(rest);
580
+ return 0;
581
+ case "init":
582
+ case "install":
583
+ // Set slidemind up in a coding harness (register the MCP server + skill).
584
+ // `install` stays as a back-compat alias for the documented `init`.
585
+ await cmdInstall(rest);
586
+ return 0;
587
+ case "uninstall":
588
+ await cmdUninstall(rest);
589
+ return 0;
590
+ case "update":
591
+ case "upgrade":
592
+ case "self-update":
593
+ await cmdUpdate(rest);
594
+ return 0;
595
+ default:
596
+ fail(`unknown command: ${cmd}\nrun 'slidemind --help' for usage`);
597
+ }
598
+ }
599
+
600
+ main(process.argv.slice(2)).catch((err) => {
601
+ process.stderr.write(`slidemind: ${err.message || err}\n`);
602
+ process.exit(1);
603
+ });
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "slidemind",
3
+ "version": "0.0.27",
4
+ "description": "Connect Claude Code, Codex & opencode to the slidemind PowerPoint add-in — let your coding agent build and edit native, on-brand PowerPoint decks against your active corporate design, from the terminal.",
5
+ "type": "module",
6
+ "bin": {
7
+ "slidemind": "bin/slidemind.mjs"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/STATWORX/intern-slidemind-cli.git"
12
+ },
13
+ "homepage": "https://github.com/STATWORX/intern-slidemind-cli",
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "files": [
18
+ "bin/",
19
+ "src/",
20
+ "skills/",
21
+ ".claude-plugin/",
22
+ "CLAUDE.md",
23
+ "README.md"
24
+ ],
25
+ "scripts": {
26
+ "build": "node bin/slidemind.mjs build",
27
+ "build-pptx": "node bin/slidemind.mjs build-pptx",
28
+ "lint:parity": "node scripts/check-skill-parity.mjs",
29
+ "release": "node scripts/release.mjs",
30
+ "test": "node --test test/*.test.mjs"
31
+ },
32
+ "dependencies": {
33
+ "@babel/parser": "^7.26.5",
34
+ "@babel/traverse": "^7.26.5",
35
+ "@babel/types": "^7.26.5",
36
+ "@clack/prompts": "^1.3.0",
37
+ "@modelcontextprotocol/sdk": "^1.29.0",
38
+ "js-yaml": "^4.1.1",
39
+ "recast": "^0.23.9",
40
+ "zod": "^3.23.8"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "keywords": [
46
+ "slides",
47
+ "presentation",
48
+ "powerpoint",
49
+ "pptx",
50
+ "office-addin",
51
+ "powerpoint-addin",
52
+ "slidemind",
53
+ "claude-code",
54
+ "claude-plugin",
55
+ "codex",
56
+ "opencode",
57
+ "mcp",
58
+ "skill"
59
+ ],
60
+ "author": "statworx",
61
+ "license": "UNLICENSED"
62
+ }