mcp-pr-description 1.0.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.
- package/README.md +101 -0
- package/dist/adapters/github.d.ts +12 -0
- package/dist/adapters/github.d.ts.map +1 -0
- package/dist/adapters/github.js +73 -0
- package/dist/adapters/github.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +151 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/generate-pr.d.ts +18 -0
- package/dist/tools/generate-pr.d.ts.map +1 -0
- package/dist/tools/generate-pr.js +114 -0
- package/dist/tools/generate-pr.js.map +1 -0
- package/dist/tools/learn-style.d.ts +7 -0
- package/dist/tools/learn-style.d.ts.map +1 -0
- package/dist/tools/learn-style.js +65 -0
- package/dist/tools/learn-style.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/file-writer.d.ts +2 -0
- package/dist/utils/file-writer.d.ts.map +1 -0
- package/dist/utils/file-writer.js +13 -0
- package/dist/utils/file-writer.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +83 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/style-cache.d.ts +5 -0
- package/dist/utils/style-cache.d.ts.map +1 -0
- package/dist/utils/style-cache.js +46 -0
- package/dist/utils/style-cache.js.map +1 -0
- package/dist/utils/style-extractor.d.ts +3 -0
- package/dist/utils/style-extractor.d.ts.map +1 -0
- package/dist/utils/style-extractor.js +87 -0
- package/dist/utils/style-extractor.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# mcp-pr-description
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that generates PR descriptions by learning from your team's merged PRs.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Learns your team's style** — Analyzes merged PRs to understand structure, tone, and patterns
|
|
8
|
+
- **Generates PRs in that style** — Uses git diff, branch name, and commits to create matching PRs
|
|
9
|
+
- **Saves to file** — Outputs `PR_DESCRIPTION.md` ready to copy-paste
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# Clone and build
|
|
15
|
+
git clone <repo-url>
|
|
16
|
+
cd mcp-pr-description
|
|
17
|
+
npm install
|
|
18
|
+
npm run build
|
|
19
|
+
|
|
20
|
+
# Or install globally (after publishing)
|
|
21
|
+
npm install -g mcp-pr-description
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
### 1. Get a GitHub Token
|
|
27
|
+
|
|
28
|
+
Create a [GitHub Personal Access Token](https://github.com/settings/tokens) with `repo` scope.
|
|
29
|
+
|
|
30
|
+
### 2. Configure MCP
|
|
31
|
+
|
|
32
|
+
Add to your MCP config file:
|
|
33
|
+
|
|
34
|
+
**VS Code (Augment):** Settings → search "augment mcp" → Edit in settings.json
|
|
35
|
+
**Cursor:** `~/.cursor/mcp.json`
|
|
36
|
+
**Claude Desktop:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"pr-description": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["mcp-pr-description"],
|
|
44
|
+
"env": {
|
|
45
|
+
"GITHUB_TOKEN": "ghp_your_token_here"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> **Note:** Using `npx` automatically resolves the package location. No need to specify a file path.
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
In your AI assistant, use natural language:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
"Learn our team's PR style"
|
|
60
|
+
→ Analyzes last 20 merged PRs, saves patterns to .pr-style.json
|
|
61
|
+
|
|
62
|
+
"Generate a PR for my changes"
|
|
63
|
+
→ Creates PR title + description based on your git changes
|
|
64
|
+
|
|
65
|
+
"Save the PR description"
|
|
66
|
+
→ Writes PR_DESCRIPTION.md to your repo root
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Tools
|
|
70
|
+
|
|
71
|
+
| Tool | Description |
|
|
72
|
+
|------|-------------|
|
|
73
|
+
| `learn_pr_style` | Analyze merged PRs and learn team patterns |
|
|
74
|
+
| `generate_pr` | Generate PR from current git diff |
|
|
75
|
+
| `save_pr_description` | Save PR to `PR_DESCRIPTION.md` |
|
|
76
|
+
| `get_pr_style` | Display learned style patterns |
|
|
77
|
+
|
|
78
|
+
## How It Works
|
|
79
|
+
|
|
80
|
+
1. **Learn** — Fetches your last N merged PRs via GitHub API
|
|
81
|
+
2. **Extract** — Identifies patterns: sections, tone, title format, ticket references
|
|
82
|
+
3. **Cache** — Saves style to `.pr-style.json` (commit this for team sharing)
|
|
83
|
+
4. **Generate** — Uses cached style + current git info to build PR prompt
|
|
84
|
+
5. **Save** — Writes final PR to `PR_DESCRIPTION.md`
|
|
85
|
+
|
|
86
|
+
## Output Files
|
|
87
|
+
|
|
88
|
+
- `.pr-style.json` — Cached team style (add to repo for team sharing)
|
|
89
|
+
- `PR_DESCRIPTION.md` — Generated PR description (copy to GitHub)
|
|
90
|
+
|
|
91
|
+
## Development
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm run dev # Watch mode
|
|
95
|
+
npm run build # Build for production
|
|
96
|
+
npm start # Run server
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PRData } from '../types.js';
|
|
2
|
+
export declare class GitHubAdapter {
|
|
3
|
+
private octokit;
|
|
4
|
+
constructor(token?: string);
|
|
5
|
+
static parseRemoteUrl(remoteUrl: string): {
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
} | null;
|
|
9
|
+
fetchMergedPRs(owner: string, repo: string, count?: number): Promise<PRData[]>;
|
|
10
|
+
verifyAccess(owner: string, repo: string): Promise<boolean>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/adapters/github.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAU;gBAEb,KAAK,CAAC,EAAE,MAAM;IAM1B,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAU1E,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAkDlF,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAQlE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Octokit } from '@octokit/rest';
|
|
2
|
+
export class GitHubAdapter {
|
|
3
|
+
octokit;
|
|
4
|
+
constructor(token) {
|
|
5
|
+
this.octokit = new Octokit({
|
|
6
|
+
auth: token || process.env.GITHUB_TOKEN,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
static parseRemoteUrl(remoteUrl) {
|
|
10
|
+
const sshMatch = remoteUrl.match(/git@github\.com:([^/]+)\/(.+?)(\.git)?$/);
|
|
11
|
+
if (sshMatch)
|
|
12
|
+
return { owner: sshMatch[1], repo: sshMatch[2] };
|
|
13
|
+
const httpsMatch = remoteUrl.match(/github\.com\/([^/]+)\/(.+?)(\.git)?$/);
|
|
14
|
+
if (httpsMatch)
|
|
15
|
+
return { owner: httpsMatch[1], repo: httpsMatch[2] };
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
async fetchMergedPRs(owner, repo, count = 20) {
|
|
19
|
+
const { data: pullRequests } = await this.octokit.pulls.list({
|
|
20
|
+
owner,
|
|
21
|
+
repo,
|
|
22
|
+
state: 'closed',
|
|
23
|
+
sort: 'updated',
|
|
24
|
+
direction: 'desc',
|
|
25
|
+
per_page: count * 2,
|
|
26
|
+
});
|
|
27
|
+
const mergedPRs = pullRequests.filter(pr => pr.merged_at !== null).slice(0, count);
|
|
28
|
+
const detailedPRs = await Promise.all(mergedPRs.map(async (pr) => {
|
|
29
|
+
try {
|
|
30
|
+
const { data: details } = await this.octokit.pulls.get({
|
|
31
|
+
owner,
|
|
32
|
+
repo,
|
|
33
|
+
pull_number: pr.number,
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
number: pr.number,
|
|
37
|
+
title: pr.title,
|
|
38
|
+
body: pr.body || '',
|
|
39
|
+
mergedAt: pr.merged_at || '',
|
|
40
|
+
author: pr.user?.login || 'unknown',
|
|
41
|
+
labels: pr.labels.map((l) => typeof l === 'string' ? l : l.name),
|
|
42
|
+
additions: details.additions,
|
|
43
|
+
deletions: details.deletions,
|
|
44
|
+
changedFiles: details.changed_files,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return {
|
|
49
|
+
number: pr.number,
|
|
50
|
+
title: pr.title,
|
|
51
|
+
body: pr.body || '',
|
|
52
|
+
mergedAt: pr.merged_at || '',
|
|
53
|
+
author: pr.user?.login || 'unknown',
|
|
54
|
+
labels: pr.labels.map((l) => typeof l === 'string' ? l : l.name),
|
|
55
|
+
additions: 0,
|
|
56
|
+
deletions: 0,
|
|
57
|
+
changedFiles: 0,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}));
|
|
61
|
+
return detailedPRs;
|
|
62
|
+
}
|
|
63
|
+
async verifyAccess(owner, repo) {
|
|
64
|
+
try {
|
|
65
|
+
await this.octokit.repos.get({ owner, repo });
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/adapters/github.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,MAAM,OAAO,aAAa;IAChB,OAAO,CAAU;IAEzB,YAAY,KAAc;QACxB,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC;YACzB,IAAI,EAAE,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;SACxC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,SAAiB;QACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAC5E,IAAI,QAAQ;YAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAE/D,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAC3E,IAAI,UAAU;YAAE,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAErE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,KAAa,EAAE,IAAY,EAAE,QAAgB,EAAE;QAClE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;YAC3D,KAAK;YACL,IAAI;YACJ,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,MAAM;YACjB,QAAQ,EAAE,KAAK,GAAG,CAAC;SACpB,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QAEnF,MAAM,WAAW,GAAa,MAAM,OAAO,CAAC,GAAG,CAC7C,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC;oBACrD,KAAK;oBACL,IAAI;oBACJ,WAAW,EAAE,EAAE,CAAC,MAAM;iBACvB,CAAC,CAAC;gBACH,OAAO;oBACL,MAAM,EAAE,EAAE,CAAC,MAAM;oBACjB,KAAK,EAAE,EAAE,CAAC,KAAK;oBACf,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE;oBACnB,QAAQ,EAAE,EAAE,CAAC,SAAS,IAAI,EAAE;oBAC5B,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,SAAS;oBACnC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;oBAChE,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,YAAY,EAAE,OAAO,CAAC,aAAa;iBACpC,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO;oBACL,MAAM,EAAE,EAAE,CAAC,MAAM;oBACjB,KAAK,EAAE,EAAE,CAAC,KAAK;oBACf,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE;oBACnB,QAAQ,EAAE,EAAE,CAAC,SAAS,IAAI,EAAE;oBAC5B,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,IAAI,SAAS;oBACnC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;oBAChE,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,CAAC;oBACZ,YAAY,EAAE,CAAC;iBAChB,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CACH,CAAC;QAEF,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAa,EAAE,IAAY;QAC5C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { learnPRStyle } from './tools/learn-style.js';
|
|
6
|
+
import { generatePR, savePRDescription, getLearnedStyle } from './tools/generate-pr.js';
|
|
7
|
+
const TOOLS = [
|
|
8
|
+
{
|
|
9
|
+
name: 'learn_pr_style',
|
|
10
|
+
description: 'Learn PR writing style from merged pull requests. ' +
|
|
11
|
+
'Analyzes structure, tone, formatting, and common patterns. ' +
|
|
12
|
+
'Run once per repo. Saves to .pr-style.json',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
count: {
|
|
17
|
+
type: 'number',
|
|
18
|
+
description: 'Number of PRs to analyze (default: 20)',
|
|
19
|
+
default: 20,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'generate_pr',
|
|
26
|
+
description: 'Generate PR title and description from current git changes. ' +
|
|
27
|
+
'Uses learned team style if available. ' +
|
|
28
|
+
'Analyzes branch name, commits, and file changes.',
|
|
29
|
+
inputSchema: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
baseBranch: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Base branch to compare (default: main)',
|
|
35
|
+
default: 'main',
|
|
36
|
+
},
|
|
37
|
+
includeDiff: {
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
description: 'Include code diff in context (default: false)',
|
|
40
|
+
default: false,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'save_pr_description',
|
|
47
|
+
description: 'Save generated PR title and description to PR_DESCRIPTION.md file. ' +
|
|
48
|
+
'Call this after generate_pr to save the output.',
|
|
49
|
+
inputSchema: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
title: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'PR title',
|
|
55
|
+
},
|
|
56
|
+
body: {
|
|
57
|
+
type: 'string',
|
|
58
|
+
description: 'PR description body (markdown)',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
required: ['title', 'body'],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'get_pr_style',
|
|
66
|
+
description: 'Show the learned PR style for this repository.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
const server = new Server({ name: 'mcp-pr-description', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
74
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
75
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
76
|
+
const { name, arguments: args = {} } = request.params;
|
|
77
|
+
try {
|
|
78
|
+
switch (name) {
|
|
79
|
+
case 'learn_pr_style': {
|
|
80
|
+
const count = typeof args.count === 'number' ? args.count : 20;
|
|
81
|
+
const result = await learnPRStyle(count);
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: 'text', text: result.displayText }],
|
|
84
|
+
isError: !result.success,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
case 'generate_pr': {
|
|
88
|
+
const baseBranch = typeof args.baseBranch === 'string' ? args.baseBranch : 'main';
|
|
89
|
+
const includeDiff = typeof args.includeDiff === 'boolean' ? args.includeDiff : false;
|
|
90
|
+
const result = await generatePR(baseBranch, includeDiff);
|
|
91
|
+
if (!result.success) {
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: 'text', text: `❌ ${result.message}` }],
|
|
94
|
+
isError: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const prefix = result.hasLearnedStyle
|
|
98
|
+
? ''
|
|
99
|
+
: '⚠️ No learned style. Run learn_pr_style first.\n\n';
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: 'text', text: prefix + (result.prompt || '') }],
|
|
102
|
+
isError: false,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
case 'save_pr_description': {
|
|
106
|
+
const title = typeof args.title === 'string' ? args.title : '';
|
|
107
|
+
const body = typeof args.body === 'string' ? args.body : '';
|
|
108
|
+
if (!title || !body) {
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: 'text', text: '❌ Title and body are required' }],
|
|
111
|
+
isError: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const result = await savePRDescription(title, body);
|
|
115
|
+
return {
|
|
116
|
+
content: [{ type: 'text', text: result.success ? `✅ ${result.message}` : `❌ ${result.message}` }],
|
|
117
|
+
isError: !result.success,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
case 'get_pr_style': {
|
|
121
|
+
const result = await getLearnedStyle();
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: 'text', text: result.displayText }],
|
|
124
|
+
isError: !result.success,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
return {
|
|
129
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
130
|
+
isError: true,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: 'text', text: `Error: ${msg}` }],
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
async function main() {
|
|
143
|
+
const transport = new StdioServerTransport();
|
|
144
|
+
await server.connect(transport);
|
|
145
|
+
console.error('mcp-pr-description server started');
|
|
146
|
+
}
|
|
147
|
+
main().catch((error) => {
|
|
148
|
+
console.error('Fatal error:', error);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAExF,MAAM,KAAK,GAAG;IACZ;QACE,IAAI,EAAE,gBAAgB;QACtB,WAAW,EACT,oDAAoD;YACpD,6DAA6D;YAC7D,4CAA4C;QAC9C,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,wCAAwC;oBACrD,OAAO,EAAE,EAAE;iBACZ;aACF;SACF;KACF;IACD;QACE,IAAI,EAAE,aAAa;QACnB,WAAW,EACT,8DAA8D;YAC9D,wCAAwC;YACxC,kDAAkD;QACpD,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,wCAAwC;oBACrD,OAAO,EAAE,MAAM;iBAChB;gBACD,WAAW,EAAE;oBACX,IAAI,EAAE,SAAS;oBACf,WAAW,EAAE,+CAA+C;oBAC5D,OAAO,EAAE,KAAK;iBACf;aACF;SACF;KACF;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EACT,qEAAqE;YACrE,iDAAiD;QACnD,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,UAAU;iBACxB;gBACD,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,gCAAgC;iBAC9C;aACF;YACD,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC;SAC5B;KACF;IACD;QACE,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,gDAAgD;QAC7D,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE,EAAE;SACf;KACF;CACF,CAAC;AAEF,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,EAChD,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;AAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AAEjF,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAEtD,IAAI,CAAC;QACH,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/D,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,KAAK,CAAC,CAAC;gBACzC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;oBACrD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO;iBACzB,CAAC;YACJ,CAAC;YAED,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC;gBAClF,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC;gBACrF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;gBAEzD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpB,OAAO;wBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;wBACxD,OAAO,EAAE,IAAI;qBACd,CAAC;gBACJ,CAAC;gBAED,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe;oBACnC,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,oDAAoD,CAAC;gBAEzD,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;oBACjE,OAAO,EAAE,KAAK;iBACf,CAAC;YACJ,CAAC;YAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;gBAC3B,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/D,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;gBAE5D,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;oBACpB,OAAO;wBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;wBAClE,OAAO,EAAE,IAAI;qBACd,CAAC;gBACJ,CAAC;gBAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBACpD,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;oBACjG,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO;iBACzB,CAAC;YACJ,CAAC;YAED,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;gBACvC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;oBACrD,OAAO,EAAE,CAAC,MAAM,CAAC,OAAO;iBACzB,CAAC;YACJ,CAAC;YAED;gBACE,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;oBAC1D,OAAO,EAAE,IAAI;iBACd,CAAC;QACN,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACrE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,EAAE,EAAE,CAAC;YAClD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;AACrD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface GeneratePRResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
prompt?: string;
|
|
4
|
+
filePath?: string;
|
|
5
|
+
message: string;
|
|
6
|
+
hasLearnedStyle: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function generatePR(baseBranch?: string, includeDiff?: boolean, writeFile?: boolean): Promise<GeneratePRResult>;
|
|
9
|
+
export declare function savePRDescription(title: string, body: string): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
filePath?: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function getLearnedStyle(): Promise<{
|
|
15
|
+
success: boolean;
|
|
16
|
+
displayText: string;
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=generate-pr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-pr.d.ts","sourceRoot":"","sources":["../../src/tools/generate-pr.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAsB,UAAU,CAC9B,UAAU,GAAE,MAAe,EAC3B,WAAW,GAAE,OAAe,EAC5B,SAAS,GAAE,OAAc,GACxB,OAAO,CAAC,gBAAgB,CAAC,CAuB3B;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5E,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAgBD;AA4DD,wBAAsB,eAAe,IAAI,OAAO,CAAC;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC,CAuBD"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { getGitInfo, getChangeSummary, getRepositoryRoot } from '../utils/git.js';
|
|
2
|
+
import { loadCachedStyle, formatStyleForDisplay } from '../utils/style-cache.js';
|
|
3
|
+
import { writePRDescriptionFile } from '../utils/file-writer.js';
|
|
4
|
+
export async function generatePR(baseBranch = 'main', includeDiff = false, writeFile = true) {
|
|
5
|
+
try {
|
|
6
|
+
const repoRoot = await getRepositoryRoot();
|
|
7
|
+
const gitInfo = await getGitInfo(undefined, baseBranch);
|
|
8
|
+
const changeSummary = await getChangeSummary(undefined, baseBranch);
|
|
9
|
+
const cachedStyle = await loadCachedStyle(repoRoot);
|
|
10
|
+
const prompt = buildPrompt(gitInfo, changeSummary, cachedStyle, includeDiff);
|
|
11
|
+
return {
|
|
12
|
+
success: true,
|
|
13
|
+
prompt,
|
|
14
|
+
message: 'PR context ready',
|
|
15
|
+
hasLearnedStyle: cachedStyle !== null,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
20
|
+
return {
|
|
21
|
+
success: false,
|
|
22
|
+
message: msg,
|
|
23
|
+
hasLearnedStyle: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export async function savePRDescription(title, body) {
|
|
28
|
+
try {
|
|
29
|
+
const repoRoot = await getRepositoryRoot();
|
|
30
|
+
const filePath = await writePRDescriptionFile(repoRoot, title, body);
|
|
31
|
+
return {
|
|
32
|
+
success: true,
|
|
33
|
+
filePath,
|
|
34
|
+
message: `PR description saved to ${filePath}`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
39
|
+
return {
|
|
40
|
+
success: false,
|
|
41
|
+
message: `Failed to save: ${msg}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function buildPrompt(gitInfo, changeSummary, style, includeDiff) {
|
|
46
|
+
const sections = [
|
|
47
|
+
'Generate a pull request title and description based on the following.',
|
|
48
|
+
'',
|
|
49
|
+
];
|
|
50
|
+
if (style) {
|
|
51
|
+
sections.push('## Team PR Style (FOLLOW THIS EXACTLY)');
|
|
52
|
+
sections.push('');
|
|
53
|
+
sections.push(formatStyleForDisplay(style));
|
|
54
|
+
sections.push('');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
sections.push('## Note: No learned style found. Run `learn_pr_style` first for better results.');
|
|
58
|
+
sections.push('');
|
|
59
|
+
}
|
|
60
|
+
sections.push(`## Branch: \`${gitInfo.branchName}\``);
|
|
61
|
+
sections.push('');
|
|
62
|
+
if (gitInfo.commitMessages.length > 0) {
|
|
63
|
+
sections.push('## Commits');
|
|
64
|
+
gitInfo.commitMessages.forEach(m => sections.push(`- ${m}`));
|
|
65
|
+
sections.push('');
|
|
66
|
+
}
|
|
67
|
+
sections.push('## Files Changed');
|
|
68
|
+
sections.push('```');
|
|
69
|
+
sections.push(changeSummary);
|
|
70
|
+
sections.push('```');
|
|
71
|
+
sections.push('');
|
|
72
|
+
if (includeDiff && gitInfo.diff) {
|
|
73
|
+
const maxLen = 6000;
|
|
74
|
+
const diff = gitInfo.diff.length > maxLen
|
|
75
|
+
? gitInfo.diff.slice(0, maxLen) + '\n... (truncated)'
|
|
76
|
+
: gitInfo.diff;
|
|
77
|
+
sections.push('## Diff');
|
|
78
|
+
sections.push('```diff');
|
|
79
|
+
sections.push(diff);
|
|
80
|
+
sections.push('```');
|
|
81
|
+
sections.push('');
|
|
82
|
+
}
|
|
83
|
+
sections.push('## Instructions');
|
|
84
|
+
sections.push('Generate:');
|
|
85
|
+
sections.push('1. **Title** - concise, following team style if available');
|
|
86
|
+
sections.push('2. **Description** - full PR body with appropriate sections');
|
|
87
|
+
sections.push('');
|
|
88
|
+
sections.push('After generating, call `save_pr_description` to save as PR_DESCRIPTION.md');
|
|
89
|
+
return sections.join('\n');
|
|
90
|
+
}
|
|
91
|
+
export async function getLearnedStyle() {
|
|
92
|
+
try {
|
|
93
|
+
const repoRoot = await getRepositoryRoot();
|
|
94
|
+
const style = await loadCachedStyle(repoRoot);
|
|
95
|
+
if (!style) {
|
|
96
|
+
return {
|
|
97
|
+
success: false,
|
|
98
|
+
displayText: '❌ No learned style. Run `learn_pr_style` first.',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
displayText: formatStyleForDisplay(style),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
displayText: `❌ Error: ${msg}`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=generate-pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-pr.js","sourceRoot":"","sources":["../../src/tools/generate-pr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learn-style.d.ts","sourceRoot":"","sources":["../../src/tools/learn-style.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,YAAY,CAAC,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkEhF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { GitHubAdapter } from '../adapters/github.js';
|
|
2
|
+
import { extractStyleFromPRs } from '../utils/style-extractor.js';
|
|
3
|
+
import { saveCachedStyle, formatStyleForDisplay } from '../utils/style-cache.js';
|
|
4
|
+
import { getRepositoryRoot, getRemoteUrl } from '../utils/git.js';
|
|
5
|
+
export async function learnPRStyle(count = 20) {
|
|
6
|
+
try {
|
|
7
|
+
const repoRoot = await getRepositoryRoot();
|
|
8
|
+
const remoteUrl = await getRemoteUrl();
|
|
9
|
+
const repoInfo = GitHubAdapter.parseRemoteUrl(remoteUrl);
|
|
10
|
+
if (!repoInfo) {
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
message: `Could not parse GitHub repository from: ${remoteUrl}`,
|
|
14
|
+
displayText: '❌ Not a GitHub repository or invalid remote URL',
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (!process.env.GITHUB_TOKEN) {
|
|
18
|
+
return {
|
|
19
|
+
success: false,
|
|
20
|
+
message: 'GITHUB_TOKEN not set',
|
|
21
|
+
displayText: '❌ GITHUB_TOKEN environment variable is required',
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const github = new GitHubAdapter();
|
|
25
|
+
const { owner, repo } = repoInfo;
|
|
26
|
+
const hasAccess = await github.verifyAccess(owner, repo);
|
|
27
|
+
if (!hasAccess) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
message: `No access to ${owner}/${repo}`,
|
|
31
|
+
displayText: `❌ Cannot access ${owner}/${repo}. Check your GITHUB_TOKEN.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const prs = await github.fetchMergedPRs(owner, repo, count);
|
|
35
|
+
if (prs.length === 0) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
message: 'No merged PRs found',
|
|
39
|
+
displayText: '⚠️ No merged PRs found in repository',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const style = extractStyleFromPRs(prs, owner, repo);
|
|
43
|
+
await saveCachedStyle(repoRoot, style);
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
message: `Learned from ${prs.length} PRs`,
|
|
47
|
+
displayText: [
|
|
48
|
+
`✅ Learned PR style from ${prs.length} merged PRs!`,
|
|
49
|
+
'',
|
|
50
|
+
formatStyleForDisplay(style),
|
|
51
|
+
'',
|
|
52
|
+
`📁 Saved to: ${repoRoot}/.pr-style.json`,
|
|
53
|
+
].join('\n'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
message: msg,
|
|
61
|
+
displayText: `❌ Error: ${msg}`,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=learn-style.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learn-style.js","sourceRoot":"","sources":["../../src/tools/learn-style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAQlE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE;IACnD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,iBAAiB,EAAE,CAAC;QAC3C,MAAM,SAAS,GAAG,MAAM,YAAY,EAAE,CAAC;QAEvC,MAAM,QAAQ,GAAG,aAAa,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,2CAA2C,SAAS,EAAE;gBAC/D,WAAW,EAAE,iDAAiD;aAC/D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YAC9B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,sBAAsB;gBAC/B,WAAW,EAAE,iDAAiD;aAC/D,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;QACnC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC;QAEjC,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACzD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,gBAAgB,KAAK,IAAI,IAAI,EAAE;gBACxC,WAAW,EAAE,mBAAmB,KAAK,IAAI,IAAI,4BAA4B;aAC1E,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QAE5D,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,qBAAqB;gBAC9B,WAAW,EAAE,sCAAsC;aACpD,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAEvC,OAAO;YACL,OAAO,EAAE,IAAI;YACb,OAAO,EAAE,gBAAgB,GAAG,CAAC,MAAM,MAAM;YACzC,WAAW,EAAE;gBACX,2BAA2B,GAAG,CAAC,MAAM,cAAc;gBACnD,EAAE;gBACF,qBAAqB,CAAC,KAAK,CAAC;gBAC5B,EAAE;gBACF,gBAAgB,QAAQ,iBAAiB;aAC1C,CAAC,IAAI,CAAC,IAAI,CAAC;SACb,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACrE,OAAO;YACL,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,GAAG;YACZ,WAAW,EAAE,YAAY,GAAG,EAAE;SAC/B,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface PRData {
|
|
2
|
+
number: number;
|
|
3
|
+
title: string;
|
|
4
|
+
body: string;
|
|
5
|
+
mergedAt: string;
|
|
6
|
+
author: string;
|
|
7
|
+
labels: string[];
|
|
8
|
+
additions: number;
|
|
9
|
+
deletions: number;
|
|
10
|
+
changedFiles: number;
|
|
11
|
+
}
|
|
12
|
+
export interface LearnedStyle {
|
|
13
|
+
sections: string[];
|
|
14
|
+
usesCheckboxes: boolean;
|
|
15
|
+
usesBulletPoints: boolean;
|
|
16
|
+
usesNumberedLists: boolean;
|
|
17
|
+
titlePattern: string | null;
|
|
18
|
+
titlePrefixExamples: string[];
|
|
19
|
+
averageBodyLength: number;
|
|
20
|
+
averageLineCount: number;
|
|
21
|
+
mentionsTickets: boolean;
|
|
22
|
+
ticketPattern: string | null;
|
|
23
|
+
tone: 'formal' | 'casual' | 'mixed';
|
|
24
|
+
usesFirstPerson: boolean;
|
|
25
|
+
usesEmojis: boolean;
|
|
26
|
+
alwaysIncludes: string[];
|
|
27
|
+
sampleCount: number;
|
|
28
|
+
lastUpdated: string;
|
|
29
|
+
repositoryInfo: {
|
|
30
|
+
owner: string;
|
|
31
|
+
repo: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export interface GitInfo {
|
|
35
|
+
diff: string;
|
|
36
|
+
branchName: string;
|
|
37
|
+
commitMessages: string[];
|
|
38
|
+
stagedFiles: string[];
|
|
39
|
+
repositoryRoot: string;
|
|
40
|
+
}
|
|
41
|
+
export interface GeneratedPR {
|
|
42
|
+
title: string;
|
|
43
|
+
body: string;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACpC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6CAA6C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-writer.d.ts","sourceRoot":"","sources":["../../src/utils/file-writer.ts"],"names":[],"mappings":"AAGA,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAWjB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { writeFile } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
export async function writePRDescriptionFile(repoRoot, title, body) {
|
|
4
|
+
const filename = 'PR_DESCRIPTION.md';
|
|
5
|
+
const filepath = join(repoRoot, filename);
|
|
6
|
+
const content = `# ${title}
|
|
7
|
+
|
|
8
|
+
${body}
|
|
9
|
+
`;
|
|
10
|
+
await writeFile(filepath, content, 'utf-8');
|
|
11
|
+
return filepath;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=file-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-writer.js","sourceRoot":"","sources":["../../src/utils/file-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,QAAgB,EAChB,KAAa,EACb,IAAY;IAEZ,MAAM,QAAQ,GAAG,mBAAmB,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,KAAK,KAAK;;EAE1B,IAAI;CACL,CAAC;IAEA,MAAM,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { GitInfo } from '../types.js';
|
|
2
|
+
export declare function getRepositoryRoot(cwd?: string): Promise<string>;
|
|
3
|
+
export declare function getBranchName(cwd?: string): Promise<string>;
|
|
4
|
+
export declare function getRemoteUrl(cwd?: string): Promise<string>;
|
|
5
|
+
export declare function getDiff(cwd?: string, baseBranch?: string): Promise<string>;
|
|
6
|
+
export declare function getCommitMessages(cwd?: string, baseBranch?: string): Promise<string[]>;
|
|
7
|
+
export declare function getChangeSummary(cwd?: string, baseBranch?: string): Promise<string>;
|
|
8
|
+
export declare function getGitInfo(cwd?: string, baseBranch?: string): Promise<GitInfo>;
|
|
9
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAY3C,wBAAsB,iBAAiB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAErE;AAED,wBAAsB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjE;AAED,wBAAsB,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMhE;AAED,wBAAsB,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBhF;AAED,wBAAsB,iBAAiB,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAc5F;AAED,wBAAsB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAYzF;AAED,wBAAsB,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASpF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
const execAsync = promisify(exec);
|
|
4
|
+
async function gitCommand(command, cwd) {
|
|
5
|
+
const { stdout } = await execAsync(command, {
|
|
6
|
+
cwd: cwd || process.cwd(),
|
|
7
|
+
maxBuffer: 10 * 1024 * 1024
|
|
8
|
+
});
|
|
9
|
+
return stdout.trim();
|
|
10
|
+
}
|
|
11
|
+
export async function getRepositoryRoot(cwd) {
|
|
12
|
+
return gitCommand('git rev-parse --show-toplevel', cwd);
|
|
13
|
+
}
|
|
14
|
+
export async function getBranchName(cwd) {
|
|
15
|
+
return gitCommand('git branch --show-current', cwd);
|
|
16
|
+
}
|
|
17
|
+
export async function getRemoteUrl(cwd) {
|
|
18
|
+
try {
|
|
19
|
+
return await gitCommand('git remote get-url origin', cwd);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return gitCommand('git remote get-url upstream', cwd);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function getDiff(cwd, baseBranch) {
|
|
26
|
+
const base = baseBranch || 'main';
|
|
27
|
+
try {
|
|
28
|
+
const diff = await gitCommand(`git diff ${base}...HEAD`, cwd);
|
|
29
|
+
if (diff)
|
|
30
|
+
return diff;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
try {
|
|
34
|
+
const diff = await gitCommand(`git diff origin/${base}...HEAD`, cwd);
|
|
35
|
+
if (diff)
|
|
36
|
+
return diff;
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
}
|
|
40
|
+
const staged = await gitCommand('git diff --cached', cwd);
|
|
41
|
+
const unstaged = await gitCommand('git diff', cwd);
|
|
42
|
+
return [staged, unstaged].filter(Boolean).join('\n');
|
|
43
|
+
}
|
|
44
|
+
export async function getCommitMessages(cwd, baseBranch) {
|
|
45
|
+
const base = baseBranch || 'main';
|
|
46
|
+
try {
|
|
47
|
+
const log = await gitCommand(`git log ${base}..HEAD --pretty=format:"%s"`, cwd);
|
|
48
|
+
return log.split('\n').filter(Boolean);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
try {
|
|
52
|
+
const log = await gitCommand(`git log origin/${base}..HEAD --pretty=format:"%s"`, cwd);
|
|
53
|
+
return log.split('\n').filter(Boolean);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export async function getChangeSummary(cwd, baseBranch) {
|
|
61
|
+
const base = baseBranch || 'main';
|
|
62
|
+
try {
|
|
63
|
+
return await gitCommand(`git diff ${base}...HEAD --stat`, cwd);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
try {
|
|
67
|
+
return await gitCommand(`git diff origin/${base}...HEAD --stat`, cwd);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return await gitCommand('git diff --stat', cwd);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function getGitInfo(cwd, baseBranch) {
|
|
75
|
+
const [repositoryRoot, branchName, diff, commitMessages] = await Promise.all([
|
|
76
|
+
getRepositoryRoot(cwd),
|
|
77
|
+
getBranchName(cwd),
|
|
78
|
+
getDiff(cwd, baseBranch),
|
|
79
|
+
getCommitMessages(cwd, baseBranch),
|
|
80
|
+
]);
|
|
81
|
+
return { repositoryRoot, branchName, diff, commitMessages, stagedFiles: [] };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=git.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LearnedStyle } from '../types.js';
|
|
2
|
+
export declare function loadCachedStyle(repoRoot: string): Promise<LearnedStyle | null>;
|
|
3
|
+
export declare function saveCachedStyle(repoRoot: string, style: LearnedStyle): Promise<void>;
|
|
4
|
+
export declare function formatStyleForDisplay(style: LearnedStyle): string;
|
|
5
|
+
//# sourceMappingURL=style-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-cache.d.ts","sourceRoot":"","sources":["../../src/utils/style-cache.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAUpF;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAG1F;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CA0BjE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
const CACHE_FILENAME = '.pr-style.json';
|
|
5
|
+
export async function loadCachedStyle(repoRoot) {
|
|
6
|
+
const cachePath = join(repoRoot, CACHE_FILENAME);
|
|
7
|
+
if (!existsSync(cachePath))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
const content = await readFile(cachePath, 'utf-8');
|
|
11
|
+
return JSON.parse(content);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export async function saveCachedStyle(repoRoot, style) {
|
|
18
|
+
const cachePath = join(repoRoot, CACHE_FILENAME);
|
|
19
|
+
await writeFile(cachePath, JSON.stringify(style, null, 2), 'utf-8');
|
|
20
|
+
}
|
|
21
|
+
export function formatStyleForDisplay(style) {
|
|
22
|
+
const lines = [
|
|
23
|
+
`## Learned PR Style for ${style.repositoryInfo.owner}/${style.repositoryInfo.repo}`,
|
|
24
|
+
`Based on ${style.sampleCount} merged PRs (updated: ${new Date(style.lastUpdated).toLocaleDateString()})`,
|
|
25
|
+
'',
|
|
26
|
+
'### Structure',
|
|
27
|
+
`- Sections: ${style.sections.length > 0 ? style.sections.join(', ') : 'None detected'}`,
|
|
28
|
+
`- Checkboxes: ${style.usesCheckboxes ? 'Yes' : 'No'}`,
|
|
29
|
+
`- Bullet points: ${style.usesBulletPoints ? 'Yes' : 'No'}`,
|
|
30
|
+
`- Average length: ~${style.averageLineCount} lines`,
|
|
31
|
+
'',
|
|
32
|
+
'### Title Style',
|
|
33
|
+
`- Pattern: ${style.titlePattern || 'No consistent pattern'}`,
|
|
34
|
+
`- Prefixes: ${style.titlePrefixExamples.length > 0 ? style.titlePrefixExamples.join(', ') : 'None'}`,
|
|
35
|
+
'',
|
|
36
|
+
'### Tone',
|
|
37
|
+
`- Style: ${style.tone}`,
|
|
38
|
+
`- First person: ${style.usesFirstPerson ? 'Yes' : 'No'}`,
|
|
39
|
+
`- Emojis: ${style.usesEmojis ? 'Yes' : 'No'}`,
|
|
40
|
+
];
|
|
41
|
+
if (style.mentionsTickets) {
|
|
42
|
+
lines.push('', '### Tickets', `- Pattern: ${style.ticketPattern}`);
|
|
43
|
+
}
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=style-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-cache.js","sourceRoot":"","sources":["../../src/utils/style-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAExC,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB;IACpD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAiB,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,KAAmB;IACzE,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;IACjD,MAAM,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,KAAmB;IACvD,MAAM,KAAK,GAAa;QACtB,2BAA2B,KAAK,CAAC,cAAc,CAAC,KAAK,IAAI,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE;QACpF,YAAY,KAAK,CAAC,WAAW,yBAAyB,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,kBAAkB,EAAE,GAAG;QACzG,EAAE;QACF,eAAe;QACf,eAAe,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,eAAe,EAAE;QACxF,iBAAiB,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QACtD,oBAAoB,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QAC3D,sBAAsB,KAAK,CAAC,gBAAgB,QAAQ;QACpD,EAAE;QACF,iBAAiB;QACjB,cAAc,KAAK,CAAC,YAAY,IAAI,uBAAuB,EAAE;QAC7D,eAAe,KAAK,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;QACrG,EAAE;QACF,UAAU;QACV,YAAY,KAAK,CAAC,IAAI,EAAE;QACxB,mBAAmB,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;QACzD,aAAa,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE;KAC/C,CAAC;IAEF,IAAI,KAAK,CAAC,eAAe,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,aAAa,EAAE,cAAc,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-extractor.d.ts","sourceRoot":"","sources":["../../src/utils/style-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAuB5F"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export function extractStyleFromPRs(prs, owner, repo) {
|
|
2
|
+
const bodies = prs.map(pr => pr.body).filter(Boolean);
|
|
3
|
+
const titles = prs.map(pr => pr.title);
|
|
4
|
+
return {
|
|
5
|
+
sections: extractCommonSections(bodies),
|
|
6
|
+
usesCheckboxes: bodies.some(b => /\[[ x]\]/.test(b)),
|
|
7
|
+
usesBulletPoints: bodies.some(b => /^[\s]*[-*]\s/m.test(b)),
|
|
8
|
+
usesNumberedLists: bodies.some(b => /^\s*\d+\.\s/m.test(b)),
|
|
9
|
+
titlePattern: detectTitlePattern(titles),
|
|
10
|
+
titlePrefixExamples: extractTitlePrefixes(titles),
|
|
11
|
+
averageBodyLength: avg(bodies.map(b => b.length)),
|
|
12
|
+
averageLineCount: avg(bodies.map(b => b.split('\n').length)),
|
|
13
|
+
mentionsTickets: bodies.some(b => /[A-Z]+-\d+/.test(b)),
|
|
14
|
+
ticketPattern: detectTicketPattern([...bodies, ...titles]),
|
|
15
|
+
tone: analyzeTone(bodies),
|
|
16
|
+
usesFirstPerson: bodies.some(b => /\b(I |I'|my |we |we'|our )/i.test(b)),
|
|
17
|
+
usesEmojis: bodies.some(b => hasEmoji(b)) || titles.some(t => hasEmoji(t)),
|
|
18
|
+
alwaysIncludes: extractCommonPhrases(bodies),
|
|
19
|
+
sampleCount: prs.length,
|
|
20
|
+
lastUpdated: new Date().toISOString(),
|
|
21
|
+
repositoryInfo: { owner, repo },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function extractCommonSections(bodies) {
|
|
25
|
+
const counts = new Map();
|
|
26
|
+
for (const body of bodies) {
|
|
27
|
+
const headers = body.match(/^#{1,3}\s+.+$/gm) || [];
|
|
28
|
+
for (const h of headers)
|
|
29
|
+
counts.set(h.trim(), (counts.get(h.trim()) || 0) + 1);
|
|
30
|
+
}
|
|
31
|
+
const threshold = bodies.length * 0.3;
|
|
32
|
+
return Array.from(counts.entries())
|
|
33
|
+
.filter(([, c]) => c >= threshold)
|
|
34
|
+
.sort((a, b) => b[1] - a[1])
|
|
35
|
+
.map(([s]) => s);
|
|
36
|
+
}
|
|
37
|
+
function detectTitlePattern(titles) {
|
|
38
|
+
const conventional = titles.filter(t => /^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert)(\(.+\))?:\s/.test(t)).length;
|
|
39
|
+
if (conventional > titles.length * 0.5)
|
|
40
|
+
return 'conventional';
|
|
41
|
+
const ticketPrefix = titles.filter(t => /^\[?[A-Z]+-\d+\]?\s/.test(t)).length;
|
|
42
|
+
if (ticketPrefix > titles.length * 0.5)
|
|
43
|
+
return 'ticket-prefix';
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function extractTitlePrefixes(titles) {
|
|
47
|
+
const prefixes = new Set();
|
|
48
|
+
for (const title of titles) {
|
|
49
|
+
const conv = title.match(/^(feat|fix|docs|style|refactor|test|chore|build|ci|perf|revert):/);
|
|
50
|
+
if (conv)
|
|
51
|
+
prefixes.add(conv[1] + ':');
|
|
52
|
+
}
|
|
53
|
+
return Array.from(prefixes);
|
|
54
|
+
}
|
|
55
|
+
function avg(nums) {
|
|
56
|
+
return nums.length ? Math.round(nums.reduce((a, b) => a + b, 0) / nums.length) : 0;
|
|
57
|
+
}
|
|
58
|
+
function detectTicketPattern(texts) {
|
|
59
|
+
const combined = texts.join(' ');
|
|
60
|
+
if (/JIRA-\d+/.test(combined))
|
|
61
|
+
return 'JIRA-\\d+';
|
|
62
|
+
if (/[A-Z]{2,}-\d+/.test(combined))
|
|
63
|
+
return '[A-Z]{2,}-\\d+';
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
function analyzeTone(bodies) {
|
|
67
|
+
let formal = 0, casual = 0;
|
|
68
|
+
for (const b of bodies) {
|
|
69
|
+
if (/\b(This PR|This commit|This change)\b/i.test(b))
|
|
70
|
+
formal++;
|
|
71
|
+
if (/\b(I |we |gonna|wanna|lol)\b/i.test(b))
|
|
72
|
+
casual++;
|
|
73
|
+
}
|
|
74
|
+
if (formal > bodies.length * 0.5)
|
|
75
|
+
return 'formal';
|
|
76
|
+
if (casual > bodies.length * 0.3)
|
|
77
|
+
return 'casual';
|
|
78
|
+
return 'mixed';
|
|
79
|
+
}
|
|
80
|
+
function hasEmoji(text) {
|
|
81
|
+
return /[\u{1F300}-\u{1F9FF}]|[\u{2600}-\u{26FF}]/u.test(text);
|
|
82
|
+
}
|
|
83
|
+
function extractCommonPhrases(bodies) {
|
|
84
|
+
const phrases = ['## Description', '## Testing', '## Changes', 'Fixes #', 'Closes #'];
|
|
85
|
+
return phrases.filter(p => bodies.filter(b => b.includes(p)).length > bodies.length * 0.5);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=style-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style-extractor.js","sourceRoot":"","sources":["../../src/utils/style-extractor.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-pr-description",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server that generates PR descriptions by learning from your team's merged PRs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-pr-description": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"prepublishOnly": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"pull-request",
|
|
23
|
+
"github",
|
|
24
|
+
"ai",
|
|
25
|
+
"pr-description"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
31
|
+
"@octokit/rest": "^21.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^22.0.0",
|
|
35
|
+
"typescript": "^5.7.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|