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.
Files changed (158) hide show
  1. package/LICENSES/README.md +2 -0
  2. package/lib/project-issues.js +11 -0
  3. package/lib/project-session-github.js +93 -0
  4. package/lib/project.js +32 -2
  5. package/lib/public/app.js +4 -0
  6. package/lib/public/css/base.css +3 -57
  7. package/lib/public/css/filebrowser.css +1 -1
  8. package/lib/public/css/fonts.css +14 -0
  9. package/lib/public/css/git-panel.css +2 -2
  10. package/lib/public/css/git-placard.css +1 -1
  11. package/lib/public/css/mobile-nav.css +13 -13
  12. package/lib/public/css/noto-sans-kr.css +126 -0
  13. package/lib/public/css/overlays.css +4 -4
  14. package/lib/public/css/profile.css +4 -4
  15. package/lib/public/css/server-settings.css +1 -1
  16. package/lib/public/css/session-github.css +112 -0
  17. package/lib/public/css/sidebar.css +2 -2
  18. package/lib/public/css/sticky-notes.css +3 -3
  19. package/lib/public/css/title-bar.css +26 -5
  20. package/lib/public/fonts/geist/Geist-Variable.woff2 +0 -0
  21. package/lib/public/fonts/geist/LICENSE.txt +92 -0
  22. package/lib/public/fonts/noto-sans-kr/LICENSE.txt +93 -0
  23. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-0-wght-normal.woff2 +0 -0
  24. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-1-wght-normal.woff2 +0 -0
  25. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-10-wght-normal.woff2 +0 -0
  26. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-100-wght-normal.woff2 +0 -0
  27. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-101-wght-normal.woff2 +0 -0
  28. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-102-wght-normal.woff2 +0 -0
  29. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-103-wght-normal.woff2 +0 -0
  30. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-104-wght-normal.woff2 +0 -0
  31. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-105-wght-normal.woff2 +0 -0
  32. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-106-wght-normal.woff2 +0 -0
  33. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-107-wght-normal.woff2 +0 -0
  34. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-108-wght-normal.woff2 +0 -0
  35. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-109-wght-normal.woff2 +0 -0
  36. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-11-wght-normal.woff2 +0 -0
  37. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-110-wght-normal.woff2 +0 -0
  38. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-111-wght-normal.woff2 +0 -0
  39. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-112-wght-normal.woff2 +0 -0
  40. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-113-wght-normal.woff2 +0 -0
  41. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-114-wght-normal.woff2 +0 -0
  42. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-115-wght-normal.woff2 +0 -0
  43. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-116-wght-normal.woff2 +0 -0
  44. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-117-wght-normal.woff2 +0 -0
  45. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-118-wght-normal.woff2 +0 -0
  46. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-119-wght-normal.woff2 +0 -0
  47. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-12-wght-normal.woff2 +0 -0
  48. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-13-wght-normal.woff2 +0 -0
  49. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-14-wght-normal.woff2 +0 -0
  50. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-15-wght-normal.woff2 +0 -0
  51. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-16-wght-normal.woff2 +0 -0
  52. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-17-wght-normal.woff2 +0 -0
  53. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-18-wght-normal.woff2 +0 -0
  54. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-19-wght-normal.woff2 +0 -0
  55. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-2-wght-normal.woff2 +0 -0
  56. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-20-wght-normal.woff2 +0 -0
  57. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-21-wght-normal.woff2 +0 -0
  58. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-22-wght-normal.woff2 +0 -0
  59. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-23-wght-normal.woff2 +0 -0
  60. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-24-wght-normal.woff2 +0 -0
  61. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-25-wght-normal.woff2 +0 -0
  62. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-26-wght-normal.woff2 +0 -0
  63. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-27-wght-normal.woff2 +0 -0
  64. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-28-wght-normal.woff2 +0 -0
  65. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-29-wght-normal.woff2 +0 -0
  66. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-3-wght-normal.woff2 +0 -0
  67. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-30-wght-normal.woff2 +0 -0
  68. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-31-wght-normal.woff2 +0 -0
  69. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-32-wght-normal.woff2 +0 -0
  70. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-33-wght-normal.woff2 +0 -0
  71. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-34-wght-normal.woff2 +0 -0
  72. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-35-wght-normal.woff2 +0 -0
  73. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-36-wght-normal.woff2 +0 -0
  74. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-37-wght-normal.woff2 +0 -0
  75. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-38-wght-normal.woff2 +0 -0
  76. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-39-wght-normal.woff2 +0 -0
  77. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-4-wght-normal.woff2 +0 -0
  78. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-40-wght-normal.woff2 +0 -0
  79. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-41-wght-normal.woff2 +0 -0
  80. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-42-wght-normal.woff2 +0 -0
  81. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-43-wght-normal.woff2 +0 -0
  82. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-44-wght-normal.woff2 +0 -0
  83. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-45-wght-normal.woff2 +0 -0
  84. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-46-wght-normal.woff2 +0 -0
  85. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-47-wght-normal.woff2 +0 -0
  86. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-48-wght-normal.woff2 +0 -0
  87. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-49-wght-normal.woff2 +0 -0
  88. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-5-wght-normal.woff2 +0 -0
  89. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-50-wght-normal.woff2 +0 -0
  90. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-51-wght-normal.woff2 +0 -0
  91. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-52-wght-normal.woff2 +0 -0
  92. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-53-wght-normal.woff2 +0 -0
  93. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-54-wght-normal.woff2 +0 -0
  94. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-55-wght-normal.woff2 +0 -0
  95. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-56-wght-normal.woff2 +0 -0
  96. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-57-wght-normal.woff2 +0 -0
  97. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-58-wght-normal.woff2 +0 -0
  98. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-59-wght-normal.woff2 +0 -0
  99. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-6-wght-normal.woff2 +0 -0
  100. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-60-wght-normal.woff2 +0 -0
  101. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-61-wght-normal.woff2 +0 -0
  102. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-62-wght-normal.woff2 +0 -0
  103. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-63-wght-normal.woff2 +0 -0
  104. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-64-wght-normal.woff2 +0 -0
  105. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-65-wght-normal.woff2 +0 -0
  106. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-66-wght-normal.woff2 +0 -0
  107. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-67-wght-normal.woff2 +0 -0
  108. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-68-wght-normal.woff2 +0 -0
  109. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-69-wght-normal.woff2 +0 -0
  110. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-7-wght-normal.woff2 +0 -0
  111. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-70-wght-normal.woff2 +0 -0
  112. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-71-wght-normal.woff2 +0 -0
  113. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-72-wght-normal.woff2 +0 -0
  114. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-73-wght-normal.woff2 +0 -0
  115. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-74-wght-normal.woff2 +0 -0
  116. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-75-wght-normal.woff2 +0 -0
  117. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-76-wght-normal.woff2 +0 -0
  118. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-77-wght-normal.woff2 +0 -0
  119. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-78-wght-normal.woff2 +0 -0
  120. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-79-wght-normal.woff2 +0 -0
  121. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-8-wght-normal.woff2 +0 -0
  122. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-80-wght-normal.woff2 +0 -0
  123. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-81-wght-normal.woff2 +0 -0
  124. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-82-wght-normal.woff2 +0 -0
  125. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-83-wght-normal.woff2 +0 -0
  126. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-84-wght-normal.woff2 +0 -0
  127. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-85-wght-normal.woff2 +0 -0
  128. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-86-wght-normal.woff2 +0 -0
  129. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-87-wght-normal.woff2 +0 -0
  130. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-88-wght-normal.woff2 +0 -0
  131. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-89-wght-normal.woff2 +0 -0
  132. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-9-wght-normal.woff2 +0 -0
  133. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-90-wght-normal.woff2 +0 -0
  134. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-91-wght-normal.woff2 +0 -0
  135. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-92-wght-normal.woff2 +0 -0
  136. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-93-wght-normal.woff2 +0 -0
  137. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-94-wght-normal.woff2 +0 -0
  138. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-95-wght-normal.woff2 +0 -0
  139. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-96-wght-normal.woff2 +0 -0
  140. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-97-wght-normal.woff2 +0 -0
  141. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-98-wght-normal.woff2 +0 -0
  142. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-99-wght-normal.woff2 +0 -0
  143. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-cyrillic-wght-normal.woff2 +0 -0
  144. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-latin-ext-wght-normal.woff2 +0 -0
  145. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-latin-wght-normal.woff2 +0 -0
  146. package/lib/public/fonts/noto-sans-kr/noto-sans-kr-vietnamese-wght-normal.woff2 +0 -0
  147. package/lib/public/modules/app-messages.js +2 -0
  148. package/lib/public/modules/git-panel.js +6 -2
  149. package/lib/public/modules/issues-render.js +2 -0
  150. package/lib/public/modules/markdown.js +3 -4
  151. package/lib/public/modules/session-github.js +153 -0
  152. package/lib/public/modules/sidebar-mobile.js +8 -1
  153. package/lib/public/modules/sidebar-sessions.js +2 -0
  154. package/lib/public/style.css +1 -0
  155. package/lib/sdk-bridge.js +11 -0
  156. package/lib/session-github.js +48 -0
  157. package/lib/sessions.js +3 -0
  158. package/package.json +1 -1
@@ -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.
@@ -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,
@@ -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: "Pretendard", "Twemoji", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
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;
@@ -1917,7 +1917,7 @@
1917
1917
  gap: 4px;
1918
1918
  padding: 8px 10px;
1919
1919
  font-size: 12px;
1920
- font-family: "Pretendard", sans-serif;
1920
+ font-family: var(--font-ui);
1921
1921
  color: var(--text-muted);
1922
1922
  cursor: pointer;
1923
1923
  white-space: nowrap;
@@ -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: "Pretendard", "Twemoji", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
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: "Pretendard", "Twemoji", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
351
+ font-family: var(--font-ui);
352
352
  font-size: 10px;
353
353
  font-weight: 600;
354
354
  font-variant-numeric: tabular-nums;
@@ -15,7 +15,7 @@
15
15
  border-radius: 8px;
16
16
  background: var(--bg-alt);
17
17
  cursor: pointer;
18
- font-family: "Pretendard", "Twemoji", system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
18
+ font-family: var(--font-ui);
19
19
  }
20
20
 
21
21
  .git-placard.hidden { display: none; }
@@ -60,7 +60,7 @@
60
60
 
61
61
  .mobile-tab-label {
62
62
  font-size: 10px;
63
- font-family: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
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: "Pretendard", system-ui, sans-serif;
1165
+ font-family: var(--font-ui);
1166
1166
  font-size: 14px;
1167
1167
  cursor: pointer;
1168
1168
  text-align: left;