@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.
@@ -146,7 +146,4 @@ export declare function getDecisions(params: {
146
146
  }): Promise<{
147
147
  results: any[];
148
148
  }>;
149
- export declare function getTeamStatus(params: {
150
- project?: string;
151
- }): Promise<any>;
152
149
  export { API_URL };
@@ -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 { getTeamStatus } from '../lib/api-client.js';
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 integration (connected via Splicr dashboard).`,
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 projectArg = args.project || 'auto';
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splicr/mcp-server",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
4
4
  "description": "Splicr MCP server — route what you read to what you're building",
5
5
  "type": "module",
6
6
  "bin": "./dist/cli.js",