slidemind 0.3.20 → 0.4.1

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 (187) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/AGENTS.md +592 -16
  4. package/LICENSE +4 -0
  5. package/README.md +203 -8
  6. package/THIRD-PARTY-NOTICES.md +201 -0
  7. package/bin/slidemind.mjs +50 -2
  8. package/package.json +5 -3
  9. package/skills/create-slides/SKILL.md +484 -886
  10. package/skills/search/SKILL.md +46 -23
  11. package/src/bridge/build-and-push.mjs +548 -73
  12. package/src/bridge/bundle.mjs +100 -31
  13. package/src/bridge/capture.mjs +14 -17
  14. package/src/bridge/client/brands.mjs +157 -18
  15. package/src/bridge/client/engine-routing.mjs +48 -13
  16. package/src/bridge/client/engine-search.mjs +24 -1
  17. package/src/bridge/client/render.mjs +27 -1
  18. package/src/bridge/client/transport.mjs +499 -23
  19. package/src/bridge/client.mjs +10 -0
  20. package/src/bridge/config.mjs +67 -6
  21. package/src/bridge/engine-download.mjs +0 -10
  22. package/src/bridge/graph-auth.mjs +55 -3
  23. package/src/bridge/image-intent-envelope.mjs +239 -0
  24. package/src/bridge/package-preview.mjs +67 -0
  25. package/src/bridge/sharepoint.mjs +914 -0
  26. package/src/builders/asset-fetch.mjs +203 -0
  27. package/src/builders/block-dividers.mjs +83 -0
  28. package/src/builders/build.mjs +171 -87
  29. package/src/builders/card-height.mjs +294 -0
  30. package/src/builders/chart-hole.mjs +183 -0
  31. package/src/builders/chart-jsx.mjs +410 -0
  32. package/src/builders/chart-labels.mjs +326 -0
  33. package/src/builders/concept-geometry.mjs +370 -0
  34. package/src/builders/concept-intent.mjs +200 -0
  35. package/src/builders/concept-layout.mjs +79 -0
  36. package/src/builders/concept-lines.mjs +509 -0
  37. package/src/builders/concept-lists.mjs +270 -0
  38. package/src/builders/concept-marks.mjs +84 -0
  39. package/src/builders/concept-relationships.mjs +12 -0
  40. package/src/builders/concept-spec.mjs +645 -0
  41. package/src/builders/concept-text.mjs +140 -0
  42. package/src/builders/concept-waves.mjs +57 -0
  43. package/src/builders/copy-fit.mjs +139 -0
  44. package/src/builders/design-tokens.mjs +347 -0
  45. package/src/builders/draft-check.mjs +552 -340
  46. package/src/builders/draft-lines.mjs +369 -0
  47. package/src/builders/edit-route.mjs +29 -0
  48. package/src/builders/figure-sketch.mjs +375 -0
  49. package/src/builders/fold-sets.mjs +639 -0
  50. package/src/builders/gate-server.mjs +9 -4
  51. package/src/builders/gate-session.js +273 -12
  52. package/src/builders/gate-shell.js +1494 -0
  53. package/src/builders/image-intent-check.mjs +308 -0
  54. package/src/builders/image-use-reconcile.mjs +107 -0
  55. package/src/builders/kit-helpers.mjs +121 -0
  56. package/src/builders/layout-names.mjs +32 -0
  57. package/src/builders/legacy-reference-uses.mjs +179 -0
  58. package/src/builders/line-fit.mjs +224 -0
  59. package/src/builders/line-native.mjs +895 -0
  60. package/src/builders/line-relations.mjs +230 -0
  61. package/src/builders/lint-fix.mjs +117 -9
  62. package/src/builders/lint.mjs +25 -20
  63. package/src/builders/list-jsx.mjs +88 -0
  64. package/src/builders/placement.mjs +133 -0
  65. package/src/builders/plan-checks.mjs +135 -0
  66. package/src/builders/plan-comments.mjs +19 -0
  67. package/src/builders/plan-handlers.mjs +95 -0
  68. package/src/builders/plan-markdown-edit.mjs +3 -3
  69. package/src/builders/plan-overlay.js +1530 -1608
  70. package/src/builders/plan.mjs +689 -186
  71. package/src/builders/preset-definitions.json +1 -0
  72. package/src/builders/preset-eval.mjs +244 -0
  73. package/src/builders/preview.html +51 -120
  74. package/src/builders/red-thread-backbone.mjs +135 -36
  75. package/src/builders/repair.mjs +205 -0
  76. package/src/builders/repeat-groups.mjs +540 -0
  77. package/src/builders/resolve-skill.mjs +5 -4
  78. package/src/builders/review-overlay.js +296 -293
  79. package/src/builders/review.mjs +305 -84
  80. package/src/builders/rule-shift.mjs +96 -0
  81. package/src/builders/seat.mjs +354 -0
  82. package/src/builders/settle-driver.mjs +236 -0
  83. package/src/builders/settle.mjs +1448 -0
  84. package/src/builders/slide-prep.mjs +67 -0
  85. package/src/builders/spec-assets.mjs +236 -0
  86. package/src/builders/spec-match.mjs +56 -0
  87. package/src/builders/stack.mjs +413 -0
  88. package/src/builders/storyboard-markdown.mjs +70 -27
  89. package/src/builders/storyboard-schema.mjs +54 -114
  90. package/src/builders/text-blocks.mjs +212 -0
  91. package/src/builders/text-card-fit.mjs +552 -0
  92. package/src/builders/text-stack.mjs +276 -0
  93. package/src/builders/transcribe.mjs +1069 -0
  94. package/src/cli/assets.mjs +164 -0
  95. package/src/cli/common.mjs +173 -12
  96. package/src/cli/concept-spec-support.mjs +76 -0
  97. package/src/cli/concepts.mjs +1002 -103
  98. package/src/cli/deck.mjs +401 -65
  99. package/src/cli/diagnose.mjs +4 -2
  100. package/src/cli/engine-helpers.mjs +17 -10
  101. package/src/cli/engine-results.mjs +76 -3
  102. package/src/cli/help.mjs +232 -28
  103. package/src/cli/install.mjs +9 -0
  104. package/src/cli/maintenance.mjs +104 -0
  105. package/src/cli/read.mjs +2 -1
  106. package/src/cli/ready.mjs +6 -3
  107. package/src/cli/record.mjs +522 -0
  108. package/src/cli/remote.mjs +818 -0
  109. package/src/cli/revision.mjs +686 -0
  110. package/src/cli/search.mjs +30 -11
  111. package/src/cli/session.mjs +56 -13
  112. package/src/cli/setup.mjs +46 -2
  113. package/src/cli/status.mjs +142 -33
  114. package/src/cli/tasks.mjs +246 -0
  115. package/src/cli/transcribe.mjs +854 -0
  116. package/src/cli/workspace-ui.mjs +260 -0
  117. package/src/cli/workspace.mjs +212 -34
  118. package/src/install-channel.mjs +64 -7
  119. package/src/lib/adopt.mjs +346 -25
  120. package/src/lib/concept-spec-binding.mjs +96 -0
  121. package/src/lib/conversions.mjs +70 -6
  122. package/src/lib/convert-folded.mjs +107 -0
  123. package/src/lib/deck-paths.mjs +418 -0
  124. package/src/lib/durable-ids.mjs +47 -0
  125. package/src/lib/edit-operations.mjs +316 -0
  126. package/src/lib/edit-state.mjs +1244 -0
  127. package/src/lib/edit-transitions.mjs +120 -0
  128. package/src/lib/extensions.mjs +501 -0
  129. package/src/lib/image-assets.mjs +465 -0
  130. package/src/lib/image-derivatives.mjs +234 -0
  131. package/src/lib/image-intent-fingerprint.mjs +70 -0
  132. package/src/lib/image-intent-tags.mjs +176 -0
  133. package/src/lib/image-intent.mjs +802 -0
  134. package/src/lib/image-use-overrides.mjs +279 -0
  135. package/src/lib/inline-components.mjs +613 -0
  136. package/src/lib/lint-converted.mjs +68 -4
  137. package/src/lib/merge-json.mjs +163 -0
  138. package/src/lib/overwrite-guard.mjs +3 -3
  139. package/src/lib/presentation-context.mjs +531 -0
  140. package/src/lib/preservation-tags.mjs +157 -0
  141. package/src/lib/preservation-verdict.mjs +1342 -0
  142. package/src/lib/publish.mjs +1557 -0
  143. package/src/lib/rebase.mjs +945 -0
  144. package/src/lib/reconcile-ack.mjs +4 -6
  145. package/src/lib/reconcile.mjs +62 -24
  146. package/src/lib/record-client.mjs +175 -0
  147. package/src/lib/record-state.mjs +151 -0
  148. package/src/lib/remote.mjs +661 -0
  149. package/src/lib/revision.mjs +1210 -0
  150. package/src/lib/slide-guids.mjs +4 -6
  151. package/src/lib/slide-preservation.mjs +423 -0
  152. package/src/lib/slide-selection.mjs +9 -0
  153. package/src/lib/workspace-transaction.mjs +117 -0
  154. package/src/pptx/content-digest.mjs +334 -0
  155. package/src/pptx/deck-assemble.mjs +1278 -0
  156. package/src/pptx/fill/engine.mjs +2 -3
  157. package/src/pptx/fill/freeform.mjs +6 -3
  158. package/src/pptx/ooxml.mjs +20 -0
  159. package/src/pptx/package-evidence.mjs +383 -0
  160. package/src/pptx/tags.mjs +163 -11
  161. package/src/update-check.mjs +22 -4
  162. package/src/workspace/adapters.mjs +222 -0
  163. package/src/workspace/auth.mjs +256 -0
  164. package/src/workspace/autostart.mjs +204 -0
  165. package/src/workspace/daemon.mjs +360 -0
  166. package/src/workspace/discovery.mjs +180 -0
  167. package/src/workspace/edit.mjs +167 -0
  168. package/src/workspace/events.mjs +52 -0
  169. package/src/workspace/gate-client.mjs +290 -0
  170. package/src/workspace/handback.mjs +73 -0
  171. package/src/workspace/lifecycle.mjs +635 -0
  172. package/src/workspace/operations.mjs +200 -0
  173. package/src/workspace/pick-folder.mjs +152 -0
  174. package/src/workspace/registry.mjs +359 -0
  175. package/src/workspace/remote-links.mjs +397 -0
  176. package/src/workspace/remote-status.mjs +359 -0
  177. package/src/workspace/routes.mjs +1331 -0
  178. package/src/workspace/server.mjs +159 -0
  179. package/src/workspace/sync.mjs +1119 -0
  180. package/src/workspace/task-actions.mjs +215 -0
  181. package/src/workspace/tasks.mjs +507 -0
  182. package/src/workspace/ui/app.js +1886 -0
  183. package/src/workspace/ui/index.html +141 -0
  184. package/src/workspace/ui/plan-bridge.js +120 -0
  185. package/src/workspace/ui/sharepoint.svg +59 -0
  186. package/src/workspace/ui/styles.css +423 -0
  187. package/src/builders/visual-families.mjs +0 -46
@@ -0,0 +1,279 @@
1
+ // Image uses recorded by a TARGETED EDIT — a change to a slide that already
2
+ // exists, made without a plan stage.
3
+ //
4
+ // <workspace>/state/image-use-overrides.json
5
+ // { "version": 1, "slides": [ { "slideId", "assetUses": [...], "revision" } ] }
6
+ //
7
+ // Three rules, all of them load-bearing:
8
+ //
9
+ // * **Keyed by the slide's durable identity, never its number.** `slideId` is
10
+ // the deck's own slide GUID (src/lib/slide-guids.mjs), which survives the
11
+ // rename-every-file reorder that IS the common deck edit. A positional key
12
+ // would silently re-point an obligation at a different slide the first time
13
+ // someone drags a card (design decision 8).
14
+ // * **An entry REPLACES, it does not patch.** The list is that slide's whole
15
+ // set of uses. An explicit `[]` is therefore meaningful: it is the record
16
+ // of an authorized clearing, and it is exactly what keeps a removed
17
+ // obligation removed — deleting the entry instead would fall back to the
18
+ // plan's uses and resurrect it.
19
+ // * **The plan wins again at the next approval.** `reconcileImageUseOverrides`
20
+ // (src/builders/image-use-reconcile.mjs, where the plan markdown lives)
21
+ // folds each entry into its slide's storyboard and clears only the entries
22
+ // it superseded. Everything here is the file half of that.
23
+ //
24
+ // Effective uses for a slide = its override if it has one, else its storyboard
25
+ // uses. That selection is `selectImageIntent` in the shared contract module;
26
+ // this file only supplies it the right slideId.
27
+
28
+ import fs from "node:fs";
29
+ import path from "node:path";
30
+
31
+ import { statePath } from "./deck-paths.mjs";
32
+ import { imageAssetError, readImageAssetManifest } from "./image-assets.mjs";
33
+ import {
34
+ IMAGE_INTENT_VERSION,
35
+ parseImageUseOverrides,
36
+ selectImageIntent,
37
+ validateSlideUses,
38
+ } from "./image-intent.mjs";
39
+ import { readRevision, slideFileOrder } from "./revision.mjs";
40
+ import { reconcileSlideGuids } from "./slide-guids.mjs";
41
+
42
+ export const OVERRIDES_FILE = "image-use-overrides.json";
43
+
44
+ export function overridesPath(workspaceDir) {
45
+ return statePath(workspaceDir, OVERRIDES_FILE);
46
+ }
47
+
48
+ const EMPTY = () => ({ version: IMAGE_INTENT_VERSION, slides: [] });
49
+
50
+ function readRaw(workspaceDir) {
51
+ const file = overridesPath(workspaceDir);
52
+ let text;
53
+ try {
54
+ text = fs.readFileSync(file, "utf8");
55
+ } catch (err) {
56
+ if (err?.code === "ENOENT") return null;
57
+ throw imageAssetError("image_asset_missing", `${file} could not be read: ${err.message}`);
58
+ }
59
+ try {
60
+ return JSON.parse(text);
61
+ } catch (err) {
62
+ throw imageAssetError("image_intent_invalid", `${file} is not valid JSON: ${err.message}`);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * The deck's overrides. An absent file is an empty set; a present one that does
68
+ * not parse is an error, never an empty set — starting over silently would drop
69
+ * every obligation a targeted edit recorded.
70
+ *
71
+ * Cross-references the deck's image registry, so an override can never point at
72
+ * an asset this deck does not have.
73
+ */
74
+ export function readImageUseOverrides(workspaceDir) {
75
+ const raw = readRaw(workspaceDir);
76
+ if (raw === null) return EMPTY();
77
+ const assetIds = readImageAssetManifest(workspaceDir).assets.map((a) => a.assetId);
78
+ const result = parseImageUseOverrides(raw, { assetIds });
79
+ if (!result.value) {
80
+ const first = result.issues[0];
81
+ throw imageAssetError(
82
+ first?.code ?? "image_intent_invalid",
83
+ `${overridesPath(workspaceDir)}: ${first?.message ?? "invalid overrides"}`,
84
+ );
85
+ }
86
+ return result.value;
87
+ }
88
+
89
+ /**
90
+ * Which edit each entry was recorded by:
91
+ * `{ [slideId]: {generation, recordedAt} }`.
92
+ *
93
+ * Read off the raw file, because the contract's parser returns the obligation
94
+ * and nothing else — provenance is per-client bookkeeping, not part of the
95
+ * record both repositories validate. It lives under one agreed key, `revision`,
96
+ * so neither side has to widen the shared schema to carry its own fields: the
97
+ * CLI writes the deck's revision `generation`, the add-in writes its run and
98
+ * operation ids, and each ignores the other's. Best effort: a missing or
99
+ * malformed stamp costs nothing that an obligation depends on.
100
+ */
101
+ export function readOverrideProvenance(workspaceDir) {
102
+ const raw = readRaw(workspaceDir);
103
+ const out = {};
104
+ for (const entry of raw?.slides ?? []) {
105
+ if (!entry || typeof entry.slideId !== "string") continue;
106
+ const revision = entry.revision ?? {};
107
+ out[entry.slideId] = {
108
+ generation: Number.isInteger(revision.generation) ? revision.generation : null,
109
+ recordedAt: typeof revision.recordedAt === "string" ? revision.recordedAt : null,
110
+ };
111
+ }
112
+ return out;
113
+ }
114
+
115
+ /** Write-then-rename, so a reader sees the old set or the new one, never half. */
116
+ export function writeImageUseOverrides(workspaceDir, doc) {
117
+ const file = overridesPath(workspaceDir);
118
+ fs.mkdirSync(path.dirname(file), { recursive: true });
119
+ const tmp = `${file}.${process.pid}.tmp`;
120
+ fs.writeFileSync(tmp, JSON.stringify(doc, null, 2) + "\n");
121
+ fs.renameSync(tmp, file);
122
+ return file;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // slide identity
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * The deck's slides in play order with the GUID each one is known by:
131
+ * `[{ slide, file, slideId }]`.
132
+ *
133
+ * Play order is filename order over jsx AND native .pptx alike
134
+ * (`slideFileOrder`), so slide numbers here are the numbers every other
135
+ * boundary means. A native slide has no GUID and simply has no `slideId` — it
136
+ * carries no authored image uses either.
137
+ *
138
+ * Reconciled, not merely read: the NN- prefix is the play order, so reordering
139
+ * a deck renames every slide file, and `reconcileSlideGuids` is what re-binds a
140
+ * renamed file to its GUID by content hash. Reading the sidecar raw would lose
141
+ * exactly the identity an override depends on, at exactly the edit it is
142
+ * supposed to survive. (It is the same call every bake and render already makes,
143
+ * and it writes only when something moved.)
144
+ */
145
+ export function slideIdentities(workspaceDir) {
146
+ const guids = reconcileSlideGuids(workspaceDir);
147
+ return slideFileOrder(workspaceDir).map((file, index) => ({
148
+ slide: index + 1,
149
+ file,
150
+ slideId: guids[`slides/${file}`] ?? null,
151
+ }));
152
+ }
153
+
154
+ /** The durable ID of slide number `n`, or null (new slide, native slide). */
155
+ export function slideIdForNumber(workspaceDir, slide) {
156
+ return slideIdentities(workspaceDir).find((s) => s.slide === slide)?.slideId ?? null;
157
+ }
158
+
159
+ /** The current number of the slide `slideId` names, or null if it is gone. */
160
+ export function slideNumberForId(workspaceDir, slideId) {
161
+ return slideIdentities(workspaceDir).find((s) => s.slideId === slideId)?.slide ?? null;
162
+ }
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // reading and writing one slide's uses
166
+ // ---------------------------------------------------------------------------
167
+
168
+ /**
169
+ * The uses that actually apply to a slide: its override if one exists (an empty
170
+ * one included — that is a deliberate clearing), otherwise the storyboard uses
171
+ * the caller read out of the plan.
172
+ *
173
+ * @param {string} workspaceDir
174
+ * @param {{slide?: number, slideId?: string|null, storyboardUses?: Array}} args
175
+ * @returns {{uses: Array, source: "override"|"storyboard", slideId: string|null}}
176
+ */
177
+ export function effectiveSlideUses(workspaceDir, { slide, slideId, storyboardUses = [] }) {
178
+ const id = slideId ?? (slide ? slideIdForNumber(workspaceDir, slide) : null);
179
+ const selected = selectImageIntent({
180
+ slideId: id,
181
+ storyboardUses,
182
+ overrides: readImageUseOverrides(workspaceDir),
183
+ });
184
+ return { ...selected, slideId: id };
185
+ }
186
+
187
+ /**
188
+ * Record one slide's complete use list.
189
+ *
190
+ * `uses` is validated through the contract's own parser against this deck's
191
+ * registry and against every OTHER entry's use IDs, so an override can neither
192
+ * invent an asset nor collide with an obligation elsewhere in the deck. An
193
+ * empty list is accepted and stored: it is the authorized clearing.
194
+ *
195
+ * The edit that recorded it is stamped alongside from the deck's existing
196
+ * revision metadata, which is what lets a later reader say which generation an
197
+ * obligation came from.
198
+ */
199
+ export function setSlideImageUses(
200
+ workspaceDir,
201
+ slideId,
202
+ uses,
203
+ {
204
+ generation = readRevision(workspaceDir)?.generation ?? null,
205
+ recordedAt = new Date().toISOString(),
206
+ } = {},
207
+ ) {
208
+ const id = String(slideId ?? "").trim();
209
+ if (!id) throw imageAssetError("image_intent_invalid", "an image use override needs a slideId");
210
+
211
+ const current = readImageUseOverrides(workspaceDir);
212
+ const manifest = readImageAssetManifest(workspaceDir);
213
+ // Deck-wide useId uniqueness, minus this slide's own current entry — which is
214
+ // being replaced, so re-recording the same list is not a collision.
215
+ const seenUseIds = new Set();
216
+ for (const entry of current.slides) {
217
+ if (entry.slideId === id) continue;
218
+ for (const use of entry.assetUses) seenUseIds.add(use.useId);
219
+ }
220
+ const parsed = validateSlideUses(uses ?? [], {
221
+ assetIds: manifest.assets.map((a) => a.assetId),
222
+ seenUseIds,
223
+ });
224
+ if (!parsed.value) {
225
+ const first = parsed.issues[0];
226
+ throw imageAssetError(first?.code ?? "image_intent_invalid", first?.message ?? "invalid image uses");
227
+ }
228
+
229
+ // Other slides' entries are carried over from the RAW file, not from the
230
+ // parsed value: the contract's parser returns the obligation only, and
231
+ // rewriting parsed entries would strip the provenance stamp off every other
232
+ // slide as a side effect of touching this one.
233
+ const kept = (readRaw(workspaceDir)?.slides ?? []).filter(
234
+ (entry) => entry?.slideId !== id,
235
+ );
236
+ const doc = {
237
+ version: IMAGE_INTENT_VERSION,
238
+ slides: [
239
+ ...kept,
240
+ {
241
+ slideId: id,
242
+ assetUses: parsed.value,
243
+ revision: { ...(generation == null ? {} : { generation }), recordedAt },
244
+ },
245
+ ],
246
+ };
247
+ writeImageUseOverrides(workspaceDir, doc);
248
+ return { slideId: id, assetUses: parsed.value, generation, recordedAt };
249
+ }
250
+
251
+ /**
252
+ * Forget a slide's override entirely, so its plan storyboard governs again.
253
+ *
254
+ * This is NOT how an obligation is cleared — clearing is
255
+ * `setSlideImageUses(dir, id, [])`, which records that the slide is to carry no
256
+ * image. Dropping the entry falls back to the plan and would bring a use back.
257
+ */
258
+ export function removeSlideOverride(workspaceDir, slideId) {
259
+ return clearSlideOverrides(workspaceDir, [slideId]) > 0;
260
+ }
261
+
262
+ /**
263
+ * Drop several entries in ONE write — what an approved plan revision does to
264
+ * the overrides it just superseded. Returns how many were dropped.
265
+ */
266
+ export function clearSlideOverrides(workspaceDir, slideIds) {
267
+ const drop = new Set(slideIds);
268
+ if (!drop.size) return 0;
269
+ // readImageUseOverrides first: a file that does not parse must fail loudly
270
+ // rather than be quietly rewritten from its own unreadable contents.
271
+ readImageUseOverrides(workspaceDir);
272
+ // Kept entries are carried over from the RAW file so their provenance stamp
273
+ // survives a fold that had nothing to do with them.
274
+ const raw = readRaw(workspaceDir)?.slides ?? [];
275
+ const kept = raw.filter((entry) => !drop.has(entry?.slideId));
276
+ if (kept.length === raw.length) return 0;
277
+ writeImageUseOverrides(workspaceDir, { version: IMAGE_INTENT_VERSION, slides: kept });
278
+ return raw.length - kept.length;
279
+ }