clay-server 4.4.0 → 4.5.0-beta.1
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/LICENSES/README.md +2 -0
- package/lib/project-issues.js +11 -0
- package/lib/project-session-github.js +93 -0
- package/lib/project.js +32 -2
- package/lib/public/app.js +4 -0
- package/lib/public/css/base.css +3 -57
- package/lib/public/css/filebrowser.css +1 -1
- package/lib/public/css/fonts.css +14 -0
- package/lib/public/css/git-panel.css +2 -2
- package/lib/public/css/git-placard.css +1 -1
- package/lib/public/css/mobile-nav.css +13 -13
- package/lib/public/css/noto-sans-kr.css +126 -0
- package/lib/public/css/overlays.css +4 -4
- package/lib/public/css/profile.css +4 -4
- package/lib/public/css/server-settings.css +1 -1
- package/lib/public/css/session-github.css +112 -0
- package/lib/public/css/sidebar.css +2 -2
- package/lib/public/css/sticky-notes.css +3 -3
- package/lib/public/css/title-bar.css +26 -5
- package/lib/public/fonts/geist/Geist-Variable.woff2 +0 -0
- package/lib/public/fonts/geist/LICENSE.txt +92 -0
- package/lib/public/fonts/noto-sans-kr/LICENSE.txt +93 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-0-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-1-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-10-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-100-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-101-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-102-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-103-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-104-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-105-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-106-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-107-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-108-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-109-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-11-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-110-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-111-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-112-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-113-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-114-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-115-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-116-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-117-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-118-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-119-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-12-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-13-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-14-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-15-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-16-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-17-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-18-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-19-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-2-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-20-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-21-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-22-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-23-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-24-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-25-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-26-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-27-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-28-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-29-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-3-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-30-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-31-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-32-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-33-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-34-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-35-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-36-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-37-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-38-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-39-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-4-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-40-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-41-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-42-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-43-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-44-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-45-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-46-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-47-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-48-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-49-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-5-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-50-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-51-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-52-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-53-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-54-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-55-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-56-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-57-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-58-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-59-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-6-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-60-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-61-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-62-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-63-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-64-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-65-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-66-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-67-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-68-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-69-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-7-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-70-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-71-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-72-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-73-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-74-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-75-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-76-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-77-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-78-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-79-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-8-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-80-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-81-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-82-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-83-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-84-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-85-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-86-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-87-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-88-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-89-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-9-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-90-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-91-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-92-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-93-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-94-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-95-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-96-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-97-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-98-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-99-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-cyrillic-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-latin-ext-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-latin-wght-normal.woff2 +0 -0
- package/lib/public/fonts/noto-sans-kr/noto-sans-kr-vietnamese-wght-normal.woff2 +0 -0
- package/lib/public/modules/app-messages.js +2 -0
- package/lib/public/modules/git-panel.js +6 -2
- package/lib/public/modules/issues-render.js +2 -0
- package/lib/public/modules/markdown.js +3 -4
- package/lib/public/modules/session-github.js +153 -0
- package/lib/public/modules/sidebar-mobile.js +8 -1
- package/lib/public/modules/sidebar-sessions.js +2 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-bridge.js +11 -0
- package/lib/session-github.js +48 -0
- package/lib/sessions.js +3 -0
- package/package.json +1 -1
package/LICENSES/README.md
CHANGED
|
@@ -5,3 +5,5 @@ Clay Studio as a whole is licensed under the GNU Affero General Public License,
|
|
|
5
5
|
Preexisting MIT-covered portions retain the grants and applicable copyright and permission notices from the MIT license. The byte-for-byte prior license text is preserved in [MIT-legacy.txt](MIT-legacy.txt), and this notice does not revoke or alter those earlier grants or notices.
|
|
6
6
|
|
|
7
7
|
Third-party components and assets retain their own licenses and notices. In particular, the bundled Source Serif 4 font remains under the [SIL Open Font License 1.1](../lib/public/fonts/source-serif-4/LICENSE.md).
|
|
8
|
+
|
|
9
|
+
The bundled UI fonts [Geist](../lib/public/fonts/geist/LICENSE.txt) (geist 1.7.2) and [Noto Sans KR](../lib/public/fonts/noto-sans-kr/LICENSE.txt) (@fontsource-variable/noto-sans-kr 5.3.0, Google Fonts v39) also retain the SIL Open Font License 1.1. Their WOFF2 files are distributed unchanged; the Korean font uses its original Unicode subsets for on-demand loading.
|
package/lib/project-issues.js
CHANGED
|
@@ -39,6 +39,17 @@ function attachProjectIssues(ctx) {
|
|
|
39
39
|
else if (msg.type === "issue_comment") notify(result, "issue_commented");
|
|
40
40
|
else if (msg.type === "issue_delete") notify(result, "issue_deleted");
|
|
41
41
|
}
|
|
42
|
+
if (msg.type === "issue_read" && result) {
|
|
43
|
+
result = Object.assign({}, result, { githubLinks: [] });
|
|
44
|
+
(result.linkedWorkSessions || []).forEach(function (link) {
|
|
45
|
+
try {
|
|
46
|
+
var work = bound.resolveWorkSession({ ref: result.ref, sessionOriginId: link.sessionId });
|
|
47
|
+
(work.githubLinks || []).forEach(function (item) {
|
|
48
|
+
if (!result.githubLinks.some(function (existing) { return existing.url === item.url; })) result.githubLinks.push(item);
|
|
49
|
+
});
|
|
50
|
+
} catch (error) { /* Linked work may no longer be visible. */ }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
42
53
|
ctx.sendTo(ws, { type: "issues_result", requestId: msg.requestId, action: msg.type, result: result, canDelete: bound.canDelete === true });
|
|
43
54
|
}).catch(function (error) {
|
|
44
55
|
var result = error && error.sessionId ? { sessionId: error.sessionId } : undefined;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
var github = require("./session-github");
|
|
2
|
+
var buildShape = require("./session-spawn-mcp-server").buildShape;
|
|
3
|
+
var CONTRACT = "GitHub work links: when an issue is clearly the task this session is implementing, or you create/work on its PR, call link_session_github with its exact GitHub URL. Mere mentions, examples, and reference material must not be linked. Do not infer a session's PR from the repository's current branch: sessions may share a checkout. This records local metadata only and does not post to GitHub. Use get_session_github to inspect/refresh linked status and discover accessible sessions already working on the same reference before starting duplicate work. Keep Issues as quiet background records, not a mandatory workflow. Unlink an incorrect association with unlink_session_github.";
|
|
4
|
+
function attachSessionGithub(ctx) {
|
|
5
|
+
var inflight = new Map();
|
|
6
|
+
var attempts = new Map();
|
|
7
|
+
function authorize(session) {
|
|
8
|
+
if (!session || ctx.sm.sessions.get(session.localId) !== session || ctx.isMate || !ctx.canUse(session)) throw new Error("GitHub links require an authorized project session.");
|
|
9
|
+
var identity = ctx.identity(session);
|
|
10
|
+
// Account login does not imply OS isolation. Match the project's execution mode.
|
|
11
|
+
if (ctx.osUsers && !identity) throw new Error("GitHub requires a mapped OS user with gh authentication in OS-isolated mode.");
|
|
12
|
+
return identity;
|
|
13
|
+
}
|
|
14
|
+
function persist(session) {
|
|
15
|
+
if (ctx.sm.saveSessionFile(session) === false) throw new Error("Could not save GitHub links.");
|
|
16
|
+
ctx.sm.broadcastSessionList();
|
|
17
|
+
}
|
|
18
|
+
async function refresh(session, force) {
|
|
19
|
+
var identity = authorize(session);
|
|
20
|
+
var id = session.localId;
|
|
21
|
+
if (inflight.has(id)) return inflight.get(id);
|
|
22
|
+
if (!force && Date.now() - (attempts.get(id) || 0) < 60000) return session.githubLinks || [];
|
|
23
|
+
attempts.set(id, Date.now());
|
|
24
|
+
var pending = (async function () {
|
|
25
|
+
var links = (session.githubLinks || []).slice();
|
|
26
|
+
var updated = [];
|
|
27
|
+
for (var link of links) {
|
|
28
|
+
try { updated.push(await github.readReference(ctx.cwd, link.url, identity, ctx.run)); }
|
|
29
|
+
catch (error) { updated.push(Object.assign({}, link, { stale: true })); }
|
|
30
|
+
}
|
|
31
|
+
authorize(session);
|
|
32
|
+
// A link/unlink during the request wins over the refresh snapshot.
|
|
33
|
+
session.githubLinks = (session.githubLinks || []).map(function (link) {
|
|
34
|
+
var index = links.indexOf(link);
|
|
35
|
+
return index < 0 ? link : updated[index];
|
|
36
|
+
});
|
|
37
|
+
if (links.length) persist(session);
|
|
38
|
+
return session.githubLinks || [];
|
|
39
|
+
})();
|
|
40
|
+
inflight.set(id, pending);
|
|
41
|
+
try { return await pending; } finally { inflight.delete(id); }
|
|
42
|
+
}
|
|
43
|
+
async function invoke(session, name, args) {
|
|
44
|
+
var identity = authorize(session);
|
|
45
|
+
if (name === "get_session_github") {
|
|
46
|
+
var links = await refresh(session, false);
|
|
47
|
+
var matches = [];
|
|
48
|
+
if (args.url) {
|
|
49
|
+
var url = github.parseReference(args.url).url.toLowerCase();
|
|
50
|
+
ctx.sm.sessions.forEach(function (other) {
|
|
51
|
+
if (!other.hidden && !other.delegated && !require("./session-provenance").isWorker(other) && ctx.canRead(session, other) && (other.githubLinks || []).some(function (link) { return link.url.toLowerCase() === url; })) matches.push({ sessionId: other.localId, title: other.title });
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return { links: links, matchingSessions: matches.slice(0, 20) };
|
|
55
|
+
}
|
|
56
|
+
var ref = github.parseReference(args.url);
|
|
57
|
+
if (name === "unlink_session_github") {
|
|
58
|
+
session.githubLinks = (session.githubLinks || []).filter(function (link) { return link.url.toLowerCase() !== ref.url.toLowerCase(); });
|
|
59
|
+
} else {
|
|
60
|
+
var verified = await github.readReference(ctx.cwd, ref.url, identity, ctx.run);
|
|
61
|
+
authorize(session);
|
|
62
|
+
var retained = (session.githubLinks || []).filter(function (link) { return link.url.toLowerCase() !== ref.url.toLowerCase(); });
|
|
63
|
+
if (retained.length >= 8) throw new Error("A session can link up to eight GitHub items.");
|
|
64
|
+
session.githubLinks = retained.concat([verified]);
|
|
65
|
+
}
|
|
66
|
+
persist(session);
|
|
67
|
+
return { links: session.githubLinks };
|
|
68
|
+
}
|
|
69
|
+
function defs(session) {
|
|
70
|
+
if (ctx.isMate || (session && !ctx.canUse(session))) return [];
|
|
71
|
+
return ["link_session_github", "get_session_github", "unlink_session_github"].map(function (name) {
|
|
72
|
+
return { name: name, permissionName: "mcp__clay-github__" + name, description: CONTRACT + " " + (name === "get_session_github" ? "Read linked status; optional URL finds visible existing work sessions." : name === "link_session_github" ? "Verify and attach one issue or PR to the current session." : "Remove one local association without modifying GitHub."), inputSchema: buildShape({ url: { type: "string", description: "Exact https://github.com/owner/repo/issues/123 or /pull/123 URL." } }, name === "get_session_github" ? [] : ["url"]), handler: async function (args) {
|
|
73
|
+
try { return { content: [{ type: "text", text: JSON.stringify(await invoke(session, name, args || {})) }] }; }
|
|
74
|
+
catch (error) { return { isError: true, content: [{ type: "text", text: error.message }] }; }
|
|
75
|
+
} };
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
getDynamicToolDefs: defs,
|
|
80
|
+
getSystemPrompt: function () { return ctx.isMate ? "" : CONTRACT; },
|
|
81
|
+
createMcpServer: function (adapter, session) { return ctx.isMate || !adapter.createToolServer ? null : adapter.createToolServer({ name: "clay-github", version: "1.0.0", tools: defs(session) }); },
|
|
82
|
+
getBridgeTools: function (session, normalize) { return defs(session).map(function (tool) { return { server: "clay-github", name: tool.name, description: tool.description, inputSchema: normalize(tool.inputSchema) }; }); },
|
|
83
|
+
callBridgeTool: function (session, name, args) { var tool = defs(session).find(function (item) { return item.name === name; }); return tool ? tool.handler(args) : Promise.reject(new Error("GitHub tool unavailable.")); },
|
|
84
|
+
handleMessage: function (ws, msg) {
|
|
85
|
+
if (msg.type !== "session_github_refresh") return false;
|
|
86
|
+
var session = ctx.sm.sessions.get(ws._clayActiveSession);
|
|
87
|
+
if (!session || !ctx.canRefresh(ws, session) || !(session.githubLinks || []).length) return true;
|
|
88
|
+
refresh(session, false).catch(function () {});
|
|
89
|
+
return true;
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
module.exports = { attachSessionGithub: attachSessionGithub };
|
package/lib/project.js
CHANGED
|
@@ -813,6 +813,24 @@ function createProjectContext(opts) {
|
|
|
813
813
|
service: opts.issuesService, projectSlug: slug, isMate: isMate, sm: sm,
|
|
814
814
|
sendTo: sendTo, getClients: function () { return clients; }, startWork: _issueWork,
|
|
815
815
|
});
|
|
816
|
+
var _sessionGithub = require("./project-session-github").attachSessionGithub({
|
|
817
|
+
cwd: cwd, sm: sm, isMate: isMate, osUsers: osUsers,
|
|
818
|
+
canUse: function (session) {
|
|
819
|
+
if (session.hidden || session.delegated || session._pairDelegation || session._delegatedBy || require("./session-provenance").isWorker(session)) return false;
|
|
820
|
+
if (!usersModule.isMultiUser()) return true;
|
|
821
|
+
var owner = session.ownerId && usersModule.findUserById(session.ownerId);
|
|
822
|
+
return !!(owner && opts.canAccessProjectSlug && opts.canAccessProjectSlug(owner.id, slug));
|
|
823
|
+
},
|
|
824
|
+
identity: function (session) {
|
|
825
|
+
return getOsUserInfoForLinuxUser(getLinuxUserForSession(session));
|
|
826
|
+
},
|
|
827
|
+
canRead: function (caller, other) {
|
|
828
|
+
return !usersModule.isMultiUser() || usersModule.canAccessSession(caller.ownerId, other, opts.getProjectAccess ? opts.getProjectAccess() : { visibility: "public" });
|
|
829
|
+
},
|
|
830
|
+
canRefresh: function (ws, session) {
|
|
831
|
+
return !usersModule.isMultiUser() || !!(ws._clayUser && ws._clayUser.id === session.ownerId);
|
|
832
|
+
},
|
|
833
|
+
});
|
|
816
834
|
var _mateKnowledge = attachProjectMateKnowledge({
|
|
817
835
|
service: opts.mateKnowledgeService || null,
|
|
818
836
|
sm: sm,
|
|
@@ -992,6 +1010,8 @@ function createProjectContext(opts) {
|
|
|
992
1010
|
// get the read-only cross-project set only when the Mate registry confirms
|
|
993
1011
|
// authoritative builtin Clay.
|
|
994
1012
|
try {
|
|
1013
|
+
var githubMcpConfig = _sessionGithub.createMcpServer(adapter, null);
|
|
1014
|
+
if (githubMcpConfig) servers["clay-github"] = githubMcpConfig;
|
|
995
1015
|
var issuesMcpConfig = _issues.createMcpServer(adapter, null);
|
|
996
1016
|
if (issuesMcpConfig) servers["clay-issues"] = issuesMcpConfig;
|
|
997
1017
|
var projectLogsMcpConfig = _projectLogs.createMcpServer(adapter, null);
|
|
@@ -1175,6 +1195,11 @@ function createProjectContext(opts) {
|
|
|
1175
1195
|
}
|
|
1176
1196
|
continue;
|
|
1177
1197
|
}
|
|
1198
|
+
if (name === "clay-github" && forSession) {
|
|
1199
|
+
var boundGithub = _sessionGithub.createMcpServer(adapter, forSession);
|
|
1200
|
+
if (boundGithub) { filtered[name] = boundGithub; hasAny = true; }
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1178
1203
|
if (name === "clay-issues" && forSession) {
|
|
1179
1204
|
var boundIssues = _issues.createMcpServer(adapter, forSession);
|
|
1180
1205
|
if (boundIssues) { filtered[name] = boundIssues; hasAny = true; }
|
|
@@ -1348,6 +1373,7 @@ function createProjectContext(opts) {
|
|
|
1348
1373
|
_capsuleCatalog.getSystemPrompt(session),
|
|
1349
1374
|
_projectLogs.getSystemPrompt(session),
|
|
1350
1375
|
_issues.getSystemPrompt(session),
|
|
1376
|
+
_sessionGithub.getSystemPrompt(session),
|
|
1351
1377
|
_scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
|
|
1352
1378
|
_mateKnowledge.getSystemPrompt(session),
|
|
1353
1379
|
_autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
|
|
@@ -1369,6 +1395,7 @@ function createProjectContext(opts) {
|
|
|
1369
1395
|
.concat(_workspaceQuery.getHistoryDynamicToolDefs(session))
|
|
1370
1396
|
.concat(_projectLogs.getDynamicToolDefs(session))
|
|
1371
1397
|
.concat(_issues.getDynamicToolDefs(session))
|
|
1398
|
+
.concat(_sessionGithub.getDynamicToolDefs(session))
|
|
1372
1399
|
.concat(_mateKnowledge.getDynamicToolDefs(session))
|
|
1373
1400
|
.concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
|
|
1374
1401
|
.concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
|
|
@@ -1760,6 +1787,7 @@ function createProjectContext(opts) {
|
|
|
1760
1787
|
// --- Project Logs (delegated to project-logs.js) ---
|
|
1761
1788
|
if (_projectLogs.handleLogsMessage(ws, msg)) return;
|
|
1762
1789
|
if (_issues.handleMessage(ws, msg)) return;
|
|
1790
|
+
if (_sessionGithub.handleMessage(ws, msg)) return;
|
|
1763
1791
|
|
|
1764
1792
|
// --- Notifications (delegated to project-notifications.js) ---
|
|
1765
1793
|
if (_notifications.handleNotificationMessage(ws, msg)) return;
|
|
@@ -2328,6 +2356,7 @@ function createProjectContext(opts) {
|
|
|
2328
2356
|
var historyTools = _workspaceQuery.getHistoryBridgeTools(boundSession, normalizeToolSchema);
|
|
2329
2357
|
for (var cht = 0; cht < historyTools.length; cht++) tools.push(historyTools[cht]);
|
|
2330
2358
|
tools = tools.concat(_issues.getBridgeTools(boundSession, normalizeToolSchema));
|
|
2359
|
+
tools = tools.concat(_sessionGithub.getBridgeTools(boundSession, normalizeToolSchema));
|
|
2331
2360
|
var logsTools = _projectLogs.getBridgeTools(boundSession, normalizeToolSchema);
|
|
2332
2361
|
for (var plti = 0; plti < logsTools.length; plti++) tools.push(logsTools[plti]);
|
|
2333
2362
|
var knowledgeTools = _mateKnowledge.getBridgeTools(boundSession, normalizeToolSchema);
|
|
@@ -2355,7 +2384,7 @@ function createProjectContext(opts) {
|
|
|
2355
2384
|
// Debate tools were added above only when this request had an
|
|
2356
2385
|
// exact session. Never duplicate them or advertise the static,
|
|
2357
2386
|
// intentionally unbound proposal server.
|
|
2358
|
-
if (inAppNames[i] === "clay-debate" || inAppNames[i] === "clay-mate-creation" || inAppNames[i] === "clay-workspace" || inAppNames[i] === "clay-history" || inAppNames[i] === "clay-issues" || inAppNames[i] === "clay-logs" || inAppNames[i] === "clay-knowledge") continue;
|
|
2387
|
+
if (inAppNames[i] === "clay-debate" || inAppNames[i] === "clay-mate-creation" || inAppNames[i] === "clay-workspace" || inAppNames[i] === "clay-history" || inAppNames[i] === "clay-github" || inAppNames[i] === "clay-issues" || inAppNames[i] === "clay-logs" || inAppNames[i] === "clay-knowledge") continue;
|
|
2359
2388
|
extractServerTools(inAppNames[i], localMcp[inAppNames[i]]);
|
|
2360
2389
|
}
|
|
2361
2390
|
}
|
|
@@ -2441,6 +2470,7 @@ function createProjectContext(opts) {
|
|
|
2441
2470
|
if (boundSession && serverName === "clay-history") {
|
|
2442
2471
|
return _workspaceQuery.callHistoryBridgeTool(boundSession, toolName, args);
|
|
2443
2472
|
}
|
|
2473
|
+
if (boundSession && serverName === "clay-github") return _sessionGithub.callBridgeTool(boundSession, toolName, args);
|
|
2444
2474
|
if (boundSession && serverName === "clay-issues") return _issues.callBridgeTool(boundSession, toolName, args);
|
|
2445
2475
|
if (boundSession && serverName === "clay-logs") {
|
|
2446
2476
|
return _projectLogs.callBridgeTool(boundSession, toolName, args);
|
|
@@ -2448,7 +2478,7 @@ function createProjectContext(opts) {
|
|
|
2448
2478
|
if (boundSession && serverName === "clay-knowledge") {
|
|
2449
2479
|
return _mateKnowledge.callBridgeTool(boundSession, toolName, args);
|
|
2450
2480
|
}
|
|
2451
|
-
if (serverName === "clay-sessions" || serverName === "clay-notes" || serverName === "clay-handoff" || serverName === "clay-documents" || serverName === "clay-debate" || serverName === "clay-mate-creation" || serverName === "clay-workspace" || serverName === "clay-history" || serverName === "clay-issues" || serverName === "clay-logs" || serverName === "clay-knowledge") {
|
|
2481
|
+
if (serverName === "clay-sessions" || serverName === "clay-notes" || serverName === "clay-handoff" || serverName === "clay-documents" || serverName === "clay-debate" || serverName === "clay-mate-creation" || serverName === "clay-workspace" || serverName === "clay-history" || serverName === "clay-github" || serverName === "clay-issues" || serverName === "clay-logs" || serverName === "clay-knowledge") {
|
|
2452
2482
|
return Promise.reject(new Error("Session-bound tool requires a valid Clay session: " + serverName + "/" + toolName));
|
|
2453
2483
|
}
|
|
2454
2484
|
if (sessionOnly) return Promise.reject(new Error("Session tool not found: " + serverName + "/" + toolName));
|
package/lib/public/app.js
CHANGED
|
@@ -286,6 +286,10 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
286
286
|
projectName: projectName,
|
|
287
287
|
cwd: "",
|
|
288
288
|
currentSlug: currentSlug,
|
|
289
|
+
githubWorkPopoverBound: false,
|
|
290
|
+
githubWorkSessions: [],
|
|
291
|
+
githubWorkProject: null,
|
|
292
|
+
githubWorkRefresh: null,
|
|
289
293
|
activeProjectSlug: null,
|
|
290
294
|
sessionActivatedProjectSlug: null,
|
|
291
295
|
sessionListProjectSlug: null,
|
package/lib/public/css/base.css
CHANGED
|
@@ -1,63 +1,9 @@
|
|
|
1
|
+
@import url("fonts.css");
|
|
2
|
+
|
|
1
3
|
/* ==========================================================================
|
|
2
4
|
Clay — Design aligned with Claude Desktop
|
|
3
5
|
========================================================================== */
|
|
4
6
|
|
|
5
|
-
/* --- Pretendard Font --- */
|
|
6
|
-
@font-face {
|
|
7
|
-
font-family: 'Pretendard';
|
|
8
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Thin.woff2') format('woff2');
|
|
9
|
-
font-weight: 100;
|
|
10
|
-
font-display: swap;
|
|
11
|
-
}
|
|
12
|
-
@font-face {
|
|
13
|
-
font-family: 'Pretendard';
|
|
14
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-ExtraLight.woff2') format('woff2');
|
|
15
|
-
font-weight: 200;
|
|
16
|
-
font-display: swap;
|
|
17
|
-
}
|
|
18
|
-
@font-face {
|
|
19
|
-
font-family: 'Pretendard';
|
|
20
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Light.woff2') format('woff2');
|
|
21
|
-
font-weight: 300;
|
|
22
|
-
font-display: swap;
|
|
23
|
-
}
|
|
24
|
-
@font-face {
|
|
25
|
-
font-family: 'Pretendard';
|
|
26
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Regular.woff2') format('woff2');
|
|
27
|
-
font-weight: 400;
|
|
28
|
-
font-display: swap;
|
|
29
|
-
}
|
|
30
|
-
@font-face {
|
|
31
|
-
font-family: 'Pretendard';
|
|
32
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Medium.woff2') format('woff2');
|
|
33
|
-
font-weight: 500;
|
|
34
|
-
font-display: swap;
|
|
35
|
-
}
|
|
36
|
-
@font-face {
|
|
37
|
-
font-family: 'Pretendard';
|
|
38
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-SemiBold.woff2') format('woff2');
|
|
39
|
-
font-weight: 600;
|
|
40
|
-
font-display: swap;
|
|
41
|
-
}
|
|
42
|
-
@font-face {
|
|
43
|
-
font-family: 'Pretendard';
|
|
44
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Bold.woff2') format('woff2');
|
|
45
|
-
font-weight: 700;
|
|
46
|
-
font-display: swap;
|
|
47
|
-
}
|
|
48
|
-
@font-face {
|
|
49
|
-
font-family: 'Pretendard';
|
|
50
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-ExtraBold.woff2') format('woff2');
|
|
51
|
-
font-weight: 800;
|
|
52
|
-
font-display: swap;
|
|
53
|
-
}
|
|
54
|
-
@font-face {
|
|
55
|
-
font-family: 'Pretendard';
|
|
56
|
-
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/pretendard@1.0/Pretendard-Black.woff2') format('woff2');
|
|
57
|
-
font-weight: 900;
|
|
58
|
-
font-display: swap;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
7
|
/* --- Twemoji COLR Font (cross-platform consistent emoji via font, no JS parsing) --- */
|
|
62
8
|
@font-face {
|
|
63
9
|
font-family: 'Twemoji';
|
|
@@ -254,7 +200,7 @@ html, body {
|
|
|
254
200
|
height: 100%;
|
|
255
201
|
background: var(--bg);
|
|
256
202
|
color: var(--text);
|
|
257
|
-
font-family:
|
|
203
|
+
font-family: var(--font-ui);
|
|
258
204
|
font-size: 15px;
|
|
259
205
|
font-weight: var(--font-weight-body);
|
|
260
206
|
line-height: 1.6;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
@import url("noto-sans-kr.css");
|
|
2
|
+
|
|
3
|
+
/* Geist 1.7.2; both bundled UI fonts retain their SIL OFL notices. */
|
|
4
|
+
@font-face {
|
|
5
|
+
font-family: "Geist";
|
|
6
|
+
src: url("../fonts/geist/Geist-Variable.woff2") format("woff2");
|
|
7
|
+
font-style: normal;
|
|
8
|
+
font-weight: 100 900;
|
|
9
|
+
font-display: swap;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:root {
|
|
13
|
+
--font-ui: "Geist", "Noto Sans KR", "Twemoji", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
14
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
overflow: hidden;
|
|
9
9
|
display: flex;
|
|
10
10
|
flex-direction: column;
|
|
11
|
-
font-family:
|
|
11
|
+
font-family: var(--font-ui);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
#sidebar-panel-git.hidden { display: none; }
|
|
@@ -348,7 +348,7 @@
|
|
|
348
348
|
gap: 5px;
|
|
349
349
|
margin-left: auto;
|
|
350
350
|
color: var(--text-dimmer);
|
|
351
|
-
font-family:
|
|
351
|
+
font-family: var(--font-ui);
|
|
352
352
|
font-size: 10px;
|
|
353
353
|
font-weight: 600;
|
|
354
354
|
font-variant-numeric: tabular-nums;
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
.mobile-tab-label {
|
|
62
62
|
font-size: 10px;
|
|
63
|
-
font-family:
|
|
63
|
+
font-family: var(--font-ui);
|
|
64
64
|
font-weight: 500;
|
|
65
65
|
line-height: 1;
|
|
66
66
|
}
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
border: none;
|
|
144
144
|
border-radius: 8px;
|
|
145
145
|
color: var(--text-secondary);
|
|
146
|
-
font-family:
|
|
146
|
+
font-family: var(--font-ui);
|
|
147
147
|
font-size: 14px;
|
|
148
148
|
cursor: pointer;
|
|
149
149
|
text-align: left;
|
|
@@ -298,7 +298,7 @@
|
|
|
298
298
|
|
|
299
299
|
.mobile-sheet-title {
|
|
300
300
|
flex: 1;
|
|
301
|
-
font-family:
|
|
301
|
+
font-family: var(--font-ui);
|
|
302
302
|
font-size: 17px;
|
|
303
303
|
font-weight: 700;
|
|
304
304
|
color: var(--text);
|
|
@@ -343,7 +343,7 @@
|
|
|
343
343
|
background: none;
|
|
344
344
|
border: none;
|
|
345
345
|
color: var(--accent);
|
|
346
|
-
font-family:
|
|
346
|
+
font-family: var(--font-ui);
|
|
347
347
|
font-size: 15px;
|
|
348
348
|
font-weight: 600;
|
|
349
349
|
cursor: pointer;
|
|
@@ -498,7 +498,7 @@
|
|
|
498
498
|
border: 1px solid transparent;
|
|
499
499
|
border-radius: 8px;
|
|
500
500
|
color: var(--text-secondary, var(--text-muted));
|
|
501
|
-
font-family:
|
|
501
|
+
font-family: var(--font-ui);
|
|
502
502
|
font-size: 13px;
|
|
503
503
|
font-weight: 500;
|
|
504
504
|
white-space: nowrap;
|
|
@@ -612,7 +612,7 @@
|
|
|
612
612
|
border: none;
|
|
613
613
|
border-radius: 10px;
|
|
614
614
|
color: var(--text-secondary);
|
|
615
|
-
font-family:
|
|
615
|
+
font-family: var(--font-ui);
|
|
616
616
|
font-size: 15px;
|
|
617
617
|
cursor: pointer;
|
|
618
618
|
text-align: left;
|
|
@@ -762,7 +762,7 @@
|
|
|
762
762
|
border: none;
|
|
763
763
|
border-radius: 10px;
|
|
764
764
|
color: var(--text-secondary);
|
|
765
|
-
font-family:
|
|
765
|
+
font-family: var(--font-ui);
|
|
766
766
|
font-size: 15px;
|
|
767
767
|
cursor: pointer;
|
|
768
768
|
text-align: left;
|
|
@@ -866,7 +866,7 @@
|
|
|
866
866
|
border: none;
|
|
867
867
|
border-radius: 8px;
|
|
868
868
|
color: var(--text-secondary);
|
|
869
|
-
font-family:
|
|
869
|
+
font-family: var(--font-ui);
|
|
870
870
|
font-size: 14px;
|
|
871
871
|
cursor: pointer;
|
|
872
872
|
text-align: left;
|
|
@@ -919,7 +919,7 @@
|
|
|
919
919
|
border: none;
|
|
920
920
|
border-radius: 8px;
|
|
921
921
|
color: var(--text-secondary);
|
|
922
|
-
font-family:
|
|
922
|
+
font-family: var(--font-ui);
|
|
923
923
|
font-size: 14px;
|
|
924
924
|
cursor: pointer;
|
|
925
925
|
text-align: left;
|
|
@@ -995,7 +995,7 @@
|
|
|
995
995
|
border: none;
|
|
996
996
|
border-radius: 10px;
|
|
997
997
|
color: var(--text-secondary);
|
|
998
|
-
font-family:
|
|
998
|
+
font-family: var(--font-ui);
|
|
999
999
|
font-size: 15px;
|
|
1000
1000
|
cursor: pointer;
|
|
1001
1001
|
text-align: left;
|
|
@@ -1038,7 +1038,7 @@
|
|
|
1038
1038
|
border: none;
|
|
1039
1039
|
border-radius: 10px;
|
|
1040
1040
|
color: var(--text-secondary);
|
|
1041
|
-
font-family:
|
|
1041
|
+
font-family: var(--font-ui);
|
|
1042
1042
|
font-size: 15px;
|
|
1043
1043
|
cursor: pointer;
|
|
1044
1044
|
text-align: left;
|
|
@@ -1138,7 +1138,7 @@
|
|
|
1138
1138
|
background: var(--bg-alt);
|
|
1139
1139
|
color: var(--text);
|
|
1140
1140
|
font-size: 15px;
|
|
1141
|
-
font-family:
|
|
1141
|
+
font-family: var(--font-ui);
|
|
1142
1142
|
outline: none;
|
|
1143
1143
|
}
|
|
1144
1144
|
|
|
@@ -1162,7 +1162,7 @@
|
|
|
1162
1162
|
border: none;
|
|
1163
1163
|
border-radius: 10px;
|
|
1164
1164
|
color: var(--text-secondary);
|
|
1165
|
-
font-family:
|
|
1165
|
+
font-family: var(--font-ui);
|
|
1166
1166
|
font-size: 14px;
|
|
1167
1167
|
cursor: pointer;
|
|
1168
1168
|
text-align: left;
|