@creator-notes/cnotes 0.26.0 → 0.30.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 +331 -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 +98 -65
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/skills/cnotes/SKILL.md +172 -75
- package/skills/cnotes/references/humanizer.md +267 -0
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* At runtime this file lives at <package>/dist/lib/install-skill.js, so the
|
|
10
10
|
* skill source is two directories up.
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import { dirname, resolve } from "path";
|
|
12
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "fs";
|
|
13
|
+
import { dirname, relative, resolve } from "path";
|
|
14
14
|
import { homedir } from "os";
|
|
15
15
|
import { fileURLToPath } from "url";
|
|
16
16
|
export const SKILL_TARGETS = ["claude", "codex"];
|
|
@@ -66,6 +66,32 @@ function filesAreIdentical(a, b) {
|
|
|
66
66
|
return false;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
/** Every file path under `dir`, relative to it (recursive). */
|
|
70
|
+
function listFilesRecursive(dir, base = dir) {
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
73
|
+
const full = resolve(dir, entry.name);
|
|
74
|
+
if (entry.isDirectory())
|
|
75
|
+
out.push(...listFilesRecursive(full, base));
|
|
76
|
+
else
|
|
77
|
+
out.push(relative(base, full));
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* True when every bundled file under `sourceDir` (SKILL.md plus any references/)
|
|
83
|
+
* exists byte-identical under `destDir`. The skill folder is a handful of small
|
|
84
|
+
* files, so this per-invocation compare stays cheap — and unlike a SKILL.md-only
|
|
85
|
+
* check it also notices a changed reference and triggers a refresh.
|
|
86
|
+
*/
|
|
87
|
+
function skillTreeUpToDate(sourceDir, destDir) {
|
|
88
|
+
for (const rel of listFilesRecursive(sourceDir)) {
|
|
89
|
+
if (!filesAreIdentical(resolve(sourceDir, rel), resolve(destDir, rel))) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
69
95
|
export const SKILL_CATALOG = [
|
|
70
96
|
{
|
|
71
97
|
name: "cnotes",
|
|
@@ -108,8 +134,7 @@ export function installSkillFolder(name, target) {
|
|
|
108
134
|
throw new Error(`Bundled skill "${name}" not found at ${source}`);
|
|
109
135
|
}
|
|
110
136
|
const dest = skillFolderDest(name, target);
|
|
111
|
-
const upToDate = existsSync(dest) &&
|
|
112
|
-
filesAreIdentical(resolve(source, "SKILL.md"), resolve(dest, "SKILL.md"));
|
|
137
|
+
const upToDate = existsSync(dest) && skillTreeUpToDate(source, dest);
|
|
113
138
|
if (!upToDate) {
|
|
114
139
|
mkdirSync(dirname(dest), { recursive: true });
|
|
115
140
|
cpSync(source, dest, { recursive: true });
|
|
@@ -143,20 +168,27 @@ export function refreshInstalledSkills() {
|
|
|
143
168
|
return results;
|
|
144
169
|
}
|
|
145
170
|
/**
|
|
146
|
-
* Copy the bundled skill
|
|
147
|
-
* Caller is responsible for any prompting
|
|
171
|
+
* Copy the bundled skill *folder* (SKILL.md plus its `references/`) into a
|
|
172
|
+
* target agent's skills dir. Caller is responsible for any prompting.
|
|
173
|
+
*
|
|
174
|
+
* The whole folder is copied — not just SKILL.md — so progressively-disclosed
|
|
175
|
+
* references (e.g. `references/humanizer.md`, loaded only for writing tasks) ship
|
|
176
|
+
* alongside the skill. `destPath` still points at the SKILL.md so callers'
|
|
177
|
+
* messages read the same.
|
|
148
178
|
*/
|
|
149
179
|
export function installSkill(target = "claude") {
|
|
150
|
-
const
|
|
180
|
+
const sourceFile = getBundledSkillPath();
|
|
151
181
|
const dest = getSkillDestPath(target);
|
|
152
|
-
|
|
153
|
-
|
|
182
|
+
const sourceDir = dirname(sourceFile);
|
|
183
|
+
const destDir = dirname(dest);
|
|
184
|
+
if (!existsSync(sourceFile)) {
|
|
185
|
+
throw new Error(`Bundled skill not found at ${sourceFile}`);
|
|
154
186
|
}
|
|
155
|
-
if (existsSync(dest) &&
|
|
187
|
+
if (existsSync(dest) && skillTreeUpToDate(sourceDir, destDir)) {
|
|
156
188
|
return { target, installed: false, destPath: dest, alreadyUpToDate: true };
|
|
157
189
|
}
|
|
158
|
-
mkdirSync(
|
|
159
|
-
|
|
190
|
+
mkdirSync(destDir, { recursive: true });
|
|
191
|
+
cpSync(sourceDir, destDir, { recursive: true }); // SKILL.md + references/
|
|
160
192
|
removeLegacySkill(target); // drop the old `cn` skill once the new one is in place
|
|
161
193
|
return { target, installed: true, destPath: dest };
|
|
162
194
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-skill.js","sourceRoot":"","sources":["../../src/lib/install-skill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"install-skill.js","sourceRoot":"","sources":["../../src/lib/install-skill.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp-server.js
CHANGED
|
@@ -101,7 +101,7 @@ async function withErrorHandling(fn) {
|
|
|
101
101
|
// ---------------------------------------------------------------------------
|
|
102
102
|
const server = new McpServer({
|
|
103
103
|
name: "cnotes-mcp",
|
|
104
|
-
version: "0.
|
|
104
|
+
version: "0.4.0",
|
|
105
105
|
});
|
|
106
106
|
// ---------------------------------------------------------------------------
|
|
107
107
|
// Resources
|
|
@@ -233,7 +233,7 @@ const NoteCreateItem = z.object({
|
|
|
233
233
|
markdown: z.string().describe("Note content as markdown (the first # h1 heading becomes the title)"),
|
|
234
234
|
tags: z.array(z.string()).optional().describe("Tags for this note"),
|
|
235
235
|
});
|
|
236
|
-
server.tool("cnotes_notes_create", "Create one or more interlinked notes in a single atomic transaction.
|
|
236
|
+
server.tool("cnotes_notes_create", "Create one or more interlinked notes in a single atomic transaction. FIRST read the target type's rubric (cnotes_types_show) and author to it. Pass `notes` as one item object or an array; each note's title is its first # h1 heading. Cross-reference in markdown with [@key: Title](relationship:type) (same-batch — @key resolves to the new display ID) or [NOTE-12: Title](relationship:references) (existing note); ALWAYS include the ': Title' part, and the relationship name is free-form (use 'references' if unsure). The response echoes each type's rubric under `rubrics`; on validation failure, `details.items` pinpoints the bad item by index + key.", {
|
|
237
237
|
notes: z
|
|
238
238
|
.union([NoteCreateItem, z.array(NoteCreateItem).min(1)])
|
|
239
239
|
.describe("Either a single item object or a non-empty array of items."),
|
|
@@ -280,7 +280,7 @@ server.tool("cnotes_notes_retype", "Change a single note's type. Atomic operatio
|
|
|
280
280
|
// ---------------------------------------------------------------------------
|
|
281
281
|
// Search tools
|
|
282
282
|
// ---------------------------------------------------------------------------
|
|
283
|
-
server.tool("cnotes_search", "Unified search across NOTES and CANVASES in one call — returns two separately-ranked sections (notes, canvases), so you never miss a
|
|
283
|
+
server.tool("cnotes_search", "Unified search across NOTES and CANVASES in one call — returns two separately-ranked sections (notes, canvases), so you never miss a canvas by searching only notes. Default (type omitted or 'all') returns both; set type to 'Canvas' for canvases only, or a note supertag (e.g. 'Decision') for notes of that type. Prefer this when you don't already know whether the answer is a note or a canvas.", {
|
|
284
284
|
query: z.string().describe("Search query"),
|
|
285
285
|
type: z.string().optional().describe("Scope: omit or 'all' for both, 'Canvas' for canvases only, or a note supertag (e.g. 'Decision') for notes of that type"),
|
|
286
286
|
limit: z.number().optional().default(10).describe("Max results per section"),
|
|
@@ -302,7 +302,7 @@ server.tool("cnotes_search_semantic", "Semantic (vector) search across NOTES onl
|
|
|
302
302
|
limit: limit ?? 10,
|
|
303
303
|
...(canvasId ? { canvasId } : {}),
|
|
304
304
|
})));
|
|
305
|
-
server.tool("cnotes_canvas_search", "Semantic + lexical search across CANVASES only
|
|
305
|
+
server.tool("cnotes_canvas_search", "Semantic + lexical search across CANVASES only — find a canvas by name, topic, or meaning (blends lexical name/theme/digest match with semantic similarity, so a query sharing no words with a canvas can still surface it). Use it to locate the right canvas before reading it; for one call that also covers notes, use cnotes_search. Pass '*' or 'all' to enumerate every canvas.", {
|
|
306
306
|
query: z.string().describe("Search query (use '*' or 'all' to list every canvas)"),
|
|
307
307
|
limit: z.number().optional().default(10).describe("Max results"),
|
|
308
308
|
}, async ({ query, limit }) => withErrorHandling(() => api.get("/api/canvas/search", {
|
|
@@ -373,28 +373,25 @@ server.tool("cnotes_canvas_add_node", "[Deprecated: use cnotes_canvas_bulk_add]
|
|
|
373
373
|
action: "bulkAddNodes",
|
|
374
374
|
items: [{ type: "note", noteId, positionX: x, positionY: y }],
|
|
375
375
|
})));
|
|
376
|
-
|
|
376
|
+
// The canvas has ONE free-form text element, called "Text", and it is a `richtext`
|
|
377
|
+
// node on the wire. The old `text` node kind (the "Heading") is LEGACY: existing ones
|
|
378
|
+
// still render / move / delete, but nothing creates them any more — a Text element with
|
|
379
|
+
// no background tint and `# Foo` inside IS a heading.
|
|
380
|
+
const TEXT_ELEMENT_CONTENT_DESC = "Text content as markdown (headings, emphasis, images all work — a heading is just `# Title`). Plain markdown ONLY — do NOT use [NOTE-X](relationship:...) or [[...]] mention syntax here (it corrupts the card, which renders blank); reference notes as plain display IDs (\"see RISK-64\"). Keep it card-sized (≤ ~3 sentences): text elements are unversioned, unsearchable presentation furniture — knowledge worth keeping belongs in a typed note.";
|
|
381
|
+
const textElementSchema = {
|
|
377
382
|
canvasId: z.string().describe("Canvas ID"),
|
|
378
|
-
|
|
379
|
-
size: z.enum(["heading", "paragraph"]).optional().default("heading").describe("Text size"),
|
|
380
|
-
color: z.enum(["normal", "muted", "highlighted"]).optional().describe("Color variant"),
|
|
381
|
-
x: z.number().optional().default(100).describe("X position"),
|
|
382
|
-
y: z.number().optional().default(100).describe("Y position"),
|
|
383
|
-
}, async ({ canvasId, text, size, color, x, y }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
384
|
-
action: "bulkAddNodes",
|
|
385
|
-
items: [{ type: "text", content: text, fontSize: size === "paragraph" ? 18 : 32, colorVariant: color, positionX: x, positionY: y }],
|
|
386
|
-
})));
|
|
387
|
-
server.tool("cnotes_canvas_add_richtext", "[Deprecated: use cnotes_canvas_bulk_add] Add a richtext annotation node to a canvas.", {
|
|
388
|
-
canvasId: z.string().describe("Canvas ID"),
|
|
389
|
-
content: z.string().describe("Richtext content as markdown. Plain markdown ONLY — do NOT use [NOTE-X](relationship:...) or [[...]] mention syntax here (it corrupts the card, which renders blank); reference notes as plain display IDs (\"see RISK-64\"). Keep it sticky-sized (≤ ~3 sentences): richtext is unversioned, unsearchable presentation furniture — knowledge worth keeping belongs in a typed note."),
|
|
383
|
+
content: z.string().describe(TEXT_ELEMENT_CONTENT_DESC),
|
|
390
384
|
size: z.enum(["small", "medium", "large"]).optional().default("small").describe("Display size"),
|
|
391
|
-
color: z.string().optional().describe("Background color hex"),
|
|
385
|
+
color: z.string().optional().describe("Background tint color hex. Default is NO background — omit unless the tint encodes a role used consistently on this canvas."),
|
|
392
386
|
x: z.number().optional().default(100).describe("X position"),
|
|
393
387
|
y: z.number().optional().default(100).describe("Y position"),
|
|
394
|
-
}
|
|
388
|
+
};
|
|
389
|
+
const addTextElement = ({ canvasId, content, size, color, x, y }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
395
390
|
action: "bulkAddNodes",
|
|
396
391
|
items: [{ type: "richtext", content, size, colorHex: color, positionX: x, positionY: y }],
|
|
397
|
-
}))
|
|
392
|
+
}));
|
|
393
|
+
server.tool("cnotes_canvas_add_text", "[Deprecated: use cnotes_canvas_bulk_add] Add a Text element to a canvas — free-form rich text placed directly on the canvas (markdown: headings, emphasis, images), with an optional background tint (none by default). A heading is just content '# Title'.", textElementSchema, addTextElement);
|
|
394
|
+
server.tool("cnotes_canvas_add_richtext", "[Deprecated: use cnotes_canvas_add_text or cnotes_canvas_bulk_add] Alias of cnotes_canvas_add_text — adds the same Text element (richtext) with the same arguments.", textElementSchema, addTextElement);
|
|
398
395
|
server.tool("cnotes_canvas_add_list", "[Deprecated: use cnotes_canvas_bulk_add] Add a list/grid node grouping notes on a canvas. The description is the only label; its first paragraph derives the searchable title.", {
|
|
399
396
|
canvasId: z.string().describe("Canvas ID"),
|
|
400
397
|
description: z.string().min(1).describe("Rich-text description (markdown). Required. First paragraph derives the searchable title."),
|
|
@@ -423,7 +420,7 @@ server.tool("cnotes_canvas_add_link", "[Deprecated: use cnotes_canvas_bulk_add]
|
|
|
423
420
|
items: [{ type: "canvas", linkedCanvasId: targetCanvasId, positionX: x, positionY: y }],
|
|
424
421
|
})));
|
|
425
422
|
// --- Primary bulk tool ---
|
|
426
|
-
server.tool("cnotes_canvas_bulk_add", "Add one or more items to a canvas. Use this for ALL canvas additions — single or multiple.
|
|
423
|
+
server.tool("cnotes_canvas_bulk_add", "Add one or more items to a canvas. Use this for ALL canvas additions — single or multiple. Supported item types: note, richtext (the Text element), list, canvas (link to another canvas), section (named region frame), and edge. ALWAYS prefer this over repeated single-item calls. Items are processed in order so edges can reference nodes created earlier in the same batch via sourceItemIndex/targetItemIndex.\n\nELEMENT CHOICE — notes are the knowledge tier (typed, versioned, searchable, the only content agents read back in full); everything else is presentation furniture. Default any claim/fact/decision/finding worth keeping to a typed NOTE. Use richtext (the Text element) for card-sized framing — orientation banners, a markdown heading used as a label ('# Q1 Goals', untinted), emphasis quoting a note, image tiles — never as the sole home of a claim; section for a named REGION of the canvas; list for grouping member notes (description = one-line frame; N distinct things = N notes); canvas for portals; edge for note-to-note relationships (notes only — richtext endpoints fail; mirror the relationship as a mention in the note bodies).", {
|
|
427
424
|
canvasId: z.string().describe("Canvas ID"),
|
|
428
425
|
notes: z.array(z.object({
|
|
429
426
|
noteId: z.string().describe("Note display ID or Convex ID"),
|
|
@@ -438,20 +435,14 @@ server.tool("cnotes_canvas_bulk_add", "Add one or more items to a canvas. Use th
|
|
|
438
435
|
y: z.number().describe("Y position"),
|
|
439
436
|
}),
|
|
440
437
|
z.object({
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
x: z.number().describe("X position"),
|
|
444
|
-
y: z.number().describe("Y position"),
|
|
445
|
-
fontSize: z.number().optional().describe("Font size (default 18, h1=32, h2=24)"),
|
|
446
|
-
colorVariant: z.enum(["normal", "muted", "highlighted"]).optional(),
|
|
447
|
-
}),
|
|
448
|
-
z.object({
|
|
438
|
+
// `richtext` IS the Text element. There is no `text` item type any more:
|
|
439
|
+
// the legacy `text` node (the old "Heading") can no longer be created.
|
|
449
440
|
type: z.literal("richtext"),
|
|
450
|
-
content: z.string().describe("Markdown content. Plain markdown ONLY — no [NOTE-X](relationship:...) / [[...]] mention syntax (corrupts the card); reference notes as plain display IDs.
|
|
441
|
+
content: z.string().describe("Markdown content — this is the Text element (headings, emphasis, images; a heading is just '# Title'). Plain markdown ONLY — no [NOTE-X](relationship:...) / [[...]] mention syntax (corrupts the card); reference notes as plain display IDs. Card-sized (≤ ~3 sentences); knowledge worth keeping goes in a note."),
|
|
451
442
|
x: z.number().describe("X position"),
|
|
452
443
|
y: z.number().describe("Y position"),
|
|
453
444
|
size: z.enum(["small", "medium", "large"]).optional().describe("Node size"),
|
|
454
|
-
colorHex: z.string().optional().describe("Background
|
|
445
|
+
colorHex: z.string().optional().describe("Background tint hex. Default is NO background — omit unless the tint encodes a role used consistently on this canvas."),
|
|
455
446
|
}),
|
|
456
447
|
z.object({
|
|
457
448
|
type: z.literal("list"),
|
|
@@ -514,13 +505,13 @@ server.tool("cnotes_canvas_bulk_add", "Add one or more items to a canvas. Use th
|
|
|
514
505
|
items: allItems,
|
|
515
506
|
});
|
|
516
507
|
}));
|
|
517
|
-
server.tool("cnotes_canvas_place", "Place items on a canvas using a DECLARATIVE LAYOUT (no x/y math). The server measures every item, packs them with no overlaps, and inserts them in a single batch. PREFER THIS over cnotes_canvas_bulk_add whenever you can express the layout as stacks/grids/anchors — which is almost always. The agent should describe STRUCTURE (rows, columns, what's near what), not pixel coordinates.\n\nELEMENT CHOICE: default content to typed NOTES (the only versioned, searchable tier agents can read back);
|
|
508
|
+
server.tool("cnotes_canvas_place", "Place items on a canvas using a DECLARATIVE LAYOUT (no x/y math). The server measures every item, packs them with no overlaps, and inserts them in a single batch. PREFER THIS over cnotes_canvas_bulk_add whenever you can express the layout as stacks/grids/anchors — which is almost always. The agent should describe STRUCTURE (rows, columns, what's near what), not pixel coordinates.\n\nELEMENT CHOICE: default content to typed NOTES (the only versioned, searchable tier agents can read back); richtext = the Text element, card-sized framing only (a heading is just content '# Title', untinted), section = a named region frame, list = member notes behind a one-line frame, edges connect notes only.\n\nThe call is best-effort, NOT atomic: 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` in the response.\n\nThe `doc` argument is a layout document:\n { root: <LayoutNode>, edges?: [{from, to, label?}], origin?: {x, y} }\n\n<LayoutNode> is one of:\n • { kind: 'stack', axis: 'vertical'|'horizontal', gap?: 'tight'|'medium'|'spacious'|<px>, align?: 'start'|'center'|'end', items: [<LayoutNode>...] }\n • { kind: 'grid', columns: <n>, gap?: ..., items: [<LayoutNode>...] }\n • { kind: 'anchor', to: '<displayId or canvas node id>', direction: 'right'|'left'|'above'|'below', gap?: ..., child: <LayoutNode> }\n • { kind: 'item', type: 'note'|'richtext'|'list'|'canvas'|'section', key?: '<name>', ...item-specific fields }\n\nLeaf item shapes (no x/y!):\n note: { kind:'item', type:'note', noteId:'NOTE-3', key?:'a' }\n richtext: { kind:'item', type:'richtext', content:'# Markdown OK', size?:'small'|'medium'|'large', colorHex?:'<hex>' } // the Text element: card-sized framing only; a heading is just content '# Title' with no colorHex; plain markdown, NO [NOTE-X](relationship:...) mention syntax (corrupts the card); knowledge worth keeping = a note\n list: { kind:'item', type:'list', description:'One-line frame — first paragraph becomes the title', noteIds?:['NOTE-1','NOTE-2'], viewMode?:'list'|'grid' } // description is REQUIRED and is a frame, never the content: N distinct things = N notes in noteIds\n canvas: { kind:'item', type:'canvas', linkedCanvasId:'<id>' }\n\nEdges reference endpoints by, in priority order: `@<key>` (any item with a `key` placed in the same call), the original noteId/displayId of a note placed in this call (e.g. 'NOTE-7'), or the displayId / convex id of a node already on the canvas.\n\nExample (a SWOT-like quadrant):\n {\n root: {\n kind:'stack', axis:'vertical', gap:'spacious',\n items:[\n { kind:'stack', axis:'horizontal', gap:'spacious', items:[\n { kind:'grid', columns:2, items:[\n { kind:'item', type:'note', noteId:'STR-1' },\n { kind:'item', type:'note', noteId:'STR-2' },\n ]},\n { kind:'grid', columns:2, items:[\n { kind:'item', type:'note', noteId:'WEAK-1' },\n ]},\n ]},\n { kind:'stack', axis:'horizontal', gap:'spacious', items:[ /* opportunities + threats */ ]},\n ],\n },\n }", {
|
|
518
509
|
canvasId: z.string().describe("Canvas ID"),
|
|
519
510
|
doc: z
|
|
520
511
|
.object({
|
|
521
512
|
root: z
|
|
522
513
|
.unknown()
|
|
523
|
-
.describe("Root LayoutNode. kind is one of: 'stack' (items flow along one axis, like CSS flexbox: {kind:'stack', axis:'vertical'|'horizontal', gap?, align?:'start'|'center'|'end', items:[LayoutNode...]}), 'grid' (N columns, items wrap to new rows: {kind:'grid', columns:<n>, gap?, items:[...]}), 'anchor' (place a sub-tree relative to an EXISTING canvas node: {kind:'anchor', to:'<displayId or node id>', direction:'right'|'left'|'above'|'below' — exactly these four, NOT top/bottom/up/down, gap?, child:LayoutNode}), 'section' (a named frame that sizes itself around its content: {kind:'section', name:'<title>', key?, child:LayoutNode}), or 'item' (a leaf — one per visible thing, NO x/y: {kind:'item', type:'note'|'
|
|
514
|
+
.describe("Root LayoutNode. kind is one of: 'stack' (items flow along one axis, like CSS flexbox: {kind:'stack', axis:'vertical'|'horizontal', gap?, align?:'start'|'center'|'end', items:[LayoutNode...]}), 'grid' (N columns, items wrap to new rows: {kind:'grid', columns:<n>, gap?, items:[...]}), 'anchor' (place a sub-tree relative to an EXISTING canvas node: {kind:'anchor', to:'<displayId or node id>', direction:'right'|'left'|'above'|'below' — exactly these four, NOT top/bottom/up/down, gap?, child:LayoutNode}), 'section' (a named frame that sizes itself around its content: {kind:'section', name:'<title>', key?, child:LayoutNode}), or 'item' (a leaf — one per visible thing, NO x/y: {kind:'item', type:'note'|'richtext'|'list'|'canvas'|'section', key?, ...type-specific fields}). gap accepts a preset 'tight'|'medium'|'spacious' or a raw pixel number (default 'medium')."),
|
|
524
515
|
edges: z
|
|
525
516
|
.array(z.unknown())
|
|
526
517
|
.optional()
|
|
@@ -540,11 +531,46 @@ server.tool("cnotes_canvas_place", "Place items on a canvas using a DECLARATIVE
|
|
|
540
531
|
action: "placeLayout",
|
|
541
532
|
doc,
|
|
542
533
|
})));
|
|
543
|
-
server.tool("
|
|
534
|
+
server.tool("cnotes_canvas_arrange", "Reflow EXISTING canvas nodes with the layout solver (no x/y math). Unlike cnotes_canvas_place (create-only), this MOVES nodes already on the canvas — mixed kinds welcome (note, canvas-link, richtext, list, legacy text). Prefer this over cnotes_canvas_bulk_move whenever you are reorganizing existing content into a grid/stack/section.\n\nLeaves in `doc` must be `{ kind:'item', type:'existing', nodeId:'<id or NOTE-n>', nodeType:'note'|'text'|'richtext'|'list'|'canvas' }`. Wrap them in a section container and pass `targetSectionId` (SECTION-<n>) to pack into that frame and auto-resize it in place — the SECTION-<n> id stays stable (never delete+re-add).\n\nConvenience shape for a section reflow:\n {\n targetSectionId: 'SECTION-90',\n autoResize: true,\n doc: {\n root: {\n kind: 'section', name: '<current name>', key: 'frame',\n child: {\n kind: 'grid', columns: 4, gap: 'medium',\n items: [\n { kind:'item', type:'existing', nodeId:'…', nodeType:'canvas' },\n { kind:'item', type:'existing', nodeId:'NOTE-3', nodeType:'note' }\n ]\n }\n }\n }\n }\n\nWhen targetSectionId is set and origin/placement are omitted, the server pins the layout to the frame's current origin (placement exact).", {
|
|
535
|
+
canvasId: z.string().describe("Canvas ID"),
|
|
536
|
+
doc: z
|
|
537
|
+
.object({
|
|
538
|
+
root: z.unknown().describe("Root LayoutNode. Same stack/grid/anchor/section primitives as place, but leaves must be type:'existing' (nodeId + nodeType). A section container sizes the target frame around the rearranged content."),
|
|
539
|
+
origin: z
|
|
540
|
+
.object({ x: z.number(), y: z.number() })
|
|
541
|
+
.optional()
|
|
542
|
+
.describe("Optional. Omit with targetSectionId to pin to the frame's current origin."),
|
|
543
|
+
placement: z
|
|
544
|
+
.enum(["auto", "append", "exact"])
|
|
545
|
+
.optional()
|
|
546
|
+
.describe("Defaults to exact (pinned) when targetSectionId is set and origin/placement are omitted."),
|
|
547
|
+
})
|
|
548
|
+
.passthrough()
|
|
549
|
+
.describe("Layout document with type:'existing' leaves."),
|
|
550
|
+
targetSectionId: z
|
|
551
|
+
.string()
|
|
552
|
+
.optional()
|
|
553
|
+
.describe("SECTION-<n> (or raw id) to pack into and auto-resize in place."),
|
|
554
|
+
autoResize: z
|
|
555
|
+
.boolean()
|
|
556
|
+
.optional()
|
|
557
|
+
.describe("Resize the target section to the solved frame bbox. Defaults true when targetSectionId is set."),
|
|
558
|
+
dryRun: z
|
|
559
|
+
.boolean()
|
|
560
|
+
.optional()
|
|
561
|
+
.describe("Solve and preview without moving anything."),
|
|
562
|
+
}, async ({ canvasId, doc, targetSectionId, autoResize, dryRun }) => withErrorHandling(() => api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(workspaceId)}`, {
|
|
563
|
+
action: "arrangeLayout",
|
|
564
|
+
doc,
|
|
565
|
+
...(targetSectionId ? { targetSectionId } : {}),
|
|
566
|
+
...(autoResize !== undefined ? { autoResize } : {}),
|
|
567
|
+
...(dryRun ? { dryRun: true } : {}),
|
|
568
|
+
})));
|
|
569
|
+
server.tool("cnotes_canvas_bulk_move", "Move multiple nodes to new positions in one operation. More efficient than calling cnotes_canvas_move_node multiple times. Prefer cnotes_canvas_arrange when reorganizing into a grid/stack/section — that path measures cards and auto-resizes frames.", {
|
|
544
570
|
canvasId: z.string().describe("Canvas ID"),
|
|
545
571
|
moves: z.array(z.object({
|
|
546
572
|
nodeId: z.string().describe("Canvas node ID"),
|
|
547
|
-
nodeType: z.enum(["note", "text", "list", "canvas", "richtext", "section"]).optional().default("note").describe("Node type"),
|
|
573
|
+
nodeType: z.enum(["note", "text", "list", "canvas", "richtext", "section"]).optional().default("note").describe("Node type. 'text' is a LEGACY kind (old heading nodes) — still movable/deletable, but no longer creatable."),
|
|
548
574
|
x: z.number().describe("New X position"),
|
|
549
575
|
y: z.number().describe("New Y position"),
|
|
550
576
|
})).describe("Array of node moves"),
|
|
@@ -617,10 +643,44 @@ server.tool("cnotes_canvas_update_edge", "Update an edge: change its label, togg
|
|
|
617
643
|
body.reverse = reverse;
|
|
618
644
|
return api.post(`/api/canvas/${canvasId}`, body);
|
|
619
645
|
}));
|
|
646
|
+
server.tool("cnotes_canvas_update_section", "Resize, rename, move, or recolor an existing section frame IN PLACE, keeping its SECTION-<n> id. Use this to grow a frame around new content — never delete + re-add a section, which mints a NEW id and breaks every [[SECTION-<n>]] mention pointing at the old one. Only the fields you pass are changed.", {
|
|
647
|
+
canvasId: z.string().describe("Canvas ID"),
|
|
648
|
+
sectionId: z
|
|
649
|
+
.string()
|
|
650
|
+
.describe("Section to update — SECTION-<n> display ID or a raw node ID"),
|
|
651
|
+
name: z.string().optional().describe("New frame title"),
|
|
652
|
+
x: z.number().optional().describe("New X position (top-left)"),
|
|
653
|
+
y: z.number().optional().describe("New Y position (top-left)"),
|
|
654
|
+
width: z.number().optional().describe("New frame width in px"),
|
|
655
|
+
height: z.number().optional().describe("New frame height in px"),
|
|
656
|
+
colorHex: z
|
|
657
|
+
.string()
|
|
658
|
+
.optional()
|
|
659
|
+
.describe("New frame tint hex, or '' to reset to the default"),
|
|
660
|
+
}, async ({ canvasId, sectionId, name, x, y, width, height, colorHex }) => withErrorHandling(() => {
|
|
661
|
+
const body = {
|
|
662
|
+
action: "updateSectionNode",
|
|
663
|
+
nodeId: sectionId,
|
|
664
|
+
};
|
|
665
|
+
if (name !== undefined)
|
|
666
|
+
body.name = name;
|
|
667
|
+
if (x !== undefined)
|
|
668
|
+
body.positionX = x;
|
|
669
|
+
if (y !== undefined)
|
|
670
|
+
body.positionY = y;
|
|
671
|
+
if (width !== undefined)
|
|
672
|
+
body.width = width;
|
|
673
|
+
if (height !== undefined)
|
|
674
|
+
body.height = height;
|
|
675
|
+
if (colorHex !== undefined)
|
|
676
|
+
body.colorHex = colorHex;
|
|
677
|
+
// workspaceId lets the server resolve a CANVAS-<n> handle, not just a raw id.
|
|
678
|
+
return api.post(`/api/canvas/${encodeURIComponent(canvasId)}?workspaceId=${encodeURIComponent(workspaceId)}`, body);
|
|
679
|
+
}));
|
|
620
680
|
server.tool("cnotes_canvas_move_node", "Move a node to a new position on a canvas.", {
|
|
621
681
|
canvasId: z.string().describe("Canvas ID"),
|
|
622
682
|
nodeId: z.string().describe("Canvas node ID"),
|
|
623
|
-
nodeType: z.enum(["note", "text", "list", "canvas", "richtext", "section"]).optional().default("note").describe("Node type"),
|
|
683
|
+
nodeType: z.enum(["note", "text", "list", "canvas", "richtext", "section"]).optional().default("note").describe("Node type. 'text' is a LEGACY kind (old heading nodes) — still movable/deletable, but no longer creatable."),
|
|
624
684
|
x: z.number().describe("New X position"),
|
|
625
685
|
y: z.number().describe("New Y position"),
|
|
626
686
|
}, async ({ canvasId, nodeId, nodeType, x, y }) => withErrorHandling(() => api.post(`/api/canvas/${canvasId}`, {
|
|
@@ -644,13 +704,13 @@ server.tool("cnotes_canvas_remove_edge", "Remove an edge from a canvas.", {
|
|
|
644
704
|
action: "removeEdge",
|
|
645
705
|
edgeId,
|
|
646
706
|
})));
|
|
647
|
-
server.tool("cnotes_canvas_digest", "Get the AI-generated digest of a canvas: narrative summary, key themes, and a per-note summary list
|
|
707
|
+
server.tool("cnotes_canvas_digest", "Get the AI-generated digest of a canvas: narrative summary, key themes, and a per-note summary list. Run this FIRST when starting on any canvas — full orientation in one call, so you don't read each note individually. The notes list is always populated even when digest status is 'none' or 'stale' (a null per-note summary just means it hasn't generated yet).", {
|
|
648
708
|
canvasId: z.string().describe("Canvas ID"),
|
|
649
709
|
}, async ({ canvasId }) => withErrorHandling(() => api.get(`/api/canvas/${encodeURIComponent(canvasId)}/digest`, { workspaceId })));
|
|
650
710
|
server.tool("cnotes_canvas_digest_generate", "Request digest (re)generation for a canvas. Use when digest status is 'none' or you want a fresh summary. The digest is generated async — poll cnotes_canvas_digest after ~60s.", {
|
|
651
711
|
canvasId: z.string().describe("Canvas ID"),
|
|
652
712
|
}, async ({ canvasId }) => withErrorHandling(() => api.post(`/api/canvas/${encodeURIComponent(canvasId)}/digest?workspaceId=${encodeURIComponent(workspaceId)}`, {})));
|
|
653
|
-
server.tool("cnotes_canvas_activity", "Show recent activity on a canvas — edits, agent runs, gestures, restores
|
|
713
|
+
server.tool("cnotes_canvas_activity", "Show recent activity on a canvas — edits, agent runs, gestures, restores, newest first. Use it to see what changed and who changed it before further edits, or to confirm a previous agent run applied. Returns { canvasName, operations[] }, each operation carrying its type, actor (human/agent/import/api), status, prompt, summary, and timestamps.", {
|
|
654
714
|
canvasId: z.string().describe("Canvas ID"),
|
|
655
715
|
limit: z.number().int().min(1).max(200).optional().describe("Max entries (default 50, max 200)"),
|
|
656
716
|
}, async ({ canvasId, limit }) => {
|
|
@@ -777,33 +837,6 @@ server.tool("cnotes_relationships_list", "List note relationships in the workspa
|
|
|
777
837
|
displayId: noteDisplayId,
|
|
778
838
|
})));
|
|
779
839
|
// ---------------------------------------------------------------------------
|
|
780
|
-
// Memory (Zep)
|
|
781
|
-
// ---------------------------------------------------------------------------
|
|
782
|
-
server.tool("cnotes_memory_query", "Search facts and entities in the knowledge graph by relevance.", {
|
|
783
|
-
query: z.string().describe("Search query"),
|
|
784
|
-
limit: z.number().optional().default(10).describe("Max results per category"),
|
|
785
|
-
}, async ({ query, limit }) => withErrorHandling(() => api.post("/api/zep/query", {
|
|
786
|
-
workspaceId,
|
|
787
|
-
query,
|
|
788
|
-
limit: limit ?? 10,
|
|
789
|
-
})));
|
|
790
|
-
server.tool("cnotes_memory_facts", "List all workspace facts from the knowledge graph.", {}, async () => withErrorHandling(async () => {
|
|
791
|
-
const data = await api.post("/api/zep/query", {
|
|
792
|
-
workspaceId,
|
|
793
|
-
query: "all facts",
|
|
794
|
-
limit: 1,
|
|
795
|
-
});
|
|
796
|
-
return data.allFacts;
|
|
797
|
-
}));
|
|
798
|
-
server.tool("cnotes_memory_entities", "List all workspace entities (people, topics, orgs) from the knowledge graph.", {}, async () => withErrorHandling(async () => {
|
|
799
|
-
const data = await api.post("/api/zep/query", {
|
|
800
|
-
workspaceId,
|
|
801
|
-
query: "all entities",
|
|
802
|
-
limit: 1,
|
|
803
|
-
});
|
|
804
|
-
return data.allEntities;
|
|
805
|
-
}));
|
|
806
|
-
// ---------------------------------------------------------------------------
|
|
807
840
|
// Versions
|
|
808
841
|
// ---------------------------------------------------------------------------
|
|
809
842
|
server.tool("cnotes_versions_list", "List versions of a note.", {
|