clay-server 2.26.0-beta.1 → 2.26.0-beta.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 (42) hide show
  1. package/bin/cli.js +5 -9
  2. package/lib/browser-mcp-server.js +496 -0
  3. package/lib/daemon.js +1 -1
  4. package/lib/os-users.js +23 -0
  5. package/lib/project-debate.js +243 -95
  6. package/lib/project-mate-interaction.js +766 -0
  7. package/lib/project-memory.js +677 -0
  8. package/lib/project.js +546 -1361
  9. package/lib/public/app.js +817 -175
  10. package/lib/public/css/debate.css +224 -2
  11. package/lib/public/css/icon-strip.css +10 -10
  12. package/lib/public/css/input.css +296 -83
  13. package/lib/public/css/mates.css +56 -57
  14. package/lib/public/css/mention.css +7 -4
  15. package/lib/public/css/menus.css +7 -0
  16. package/lib/public/css/messages.css +17 -0
  17. package/lib/public/css/mobile-nav.css +3 -1
  18. package/lib/public/css/overlays.css +181 -0
  19. package/lib/public/css/rewind.css +79 -0
  20. package/lib/public/css/server-settings.css +1 -0
  21. package/lib/public/css/sidebar.css +10 -0
  22. package/lib/public/css/title-bar.css +189 -3
  23. package/lib/public/index.html +53 -16
  24. package/lib/public/modules/context-sources.js +328 -0
  25. package/lib/public/modules/debate.js +184 -97
  26. package/lib/public/modules/input.js +18 -1
  27. package/lib/public/modules/mate-knowledge.js +11 -11
  28. package/lib/public/modules/mate-memory.js +5 -5
  29. package/lib/public/modules/mate-sidebar.js +13 -9
  30. package/lib/public/modules/mention.js +40 -2
  31. package/lib/public/modules/notifications.js +109 -1
  32. package/lib/public/modules/rewind.js +36 -0
  33. package/lib/public/modules/sidebar.js +107 -28
  34. package/lib/public/modules/terminal.js +8 -0
  35. package/lib/public/modules/theme.js +2 -1
  36. package/lib/public/modules/tools.js +69 -24
  37. package/lib/sdk-bridge.js +81 -7
  38. package/lib/sdk-worker.js +13 -1
  39. package/lib/server.js +42 -0
  40. package/lib/sessions.js +39 -7
  41. package/lib/terminal-manager.js +36 -6
  42. package/package.json +2 -2
@@ -0,0 +1,328 @@
1
+ // Context Sources — attach terminal output and browser tabs as context for Claude
2
+
3
+ var ctx = null;
4
+ var activeSourceIds = new Set();
5
+ var terminalList = []; // synced from terminal module's term_list
6
+ var browserTabList = []; // synced from Chrome extension via postMessage
7
+
8
+ export function initContextSources(_ctx) {
9
+ ctx = _ctx;
10
+
11
+ var addBtn = document.getElementById("context-sources-add");
12
+ var picker = document.getElementById("context-sources-picker");
13
+
14
+ addBtn.addEventListener("click", function(e) {
15
+ e.stopPropagation();
16
+ if (picker.classList.contains("hidden")) {
17
+ renderPicker();
18
+ picker.classList.remove("hidden");
19
+ document.addEventListener("click", closePicker, true);
20
+ } else {
21
+ closePicker();
22
+ }
23
+ });
24
+
25
+ picker.addEventListener("click", function(e) {
26
+ e.stopPropagation();
27
+ });
28
+ }
29
+
30
+ function closePicker() {
31
+ var picker = document.getElementById("context-sources-picker");
32
+ picker.classList.add("hidden");
33
+ document.removeEventListener("click", closePicker, true);
34
+ }
35
+
36
+ // Restore state from server
37
+ export function handleContextSourcesState(msg) {
38
+ var saved = msg.active || [];
39
+ activeSourceIds = new Set(saved);
40
+ renderChips();
41
+ }
42
+
43
+ // Save active sources to server
44
+ function saveToServer() {
45
+ if (ctx && ctx.ws && ctx.connected) {
46
+ ctx.ws.send(JSON.stringify({
47
+ type: "context_sources_save",
48
+ active: Array.from(activeSourceIds)
49
+ }));
50
+ }
51
+ }
52
+
53
+ // Called when term_list arrives from server
54
+ export function updateTerminalList(terminals) {
55
+ terminalList = terminals || [];
56
+
57
+ // Remove active sources that no longer exist
58
+ var changed = false;
59
+ for (var id of activeSourceIds) {
60
+ if (id.startsWith("term:")) {
61
+ var termId = parseInt(id.split(":")[1], 10);
62
+ var found = false;
63
+ for (var i = 0; i < terminalList.length; i++) {
64
+ if (terminalList[i].id === termId) { found = true; break; }
65
+ }
66
+ if (!found) {
67
+ activeSourceIds.delete(id);
68
+ changed = true;
69
+ }
70
+ }
71
+ }
72
+
73
+ if (changed) saveToServer();
74
+ renderChips();
75
+
76
+ // If picker is open, re-render it
77
+ var picker = document.getElementById("context-sources-picker");
78
+ if (!picker.classList.contains("hidden")) {
79
+ renderPicker();
80
+ }
81
+ }
82
+
83
+ // Called when Chrome extension sends tab list via postMessage
84
+ export function updateBrowserTabList(tabs) {
85
+ browserTabList = tabs || [];
86
+
87
+ // Remove active tab sources that no longer exist
88
+ var changed = false;
89
+ for (var id of activeSourceIds) {
90
+ if (id.startsWith("tab:")) {
91
+ var tabId = parseInt(id.split(":")[1], 10);
92
+ var found = false;
93
+ for (var i = 0; i < browserTabList.length; i++) {
94
+ if (browserTabList[i].id === tabId) { found = true; break; }
95
+ }
96
+ if (!found) {
97
+ activeSourceIds.delete(id);
98
+ changed = true;
99
+ }
100
+ }
101
+ }
102
+
103
+ if (changed) saveToServer();
104
+ renderChips();
105
+
106
+ // If picker is open, re-render it
107
+ var picker = document.getElementById("context-sources-picker");
108
+ if (!picker.classList.contains("hidden")) {
109
+ renderPicker();
110
+ }
111
+ }
112
+
113
+ function toggleSource(sourceId) {
114
+ if (activeSourceIds.has(sourceId)) {
115
+ activeSourceIds.delete(sourceId);
116
+ } else {
117
+ activeSourceIds.add(sourceId);
118
+ }
119
+ saveToServer();
120
+ renderChips();
121
+ renderPicker();
122
+ }
123
+
124
+ function removeSource(sourceId) {
125
+ activeSourceIds.delete(sourceId);
126
+ saveToServer();
127
+ renderChips();
128
+
129
+ var picker = document.getElementById("context-sources-picker");
130
+ if (!picker.classList.contains("hidden")) {
131
+ renderPicker();
132
+ }
133
+ }
134
+
135
+ function renderChips() {
136
+ var container = document.getElementById("context-sources-chips");
137
+ container.innerHTML = "";
138
+
139
+ for (var id of activeSourceIds) {
140
+ var chip = document.createElement("div");
141
+ chip.className = "context-chip";
142
+
143
+ var label = getSourceLabel(id);
144
+ var iconName = getSourceIcon(id);
145
+
146
+ var labelEl = document.createElement("span");
147
+ labelEl.className = "context-chip-label";
148
+ labelEl.innerHTML =
149
+ '<i data-lucide="' + iconName + '"></i>' +
150
+ '<span>' + escapeHtml(label) + '</span>';
151
+ chip.appendChild(labelEl);
152
+
153
+ var removeBtn = document.createElement("button");
154
+ removeBtn.type = "button";
155
+ removeBtn.className = "context-chip-remove";
156
+ removeBtn.title = "Remove";
157
+ removeBtn.innerHTML = '<i data-lucide="minus"></i>';
158
+ removeBtn.setAttribute("data-source-id", id);
159
+ removeBtn.addEventListener("click", function(e) {
160
+ e.stopPropagation();
161
+ removeSource(this.getAttribute("data-source-id"));
162
+ if (typeof lucide !== "undefined") lucide.createIcons();
163
+ });
164
+
165
+ chip.appendChild(removeBtn);
166
+ container.appendChild(chip);
167
+ }
168
+
169
+ // Update add button label
170
+ var addBtn = document.getElementById("context-sources-add");
171
+ var labelSpan = addBtn.querySelector("span");
172
+ if (activeSourceIds.size > 0) {
173
+ labelSpan.textContent = "";
174
+ labelSpan.style.display = "none";
175
+ } else {
176
+ labelSpan.textContent = "Context Sources";
177
+ labelSpan.style.display = "";
178
+ }
179
+
180
+ if (typeof lucide !== "undefined") lucide.createIcons();
181
+ }
182
+
183
+ function renderPicker() {
184
+ // --- Terminals section ---
185
+ var termSection = document.getElementById("context-picker-terminals");
186
+ termSection.innerHTML = "";
187
+
188
+ var termLabel = document.createElement("div");
189
+ termLabel.className = "context-picker-section-label";
190
+ termLabel.textContent = "Terminals";
191
+ termSection.appendChild(termLabel);
192
+
193
+ if (terminalList.length === 0) {
194
+ var termEmpty = document.createElement("div");
195
+ termEmpty.className = "context-picker-empty";
196
+ termEmpty.textContent = "No terminals open";
197
+ termSection.appendChild(termEmpty);
198
+ } else {
199
+ for (var i = 0; i < terminalList.length; i++) {
200
+ var term = terminalList[i];
201
+ var termSourceId = "term:" + term.id;
202
+ var termActive = activeSourceIds.has(termSourceId);
203
+
204
+ var termItem = document.createElement("div");
205
+ termItem.className = "context-picker-item" + (termActive ? " active" : "");
206
+ termItem.setAttribute("data-source-id", termSourceId);
207
+
208
+ termItem.innerHTML =
209
+ '<i data-lucide="square-terminal"></i>' +
210
+ '<span>' + escapeHtml(term.title || ("Terminal " + term.id)) + '</span>' +
211
+ '<i data-lucide="check" class="context-picker-check"></i>';
212
+
213
+ termItem.addEventListener("click", function() {
214
+ toggleSource(this.getAttribute("data-source-id"));
215
+ if (typeof lucide !== "undefined") lucide.createIcons();
216
+ });
217
+
218
+ termSection.appendChild(termItem);
219
+ }
220
+ }
221
+
222
+ // --- Browser Tabs section ---
223
+ var tabSection = document.getElementById("context-picker-tabs");
224
+ tabSection.innerHTML = "";
225
+
226
+ var tabLabel = document.createElement("div");
227
+ tabLabel.className = "context-picker-section-label";
228
+ tabLabel.textContent = "Browser Tabs";
229
+ tabSection.appendChild(tabLabel);
230
+
231
+ if (browserTabList.length === 0) {
232
+ // Extension not connected: show notice with setup button
233
+ var notice = document.createElement("div");
234
+ notice.className = "context-picker-ext-notice";
235
+ notice.innerHTML =
236
+ '<span class="context-picker-ext-notice-text">Chrome extension required to access browser tabs.</span>' +
237
+ '<button class="context-picker-ext-btn" type="button"><i data-lucide="puzzle"></i> Setup Extension</button>';
238
+ var setupBtn = notice.querySelector(".context-picker-ext-btn");
239
+ setupBtn.addEventListener("click", function (e) {
240
+ e.stopPropagation();
241
+ closePicker();
242
+ var extPill = document.getElementById("ext-pill");
243
+ if (extPill) extPill.click();
244
+ });
245
+ tabSection.appendChild(notice);
246
+ } else {
247
+ for (var j = 0; j < browserTabList.length; j++) {
248
+ var tab = browserTabList[j];
249
+ var tabSourceId = "tab:" + tab.id;
250
+ var tabActive = activeSourceIds.has(tabSourceId);
251
+
252
+ var tabItem = document.createElement("div");
253
+ tabItem.className = "context-picker-item" + (tabActive ? " active" : "");
254
+ tabItem.setAttribute("data-source-id", tabSourceId);
255
+
256
+ var tabTitle = tab.title || tab.url || "Tab";
257
+ // Truncate long URLs for display
258
+ var tabDisplay = tabTitle.length > 50 ? tabTitle.slice(0, 47) + "..." : tabTitle;
259
+
260
+ var faviconHtml = "";
261
+ if (tab.favIconUrl) {
262
+ faviconHtml = '<img src="' + escapeHtml(tab.favIconUrl) + '" class="context-picker-favicon" onerror="this.style.display=\'none\';this.nextElementSibling.style.display=\'\'">' +
263
+ '<i data-lucide="globe" style="display:none"></i>';
264
+ } else {
265
+ faviconHtml = '<i data-lucide="globe"></i>';
266
+ }
267
+
268
+ tabItem.innerHTML =
269
+ faviconHtml +
270
+ '<span title="' + escapeHtml(tab.url || "") + '">' + escapeHtml(tabDisplay) + '</span>' +
271
+ '<i data-lucide="check" class="context-picker-check"></i>';
272
+
273
+ tabItem.addEventListener("click", function() {
274
+ toggleSource(this.getAttribute("data-source-id"));
275
+ if (typeof lucide !== "undefined") lucide.createIcons();
276
+ });
277
+
278
+ tabSection.appendChild(tabItem);
279
+ }
280
+ }
281
+
282
+ if (typeof lucide !== "undefined") lucide.createIcons();
283
+ }
284
+
285
+ function getSourceLabel(id) {
286
+ if (id.startsWith("term:")) {
287
+ var termId = parseInt(id.split(":")[1], 10);
288
+ for (var i = 0; i < terminalList.length; i++) {
289
+ if (terminalList[i].id === termId) {
290
+ return terminalList[i].title || ("Terminal " + termId);
291
+ }
292
+ }
293
+ return "Terminal " + termId;
294
+ }
295
+ if (id.startsWith("tab:")) {
296
+ var tabId = parseInt(id.split(":")[1], 10);
297
+ for (var j = 0; j < browserTabList.length; j++) {
298
+ if (browserTabList[j].id === tabId) {
299
+ var title = browserTabList[j].title || browserTabList[j].url || "";
300
+ return title.length > 30 ? title.slice(0, 27) + "..." : title;
301
+ }
302
+ }
303
+ return "Tab " + tabId;
304
+ }
305
+ return id;
306
+ }
307
+
308
+ function getSourceIcon(id) {
309
+ if (id.startsWith("term:")) return "square-terminal";
310
+ if (id.startsWith("tab:")) return "globe";
311
+ return "circle";
312
+ }
313
+
314
+ // Get active source IDs (for use when sending messages)
315
+ export function getActiveSources() {
316
+ return Array.from(activeSourceIds);
317
+ }
318
+
319
+ // Check if any sources are active
320
+ export function hasActiveSources() {
321
+ return activeSourceIds.size > 0;
322
+ }
323
+
324
+ function escapeHtml(str) {
325
+ var div = document.createElement("div");
326
+ div.textContent = str;
327
+ return div.innerHTML;
328
+ }