klypix-mcp 1.43.1 → 1.45.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 +148 -19
- package/NOTICE +6 -0
- package/README.md +52 -15
- package/bin/klypix-conformance.mjs +4 -0
- package/bin/klypix-doctor.mjs +10 -1
- package/bin/klypix-install.mjs +5 -0
- package/bin/klypix-link.mjs +28 -1
- package/bin/klypix-mcp.mjs +32 -1
- package/bin/klypix-uninstall.mjs +194 -0
- package/bin/klypix-worker.mjs +46 -12
- package/examples/showcase-brain.klypix +0 -0
- package/examples/showcase-wedding.klypix +0 -0
- package/package.json +7 -4
- package/src/agent-presence.mjs +37 -4
- package/src/global-brain-hook.mjs +9 -0
- package/src/klypix-format.mjs +300 -3
- package/src/mcp-presence.mjs +75 -13
- package/src/uninstall.mjs +548 -0
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
|
|
12
|
-
├── manifest.json
|
|
13
|
-
├── canvas.json
|
|
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
|
-
│ └── <
|
|
16
|
-
└── assets/
|
|
17
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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>",
|
|
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
|
-
"
|
|
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
|
|
63
|
-
|
|
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
|
-
|
|
78
|
-
|
|
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
package/README.md
CHANGED
|
@@ -85,16 +85,28 @@ It verifies 7 coordination behaviours — not the 18 tools, and not the retrieva
|
|
|
85
85
|
|
|
86
86
|
## Quick start
|
|
87
87
|
|
|
88
|
-
**Claude Code + Codex
|
|
88
|
+
**Claude Code + Codex:**
|
|
89
89
|
|
|
90
90
|
```bash
|
|
91
91
|
npx klypix-mcp install
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
This copies the engine and a local MCP runtime into `~/.claude/project-brain`, wires Claude Code's
|
|
95
|
-
four lifecycle hooks,
|
|
96
|
-
|
|
97
|
-
|
|
95
|
+
four lifecycle hooks, writes Codex's global `~/.codex/AGENTS.md` guidance block, and wires Codex's
|
|
96
|
+
MCP connection **for the project you run it in**.
|
|
97
|
+
|
|
98
|
+
Be precise about what "machine-global" covers:
|
|
99
|
+
|
|
100
|
+
- **Machine-global** — the engine + runtime in `~/.claude/project-brain`, the four Claude Code
|
|
101
|
+
hooks in `~/.claude/settings.json`, and the `~/.codex/AGENTS.md` guidance. Claude Code is
|
|
102
|
+
therefore covered in every project on that machine that has a `./brain.klypix`.
|
|
103
|
+
- **Per project** — Codex's MCP connection. `install` writes it into `<cwd>/.codex/config.toml`,
|
|
104
|
+
only when that directory has a `brain.klypix`, and it deliberately **removes** any *global*
|
|
105
|
+
`~/.codex/config.toml` KLYPIX entry (a global entry resolves its `--vault` from the wrong
|
|
106
|
+
directory and binds the wrong brain). Run `install` — or `link` — once inside each brain project
|
|
107
|
+
you want Codex wired to.
|
|
108
|
+
|
|
109
|
+
It does **not** set up Cursor, Cline, Windsurf, Copilot, Gemini CLI or Aider — those need `link`.
|
|
98
110
|
|
|
99
111
|
Optional, opt-in, and approved inside Codex itself:
|
|
100
112
|
|
|
@@ -174,8 +186,9 @@ behaviour is unverified.
|
|
|
174
186
|
| **Aider** | Rules file only (no MCP) | `link` | CLI path: `npx klypix-read` | CLI path: `npx klypix-append` | — |
|
|
175
187
|
| **Claude Desktop** | One-time manual config edit | you | Model must call `brain_sync` | Model must call `brain_note` | For the MCP connection |
|
|
176
188
|
|
|
177
|
-
`install` and `link` are different things and are not interchangeable: `install`
|
|
178
|
-
|
|
189
|
+
`install` and `link` are different things and are not interchangeable: `install` only touches Claude
|
|
190
|
+
Code and Codex, and is machine-global for everything except Codex's MCP connection, which it writes
|
|
191
|
+
per project (see *Quick start*); `link` is per project and is what wires everything else.
|
|
179
192
|
|
|
180
193
|
**Claude Desktop** — add this to `claude_desktop_config.json` by hand; nothing writes that file
|
|
181
194
|
for you:
|
|
@@ -329,10 +342,12 @@ Exactly 18, machine-verifiable with `npx klypix-mcp doctor`.
|
|
|
329
342
|
|
|
330
343
|
## One file you can hold
|
|
331
344
|
|
|
332
|
-
The whole brain — layout, cards, arrows, and the actual bytes (images, PDFs, audio,
|
|
345
|
+
The whole brain — layout, cards, arrows, and the actual bytes (images, PDFs, audio, video) — is a
|
|
333
346
|
single `.klypix` file: a plain ZIP with `manifest.json`, `canvas.json`, one JSON file per card, and
|
|
334
347
|
an `assets/` folder. Email it. Git it. Hand it to an agent. A folder of markdown points at its
|
|
335
|
-
attachments; this file carries them.
|
|
348
|
+
attachments; this file carries them. (Binaries are embedded by the **KLYPIX app** when you drop a
|
|
349
|
+
file onto a canvas; this package's `create_canvas` / `add_to_canvas` / `buildKlypix` write cards and
|
|
350
|
+
arrows, not assets — they read assets fine, they just don't create them.)
|
|
336
351
|
|
|
337
352
|
The parser is this package, Apache-2.0, so any tool or agent can read and write the format. Full
|
|
338
353
|
spec: [FORMAT.md](FORMAT.md).
|
|
@@ -341,14 +356,24 @@ Markdown export, JSON Canvas 1.0 export and direct opening of Obsidian `.canvas`
|
|
|
341
356
|
of the **KLYPIX desktop app**, not of this package — there is no export command among this
|
|
342
357
|
package's binaries.
|
|
343
358
|
|
|
344
|
-
**"Project" means any project.** Two showcase brains ship in the GitHub repo
|
|
345
|
-
[`examples/`](examples/), identical in engine, different in life:
|
|
359
|
+
**"Project" means any project.** Two showcase brains ship in the npm package *and* the GitHub repo
|
|
360
|
+
under [`examples/`](examples/), identical in engine, different in life:
|
|
346
361
|
[`showcase-brain.klypix`](examples/showcase-brain.klypix) is *Aurora*, a fictional weather app
|
|
347
362
|
mid-build (radar tiles, API caps, a correction with its receipt), and
|
|
348
363
|
[`showcase-wedding.klypix`](examples/showcase-wedding.klypix) is *Our Wedding* (venue, vendors,
|
|
349
364
|
guest list, the same correction machinery pointed at a caterer). Same 📌 Focus, same arrows, same
|
|
350
365
|
brief. If it has decisions worth keeping, it gets a brain.
|
|
351
366
|
|
|
367
|
+
They ship inside the tarball, so you can read one straight out of `node_modules`:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
npm i klypix-mcp
|
|
371
|
+
npx klypix-read node_modules/klypix-mcp/examples/showcase-brain.klypix
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Both are text-and-arrows only — 14 cards, 4 arrows, no `assets/` entry — so they demonstrate the
|
|
375
|
+
card / container / connection model, not the embedded-binaries half of the format.
|
|
376
|
+
|
|
352
377
|
## Use it as a library
|
|
353
378
|
|
|
354
379
|
```js
|
|
@@ -407,9 +432,11 @@ entirely.
|
|
|
407
432
|
- **Coordination state is local files.** The brain is a file in your repo; the presence lane is a
|
|
408
433
|
file under your home directory. Nothing is uploaded.
|
|
409
434
|
- **`install` writes to your home directory:** `~/.claude/project-brain` (engine + runtime),
|
|
410
|
-
`~/.claude/settings.json` (four hooks — written even if Claude Code is not installed)
|
|
411
|
-
|
|
412
|
-
|
|
435
|
+
`~/.claude/settings.json` (four hooks — written even if Claude Code is not installed),
|
|
436
|
+
`~/.codex/AGENTS.md` (guidance block), and with `--codex-hooks`, `~/.codex/hooks.json`. It also
|
|
437
|
+
writes `<cwd>/.codex/config.toml` **inside the project** you run it in, and removes any KLYPIX
|
|
438
|
+
entry from the global `~/.codex/config.toml`. **`link` writes 14 files inside the project** you
|
|
439
|
+
run it in; `link --check` audits them without writing.
|
|
413
440
|
- **Codex hooks require Codex's own trust approval** and are opt-in via `--codex-hooks`.
|
|
414
441
|
|
|
415
442
|
## Current limitations
|
|
@@ -498,5 +525,15 @@ and write.
|
|
|
498
525
|
|
|
499
526
|
---
|
|
500
527
|
|
|
501
|
-
|
|
502
|
-
|
|
528
|
+
## Licence
|
|
529
|
+
|
|
530
|
+
This package — the MCP server, the agent hooks and the `.klypix` format parser — is
|
|
531
|
+
**Apache-2.0** ([`LICENSE`](LICENSE), attribution in [`NOTICE`](NOTICE)). Versions up to and
|
|
532
|
+
including **1.28.0** were published under MIT and remain available under those terms; **1.29.0** was
|
|
533
|
+
the first Apache-2.0 release.
|
|
534
|
+
|
|
535
|
+
The KLYPIX desktop app and the klypix.com web app are **separate, proprietary products** — their
|
|
536
|
+
source is not public, and their terms do not restrict anything Apache-2.0 grants you here. This
|
|
537
|
+
package works with no app installed.
|
|
538
|
+
|
|
539
|
+
Apache-2.0 © [Dahshan Labs](https://klypix.com).
|
|
@@ -17,6 +17,10 @@ import { LoggingMessageNotificationSchema } from '@modelcontextprotocol/sdk/type
|
|
|
17
17
|
import { buildKlypixMap } from '../src/klypix-core.mjs';
|
|
18
18
|
|
|
19
19
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
// ARGV: flag lookup is position-independent, so this file is already correct for
|
|
21
|
+
// both `klypix-conformance --json` and `klypix-mcp conformance --json` (the
|
|
22
|
+
// dispatcher splices its verb out — see bin/klypix-worker.mjs runVerb). Kept as
|
|
23
|
+
// `includes` deliberately; test/cli-args.mjs asserts the two forms stay identical.
|
|
20
24
|
const jsonMode = process.argv.includes('--json');
|
|
21
25
|
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
26
|
const PKG_VERSION = (() => {
|
package/bin/klypix-doctor.mjs
CHANGED
|
@@ -13,12 +13,21 @@
|
|
|
13
13
|
//
|
|
14
14
|
// Synchronous-ish top-level by design: the dispatcher does `await import(this);
|
|
15
15
|
// process.exit(...)`, so all work completes during module evaluation.
|
|
16
|
+
import fs from 'fs';
|
|
16
17
|
import path from 'path';
|
|
17
18
|
import { execSync } from 'child_process';
|
|
18
19
|
import { inspect, render, inspectAll } from '../src/brain-doctor.mjs';
|
|
19
20
|
|
|
21
|
+
// ARGV: same dual shape as klypix-link — this file is both `klypix-doctor` and the
|
|
22
|
+
// target of `klypix-mcp doctor` (the dispatcher splices its verb out first, see
|
|
23
|
+
// bin/klypix-worker.mjs runVerb). slice(2) is the one correct shape; the strip is
|
|
24
|
+
// belt-and-braces for a dispatcher that did not splice, and refuses to eat a
|
|
25
|
+
// directory actually named ./doctor.
|
|
26
|
+
const isDir = (p) => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
|
|
27
|
+
|
|
20
28
|
try {
|
|
21
|
-
const
|
|
29
|
+
const raw = process.argv.slice(2);
|
|
30
|
+
const argv = (raw[0] === 'doctor' && !isDir(path.resolve(raw[0]))) ? raw.slice(1) : raw;
|
|
22
31
|
const has = (f) => argv.includes(f);
|
|
23
32
|
const val = (f) => { const i = argv.indexOf(f); return i >= 0 ? argv[i + 1] : undefined; };
|
|
24
33
|
const color = !has('--no-color') && process.stdout.isTTY !== false;
|
package/bin/klypix-install.mjs
CHANGED
|
@@ -34,6 +34,11 @@ const PKG_ROOT = path.resolve(__dirname, '..');
|
|
|
34
34
|
const SRC = path.join(PKG_ROOT, 'src');
|
|
35
35
|
const BIN = path.join(PKG_ROOT, 'bin');
|
|
36
36
|
const VERSION = (() => { try { return JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8')).version || ''; } catch { return ''; } })();
|
|
37
|
+
// ARGV: flag lookup is position-independent, so this file is correct for both
|
|
38
|
+
// `klypix-install --force` and `klypix-mcp install --force` (the dispatcher splices
|
|
39
|
+
// its verb out before importing — see bin/klypix-worker.mjs runVerb). It takes no
|
|
40
|
+
// positional argument; if one is ever added, read process.argv.slice(2) so both
|
|
41
|
+
// invocation shapes stay identical (locked by test/cli-args.mjs).
|
|
37
42
|
const FORCE = process.argv.includes('--force');
|
|
38
43
|
const CODEX_HOOKS = process.argv.includes('--codex-hooks');
|
|
39
44
|
const RUNTIME_ONLY = process.argv.includes('--runtime-only');
|
package/bin/klypix-link.mjs
CHANGED
|
@@ -16,11 +16,24 @@
|
|
|
16
16
|
//
|
|
17
17
|
// Synchronous top-level by design: the dispatcher does `await import(this); process.exit(0)`,
|
|
18
18
|
// so all work (and its logs) must complete during module evaluation, before exit.
|
|
19
|
+
import fs from 'fs';
|
|
19
20
|
import path from 'path';
|
|
20
21
|
import { compactAgentsBrief, linkProject } from '../src/agent-rules.mjs';
|
|
21
22
|
|
|
23
|
+
// ARGV: this file is BOTH `klypix-link` (its own published bin) and the target of
|
|
24
|
+
// `klypix-mcp link` (the dispatcher splices its verb out of process.argv before
|
|
25
|
+
// importing us — see bin/klypix-worker.mjs runVerb). slice(2) is therefore the ONE
|
|
26
|
+
// correct shape for both. It used to be slice(3), which is right only via the
|
|
27
|
+
// dispatcher: as a standalone bin `klypix-link --check` silently dropped --check
|
|
28
|
+
// and WROTE all 14 managed files with exit 0, and `klypix-link <dir>` wrote into
|
|
29
|
+
// the cwd instead of <dir>. The strip below is belt-and-braces for a dispatcher
|
|
30
|
+
// that did not splice, and it refuses to eat a project folder actually named
|
|
31
|
+
// ./link.
|
|
32
|
+
const isDir = (p) => { try { return fs.statSync(p).isDirectory(); } catch { return false; } };
|
|
33
|
+
|
|
22
34
|
try {
|
|
23
|
-
const
|
|
35
|
+
const raw = process.argv.slice(2);
|
|
36
|
+
const args = (raw[0] === 'link' && !isDir(path.resolve(raw[0]))) ? raw.slice(1) : raw;
|
|
24
37
|
const check = args.includes('--check');
|
|
25
38
|
const dirArg = args.find(a => !a.startsWith('-'));
|
|
26
39
|
const projectDir = path.resolve(dirArg || process.cwd());
|
|
@@ -56,6 +69,20 @@ try {
|
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
const changed = [...rules, ...mcp, compactBrief].filter(r => r.action && !['unchanged', 'skipped'].includes(r.action)).length;
|
|
72
|
+
// A SKIPPED target is not wired (invalid MCP JSON left untouched, or a Codex
|
|
73
|
+
// TOML write that failed). Write mode used to filter those out of the count,
|
|
74
|
+
// print the blanket "every agent … now reads + captures" line anyway, and exit
|
|
75
|
+
// 0 — while `--check` on the same project correctly reported drift and exited 1.
|
|
76
|
+
// Report the truth and fail, so the two modes agree.
|
|
77
|
+
const skipped = [...rules, ...mcp].filter(r => r.action === 'skipped');
|
|
78
|
+
const managed = rules.length + mcp.length;
|
|
79
|
+
if (skipped.length) {
|
|
80
|
+
console.log(`\n✗ ${skipped.length} target(s) could not be wired:`);
|
|
81
|
+
for (const r of skipped) console.log(` ⚠ ${r.tool.padEnd(26)} ${r.file}${r.why ? ' — ' + r.why : ''}`);
|
|
82
|
+
console.log(`\n wired ${managed - skipped.length} of ${managed} managed file(s) (${changed} written/updated this run).`);
|
|
83
|
+
console.log(' Fix the file(s) above (or move them aside) and re-run `npx klypix-mcp link`.');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
59
86
|
console.log(`\n✓ ${changed} file(s) written/updated — every agent opened in this project now reads + captures ./brain.klypix.`);
|
|
60
87
|
console.log(' Codex, Cline, Gemini/Antigravity, Cursor, and VS Code get project MCP configs; Windsurf uses its global MCP plus project rules.');
|
|
61
88
|
console.log(' Verify anytime with `npx klypix-mcp link --check` (or `npx klypix-mcp doctor`).');
|
package/bin/klypix-mcp.mjs
CHANGED
|
@@ -20,7 +20,38 @@ const PKG_VERSION = (() => {
|
|
|
20
20
|
})();
|
|
21
21
|
|
|
22
22
|
const DIRECT = new Set(['install', 'link', 'doctor', 'conformance', 'garden-code', 'init']);
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
const USAGE = [
|
|
25
|
+
`klypix-mcp ${PKG_VERSION} — shared project brain + MCP coordination server.`,
|
|
26
|
+
'',
|
|
27
|
+
'Verbs:',
|
|
28
|
+
' install [--force] [--codex-hooks] install/update this machine\'s brain engine + Claude Code hooks',
|
|
29
|
+
' link [dir] [--check] project this project\'s 14 managed agent config files (--check audits, writes nothing, exits 1 on drift)',
|
|
30
|
+
' doctor [--npm] [--all] [--json] read-only self-check; exits 1 on drift',
|
|
31
|
+
' conformance [--json] launch two real MCP clients against this build',
|
|
32
|
+
' init seed a starter ./brain.klypix + print an MCP config',
|
|
33
|
+
' garden-code [brain] print the human approval code for brain_garden',
|
|
34
|
+
'',
|
|
35
|
+
'With no verb (or any --flag, e.g. --vault <dir>) it runs as an MCP stdio server.',
|
|
36
|
+
'There is no uninstall command — removal is manual (see README).',
|
|
37
|
+
].join('\n');
|
|
38
|
+
|
|
39
|
+
const verb = process.argv[2];
|
|
40
|
+
if (verb === '--help' || verb === '-h' || verb === 'help') {
|
|
41
|
+
console.log(USAGE);
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
// A bare unknown WORD used to fall through to the stdio server, which then sat
|
|
45
|
+
// waiting on a stdin that no host was driving — so `npx klypix-mcp uninstall`
|
|
46
|
+
// (or any typo) printed nothing and exited 0, indistinguishable from success.
|
|
47
|
+
// Only non-dash tokens are rejected: a real host launch is `--vault <dir>`,
|
|
48
|
+
// and the local-bundle launch form puts '--vault' at argv[2] too.
|
|
49
|
+
if (verb && !verb.startsWith('-') && !DIRECT.has(verb)) {
|
|
50
|
+
console.error(`klypix-mcp: unknown command "${verb}".\n\n${USAGE}`);
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (DIRECT.has(verb)) {
|
|
24
55
|
await import('./klypix-worker.mjs');
|
|
25
56
|
} else {
|
|
26
57
|
const { runMcpSupervisor } = await import('../src/mcp-supervisor.mjs');
|