@weavetab/plugin-github 1.0.0 → 1.1.0

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.
@@ -1,21 +1,18 @@
1
- import type { CdpSession, GithubError, RepoContext } from "./types.js";
1
+ import type { CdpSession, DetailedRepoContext, GithubError, RepoContext } from "./types.js";
2
2
  /**
3
- * Parse a repo slug from a string that can be:
4
- * - "owner/repo"
5
- * - "https://github.com/owner/repo[/anything]"
6
- *
7
- * Returns null (instead of throwing) for any non-string input so callers never
8
- * encounter `TypeError: slug.trim is not a function`.
3
+ * Parses deep context from a GitHub URL string.
4
+ */
5
+ export declare function parseDetailedUrl(urlStr: string): DetailedRepoContext | null;
6
+ /**
7
+ * Parse a repo slug or full URL into a basic RepoContext.
9
8
  */
10
9
  export declare function parseRepoSlug(slug: unknown): RepoContext | null;
10
+ /**
11
+ * Resolve detailed GitHub repository and route context.
12
+ */
13
+ export declare function getDetailedRepoContext(session: CdpSession | undefined, repoSlug?: unknown): Promise<DetailedRepoContext | GithubError>;
11
14
  /**
12
15
  * Resolve the current GitHub repository target.
13
- *
14
- * @param session - CDP session used only to read the active tab URL.
15
- * @param repoSlug - Optional "owner/repo" or full GitHub URL. When provided, it
16
- * is used directly and the active tab is never read or mutated.
17
- *
18
- * GitHub data is always fetched over HTTP, so the active tab is never navigated.
19
16
  */
20
17
  export declare function getRepoContext(session: CdpSession | undefined, repoSlug?: unknown): Promise<RepoContext | GithubError>;
21
18
  export declare function isGithubError(result: unknown): result is GithubError;
@@ -1,31 +1,58 @@
1
1
  /**
2
- * Parse a repo slug from a string that can be:
3
- * - "owner/repo"
4
- * - "https://github.com/owner/repo[/anything]"
5
- *
6
- * Returns null (instead of throwing) for any non-string input so callers never
7
- * encounter `TypeError: slug.trim is not a function`.
2
+ * Parses deep context from a GitHub URL string.
3
+ */
4
+ export function parseDetailedUrl(urlStr) {
5
+ try {
6
+ const u = new URL(urlStr);
7
+ if (u.hostname !== "github.com" && u.hostname !== "www.github.com")
8
+ return null;
9
+ const parts = u.pathname.split("/").filter(Boolean);
10
+ if (parts.length < 2)
11
+ return null;
12
+ const owner = parts[0];
13
+ const repo = parts[1];
14
+ const section = parts[2];
15
+ const ctx = { owner, repo, section };
16
+ if (!section)
17
+ return ctx;
18
+ if (section === "pull" && parts[3]) {
19
+ const num = parseInt(parts[3], 10);
20
+ if (!Number.isNaN(num))
21
+ ctx.pullNumber = num;
22
+ }
23
+ else if (section === "issues" && parts[3]) {
24
+ const num = parseInt(parts[3], 10);
25
+ if (!Number.isNaN(num))
26
+ ctx.issueNumber = num;
27
+ }
28
+ else if (section === "commit" && parts[3]) {
29
+ ctx.commitSha = parts[3];
30
+ }
31
+ else if ((section === "blob" || section === "tree") && parts[3]) {
32
+ ctx.branch = parts[3];
33
+ if (parts.length > 4) {
34
+ ctx.filePath = parts.slice(4).join("/");
35
+ }
36
+ }
37
+ return ctx;
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ /**
44
+ * Parse a repo slug or full URL into a basic RepoContext.
8
45
  */
9
46
  export function parseRepoSlug(slug) {
10
- // Runtime guard: MCP argument dispatch can forward an object / number / null
11
- // when the model omits the field in some call patterns.
12
47
  if (typeof slug !== "string")
13
48
  return null;
14
49
  const trimmed = slug.trim();
15
50
  if (!trimmed)
16
51
  return null;
17
- // Full GitHub URL
18
- try {
19
- const u = new URL(trimmed);
20
- if (u.hostname === "github.com" || u.hostname === "www.github.com") {
21
- const parts = u.pathname.split("/").filter(Boolean);
22
- if (parts.length >= 2)
23
- return { owner: parts[0], repo: parts[1] };
24
- }
25
- }
26
- catch {
27
- /* not a URL — fall through to shorthand parse */
28
- }
52
+ // Try parsing full URL first
53
+ const detailed = parseDetailedUrl(trimmed);
54
+ if (detailed)
55
+ return { owner: detailed.owner, repo: detailed.repo };
29
56
  // "owner/repo" shorthand
30
57
  const parts = trimmed.split("/").filter(Boolean);
31
58
  if (parts.length >= 2)
@@ -33,18 +60,14 @@ export function parseRepoSlug(slug) {
33
60
  return null;
34
61
  }
35
62
  /**
36
- * Resolve the current GitHub repository target.
37
- *
38
- * @param session - CDP session used only to read the active tab URL.
39
- * @param repoSlug - Optional "owner/repo" or full GitHub URL. When provided, it
40
- * is used directly and the active tab is never read or mutated.
41
- *
42
- * GitHub data is always fetched over HTTP, so the active tab is never navigated.
63
+ * Resolve detailed GitHub repository and route context.
43
64
  */
44
- export async function getRepoContext(session, repoSlug) {
65
+ export async function getDetailedRepoContext(session, repoSlug) {
45
66
  const slugStr = typeof repoSlug === "string" && repoSlug.trim() !== "" ? repoSlug.trim() : undefined;
46
- // Explicit slug wins over the current tab URL.
47
67
  if (slugStr) {
68
+ const detailed = parseDetailedUrl(slugStr);
69
+ if (detailed)
70
+ return detailed;
48
71
  const parsed = parseRepoSlug(slugStr);
49
72
  if (!parsed) {
50
73
  return {
@@ -57,7 +80,7 @@ export async function getRepoContext(session, repoSlug) {
57
80
  if (!session) {
58
81
  return {
59
82
  error: "NO_CONTEXT",
60
- message: "No active browser session. Pass a 'repo' argument (e.g. \"owner/repo\") or open a github.com repository in the browser.",
83
+ message: "No active browser session. Pass a 'repo' argument (e.g. \"owner/repo\") or open a github.com page in the browser.",
61
84
  };
62
85
  }
63
86
  let urlStr;
@@ -77,27 +100,20 @@ export async function getRepoContext(session, repoSlug) {
77
100
  if (typeof urlStr !== "string" || !urlStr) {
78
101
  return { error: "CONTEXT_ERROR", message: "Active tab URL is empty." };
79
102
  }
80
- let url;
81
- try {
82
- url = new URL(urlStr);
83
- }
84
- catch {
85
- return { error: "INVALID_URL", message: "Active tab URL is invalid." };
86
- }
87
- if (url.hostname !== "github.com") {
88
- return {
89
- error: "NOT_ON_GITHUB_REPO",
90
- message: "Navigate to a GitHub repository first, or pass a 'repo' argument (e.g. \"owner/repo\").",
91
- };
92
- }
93
- const parts = url.pathname.split("/").filter(Boolean);
94
- if (parts.length < 2) {
103
+ const detailed = parseDetailedUrl(urlStr);
104
+ if (!detailed) {
95
105
  return {
96
106
  error: "NOT_ON_GITHUB_REPO",
97
- message: "Navigate to a specific GitHub repository first (e.g. github.com/owner/repo).",
107
+ message: "Active tab is not on a GitHub repository page. Pass a 'repo' argument (e.g. \"owner/repo\").",
98
108
  };
99
109
  }
100
- return { owner: parts[0], repo: parts[1] };
110
+ return detailed;
111
+ }
112
+ /**
113
+ * Resolve the current GitHub repository target.
114
+ */
115
+ export async function getRepoContext(session, repoSlug) {
116
+ return getDetailedRepoContext(session, repoSlug);
101
117
  }
102
118
  export function isGithubError(result) {
103
119
  return (!!result &&
package/dist/types.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * and on the isolated storage contract exposed by the plugin runtime.
7
7
  */
8
8
  import type { PluginStorage } from "@weavetab/sdk";
9
- /** Minimal CDP client surface used to read the active tab URL. */
9
+ /** Minimal CDP client surface used to read active tab URL and DOM. */
10
10
  export interface CdpSession {
11
11
  Runtime: {
12
12
  evaluate(params: {
@@ -19,11 +19,19 @@ export interface CdpSession {
19
19
  }>;
20
20
  };
21
21
  }
22
- /** A resolved GitHub repository target. */
22
+ /** A resolved GitHub repository target with optional deep route context. */
23
23
  export interface RepoContext {
24
24
  owner: string;
25
25
  repo: string;
26
26
  }
27
+ export interface DetailedRepoContext extends RepoContext {
28
+ section?: "pull" | "issues" | "blob" | "tree" | "commit" | "releases" | string;
29
+ issueNumber?: number;
30
+ pullNumber?: number;
31
+ commitSha?: string;
32
+ branch?: string;
33
+ filePath?: string;
34
+ }
27
35
  /** Structured, non-throwing error returned to the MCP layer. */
28
36
  export interface GithubError {
29
37
  error: string;
@@ -56,12 +64,53 @@ export interface GithubRunContext {
56
64
  cacheTtlMs: number;
57
65
  maxReadLines: number;
58
66
  defaultIssueLimit: number;
67
+ token?: string;
68
+ }
69
+ export interface GithubAnalyzeArgs {
70
+ repo?: string;
71
+ }
72
+ export interface GithubReadArgs {
73
+ repo?: string;
74
+ paths?: string[];
75
+ branch?: string;
76
+ line_range?: number[];
77
+ }
78
+ export interface GithubIssuesArgs {
79
+ repo?: string;
80
+ number?: number;
81
+ search?: string;
82
+ labels?: string[];
83
+ state?: "open" | "closed" | "all";
84
+ limit?: number;
85
+ }
86
+ export interface GithubPullRequestArgs {
87
+ repo?: string;
88
+ number?: number;
89
+ state?: "open" | "closed" | "all";
90
+ limit?: number;
91
+ }
92
+ export interface GithubDiffArgs {
93
+ repo?: string;
94
+ pull_number?: number;
95
+ commit_sha?: string;
96
+ base?: string;
97
+ head?: string;
98
+ format?: "diff" | "patch";
99
+ }
100
+ export interface DiffStats {
101
+ files_changed: string[];
102
+ additions: number;
103
+ deletions: number;
104
+ }
105
+ export interface GithubActiveTabArgs {
106
+ scope?: "auto" | "summary" | "files" | "comments" | "raw";
59
107
  }
60
- /** The single tool's argument shape. */
108
+ /** Backwards-compatible legacy single tool argument shape. */
61
109
  export interface GithubToolArgs {
62
110
  action: "analyze" | "read" | "issues" | "pr";
63
111
  repo?: string;
64
112
  paths?: string[];
113
+ branch?: string;
65
114
  line_range?: number[];
66
115
  number?: number;
67
116
  search?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weavetab/plugin-github",
3
- "version": "1.0.0",
4
- "description": "Official Weavetab GitHub plugin - analyze repository structure, read raw source files, search and inspect issues, and examine pull requests over the GitHub API without burning browser turns.",
3
+ "version": "1.1.0",
4
+ "description": "Official Weavetab GitHub plugin - modular repository analysis, raw source reads, issues, pull requests, raw diffs, and active-tab DOM inspection without burning browser turns or API quota.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "prepublishOnly": "npm run build"
25
25
  },
26
26
  "dependencies": {
27
- "@weavetab/sdk": "^2.5.1"
27
+ "@weavetab/sdk": "^2.5.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^20.19.0",
package/weavetab.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "$schema": "https://weavetab.dev/schema/plugin.json",
3
3
  "name": "@weavetab/plugin-github",
4
- "version": "1.0.0",
5
- "description": "Official Weavetab GitHub plugin. Analyze repository structure, read raw source files, search and inspect issues, and examine pull requests over the public GitHub REST API — fetched directly over HTTP, so no browser turns are consumed.",
4
+ "displayName": "GitHub",
5
+ "version": "1.1.0",
6
+ "description": "Official Weavetab GitHub plugin. Modular tools to analyze repositories, read raw source files, search issues, inspect pull requests, fetch raw diffs, and inspect active tabs directly without burning browser turns or API quota.",
6
7
  "entry": "dist/index.js",
7
8
  "engines": {
8
- "weavetab": "^2.5.1",
9
+ "weavetab": "^2.5.2",
9
10
  "node": ">=18.0.0"
10
11
  },
11
12
  "environments": [
@@ -24,7 +25,8 @@
24
25
  "Developer Tools",
25
26
  "Repositories",
26
27
  "Issues",
27
- "Pull Requests"
28
+ "Pull Requests",
29
+ "Diff"
28
30
  ],
29
31
  "links": {
30
32
  "github": "https://github.com/weavetab/plugin-github",
@@ -39,11 +41,11 @@
39
41
  "config": {
40
42
  "read": true,
41
43
  "write": true,
42
- "reason": "Reads and persists the isolated plugin configuration (cache TTL, read limits, default issue limit) at ~/.weavetab/plugins/@weavetab_plugin-github/config.json."
44
+ "reason": "Reads and persists plugin configuration (cache TTL, read limits, issue limits, optional token) at ~/.weavetab/plugins/@weavetab_plugin-github/config.json."
43
45
  },
44
46
  "cdp": {
45
47
  "evaluate": true,
46
- "reason": "Reads the active tab URL only, to infer the target repository when the optional 'repo' argument is omitted. Never navigates or mutates the page."
48
+ "reason": "Evaluates active tab URL and DOM on github.com to infer targets or inspect active views without API calls."
47
49
  },
48
50
  "network": {
49
51
  "domains": [
@@ -51,63 +53,161 @@
51
53
  "raw.githubusercontent.com",
52
54
  "github.com"
53
55
  ],
54
- "reason": "Fetches repository metadata, file trees, raw source files, issues and pull requests from the public GitHub REST API and raw content CDN."
56
+ "reason": "Fetches repository metadata, file trees, raw source files, issues, pull requests, and raw git diffs from GitHub REST API, CDN, and web endpoints."
55
57
  },
56
58
  "tools": {
57
59
  "add": true,
58
- "reason": "Registers the single 'github' MCP tool."
60
+ "reason": "Registers github_analyze, github_read, github_issues, github_pull_request, github_diff, and github_active_tab MCP tools."
59
61
  }
60
62
  },
61
63
  "tools": [
62
64
  {
63
- "name": "github",
64
- "description": "Interact with GitHub repositories without burning browser turns. Analyze a repo's file tree and README, read raw source files, list/search/inspect issues, and examine pull request details, changed files and comments — all fetched over the GitHub REST API.",
65
+ "name": "github_analyze",
66
+ "description": "Analyze a GitHub repository's metadata, default branch, recursive file tree, and README snippet over the GitHub REST API without burning browser turns.",
65
67
  "inputSchema": {
66
68
  "type": "object",
67
69
  "properties": {
68
- "action": {
69
- "type": "string",
70
- "enum": ["analyze", "read", "issues", "pr"],
71
- "description": "The GitHub operation to perform. 'analyze' = repository metadata, recursive file tree and README snippet. 'read' = raw file contents (requires 'paths'). 'issues' = list, search or inspect issues. 'pr' = list or inspect pull requests."
72
- },
73
70
  "repo": {
74
71
  "type": "string",
75
- "description": "Target repository as an 'owner/repo' slug or a full https://github.com/owner/repo URL. Optional — when omitted, the repository is inferred from the active browser tab (which must be on a github.com repo page)."
76
- },
72
+ "description": "Target repository as 'owner/repo' or full github.com URL. When omitted, inferred from the active browser tab."
73
+ }
74
+ }
75
+ }
76
+ },
77
+ {
78
+ "name": "github_read",
79
+ "description": "Fetch raw source files from a GitHub repository directly over HTTP without burning browser turns or API search quota.",
80
+ "inputSchema": {
81
+ "type": "object",
82
+ "properties": {
77
83
  "paths": {
78
84
  "type": "array",
79
85
  "items": { "type": "string" },
80
- "description": "READ ONLY. File paths relative to the repository root to fetch (e.g. ['src/index.ts', 'README.md'])."
86
+ "description": "File paths relative to the repository root, e.g. ['src/index.ts', 'README.md']."
87
+ },
88
+ "repo": {
89
+ "type": "string",
90
+ "description": "Target repository as 'owner/repo' or full github.com URL. When omitted, inferred from active tab."
91
+ },
92
+ "branch": {
93
+ "type": "string",
94
+ "description": "Target branch or tag (defaults to 'main' or cached default branch). Avoids API discovery calls."
81
95
  },
82
96
  "line_range": {
83
97
  "type": "array",
84
98
  "items": { "type": "number" },
85
- "description": "READ ONLY. Optional [start, end] 1-indexed line range to return from each file. When omitted, files longer than 'maxReadLines' are truncated from the top."
99
+ "description": "Optional [start, end] 1-indexed line range to return per file."
100
+ }
101
+ }
102
+ }
103
+ },
104
+ {
105
+ "name": "github_issues",
106
+ "description": "List, search, or inspect issues and comments in a GitHub repository without burning browser turns.",
107
+ "inputSchema": {
108
+ "type": "object",
109
+ "properties": {
110
+ "repo": {
111
+ "type": "string",
112
+ "description": "Target repository as 'owner/repo' or full github.com URL."
86
113
  },
87
114
  "number": {
88
115
  "type": "number",
89
- "description": "ISSUES / PR ONLY. Issue or pull request number to fetch the full detail and comments for. When omitted, a recent list is returned instead."
116
+ "description": "Specific issue number to fetch full details and timeline comments for."
90
117
  },
91
118
  "search": {
92
119
  "type": "string",
93
- "description": "ISSUES ONLY. Free-text search query to find issues (uses the GitHub Search API, scoped to the target repo)."
120
+ "description": "Free-text query to search issues in the repository."
94
121
  },
95
122
  "labels": {
96
123
  "type": "array",
97
124
  "items": { "type": "string" },
98
- "description": "ISSUES ONLY. Restrict results to issues carrying all of the given labels."
125
+ "description": "Restrict results to these labels."
126
+ },
127
+ "state": {
128
+ "type": "string",
129
+ "enum": ["open", "closed", "all"],
130
+ "description": "Filter by issue state (default 'open')."
131
+ },
132
+ "limit": {
133
+ "type": "number",
134
+ "description": "Maximum number of issues to return (default 5)."
135
+ }
136
+ }
137
+ }
138
+ },
139
+ {
140
+ "name": "github_pull_request",
141
+ "description": "Examine pull request details, changed files, diff stats, and comments, or list recent pull requests in a GitHub repository without burning browser turns.",
142
+ "inputSchema": {
143
+ "type": "object",
144
+ "properties": {
145
+ "repo": {
146
+ "type": "string",
147
+ "description": "Target repository as 'owner/repo' or full github.com URL."
148
+ },
149
+ "number": {
150
+ "type": "number",
151
+ "description": "Pull request number to fetch full detail, changed files, and comments for."
99
152
  },
100
153
  "state": {
101
154
  "type": "string",
102
155
  "enum": ["open", "closed", "all"],
103
- "description": "ISSUES ONLY. Filter by issue state."
156
+ "description": "Filter pull requests by state (default 'all' when listing)."
104
157
  },
105
158
  "limit": {
106
159
  "type": "number",
107
- "description": "ISSUES ONLY. Maximum number of issues to return when listing or searching (default 5)."
160
+ "description": "Maximum pull requests to return when listing (default 10)."
161
+ }
162
+ }
163
+ }
164
+ },
165
+ {
166
+ "name": "github_diff",
167
+ "description": "Fetch raw git diffs or patches for pull requests, commits, or branch comparisons directly from github.com without consuming REST API rate limits.",
168
+ "inputSchema": {
169
+ "type": "object",
170
+ "properties": {
171
+ "repo": {
172
+ "type": "string",
173
+ "description": "Target repository as 'owner/repo' or full github.com URL."
174
+ },
175
+ "pull_number": {
176
+ "type": "number",
177
+ "description": "Pull request number to fetch the diff or patch for."
178
+ },
179
+ "commit_sha": {
180
+ "type": "string",
181
+ "description": "Commit SHA to fetch the diff or patch for."
182
+ },
183
+ "base": {
184
+ "type": "string",
185
+ "description": "Base branch or commit for compare diff (requires 'head')."
186
+ },
187
+ "head": {
188
+ "type": "string",
189
+ "description": "Head branch or commit for compare diff (requires 'base')."
190
+ },
191
+ "format": {
192
+ "type": "string",
193
+ "enum": ["diff", "patch"],
194
+ "description": "Output format: 'diff' (unified diff) or 'patch' (git patch with metadata). Default 'diff'."
195
+ }
196
+ }
197
+ }
198
+ },
199
+ {
200
+ "name": "github_active_tab",
201
+ "description": "Inspect the active browser tab on github.com via local CDP evaluation. Extracts repository overview, open PR/issue discussion, or file content directly from the browser DOM with ZERO API calls. Works on private repositories you are logged into.",
202
+ "inputSchema": {
203
+ "type": "object",
204
+ "properties": {
205
+ "scope": {
206
+ "type": "string",
207
+ "enum": ["auto", "summary", "files", "comments", "raw"],
208
+ "description": "Scope of data extraction from the page DOM (default 'auto')."
108
209
  }
109
- },
110
- "required": ["action"]
210
+ }
111
211
  }
112
212
  }
113
213
  ]