@weavetab/mcp 2.5.0 → 2.5.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 (168) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/LICENSE +216 -216
  4. package/README.md +269 -269
  5. package/SECURITY.md +207 -207
  6. package/TROUBLESHOOTING.md +373 -365
  7. package/dist/anticaptcha/detector.js +395 -395
  8. package/dist/anticaptcha/solver.js +130 -130
  9. package/dist/cdp/bridge.js +73 -73
  10. package/dist/cdp/connector.js +46 -22
  11. package/dist/cdp/extension.js +34 -34
  12. package/dist/cdp/helpers.js +174 -174
  13. package/dist/cdp/pruner.js +195 -195
  14. package/dist/cdp/ref_cache.js +66 -66
  15. package/dist/cdp/security.js +10 -10
  16. package/dist/cdp/verify.js +57 -57
  17. package/dist/cdp/walker.js +563 -525
  18. package/dist/cli/plugin.js +26 -3
  19. package/dist/config/loader.d.ts +0 -3
  20. package/dist/config/loader.js +0 -9
  21. package/dist/index.js +0 -4
  22. package/dist/intelligence/intent.js +81 -81
  23. package/dist/intelligence/sessionMemory.js +2 -2
  24. package/dist/overlay/ghost.js +22 -22
  25. package/dist/overlay/injector.js +89 -89
  26. package/dist/plugin/extension_bridge.js +54 -54
  27. package/dist/plugin/interface.d.ts +7 -0
  28. package/dist/plugin/loader.js +3 -4
  29. package/dist/plugin/schema.d.ts +11 -0
  30. package/dist/plugin/schema.js +59 -0
  31. package/dist/security/guard.js +18 -18
  32. package/dist/sensors/actionability.js +167 -167
  33. package/dist/sensors/guard.js +29 -29
  34. package/dist/sensors/network.js +15 -17
  35. package/dist/server.d.ts +1 -1
  36. package/dist/server.js +173 -144
  37. package/dist/state/delta.js +6 -7
  38. package/dist/tools/automation.js +85 -85
  39. package/dist/tools/canvas.js +48 -25
  40. package/dist/tools/checkpoint.js +94 -35
  41. package/dist/tools/click.js +52 -41
  42. package/dist/tools/clipboard.js +80 -74
  43. package/dist/tools/design_extract.js +441 -441
  44. package/dist/tools/dialog.js +13 -13
  45. package/dist/tools/emulation.d.ts +2 -0
  46. package/dist/tools/emulation.js +7 -0
  47. package/dist/tools/fill.js +344 -347
  48. package/dist/tools/find.js +78 -52
  49. package/dist/tools/highlight.js +67 -67
  50. package/dist/tools/inspect.js +54 -25
  51. package/dist/tools/key.js +42 -35
  52. package/dist/tools/navigate.d.ts +1 -1
  53. package/dist/tools/navigate.js +35 -35
  54. package/dist/tools/office/parse.js +62 -18
  55. package/dist/tools/pdf.d.ts +9 -0
  56. package/dist/tools/pdf.js +7 -4
  57. package/dist/tools/performance.d.ts +11 -4
  58. package/dist/tools/performance.js +94 -42
  59. package/dist/tools/performance_test.d.ts +2 -2
  60. package/dist/tools/performance_test.js +69 -69
  61. package/dist/tools/pointer.js +116 -116
  62. package/dist/tools/read.js +99 -67
  63. package/dist/tools/recording.js +9 -0
  64. package/dist/tools/scrape.js +146 -146
  65. package/dist/tools/screenshot.d.ts +2 -0
  66. package/dist/tools/screenshot.js +46 -4
  67. package/dist/tools/scroll.d.ts +3 -1
  68. package/dist/tools/scroll.js +13 -15
  69. package/dist/tools/select.d.ts +3 -0
  70. package/dist/tools/select.js +131 -124
  71. package/dist/tools/snapshot.js +8 -6
  72. package/dist/tools/storage.d.ts +5 -1
  73. package/dist/tools/storage.js +16 -0
  74. package/dist/tools/tabs.d.ts +7 -5
  75. package/dist/tools/tabs.js +79 -26
  76. package/dist/tools/thought.js +8 -8
  77. package/dist/tools/type.js +274 -274
  78. package/dist/tools/type_secret.js +23 -23
  79. package/dist/tools/upload.js +3 -3
  80. package/dist/tools/video_compiler.js +9 -0
  81. package/dist/tools/wait.js +35 -35
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/docs/DOCUMENTATION.md +583 -585
  85. package/docs/PLUGINS.md +139 -121
  86. package/docs/TOOLS.md +3 -4
  87. package/docs/architecture/blind-injection.md +83 -83
  88. package/docs/architecture/overview.md +49 -49
  89. package/docs/architecture/security.md +36 -36
  90. package/docs/architecture/transport.md +37 -37
  91. package/docs/cli/cli-commands.md +50 -50
  92. package/docs/cli/cli-flags.md +28 -28
  93. package/docs/cli/cli-scripting.md +35 -35
  94. package/docs/configuration/config-locations.md +23 -23
  95. package/docs/configuration/configuration.md +39 -27
  96. package/docs/configuration/env-variables.md +19 -19
  97. package/docs/configuration/folders.md +62 -55
  98. package/docs/desktop-automation/electron-apps.md +225 -214
  99. package/docs/get-started/installation.md +1 -1
  100. package/docs/get-started/introduction.md +60 -60
  101. package/docs/get-started/quickstart.md +2 -2
  102. package/docs/get-started/setup-wizard.md +27 -27
  103. package/docs/intelligence/site-profiles.md +151 -151
  104. package/docs/tools/tools.md +582 -631
  105. package/docs/troubleshooting/trouble-browser.md +27 -27
  106. package/docs/troubleshooting/trouble-loop.md +31 -31
  107. package/ext-dist/background.js +415 -415
  108. package/ext-dist/content.js +2527 -2527
  109. package/ext-dist/dashboard.html +583 -583
  110. package/ext-dist/hud/hud.js +3 -3
  111. package/ext-dist/hud/index.html +20 -20
  112. package/ext-dist/manifest.json +73 -73
  113. package/ext-dist/styles.css +223 -223
  114. package/mcp.json +796 -805
  115. package/package.json +118 -118
  116. package/server.json +23 -23
  117. package/dist/cdp/walker.test.d.ts +0 -2
  118. package/dist/cdp/walker.test.js +0 -172
  119. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  120. package/dist/intelligence/sessionMemory.test.js +0 -62
  121. package/dist/intelligence/trail.test.d.ts +0 -2
  122. package/dist/intelligence/trail.test.js +0 -110
  123. package/dist/state/cache.test.d.ts +0 -2
  124. package/dist/state/cache.test.js +0 -76
  125. package/dist/state/delta.test.d.ts +0 -2
  126. package/dist/state/delta.test.js +0 -279
  127. package/dist/state/groups.d.ts +0 -15
  128. package/dist/state/groups.js +0 -80
  129. package/dist/tools/accessibility.d.ts +0 -14
  130. package/dist/tools/accessibility.js +0 -32
  131. package/dist/tools/ask.d.ts +0 -14
  132. package/dist/tools/ask.js +0 -49
  133. package/dist/tools/click_and_wait.d.ts +0 -33
  134. package/dist/tools/click_and_wait.js +0 -149
  135. package/dist/tools/drag.d.ts +0 -15
  136. package/dist/tools/drag.js +0 -29
  137. package/dist/tools/geo.d.ts +0 -19
  138. package/dist/tools/geo.js +0 -29
  139. package/dist/tools/github/analyze.d.ts +0 -4
  140. package/dist/tools/github/analyze.js +0 -113
  141. package/dist/tools/github/cache.d.ts +0 -21
  142. package/dist/tools/github/cache.js +0 -28
  143. package/dist/tools/github/index.d.ts +0 -19
  144. package/dist/tools/github/index.js +0 -40
  145. package/dist/tools/github/issues.d.ts +0 -9
  146. package/dist/tools/github/issues.js +0 -88
  147. package/dist/tools/github/pr.d.ts +0 -5
  148. package/dist/tools/github/pr.js +0 -88
  149. package/dist/tools/github/rateLimiter.d.ts +0 -4
  150. package/dist/tools/github/rateLimiter.js +0 -46
  151. package/dist/tools/github/read.d.ts +0 -34
  152. package/dist/tools/github/read.js +0 -74
  153. package/dist/tools/github/repoContext.d.ts +0 -20
  154. package/dist/tools/github/repoContext.js +0 -129
  155. package/dist/tools/hover.d.ts +0 -19
  156. package/dist/tools/hover.js +0 -57
  157. package/dist/tools/integration.test.d.ts +0 -2
  158. package/dist/tools/integration.test.js +0 -96
  159. package/dist/tools/macro_compiler.d.ts +0 -7
  160. package/dist/tools/macro_compiler.js +0 -82
  161. package/dist/tools/mouse.d.ts +0 -21
  162. package/dist/tools/mouse.js +0 -62
  163. package/dist/tools/response.d.ts +0 -129
  164. package/dist/tools/response.js +0 -5
  165. package/dist/tools/wait_for.d.ts +0 -23
  166. package/dist/tools/wait_for.js +0 -30
  167. package/dist/tools/windows.d.ts +0 -22
  168. package/dist/tools/windows.js +0 -57
@@ -1,149 +0,0 @@
1
- // src/tools/click_and_wait.ts
2
- // Consolidated tool: find element + click + wait for response in one CDP round-trip
3
- // Eliminates the common pattern: browser_map → browser_click → browser_wait
4
- import { buildActionMap, findBestMatch, safeSelector, toMarkdownTable } from "../cdp/walker.js";
5
- import { recordAction, trackToolCall, detectStuckLoop } from "../intelligence/trail.js";
6
- import { waitForDOMStable } from "../sensors/guard.js";
7
- import { waitForNetworkIdle } from "../sensors/wait.js";
8
- import { ghostMove, ghostClick } from "../overlay/ghost.js";
9
- import { cacheActionMap, getCachedActionMap } from "../state/cache.js";
10
- import { storeSnapshot, computeDelta } from "../state/delta.js";
11
- import { sessionRegistry } from "../state/session.js";
12
- import { getConfigAgentId } from "../config/loader.js";
13
- import { operationMonitor } from "../sensors/operation_monitor.js";
14
- /**
15
- * Consolidated tool that combines element resolution, clicking, and waiting.
16
- * Eliminates 2-3 tool calls in the common pattern: find → click → wait
17
- */
18
- export async function weaveClickAndWait(session, args, config) {
19
- const { id, label, intent, x, y, waitAfter = 500, waitForNetwork = true, includeMap = false, fast = false, tab_id } = args;
20
- const { Page, DOM, Input } = session;
21
- const startMs = Date.now();
22
- const targetId = sessionRegistry.getActive(getConfigAgentId(config))?.targetId ?? "default";
23
- const operationId = `clickAndWait-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
24
- operationMonitor.startOperation(operationId, 'browser_click_and_wait');
25
- // Handle tab switching if tab_id is provided
26
- let targetSession = session;
27
- if (tab_id) {
28
- const { connectToTab } = await import("../cdp/connector.js");
29
- const client = await connectToTab(tab_id);
30
- targetSession = client.session;
31
- }
32
- // Check for stuck loop before processing
33
- const stuckCheck = detectStuckLoop();
34
- if (stuckCheck.stuck) {
35
- return {
36
- success: false,
37
- error_code: "STUCK_LOOP",
38
- message: stuckCheck.diagnosis || "Agent is stuck in a loop",
39
- suggestion: "Try a different approach or reset the loop counter",
40
- };
41
- }
42
- // Get initial URL
43
- const initialUrl = (await targetSession.Page.getFrameTree()).frameTree.frame.url;
44
- // Build or get cached action map
45
- let map = getCachedActionMap(targetId, initialUrl);
46
- if (!map) {
47
- map = await buildActionMap(targetSession, { lite: true }, targetId);
48
- cacheActionMap(targetId, map);
49
- }
50
- await storeSnapshot(targetId, initialUrl, map.elements);
51
- // Resolve element
52
- let element;
53
- if (id) {
54
- element = map.elements.find((el) => el.id === id || el.id.endsWith(id));
55
- }
56
- if (!element && label) {
57
- element = findBestMatch(map.elements, label);
58
- }
59
- if (!element && intent) {
60
- element = findBestMatch(map.elements, intent);
61
- }
62
- if (!element && !x && !y) {
63
- return {
64
- success: false,
65
- error_code: "ELEMENT_NOT_FOUND",
66
- message: `Element not found with id="${id}", label="${label}", or intent="${intent}"`,
67
- suggestion: "Try browser_map to see available elements, or use x/y coordinates",
68
- };
69
- }
70
- const clickX = x ?? element?.x;
71
- const clickY = y ?? element?.y;
72
- const selector = element ? safeSelector(element) : undefined;
73
- if (!clickX || !clickY) {
74
- return {
75
- success: false,
76
- error_code: "NO_COORDINATES",
77
- message: "Could not determine click coordinates",
78
- suggestion: "Provide explicit x/y coordinates or ensure element has position data",
79
- };
80
- }
81
- // Perform click with ghost move
82
- await ghostMove(targetSession, clickX, clickY, config, element?.label || "target");
83
- await ghostClick(targetSession, clickX, clickY, { pointerType: 'mouse' });
84
- await new Promise(r => setTimeout(r, 30 + Math.random() * 40));
85
- await targetSession.Input.dispatchMouseEvent({
86
- type: 'mouseReleased',
87
- x: clickX,
88
- y: clickY,
89
- button: 'left',
90
- clickCount: 1,
91
- pointerType: 'mouse'
92
- });
93
- // Wait for response
94
- const waitStart = Date.now();
95
- if (waitForNetwork) {
96
- await Promise.all([
97
- waitForDOMStable(targetSession, 50, 300),
98
- waitForNetworkIdle(targetSession, waitAfter, waitAfter * 2).catch(() => { })
99
- ]);
100
- }
101
- else {
102
- await new Promise(r => setTimeout(r, waitAfter));
103
- }
104
- const waitedMs = Date.now() - waitStart;
105
- // Check result
106
- const finalUrl = (await targetSession.Page.getFrameTree()).frameTree.frame.url;
107
- const urlChanged = finalUrl !== initialUrl;
108
- let newMap = map;
109
- let domChanged = false;
110
- if (urlChanged || includeMap) {
111
- newMap = await buildActionMap(targetSession, { lite: true }, targetId);
112
- cacheActionMap(targetId, newMap);
113
- const delta = computeDelta(targetId, initialUrl, newMap.elements);
114
- domChanged = delta ? delta.added.length + delta.removed.length + delta.changed.length > 0 : urlChanged;
115
- }
116
- else {
117
- const delta = computeDelta(targetId, initialUrl, map.elements);
118
- domChanged = delta ? delta.added.length + delta.removed.length + delta.changed.length > 0 : false;
119
- }
120
- const executionMs = Date.now() - startMs;
121
- // Track tool call
122
- trackToolCall("browser_click_and_wait", element?.id || id || label || "coordinates", {
123
- success: true,
124
- urlChanged
125
- });
126
- recordAction({
127
- timestamp: new Date().toISOString(),
128
- tool: "browser_click_and_wait",
129
- input: { ref: element?.id || id || label, waitAfter, waitForNetwork },
130
- result: { success: true, urlChanged, domChanged, waitedMs },
131
- });
132
- operationMonitor.endOperation(operationId);
133
- const result = {
134
- success: true,
135
- clicked: element?.label || "coordinates",
136
- ref: element?.id || id || "coordinates",
137
- execution_ms: executionMs,
138
- tab_id: targetId,
139
- url: finalUrl,
140
- title: newMap?.title || "",
141
- url_changed: urlChanged,
142
- ...(includeMap ? { page_map: newMap ? toMarkdownTable(newMap.elements, finalUrl, newMap.title) : "" } : {}),
143
- dom_changed: domChanged,
144
- stuck_warning: false,
145
- waited_ms: waitedMs,
146
- network_idle: waitForNetwork,
147
- };
148
- return result;
149
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * Weavetab — tools/drag.ts
3
- * browser_drag: mousedown on sourceNodeId, mousemove to targetNodeId, mouseup.
4
- * Weavetab by fy2ne
5
- */
6
- import CDP from "chrome-remote-interface";
7
- import type { Config } from "../config/loader.js";
8
- export interface DragOptions {
9
- sourceNodeId: number;
10
- targetNodeId: number;
11
- }
12
- export declare function browserDrag(session: CDP.Client, options: DragOptions, _config: Config): Promise<{
13
- success: boolean;
14
- }>;
15
- //# sourceMappingURL=drag.d.ts.map
@@ -1,29 +0,0 @@
1
- export async function browserDrag(session, options, _config) {
2
- const { sourceNodeId, targetNodeId } = options;
3
- if (!sourceNodeId || !targetNodeId) {
4
- throw new Error("browser_drag requires sourceNodeId and targetNodeId");
5
- }
6
- const getCoords = async (nodeId) => {
7
- const { model } = await session.DOM.getBoxModel({ backendNodeId: nodeId });
8
- return {
9
- x: (model.content[0] + model.content[2]) / 2,
10
- y: (model.content[1] + model.content[5]) / 2
11
- };
12
- };
13
- const src = await getCoords(sourceNodeId);
14
- const tgt = await getCoords(targetNodeId);
15
- // 1. Move to source
16
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: src.x, y: src.y });
17
- // 2. Mouse down
18
- await session.Input.dispatchMouseEvent({ type: "mousePressed", x: src.x, y: src.y, button: "left", clickCount: 1 });
19
- // Small delay to let UI react (e.g. drag start handlers)
20
- await new Promise(r => setTimeout(r, 100));
21
- // 3. Move to target with button held
22
- // In a real smooth drag, we'd interpolate this, but a direct move often works.
23
- await session.Input.dispatchMouseEvent({ type: "mouseMoved", x: tgt.x, y: tgt.y, button: "left" });
24
- await new Promise(r => setTimeout(r, 100));
25
- // 4. Mouse up
26
- await session.Input.dispatchMouseEvent({ type: "mouseReleased", x: tgt.x, y: tgt.y, button: "left", clickCount: 1 });
27
- return { success: true };
28
- }
29
- //# sourceMappingURL=drag.js.map
@@ -1,19 +0,0 @@
1
- /**
2
- * Weavetab — tools/geo.ts
3
- * browser_geo: Override geolocation and timezone.
4
- * Weavetab by fy2ne
5
- */
6
- import CDP from "chrome-remote-interface";
7
- import type { Config } from "../config/loader.js";
8
- export interface GeoOptions {
9
- action: "set" | "clear";
10
- latitude?: number;
11
- longitude?: number;
12
- accuracy?: number;
13
- timezoneId?: string;
14
- }
15
- export declare function browserGeo(session: CDP.Client, options: GeoOptions, _config: Config): Promise<{
16
- success: boolean;
17
- message: string;
18
- }>;
19
- //# sourceMappingURL=geo.d.ts.map
package/dist/tools/geo.js DELETED
@@ -1,29 +0,0 @@
1
- export async function browserGeo(session, options, _config) {
2
- const { action, latitude, longitude, accuracy = 100, timezoneId } = options;
3
- if (action === "clear") {
4
- await session.Emulation.clearGeolocationOverride();
5
- await session.Emulation.setTimezoneOverride({ timezoneId: "" });
6
- return { success: true, message: "Cleared geolocation and timezone overrides" };
7
- }
8
- if (action === "set") {
9
- let msg = "";
10
- if (latitude !== undefined && longitude !== undefined) {
11
- await session.Emulation.setGeolocationOverride({
12
- latitude,
13
- longitude,
14
- accuracy
15
- });
16
- msg += `Geolocation set to ${latitude}, ${longitude}. `;
17
- }
18
- if (timezoneId) {
19
- await session.Emulation.setTimezoneOverride({ timezoneId });
20
- msg += `Timezone set to ${timezoneId}.`;
21
- }
22
- if (!msg) {
23
- throw new Error("Must provide latitude/longitude or timezoneId to set");
24
- }
25
- return { success: true, message: msg.trim() };
26
- }
27
- throw new Error("Unknown action");
28
- }
29
- //# sourceMappingURL=geo.js.map
@@ -1,4 +0,0 @@
1
- import CDP from "chrome-remote-interface";
2
- import type { Config } from "../../config/loader.js";
3
- import { GithubRepoCache } from "./cache.js";
4
- export declare function githubAnalyze(session: CDP.Client, config: Config, repoSlug?: string): Promise<import("./repoContext.js").GithubToolError | GithubRepoCache>;
@@ -1,113 +0,0 @@
1
- import { getRepoContext, isGithubError } from "./repoContext.js";
2
- import { loadRepoCache, saveRepoCache } from "./cache.js";
3
- import { canMakeApiCall, handleApiResponse } from "./rateLimiter.js";
4
- export async function githubAnalyze(session, config, repoSlug) {
5
- const context = await getRepoContext(session, repoSlug);
6
- if (isGithubError(context))
7
- return context;
8
- const { owner, repo } = context;
9
- // Check cache
10
- const cached = await loadRepoCache(owner, repo, config);
11
- if (cached) {
12
- return cached;
13
- }
14
- // Check rate limit before proceeding
15
- const rateLimitCheck = canMakeApiCall();
16
- if (isGithubError(rateLimitCheck))
17
- return rateLimitCheck;
18
- // 1. Fetch repo metadata
19
- const repoUrl = `https://api.github.com/repos/${owner}/${repo}`;
20
- const repoRes = await fetch(repoUrl, {
21
- headers: {
22
- "Accept": "application/vnd.github.v3+json",
23
- "User-Agent": "Weavetab-MCP"
24
- }
25
- });
26
- const handledRepoRes = handleApiResponse(repoRes);
27
- if (isGithubError(handledRepoRes))
28
- return handledRepoRes;
29
- if (repoRes.status === 404) {
30
- return {
31
- error: "REPO_NOT_FOUND",
32
- message: "Repository not found or not accessible. Note: Only public repositories are supported."
33
- };
34
- }
35
- if (!repoRes.ok) {
36
- return {
37
- error: "API_ERROR",
38
- message: `GitHub API error: ${repoRes.statusText}`
39
- };
40
- }
41
- const repoData = await repoRes.json();
42
- const default_branch = repoData.default_branch || "main";
43
- const description = repoData.description || null;
44
- const language = repoData.language || null;
45
- // Check rate limit again just in case
46
- const rateLimitCheck2 = canMakeApiCall();
47
- if (isGithubError(rateLimitCheck2))
48
- return rateLimitCheck2;
49
- // 2. Fetch file tree
50
- const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${default_branch}?recursive=1`;
51
- const treeRes = await fetch(treeUrl, {
52
- headers: {
53
- "Accept": "application/vnd.github.v3+json",
54
- "User-Agent": "Weavetab-MCP"
55
- }
56
- });
57
- const handledTreeRes = handleApiResponse(treeRes);
58
- if (isGithubError(handledTreeRes))
59
- return handledTreeRes;
60
- let file_tree = [];
61
- if (treeRes.ok) {
62
- const treeData = await treeRes.json();
63
- if (treeData.tree && Array.isArray(treeData.tree)) {
64
- // Pick flat structure - it's more compact and easy to grep
65
- file_tree = treeData.tree.map((t) => ({
66
- path: t.path,
67
- mode: t.mode,
68
- type: t.type,
69
- sha: t.sha,
70
- size: t.size,
71
- url: t.url
72
- }));
73
- }
74
- }
75
- // 3. Fetch README (raw)
76
- let readme_snippet = "";
77
- // Check common README paths
78
- const possibleReadmes = ["README.md", "README", "readme.md", "Readme.md", "README.txt"];
79
- const readmeEntry = file_tree.find(f => possibleReadmes.includes(f.path));
80
- if (readmeEntry) {
81
- const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${default_branch}/${readmeEntry.path}`;
82
- try {
83
- const controller = new AbortController();
84
- const timeout = setTimeout(() => controller.abort(), 10000);
85
- const rawRes = await fetch(rawUrl, { signal: controller.signal });
86
- clearTimeout(timeout);
87
- if (rawRes.ok) {
88
- const fullReadme = await rawRes.text();
89
- // Truncate snippet to first 100 lines
90
- const lines = fullReadme.split('\n');
91
- readme_snippet = lines.slice(0, 100).join('\n');
92
- if (lines.length > 100) {
93
- readme_snippet += "\n\n... (truncated)";
94
- }
95
- }
96
- }
97
- catch {
98
- // ignore
99
- }
100
- }
101
- const cacheData = {
102
- owner,
103
- repo,
104
- default_branch,
105
- description,
106
- language,
107
- file_tree,
108
- readme_snippet,
109
- analyzed_at: Date.now()
110
- };
111
- await saveRepoCache(cacheData);
112
- return cacheData;
113
- }
@@ -1,21 +0,0 @@
1
- import type { Config } from "../../config/loader.js";
2
- export interface TreeEntry {
3
- path: string;
4
- mode: string;
5
- type: string;
6
- sha: string;
7
- size?: number;
8
- url: string;
9
- }
10
- export interface GithubRepoCache {
11
- owner: string;
12
- repo: string;
13
- default_branch: string;
14
- description: string | null;
15
- language: string | null;
16
- file_tree: TreeEntry[];
17
- readme_snippet: string;
18
- analyzed_at: number;
19
- }
20
- export declare function loadRepoCache(owner: string, repo: string, config: Config): Promise<GithubRepoCache | null>;
21
- export declare function saveRepoCache(data: GithubRepoCache): Promise<void>;
@@ -1,28 +0,0 @@
1
- import { readFile, writeFile, mkdir } from "fs/promises";
2
- import { join } from "path";
3
- import { homedir } from "os";
4
- import { getConfigGithubCacheTtlMs } from "../../config/loader.js";
5
- const CACHE_DIR = join(homedir(), ".weavetab-system", "github_cache");
6
- function getCacheFile(owner, repo) {
7
- const safeOwner = owner.replace(/[^a-zA-Z0-9.-]/g, "_");
8
- const safeRepo = repo.replace(/[^a-zA-Z0-9.-]/g, "_");
9
- return join(CACHE_DIR, `${safeOwner}_${safeRepo}.json`);
10
- }
11
- export async function loadRepoCache(owner, repo, config) {
12
- try {
13
- const content = await readFile(getCacheFile(owner, repo), "utf-8");
14
- const data = JSON.parse(content);
15
- const ttl = getConfigGithubCacheTtlMs(config) ?? (30 * 60 * 1000);
16
- if (Date.now() - data.analyzed_at > ttl) {
17
- return null; // Stale cache
18
- }
19
- return data;
20
- }
21
- catch {
22
- return null;
23
- }
24
- }
25
- export async function saveRepoCache(data) {
26
- await mkdir(CACHE_DIR, { recursive: true });
27
- await writeFile(getCacheFile(data.owner, data.repo), JSON.stringify(data), "utf-8");
28
- }
@@ -1,19 +0,0 @@
1
- import CDP from "chrome-remote-interface";
2
- import type { Config } from "../../config/loader.js";
3
- import { githubAnalyze } from "./analyze.js";
4
- import { githubRead } from "./read.js";
5
- import { githubIssues } from "./issues.js";
6
- import { githubGetPr } from "./pr.js";
7
- export { githubAnalyze, githubRead, githubIssues, githubGetPr };
8
- export interface BrowserGithubArgs {
9
- action: "analyze" | "read" | "issues" | "pr";
10
- repo?: string;
11
- paths?: string[];
12
- line_range?: [number, number];
13
- number?: number;
14
- search?: string;
15
- labels?: string[];
16
- state?: "open" | "closed" | "all";
17
- limit?: number;
18
- }
19
- export declare function browserGithub(session: CDP.Client, args: BrowserGithubArgs, cfg: Config): Promise<any>;
@@ -1,40 +0,0 @@
1
- import { githubAnalyze } from "./analyze.js";
2
- import { githubRead } from "./read.js";
3
- import { githubIssues } from "./issues.js";
4
- import { githubGetPr } from "./pr.js";
5
- export { githubAnalyze, githubRead, githubIssues, githubGetPr };
6
- export async function browserGithub(session, args, cfg) {
7
- switch (args.action) {
8
- case "analyze":
9
- return await githubAnalyze(session, cfg, args.repo);
10
- case "read": {
11
- const paths = args.paths || [];
12
- if (!paths.length) {
13
- return {
14
- error: "MISSING_PATHS",
15
- message: "action 'read' requires 'paths' parameter with at least one file path."
16
- };
17
- }
18
- return await githubRead(session, { repo: args.repo, paths, line_range: args.line_range }, cfg);
19
- }
20
- case "issues":
21
- return await githubIssues(session, {
22
- repo: args.repo,
23
- search: args.search,
24
- number: args.number,
25
- labels: args.labels,
26
- state: args.state,
27
- limit: args.limit
28
- });
29
- case "pr":
30
- return await githubGetPr(session, {
31
- repo: args.repo,
32
- number: args.number
33
- });
34
- default:
35
- return {
36
- error: "INVALID_ACTION",
37
- message: `Unknown action: ${args.action}. Must be 'analyze', 'read', 'issues', or 'pr'.`
38
- };
39
- }
40
- }
@@ -1,9 +0,0 @@
1
- import CDP from "chrome-remote-interface";
2
- export declare function githubIssues(session: CDP.Client, args: {
3
- repo?: string;
4
- search?: string;
5
- number?: number;
6
- labels?: string[];
7
- state?: 'open' | 'closed' | 'all';
8
- limit?: number;
9
- }): Promise<any>;
@@ -1,88 +0,0 @@
1
- import { getRepoContext, isGithubError } from "./repoContext.js";
2
- import { canMakeApiCall, handleApiResponse } from "./rateLimiter.js";
3
- export async function githubIssues(session, args) {
4
- const context = await getRepoContext(session, args.repo);
5
- if (isGithubError(context))
6
- return context;
7
- const { owner, repo } = context;
8
- const rateLimitCheck = canMakeApiCall();
9
- if (isGithubError(rateLimitCheck))
10
- return rateLimitCheck;
11
- const headers = {
12
- "Accept": "application/vnd.github.v3+json",
13
- "User-Agent": "Weavetab-MCP"
14
- };
15
- // If number is provided, fetch specific issue + comments
16
- if (args.number) {
17
- const issueUrl = `https://api.github.com/repos/${owner}/${repo}/issues/${args.number}`;
18
- const issueRes = await fetch(issueUrl, { headers });
19
- const handledIssueRes = handleApiResponse(issueRes);
20
- if (isGithubError(handledIssueRes))
21
- return handledIssueRes;
22
- if (!issueRes.ok) {
23
- return { error: "FETCH_FAILED", status: issueRes.status, message: `Failed to fetch issue #${args.number}` };
24
- }
25
- const issueData = await issueRes.json();
26
- let comments = [];
27
- if (issueData.comments > 0) {
28
- const rateLimitCheck2 = canMakeApiCall();
29
- if (!isGithubError(rateLimitCheck2)) {
30
- const commentsUrl = `https://api.github.com/repos/${owner}/${repo}/issues/${args.number}/comments`;
31
- const commentsRes = await fetch(commentsUrl, { headers });
32
- const handledCommentsRes = handleApiResponse(commentsRes);
33
- if (!isGithubError(handledCommentsRes) && commentsRes.ok) {
34
- const commentsData = await commentsRes.json();
35
- comments = commentsData.map((c) => ({
36
- id: c.id,
37
- user: c.user?.login,
38
- body: c.body,
39
- created_at: c.created_at
40
- }));
41
- }
42
- }
43
- }
44
- return {
45
- number: issueData.number,
46
- title: issueData.title,
47
- state: issueData.state,
48
- user: issueData.user?.login,
49
- body: issueData.body,
50
- created_at: issueData.created_at,
51
- labels: issueData.labels?.map((l) => l.name),
52
- comments
53
- };
54
- }
55
- // Else list issues
56
- let urlStr = `https://api.github.com/repos/${owner}/${repo}/issues?per_page=${args.limit || 5}`;
57
- if (args.state)
58
- urlStr += `&state=${args.state}`;
59
- if (args.labels && args.labels.length > 0)
60
- urlStr += `&labels=${args.labels.join(',')}`;
61
- // GitHub Search API is different from Issues API. If search is provided, we should use search API.
62
- if (args.search) {
63
- let q = `repo:${owner}/${repo} is:issue ${args.search}`;
64
- if (args.state && args.state !== 'all')
65
- q += ` is:${args.state}`;
66
- if (args.labels)
67
- args.labels.forEach(l => q += ` label:"${l}"`);
68
- urlStr = `https://api.github.com/search/issues?q=${encodeURIComponent(q)}&per_page=${args.limit || 5}`;
69
- }
70
- const listRes = await fetch(urlStr, { headers });
71
- const handledListRes = handleApiResponse(listRes);
72
- if (isGithubError(handledListRes))
73
- return handledListRes;
74
- if (!listRes.ok) {
75
- return { error: "FETCH_FAILED", status: listRes.status, message: "Failed to list/search issues" };
76
- }
77
- const listData = await listRes.json();
78
- const items = args.search ? listData.items : listData;
79
- return items.map((i) => ({
80
- number: i.number,
81
- title: i.title,
82
- state: i.state,
83
- user: i.user?.login,
84
- labels: i.labels?.map((l) => l.name),
85
- created_at: i.created_at,
86
- comments_count: i.comments
87
- }));
88
- }
@@ -1,5 +0,0 @@
1
- import CDP from "chrome-remote-interface";
2
- export declare function githubGetPr(session: CDP.Client, args: {
3
- repo?: string;
4
- number?: number;
5
- }): Promise<any>;
@@ -1,88 +0,0 @@
1
- import { getRepoContext, isGithubError } from "./repoContext.js";
2
- import { canMakeApiCall, handleApiResponse } from "./rateLimiter.js";
3
- export async function githubGetPr(session, args) {
4
- const context = await getRepoContext(session, args.repo);
5
- if (isGithubError(context))
6
- return context;
7
- const { owner, repo } = context;
8
- const rateLimitCheck = canMakeApiCall();
9
- if (isGithubError(rateLimitCheck))
10
- return rateLimitCheck;
11
- const headers = {
12
- "Accept": "application/vnd.github.v3+json",
13
- "User-Agent": "Weavetab-MCP"
14
- };
15
- if (args.number) {
16
- const prUrl = `https://api.github.com/repos/${owner}/${repo}/pulls/${args.number}`;
17
- const prRes = await fetch(prUrl, { headers });
18
- const handledPrRes = handleApiResponse(prRes);
19
- if (isGithubError(handledPrRes))
20
- return handledPrRes;
21
- if (!prRes.ok) {
22
- return { error: "FETCH_FAILED", status: prRes.status, message: `Failed to fetch PR #${args.number}` };
23
- }
24
- const prData = await prRes.json();
25
- let files_changed = [];
26
- const rateLimitCheck2 = canMakeApiCall();
27
- if (!isGithubError(rateLimitCheck2)) {
28
- const filesUrl = `https://api.github.com/repos/${owner}/${repo}/pulls/${args.number}/files`;
29
- const filesRes = await fetch(filesUrl, { headers });
30
- const handledFilesRes = handleApiResponse(filesRes);
31
- if (!isGithubError(handledFilesRes) && filesRes.ok) {
32
- const filesData = await filesRes.json();
33
- files_changed = filesData.map((f) => f.filename);
34
- }
35
- }
36
- let comments = [];
37
- if (prData.comments > 0) {
38
- const rateLimitCheck3 = canMakeApiCall();
39
- if (!isGithubError(rateLimitCheck3)) {
40
- const commentsUrl = `https://api.github.com/repos/${owner}/${repo}/issues/${args.number}/comments`;
41
- const commentsRes = await fetch(commentsUrl, { headers });
42
- const handledCommentsRes = handleApiResponse(commentsRes);
43
- if (!isGithubError(handledCommentsRes) && commentsRes.ok) {
44
- const commentsData = await commentsRes.json();
45
- comments = commentsData.map((c) => ({
46
- id: c.id,
47
- user: c.user?.login,
48
- body: c.body,
49
- created_at: c.created_at
50
- }));
51
- }
52
- }
53
- }
54
- return {
55
- number: prData.number,
56
- title: prData.title,
57
- state: prData.state,
58
- user: prData.user?.login,
59
- body: prData.body,
60
- created_at: prData.created_at,
61
- merged: prData.merged,
62
- mergeable: prData.mergeable,
63
- additions: prData.additions,
64
- deletions: prData.deletions,
65
- changed_files: prData.changed_files,
66
- files: files_changed,
67
- comments
68
- };
69
- }
70
- // Else list recent PRs
71
- const listUrl = `https://api.github.com/repos/${owner}/${repo}/pulls?state=all&per_page=10`;
72
- const listRes = await fetch(listUrl, { headers });
73
- const handledListRes = handleApiResponse(listRes);
74
- if (isGithubError(handledListRes))
75
- return handledListRes;
76
- if (!listRes.ok) {
77
- return { error: "FETCH_FAILED", status: listRes.status, message: "Failed to list PRs" };
78
- }
79
- const listData = await listRes.json();
80
- return listData.map((pr) => ({
81
- number: pr.number,
82
- title: pr.title,
83
- state: pr.state,
84
- user: pr.user?.login,
85
- created_at: pr.created_at,
86
- updated_at: pr.updated_at
87
- }));
88
- }