slidemind 0.3.13 → 0.3.14
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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/AGENTS.md +86 -21
- package/README.md +14 -17
- package/bin/slidemind.mjs +65 -4969
- package/package.json +1 -1
- package/skills/create-slides/SKILL.md +113 -74
- package/skills/search/SKILL.md +25 -7
- package/src/bridge/build-and-push.mjs +108 -15
- package/src/bridge/bundle.mjs +69 -0
- package/src/bridge/capture.mjs +19 -0
- package/src/bridge/client/brands.mjs +622 -0
- package/src/bridge/client/engine-routing.mjs +237 -0
- package/src/bridge/client/engine-search.mjs +310 -0
- package/src/bridge/client/engine-slides.mjs +435 -0
- package/src/bridge/client/pane-ops.mjs +335 -0
- package/src/bridge/client/presentations.mjs +587 -0
- package/src/bridge/client/render.mjs +317 -0
- package/src/bridge/client/transport.mjs +695 -0
- package/src/bridge/client.mjs +80 -3452
- package/src/bridge/config.mjs +1 -1
- package/src/bridge/engine-download.mjs +15 -2
- package/src/builders/build.mjs +11 -804
- package/src/builders/draft-check.mjs +376 -0
- package/src/builders/lint.mjs +38 -2
- package/src/builders/preview.html +799 -0
- package/src/builders/review.mjs +23 -4
- package/src/builders/visual-families.mjs +6 -8
- package/src/cli/common.mjs +179 -0
- package/src/cli/concepts.mjs +310 -0
- package/src/cli/connection.mjs +147 -0
- package/src/cli/deck.mjs +608 -0
- package/src/cli/designs.mjs +113 -0
- package/src/cli/diagnose.mjs +141 -0
- package/src/cli/download-bundle.mjs +144 -0
- package/src/cli/engine-helpers.mjs +429 -0
- package/src/cli/engine-results.mjs +320 -0
- package/src/cli/help.mjs +201 -0
- package/src/cli/install.mjs +257 -0
- package/src/cli/read.mjs +120 -0
- package/src/cli/ready.mjs +805 -0
- package/src/cli/search.mjs +608 -0
- package/src/cli/session.mjs +177 -0
- package/src/cli/setup.mjs +282 -0
- package/src/cli/slide-reuse.mjs +334 -0
- package/src/cli/status.mjs +118 -0
- package/src/cli/workspace.mjs +302 -0
- package/src/installers/codex.mjs +1 -1
- package/src/update-check.mjs +231 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slidemind",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
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
|
+
"version": "0.3.14",
|
|
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/AGENTS.md
CHANGED
|
@@ -137,7 +137,17 @@ The script bumps `package.json` and commits — it does not tag (CI does that).
|
|
|
137
137
|
|
|
138
138
|
Teammates pick up the release with `npm install -g slidemind@latest` (or `slidemind update` if already installed); pin with `slidemind@X.Y.Z` / `slidemind update --ref X.Y.Z`.
|
|
139
139
|
|
|
140
|
-
**The add-in's version floor.** The add-in serves the oldest CLI it still speaks two ways: `minCliVersion` in `/api/cli/capabilities` + `/api/cli/readiness`, and an `x-slidemind-min-cli` header on **every** response of its CLI router. `bridgeFetch`/`bridgeStreamToFile` check that header on each call (so the pane routes — search/read/insert — are covered without a probe), `assertCliVersionSupported` pre-flights render/translate, and `
|
|
140
|
+
**The add-in's version floor.** The add-in serves the oldest CLI it still speaks two ways: `minCliVersion` in `/api/cli/capabilities` + `/api/cli/readiness`, and an `x-slidemind-min-cli` header on **every** response of its CLI router. `bridgeFetch`/`bridgeStreamToFile` check that header on each call (so the pane routes — search/read/insert — are covered without a probe), `assertCliVersionSupported` pre-flights render/translate, and `ready` (both its normal and its `--diagnose` mode) opts out via `setDiagnosticMode(true)` because they report the skew as the `cli_update_required` blocker themselves. The remediation the CLI prints is `slidemind update` → `slidemind@latest`, so **the add-in's floor must never exceed the version published to npm** — raise it only in lockstep with the release that satisfies it, or every paired CLI hard-blocks on an instruction that cannot resolve.
|
|
141
|
+
|
|
142
|
+
**The update nudge + background self-update** (`src/update-check.mjs`). The version floor above only fires once a CLI is too old to talk to the add-in at all — by then the user is hard blocked. The softer signal runs after **every** command: the npm dist-tag (`https://registry.npmjs.org/slidemind/latest`, override with `SLIDEMIND_REGISTRY_URL`) is fetched at most once per 6h per config dir (`SLIDEMIND_UPDATE_CHECK_INTERVAL_MS`), cached in `<configDir>/update-check.json`, and a newer version prints one line on **stderr** (never stdout — `--json` payloads stay clean). On the npm channel it also spawns a detached `npm install -g slidemind@latest` (log: `<configDir>/update.log`), so the next run is current without anyone typing anything.
|
|
143
|
+
|
|
144
|
+
Four rules the implementation exists to keep:
|
|
145
|
+
|
|
146
|
+
- **After the command, never before.** A background global install replaces the package directory; running it mid-command could pull the rug out from under a dynamic import.
|
|
147
|
+
- **Only the npm channel auto-updates.** `updateChannel()` returns `plugin` (only `/plugin` replaces that binary — a global install lands a second, unused copy), `npx` (nothing to update), `dev` (a checkout, i.e. `npm link` — an install would clobber the developer's symlink, so it is neither updated nor nudged; this is also why the test suite is silent by default, with `SLIDEMIND_UPDATE_CHECK=1` to force it on), or `npm`.
|
|
148
|
+
- **One auto-update attempt per released version per interval.** A root-owned npm prefix makes the install fail forever; the cache records the attempt and the next run says the update did not take instead of respawning it every command.
|
|
149
|
+
- **A version-floor hit forces the check.** When a command dies with `cli_update_required`, `bin/slidemind.mjs` passes `force: true`, which bypasses the 6h cache (it may predate the release that raised the floor) and the one-attempt guard, so the auto-update starts on the very command that hit the floor.
|
|
150
|
+
- **Never fatal, never slow.** Fetch timeout 2s (`SLIDEMIND_UPDATE_CHECK_TIMEOUT_MS`), every failure swallowed. Opt out with `SLIDEMIND_NO_UPDATE_CHECK=1` (silence) or `SLIDEMIND_NO_AUTO_UPDATE=1` (nudge only); `CI` disables the auto-install by itself.
|
|
141
151
|
|
|
142
152
|
> **One-time GitHub setup:** the workflow uses the default `GITHUB_TOKEN`. If branch-protection rules on `main` block bot pushes, allow the `github-actions[bot]` actor (or relax "require pull request before merging" for that bot).
|
|
143
153
|
|
|
@@ -156,7 +166,7 @@ slidemind/
|
|
|
156
166
|
├── .agents/
|
|
157
167
|
│ └── plugins/marketplace.json Codex marketplace — serves this repo (git clone) as the plugin; NOT in the npm tarball
|
|
158
168
|
├── bin/
|
|
159
|
-
│ ├── slidemind.mjs CLI
|
|
169
|
+
│ ├── slidemind.mjs CLI entry: scrubs the output streams, bootstraps the sim env, dispatches to src/cli/*
|
|
160
170
|
│ └── slidemind + slidemind.cmd PATH shims for Claude Code plugin sessions (npm links slidemind.mjs itself)
|
|
161
171
|
├── skills/ canonical, installable skill payload — thin CLI workflow glue only
|
|
162
172
|
│ ├── create-slides/
|
|
@@ -171,9 +181,12 @@ slidemind/
|
|
|
171
181
|
│ loop + vocabulary→CLI command map. The craft is
|
|
172
182
|
│ READ from the bundle's workflow/search.md — owned by the add-in.
|
|
173
183
|
├── src/ implementation of the slidemind CLI
|
|
174
|
-
│ ├──
|
|
184
|
+
│ ├── cli/ the subcommands, one module per job (each <= ~600 lines; ready.mjs is the one 783-line function): common.mjs (paths, fail, deck resolution, gates, lazy loaders) · help · designs · connection (the two readiness ladders) ·
|
|
185
|
+
│ │ deck (plan/review/imagen/inspect-template-deck/build-pptx/view-draft) · concepts · workspace (new/adopt/push) · engine-helpers + search (search/ls) + slide-reuse (insert-slide/add-slide) + engine-results (view-slide/read-source/download-result) ·
|
|
186
|
+
│ │ download-bundle · ready · diagnose · read · session (login/whoami/logout) · install (init/uninstall/update) · setup
|
|
187
|
+
│ ├── bridge/ add-in local-bridge: client.mjs (the public surface; implementation split by job under client/: transport, presentations, pane-ops, brands, render, engine-routing, engine-search, engine-slides), bundle.mjs (materialize), build-and-push.mjs (server render; native-slide splicing), engine-download.mjs + recolor.mjs (image/icon fetch + recolour),
|
|
175
188
|
│ │ setup-claim.mjs (decode/redeem the add-in's one-command claim — the only connect path) + http-json.mjs + config.mjs (per-backend sessions)
|
|
176
|
-
│ ├── builders/ build.mjs (HTML preview), review.mjs (spatial preview-review gate + local server; stable per-deck port, graceful fallback, tab reuse across rounds), plan.mjs (plan sign-off gate) + plan-overlay.js + plan-md.mjs (markdown→HTML) + gate-server.mjs (shared gate primitives), resolve-skill.mjs (→ bundle), lint.mjs (runs the add-in's engine)
|
|
189
|
+
│ ├── builders/ build.mjs (HTML preview; the page itself is preview.html), review.mjs (spatial preview-review gate + local server; stable per-deck port, graceful fallback, tab reuse across rounds), plan.mjs (plan sign-off gate) + plan-overlay.js + plan-md.mjs (markdown→HTML) + gate-server.mjs (shared gate primitives), resolve-skill.mjs (→ bundle), lint.mjs (runs the add-in's engine)
|
|
177
190
|
│ ├── pptx/ host-free local-.pptx support (fflate-based): read-local.mjs (read a file's slides — output-identical to the host read), slide-swap.mjs (fold a picked slide into a deck as native .pptx — swaps it into the lowest slot the splicer reads, then prunes the package to that slide's dependency closure so a slice carries only what it needs), zip.mjs + ooxml.mjs (shared unzip/order helpers)
|
|
178
191
|
│ ├── lib/ deck-workspace bookkeeping: adopt.mjs (rebuild a deck from a file's embedded source + convert its foreign slides through the add-in's convert-pptx route), conversions.mjs (the deck's `conversions/<stem>.json` identity+evidence records and the preserve source package push threads back), slide-guids.mjs, slide-order.mjs, reused-slides.mjs
|
|
179
192
|
│ └── installers/ per-harness installers: claude.mjs, codex.mjs, opencode.mjs, lib.mjs
|
|
@@ -185,23 +198,21 @@ slidemind/
|
|
|
185
198
|
|
|
186
199
|
The skills at the **root** of `skills/` are deliberate: that's the layout Claude Code, Codex (0.128+), and opencode all expect for skill auto-discovery — `<harness-root>/skills/<name>/SKILL.md`. There are **two** skills, each auto-discovered by `listSkills()` (no installer change to add one): `create-slides` (installs as `slidemind-create-slides`) owns the user-facing plan / create / edit / render workflow, and `search` (installs as `slidemind-search`) owns engine discovery + slide reuse (search → insert live / fold into a deck). Both are **brand-agnostic** — every brand rule lives in the per-CD bundle's `design.md` / `workflow/*.md`, which the skills point the agent at. (Pre-skills opencode versions used an `agent/` shim under `~/.config/opencode/`; the installer removes any leftover from those installs on every run.)
|
|
187
200
|
|
|
188
|
-
We **do not** ship separate slash commands. Earlier versions had a top-level `commands/` directory that became `/slidemind:plan` etc. on Claude Code and `~/.codex/prompts/slidemind-*.md` on Codex. The Codex prompts mechanism is broken in 0.117+ ([openai/codex#15941](https://github.com/openai/codex/issues/15941)) and officially deprecated in favour of skills, so we collapsed everything into the skill itself. Re-running `slidemind init` actively wipes legacy command/prompt entries on every harness, and clears the materialized bundle cache (`bundleCacheRoot()`) so the next `
|
|
201
|
+
We **do not** ship separate slash commands. Earlier versions had a top-level `commands/` directory that became `/slidemind:plan` etc. on Claude Code and `~/.codex/prompts/slidemind-*.md` on Codex. The Codex prompts mechanism is broken in 0.117+ ([openai/codex#15941](https://github.com/openai/codex/issues/15941)) and officially deprecated in favour of skills, so we collapsed everything into the skill itself. Re-running `slidemind init` actively wipes legacy command/prompt entries on every harness, and clears the materialized bundle cache (`bundleCacheRoot()`) so the next `view-draft`/`push`/`download-bundle` re-fetches every design bundle fresh from the add-in.
|
|
189
202
|
|
|
190
203
|
## CLI surface
|
|
191
204
|
|
|
192
205
|
```bash
|
|
193
|
-
slidemind ready [deck] # one-shot session start: connection ladder + design pick (--brand > deck binding > the one live window design > the one cached design > the one listed design; else ambiguous_design with the full menu) + bundle download + deck bind; reports every design at hand with theme colours + every verified open window; --to/--brand/--force/--connect-only/--json (PowerPoint closed is a normal case)
|
|
194
|
-
slidemind
|
|
195
|
-
slidemind presentations # list PowerPoint windows registered with the add-in
|
|
206
|
+
slidemind ready [deck] # one-shot session start (with a deck: also its disk-read stage line — plan/concepts/assets/slides/draft check/review — and the next command): connection ladder + design pick (--brand > deck binding > the one live window design > the one cached design > the one listed design; else ambiguous_design with the full menu) + bundle download + deck bind; reports every design at hand with theme colours + every verified open window; --to/--brand/--force/--connect-only/--json (PowerPoint closed is a normal case)
|
|
207
|
+
slidemind ready --diagnose # read-only report on TWO independent ladders — backend authoring (session/backend/version/engines) and live PowerPoint (windows/pane, each window's state + wake hint); materializes nothing, binds nothing
|
|
196
208
|
slidemind read [presId|file.pptx] # pull an open presentation's content (presId; HOST REQUIRED) OR read a local .pptx on disk (file path; host-free) — same per-slide output (--mode summary|raw)
|
|
197
|
-
slidemind download-bundle [deck] # materialize
|
|
209
|
+
slidemind download-bundle [deck] # materialize a corporate design's bundle locally — pane-free for a bound deck or --brand <presFP>; a window is only consulted when the design isn't named yet (or --to targets one)
|
|
198
210
|
slidemind new <name> # scaffold ./decks/<name>/ (auto-binds to the one open design, else the one cached design, when its bundle is already materialized; --brand <presFP> binds to the design the user chose instead; --to/--no-bind)
|
|
199
211
|
slidemind plan <deck> # plan sign-off gate on <deck>/plan.md; markdown editor + live preview + overall feedback, blocks for approve/comments (host-free)
|
|
200
|
-
slidemind build <deck> # build self-contained HTML preview
|
|
201
212
|
slidemind review <deck> # spatial preview-review gate; blocks for approve/comments (host-free)
|
|
202
|
-
slidemind build-pptx <deck> # compose: add-in; template pack: host-free with --pack <dir>; never overwrites manual PowerPoint edits — text edits fold back, the rest refuses, --force discards (docs/slidemind-cli-connect/16-manual-edit-reconcile.md)
|
|
213
|
+
slidemind build-pptx <deck> # compose: add-in, pane-free (deck binding → the one cached design → the one listed design; a window only with --to or when none of those answers); template pack: host-free with --pack <dir>; never overwrites manual PowerPoint edits — text edits fold back, the rest refuses, --force discards (docs/slidemind-cli-connect/16-manual-edit-reconcile.md)
|
|
203
214
|
slidemind inspect-template-deck <deck> # normalized pack model for pack-owned validation; --pack <dir> --json
|
|
204
|
-
slidemind view-draft <deck> # render the deck's slides to per-slide PNGs so the agent can look before review; no push, no .pptx (host-free once the bundle is materialized)
|
|
215
|
+
slidemind view-draft <deck> # lint + build the HTML preview (this is where `slidemind build` went), then render the deck's slides to per-slide PNGs so the agent can look before review; --fix applies the deterministic lint autofixes; --lint-only stops after the lint; no push, no .pptx (host-free once the bundle is materialized); --only NN for one slide; the server's export gate (slot-fit/diagram-geometry/connector-endpoints) runs on the captured slides BY DEFAULT — verdict only, skipped with a note when the backend is unreachable (--translate insists, --no-translate skips) — so a slide is proven buildable before review; --json
|
|
205
216
|
slidemind push <deck> # render + push slides into a running PowerPoint (HOST REQUIRED); --replace reconciles the LIVE deck first (same doc), insert pushes never do
|
|
206
217
|
slidemind ls engines # list the live engine catalog (image + slide; --type/--subtype) (pane-free on add-ins with engineRoutes; HOST REQUIRED on older ones)
|
|
207
218
|
slidemind search "<query>" # search an engine for imagery or slides (--engine/--intent/--top/--deck) (pane-free on add-ins with engineRoutes; HOST REQUIRED on older ones)
|
|
@@ -212,17 +223,73 @@ slidemind add-slide [opts] # fold slide(s) into a deck as native .pptx
|
|
|
212
223
|
slidemind adopt <file.pptx> # rebuild a deck workspace from a FILE's embedded source; foreign slides convert to editable jsx through the add-in's convert-pptx route where it is served (--deck/--brand/--no-convert) — live decks are add-in-only in v1 (docs/slidemind-cli-connect/14-foreign-slide-conversion.md)
|
|
213
224
|
slidemind login [--backend <url|env>] # sign in to Microsoft 365 (browser + PKCE); usually unnecessary — the first engine command runs this flow ITSELF and blocks until the human finishes (graph-auth `ensureSignedIn`, once per process). One sign-in mints Graph + SharePoint tokens from one refresh token; docs/slidemind-cli-connect/15-graph-delegated-cli.md
|
|
214
225
|
slidemind whoami # list every connected environment (* = default) + its Microsoft 365 sign-in state
|
|
215
|
-
slidemind use <url|env> # set the default environment
|
|
216
226
|
slidemind logout [url|env] # unpair one environment (--all unpairs every one; also drops its Microsoft 365 sign-in)
|
|
217
227
|
slidemind init --for <target> # install skill into claude | codex | opencode | all | auto (also clears the bundle cache); `setup` already does this
|
|
218
228
|
slidemind uninstall --for <target> # remove the skill (also clears the bundle cache)
|
|
219
229
|
```
|
|
220
230
|
|
|
231
|
+
**Bundle freshness is automatic (2026-09-04).** A corporate design can change
|
|
232
|
+
while a session runs, and every local step afterwards — HTML build, lint,
|
|
233
|
+
capture, the draft checks, and the etag the server render is validated against —
|
|
234
|
+
reads the deck's cached copy. So each deck command (`view-draft`, `review`,
|
|
235
|
+
`concepts`, `build-pptx` for compose decks, `push`) calls
|
|
236
|
+
`bundle.refreshDeckBundle(deckDir)` before it works: an etag-gated incremental
|
|
237
|
+
re-sync of the deck's bound design that downloads nothing when the design is
|
|
238
|
+
unchanged, rewrites `.bundle-ref.json` when it moved (and says so on stdout),
|
|
239
|
+
and returns null on an unreachable backend so the host-free steps stay host-free.
|
|
240
|
+
It clamps `SLIDEMIND_BRIDGE_TIMEOUT_MS` to 6s (`SLIDEMIND_BUNDLE_REFRESH_TIMEOUT_MS`)
|
|
241
|
+
and disables the transient retry for its own call, because a dead backend must
|
|
242
|
+
cost seconds, not the 35s render budget. `download-bundle --all` is the same
|
|
243
|
+
sync across every design cached on this machine, and `ls designs` lists what is
|
|
244
|
+
cached here plus what the backends serve — neither needs a PowerPoint window.
|
|
245
|
+
|
|
246
|
+
**One surface per job — four commands were folded away (2026-09-04).** The CLI
|
|
247
|
+
had grown parallel entry points that answered the same question, so agents (and
|
|
248
|
+
humans) had to know which of two commands to reach for:
|
|
249
|
+
|
|
250
|
+
- `doctor` and `presentations` → **`slidemind ready --diagnose`**. Same
|
|
251
|
+
implementation, same `--json` contract (`authoring` / `host` / `backends` /
|
|
252
|
+
`presentations`), plus each non-pane-ready window's wake hint, which used to
|
|
253
|
+
exist only in `presentations`. `ready` is already the session surface; a
|
|
254
|
+
read-only mode of it beats two more command names. What went with them:
|
|
255
|
+
`presentations --all` (a snapshot flag on a command nobody called) and its
|
|
256
|
+
bounded re-registration wait — `--diagnose` reports the registry as it stands.
|
|
257
|
+
- `build` → **`slidemind view-draft`**, which now runs `buildDeck()` itself
|
|
258
|
+
(lint + the deck's copy of `corporate-design.css`, which its own type-ramp
|
|
259
|
+
check already silently depended on) and carries `--fix`. The HTML preview is
|
|
260
|
+
still written, and `review` still builds it too. `src/builders/build.mjs`
|
|
261
|
+
stays as a module and a `node`-runnable script.
|
|
262
|
+
- `use` → nothing; `setup` sets the default backend and `--backend <url|env>` /
|
|
263
|
+
`SLIDEMIND_BACKEND` scope a command. A normal user has one backend.
|
|
264
|
+
- Aliases `connection`, `status`, `upgrade` → deleted (`update` and
|
|
265
|
+
`self-update` remain, `install` remains as the back-compat alias of `init`).
|
|
266
|
+
|
|
267
|
+
Don't reintroduce a second command for something `ready`, `view-draft` or
|
|
268
|
+
`build-pptx` already answers — add a flag to the one that owns the job. The
|
|
269
|
+
folded names (`build`, `doctor`, `presentations`, `status`, `connection`, `use`,
|
|
270
|
+
`upgrade`) still resolve to a one-line redirect naming the owner — stale skills
|
|
271
|
+
and habits type them — never to a bare "unknown command".
|
|
272
|
+
|
|
273
|
+
**Agent-facing conventions (2026-09-05).** Every command takes `--help` (its
|
|
274
|
+
own usage block, `src/cli/help.mjs` → `commandBlock`); the deck commands take
|
|
275
|
+
`--json` (`ready`, `view-draft`, `concepts`, `build-pptx`, `download-result`,
|
|
276
|
+
`search`, `add-slide`, `read`, `ls`). `ready <deck>` ends with a `stage:` +
|
|
277
|
+
`next:` pair read from disk (`src/cli/status.mjs` → `deckStage`): plan gate /
|
|
278
|
+
concepts / sourcing / authoring / draft check / review / deliver — the agent's
|
|
279
|
+
recovery point after a context summarization, replacing the prose table the
|
|
280
|
+
skill used to carry. `concepts` always ends on one `CONCEPTS: n/m <status>`
|
|
281
|
+
line. The lint printer (`src/builders/lint.mjs` → `collapseLintLines`) dedupes
|
|
282
|
+
the engine's lines and folds the "template id, may resolve at render time"
|
|
283
|
+
class into one `[lint:note]` per file — the rule stays the add-in's, only the
|
|
284
|
+
CLI's rendering changes. A fanned-out `ls engines` never opens the browser
|
|
285
|
+
sign-in for an environment nobody asked about (`signIn: false` on the
|
|
286
|
+
multi-environment listing; the refusing env is named in a note instead).
|
|
287
|
+
|
|
221
288
|
`<deck>` resolves as: absolute path → `./<arg>` if it exists → `./decks/<arg>`. The decks directory is `./decks/` by default; override with `SLIDEMIND_DECKS=/abs/path`.
|
|
222
289
|
|
|
223
290
|
**Multi-environment sessions.** The CLI stores one session **per backend** (`~/.config/slidemind/session.json`, v2 `{version, sessions[], default}`; the legacy single-record v1 shape is migrated on read). Connect once per environment (local / dev / prod) and every command **fans out** across all connected backends, tags each open window with its `env`, and **routes by the presentation's origin backend** automatically — no manual switching. Adding an environment is easiest with `slidemind setup <claim> --no-install --for none` — the claim carries its own backend, so nothing has to be typed or configured. There is no other way in: without a claim the CLI has no backend to talk to. Later commands may scope an existing session with `--backend <url|env>` / `SLIDEMIND_BACKEND`. A normal user connects exactly one backend.
|
|
224
291
|
|
|
225
|
-
**The env label is routing metadata, not narrative.** Because a normal user has exactly one backend, naming it on every line is noise that makes ordinary output read like a debugging session — so human-facing output shows an env label **only when more than one backend is connected**. `src/bridge/config.mjs` owns the rule (`isMultiEnv()`, `envTag()`, `envPhrase()`); call those rather than interpolating `[${env}]` by hand, and word the sentence so it still reads with the label absent. Three things are deliberately NOT gated: `--json` payloads (agents route on `env`), `
|
|
292
|
+
**The env label is routing metadata, not narrative.** Because a normal user has exactly one backend, naming it on every line is noise that makes ordinary output read like a debugging session — so human-facing output shows an env label **only when more than one backend is connected**. `src/bridge/config.mjs` owns the rule (`isMultiEnv()`, `envTag()`, `envPhrase()`); call those rather than interpolating `[${env}]` by hand, and word the sentence so it still reads with the label absent. Three things are deliberately NOT gated: `--json` payloads (agents route on `env`), `ready --diagnose` and `whoami` (reporting environments is their whole job), and any label the user just typed (`--backend prod`, `logout dev`) — suppressing an echo of their own argument is confusing, not quiet. `setup` decides once per run, **before** it spends the claim, so its four mentions agree with each other rather than flipping when `upsertSession` changes the count.
|
|
226
293
|
|
|
227
294
|
**No credential may reach stdout or stderr.** The CLI holds four — the per-backend `sessionToken`, a Graph refresh token, a Graph access token, and the one-time `smc1_…` claim — and none is printed on purpose. The risk is transitive: a server-controlled **response body** used verbatim as an error detail (`setup-claim.mjs` redeem failures, the Entra token endpoint, `response.body.slice(0, 400)` on every non-2xx bridge call), plus `unknown command: <argv>` echoing a claim pasted without its verb. Patching those four sites would leave the fifth to whoever adds the next route, so containment lives at the output boundary instead: `src/bridge/secrets.mjs` holds the values this process knows are secret, and `bin/slidemind.mjs` wraps `process.stdout.write`/`process.stderr.write` **once, before any command runs**, so every line, `--json` payload and error message is scrubbed on its way out. Matching is by VALUE, not by pattern — exact, no false positives on ordinary prose — and the replacement is an opaque `«secret:4f9c»` tag (4 hex of sha256) that reveals nothing while still letting two occurrences of one credential be recognised as the same. Register a secret the moment it enters memory (`readSessions`, `getGraphRefreshToken`, `requestToken`, `decodeSetupClaim`, and argv); values under 8 characters are ignored on purpose, so **test fixtures must use realistic lengths** or the guard silently cannot fail. `test/secret-containment.test.mjs` drives the real bin against a relay that deliberately echoes the credential it was sent.
|
|
228
295
|
|
|
@@ -234,7 +301,7 @@ slidemind uninstall --for <target> # remove the skill (also clears the bundle
|
|
|
234
301
|
|
|
235
302
|
**The two ops that stay pane-bound self-heal a presId churn.** Every pane reconnect mints a NEW presId for the same window, so `push`'s insert and `insert-slide` re-list the registry and re-match the window before giving up: `waitForPaneReady` matches the same presId, then `fileName`+`brandId`, then — when a Save-As renamed the window mid-run — the **design fingerprint alone** (`presFP == brandId`), and only when exactly one connected window carries it (two windows on one design are indistinguishable, and pushing into the wrong one is worse than waiting). `engineOpWithPaneWait` takes that window identity plus an opt-in `retargetOnMissing`, which **only the live insert sets**: for an idempotent engine read a 404 is the search record talking, not the window, so waiting on the registry would just stall the agent (`isMissingPresentationError` keeps a coded 404 like `unknown_search` out of it). Both retries re-send the SAME `operationId`, so the pane-side dedup makes them safe; both retry exactly ONCE and then surface the wake-PowerPoint blocker — `push` by saving the rendered `.pptx` next to the deck rather than throwing the bytes away, `insert-slide` by naming the click and the window-free `add-slide` alternative.
|
|
236
303
|
|
|
237
|
-
**The readiness ladder is two ladders.** `bin/slidemind.mjs` authors them once: `backendBlocker(pub)` (paired → every backend unreachable → CLI below the floor) is what render, bundle and the pane-free engine routes ride on, and `paneBlocker(pub)` (no window → not render-ready → pane suspended) governs only push's insert, `insert-slide` and reading an open presentation. `connectionBlocker = backendBlocker ?? paneBlocker` keeps every pre-existing message byte-identical (one addition: when every backend failed with code `backend_unavailable`, the add-in's `503` for "my session store did not answer", the rung is `backend_unavailable` with actor `agent` and says wait, never re-pair; a `401` is reserved for "the add-in answered: no such session", and its message no longer tells the user to `logout --all`, because `setup` replaces the backend's record in place and keeps its Microsoft 365 refresh token when the add-in-reported `oid` says the same person re-paired — a different or unknown person drops it), `
|
|
304
|
+
**The readiness ladder is two ladders.** `bin/slidemind.mjs` authors them once: `backendBlocker(pub)` (paired → every backend unreachable → CLI below the floor) is what render, bundle and the pane-free engine routes ride on, and `paneBlocker(pub)` (no window → not render-ready → pane suspended) governs only push's insert, `insert-slide` and reading an open presentation. `connectionBlocker = backendBlocker ?? paneBlocker` keeps every pre-existing message byte-identical (one addition: when every backend failed with code `backend_unavailable`, the add-in's `503` for "my session store did not answer", the rung is `backend_unavailable` with actor `agent` and says wait, never re-pair; a `401` is reserved for "the add-in answered: no such session", and its message no longer tells the user to `logout --all`, because `setup` replaces the backend's record in place and keeps its Microsoft 365 refresh token when the add-in-reported `oid` says the same person re-paired — a different or unknown person drops it), `ready --diagnose` reports the two separately (`authoring` / `host` in `--json`) so an agent stops reading "pane gone" as "cannot work", and the Microsoft 365 sign-in state is reported next to the authoring ladder — never a rung, because it must not gate render or bundle (`ready`'s `m365.degradation.code: "login_required"`, computed LOCALLY from `hasGraphSession` — the backend advertises the contract via `cliLoginV1`, never per-user state). `ready`'s ladder for a **bound** deck is session → bundle freshness → (informational) engine + pane reachability: the deck's own binding is the design, a window on it is targeted, none at all materializes over the brand route, and a window on a DIFFERENT design is reported, never adopted. An unbound deck picks the one design the verified live windows agree on (`resolveDesignSource` lets a live pane outrank suspended windows on other designs), else the one design cached on this machine, else the one design `GET /api/cli/brands` lists (capability `brandsList`, `client.listBrands()`); several of any stop with `ambiguous_design`, whose payload is the same `designs` menu every success carries (cached + open + listed, each with name, theme colours and its open windows) naming `--brand <presFP>`. **The zombie prune runs BEFORE any of that**: on backends advertising `paneLivenessV1` every window claiming a live pane is pinged in parallel and a dead one leaves the registry, because a closed document's webview keeps its lease and used to fake a second design — the exact false `ambiguous_design` a single open deck produced. `client.engineReachability({ backends })` is the engine half of both reports — env labels only, never a URL.
|
|
238
305
|
|
|
239
306
|
## External skill packs (this repo ships none)
|
|
240
307
|
|
|
@@ -288,13 +355,11 @@ Both skills are **brand-agnostic workflow guidance** and must not duplicate any
|
|
|
288
355
|
|
|
289
356
|
```bash
|
|
290
357
|
slidemind ready <deck> # do this first: connection ladder + resolve the open design + materialize its bundle + bind the deck (host required)
|
|
291
|
-
slidemind download-bundle <deck> # lower-level: materialize-only (no connection ladder, no bind) — `ready` now covers this
|
|
358
|
+
slidemind download-bundle <deck> # lower-level: materialize-only (no connection ladder, no bind) — `ready` now covers this (host-free once the deck is bound)
|
|
292
359
|
slidemind plan <deck> # plan sign-off gate on <deck>/plan.md; blocks until approve/comments (host-free)
|
|
293
|
-
slidemind build <deck> # HTML preview (reads the materialized bundle)
|
|
294
|
-
slidemind build <deck> --open # HTML + open in browser
|
|
295
360
|
slidemind review <deck> # spatial preview-review gate; blocks until approve/comments (host-free)
|
|
296
|
-
slidemind build-pptx <deck> # native PPTX rendered server-side via the add-in (
|
|
297
|
-
slidemind view-draft <deck> # per-slide PNG draft preview, no push/no .pptx — the agent's look-before-review self-check (host-free once the bundle is cached)
|
|
361
|
+
slidemind build-pptx <deck> # native PPTX rendered server-side via the add-in (pane-free; a window only with --to)
|
|
362
|
+
slidemind view-draft <deck> # lint + HTML build, then per-slide PNG draft preview, no push/no .pptx — the agent's look-before-review self-check (host-free once the bundle is cached); --fix autofixes lint
|
|
298
363
|
slidemind push <deck> # render + push into a running PowerPoint (host required)
|
|
299
364
|
slidemind read [presId|file.pptx] # pull current content of an open presentation (presId; host required) OR read a local .pptx (file path; host-free) (--mode summary|raw)
|
|
300
365
|
slidemind ls engines # list the live engine catalog (image + slide) (host required)
|
|
@@ -304,4 +369,4 @@ slidemind add-slide [opts] # fold slide(s) into a deck as native .pptx
|
|
|
304
369
|
npm run lint:parity # assert EVERY skill (create-slides + search) carries no brand rules (CI guard)
|
|
305
370
|
```
|
|
306
371
|
|
|
307
|
-
No linter beyond the parity guard, `node --test` for the suite, no package manager beyond npm itself. Slide-JSX lint
|
|
372
|
+
No linter beyond the parity guard, `node --test` for the suite, no package manager beyond npm itself. Slide-JSX lint (run by `view-draft` and `review` via `buildDeck`) uses the **add-in's** capability-table engine, materialized into the bundle cache as `lint-engine.mjs` (+ `primitives.json`) and dynamic-imported by `src/builders/lint.mjs` — this repo owns no lint rules; they update on the next bundle fetch. CI (`.github/workflows/test.yml`) runs the suite + parity guard on Linux, **Windows**, and macOS (Node 18 + 22) — the CLI promises native-Windows support, so keep it green there. The HTML preview is self-contained except for React + Babel-standalone pulled from a CDN on first load.
|
package/README.md
CHANGED
|
@@ -237,8 +237,8 @@ It does exactly the same work: connect, refresh the skills, run `ready`. Add `--
|
|
|
237
237
|
Either way, verify with:
|
|
238
238
|
|
|
239
239
|
```bash
|
|
240
|
-
slidemind whoami
|
|
241
|
-
slidemind
|
|
240
|
+
slidemind whoami # lists connected environment(s)
|
|
241
|
+
slidemind ready --diagnose # full report: backends, CLI version, open PowerPoint windows
|
|
242
242
|
```
|
|
243
243
|
|
|
244
244
|
A setup command works once and expires after about 10 minutes; copy a fresh one from the add-in if that happens.
|
|
@@ -254,7 +254,7 @@ slidemind logout # unpair this machine
|
|
|
254
254
|
```bash
|
|
255
255
|
slidemind ready # one-shot session start: check the connection, resolve the open design, fetch its bundle (host required)
|
|
256
256
|
slidemind new my-deck # scaffold ./decks/my-deck/ (auto-binds to the open design when one is available)
|
|
257
|
-
slidemind
|
|
257
|
+
slidemind view-draft my-deck # lint + render each slide to a PNG you can look at (offline once the bundle is cached)
|
|
258
258
|
slidemind review my-deck # spatial preview review: leave comments / approve in the browser (offline)
|
|
259
259
|
slidemind build-pptx my-deck # render a native .pptx via the add-in (host required)
|
|
260
260
|
# template packs may pass --pack <exact-pack-directory>
|
|
@@ -292,18 +292,18 @@ Under the hood the skill just runs the `slidemind` CLI in the agent's shell —
|
|
|
292
292
|
|
|
293
293
|
```text
|
|
294
294
|
slidemind ready [deck] One-shot session start: connect + resolve the open design + fetch bundle + bind deck (host required)
|
|
295
|
-
--to <presId> --force --connect-only --json
|
|
295
|
+
--to <presId> --force --connect-only --diagnose --json
|
|
296
296
|
slidemind new <name> Scaffold ./decks/<name>/ (auto-binds to the open design when available)
|
|
297
297
|
--to <presId> --no-bind
|
|
298
298
|
slidemind plan <deck> Plan sign-off gate on <deck>/plan.md; blocks for approve/comments (offline)
|
|
299
|
-
slidemind build <deck> Build a self-contained HTML preview (offline)
|
|
300
299
|
slidemind review <deck> Spatial preview review; blocks for approve/comments (offline)
|
|
301
300
|
slidemind build-pptx <deck> Render a native .pptx via the add-in (host required)
|
|
302
301
|
--pack <dir> for an explicit fill-template pack
|
|
303
302
|
slidemind inspect-template-deck <deck> Emit a normalized template-deck model for pack validation
|
|
304
303
|
--pack <dir> --json [--allow-unrendered-freeform]
|
|
305
|
-
slidemind view-draft <deck>
|
|
306
|
-
|
|
304
|
+
slidemind view-draft <deck> Lint + build the HTML preview, then render slides to per-slide PNGs,
|
|
305
|
+
no push — look before review (offline once cached)
|
|
306
|
+
--to <presId> --out <dir> --open --fix --only <file|N> --translate
|
|
307
307
|
slidemind push <deck> Build + push slides into an open PowerPoint (host required)
|
|
308
308
|
slidemind ls engines List the live engine catalog (image + slide) (host required)
|
|
309
309
|
slidemind search "<query>" Search an engine for imagery or slides (host required)
|
|
@@ -322,7 +322,6 @@ slidemind adopt <file.pptx> Rebuild a deck workspace from a .pptx — slide
|
|
|
322
322
|
add-in offers it, the rest fold in native
|
|
323
323
|
--deck <slug> --brand <presFP> --no-convert
|
|
324
324
|
slidemind download-bundle [deck] Materialize the active corporate design locally (host required; lower-level — `ready` covers this)
|
|
325
|
-
slidemind presentations List PowerPoint windows registered with the add-in
|
|
326
325
|
slidemind read [presId|file.pptx] Read an open presentation (presId; host required) or a local .pptx on disk (file path; host-free)
|
|
327
326
|
--mode summary|raw (default raw) [--to <presId>] [--json]
|
|
328
327
|
slidemind setup <claim> One-command onboarding: connect + global install + skills + ready
|
|
@@ -330,7 +329,6 @@ slidemind setup <claim> One-command onboarding: connect + global instal
|
|
|
330
329
|
slidemind login [--backend <e>] Sign in to Microsoft 365 (opens your browser) so SharePoint-backed
|
|
331
330
|
downloads and slide extractions work without an open PowerPoint window
|
|
332
331
|
slidemind whoami Show connected environment(s) and their Microsoft 365 sign-in state
|
|
333
|
-
slidemind use <url|env> Set the default environment
|
|
334
332
|
slidemind logout [url|env] Unpair an environment (--all unpairs every one; drops its Microsoft 365 sign-in too)
|
|
335
333
|
slidemind init --for <target> Install the skill into a harness (clears the bundle cache)
|
|
336
334
|
<target> = claude | codex | opencode | all | auto (every harness found here)
|
|
@@ -347,7 +345,7 @@ slidemind --version Print version
|
|
|
347
345
|
|
|
348
346
|
| Command | Output | When to use |
|
|
349
347
|
|---|---|---|
|
|
350
|
-
| `slidemind
|
|
348
|
+
| `slidemind view-draft <deck>` | `decks/<deck>/.draft-preview/*.png` (+ `presentation.html`) | Look at each slide before anyone else does (offline) |
|
|
351
349
|
| `slidemind build-pptx <deck>` | `decks/<deck>/<deck>.pptx` | An editable native PowerPoint file |
|
|
352
350
|
| `slidemind push <deck>` | (streamed via the add-in) | Live insertion into an open PowerPoint window |
|
|
353
351
|
|
|
@@ -368,7 +366,7 @@ It runs entirely offline (no open PowerPoint needed) — review happens *before*
|
|
|
368
366
|
With PowerPoint open and the slidemind add-in loaded, stream a deck's slides straight into the running window — no `.pptx` written to disk:
|
|
369
367
|
|
|
370
368
|
```bash
|
|
371
|
-
slidemind
|
|
369
|
+
slidemind ready --diagnose # list windows (presId, env, title, pane state)
|
|
372
370
|
slidemind push my-deck # append after the current slides
|
|
373
371
|
slidemind push my-deck --to <presId> # required if more than one window is open
|
|
374
372
|
slidemind push my-deck --at 0 # insert after slide 1 (0-based index)
|
|
@@ -391,11 +389,10 @@ If you run the add-in against more than one backend (local / dev / prod), the CL
|
|
|
391
389
|
slidemind setup <claim-from-local-add-in> --no-install --for none # each claim carries its own backend
|
|
392
390
|
slidemind setup <claim-from-dev-add-in> --no-install --for none
|
|
393
391
|
slidemind whoami # lists every connected environment (* = default)
|
|
394
|
-
slidemind
|
|
392
|
+
slidemind ready --diagnose # windows from all environments, each tagged [env]
|
|
395
393
|
slidemind push my-deck --to <presId> # routed to whichever environment owns that window
|
|
396
|
-
slidemind use dev # set the default environment
|
|
397
394
|
slidemind logout dev # disconnect one (or --all to disconnect every one)
|
|
398
|
-
slidemind --backend local
|
|
395
|
+
slidemind --backend local ready --diagnose # scope a single command to one environment
|
|
399
396
|
```
|
|
400
397
|
|
|
401
398
|
`setup` is the easiest way to add an environment because the claim carries its backend — that is exactly the fact the CLI cannot otherwise know. `--no-install --for none` keeps it to the connection alone, leaving your existing binary and skills untouched. Because the claim carries the backend, there is no environment to select and no URL to configure — the CLI hardcodes no backend at all. Once connected, sessions stay per-environment and routing is automatic. A normal user connects one environment.
|
|
@@ -420,12 +417,12 @@ Equivalently: `npm install -g slidemind@latest && slidemind init --for auto`.
|
|
|
420
417
|
|---|---|
|
|
421
418
|
| `slidemind: command not found` (or not recognized) | The npm global bin isn't on your `PATH`. Run `npm prefix -g` and add its `bin` (Windows: `%APPDATA%\npm`) to `PATH`, or use `npx slidemind`. |
|
|
422
419
|
| PowerShell: *running scripts is disabled* | npm's `.ps1` shim hit the execution policy. Use `slidemind.cmd <command>`, or `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`. |
|
|
423
|
-
| A command can't reach PowerPoint (
|
|
424
|
-
| `slidemind
|
|
420
|
+
| A command can't reach PowerPoint ("no host", nothing seems connected) | Run **`slidemind ready`** — it checks the secure connection, backend, CLI version, and open windows in one shot and prints the single next step to take. |
|
|
421
|
+
| `slidemind ready --diagnose` lists no window immediately after approval | Bring PowerPoint forward and click the slidemind pane once; its authenticated relay reconnects and registers automatically. Reopen the pane only if the Office webview itself is suspended. |
|
|
425
422
|
| `Not connected` / `not_connected` | In **PowerPoint → slidemind → Settings → General → Connect CLI**, press the link button and run the command it shows. |
|
|
426
423
|
| `this setup command has already been used or has expired` | A setup claim works exactly once and lasts ~10 minutes. Copy a fresh one from **Connect CLI → Set up CLI**. |
|
|
427
424
|
| `this slidemind add-in is too old for one-command setup` | That deployment predates the setup route and must be updated before this CLI can connect to it. |
|
|
428
|
-
| `build-pptx` / `push` / `search` error about no host | These require an open PowerPoint host with an active corporate design. `slidemind
|
|
425
|
+
| `build-pptx` / `push` / `search` error about no host | These require an open PowerPoint host with an active corporate design. `slidemind view-draft` (per-slide PNGs + HTML preview) is the fully-offline preview. |
|
|
429
426
|
| `EACCES` on `npm install -g` (macOS/Linux) | Don't `sudo npm`. Use a user-owned Node via [nvm](https://github.com/nvm-sh/nvm) or Homebrew, or run `mkdir -p ~/.npm-global && npm config set prefix ~/.npm-global`, add `~/.npm-global/bin` to `PATH`, then retry. |
|
|
430
427
|
|
|
431
428
|
---
|