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.
- package/bin/cli.js +5 -9
- package/lib/browser-mcp-server.js +496 -0
- package/lib/daemon.js +1 -1
- package/lib/os-users.js +23 -0
- package/lib/project-debate.js +243 -95
- package/lib/project-mate-interaction.js +766 -0
- package/lib/project-memory.js +677 -0
- package/lib/project.js +546 -1361
- package/lib/public/app.js +817 -175
- package/lib/public/css/debate.css +224 -2
- package/lib/public/css/icon-strip.css +10 -10
- package/lib/public/css/input.css +296 -83
- package/lib/public/css/mates.css +56 -57
- package/lib/public/css/mention.css +7 -4
- package/lib/public/css/menus.css +7 -0
- package/lib/public/css/messages.css +17 -0
- package/lib/public/css/mobile-nav.css +3 -1
- package/lib/public/css/overlays.css +181 -0
- package/lib/public/css/rewind.css +79 -0
- package/lib/public/css/server-settings.css +1 -0
- package/lib/public/css/sidebar.css +10 -0
- package/lib/public/css/title-bar.css +189 -3
- package/lib/public/index.html +53 -16
- package/lib/public/modules/context-sources.js +328 -0
- package/lib/public/modules/debate.js +184 -97
- package/lib/public/modules/input.js +18 -1
- package/lib/public/modules/mate-knowledge.js +11 -11
- package/lib/public/modules/mate-memory.js +5 -5
- package/lib/public/modules/mate-sidebar.js +13 -9
- package/lib/public/modules/mention.js +40 -2
- package/lib/public/modules/notifications.js +109 -1
- package/lib/public/modules/rewind.js +36 -0
- package/lib/public/modules/sidebar.js +107 -28
- package/lib/public/modules/terminal.js +8 -0
- package/lib/public/modules/theme.js +2 -1
- package/lib/public/modules/tools.js +69 -24
- package/lib/sdk-bridge.js +81 -7
- package/lib/sdk-worker.js +13 -1
- package/lib/server.js +42 -0
- package/lib/sessions.js +39 -7
- package/lib/terminal-manager.js +36 -6
- 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
|
+
}
|