Skip to content

Commit 5ab15f9

Browse files
author
Marvin Zhang
committed
feat: Add GitHub project integration scripts for devlog management
- Implemented dogfood-github-project.mjs to sync devlog entries with GitHub projects. - Created get-github-project-id.mjs to retrieve project IDs using GraphQL API. - Developed github-project-setup-guide.mjs for step-by-step integration setup instructions. - Introduced github-rest-project-demo.mjs to demonstrate project management using GitHub REST API. - Added github-rest-project-manager.mjs for enhanced project management with issues, labels, and milestones. - Implemented sync-all-to-github.mjs to sync multiple devlog entries to GitHub. - Created test-github-integration.mjs to validate GitHub integration functionality. - Developed test-github-rest-projects.mjs to test REST API project management capabilities. - Added test-issue-creation.mjs to verify issue creation via GitHub API.
1 parent 87da301 commit 5ab15f9

20 files changed

+1557
-85
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ When working with the `@devlog/web` application, use Playwright MCP tools for co
5353

5454
### 7. Testing Setup
5555
- Start the web application with the `Web: Dev (Full Stack)` task before testing
56-
- Use `mcp_playwright_browser_navigate` to access the local development server
57-
- Default development URL: `http://localhost:5173` (Vite dev server)
58-
- Always take a snapshot with `mcp_playwright_browser_snapshot` before interacting with elements
56+
- Use `browser_navigate` to access the local development server
57+
- Default development URL: `http://localhost:3000` (Vite dev server)
58+
- Always take a snapshot with `browser_snapshot` before interacting with elements
5959

6060
### 8. Core Testing Patterns
6161
- **Navigation Testing**: Verify routes and page transitions work correctly

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,8 @@ build/
140140
.idea
141141

142142
# Devlog directory - contains local development notes
143-
.devlog/
143+
.devlog/
144+
145+
# Devlog configuration with credentials
146+
devlog.config.json
147+
devlog-integrations.config.json

GITHUB_SETUP.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# GitHub Integration Setup Guide
2+
3+
## 🔑 Required Token Permissions
4+
5+
Your GitHub Personal Access Token needs these permissions for full devlog integration:
6+
7+
### For Issues Integration:
8+
-**repo** (Full control of private repositories)
9+
- OR **public_repo** (Access public repositories) if you only work with public repos
10+
11+
### For Projects Integration:
12+
-**project** (Full control of projects)
13+
14+
## 🛠️ How to Update Your Token
15+
16+
1. **Go to GitHub Settings**
17+
- Visit: https://github.com/settings/tokens
18+
- Find your existing token or create a new one
19+
20+
2. **Update Permissions**
21+
- Check the **repo** scope (for issues)
22+
- Check the **project** scope (for projects)
23+
- Save the token
24+
25+
3. **Update devlog.config.json**
26+
- Replace the token in your config file
27+
- Keep the same owner, repo, projectNumber settings
28+
29+
## 🧪 Test the Integration
30+
31+
Once you've updated the token permissions, test with:
32+
33+
```bash
34+
# Test basic issue creation
35+
node scripts/test-issue-creation.mjs
36+
37+
# Test full devlog integration
38+
node scripts/test-github-integration.mjs
39+
40+
# Get the correct project ID for GraphQL
41+
node scripts/get-github-project-id.mjs
42+
```
43+
44+
## 📋 Current Status
45+
46+
- ✅ Repository `tikazyq/devlog` exists and is accessible
47+
- ✅ Configuration file `devlog.config.json` is properly formatted
48+
- ❌ Token needs additional permissions (repo + project)
49+
50+
## 🎯 Expected Results
51+
52+
After updating permissions, you should be able to:
53+
54+
1. **Sync devlog entries to GitHub issues**
55+
```javascript
56+
await devlog.syncWithGitHub('your-devlog-id')
57+
```
58+
59+
2. **Sync devlog entries to GitHub project**
60+
```javascript
61+
await devlog.syncWithGitHubProject('your-devlog-id')
62+
```
63+
64+
3. **Import existing project items**
65+
```javascript
66+
await devlog.importGitHubProjectItems()
67+
```
68+
69+
The integration will create GitHub issues with proper titles, descriptions, and labels based on your devlog entries, and link them to your project board.

INTEGRATIONS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ This guide helps you configure devlog to sync with enterprise project management
44

55
## Configuration File Setup
66

7-
Create a configuration file named `devlog-integrations.config.json` in one of these locations:
7+
Create a configuration file named `devlog.config.json` in one of these locations:
88
- Your project root directory
9-
- `~/.devlog-integrations.config.json` (global config)
9+
- `~/.devlog.config.json` (global config)
1010
- `.devlog/integrations.config.json` (workspace-specific)
1111

12-
Copy the template from `devlog-integrations.config.template.json` and fill in your credentials.
12+
Copy the template from `devlog.config.template.json` and fill in your credentials.
1313

1414
## Platform-Specific Setup
1515

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ Devlog supports synchronization with popular enterprise project management platf
152152

153153
1. Copy the integration config template:
154154
```bash
155-
cp devlog-integrations.config.template.json devlog-integrations.config.json
155+
cp devlog.config.template.json devlog.config.json
156156
```
157157

158158
2. Fill in your platform credentials (see [INTEGRATIONS.md](./INTEGRATIONS.md) for detailed setup)

devlog.config.example.json

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
"github": {
1515
"owner": "your-github-username",
1616
"repo": "your-repository",
17-
"token": "your-github-token"
17+
"token": "your-github-token",
18+
"projectNumber": 2,
19+
"projectId": "your-project-id"
1820
},
1921
"slack": {
2022
"botToken": "xoxb-your-slack-bot-token",

packages/mcp-server/src/index.ts

Lines changed: 29 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import * as path from "path";
1515
// Load integrations config if available
1616
function loadIntegrationsConfig(): EnterpriseIntegration | undefined {
1717
const configPaths = [
18-
"devlog-integrations.config.json",
18+
"devlog.config.json",
19+
"devlog-integrations.config.json", // backward compatibility
1920
".devlog/integrations.config.json",
20-
path.join(process.env.HOME || "~", ".devlog-integrations.config.json")
21+
path.join(process.env.HOME || "~", ".devlog.config.json"),
22+
path.join(process.env.HOME || "~", ".devlog-integrations.config.json") // backward compatibility
2123
];
2224

2325
for (const configPath of configPaths) {
@@ -475,59 +477,30 @@ const tools: Tool[] = [
475477
},
476478
},
477479
{
478-
name: "find_or_create_devlog",
479-
description: "Find an existing devlog entry by title or create a new one if it doesn't exist",
480+
name: "sync_with_github_project",
481+
description: "Sync a devlog entry with GitHub Project (create or update project item)",
480482
inputSchema: {
481483
type: "object",
482484
properties: {
483485
id: {
484486
type: "string",
485-
description: "Unique identifier for the devlog entry",
486-
},
487-
title: {
488-
type: "string",
489-
description: "Title of the task/feature/bugfix",
490-
},
491-
type: {
492-
type: "string",
493-
enum: ["feature", "bugfix", "task", "refactor", "docs"],
494-
description: "Type of work being done",
495-
},
496-
description: {
497-
type: "string",
498-
description: "Detailed description of the work",
499-
},
500-
priority: {
501-
type: "string",
502-
enum: ["low", "medium", "high", "critical"],
503-
description: "Priority level",
504-
default: "medium",
505-
},
506-
businessContext: {
507-
type: "string",
508-
description: "Business context - why this work matters and what problem it solves",
509-
},
510-
technicalContext: {
511-
type: "string",
512-
description: "Technical context - architecture decisions, constraints, assumptions",
513-
},
514-
acceptanceCriteria: {
515-
type: "array",
516-
items: { type: "string" },
517-
description: "Acceptance criteria or definition of done",
518-
},
519-
initialInsights: {
520-
type: "array",
521-
items: { type: "string" },
522-
description: "Initial insights or knowledge about this work",
487+
description: "ID of the devlog entry to sync",
523488
},
524-
relatedPatterns: {
525-
type: "array",
526-
items: { type: "string" },
527-
description: "Related patterns or examples from other projects",
489+
},
490+
required: ["id"],
491+
},
492+
},
493+
{
494+
name: "import_github_project_items",
495+
description: "Import existing GitHub project items as devlog entries",
496+
inputSchema: {
497+
type: "object",
498+
properties: {
499+
projectNumber: {
500+
type: "number",
501+
description: "GitHub project number (optional, uses configured project if not provided)",
528502
},
529503
},
530-
required: ["title", "type", "description"],
531504
},
532505
},
533506
];
@@ -619,6 +592,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
619592
}
620593
return await devlogAdapter.syncAllIntegrations(args.id as string);
621594

595+
case "sync_with_github_project":
596+
if (!args || typeof args !== 'object' || !('id' in args)) {
597+
throw new Error("Missing required parameter: id");
598+
}
599+
return await devlogAdapter.syncWithGitHubProject(args.id as string);
600+
601+
case "import_github_project_items":
602+
return await devlogAdapter.importGitHubProjectItems(args);
603+
622604
case "find_or_create_devlog":
623605
if (!args || typeof args !== 'object') {
624606
throw new Error("Missing or invalid arguments");

packages/mcp-server/src/mcp-adapter.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,60 @@ export class MCPDevlogAdapter {
378378
}
379379
}
380380

381+
async syncWithGitHubProject(id: string): Promise<CallToolResult> {
382+
try {
383+
const entry = await this.devlogManager.syncWithGitHubProject(id);
384+
const githubProjectRef = entry.externalReferences?.find(ref =>
385+
ref.system === "github" && ref.url?.includes('projects')
386+
);
387+
388+
return {
389+
content: [
390+
{
391+
type: "text",
392+
text: `Successfully synced devlog ${entry.id} with GitHub Project.\n\nProject Item: ${githubProjectRef?.id}\nURL: ${githubProjectRef?.url}\nStatus: ${githubProjectRef?.status}\nLast Sync: ${githubProjectRef?.lastSync}`,
393+
},
394+
],
395+
};
396+
} catch (error) {
397+
return {
398+
content: [
399+
{
400+
type: "text",
401+
text: `Failed to sync with GitHub Project: ${error}`,
402+
},
403+
],
404+
isError: true,
405+
};
406+
}
407+
}
408+
409+
async importGitHubProjectItems(args?: any): Promise<CallToolResult> {
410+
try {
411+
const projectNumber = args?.projectNumber;
412+
const importedEntries = await this.devlogManager.importGitHubProjectItems(projectNumber);
413+
414+
return {
415+
content: [
416+
{
417+
type: "text",
418+
text: `Successfully imported ${importedEntries.length} GitHub project items as devlog entries.\n\nImported entries:\n${importedEntries.map(entry => `- ${entry.id}: ${entry.title}`).join('\n')}`,
419+
},
420+
],
421+
};
422+
} catch (error) {
423+
return {
424+
content: [
425+
{
426+
type: "text",
427+
text: `Failed to import GitHub project items: ${error}`,
428+
},
429+
],
430+
isError: true,
431+
};
432+
}
433+
}
434+
381435
async syncAllIntegrations(id: string): Promise<CallToolResult> {
382436
try {
383437
const entry = await this.devlogManager.syncAllIntegrations(id);

scripts/check-github-repo.mjs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Check GitHub Repository Access
5+
*/
6+
7+
import fs from 'fs';
8+
9+
async function checkGitHubRepo() {
10+
console.log('🔍 Checking GitHub Repository Access');
11+
console.log('=====================================\n');
12+
13+
try {
14+
const config = JSON.parse(fs.readFileSync('devlog.config.json', 'utf-8'));
15+
const github = config.integrations.github;
16+
17+
console.log(`📦 Repository: ${github.owner}/${github.repo}`);
18+
console.log(`🔑 Token: ${github.token.substring(0, 20)}...`);
19+
20+
// Check if repo exists and is accessible
21+
const response = await fetch(`https://api.github.com/repos/${github.owner}/${github.repo}`, {
22+
headers: {
23+
'Authorization': `token ${github.token}`,
24+
'Accept': 'application/vnd.github.v3+json'
25+
}
26+
});
27+
28+
if (response.ok) {
29+
const repo = await response.json();
30+
console.log(`✅ Repository found: ${repo.full_name}`);
31+
console.log(`📝 Description: ${repo.description || 'No description'}`);
32+
console.log(`🔒 Private: ${repo.private}`);
33+
34+
// Check token permissions
35+
const permissionsResponse = await fetch(`https://api.github.com/repos/${github.owner}/${github.repo}`, {
36+
headers: {
37+
'Authorization': `token ${github.token}`,
38+
'Accept': 'application/vnd.github.v3+json'
39+
}
40+
});
41+
42+
if (permissionsResponse.headers.get('x-oauth-scopes')) {
43+
console.log(`🔐 Token scopes: ${permissionsResponse.headers.get('x-oauth-scopes')}`);
44+
}
45+
46+
} else if (response.status === 404) {
47+
console.log(`❌ Repository ${github.owner}/${github.repo} not found or not accessible`);
48+
console.log('💡 This might be a private repository or the repository name is incorrect');
49+
} else {
50+
console.log(`❌ Error accessing repository: ${response.status} ${response.statusText}`);
51+
}
52+
53+
} catch (error) {
54+
console.error('❌ Error:', error.message);
55+
}
56+
}
57+
58+
checkGitHubRepo();

0 commit comments

Comments
 (0)