@creator-notes/cnotes 0.26.0 → 0.29.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/README.md +3 -4
- package/dist/cn.js +0 -2
- package/dist/cn.js.map +1 -1
- package/dist/commands/canvas.d.ts.map +1 -1
- package/dist/commands/canvas.js +125 -54
- package/dist/commands/canvas.js.map +1 -1
- package/dist/commands/relationships.d.ts.map +1 -1
- package/dist/commands/relationships.js +145 -14
- package/dist/commands/relationships.js.map +1 -1
- package/dist/lib/api-client.d.ts +1 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +2 -1
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/build-schema.d.ts +3 -1
- package/dist/lib/build-schema.d.ts.map +1 -1
- package/dist/lib/build-schema.js +3 -1
- package/dist/lib/build-schema.js.map +1 -1
- package/dist/lib/canvas-read.d.ts +59 -13
- package/dist/lib/canvas-read.d.ts.map +1 -1
- package/dist/lib/canvas-read.js +209 -17
- package/dist/lib/canvas-read.js.map +1 -1
- package/dist/lib/install-skill.d.ts +7 -2
- package/dist/lib/install-skill.d.ts.map +1 -1
- package/dist/lib/install-skill.js +44 -12
- package/dist/lib/install-skill.js.map +1 -1
- package/dist/mcp-server.js +56 -58
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/cnotes/SKILL.md +144 -73
- package/skills/cnotes/references/humanizer.md +267 -0
package/skills/cnotes/SKILL.md
CHANGED
|
@@ -9,6 +9,26 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
A command-line interface for CreatorNotes — create notes, build canvases, search knowledge, and manage workspaces from the terminal.
|
|
11
11
|
|
|
12
|
+
## Writing quality — the humanizer (read the reference before authoring prose)
|
|
13
|
+
|
|
14
|
+
Whenever a task **creates or edits audience-facing prose** that will be saved to
|
|
15
|
+
CreatorNotes — a note body, a pre-read, a strategy or workshop note, a public
|
|
16
|
+
draft, a Slack message, or a canvas orientation banner — **read
|
|
17
|
+
[`references/humanizer.md`](references/humanizer.md) first**, then draft, audit,
|
|
18
|
+
and save through it. It removes generic model habits (the kill-list, fake
|
|
19
|
+
contrasts, forced triads) while a preservation gate holds every number, date, ID,
|
|
20
|
+
link, and mention title fixed, so improving prose never corrupts meaning.
|
|
21
|
+
|
|
22
|
+
**Skip it** — do not humanize — for raw capture and structured data: transcripts,
|
|
23
|
+
voice memos, customer quotes, source evidence, schemas, tables, and metric-heavy
|
|
24
|
+
reference material. Store those unchanged and humanize only the derived artifact.
|
|
25
|
+
|
|
26
|
+
**The user's words override.** "Save this as raw capture", "audit only, change
|
|
27
|
+
nothing", "preserve timings and links", "use the neutral / company voice", and
|
|
28
|
+
"rewrite in my personal voice" each select a specific mode described in the
|
|
29
|
+
reference. Loading the reference costs no extra context on non-writing tasks —
|
|
30
|
+
pull it in only when the work is prose.
|
|
31
|
+
|
|
12
32
|
## Getting Started
|
|
13
33
|
|
|
14
34
|
### Install
|
|
@@ -199,8 +219,12 @@ cnotes canvas get <canvasId>
|
|
|
199
219
|
# reason about occupancy in one read instead of unioning the per-kind arrays.
|
|
200
220
|
# (You rarely need this for placement — `cnotes canvas place` already avoids overlaps.)
|
|
201
221
|
|
|
202
|
-
# Read
|
|
203
|
-
# order (top-to-bottom, then
|
|
222
|
+
# Read a canvas as STRUCTURE, not just content — one round-trip to think across it.
|
|
223
|
+
# Returns the notes as concatenated markdown in display order (top-to-bottom, then
|
|
224
|
+
# left-to-right) GROUPED UNDER THEIR SECTION FRAME, plus the edges (the argument)
|
|
225
|
+
# and the portals (the navigation). --json gives { canvas, sections, elements,
|
|
226
|
+
# edges, portals }; every element carries the `section` it sits inside.
|
|
227
|
+
# You should not need a second `canvas get` just to see the frames or the wiring.
|
|
204
228
|
cnotes canvas read <canvasId>
|
|
205
229
|
|
|
206
230
|
# AI digest of canvas: narrative summary + per-note summary list for every note on the canvas
|
|
@@ -218,23 +242,25 @@ cnotes canvas archive <canvasId>
|
|
|
218
242
|
cnotes canvas unarchive <canvasId>
|
|
219
243
|
|
|
220
244
|
# Add nodes to canvas — see "Canvas Elements — When to Use What" before choosing.
|
|
221
|
-
# Default to NOTES for content; text/
|
|
245
|
+
# Default to NOTES for content; text/list/section are presentation furniture.
|
|
222
246
|
cnotes canvas add-node <canvasId> --note <noteId> [--x <n>] [--y <n>] # (despite the name, add-node adds NOTE nodes only)
|
|
223
|
-
# text =
|
|
224
|
-
|
|
225
|
-
#
|
|
247
|
+
# text = free-form rich text placed on the canvas (wire type: richtext). Markdown —
|
|
248
|
+
# headings, emphasis, images. Optional background tint (--color); DEFAULT IS NO BACKGROUND.
|
|
249
|
+
# A HEADING IS JUST `--content '# Q1 Goals'` with no tint — there is no separate heading
|
|
250
|
+
# element. CARD-sized framing (at most ~3 sentences): orientation banners, emphasis
|
|
226
251
|
# quoting a note, image tiles. Unversioned, unsearchable, no display ID (nothing can cite
|
|
227
252
|
# it) — never the sole home of a claim. NO [NOTE-X](relationship:...) / [[...]] mention
|
|
228
253
|
# syntax (corrupts the card; use plain display IDs). Knowledge worth keeping = a typed note.
|
|
229
|
-
cnotes canvas add-
|
|
230
|
-
cnotes canvas add-
|
|
254
|
+
cnotes canvas add-text <canvasId> --content "<markdown>" [--size small|medium|large] [--color "<hex>"] [--x <n>] [--y <n>] # inline (short only)
|
|
255
|
+
cnotes canvas add-text <canvasId> --content-file <path.md> [--size small|medium|large] [--color "<hex>"] [--x <n>] [--y <n>]
|
|
256
|
+
cnotes canvas add-richtext ... # DEPRECATED alias of add-text (identical flags). Use add-text.
|
|
231
257
|
# A list item is a COLLECTION OF NOTES, not a text block. --description is a one-line FRAME;
|
|
232
258
|
# the content is the member notes you pass to --notes. If you have N distinct things
|
|
233
259
|
# (questions, risks, options), create N notes first — pick the right type, e.g. a Question
|
|
234
260
|
# note per open question — then group them: --notes ID1,ID2,ID3. Pasting a numbered/bulleted
|
|
235
261
|
# list into --description with no --notes is the wrong shape (you get a "0 items" container
|
|
236
|
-
# masquerading as prose). For a short
|
|
237
|
-
# member notes) use add-
|
|
262
|
+
# masquerading as prose). For a short card-sized framing blurb (at most ~3 sentences, no
|
|
263
|
+
# member notes) use add-text; a multi-item markdown block is N typed notes, never a text element
|
|
238
264
|
# (see "Canvas Elements — When to Use What").
|
|
239
265
|
cnotes canvas add-list <canvasId> --description "<markdown>" [--notes <id1,id2,...>] [--view list|grid] [--x <n>] [--y <n>]
|
|
240
266
|
cnotes canvas add-link <canvasId> --target <otherCanvasId> [--x <n>] [--y <n>]
|
|
@@ -248,6 +274,13 @@ cnotes canvas add-link <canvasId> --target <otherCanvasId> [--x <n>] [--y <n>]
|
|
|
248
274
|
cnotes canvas add-section <canvasId> --name "<title>" --notes <id1,id2,...> [--columns <n>] [--color "<hex>"]
|
|
249
275
|
cnotes canvas add-section <canvasId> --name "<title>" [--x <n>] [--y <n>] [--width <n>] [--height <n>] # empty frame
|
|
250
276
|
|
|
277
|
+
# Resize / rename / move / recolor an EXISTING frame, IN PLACE. Takes the SECTION-<n>
|
|
278
|
+
# display id. ALWAYS use this to grow a frame around new content — deleting and
|
|
279
|
+
# re-adding a section mints a NEW id (SECTION-51 becomes SECTION-57) and silently
|
|
280
|
+
# breaks every [[SECTION-51]] mention pointing at the old one.
|
|
281
|
+
cnotes canvas update-section <canvasId> --section SECTION-51 --height 1800
|
|
282
|
+
cnotes canvas update-section <canvasId> --section SECTION-51 --name "Open Questions" [--x <n>] [--y <n>] [--width <n>] [--color "<hex>"] [--clear-color]
|
|
283
|
+
|
|
251
284
|
# Place items using a declarative layout (NO x/y math — server packs them)
|
|
252
285
|
# This is the PREFERRED way to add multiple items. See "Canvas Layout" below.
|
|
253
286
|
cnotes canvas place <canvasId> --spec ./layout.json
|
|
@@ -258,22 +291,27 @@ cnotes canvas place <canvasId> --spec-inline '<short JSON>' # for tiny specs on
|
|
|
258
291
|
cnotes canvas bulk-add <canvasId> --notes '[{"noteId":"NOTE-1","x":100,"y":100},{"noteId":"NOTE-2","x":400,"y":100}]'
|
|
259
292
|
|
|
260
293
|
# Move / remove nodes (--type for non-note nodes, e.g. --type section)
|
|
261
|
-
|
|
294
|
+
# NOTE: `text` in these type lists is the LEGACY heading kind (old canvases only). It can no
|
|
295
|
+
# longer be CREATED, but existing ones stay movable and deletable — that is why it is still
|
|
296
|
+
# accepted here. Today's Text element is `richtext`.
|
|
297
|
+
cnotes canvas move-node <canvasId> --node <nodeId> --x <n> --y <n> [--type note|richtext|list|canvas|section|text]
|
|
262
298
|
cnotes canvas remove-node <canvasId> --node <nodeId>
|
|
263
299
|
|
|
264
300
|
# Bulk remove multiple nodes at once
|
|
265
301
|
cnotes canvas bulk-remove <canvasId> --nodes '["nodeId1","nodeId2"]'
|
|
266
302
|
|
|
267
303
|
# Bulk move multiple nodes at once (more efficient than repeated move-node)
|
|
268
|
-
cnotes canvas bulk-move <canvasId> --moves '[{"nodeId":"abc","nodeType":"note","x":200,"y":300},{"nodeId":"def","nodeType":"
|
|
269
|
-
# nodeType: note |
|
|
304
|
+
cnotes canvas bulk-move <canvasId> --moves '[{"nodeId":"abc","nodeType":"note","x":200,"y":300},{"nodeId":"def","nodeType":"richtext","x":500,"y":300}]'
|
|
305
|
+
# nodeType: note | richtext | list | canvas | section | text (defaults to "note"; `text` = legacy)
|
|
270
306
|
|
|
271
|
-
# Connect nodes — edges can join ANY node type (note,
|
|
307
|
+
# Connect nodes — edges can join ANY node type (note, richtext, list, canvas-link, legacy text),
|
|
272
308
|
# as long as both endpoints live on the same canvas.
|
|
273
|
-
# Labels are short verbs ("fixes", "grounds", "then").
|
|
274
|
-
#
|
|
275
|
-
#
|
|
276
|
-
#
|
|
309
|
+
# Labels are short verbs ("fixes", "grounds", "then"). `canvas read` DOES return edges now,
|
|
310
|
+
# but an edge is still CANVAS-LOCAL: it is not part of the note graph, so it never shows up in
|
|
311
|
+
# `rel list`, is not searchable, and means nothing away from this canvas. So when an edge
|
|
312
|
+
# encodes a relationship between NOTES, ALSO mirror it as a relationship mention in the note
|
|
313
|
+
# bodies — that is the copy that travels. Edges to text/list/canvas-link nodes are diagram
|
|
314
|
+
# decoration (labels, portals) and need no mention.
|
|
277
315
|
cnotes canvas add-edge <canvasId> --source <nodeId> --target <nodeId> [--label "<text>"]
|
|
278
316
|
|
|
279
317
|
# Templates — create structured canvases from predefined layouts
|
|
@@ -337,19 +375,47 @@ cnotes timeline -q "auth decision" --since 30d # search within a window
|
|
|
337
375
|
# incoming → notes that link TO this note (backlinks — "what cites me")
|
|
338
376
|
# outgoing → notes this note links to ("what I cite")
|
|
339
377
|
# Each row carries the related note inline so you rarely need a follow-up fetch:
|
|
340
|
-
# { direction, isStale, madeAgainstVersion,
|
|
378
|
+
# { id, direction, origin, live, isStale, madeAgainstVersion, relatedNoteCurrentVersion,
|
|
341
379
|
# relatedNote: { displayId, title, type, workspaceName },
|
|
342
380
|
# relationshipType: { name, label } }
|
|
343
|
-
# `isStale: true` = the related note has changed since the link was made.
|
|
344
381
|
cnotes rel list --note <displayId>
|
|
345
382
|
cnotes rel list --note INSIGHT-1 --json | jq '.incoming[].relatedNote.displayId'
|
|
346
383
|
|
|
384
|
+
# By default you see links that hold RIGHT NOW. --history adds the superseded ones.
|
|
385
|
+
cnotes rel list --note <displayId> --history
|
|
386
|
+
cnotes rel list [--type implements] [--limit <n>] # workspace-wide
|
|
387
|
+
|
|
388
|
+
# Delete a relationship by id (ids come from `rel list`).
|
|
389
|
+
cnotes rel rm <relationshipId>
|
|
390
|
+
|
|
347
391
|
# List the relationship types defined in the workspace. Check this BEFORE
|
|
348
392
|
# using a relationship:<type> label so you reuse an existing type instead of
|
|
349
393
|
# creating a near-duplicate.
|
|
350
394
|
cnotes rel types
|
|
351
395
|
```
|
|
352
396
|
|
|
397
|
+
**Read the two provenance fields before you trust a link.** They answer different
|
|
398
|
+
questions, and conflating them is the classic mistake:
|
|
399
|
+
|
|
400
|
+
| Field | Meaning |
|
|
401
|
+
|---|---|
|
|
402
|
+
| `origin: "content"` | Extracted from a `[[type::TARGET]]` chip in the note body. Its lifecycle IS the body's — delete the chip and the link stops being live. |
|
|
403
|
+
| `origin: "declared"` | Not attributable to a chip in the current body: a structural link (from a duplicate/move), one created explicitly, **or an old link predating chip tracking** (see caveat below). Editing the body never retires it, so a declared link that no longer holds can ONLY be removed with `rel rm`. |
|
|
404
|
+
| `live: false` | A content chip that is **gone from the note's current version**. This is the obsolete one. Hidden unless you pass `--history`. |
|
|
405
|
+
| `isStale: true` | **Citation drift, NOT obsolescence.** The link holds; the note it points at has simply advanced since it was cited (`madeAgainstVersion` < `relatedNoteCurrentVersion`). Often the most interesting signal in the graph — a claim resting on a source that moved. Worth re-reading, never worth auto-deleting. |
|
|
406
|
+
|
|
407
|
+
Relationships are append-only: every save re-extracts the body's chips into fresh
|
|
408
|
+
rows and the old ones become history rather than being deleted. The default read
|
|
409
|
+
already hides superseded rows, so `rel list` reflects the CURRENT content — you do
|
|
410
|
+
not need to filter it yourself.
|
|
411
|
+
|
|
412
|
+
**Caveat on `declared`:** chip tracking was added after the fact, so links created
|
|
413
|
+
before it report `declared` even when they DID come from a body chip. Read
|
|
414
|
+
`declared` as "not tracked to a chip", not as proof the link was never in the
|
|
415
|
+
prose — the note body is the authority. This only ever makes a link too durable
|
|
416
|
+
(it stays live), never too fragile, so `declared` is always safe to trust as
|
|
417
|
+
"still asserted"; just don't infer from it that the prose never said so.
|
|
418
|
+
|
|
353
419
|
**See how a found note connects (the editor header, for agents).** When you
|
|
354
420
|
land on a note via `search semantic` / `notes list`, the two affordances the
|
|
355
421
|
editor shows at the top of the panel are both one call away — use them to
|
|
@@ -393,18 +459,6 @@ cnotes types update <name> [--display-name <name>] [--description <text>] [--col
|
|
|
393
459
|
cnotes notes retype <displayId> --type <NewType>
|
|
394
460
|
```
|
|
395
461
|
|
|
396
|
-
### Memory (`cnotes memory` / `cnotes mem`)
|
|
397
|
-
```bash
|
|
398
|
-
# Search facts and entities by relevance
|
|
399
|
-
cnotes memory query "<query>" [--limit <n>] [--source <conversation|notes>]
|
|
400
|
-
|
|
401
|
-
# List all workspace facts (extracted from notes and conversations)
|
|
402
|
-
cnotes memory facts [--source <conversation|notes>]
|
|
403
|
-
|
|
404
|
-
# List all entities (people, topics, orgs) identified by Zep
|
|
405
|
-
cnotes memory entities [--source <conversation|notes>]
|
|
406
|
-
```
|
|
407
|
-
|
|
408
462
|
### Files (`cnotes files` / `cnotes f`)
|
|
409
463
|
```bash
|
|
410
464
|
# Upload an image to the workspace (max 5MB, supports .jpg .jpeg .png .gif .webp)
|
|
@@ -588,59 +642,71 @@ If `status` is `"none"` or `"stale"`, the `notes` array is still populated and a
|
|
|
588
642
|
|
|
589
643
|
### Canvas Elements — When to Use What
|
|
590
644
|
|
|
645
|
+
The vocabulary is five elements:
|
|
646
|
+
|
|
647
|
+
| Element | What it is | CLI |
|
|
648
|
+
|---|---|---|
|
|
649
|
+
| **note** | A reusable knowledge object: typed, versioned, searchable, with a display ID other notes can mention | `add-node`, `place` `type:"note"` |
|
|
650
|
+
| **text** | Free-form rich text placed directly on the canvas — markdown (headings, emphasis, images), optional background tint (none by default). Wire type is `richtext`. | `add-text`, `place` `type:"richtext"` |
|
|
651
|
+
| **list** | A container whose content is its MEMBER NOTES; the description is a one-line frame | `add-list` |
|
|
652
|
+
| **section** | A named region frame (Miro-style). Mints `SECTION-<n>`, mention-addressable | `add-section` |
|
|
653
|
+
| **canvas** | A portal to another canvas | `add-link`, `place` `type:"canvas"` |
|
|
654
|
+
|
|
655
|
+
**There is no separate heading element.** A heading is a **text** element whose content is a markdown heading and which has no background tint: `add-text <canvasId> --content '# Q1 Goals'`. (An older `text` node kind — plain string + font size — is retired: existing ones still render and can be moved or deleted, but nothing creates them any more.)
|
|
656
|
+
|
|
591
657
|
A canvas holds two tiers of material, and the difference is load-bearing:
|
|
592
658
|
|
|
593
|
-
- **Notes**
|
|
594
|
-
- **Everything else** — text
|
|
659
|
+
- **Notes** are the knowledge tier: typed, versioned, searchable, citable by display ID — the only tier whose content other notes can mention, search can find, and history records. Anything a future reader must find, cite, verify, or build on lives here.
|
|
660
|
+
- **Everything else** — text elements, list containers, section frames, edges, portals — is the presentation tier: orientation, grouping, emphasis, navigation. Text elements, lists, edges and portals have no display ID (nothing can cite them), no version history (they mutate silently), and no search presence. (Sections are the one exception: a frame mints a `SECTION-<n>` you can mention — it addresses a *place*, not knowledge.) They shape how the knowledge lands; they never hold the knowledge.
|
|
595
661
|
|
|
596
|
-
**The default is a typed note.** Before reaching for any other element, apply one test: *will any future reader — a human, or the next agent session pointed at this canvas for context — need to find, cite, or trust this?* If yes, it MUST be a note. "It renders nicely on the canvas" is never a reason to put knowledge in
|
|
662
|
+
**The default is a typed note.** Before reaching for any other element, apply one test: *will any future reader — a human, or the next agent session pointed at this canvas for context — need to find, cite, or trust this?* If yes, it MUST be a note. "It renders nicely on the canvas" is never a reason to put knowledge in a text element: that trades permanent legibility (versions, search, citable IDs) for screen presence now. The decision-table rows below are the sanctioned exceptions — when a row matches your content exactly, the row wins.
|
|
597
663
|
|
|
598
664
|
#### Decision table
|
|
599
665
|
|
|
600
666
|
| You have | Use | NOT |
|
|
601
667
|
|---|---|---|
|
|
602
|
-
| A claim, fact, decision, risk, question, verdict, score, spec, or example worth keeping | A typed **note** (right supertag, `# h1` title) |
|
|
603
|
-
| N distinct things (questions, risks, options, findings) | **N notes** grouped in a **list**; `--description` = one-line frame | bullets in one
|
|
604
|
-
| A named REGION of the canvas ("Shaping", "Open Questions") | a **section** frame (`add-section`) — draggable region, mention-addressable as `[[SECTION-<n>]]` from note bodies | a text
|
|
605
|
-
| A one-line
|
|
606
|
-
| Prose telling the reader how to traverse THIS canvas | **
|
|
607
|
-
| Emphasis — restating a key claim for screen presence | **
|
|
608
|
-
| An image tile (logo, mockup, screenshot) | **
|
|
609
|
-
| A grid of images for review (mood board) | N
|
|
668
|
+
| A claim, fact, decision, risk, question, verdict, score, spec, or example worth keeping | A typed **note** (right supertag, `# h1` title) | a text element |
|
|
669
|
+
| N distinct things (questions, risks, options, findings) | **N notes** grouped in a **list**; `--description` = one-line frame | bullets in one text element or in a list `--description` |
|
|
670
|
+
| A named REGION of the canvas ("Shaping", "Open Questions") | a **section** frame (`add-section`) — draggable region, mention-addressable as `[[SECTION-<n>]]` from note bodies | a floating text heading over an implied area |
|
|
671
|
+
| A heading / one-line label where a frame is too heavy (a column header inside a frame, a lane key) | a **text** element holding a markdown heading — `add-text --content '# Q1 Goals'`, no `--color` (leave it untinted), title case | a tinted card, or a section frame for something that isn't a region |
|
|
672
|
+
| Prose telling the reader how to traverse THIS canvas | a **text** orientation banner (one per canvas or band; up to ~5 sentences) | a Guide note nobody needs off-canvas |
|
|
673
|
+
| Emphasis — restating a key claim for screen presence | a **text** element that QUOTES a note (the note stays the home) | the text element as the only copy |
|
|
674
|
+
| An image tile (logo, mockup, screenshot) | a **text** element holding the image, beside the owning note; embed the same image in the note body via `cnotes files upload <path> --markdown` so the note stays self-contained | the image as the knowledge itself |
|
|
675
|
+
| A grid of images for review (mood board) | N text-element image tiles in a `grid` place spec + ONE note holding the decision criteria / rationale for the set | a caption note per tile, or rationale in the tiles |
|
|
610
676
|
| "A connects to B" between two notes | a labeled **edge** (short verb phrase), notes only | prose explaining the connection |
|
|
611
|
-
| A pointer to another canvas | a **portal** — `add-link` on the CLI, `{"type": "canvas", "linkedCanvasId": ...}` in place specs, returned as `canvasLinkNodes` by `canvas get` | a
|
|
612
|
-
| Backstage narration (scripts, speaker notes) | NOTES on a PRIVATE sibling canvas (cue-card
|
|
677
|
+
| A pointer to another canvas | a **portal** — `add-link` on the CLI, `{"type": "canvas", "linkedCanvasId": ...}` in place specs, returned as `canvasLinkNodes` by `canvas get` | a text element saying "see other canvas" |
|
|
678
|
+
| Backstage narration (scripts, speaker notes) | NOTES on a PRIVATE sibling canvas (cue-card text elements are fine there); portal FROM the private canvas TO the shared one, never the reverse | narration on the shared canvas |
|
|
613
679
|
|
|
614
680
|
#### Hard rules (each one has burned a real canvas)
|
|
615
681
|
|
|
616
|
-
1. **A canvas element must never be the sole home of a claim.** Verdict tallies, dates, scan provenance, and even a moat-defining sentence have each been authored as
|
|
617
|
-
2. **No relationship-mention syntax inside
|
|
618
|
-
3. **
|
|
619
|
-
4. **Detail's HOME is the note body;
|
|
620
|
-
5. **Edges can connect any node type** (note, text, list,
|
|
621
|
-
6. **Color is semantic or absent.** Default neutral
|
|
682
|
+
1. **A canvas element must never be the sole home of a claim.** Verdict tallies, dates, scan provenance, and even a moat-defining sentence have each been authored as text elements — then cited downstream while being unversioned, unsearchable, and uncitable. If a text element says something true and useful, a note says it first; the text element may quote the note. (Canvas-specific wayfinding — how to read THIS canvas — is the one exemption; it has no off-canvas value.)
|
|
683
|
+
2. **No relationship-mention syntax inside text elements or list descriptions.** `[NOTE-12](relationship:references)` and `[[NOTE-12]]` corrupt a canvas text element — the card renders blank. Reference notes in canvas prose as plain display IDs ("see RISK-64"). Mentions belong in NOTE bodies, where they create real graph edges.
|
|
684
|
+
3. **A text element is capped at card size**: at most 3 sentences, one heading at most, never a list of distinct knowledge items (questions, risks, findings — those are N notes; a 3-line color legend or lane key is fine). The one exception is the orientation banner (table row 5, up to ~5 sentences). A heading-only text element (`--content '# Q1 Goals'`) is of course fine — that is the smallest legitimate use. The cap is physical as much as doctrinal: a text element renders its full content inline, so long blocks explode the canvas — one canvas hit 43,000px tall from eight text reference blocks and had to be rebuilt as notes.
|
|
685
|
+
4. **Detail's HOME is the note body; text detail cards are projections.** Write the full spec/content INTO each note's body first. A parallel text card under a note is allowed only as a projection — it condenses or quotes a note that already contains everything, and says so ("from STAGE-3"). A detail card whose content exists nowhere else is the anti-pattern: one teaching canvas carried its entire method in text cards over one-line stub notes, and an agent reading it back got seven stubs and no method.
|
|
686
|
+
5. **Edges can connect any node type** (note, text, list, canvas-link — plus legacy heading nodes), as long as both endpoints are on the same canvas; labels are short verb phrases ("fixes", "grounds", "depends on"). `canvas read` returns edges, so they are no longer invisible on read-back — but an edge is still CANVAS-LOCAL: it is not part of the note graph, never appears in `rel list`, is not searchable, and means nothing once you leave this canvas. So any relationship **between notes** that must survive off the canvas has to ALSO exist as a mention inside the note bodies. The mention is the copy that travels; the edge is the copy you can see. Edges to text/list/canvas-link nodes are diagram decoration (pointing a label at a cluster, wiring a portal) and carry no note-to-note relationship to mirror.
|
|
687
|
+
6. **Color is semantic or absent.** Default neutral — a text element has NO background unless you pass `--color`, and that is usually the right look (an untinted heading is just type on the canvas). Use a saturated hex only to encode a role applied consistently on this canvas (e.g. risks red, evidence blue), 2–4 roles max. If you cannot name the role, omit the color. Never pastels — they composite to mud on the canvas background.
|
|
622
688
|
|
|
623
689
|
#### Two completion tests
|
|
624
690
|
|
|
625
691
|
Run both before ending your operation:
|
|
626
692
|
|
|
627
|
-
**The read-back test (for the next agent):** run `cnotes canvas read` and check that every claim is present as a NOTE section with a display ID, and the reasoning can be followed through note bodies and their mentions. Content that appears only as anonymous
|
|
693
|
+
**The read-back test (for the next agent):** run `cnotes canvas read` and check that every claim is present as a NOTE section with a display ID, and the reasoning can be followed through note bodies and their mentions. Content that appears only as anonymous text-element prose fails the test even when visible — nothing can cite, search, or version it. List members read back as ID+title links without bodies; that is fine — the IDs are citable and batch-fetchable via `cnotes notes get`. As a heuristic for working canvases, at least ~4 notes per text element (zero text elements is fine); fewer means text elements are carrying content. Explainer canvases using the projection pattern (rule 4) are exempt from the ratio, not from the test.
|
|
628
694
|
|
|
629
695
|
**The glance test (for the human):** zoomed to fit, a person should grasp what this canvas argues and where to start reading in about 10 seconds. A canvas that passes read-back but renders as an undifferentiated card grid is also not done — that is what section labels, orientation banners, projections, and layout are for.
|
|
630
696
|
|
|
631
697
|
#### Worked example
|
|
632
698
|
|
|
633
|
-
Right shape (a real competitor scan): 31 typed notes (COMPETITOR profile, FACT evidence row, INSIGHT verdicts per dimension, RISK/IDEA/STRATEGY response bands, QUESTION follow-ups in a list), three short
|
|
699
|
+
Right shape (a real competitor scan): 31 typed notes (COMPETITOR profile, FACT evidence row, INSIGHT verdicts per dimension, RISK/IDEA/STRATEGY response bands, QUESTION follow-ups in a list), three short text banners framing the bands ("Threats, Steals, Response — what hurts us, what's worth taking, the plan"), and labeled edges mirrored as mentions (`FACT-44 --grounds--> INSIGHT-361`).
|
|
634
700
|
|
|
635
|
-
Wrong shape (same material): one 5,000-char
|
|
701
|
+
Wrong shape (same material): one 5,000-char text element holding a "deep scan writeup", a text scoreboard holding the verdict tally, and a few notes for leftovers. Search finds nothing, versions record nothing, nothing can cite or link any of it — and the headline verdict goes stale the first time someone edits the card.
|
|
636
702
|
|
|
637
703
|
#### Current platform limitations (re-check before relying on these)
|
|
638
704
|
|
|
639
705
|
The durable rules above stand on versioning, search, and citability. Four rules also lean on today's platform behavior — if a future release changes these, the corresponding rule can relax:
|
|
640
706
|
|
|
641
|
-
- Mention syntax corrupts canvas
|
|
642
|
-
- `canvas read`
|
|
643
|
-
- Agents have no update verb for text
|
|
707
|
+
- Mention syntax corrupts canvas text elements / list descriptions (motivates rule 2; an @-mention affordance is planned).
|
|
708
|
+
- `canvas read` returns sections, edges and portals, but list containers still read back as their one-line frame only. An edge read back is still canvas-local and ungraphed (motivates rule 5's mirror-as-mentions requirement).
|
|
709
|
+
- Agents have no update verb for **text or list** elements — to change one, remove and re-add it (one more reason content that evolves belongs in notes, which have versions). Section frames are the exception: `canvas update-section` edits a frame in place.
|
|
644
710
|
|
|
645
711
|
### Wrapping batch work in an operation (ENFORCED for `notes create`)
|
|
646
712
|
|
|
@@ -652,7 +718,7 @@ When an AI agent (including you, Claude) is doing a **batch of work** — creati
|
|
|
652
718
|
|
|
653
719
|
| Action | Wrap? |
|
|
654
720
|
|---|---|
|
|
655
|
-
| Reading: `digest`, `get`, `list`, `activity`, `timeline`, `search`, `
|
|
721
|
+
| Reading: `digest`, `get`, `list`, `activity`, `timeline`, `search`, `rel list` | **No** — read-only |
|
|
656
722
|
| **Any `cnotes notes create`** (even a single note) | **Yes — enforced.** Creates run through the batch endpoint, which now rejects writes with no active run |
|
|
657
723
|
| A single `notes update` / tag / pin / archive change | **No** — single metadata edits don't go through the guarded create path |
|
|
658
724
|
| Two or more writes that belong to one intent (multi-note batch, canvas build-out, multi-step refactor) | **Yes** |
|
|
@@ -668,7 +734,7 @@ cnotes operations begin --prompt "Reorganize roadmap into quarters" --json
|
|
|
668
734
|
|
|
669
735
|
# 2. Do the work. Every cnotes write here attaches to the run automatically.
|
|
670
736
|
cnotes notes create --notes '[ ... ]'
|
|
671
|
-
cnotes canvas add-text <canvasId> --
|
|
737
|
+
cnotes canvas add-text <canvasId> --content '# Q1' --x 0 --y 0
|
|
672
738
|
cnotes canvas place <canvasId> --spec /tmp/q1.json
|
|
673
739
|
cnotes canvas add-edge <canvasId> --source <a> --target <b> --label "depends on"
|
|
674
740
|
|
|
@@ -743,14 +809,16 @@ The schema is strict: an unknown or typo'd key anywhere in the doc (e.g. `gapp`,
|
|
|
743
809
|
|
|
744
810
|
```json
|
|
745
811
|
{ "kind": "item", "type": "note", "noteId": "NOTE-3", "key": "a" }
|
|
746
|
-
{ "kind": "item", "type": "
|
|
747
|
-
{ "kind": "item", "type": "richtext", "content": "
|
|
812
|
+
{ "kind": "item", "type": "richtext", "content": "# Q1 Goals" }
|
|
813
|
+
{ "kind": "item", "type": "richtext", "content": "Read this band left to right.", "size": "medium", "colorHex": "#c2410c" }
|
|
748
814
|
{ "kind": "item", "type": "list", "description": "Risks tracked weekly — owner: @ana", "noteIds": ["A","B","C"], "viewMode": "list" }
|
|
749
815
|
{ "kind": "item", "type": "canvas", "linkedCanvasId": "abc..." }
|
|
750
816
|
{ "kind": "item", "type": "section", "name": "Open Questions", "width": 800, "height": 500 }
|
|
751
817
|
```
|
|
752
818
|
|
|
753
|
-
|
|
819
|
+
`richtext` IS the **text** element (the CLI's `add-text`) — the only free-form text leaf. A heading is a `richtext` leaf whose content is `"# Title"` with no `colorHex`. There is no `"type": "text"` leaf: the old heading node kind is retired and the layout engine rejects it.
|
|
820
|
+
|
|
821
|
+
Leaf enums (exact values): richtext `size` = `small` | `medium` | `large`, `colorHex` = a saturated hex (omit for no background); list `viewMode` = `list` | `grid`, list `size` = `sm` | `md` | `lg` | `xl`.
|
|
754
822
|
|
|
755
823
|
#### Section container (frame WITH content — preferred)
|
|
756
824
|
|
|
@@ -774,12 +842,15 @@ surface it and mention it as `[[SECTION-<n>]]` from note bodies. For the
|
|
|
774
842
|
simple case, `add-section --notes id1,id2` is the same thing as a one-liner.
|
|
775
843
|
To frame content that ALREADY sits on the canvas, add an empty `add-section`
|
|
776
844
|
with bounds enclosing it (frames aren't collision obstacles, overlap is fine).
|
|
845
|
+
To grow or rename a frame that already exists, use `canvas update-section` —
|
|
846
|
+
never delete + re-add, which mints a new SECTION-<n> and breaks every
|
|
847
|
+
`[[SECTION-<n>]]` mention pointing at the old one.
|
|
777
848
|
|
|
778
849
|
The optional `key` lets edges and other items reference this leaf later (`@<key>`).
|
|
779
850
|
|
|
780
|
-
`richtext.content` accepts markdown directly — the server converts to TipTap before measuring. Plain markdown only: NO `[NOTE-X](relationship:...)` / `[[...]]` mention syntax (it corrupts the card — reference notes as plain display IDs), and keep it
|
|
851
|
+
`richtext.content` (the **text** element) accepts markdown directly — the server converts to TipTap before measuring. Plain markdown only: NO `[NOTE-X](relationship:...)` / `[[...]]` mention syntax (it corrupts the card — reference notes as plain display IDs), and keep it card-sized per "Canvas Elements — When to Use What".
|
|
781
852
|
|
|
782
|
-
`list.description` is a one-line **frame**, never the content itself — the content is `noteIds` (the member notes). For N distinct things, create N notes (right type per item, e.g. `Question` for open questions) and list their ids. A `list` with a multi-item description and an empty `noteIds` is the wrong shape — use a
|
|
853
|
+
`list.description` is a one-line **frame**, never the content itself — the content is `noteIds` (the member notes). For N distinct things, create N notes (right type per item, e.g. `Question` for open questions) and list their ids. A `list` with a multi-item description and an empty `noteIds` is the wrong shape — use a card-sized **text** element (`richtext`, at most ~3 sentences) only for a short framing block with no members; anything longer, or any set of distinct items, is N notes.
|
|
783
854
|
|
|
784
855
|
#### Edges
|
|
785
856
|
|
|
@@ -896,8 +967,8 @@ Edges live at the top level alongside `root`. Each endpoint is resolved in this
|
|
|
896
967
|
|
|
897
968
|
- **Anchors take zero space in their parent** — when nested inside a `stack` or `grid`, an anchor reserves no slot, so siblings collapse together. Anchors are best used at the root or as their own top-level branch.
|
|
898
969
|
- **Anchor `to:` must be a node already on the canvas** — local `@key` refs only work for edges, not for anchor targets in this version.
|
|
899
|
-
- **Never use ALL CAPS**
|
|
900
|
-
- **
|
|
970
|
+
- **Never use ALL CAPS** in a text element — headings included. Use title case (e.g., "Key Tensions" not "KEY TENSIONS").
|
|
971
|
+
- **Text-element content** is authored as markdown — the server converts it server-side. A heading is `"# Key Tensions"` with no `colorHex`. Keep it card-sized (at most ~3 sentences; see Canvas Elements hard rule 3).
|
|
901
972
|
|
|
902
973
|
#### When the layout engine isn't enough
|
|
903
974
|
|
|
@@ -921,7 +992,7 @@ When creating multiple related notes, interlink at two levels:
|
|
|
921
992
|
- After adding nodes, connect them with `cnotes canvas add-edge`, using short verb labels ("triggers", "depends on").
|
|
922
993
|
- Edges can join any node type, as long as both endpoints are on the same canvas.
|
|
923
994
|
|
|
924
|
-
Both levels are needed — edges are
|
|
995
|
+
Both levels are needed — edges are canvas-local, mentions are content-level. `cnotes canvas read` does return edges, but only the mention creates a real graph edge that `rel list` and search can see, so any relationship that matters must also exist as a mention in the note bodies (see Canvas Elements hard rules 2 and 5).
|
|
925
996
|
|
|
926
997
|
#### Placeholder Syntax for `cnotes notes create`
|
|
927
998
|
|
|
@@ -932,14 +1003,14 @@ This builds on [@B: The Problem](relationship:references) and enables [@C: The W
|
|
|
932
1003
|
|
|
933
1004
|
The relationship type is free-form, but **reuse an existing type before inventing one** — see [Content via Markdown](#content-via-markdown) above for the reuse rule, why needless synonyms are harmful, and the built-in types to reach for first.
|
|
934
1005
|
|
|
935
|
-
###
|
|
1006
|
+
### Gathering Context with Search
|
|
936
1007
|
|
|
937
|
-
Before creating or updating notes on a topic, **
|
|
1008
|
+
Before creating or updating notes on a topic, **search existing notes first**:
|
|
938
1009
|
|
|
939
|
-
1. **
|
|
940
|
-
2. **
|
|
941
|
-
3. **
|
|
942
|
-
4. **Synthesize:** Combine
|
|
1010
|
+
1. **Notes:** `cnotes search semantic "<topic>"` — find conceptually related notes.
|
|
1011
|
+
2. **Canvases:** `cnotes canvas search "<topic>"` — separate corpus from notes.
|
|
1012
|
+
3. **Follow up:** Open hits with `cnotes notes get` / `cnotes canvas get`, then chase `rel list` and in-note mentions.
|
|
1013
|
+
4. **Synthesize:** Combine what you found from 2-3 searches before acting.
|
|
943
1014
|
|
|
944
1015
|
### Error Handling
|
|
945
1016
|
- Branch on the **exit code** first (see the table in [Reading output](#reading-output-start-here)) — it classes the failure without parsing prose — then read the `hint` in the `{ error, code, exitCode, hint }` JSON error on stderr; it names the command that fixes it.
|