slidemind 0.3.3 → 0.3.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.
@@ -3,6 +3,7 @@
3
3
  "interface": {
4
4
  "displayName": "slidemind",
5
5
  "shortDescription": "On-brand PowerPoint decks from your coding agent",
6
+ "logo": "./assets/app-icon.png",
6
7
  "longDescription": "slidemind connects Codex to the slidemind PowerPoint add-in over its local bridge, so the agent can build and edit native .pptx decks against whatever corporate design the add-in has active. It is brand-agnostic: the corporate design, the layout palette, and the OOXML render all live in the add-in, and the agent reads every brand rule from the design bundle it serves. This marketplace publishes the slidemind plugin, which carries the authoring and search skills. The slidemind CLI itself is installed separately from public npm (npm install -g slidemind); the skills explain that step when the command is missing."
7
8
  },
8
9
  "plugins": [
@@ -22,6 +23,7 @@
22
23
  "displayName": "slidemind",
23
24
  "shortDescription": "On-brand PowerPoint decks from Codex",
24
25
  "developerName": "statworx",
26
+ "logo": "./assets/app-icon.png",
25
27
  "longDescription": "Adds two skills. slidemind-create-slides owns the intake, plan, create and edit workflow that turns per-slide sources into an editable .pptx rendered by the add-in, plus a local HTML preview. slidemind-search discovers and reuses existing slides and brand imagery from the corporate design's engines, or from a PowerPoint file on disk. Both are brand-agnostic and read the active corporate design's rules, layout palette, and authoring workflow from the design bundle the add-in serves. Requires the slidemind CLI (npm install -g slidemind) and an open PowerPoint with the add-in for anything that renders or reads a live deck."
26
28
  }
27
29
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidemind",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Connect Claude Code to the slidemind PowerPoint add-in — build and edit native, on-brand PowerPoint decks against the active corporate design, from the terminal.",
5
5
  "author": {
6
6
  "name": "statworx",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidemind",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Connect Codex to the slidemind PowerPoint add-in — build and edit native, on-brand PowerPoint decks against the active corporate design. Requires the slidemind CLI (npm install -g slidemind); the skills explain the install when it is missing.",
5
5
  "author": {
6
6
  "name": "statworx",
package/bin/slidemind.mjs CHANGED
@@ -1113,6 +1113,33 @@ async function cmdSearch(rest) {
1113
1113
  // subtype no longer defaulted to "image"; when the subtype is unknown we omit
1114
1114
  // it and let the add-in resolve the caller's groups itself.
1115
1115
  let subtype = slideIntent ? "slide" : null;
1116
+ // `--engine Icons` — a display name straight off `ls engines` — would reach
1117
+ // the backend verbatim and 404 as "Engine not found". Humans type names,
1118
+ // agents paste ids; when the value cannot be an id (spaces, non-hex), look it
1119
+ // up in the live catalog by label. Best-effort on purpose: no unique match
1120
+ // passes the value through so the existing unknown-engine hint still fires.
1121
+ if (
1122
+ engineId &&
1123
+ !bundle.isSearchScopeId(engineId) &&
1124
+ !/^[0-9a-f-]{8,}$/i.test(engineId)
1125
+ ) {
1126
+ const live = await client.listEngines({ backendURL }).catch(() => null);
1127
+ const wanted = engineId.toLowerCase();
1128
+ const ids = new Set(
1129
+ (live?.engines ?? [])
1130
+ .filter((e) => String(e.label ?? "").toLowerCase() === wanted)
1131
+ .map((e) => e.id),
1132
+ );
1133
+ if (ids.size === 1) {
1134
+ const [id] = ids;
1135
+ process.stderr.write(`engine '${engineId}' resolved to ${id}\n`);
1136
+ engineId = id;
1137
+ } else if (ids.size > 1) {
1138
+ fail(
1139
+ `--engine '${engineId}' names ${ids.size} different engines across your connected environments — pass the id from 'slidemind ls engines', or scope with --backend.`,
1140
+ );
1141
+ }
1142
+ }
1116
1143
  if (!engineId) {
1117
1144
  if (slideIntent) {
1118
1145
  // Slide search with no engine named: search the corpus UNFILTERED rather
@@ -3294,7 +3321,12 @@ async function cmdReady(rest) {
3294
3321
  registry = (await client.listOpenPresentationsDetailed()).presentations;
3295
3322
  }
3296
3323
  }
3297
- if (!registry.some(isRR)) {
3324
+ // Everything below terminates the command: it is the whole "no render-ready
3325
+ // window" resolution (connect-only engine answer → bound-deck rung → brand
3326
+ // list → late blocker). A named closure because TWO paths land here: the
3327
+ // registry genuinely holding no render-ready window, and the ping check
3328
+ // below pruning the last one as a zombie.
3329
+ const finishNoRenderReady = async () => {
3298
3330
  // --connect-only asks one question — "can I work?" — and since the engine
3299
3331
  // routes answer off the session, a backend that serves them IS the answer:
3300
3332
  // discovery, search and every per-result fetch run with PowerPoint closed.
@@ -3402,6 +3434,10 @@ async function cmdReady(rest) {
3402
3434
  };
3403
3435
  finishBlocker({ blocker: b });
3404
3436
  return;
3437
+ };
3438
+ if (!registry.some(isRR)) {
3439
+ await finishNoRenderReady();
3440
+ return;
3405
3441
  }
3406
3442
 
3407
3443
  // --- --connect-only: the connection is up with ≥1 render-ready window; the
@@ -3434,11 +3470,65 @@ async function cmdReady(rest) {
3434
3470
  }
3435
3471
 
3436
3472
  // --- Local: resolve WHICH design to bind against. ---
3437
- const resolution = client.resolveDesignSource(registry, {
3473
+ let resolution = client.resolveDesignSource(registry, {
3438
3474
  explicitPresId: to,
3439
3475
  deckPresFP,
3440
3476
  });
3441
3477
 
3478
+ // --- Zombie check: an implicitly adopted window that claims a LIVE pane gets
3479
+ // one truth ping before its design silently wins. A webview whose document
3480
+ // was closed keeps its socket — and thus a "connected" registry lease —
3481
+ // alive indefinitely, and only a host round-trip inside the pane can tell.
3482
+ // Narrow on purpose: an explicit --to is the user's call, a suspended window
3483
+ // keeps today's silent-adopt semantics (backgrounded PowerPoint must not
3484
+ // start asking questions), and a backend without paneLivenessV1 fails open.
3485
+ // A dead window is pruned and resolution re-runs on the remainder; pruning
3486
+ // the last one lands on the same rungs as an empty registry (bound deck →
3487
+ // brand list → blocker). ---
3488
+ const backendAdvertisesPing = (entry) =>
3489
+ (statusRaw.backends ?? []).some(
3490
+ (b) =>
3491
+ b.ok &&
3492
+ b.backendURL === entry.backendURL &&
3493
+ b.capabilities?.paneLivenessV1 === true,
3494
+ );
3495
+ const entryIsAlive = async (entry) => {
3496
+ try {
3497
+ const ack = await client.pingPresentation({
3498
+ presId: entry.presId,
3499
+ backendURL: entry.backendURL ?? null,
3500
+ });
3501
+ return ack?.ok !== false;
3502
+ } catch (err) {
3503
+ // Dead only on the typed pane verdicts; anything else (404 route on an
3504
+ // older backend, a transport blip) fails open — adopt as before.
3505
+ return ![
3506
+ "pane_suspended",
3507
+ "pane_timeout",
3508
+ "pane_disconnected",
3509
+ "bridge_timeout",
3510
+ ].includes(err?.code);
3511
+ }
3512
+ };
3513
+ while (
3514
+ !to &&
3515
+ resolution.status === "resolved" &&
3516
+ client.isPaneReadyPresentation(resolution.entry) &&
3517
+ backendAdvertisesPing(resolution.entry)
3518
+ ) {
3519
+ if (await entryIsAlive(resolution.entry)) break;
3520
+ const deadId = resolution.entry.presId;
3521
+ registry = registry.filter((p) => p.presId !== deadId);
3522
+ if (!registry.some(isRR)) {
3523
+ await finishNoRenderReady();
3524
+ return;
3525
+ }
3526
+ resolution = client.resolveDesignSource(registry, {
3527
+ explicitPresId: to,
3528
+ deckPresFP,
3529
+ });
3530
+ }
3531
+
3442
3532
  if (resolution.status === "none") {
3443
3533
  // Defensive: a render-ready window exists (poll passed) so this is rare.
3444
3534
  finishBlocker({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slidemind",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
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
5
  "type": "module",
6
6
  "bin": {