klypix-mcp 1.43.0 → 1.44.0

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/FORMAT.md CHANGED
@@ -5,21 +5,35 @@
5
5
  your.klypix`), and parseable with the Apache-2.0 library in this package. You own it;
6
6
  any agent or app can read and write it.
7
7
 
8
+ Two working examples ship in this package under [`examples/`](examples/). Both are
9
+ text-and-arrows only (14 cards, no `assets/` entry), so they demonstrate the card /
10
+ container / connection model, not the embedded-binary half described below.
11
+
8
12
  ## Container layout
9
13
 
10
14
  ```
11
- your.klypix (a ZIP archive)
12
- ├── manifest.json metadata + stats
13
- ├── canvas.json spatial layout: order, positions, connections, lines, strokes, settings
15
+ your.klypix (a ZIP archive)
16
+ ├── manifest.json metadata + stats; read FIRST
17
+ ├── canvas.json spatial layout: order, positions, connections, lines, strokes, view, settings
14
18
  ├── items/
15
- │ └── <2-hex>/<id>.json one file per item (content only; position lives in canvas.json)
16
- └── assets/
17
- └── <assetId> embedded binaries (images, PDFs, audio, video, files)
19
+ │ └── <shard>/<id>.json one file per item (content only; geometry lives in canvas.json)
20
+ └── assets/ embedded binaries — any non-directory entry here is an asset
21
+ ├── images/<shard>/<sha256>.<ext>
22
+ ├── files/<shard>/<sha256>.bin
23
+ └── thumbs/<itemId>.png
18
24
  ```
19
25
 
20
- Item files are **sharded** by the first 2 hex chars of the id's random part
21
- (e.g. `items/a3/txt_a3f9…json`) so a canvas with thousands of items stays fast
22
- to read partially.
26
+ `<shard>` is the **first two characters of the id (or sha) after its namespace
27
+ prefix is stripped**, lowercased, left-padded with `_` if shorter — e.g.
28
+ `txt_hto5hb3r_0_0` → `items/ht/txt_hto5hb3r_0_0.json`, and sha `a3f9…` →
29
+ `assets/images/a3/a3f9….png`. Ids are base36-ish, not strictly hex, so treat the
30
+ shard as "two characters", not "two hex digits". Sharding bounds files-per-directory
31
+ and lets a canvas with thousands of items be read partially.
32
+
33
+ Readers should treat the `assets/` sub-layout as **convention, not contract**: this
34
+ package's parser counts and reads *any* non-directory entry under `assets/`, so a
35
+ writer that flattens to `assets/<assetId>` still parses. Only the KLYPIX app's own
36
+ writer guarantees the content-addressed `images/ files/ thumbs/` split.
23
37
 
24
38
  ## `manifest.json`
25
39
 
@@ -28,13 +42,30 @@ to read partially.
28
42
  "format": "klypix",
29
43
  "version": 4,
30
44
  "schemaVersion": 4,
45
+ "kind": "brain",
31
46
  "createdAt": "ISO-8601",
32
47
  "updatedAt": "ISO-8601",
33
48
  "title": "My board",
34
- "stats": { "itemCount": 12, "assetCount": 3, "totalBytes": 0 }
49
+ "stats": { "itemCount": 12, "assetCount": 3, "totalBytes": 0 },
50
+ "sync": { "enabled": false, "lastSyncRev": null, "lastSyncAt": null, "deviceId": "dev_…" }
35
51
  }
36
52
  ```
37
53
 
54
+ - `version` — on-disk **layout** version (the container shape).
55
+ - `schemaVersion` — **document** version (item/connection shapes).
56
+ - **`kind: "brain"`** — optional and additive; present only on a **project brain**.
57
+ A brain is the co-owned agent/human memory file, and it gets stricter write
58
+ semantics: union-merge-on-save under a capture lock, tombstone-only deletes. It is
59
+ detected as `manifest.kind === "brain"` **OR** a `brain.*` basename — the filename
60
+ convention keeps working forever, and the flag makes those semantics survive a
61
+ rename. Plain canvases omit `kind` entirely, and older readers ignore it.
62
+ - `stats` is **informational only.** `totalBytes` is an uncompressed estimate (the
63
+ on-disk size depends on compression), and `itemCount` is derived from
64
+ `canvas.json.order`, which can disagree with the number of files actually under
65
+ `items/`. Never use `stats` as an integrity check.
66
+ - `sync` records whether the file is opted into the app's cloud sync; it carries no
67
+ content and no credentials.
68
+
38
69
  ## `canvas.json`
39
70
 
40
71
  ```json
@@ -46,21 +77,34 @@ to read partially.
46
77
  "<id>": { "x": 0, "y": 0, "w": 280, "h": 80, "zKey": "a001", "zIndex": 1, "parentId": null }
47
78
  },
48
79
  "connections": [ // arrows between items
49
- { "id": "con_…", "fromId": "<id>", "toId": "<id>", "relationship": "leads_to", "arrowHead": true }
80
+ { "id": "con_…", "fromId": "<id>", "toId": "<id>",
81
+ "relationship": "conflicts_with", "label": "corrects", // both optional
82
+ "arrowHead": true, "width": 2, "color": "#10b981", "style": "solid" }
50
83
  ],
51
84
  "lines": [], "strokes": [], // freehand drawing
52
- "settings": { "background": "#0a0a0f" }
85
+ "nextGroupNumber": 4, // container auto-naming counter
86
+ "settings": { "background": "#0a0a0f", "gridStyle": "dots", "gridColor": "…" } // all optional
53
87
  }
54
88
  ```
55
89
 
56
90
  Position/geometry is kept in `canvas.json.positions`, **not** in the item file —
57
91
  so moving an item never rewrites its content, and the spatial layout can be read
58
- without loading every item.
92
+ without loading every item. `zKey` is a fractional index and is the source of truth
93
+ for z-order; `zIndex` is a numeric cache kept in step with the `order[]` index.
94
+ `settings` is optional and captures the *sender's* visual context, so a recipient
95
+ does not see someone else's canvas in their own theme.
96
+
97
+ `order` and `positions` are the two indexes a reader walks. A tolerant reader skips
98
+ an `order` entry with no `positions` row and an entry with no file under `items/`
99
+ (this package's parser does both) — but note that a writer which then re-serializes
100
+ that state can make the omission permanent.
59
101
 
60
102
  ## Item files — `items/<shard>/<id>.json`
61
103
 
62
- Content only (no x/y/w/h). Common types: `text`, `box`, `image`, `file`,
63
- `code`, `video`, `audio`, `link`, `canvasLink`, `container`. Example text item:
104
+ Content only — no `id` (it is in the path) and no `x/y/w/h/zIndex/zKey/parentId`
105
+ (those are in `canvas.json.positions`). The `type` field is one of exactly eleven
106
+ strings: `text`, `box`, `image`, `file`, `container`, `approval`, `link`,
107
+ `canvas-link`, `video`, `audio`, `code`. Example text item:
64
108
 
65
109
  ```json
66
110
  {
@@ -70,12 +114,44 @@ Content only (no x/y/w/h). Common types: `text`, `box`, `image`, `file`,
70
114
  "color": "#e8e8ed",
71
115
  "border": true,
72
116
  "heading": false,
73
- "createdBy": "agent"
117
+ "createdBy": "agent",
118
+ "createdVia": "claude-code"
74
119
  }
75
120
  ```
76
121
 
77
- Items can reference an embedded binary in `assets/` (e.g. an `image`/`file`/
78
- `video`/`audio` item points at its asset id).
122
+ `createdBy` (`user` | `agent`) and the optional `createdVia` (which agent/channel
123
+ captured it) are the provenance bits the brain surfaces as badges and lenses.
124
+
125
+ ## Bytes: when they are embedded vs referenced
126
+
127
+ **Embedded** — the bytes live inside the ZIP under `assets/`, and the item JSON
128
+ references them by key/sha:
129
+
130
+ | Item type | What is embedded |
131
+ |---|---|
132
+ | `image` | the original image (plus an optional downscaled `thumbnailAssetId`). Non-destructive crop/rotate/annotation data lives in the item JSON; the original asset stays canonical. |
133
+ | `file` | the original file bytes. A **folder card** (`isFolder: true`) embeds the whole directory as one ZIP asset, with a `folderManifest` listing what's inside so the card renders without unzipping. |
134
+ | `video`, `audio` | the media bytes, streamed from the asset at render time rather than loaded whole. |
135
+
136
+ **Referenced, never embedded** — nothing is copied into the file:
137
+
138
+ | Item type | What it points at |
139
+ |---|---|
140
+ | `link` | a remote `url` (+ cached Open Graph title/description; the OG image is fetched live, not stored) |
141
+ | `canvas-link` | another `.klypix` file by **absolute path on disk** — so it breaks if you email the file alone |
142
+ | `text`, `code`, `box`, `container`, `approval` | content is inline in the item JSON; there is no asset at all |
143
+
144
+ `file` / `video` / `audio` items may also carry `originalPath` — a convenience
145
+ pointer for "open in the OS app". The bytes are still embedded; the path is not the
146
+ source of truth.
147
+
148
+ Legacy files may carry an `image` item's bytes as an inline base64 `src` data URL
149
+ instead of an asset. New writers use `assetId` and leave `src` empty.
150
+
151
+ **This package reads assets but does not create them.** `create_canvas`,
152
+ `add_to_canvas` and `buildKlypix` write `manifest.json`, `canvas.json` and item
153
+ files — cards and arrows. Embedding binaries is done by the KLYPIX app when you drop
154
+ a file onto a canvas.
79
155
 
80
156
  ## Connections, links, tags
81
157
 
@@ -85,10 +161,56 @@ Items can reference an embedded binary in `assets/` (e.g. an `image`/`file`/
85
161
  - **`[[wikilinks]]`** inside text content cross-link cards (and auto-draw edges).
86
162
  - **`#tags`** inside text content group cards.
87
163
 
164
+ ## Versioning — and a real limitation
165
+
166
+ `manifest.version` is the layout version and `manifest.schemaVersion` the document
167
+ version. Both are `4` today.
168
+
169
+ **There is no migration path registered for v4, and the v4 load path does not run
170
+ one.** Be precise about what that means, because it is a live limitation, not a
171
+ to-do that is quietly handled:
172
+
173
+ - The migration framework exists and works, but its table contains exactly two
174
+ steps, `1 → 2` and `2 → 3`. Only the legacy `.any` (v1–v3) read path walks it; the
175
+ v4 path never enters the migration runner.
176
+ - Both this package's parser and the KLYPIX app's v4 reader treat
177
+ `manifest.version >= 4` as "this is v4" and dispatch on nothing else — the app's
178
+ deserializer never reads `manifest.version`, `manifest.schemaVersion` or
179
+ `canvas.json.version` at all.
180
+ - So a hypothetical v5 or v6 file **opens as v4**: fields the reader does not know
181
+ are dropped, with no "this build is too old to open it" error anywhere. Saving it
182
+ from the app then rewrites `version: 4` — a silent downgrade. (This package's
183
+ `appendToKlypix` is non-destructive by construction: it mutates the parsed
184
+ manifest and `canvas.json` in place and leaves every existing item file byte-for-byte
185
+ untouched, so unknown fields survive a round trip through it.)
186
+
187
+ Consequences for anyone implementing against this format: do not assume a future
188
+ version will be rejected for you, and if you write `.klypix` files, do not bump
189
+ `manifest.version` past 4 expecting existing readers to refuse them safely. If you
190
+ extend the format, prefer **additive** optional fields (the way `manifest.kind` was
191
+ added) over a version bump.
192
+
88
193
  ## Legacy `.any` (v1–v3)
89
194
 
90
195
  Older files keep an inline `items` array at the root of `canvas.json` instead of
91
- the `items/` folder + `positions` map. The parser in this package handles both.
196
+ the `items/` folder + `positions` map. The parser in this package handles both, and
197
+ detects the older shape by the absence of `positions` even if the manifest is
198
+ missing. Legacy files are migrated `1 → 2 → 3` on open by the app.
199
+
200
+ ## Practical size limits
201
+
202
+ The format itself imposes no size limit — a `.klypix` is a ZIP. The limits below are
203
+ **guards in the KLYPIX app**, not properties of the format, and this package's
204
+ parser enforces none of them:
205
+
206
+ | Limit | Value | Where it applies |
207
+ |---|---|---|
208
+ | Per-file (leaf) on folder ingest | **200 MB** | a single file inside a dropped folder; anything larger is skipped and recorded in the card's `folderSkipped` list so you can see what was left out |
209
+ | Per folder card, total | **1 GB** | ingestion stops once a dropped folder would push the card past this |
210
+ | Per cloud share | **50 MB** | sharing a canvas through the app's cloud; a larger canvas is refused with a `too-large` result rather than truncated |
211
+
212
+ Nothing stops you writing a larger file locally with this library; expect the app's
213
+ share and folder-ingest paths to refuse it.
92
214
 
93
215
  ## Read / write it
94
216
 
@@ -102,4 +224,11 @@ const buf = await buildKlypix({ title: 'Plan', cards: [{ text: 'kickoff' }] });
102
224
  fs.writeFileSync('plan.klypix', buf);
103
225
  ```
104
226
 
227
+ Or from a shell, against one of the examples in the tarball:
228
+
229
+ ```bash
230
+ npm i klypix-mcp
231
+ npx klypix-read node_modules/klypix-mcp/examples/showcase-brain.klypix
232
+ ```
233
+
105
234
  That's the whole contract: **a ZIP you own, that any model can read and write.**
package/NOTICE ADDED
@@ -0,0 +1,6 @@
1
+ klypix-mcp
2
+ Copyright 2026 Abdullah Aldahshan — Dahshan Labs
3
+
4
+ This product is licensed under the Apache License, Version 2.0 (see LICENSE).
5
+ Prior published versions (<= 1.28.0) were released under the MIT License and
6
+ remain available under those terms.