@creator-notes/cnotes 0.16.11

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 (158) hide show
  1. package/.claude-plugin/plugin.json +14 -0
  2. package/.mcp.json +12 -0
  3. package/LICENSE +21 -0
  4. package/README.md +303 -0
  5. package/dist/cn.d.ts +3 -0
  6. package/dist/cn.d.ts.map +1 -0
  7. package/dist/cn.js +124 -0
  8. package/dist/cn.js.map +1 -0
  9. package/dist/commands/auth.d.ts +10 -0
  10. package/dist/commands/auth.d.ts.map +1 -0
  11. package/dist/commands/auth.js +188 -0
  12. package/dist/commands/auth.js.map +1 -0
  13. package/dist/commands/canvas.d.ts +3 -0
  14. package/dist/commands/canvas.d.ts.map +1 -0
  15. package/dist/commands/canvas.js +1383 -0
  16. package/dist/commands/canvas.js.map +1 -0
  17. package/dist/commands/claude-hook.d.ts +28 -0
  18. package/dist/commands/claude-hook.d.ts.map +1 -0
  19. package/dist/commands/claude-hook.js +59 -0
  20. package/dist/commands/claude-hook.js.map +1 -0
  21. package/dist/commands/config.d.ts +3 -0
  22. package/dist/commands/config.d.ts.map +1 -0
  23. package/dist/commands/config.js +47 -0
  24. package/dist/commands/config.js.map +1 -0
  25. package/dist/commands/files.d.ts +3 -0
  26. package/dist/commands/files.d.ts.map +1 -0
  27. package/dist/commands/files.js +119 -0
  28. package/dist/commands/files.js.map +1 -0
  29. package/dist/commands/init.d.ts +3 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/init.js +473 -0
  32. package/dist/commands/init.js.map +1 -0
  33. package/dist/commands/mcp.d.ts +15 -0
  34. package/dist/commands/mcp.d.ts.map +1 -0
  35. package/dist/commands/mcp.js +118 -0
  36. package/dist/commands/mcp.js.map +1 -0
  37. package/dist/commands/memory.d.ts +3 -0
  38. package/dist/commands/memory.d.ts.map +1 -0
  39. package/dist/commands/memory.js +150 -0
  40. package/dist/commands/memory.js.map +1 -0
  41. package/dist/commands/notes.d.ts +3 -0
  42. package/dist/commands/notes.d.ts.map +1 -0
  43. package/dist/commands/notes.js +706 -0
  44. package/dist/commands/notes.js.map +1 -0
  45. package/dist/commands/operations.d.ts +18 -0
  46. package/dist/commands/operations.d.ts.map +1 -0
  47. package/dist/commands/operations.js +231 -0
  48. package/dist/commands/operations.js.map +1 -0
  49. package/dist/commands/relationships.d.ts +3 -0
  50. package/dist/commands/relationships.d.ts.map +1 -0
  51. package/dist/commands/relationships.js +94 -0
  52. package/dist/commands/relationships.js.map +1 -0
  53. package/dist/commands/schema.d.ts +12 -0
  54. package/dist/commands/schema.d.ts.map +1 -0
  55. package/dist/commands/schema.js +85 -0
  56. package/dist/commands/schema.js.map +1 -0
  57. package/dist/commands/search.d.ts +3 -0
  58. package/dist/commands/search.d.ts.map +1 -0
  59. package/dist/commands/search.js +57 -0
  60. package/dist/commands/search.js.map +1 -0
  61. package/dist/commands/theme.d.ts +3 -0
  62. package/dist/commands/theme.d.ts.map +1 -0
  63. package/dist/commands/theme.js +184 -0
  64. package/dist/commands/theme.js.map +1 -0
  65. package/dist/commands/timeline.d.ts +3 -0
  66. package/dist/commands/timeline.d.ts.map +1 -0
  67. package/dist/commands/timeline.js +97 -0
  68. package/dist/commands/timeline.js.map +1 -0
  69. package/dist/commands/types.d.ts +3 -0
  70. package/dist/commands/types.d.ts.map +1 -0
  71. package/dist/commands/types.js +139 -0
  72. package/dist/commands/types.js.map +1 -0
  73. package/dist/commands/versions.d.ts +3 -0
  74. package/dist/commands/versions.d.ts.map +1 -0
  75. package/dist/commands/versions.js +120 -0
  76. package/dist/commands/versions.js.map +1 -0
  77. package/dist/commands/workspace.d.ts +13 -0
  78. package/dist/commands/workspace.d.ts.map +1 -0
  79. package/dist/commands/workspace.js +176 -0
  80. package/dist/commands/workspace.js.map +1 -0
  81. package/dist/lib/api-client.d.ts +45 -0
  82. package/dist/lib/api-client.d.ts.map +1 -0
  83. package/dist/lib/api-client.js +198 -0
  84. package/dist/lib/api-client.js.map +1 -0
  85. package/dist/lib/auth-store.d.ts +47 -0
  86. package/dist/lib/auth-store.d.ts.map +1 -0
  87. package/dist/lib/auth-store.js +116 -0
  88. package/dist/lib/auth-store.js.map +1 -0
  89. package/dist/lib/brand.d.ts +32 -0
  90. package/dist/lib/brand.d.ts.map +1 -0
  91. package/dist/lib/brand.js +32 -0
  92. package/dist/lib/brand.js.map +1 -0
  93. package/dist/lib/build-schema.d.ts +97 -0
  94. package/dist/lib/build-schema.d.ts.map +1 -0
  95. package/dist/lib/build-schema.js +139 -0
  96. package/dist/lib/build-schema.js.map +1 -0
  97. package/dist/lib/canvas-read.d.ts +54 -0
  98. package/dist/lib/canvas-read.d.ts.map +1 -0
  99. package/dist/lib/canvas-read.js +145 -0
  100. package/dist/lib/canvas-read.js.map +1 -0
  101. package/dist/lib/claude-session.d.ts +73 -0
  102. package/dist/lib/claude-session.d.ts.map +1 -0
  103. package/dist/lib/claude-session.js +104 -0
  104. package/dist/lib/claude-session.js.map +1 -0
  105. package/dist/lib/config.d.ts +28 -0
  106. package/dist/lib/config.d.ts.map +1 -0
  107. package/dist/lib/config.js +66 -0
  108. package/dist/lib/config.js.map +1 -0
  109. package/dist/lib/env.d.ts +14 -0
  110. package/dist/lib/env.d.ts.map +1 -0
  111. package/dist/lib/env.js +16 -0
  112. package/dist/lib/env.js.map +1 -0
  113. package/dist/lib/errors.d.ts +47 -0
  114. package/dist/lib/errors.d.ts.map +1 -0
  115. package/dist/lib/errors.js +194 -0
  116. package/dist/lib/errors.js.map +1 -0
  117. package/dist/lib/fs-utils.d.ts +2 -0
  118. package/dist/lib/fs-utils.d.ts.map +1 -0
  119. package/dist/lib/fs-utils.js +16 -0
  120. package/dist/lib/fs-utils.js.map +1 -0
  121. package/dist/lib/install-hook.d.ts +86 -0
  122. package/dist/lib/install-hook.d.ts.map +1 -0
  123. package/dist/lib/install-hook.js +168 -0
  124. package/dist/lib/install-hook.js.map +1 -0
  125. package/dist/lib/install-mcp.d.ts +21 -0
  126. package/dist/lib/install-mcp.d.ts.map +1 -0
  127. package/dist/lib/install-mcp.js +133 -0
  128. package/dist/lib/install-mcp.js.map +1 -0
  129. package/dist/lib/install-skill.d.ts +49 -0
  130. package/dist/lib/install-skill.d.ts.map +1 -0
  131. package/dist/lib/install-skill.js +113 -0
  132. package/dist/lib/install-skill.js.map +1 -0
  133. package/dist/lib/output.d.ts +29 -0
  134. package/dist/lib/output.d.ts.map +1 -0
  135. package/dist/lib/output.js +78 -0
  136. package/dist/lib/output.js.map +1 -0
  137. package/dist/lib/resolve-note.d.ts +7 -0
  138. package/dist/lib/resolve-note.d.ts.map +1 -0
  139. package/dist/lib/resolve-note.js +23 -0
  140. package/dist/lib/resolve-note.js.map +1 -0
  141. package/dist/lib/stdin.d.ts +5 -0
  142. package/dist/lib/stdin.d.ts.map +1 -0
  143. package/dist/lib/stdin.js +11 -0
  144. package/dist/lib/stdin.js.map +1 -0
  145. package/dist/lib/style.d.ts +10 -0
  146. package/dist/lib/style.d.ts.map +1 -0
  147. package/dist/lib/style.js +17 -0
  148. package/dist/lib/style.js.map +1 -0
  149. package/dist/lib/themes.d.ts +44 -0
  150. package/dist/lib/themes.d.ts.map +1 -0
  151. package/dist/lib/themes.js +168 -0
  152. package/dist/lib/themes.js.map +1 -0
  153. package/dist/mcp-server.d.ts +3 -0
  154. package/dist/mcp-server.d.ts.map +1 -0
  155. package/dist/mcp-server.js +782 -0
  156. package/dist/mcp-server.js.map +1 -0
  157. package/package.json +66 -0
  158. package/skills/cnotes/SKILL.md +680 -0
@@ -0,0 +1,680 @@
1
+ ---
2
+ name: cnotes
3
+ description: CreatorNotes CLI for the terminal. The command is `cnotes`. Use when the user mentions CreatorNotes or the cnotes CLI, asks to use this CLI skill, or wants to create, read, update, search, summarize, organize, or visualize notes, canvases, or transcripts in a CreatorNotes workspace. Covers cnotes notes, cnotes canvas, cnotes timeline, cnotes search, cnotes versions, relationships, and operations. Prefer the cnotes CLI, verify the active workspace with cnotes workspace current, use --json when parsing output, and wrap write operations in cnotes operations begin and cnotes operations end.
4
+ metadata:
5
+ short-description: Work with CreatorNotes via the cnotes CLI
6
+ ---
7
+
8
+ # CreatorNotes CLI (`cnotes`)
9
+
10
+ A command-line interface for CreatorNotes — create notes, build canvases, search knowledge, and manage workspaces from the terminal.
11
+
12
+ ## Getting Started
13
+
14
+ ### Install
15
+
16
+ ```bash
17
+ npm install -g @creator-notes/cnotes
18
+ ```
19
+
20
+ ### Authenticate
21
+
22
+ ```bash
23
+ cnotes auth login # Opens browser for OAuth
24
+ cnotes auth login --token <key> # Direct API key
25
+ cnotes auth status # Check current session
26
+ ```
27
+
28
+ ### Select a Workspace
29
+
30
+ ```bash
31
+ cnotes workspace list
32
+ cnotes workspace select <id>
33
+ cnotes workspace current # Verify active workspace + see its relationship types
34
+ ```
35
+
36
+ `cnotes workspace current` lists the relationship types already defined in the
37
+ workspace. Reuse those labels when cross-linking notes rather than coining new
38
+ ones — see [Relationship mentions](#content-via-markdown).
39
+
40
+ ## Command Reference
41
+
42
+ ### Global Flags (apply to ALL commands)
43
+ ```
44
+ --json Output raw JSON (best for piping / parsing)
45
+ -q, --quiet Minimal output (IDs only — useful for scripting)
46
+ -w, --workspace <id> Override active workspace
47
+ --server <url> Override server URL (default: https://creatornotes.app)
48
+ ```
49
+
50
+ ### Reading output (start here)
51
+
52
+ `cnotes` follows one output contract across every command. Learn it once and you never have to guess.
53
+
54
+ 1. **Success is the exit code, not anything in the body.** Exit `0` means the command succeeded. Never decide success by looking for an `ok` field or a particular key. The body is data; the exit code is the verdict. Failures use a typed table so you can branch on the class of error:
55
+
56
+ | exit | meaning | typical fix |
57
+ |------|---------|-------------|
58
+ | `0` | success | n/a |
59
+ | `2` | auth required | `cnotes auth login` |
60
+ | `3` | validation (bad input) | fix the input shown in the error |
61
+ | `4` | not found | the `hint` names the list command to run |
62
+ | `5` | conflict (already exists / state collision) | re-fetch and retry |
63
+ | `6` | rate limited | back off and retry |
64
+ | `7` | permission denied | `cnotes workspace current` |
65
+
66
+ 2. **Reads return their data directly; writes return an envelope.** `cnotes notes list/get/search --json` emit a bare array, so `… --json | jq '.[0].displayId'` works. `cnotes notes create --json` returns an object: `{ results, relationshipsCreated, relationshipTypesCreated }`. The created notes live under **`results`** (not `notes`, not `ok`). Read them with `jq '.results[].displayId'`. Each result carries `displayId` and `title`, the identifiers you reuse next, not only the internal id.
67
+
68
+ 3. **Errors hand you the next move.** A failed command prints `{ error, code, exitCode, hint }` to stderr in `--json` mode. The `hint` is actionable: a wrong workspace id returns a 404 with `resource: "workspace"` and the hint `Run \`cnotes workspace list\``, rather than an opaque 500. Read the `hint` before retrying.
69
+
70
+ ### Notes (`cnotes notes` / `cnotes n`)
71
+ ```bash
72
+ # List notes (default 20, excludes drafts)
73
+ cnotes notes list [--search <query>] [--type <type>] [--tags <csv>] [--pinned] [--limit <n>]
74
+
75
+ # Get one or more notes by display ID (always returns an array, in input order,
76
+ # in a single round-trip). Pass one ID or many — never call this in a loop.
77
+ cnotes notes get <id> [<id>...] [--all-versions] [--content-only]
78
+ cnotes notes get MEETING-12
79
+ cnotes notes get MEETING-12 PRD-3 IDEA-7
80
+
81
+ # Reading a LARGE note/transcript: --content-only prints just the raw markdown
82
+ # (no JSON envelope, no metadata chrome), so the terminal won't clip a giant
83
+ # JSON line. Best for transcripts you want to read or chunk.
84
+ cnotes notes get TRANSCRIPT-1 --content-only
85
+ cnotes notes get TRANSCRIPT-1 --content-only > /tmp/transcript.md # then chunk the file
86
+ # Without --content-only, fetch JSON and pull `.content` yourself:
87
+ # cnotes notes get TRANSCRIPT-1 --json | jq -r '.[0].content'
88
+ # (Reads are a bare array, so a single note is always at .[0].)
89
+
90
+ # Create one or more notes (always batch-shaped). Pass --notes as a single
91
+ # JSON object or an array. Title comes from the # h1 heading in each note's
92
+ # markdown. Use [@key: Title](relationship:references) to cross-reference
93
+ # another item in the same batch — @key is replaced with the new note's
94
+ # display ID after creation.
95
+ cnotes notes create --notes '{"key":"A","type":"Insight","markdown":"# Solo note","tags":["ux"]}'
96
+ cnotes notes create --notes '[
97
+ {"key":"A","type":"PainPoint","markdownFile":"./problem.md"},
98
+ {"key":"B","type":"Insight","markdown":"# Fix\nSee [@A: Problem](relationship:references)."}
99
+ ]'
100
+ # Each item: {key, type, markdown | markdownFile, tags?}.
101
+ # On validation failure the response lists every bad item by index + key + field
102
+ # (and `details.items` in --json mode), so you can fix the whole batch in one shot.
103
+
104
+ # Update metadata only (not content — use versions for that)
105
+ # NOTE: --title is NOT available. Title is read-only, derived from the # h1 heading in version content.
106
+ # To change a note's title, create a new version with a different # h1 heading.
107
+ cnotes notes update <id> [--type <t>] [--tags <csv>] [--archive] [--unarchive] [--pin] [--unpin]
108
+
109
+ # Delete (archive)
110
+ cnotes notes delete <id>
111
+
112
+ # Bulk archive/unarchive multiple notes at once
113
+ cnotes notes bulk-archive --ids '["MEETING-1","PRD-3","IDEA-7"]' [--unarchive]
114
+
115
+ # Bulk retype multiple notes to a new type (atomic per note: creates new, archives old, cross-links)
116
+ cnotes notes bulk-retype --ids '["NOTE-1","NOTE-3","NOTE-5"]' --type Insight
117
+
118
+ # Text search
119
+ cnotes notes search <query> [--type <type>] [--limit <n>]
120
+ ```
121
+
122
+ ### Versions (`cnotes versions` / `cnotes v`)
123
+ ```bash
124
+ # List versions
125
+ cnotes versions list <noteId>
126
+
127
+ # Create a new version (updates note content)
128
+ cnotes versions create <noteId> --description "what changed" --markdown-file <path.md>
129
+ cnotes versions create <noteId> --description "what changed" --markdown "<short content>" # inline (short only)
130
+ cnotes versions create <noteId> --description "what changed" --markdown-stdin < changes.md
131
+ ```
132
+
133
+ ### Canvas (`cnotes canvas` / `cnotes c`)
134
+ ```bash
135
+ # List / get (archived canvases excluded by default)
136
+ cnotes canvas list [--include-archived]
137
+ cnotes canvas get <canvasId>
138
+ # `get` returns each node kind in its own array (nodes/textNodes/richtextNodes/
139
+ # listNodes/canvasLinkNodes) AND a unified `allNodes` array — every node flattened
140
+ # to {id, kind, displayId, label, positionX, positionY, width, height} — plus
141
+ # `contentBounds` (the bounding box of everything). Use allNodes/contentBounds to
142
+ # reason about occupancy in one read instead of unioning the per-kind arrays.
143
+ # (You rarely need this for placement — `cnotes canvas place` already avoids overlaps.)
144
+
145
+ # Read every note on a canvas as one concatenated markdown document, in display
146
+ # order (top-to-bottom, then left-to-right) — think across a canvas in one round-trip
147
+ cnotes canvas read <canvasId>
148
+
149
+ # AI digest of canvas: narrative summary + per-note summary list for every note on the canvas
150
+ cnotes canvas digest <canvasId>
151
+
152
+ # Create / update / delete
153
+ cnotes canvas create "<name>" [--goal "<text>"] [--audience "<text>"]
154
+ cnotes canvas update <canvasId> [--title "<text>"] [--goal "<text>"] [--audience "<text>"]
155
+ cnotes canvas delete <canvasId>
156
+ cnotes canvas set-as-home <canvasId>
157
+ cnotes canvas archive <canvasId>
158
+ cnotes canvas unarchive <canvasId>
159
+
160
+ # Add nodes to canvas
161
+ cnotes canvas add-node <canvasId> --note <noteId> [--x <n>] [--y <n>]
162
+ cnotes canvas add-text <canvasId> --text "<content>" [--size heading|paragraph] [--x <n>] [--y <n>]
163
+ cnotes canvas add-richtext <canvasId> --content-file <path.md> [--size small|medium|large] [--color "#3B82F6"] [--x <n>] [--y <n>]
164
+ cnotes canvas add-richtext <canvasId> --content "<content>" [--size small|medium|large] [--color "#3B82F6"] [--x <n>] [--y <n>] # inline (short only)
165
+ # A list item is a COLLECTION OF NOTES, not a text block. --description is a one-line FRAME;
166
+ # the content is the member notes you pass to --notes. If you have N distinct things
167
+ # (questions, risks, options), create N notes first — pick the right type, e.g. a Question
168
+ # note per open question — then group them: --notes ID1,ID2,ID3. Pasting a numbered/bulleted
169
+ # list into --description with no --notes is the wrong shape (you get a "0 items" container
170
+ # masquerading as prose). For a standalone markdown block, use add-richtext instead.
171
+ cnotes canvas add-list <canvasId> --description "<markdown>" [--notes <id1,id2,...>] [--view list|grid] [--x <n>] [--y <n>]
172
+ cnotes canvas add-link <canvasId> --target <otherCanvasId> [--x <n>] [--y <n>]
173
+
174
+ # Place items using a declarative layout (NO x/y math — server packs them)
175
+ # This is the PREFERRED way to add multiple items. See "Canvas Layout" below.
176
+ cnotes canvas place <canvasId> --spec ./layout.json
177
+ cnotes canvas place <canvasId> --spec-stdin # JSON from stdin
178
+ cnotes canvas place <canvasId> --spec-inline '<short JSON>' # for tiny specs only
179
+
180
+ # Bulk add with explicit positions (escape hatch — only for pixel-perfect templates)
181
+ cnotes canvas bulk-add <canvasId> --notes '[{"noteId":"NOTE-1","x":100,"y":100},{"noteId":"NOTE-2","x":400,"y":100}]'
182
+
183
+ # Move / remove nodes
184
+ cnotes canvas move-node <canvasId> --node <nodeId> --x <n> --y <n>
185
+ cnotes canvas remove-node <canvasId> --node <nodeId>
186
+
187
+ # Bulk remove multiple nodes at once
188
+ cnotes canvas bulk-remove <canvasId> --nodes '["nodeId1","nodeId2"]'
189
+
190
+ # Bulk move multiple nodes at once (more efficient than repeated move-node)
191
+ cnotes canvas bulk-move <canvasId> --moves '[{"nodeId":"abc","nodeType":"note","x":200,"y":300},{"nodeId":"def","nodeType":"text","x":500,"y":300}]'
192
+ # nodeType: note | text | list | canvas | richtext (defaults to "note")
193
+
194
+ # Connect nodes
195
+ cnotes canvas add-edge <canvasId> --source <nodeId> --target <nodeId> [--label "<text>"]
196
+
197
+ # Templates — create structured canvases from predefined layouts
198
+ cnotes canvas templates # List available templates
199
+ cnotes canvas from-template <templateId> [--title "<title>"] # Create empty skeleton
200
+ cnotes canvas from-template <templateId> --populate # Auto-populate zones with matching notes
201
+ # Templates: sprint-retrospective, swot-analysis, kanban-board, feature-prioritization, meeting-notes, product-roadmap
202
+
203
+ # Agent-run bracketing — group a sequence of canvas changes into one operation
204
+ # with before/after snapshots so the user can see "AI did X" and revert as a unit.
205
+ # REQUIRED when an AI agent modifies a canvas. See "Agent-Run Bracketing" below.
206
+ cnotes canvas agent-run wrap --canvas <id> --prompt "<intent>" -- <command> # one-shot
207
+ cnotes canvas agent-run begin --canvas <id> --prompt "<intent>" [--rationale "<plan>"] # multi-step
208
+ cnotes canvas agent-run end --canvas <id>
209
+ cnotes canvas activity <canvasId> # inspect operations + attribution after the fact
210
+ ```
211
+
212
+ ### Timeline (`cnotes timeline` / `cnotes tl`)
213
+ ```bash
214
+ # Show recent changes (default: last 7 days)
215
+ cnotes timeline [--since <when>] [--until <when>] [--type <noteType>] [--note <filter>] [--limit <n>]
216
+
217
+ # Examples
218
+ cnotes timeline # last 7 days
219
+ cnotes timeline --since 30d # last 30 days
220
+ cnotes timeline --since 2w --type Meeting # meetings in last 2 weeks
221
+ cnotes timeline --since 7d --note standup # filter by note name
222
+ # Supported --since formats: relative (7d, 2w, 3h, 30m), ISO date (2026-03-21), unix ms
223
+ ```
224
+
225
+ ### Relationships (`cnotes relationships` / `cnotes rel`)
226
+ ```bash
227
+ cnotes rel list [--note <displayId>]
228
+
229
+ # List the relationship types defined in the workspace. Check this BEFORE
230
+ # using a relationship:<type> label so you reuse an existing type instead of
231
+ # creating a near-duplicate.
232
+ cnotes rel types
233
+ ```
234
+
235
+ ### Semantic Search (`cnotes search` / `cnotes s`)
236
+ ```bash
237
+ cnotes search semantic "<query>" [--limit <n>] [--canvas <canvasId>]
238
+ ```
239
+
240
+ ### Types (`cnotes types` / `cnotes t`)
241
+ ```bash
242
+ # List all types (supertags) in the workspace
243
+ cnotes types list
244
+
245
+ # Create a new type (prefix is auto-derived from display name)
246
+ cnotes types create <name> [--display-name <name>] [--description <text>] [--color <hex>]
247
+
248
+ # Update an existing type (prefix updates automatically when display name changes)
249
+ cnotes types update <name> [--display-name <name>] [--description <text>] [--color <hex>]
250
+
251
+ # Retype a note (atomic: creates new note with target type, archives old one, cross-links both)
252
+ cnotes notes retype <displayId> --type <NewType>
253
+ ```
254
+
255
+ ### Memory (`cnotes memory` / `cnotes mem`)
256
+ ```bash
257
+ # Search facts and entities by relevance
258
+ cnotes memory query "<query>" [--limit <n>] [--source <conversation|notes>]
259
+
260
+ # List all workspace facts (extracted from notes and conversations)
261
+ cnotes memory facts [--source <conversation|notes>]
262
+
263
+ # List all entities (people, topics, orgs) identified by Zep
264
+ cnotes memory entities [--source <conversation|notes>]
265
+ ```
266
+
267
+ ### Files (`cnotes files` / `cnotes f`)
268
+ ```bash
269
+ # Upload an image to the workspace (max 5MB, supports .jpg .jpeg .png .gif .webp)
270
+ cnotes files upload <path> [--markdown] # --markdown outputs ![filename](url) syntax
271
+ ```
272
+
273
+ ### Init & MCP (`cnotes init` / `cnotes mcp`)
274
+ ```bash
275
+ # Interactive setup wizard — auth, workspace, AI integrations, skill install
276
+ cnotes init
277
+
278
+ # Configure MCP server for AI tools (also handled by cnotes init)
279
+ cnotes mcp setup # Claude Desktop
280
+ cnotes mcp setup-codex # OpenAI Codex
281
+ ```
282
+
283
+ ### Theme (`cnotes theme`)
284
+
285
+ Personal UI theme preference (synced to your account, applies on next browser reload — does NOT live-update an open tab).
286
+
287
+ ```bash
288
+ # List built-in presets — current one is marked with *
289
+ cnotes theme list
290
+
291
+ # Show the active theme + any per-token color overrides
292
+ cnotes theme get
293
+
294
+ # Switch to a built-in preset
295
+ cnotes theme set <id> # e.g. dracula, ember, catppuccin-mocha, sage, light, dark
296
+
297
+ # Override a single color (switches the theme to "custom" automatically)
298
+ cnotes theme set-color <token> <hex> # e.g. cnotes theme set-color primary "#ff00aa"
299
+
300
+ # Drop all custom overrides and return to the base preset
301
+ cnotes theme reset
302
+ ```
303
+
304
+ **Editable tokens** (anything else is rejected with 422):
305
+ `background`, `card`, `popover`, `muted`, `border`, `sidebar-background`,
306
+ `primary`, `primary-foreground`, `ring`, `accent-teal`,
307
+ `foreground`, `muted-foreground`, `prose-body`, `prose-heading`,
308
+ `code-background`, `code-foreground`, `blockquote-border`,
309
+ `destructive`, `success`, `warning`, `info`.
310
+
311
+ `set-color` merges into existing overrides — call it repeatedly to build up a custom palette. `reset` returns to the preset that was the base of your custom theme (or `dark` if you started from scratch).
312
+
313
+ ### Config
314
+ ```bash
315
+ cnotes config get-server
316
+ cnotes config set-server <url>
317
+ ```
318
+
319
+ ## Assembling URLs for Responses
320
+
321
+ When you reference a workspace or canvas in a response (so the user can click through), build the URL yourself in this exact shape:
322
+
323
+ ```
324
+ <origin>/<workspaceId>?canvasId=<canvasId>
325
+ ```
326
+
327
+ - `<origin>` is `https://creatornotes.app` for prod, or `http://localhost:3000` when the active session / `--server` points at local dev. Match whichever the CLI is currently talking to.
328
+ - `<workspaceId>` and `<canvasId>` are the Convex IDs returned by `cnotes workspace current` / `cnotes canvas get` / `cnotes canvas list --json`. Use raw IDs — do not slug-ify.
329
+ - To link a workspace without a specific canvas, omit the query: `<origin>/<workspaceId>`. The app will redirect to the last-visited or home canvas.
330
+
331
+ **Do not invent path segments.** These shapes do NOT exist and will 404 or redirect somewhere wrong:
332
+
333
+ - `/workspace/<id>/canvas/<canvasId>` ← wrong, fabricated
334
+ - `/workspaces/<id>` ← wrong
335
+ - `/canvas/<canvasId>` ← wrong (there is a `/shared/<canvasId>` route, but only for public share links)
336
+
337
+ If you need the origin, read it from `cnotes config get-server` rather than guessing.
338
+
339
+ ## Best Practices
340
+
341
+ ### Note IDs
342
+ - Notes have **display IDs** like `MEETING-12`, `PRD-5`, `IDEA-3` — use these in commands.
343
+ - Convex IDs (e.g., `n17a43p7aenrxyr...`) also work but are less readable.
344
+ - When creating notes, capture the display ID from the output for subsequent commands.
345
+
346
+ ### Note Types
347
+ - The `--type` parameter must reference an **existing supertag** in the workspace. Free-text type strings are rejected.
348
+ - Before creating a note, check available types with `cnotes types list`. **Run `cnotes types list --json` and read each type's `validationPrompt` (its quality rubric) before authoring** — it spells out what a good instance of that type contains (e.g. a Decision needs the alternatives, rationale, and a revisit trigger). Author to the rubric so the note lands well-formed, rather than getting nudged after save.
349
+ - If the type you need doesn't exist, create it first: `cnotes types create "MyType" --display-name "My Type"`
350
+ - Type names are **case-sensitive** and use PascalCase (e.g., `Update`, `PainPoint`, `Meeting`).
351
+ - The display ID prefix is **auto-derived** from the display name: spaces become hyphens, all uppercase (e.g., display name "Pain Point" → prefix `PAIN-POINT`, display name "Idea" → prefix `IDEA`).
352
+ - Prefix is **immutable** once notes of that type exist. To change a note's type, use `cnotes notes retype <displayId> --type <NewType>`.
353
+
354
+ ### Content via Markdown
355
+ - Markdown is the only content format on the wire. For multi-line content, write to a file and pass the path via `markdownFile`:
356
+ ```bash
357
+ cnotes notes create --notes '[{"key":"A","type":"Note","markdownFile":"./note.md"}]'
358
+ ```
359
+ - Use inline `markdown` only for short, single-line content.
360
+ - **Always start markdown content with a `# h1` heading.** This becomes the note title. Follow proper markdown structure: `## h2` for major sections, `### h3` for subsections. Well-structured heading hierarchy is expected for all notes.
361
+ - **Relationship mentions in markdown**: Use `[NOTE-123](relationship:references)` or `[NOTE-123: Title](relationship:references)` syntax — plain text "NOTE-123" does NOT create clickable mentions.
362
+ - **Reuse an existing relationship type before inventing one.** Run `cnotes rel types` (or read the list printed by `cnotes workspace current`) and pick the closest existing label. The type slot is free-form, so an unknown label is auto-created as a custom workspace type rather than dropped — but that means a typo or a needless synonym (`support` vs `supports`, `relates-to` vs `related-to`) silently pollutes the workspace's type vocabulary forever. Only coin a new type when nothing existing fits.
363
+ - Names are normalized to lowercase + hyphens, so `Depends On`, `depends on`, and `depends-on` all resolve to the same type. When `cnotes notes create` does create new types, it reports them (`+ created N new relationship types: …`) — treat that line as a prompt to double-check you didn't mean an existing one.
364
+ - Common built-in types to reach for first:
365
+ - `depends-on` / `blocks` — dependency ordering between notes
366
+ - `references` — general "see also" link (default if omitted)
367
+ - `implements` — a note that delivers on what another identifies (e.g., feature → requirement, solution → gap)
368
+ - `derived-from` — a note created based on another's content
369
+ - `extends` — adds depth or detail to another note's topic
370
+ - `invalidates` / `supersedes` — supersedes or contradicts another note
371
+ - `related-to` — loose thematic connection
372
+ - `duplicates` — notes covering the same thing
373
+ - Simple mention shorthand: `[NOTE-123]` or `[NOTE-123: Title]` (defaults to "references")
374
+
375
+ ### Updating Content vs Metadata
376
+ - To change note **content** (including title), create a new **version**: `cnotes versions create <id> --description "..." --markdown-file ./updated.md`
377
+ - Title is derived from the `# h1` heading in the latest version — to rename a note, create a new version with a different heading.
378
+ - To change note **type/tags/pin/archive**, use: `cnotes notes update <id> --type "..." --tags "..."`
379
+ - These are separate operations by design.
380
+
381
+ ### JSON Mode for Scripting
382
+ - Use `--json` to get raw JSON output for parsing with `jq` or piping between commands. See [Reading output](#reading-output-start-here) for the contract behind these shapes.
383
+ - Reads emit a bare array; writes emit an envelope. Read created notes from `results`, never from `notes`/`ok`:
384
+ ```bash
385
+ # Capture the display IDs a batch create produced
386
+ IDS=$(cnotes notes create --json --notes '[…]' | jq -r '.results[].displayId')
387
+ ```
388
+ - Treat a `0` exit code as the only success signal. Do not re-run a write because an expected key looked empty; re-running a successful `notes create` duplicates the batch.
389
+ - On validation failure with `--json`, the per-item errors come back in `details.items` (array of `{index, key?, field, message}`) so they can be parsed and surfaced precisely.
390
+ - Use `-q` (quiet) to get just the ID(s), useful for chaining:
391
+ ```bash
392
+ NOTE_ID=$(cnotes notes create --notes '{"key":"A","type":"Note","markdown":"# Quick note"}' -q)
393
+ cnotes canvas add-node <canvasId> --note "$NOTE_ID"
394
+ ```
395
+
396
+ ### Canvas Orientation
397
+
398
+ **Always start canvas work with `cnotes canvas digest <canvasId> --json`.** The digest returns the narrative summary, key themes, and a per-note summary list covering every note on the canvas. Use this as your single source of context before making any changes — do not read each note individually unless you need full content.
399
+
400
+ The `notes` array entries have the shape `{ displayId, title, type, summary }`. When `summary` is `null`, the AI summary hasn't been generated yet (fresh note, or too short to summarize); run `cnotes notes get <displayId> [<displayId>...]` (batch-fetch in one call) if you need full content.
401
+
402
+ If `status` is `"none"` or `"stale"`, the `notes` array is still populated and accurate — you can start working immediately. Trigger a narrative refresh with `POST /api/canvas/<canvasId>/digest` only if you need the narrative itself.
403
+
404
+ ### Wrapping batch work in an operation (ENFORCED for `notes create`)
405
+
406
+ When an AI agent (including you, Claude) is doing a **batch of work** — creating notes, modifying 2+ entities, or building a canvas with content — wrap it in a workspace-level **operation**. The batch then shows up in the timeline as a single legible row with intent + evidence + count, and the user can later revert the whole run as one unit.
407
+
408
+ Without wrapping, each write is a standalone timeline row with no shared intent, no grouping, and no way to revert as a batch.
409
+
410
+ **This is enforced for note creation.** `cnotes notes create` (the batch-create endpoint) is rejected with `operation_required` (HTTP 422) unless a run is active — the server refuses to create notes outside an operation. Begin one first; the active run then auto-attaches to every subsequent write.
411
+
412
+ #### When to wrap
413
+
414
+ | Action | Wrap? |
415
+ |---|---|
416
+ | Reading: `digest`, `get`, `list`, `activity`, `timeline`, `search`, `memory query`, `rel list` | **No** — read-only |
417
+ | **Any `cnotes notes create`** (even a single note) | **Yes — enforced.** Creates run through the batch endpoint, which now rejects writes with no active run |
418
+ | A single `notes update` / tag / pin / archive change | **No** — single metadata edits don't go through the guarded create path |
419
+ | Two or more writes that belong to one intent (multi-note batch, canvas build-out, multi-step refactor) | **Yes** |
420
+ | Canvas mutations done as part of an agent task | **Yes** — even one canvas command, if it's part of a larger batch |
421
+
422
+ #### The pattern
423
+
424
+ `cnotes operations begin` opens a run; **every subsequent cnotes write on this server attaches to it automatically**, across any shell, until you call `cnotes operations end`. No env var to export, no eval trick — the active run is persisted in `~/.cnotes/config.json` per server, so stateless callers (separate Bash invocations, disjoint tool calls) all attach correctly.
425
+
426
+ ```bash
427
+ # 1. Open the run with the user's intent as the prompt.
428
+ cnotes operations begin --prompt "Reorganize roadmap into quarters" --json
429
+
430
+ # 2. Do the work. Every cnotes write here attaches to the run automatically.
431
+ cnotes notes create --notes '[ ... ]'
432
+ cnotes canvas add-text <canvasId> --text "Q1" --size heading --x 0 --y 0
433
+ cnotes canvas place <canvasId> --spec /tmp/q1.json
434
+ cnotes canvas add-edge <canvasId> --source <a> --target <b> --label "depends on"
435
+
436
+ # 3. Close the run when done.
437
+ cnotes operations end
438
+ ```
439
+
440
+ **Always close the run.** If `begin` succeeded, `end` must run — otherwise the operation stays `running` and subsequent `cnotes operations begin` calls will refuse (with a clear "active run already exists, end it first or pass --force" message). The server-side reaper will eventually mark a stale `running` row as `abandoned` after about an hour, but that's a safety net, not the intended flow.
441
+
442
+ **If something goes wrong mid-batch** and you can't recover: `cnotes operations end --force` (on a future begin) ends the prior run server-side and starts a fresh one.
443
+
444
+ #### Writing a good `--prompt`
445
+
446
+ The prompt is the **intent** the user sees on the timeline row. Use the user's own language where you can:
447
+
448
+ - "Build a fan-out under Goals heading with Q1 milestones"
449
+ - "Reorganize the canvas into a SWOT layout"
450
+ - "Add risks list and connect to the parent goal"
451
+
452
+ Avoid generic prompts like "make changes" or "update canvas" — they make the timeline useless.
453
+
454
+ #### Verifying it worked
455
+
456
+ After your run, the user (or you) can:
457
+ - Look at the **timeline panel** in the workspace → the run appears as one row with a bot icon, your prompt as the headline, and a "N notes" count
458
+ - Expand the row → each member note appears nested
459
+ - Run `cnotes operations get <agentRunId>` → confirms `affectedNoteCount` reflects everything the run touched
460
+
461
+ If the timeline shows individual note rows instead of one grouped run, the writes didn't attach — usually because `begin` was never called, or `end` was called too early (before the writes).
462
+
463
+ ### Canvas Layout
464
+
465
+ **Don't compute coordinates by hand. Use `cnotes canvas place` with a layout spec.**
466
+
467
+ The server measures every item, packs them into a no-overlap layout, and inserts them in a single batch. You describe **structure** (rows, columns, what's near what) — the server resolves pixels. This is what `cnotes canvas place` is for, and you should reach for it whenever you're adding more than one item.
468
+
469
+ The call is **best-effort, not transactional** — per-item failures are reported in the response (HTTP 207) but the items that succeeded stay on the canvas. Always inspect `items[].error` and `edges[].error` before assuming success.
470
+
471
+ **You don't have to find a free coordinate.** `place` is collision-aware against content already on the canvas. By default (`placement: "auto"`) it drops your layout in a clear band **below everything that's already there**, and if you do pass an `origin` that would overlap, it slides the whole block straight down until it's clear. So appending a new section to a non-empty canvas Just Works — no need to read existing positions and guess a safe `origin` first. Use `--placement append` to force "below everything", or `--placement exact` (or `"placement": "exact"` in the doc) to honor your `origin` verbatim for pixel-perfect templates.
472
+
473
+ Use `cnotes canvas bulk-add` with explicit `x/y` ONLY for pixel-perfect templates (e.g. demo recordings or tutorial walkthroughs that need to match a specific screenshot).
474
+
475
+ #### The layout DSL
476
+
477
+ A layout document is `{ root, edges?, origin?, placement? }` where `root` is one of four node kinds:
478
+
479
+ - **`stack`** — items flow along one axis (like CSS flexbox)
480
+ ```json
481
+ { "kind": "stack", "axis": "vertical", "gap": "medium",
482
+ "align": "start", "items": [ /* LayoutNode */ ] }
483
+ ```
484
+ - **`grid`** — N columns, items wrap to new rows (like CSS grid auto-flow)
485
+ ```json
486
+ { "kind": "grid", "columns": 3, "gap": "medium",
487
+ "items": [ /* LayoutNode */ ] }
488
+ ```
489
+ - **`anchor`** — place a sub-tree relative to an EXISTING canvas node
490
+ ```json
491
+ { "kind": "anchor", "to": "NOTE-12", "direction": "right",
492
+ "gap": "medium", "child": /* LayoutNode */ }
493
+ ```
494
+ - **`item`** — a leaf node (no x/y!). One per visible thing on the canvas.
495
+
496
+ `gap` accepts a preset (`"tight"`, `"medium"`, `"spacious"`) or a raw pixel number. Defaults to `"medium"`.
497
+
498
+ `placement` (top-level, optional) controls where the whole layout sits relative to existing content: `"auto"` (default, collision-aware — below existing content / de-collide), `"append"` (always below everything), `"exact"` (honor `origin` verbatim). `origin` is the top-left corner; omit it and `auto`/`append` pick a safe one for you.
499
+
500
+ #### Leaf item shapes
501
+
502
+ ```json
503
+ { "kind": "item", "type": "note", "noteId": "NOTE-3", "key": "a" }
504
+ { "kind": "item", "type": "text", "content": "Hello", "fontSize": 32, "colorVariant": "muted" }
505
+ { "kind": "item", "type": "richtext", "content": "# Markdown OK", "size": "medium", "colorHex": "#3B82F6" }
506
+ { "kind": "item", "type": "list", "description": "Risks tracked weekly — owner: @ana", "noteIds": ["A","B","C"], "viewMode": "list" }
507
+ { "kind": "item", "type": "canvas", "linkedCanvasId": "abc..." }
508
+ ```
509
+
510
+ The optional `key` lets edges and other items reference this leaf later (`@<key>`).
511
+
512
+ `richtext.content` accepts markdown directly — the server converts to TipTap before measuring.
513
+
514
+ `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 `richtext` when you genuinely want one standalone markdown block with no members.
515
+
516
+ #### Edges
517
+
518
+ Edges live at the top level alongside `root`. Each endpoint is resolved in this priority order:
519
+
520
+ 1. **`@<key>`** — any item placed in the same call that carries a matching `key`
521
+ 2. **Original `noteId`** of a note placed in this call (e.g. `"NOTE-7"`) — no `key` needed
522
+ 3. **Display id or convex id** of a node already on the canvas
523
+
524
+ ```json
525
+ "edges": [
526
+ { "from": "@a", "to": "@b", "label": "depends on" },
527
+ { "from": "NOTE-7", "to": "@a" }
528
+ ]
529
+ ```
530
+
531
+ #### Examples
532
+
533
+ **Fan-out under a heading** (the failure case the agent kept getting wrong):
534
+ ```json
535
+ {
536
+ "root": {
537
+ "kind": "stack", "axis": "vertical", "gap": "medium",
538
+ "items": [
539
+ { "kind": "item", "type": "richtext",
540
+ "content": "# Goals\nWhat we're committing to this quarter.",
541
+ "size": "medium" },
542
+ { "kind": "grid", "columns": 4,
543
+ "items": [
544
+ { "kind": "item", "type": "note", "noteId": "GOAL-1" },
545
+ { "kind": "item", "type": "note", "noteId": "GOAL-2" },
546
+ { "kind": "item", "type": "note", "noteId": "GOAL-3" },
547
+ { "kind": "item", "type": "note", "noteId": "GOAL-4" }
548
+ ] }
549
+ ]
550
+ }
551
+ }
552
+ ```
553
+
554
+ **SWOT-style four-quadrant layout**:
555
+ ```json
556
+ {
557
+ "root": {
558
+ "kind": "stack", "axis": "vertical", "gap": "spacious",
559
+ "items": [
560
+ { "kind": "stack", "axis": "horizontal", "gap": "spacious", "items": [
561
+ { "kind": "grid", "columns": 2, "items": [
562
+ { "kind": "item", "type": "note", "noteId": "STR-1" },
563
+ { "kind": "item", "type": "note", "noteId": "STR-2" }
564
+ ] },
565
+ { "kind": "grid", "columns": 2, "items": [
566
+ { "kind": "item", "type": "note", "noteId": "WEAK-1" }
567
+ ] }
568
+ ] },
569
+ { "kind": "stack", "axis": "horizontal", "gap": "spacious", "items": [
570
+ { "kind": "grid", "columns": 2, "items": [
571
+ { "kind": "item", "type": "note", "noteId": "OPP-1" }
572
+ ] },
573
+ { "kind": "grid", "columns": 2, "items": [
574
+ { "kind": "item", "type": "note", "noteId": "THR-1" }
575
+ ] }
576
+ ] }
577
+ ]
578
+ }
579
+ }
580
+ ```
581
+
582
+ **Anchor a new column to the right of an existing note**:
583
+ ```json
584
+ {
585
+ "root": {
586
+ "kind": "anchor", "to": "NOTE-12", "direction": "right", "gap": "medium",
587
+ "child": {
588
+ "kind": "stack", "axis": "vertical", "gap": "tight",
589
+ "items": [
590
+ { "kind": "item", "type": "note", "noteId": "NEW-1" },
591
+ { "kind": "item", "type": "note", "noteId": "NEW-2" }
592
+ ]
593
+ }
594
+ }
595
+ }
596
+ ```
597
+
598
+ **Diamond topology with edges**:
599
+ ```json
600
+ {
601
+ "root": {
602
+ "kind": "stack", "axis": "vertical", "gap": "medium", "align": "center",
603
+ "items": [
604
+ { "kind": "item", "type": "note", "noteId": "A", "key": "root" },
605
+ { "kind": "stack", "axis": "horizontal", "gap": "medium", "items": [
606
+ { "kind": "item", "type": "note", "noteId": "B", "key": "left" },
607
+ { "kind": "item", "type": "note", "noteId": "C", "key": "right" }
608
+ ] },
609
+ { "kind": "item", "type": "note", "noteId": "D", "key": "tail" }
610
+ ]
611
+ },
612
+ "edges": [
613
+ { "from": "@root", "to": "@left" },
614
+ { "from": "@root", "to": "@right" },
615
+ { "from": "@left", "to": "@tail" },
616
+ { "from": "@right", "to": "@tail" }
617
+ ]
618
+ }
619
+ ```
620
+
621
+ #### Workflow
622
+
623
+ 1. Write the layout doc to a temp file (`/tmp/layout.json`).
624
+ 2. Run `cnotes canvas place <canvasId> --spec /tmp/layout.json --json`.
625
+ 3. The response includes `items[]` (with resolved `positionX/Y`, `width`, `height`, `id`, and your `key`), `edges[]` (with resolved edge ids), and `bbox` (the bounding box of the whole layout).
626
+
627
+ #### Constraints
628
+
629
+ - **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.
630
+ - **Anchor `to:` must be a node already on the canvas** — local `@key` refs only work for edges, not for anchor targets in this version.
631
+ - **Never use ALL CAPS** for text node content. Use title case (e.g., "Key Tensions" not "KEY TENSIONS").
632
+ - **Multi-line richtext content** is best authored as markdown — the server converts it server-side.
633
+
634
+ #### When the layout engine isn't enough
635
+
636
+ A few cases still need explicit `x/y` via `bulk-add`:
637
+ - Reproducing a canvas pixel-for-pixel for a demo/screenshot
638
+ - Templates with intentional off-grid placement
639
+ - Adding a single item next to a known coordinate without invoking the solver
640
+
641
+ In all other cases — and especially when adding 3+ items at once — use `place`.
642
+
643
+ ### Interlinking Notes
644
+
645
+ When creating multiple related notes, interlink at two levels:
646
+
647
+ **1. Relationship mentions inside note content** — connects notes in the knowledge graph:
648
+ - Use `cnotes notes create` with an array and `@key` placeholders for cross-references.
649
+ - Example: `[@B: The Problem](relationship:references)` in markdown.
650
+ - The server resolves placeholders to real display IDs in one atomic transaction.
651
+
652
+ **2. Canvas edges between nodes** — visual connections on the canvas:
653
+ - After adding nodes, connect them with `cnotes canvas add-edge`.
654
+ - Use descriptive labels (e.g., "triggers", "depends on").
655
+
656
+ Both levels are needed — edges are visual-only, mentions are content-level.
657
+
658
+ #### Placeholder Syntax for `cnotes notes create`
659
+
660
+ Use `[@key: Title](relationship:type)` where `key` matches a note's `key` field in the `--notes` JSON:
661
+ ```markdown
662
+ This builds on [@B: The Problem](relationship:references) and enables [@C: The Workflow](relationship:derived-from).
663
+ ```
664
+
665
+ The relationship type is free-form, but **reuse an existing type before inventing one** — run `cnotes rel types` and pick the closest match. Unknown labels are auto-created (not dropped), so a needless synonym permanently clutters the workspace's vocabulary. Common built-ins to reach for first: `depends-on`, `blocks`, `related-to`, `derived-from`, `references`, `extends`, `implements`, `invalidates`, `supersedes`, `duplicates`. See the markdown content section above for when to use each type.
666
+
667
+ ### Memory
668
+
669
+ Before creating or updating notes on a topic, **query memory first** to understand what is already known:
670
+
671
+ 1. **Start broad:** `cnotes memory query "<topic>"` — get the top facts and entities.
672
+ 2. **Follow entities:** Run follow-up queries on related people, teams, or initiatives.
673
+ 3. **Check temporal validity:** Facts have `validAt`/`invalidAt` timestamps — prefer current facts.
674
+ 4. **Synthesize:** Combine facts from 2-3 queries before acting.
675
+
676
+ ### Error Handling
677
+ - Branch on the **exit code** first (see the table in [Reading output](#reading-output-start-here)); it tells you the class of failure without parsing prose.
678
+ - In `--json` mode a failure prints `{ error, code, exitCode, hint }` to stderr. The `hint` names the command that fixes it, so read it before retrying.
679
+ - If a command fails with auth errors (exit `2`), run `cnotes auth status` to verify credentials.
680
+ - If a workspace error occurs (exit `4` not found, or `7` permission), verify with `cnotes workspace current` and list options with `cnotes workspace list`.