@splicr/mcp-server 0.11.1 → 0.11.2
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/dist/lib/api-client.d.ts
CHANGED
package/dist/lib/api-client.js
CHANGED
|
@@ -104,8 +104,4 @@ export async function getDecisions(params) {
|
|
|
104
104
|
const data = await apiRequest('POST', '/mcp/decisions', params);
|
|
105
105
|
return { results: data.results ?? [] };
|
|
106
106
|
}
|
|
107
|
-
export async function getTeamStatus(params) {
|
|
108
|
-
const query = params.project ? `?project=${encodeURIComponent(params.project)}` : '';
|
|
109
|
-
return await apiRequest('GET', `/mcp/team-status${query}`);
|
|
110
|
-
}
|
|
111
107
|
export { API_URL };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local GitHub Status - uses `gh` CLI instead of stored token.
|
|
3
|
+
* Zero onboarding friction: if the user has `gh auth login` done, this just works.
|
|
4
|
+
*/
|
|
5
|
+
export interface TeamStatus {
|
|
6
|
+
repo: string;
|
|
7
|
+
open_prs: Array<{
|
|
8
|
+
number: number;
|
|
9
|
+
title: string;
|
|
10
|
+
author: string;
|
|
11
|
+
branch: string;
|
|
12
|
+
updated: string;
|
|
13
|
+
draft: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
recent_merges: Array<{
|
|
16
|
+
title: string;
|
|
17
|
+
author: string;
|
|
18
|
+
merged_at: string;
|
|
19
|
+
}>;
|
|
20
|
+
active_branches: Array<{
|
|
21
|
+
name: string;
|
|
22
|
+
}>;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function getLocalGitHubStatus(cwd: string): Promise<TeamStatus>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local GitHub Status - uses `gh` CLI instead of stored token.
|
|
3
|
+
* Zero onboarding friction: if the user has `gh auth login` done, this just works.
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { getGitRemoteUrl, normalizeGitUrl } from './project-detector.js';
|
|
7
|
+
/** Check if `gh` CLI is available and authenticated */
|
|
8
|
+
function isGhAvailable() {
|
|
9
|
+
try {
|
|
10
|
+
execSync('gh auth status', { encoding: 'utf-8', stdio: 'pipe' });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Extract owner/repo from a git remote URL */
|
|
18
|
+
function extractRepoFullName(cwd) {
|
|
19
|
+
const remote = getGitRemoteUrl(cwd);
|
|
20
|
+
if (!remote)
|
|
21
|
+
return null;
|
|
22
|
+
const normalized = normalizeGitUrl(remote);
|
|
23
|
+
// https://github.com/owner/repo -> owner/repo
|
|
24
|
+
const match = normalized.match(/github\.com\/([^/]+\/[^/]+)/);
|
|
25
|
+
return match ? match[1] : null;
|
|
26
|
+
}
|
|
27
|
+
/** Run a gh api command and parse JSON result */
|
|
28
|
+
function ghApi(endpoint) {
|
|
29
|
+
try {
|
|
30
|
+
const result = execSync(`gh api "${endpoint}" --paginate`, {
|
|
31
|
+
encoding: 'utf-8',
|
|
32
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
+
timeout: 10000,
|
|
34
|
+
});
|
|
35
|
+
return JSON.parse(result);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function getLocalGitHubStatus(cwd) {
|
|
42
|
+
const repoFullName = extractRepoFullName(cwd);
|
|
43
|
+
if (!repoFullName) {
|
|
44
|
+
return { repo: 'unknown', open_prs: [], recent_merges: [], active_branches: [], error: 'No GitHub remote found in this directory.' };
|
|
45
|
+
}
|
|
46
|
+
if (!isGhAvailable()) {
|
|
47
|
+
return { repo: repoFullName, open_prs: [], recent_merges: [], active_branches: [], error: 'GitHub CLI (gh) not authenticated. Run `gh auth login` to enable team status.' };
|
|
48
|
+
}
|
|
49
|
+
const status = {
|
|
50
|
+
repo: repoFullName,
|
|
51
|
+
open_prs: [],
|
|
52
|
+
recent_merges: [],
|
|
53
|
+
active_branches: [],
|
|
54
|
+
};
|
|
55
|
+
// Fetch open PRs
|
|
56
|
+
const openPRs = ghApi(`repos/${repoFullName}/pulls?state=open&sort=updated&direction=desc&per_page=10`);
|
|
57
|
+
if (openPRs) {
|
|
58
|
+
status.open_prs = openPRs.map(pr => ({
|
|
59
|
+
number: pr.number,
|
|
60
|
+
title: pr.title,
|
|
61
|
+
author: pr.user.login,
|
|
62
|
+
branch: pr.head?.ref || pr.headRefName || '',
|
|
63
|
+
updated: pr.updated_at || pr.updatedAt || '',
|
|
64
|
+
draft: pr.draft ?? pr.isDraft ?? false,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
// Fetch recently merged PRs
|
|
68
|
+
const mergedPRs = ghApi(`repos/${repoFullName}/pulls?state=closed&sort=updated&direction=desc&per_page=10`);
|
|
69
|
+
if (mergedPRs) {
|
|
70
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
71
|
+
status.recent_merges = mergedPRs
|
|
72
|
+
.filter(pr => {
|
|
73
|
+
const mergedDate = pr.merged_at || pr.mergedAt || pr.updated_at || pr.updatedAt;
|
|
74
|
+
return mergedDate && new Date(mergedDate) >= sevenDaysAgo;
|
|
75
|
+
})
|
|
76
|
+
.map(pr => ({
|
|
77
|
+
title: pr.title,
|
|
78
|
+
author: pr.user.login,
|
|
79
|
+
merged_at: pr.merged_at || pr.mergedAt || pr.updated_at || pr.updatedAt || '',
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
// Fetch branches
|
|
83
|
+
const branches = ghApi(`repos/${repoFullName}/branches?per_page=20`);
|
|
84
|
+
if (branches) {
|
|
85
|
+
const skipBranches = new Set(['main', 'master', 'develop', 'dev', 'staging', 'production']);
|
|
86
|
+
status.active_branches = branches
|
|
87
|
+
.filter(b => !skipBranches.has(b.name))
|
|
88
|
+
.slice(0, 10)
|
|
89
|
+
.map(b => ({ name: b.name }));
|
|
90
|
+
}
|
|
91
|
+
return status;
|
|
92
|
+
}
|
|
@@ -3,12 +3,7 @@ export declare const getTeamStatusSchema: {
|
|
|
3
3
|
description: string;
|
|
4
4
|
inputSchema: {
|
|
5
5
|
type: "object";
|
|
6
|
-
properties: {
|
|
7
|
-
project: {
|
|
8
|
-
type: "string";
|
|
9
|
-
description: string;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
6
|
+
properties: {};
|
|
12
7
|
};
|
|
13
8
|
};
|
|
14
9
|
export declare function handleGetTeamStatus(args: Record<string, unknown>): Promise<string>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { detectProject } from '../lib/project-detector.js';
|
|
1
|
+
import { getLocalGitHubStatus } from '../lib/github-local.js';
|
|
3
2
|
import * as session from '../lib/session-state.js';
|
|
4
3
|
export const getTeamStatusSchema = {
|
|
5
4
|
name: 'get_team_status',
|
|
@@ -10,25 +9,14 @@ Use when:
|
|
|
10
9
|
- Before starting work on a feature (check if someone else is already on it)
|
|
11
10
|
- To understand recent changes to the codebase
|
|
12
11
|
|
|
13
|
-
Requires GitHub
|
|
12
|
+
Requires GitHub CLI (gh) to be authenticated. Run \`gh auth login\` if not set up.`,
|
|
14
13
|
inputSchema: {
|
|
15
14
|
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
project: { type: 'string', description: 'Project name or "auto" (default: auto)' },
|
|
18
|
-
},
|
|
15
|
+
properties: {},
|
|
19
16
|
},
|
|
20
17
|
};
|
|
21
18
|
export async function handleGetTeamStatus(args) {
|
|
22
|
-
const
|
|
23
|
-
let projectName;
|
|
24
|
-
if (projectArg === 'auto') {
|
|
25
|
-
const detected = await detectProject(process.cwd());
|
|
26
|
-
projectName = detected?.name;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
projectName = projectArg;
|
|
30
|
-
}
|
|
31
|
-
const data = await getTeamStatus({ project: projectName });
|
|
19
|
+
const data = await getLocalGitHubStatus(process.cwd());
|
|
32
20
|
session.recordToolCall();
|
|
33
21
|
if (data.error) {
|
|
34
22
|
return data.error;
|