clay-server 3.1.0-beta.1 → 3.1.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/lib/mates-prompts.js +3 -2
- package/lib/project-session-notes.js +6 -6
- package/lib/public/css/sticky-notes.css +25 -0
- package/lib/public/modules/sticky-note-markdown.js +89 -0
- package/lib/public/modules/sticky-notes.js +35 -103
- package/lib/session-notes-mcp-server.js +3 -3
- package/package.json +1 -1
package/lib/mates-prompts.js
CHANGED
|
@@ -45,8 +45,9 @@ var STICKY_NOTES_SECTION =
|
|
|
45
45
|
"\n\n" + STICKY_NOTES_MARKER + "\n" +
|
|
46
46
|
"## Sticky Notes\n\n" +
|
|
47
47
|
"**This section is managed by the system and cannot be removed.**\n\n" +
|
|
48
|
-
"Your `knowledge/sticky-notes.md` file
|
|
49
|
-
"
|
|
48
|
+
"Your `knowledge/sticky-notes.md` file is shared project memory that persists across sessions. " +
|
|
49
|
+
"It may contain checklists, to-do items, work goals, handoffs, unfinished work, durable decisions and constraints, or project knowledge that future sessions should remember. " +
|
|
50
|
+
"Read this file when starting a conversation and use relevant notes to recover the current context and next actions. " +
|
|
50
51
|
"These notes are read-only. You cannot create, update, or delete them.\n";
|
|
51
52
|
|
|
52
53
|
var PROJECT_REGISTRY_MARKER = "<!-- PROJECT_REGISTRY_MANAGED_BY_SYSTEM -->";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
var sessionNotesMcp = require("./session-notes-mcp-server");
|
|
2
2
|
|
|
3
|
-
var MAX_NOTE_TEXT_CHARS =
|
|
3
|
+
var MAX_NOTE_TEXT_CHARS = 20000;
|
|
4
4
|
var MAX_ACTIVE_NOTES = 20;
|
|
5
|
-
var MAX_PROMPT_CHARS =
|
|
6
|
-
var MAX_NOTE_PROMPT_CHARS =
|
|
7
|
-
var NOTES_LABEL = "--- Project sticky notes (
|
|
5
|
+
var MAX_PROMPT_CHARS = 4000;
|
|
6
|
+
var MAX_NOTE_PROMPT_CHARS = 800;
|
|
7
|
+
var NOTES_LABEL = "--- Project sticky notes (cross-session work memory; manage via clay-notes tools) ---";
|
|
8
8
|
var NOTES_HINT = "- More notes are available; call list_notes for the rest.";
|
|
9
|
-
var PROACTIVE_POLICY = "
|
|
9
|
+
var PROACTIVE_POLICY = "The board persists across sessions and is shared with the user and every Clay agent working on this project. People may use it freely. As a Clay agent, use it specifically as cross-session work memory: checklists and to-do lists, work goals, handoffs, unfinished work, durable decisions and constraints, and knowledge that should remain available after this session ends. Create or update a note proactively when the user asks for a handoff, establishes something future sessions must remember, or when preserving the current goal and next actions would make continuation easier. Put a concise plain-text title on the first line. Add a detailed body with the relevant background, rationale, decisions, completed work, current state, validation, next steps, blockers, and references. If an injected preview is relevant or truncated, call list_notes and read the full note before acting. Do not use notes as a transcript, routine progress log, or self-announcement. Keep one coherent topic per note and consolidate or remove stale notes instead of creating duplicates.";
|
|
10
10
|
var NOTE_COLORS = ["yellow", "blue", "green", "pink", "orange", "purple"];
|
|
11
11
|
|
|
12
12
|
function toolResult(value) {
|
|
@@ -128,7 +128,7 @@ function attachSessionNotes(ctx) {
|
|
|
128
128
|
function writeNote(args, caller) {
|
|
129
129
|
if (!caller) return toolError("write_note requires a session-bound tool server");
|
|
130
130
|
var rawText = typeof args.text === "string" ? args.text : "";
|
|
131
|
-
if (rawText.length > MAX_NOTE_TEXT_CHARS) return toolError("text exceeds
|
|
131
|
+
if (rawText.length > MAX_NOTE_TEXT_CHARS) return toolError("text exceeds " + MAX_NOTE_TEXT_CHARS + " characters");
|
|
132
132
|
var text = rawText.trim();
|
|
133
133
|
if (!text) return toolError("text is required");
|
|
134
134
|
var notes = nm.list() || [];
|
|
@@ -335,15 +335,37 @@
|
|
|
335
335
|
}
|
|
336
336
|
|
|
337
337
|
.sticky-note-rendered .sn-check {
|
|
338
|
+
display: inline-flex;
|
|
339
|
+
align-items: center;
|
|
340
|
+
justify-content: center;
|
|
341
|
+
width: 14px;
|
|
342
|
+
height: 14px;
|
|
338
343
|
font-size: 12px;
|
|
339
344
|
margin-right: 4px;
|
|
340
345
|
opacity: 0.6;
|
|
346
|
+
cursor: pointer;
|
|
347
|
+
user-select: none;
|
|
348
|
+
border-radius: 3px;
|
|
349
|
+
transition: opacity 0.15s, background 0.15s, transform 0.15s;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.sticky-note-rendered .sn-check:hover,
|
|
353
|
+
.sticky-note-rendered .sn-check:focus-visible {
|
|
354
|
+
opacity: 1;
|
|
355
|
+
background: rgba(0,0,0,0.07);
|
|
356
|
+
outline: none;
|
|
357
|
+
transform: scale(1.08);
|
|
341
358
|
}
|
|
342
359
|
|
|
343
360
|
.sticky-note-rendered .sn-check.checked {
|
|
344
361
|
opacity: 0.9;
|
|
345
362
|
}
|
|
346
363
|
|
|
364
|
+
.dark-theme .sticky-note-rendered .sn-check:hover,
|
|
365
|
+
.dark-theme .sticky-note-rendered .sn-check:focus-visible {
|
|
366
|
+
background: rgba(255,255,255,0.1);
|
|
367
|
+
}
|
|
368
|
+
|
|
347
369
|
/* --- Resize handle (bottom-right corner) --- */
|
|
348
370
|
.sticky-note-resize {
|
|
349
371
|
position: absolute;
|
|
@@ -649,9 +671,12 @@
|
|
|
649
671
|
font-weight: 400 !important;
|
|
650
672
|
color: var(--text-dimmer) !important;
|
|
651
673
|
margin-top: 6px !important;
|
|
674
|
+
max-width: 520px;
|
|
652
675
|
display: flex;
|
|
653
676
|
align-items: center;
|
|
677
|
+
justify-content: center;
|
|
654
678
|
gap: 4px;
|
|
679
|
+
line-height: 1.55;
|
|
655
680
|
}
|
|
656
681
|
|
|
657
682
|
.notes-archive-empty-sub .lucide {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Markdown helpers for sticky-note titles, handoff bodies, and checklists.
|
|
2
|
+
|
|
3
|
+
export function getTitle(text) {
|
|
4
|
+
if (!text) return "";
|
|
5
|
+
var idx = text.indexOf("\n");
|
|
6
|
+
return idx === -1 ? text : text.substring(0, idx);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function formatInline(text) {
|
|
10
|
+
var escaped = text
|
|
11
|
+
.replace(/&/g, "&")
|
|
12
|
+
.replace(/</g, "<")
|
|
13
|
+
.replace(/>/g, ">");
|
|
14
|
+
return escaped
|
|
15
|
+
.replace(/(https?:\/\/[^\s<]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>')
|
|
16
|
+
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
|
|
17
|
+
.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>")
|
|
18
|
+
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
|
19
|
+
.replace(/~~(.+?)~~/g, "<del>$1</del>")
|
|
20
|
+
.replace(/^(?:-\s*)?\[[xX]\]/gm, '<span class="sn-check checked" role="checkbox" aria-checked="true" tabindex="0" contenteditable="false">✓</span>')
|
|
21
|
+
.replace(/^(?:-\s*)?\[ \]/gm, '<span class="sn-check" role="checkbox" aria-checked="false" tabindex="0" contenteditable="false">☐</span>')
|
|
22
|
+
.replace(/\n/g, "<br>");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function renderMiniMarkdown(text) {
|
|
26
|
+
if (!text) return "";
|
|
27
|
+
var lines = text.split("\n");
|
|
28
|
+
var title = lines[0];
|
|
29
|
+
var body = lines.slice(1).join("\n");
|
|
30
|
+
var html = '<div class="sn-title">' + formatInline(title) + "</div>";
|
|
31
|
+
if (body.trim()) html += formatInline(body);
|
|
32
|
+
return html;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function childrenToMarkdown(element) {
|
|
36
|
+
var result = "";
|
|
37
|
+
for (var i = 0; i < element.childNodes.length; i++) {
|
|
38
|
+
result += nodeToMarkdown(element.childNodes[i]);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function nodeToMarkdown(node) {
|
|
44
|
+
if (node.nodeType === 3) return node.textContent;
|
|
45
|
+
if (node.nodeType !== 1) return "";
|
|
46
|
+
|
|
47
|
+
var tag = node.tagName;
|
|
48
|
+
var inner = childrenToMarkdown(node);
|
|
49
|
+
switch (tag) {
|
|
50
|
+
case "STRONG": case "B": return "**" + inner + "**";
|
|
51
|
+
case "EM": case "I": return "*" + inner + "*";
|
|
52
|
+
case "DEL": case "S": case "STRIKE": return "~~" + inner + "~~";
|
|
53
|
+
case "CODE": return "`" + inner + "`";
|
|
54
|
+
case "BR": return "\n";
|
|
55
|
+
case "DIV":
|
|
56
|
+
if (node.classList.contains("sn-title")) return inner;
|
|
57
|
+
if (node.classList.contains("sn-placeholder")) return "";
|
|
58
|
+
return "\n" + inner;
|
|
59
|
+
case "P": return "\n" + inner;
|
|
60
|
+
case "A": return node.getAttribute("href") || inner;
|
|
61
|
+
case "SPAN":
|
|
62
|
+
if (node.classList.contains("sn-check")) {
|
|
63
|
+
return node.classList.contains("checked") ? "- [x]" : "- [ ]";
|
|
64
|
+
}
|
|
65
|
+
if (node.classList.contains("sn-placeholder")) return "";
|
|
66
|
+
return inner;
|
|
67
|
+
default: return inner;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function extractMarkdown(rendered) {
|
|
72
|
+
var titleEl = rendered.querySelector(".sn-title");
|
|
73
|
+
if (titleEl) {
|
|
74
|
+
var titleMarkdown = childrenToMarkdown(titleEl);
|
|
75
|
+
var rest = "";
|
|
76
|
+
var afterTitle = false;
|
|
77
|
+
for (var i = 0; i < rendered.childNodes.length; i++) {
|
|
78
|
+
var child = rendered.childNodes[i];
|
|
79
|
+
if (child === titleEl) {
|
|
80
|
+
afterTitle = true;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (afterTitle) rest += nodeToMarkdown(child);
|
|
84
|
+
}
|
|
85
|
+
if (rest && rest.charAt(0) === "\n") rest = rest.substring(1);
|
|
86
|
+
return titleMarkdown + (rest ? "\n" + rest : "");
|
|
87
|
+
}
|
|
88
|
+
return childrenToMarkdown(rendered).replace(/^\n+/, "");
|
|
89
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { refreshIcons, iconHtml } from './icons.js';
|
|
2
2
|
import { VENDOR_AVATARS } from './app-rendering.js';
|
|
3
|
+
import { extractMarkdown, getTitle, renderMiniMarkdown } from './sticky-note-markdown.js';
|
|
3
4
|
|
|
4
5
|
var ctx;
|
|
5
6
|
var notes = new Map(); // id -> { data, el }
|
|
@@ -136,106 +137,11 @@ function debouncedTextUpdate(id, text) {
|
|
|
136
137
|
}, 500);
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
// --- Simple markdown ---
|
|
140
|
-
|
|
141
|
-
function getTitle(text) {
|
|
142
|
-
if (!text) return "";
|
|
143
|
-
var idx = text.indexOf("\n");
|
|
144
|
-
return idx === -1 ? text : text.substring(0, idx);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function renderMiniMarkdown(text) {
|
|
148
|
-
if (!text) return "";
|
|
149
|
-
var lines = text.split("\n");
|
|
150
|
-
var title = lines[0];
|
|
151
|
-
var body = lines.slice(1).join("\n");
|
|
152
|
-
|
|
153
|
-
function fmt(s) {
|
|
154
|
-
var escaped = s
|
|
155
|
-
.replace(/&/g, "&")
|
|
156
|
-
.replace(/</g, "<")
|
|
157
|
-
.replace(/>/g, ">");
|
|
158
|
-
return escaped
|
|
159
|
-
.replace(/(https?:\/\/[^\s<]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>')
|
|
160
|
-
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
|
|
161
|
-
.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/g, "<em>$1</em>")
|
|
162
|
-
.replace(/`([^`]+)`/g, "<code>$1</code>")
|
|
163
|
-
.replace(/~~(.+?)~~/g, "<del>$1</del>")
|
|
164
|
-
.replace(/^- \[x\]/gm, '<span class="sn-check checked">✓</span>')
|
|
165
|
-
.replace(/^- \[ \]/gm, '<span class="sn-check">☐</span>')
|
|
166
|
-
.replace(/\n/g, "<br>");
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
var html = '<div class="sn-title">' + fmt(title) + '</div>';
|
|
170
|
-
if (body.trim()) {
|
|
171
|
-
html += fmt(body);
|
|
172
|
-
}
|
|
173
|
-
return html;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
140
|
function syncTitle(noteEl, text) {
|
|
177
141
|
var spacer = noteEl.querySelector(".sticky-note-spacer");
|
|
178
142
|
if (spacer) spacer.textContent = getTitle(text) || "Untitled";
|
|
179
143
|
}
|
|
180
144
|
|
|
181
|
-
// --- HTML-to-Markdown reverse conversion (for contenteditable) ---
|
|
182
|
-
|
|
183
|
-
function nodeToMd(node) {
|
|
184
|
-
if (node.nodeType === 3) return node.textContent;
|
|
185
|
-
if (node.nodeType !== 1) return "";
|
|
186
|
-
|
|
187
|
-
var tag = node.tagName;
|
|
188
|
-
var inner = childrenToMd(node);
|
|
189
|
-
|
|
190
|
-
switch (tag) {
|
|
191
|
-
case "STRONG": case "B": return "**" + inner + "**";
|
|
192
|
-
case "EM": case "I": return "*" + inner + "*";
|
|
193
|
-
case "DEL": case "S": case "STRIKE": return "~~" + inner + "~~";
|
|
194
|
-
case "CODE": return "`" + inner + "`";
|
|
195
|
-
case "BR": return "\n";
|
|
196
|
-
case "DIV":
|
|
197
|
-
if (node.classList.contains("sn-title")) return inner;
|
|
198
|
-
if (node.classList.contains("sn-placeholder")) return "";
|
|
199
|
-
// Browser-generated div from Enter key = new line
|
|
200
|
-
return "\n" + inner;
|
|
201
|
-
case "P": return "\n" + inner;
|
|
202
|
-
case "A": return node.getAttribute("href") || inner;
|
|
203
|
-
case "SPAN":
|
|
204
|
-
if (node.classList.contains("sn-check")) {
|
|
205
|
-
return node.classList.contains("checked") ? "- [x]" : "- [ ]";
|
|
206
|
-
}
|
|
207
|
-
if (node.classList.contains("sn-placeholder")) return "";
|
|
208
|
-
return inner;
|
|
209
|
-
default: return inner;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function childrenToMd(el) {
|
|
214
|
-
var result = "";
|
|
215
|
-
for (var i = 0; i < el.childNodes.length; i++) {
|
|
216
|
-
result += nodeToMd(el.childNodes[i]);
|
|
217
|
-
}
|
|
218
|
-
return result;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function extractMdFromRendered(rendered) {
|
|
222
|
-
var titleEl = rendered.querySelector(".sn-title");
|
|
223
|
-
if (titleEl) {
|
|
224
|
-
var titleMd = childrenToMd(titleEl);
|
|
225
|
-
var rest = "";
|
|
226
|
-
var afterTitle = false;
|
|
227
|
-
for (var i = 0; i < rendered.childNodes.length; i++) {
|
|
228
|
-
var child = rendered.childNodes[i];
|
|
229
|
-
if (child === titleEl) { afterTitle = true; continue; }
|
|
230
|
-
if (afterTitle) rest += nodeToMd(child);
|
|
231
|
-
}
|
|
232
|
-
if (rest && rest.charAt(0) === "\n") rest = rest.substring(1);
|
|
233
|
-
return titleMd + (rest ? "\n" + rest : "");
|
|
234
|
-
}
|
|
235
|
-
var md = childrenToMd(rendered);
|
|
236
|
-
return md.replace(/^\n+/, "");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
145
|
// --- Note rendering ---
|
|
240
146
|
|
|
241
147
|
function renderNote(data) {
|
|
@@ -613,13 +519,29 @@ function setupTextEdit(textarea, rendered, noteId, mdBtn) {
|
|
|
613
519
|
var noteEl = textarea.closest(".sticky-note");
|
|
614
520
|
var mdMode = false;
|
|
615
521
|
|
|
522
|
+
function saveRenderedMarkdown() {
|
|
523
|
+
var markdown = extractMarkdown(rendered);
|
|
524
|
+
textarea.value = markdown;
|
|
525
|
+
debouncedTextUpdate(noteId, markdown);
|
|
526
|
+
syncTitle(noteEl, markdown);
|
|
527
|
+
rendered.classList.toggle("is-empty", !markdown.trim());
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function toggleChecklistItem(check) {
|
|
531
|
+
var checked = !check.classList.contains("checked");
|
|
532
|
+
check.classList.toggle("checked", checked);
|
|
533
|
+
check.textContent = checked ? "✓" : "☐";
|
|
534
|
+
check.setAttribute("aria-checked", checked ? "true" : "false");
|
|
535
|
+
saveRenderedMarkdown();
|
|
536
|
+
}
|
|
537
|
+
|
|
616
538
|
// MD button: toggle between contenteditable rendered view and raw textarea
|
|
617
539
|
mdBtn.addEventListener("click", function (e) {
|
|
618
540
|
e.stopPropagation();
|
|
619
541
|
mdMode = !mdMode;
|
|
620
542
|
if (mdMode) {
|
|
621
543
|
// Switch to raw markdown editing
|
|
622
|
-
var md =
|
|
544
|
+
var md = extractMarkdown(rendered);
|
|
623
545
|
textarea.value = md;
|
|
624
546
|
textarea.style.display = "";
|
|
625
547
|
rendered.style.display = "none";
|
|
@@ -646,11 +568,15 @@ function setupTextEdit(textarea, rendered, noteId, mdBtn) {
|
|
|
646
568
|
|
|
647
569
|
// Sync contenteditable changes to textarea (data store) and save
|
|
648
570
|
rendered.addEventListener("input", function () {
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
571
|
+
saveRenderedMarkdown();
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
rendered.addEventListener("click", function (e) {
|
|
575
|
+
var check = e.target.closest && e.target.closest(".sn-check");
|
|
576
|
+
if (!check || !rendered.contains(check)) return;
|
|
577
|
+
e.preventDefault();
|
|
578
|
+
e.stopPropagation();
|
|
579
|
+
toggleChecklistItem(check);
|
|
654
580
|
});
|
|
655
581
|
|
|
656
582
|
// On blur, re-render to normalize HTML structure
|
|
@@ -658,7 +584,7 @@ function setupTextEdit(textarea, rendered, noteId, mdBtn) {
|
|
|
658
584
|
// Don't re-render if clicking format toolbar (it prevents default, but just in case)
|
|
659
585
|
if (e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest(".sn-format-toolbar")) return;
|
|
660
586
|
closeFormatToolbar();
|
|
661
|
-
var md =
|
|
587
|
+
var md = extractMarkdown(rendered);
|
|
662
588
|
textarea.value = md;
|
|
663
589
|
if (md.trim()) {
|
|
664
590
|
rendered.innerHTML = renderMiniMarkdown(md);
|
|
@@ -696,6 +622,12 @@ function setupTextEdit(textarea, rendered, noteId, mdBtn) {
|
|
|
696
622
|
|
|
697
623
|
// Insert <br> on Enter instead of <div>
|
|
698
624
|
rendered.addEventListener("keydown", function (e) {
|
|
625
|
+
if (e.target.classList && e.target.classList.contains("sn-check") &&
|
|
626
|
+
(e.key === "Enter" || e.key === " ")) {
|
|
627
|
+
e.preventDefault();
|
|
628
|
+
toggleChecklistItem(e.target);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
699
631
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
700
632
|
e.preventDefault();
|
|
701
633
|
document.execCommand("insertLineBreak");
|
|
@@ -979,7 +911,7 @@ function renderArchiveCards() {
|
|
|
979
911
|
if (notes.size === 0) {
|
|
980
912
|
var empty = document.createElement("div");
|
|
981
913
|
empty.className = "notes-archive-empty";
|
|
982
|
-
empty.innerHTML = iconHtml("sticky-note") + "<p>
|
|
914
|
+
empty.innerHTML = iconHtml("sticky-note") + "<p>Keep what matters across sessions</p><p class=\"notes-archive-empty-sub\">Use sticky notes for checklists, goals, handoffs, and durable project knowledge. You and every Clay agent can find them in future sessions.</p><p class=\"notes-archive-empty-sub\">Create one with the " + iconHtml("sticky-note") + " button in the title bar</p>";
|
|
983
915
|
grid.appendChild(empty);
|
|
984
916
|
refreshIcons();
|
|
985
917
|
return;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var buildShape = require("./session-spawn-mcp-server").buildShape;
|
|
4
4
|
|
|
5
|
-
var MEMORY_CONTRACT = "Shared project memory
|
|
5
|
+
var MEMORY_CONTRACT = "Shared project memory that persists across sessions and is visible to the user and other Clay agents. People may use the board freely. As a Clay agent, use notes specifically for actionable or durable work memory: checklists and to-do lists, work goals, handoffs, unfinished work, durable decisions and constraints, and knowledge that should still be available after the current session ends. Every note starts with a concise plain-text title on the first line. Add a detailed body whenever another worker would otherwise need to reconstruct the context. Keep one coherent topic per note, update an existing note instead of adding a near-duplicate, and delete notes that stop being true. Do not use notes as a transcript, a routine progress log, or an announcement of your own activity.";
|
|
6
6
|
|
|
7
7
|
function getToolDefs(handlers) {
|
|
8
8
|
return [
|
|
@@ -14,10 +14,10 @@ function getToolDefs(handlers) {
|
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
name: "write_note",
|
|
17
|
-
description: MEMORY_CONTRACT + " Create a new note, or update an existing note by id. Long
|
|
17
|
+
description: MEMORY_CONTRACT + " Create a new note, or update an existing note by id. Long task and handoff notes are allowed, with a generous 20000-character abuse guard.",
|
|
18
18
|
inputSchema: buildShape({
|
|
19
19
|
id: { type: "string", description: "Existing note id to update. Omit to create a note." },
|
|
20
|
-
text: { type: "string", description: "Sticky-note text.
|
|
20
|
+
text: { type: "string", description: "Sticky-note text. Put the title on the first line, followed by the detailed handoff body. The board permits up to 20000 characters." },
|
|
21
21
|
color: { type: "string", enum: ["yellow", "blue", "green", "pink", "orange", "purple"], description: "Optional sticky-note color." },
|
|
22
22
|
}, ["text"]),
|
|
23
23
|
handler: function (args) { return handlers.write(args || {}); },
|
package/package.json
CHANGED