Initial commit
This commit is contained in:
20
.claude-plugin/plugin.json
Normal file
20
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "github-dev",
|
||||||
|
"description": "Git workflow automation with commit-manager and pr-manager agents. 5 slash commands for commits/PRs/branch cleanup, confirmation hooks, GitHub MCP, and workflow skills.",
|
||||||
|
"version": "1.2.1",
|
||||||
|
"author": {
|
||||||
|
"name": "Fatih Akyon"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
],
|
||||||
|
"agents": [
|
||||||
|
"./agents"
|
||||||
|
],
|
||||||
|
"commands": [
|
||||||
|
"./commands"
|
||||||
|
],
|
||||||
|
"hooks": [
|
||||||
|
"./hooks"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# github-dev
|
||||||
|
|
||||||
|
Git workflow automation with commit-manager and pr-manager agents. 5 slash commands for commits/PRs/branch cleanup, confirmation hooks, GitHub MCP, and workflow skills.
|
||||||
60
agents/commit-manager.md
Normal file
60
agents/commit-manager.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
name: commit-manager
|
||||||
|
description: Use this agent when you have staged files ready for commit and need intelligent commit planning and execution. Examples: <example>Context: User has staged multiple files with different types of changes and wants to commit them properly. user: 'I've staged several files with bug fixes and new features. Can you help me commit these?' assistant: 'I'll use the commit-manager agent to analyze your staged files, create an optimal commit plan, and handle the commit process.' <commentary>The user has staged files and needs commit assistance, so use the commit-manager agent to handle the entire commit workflow.</commentary></example> <example>Context: User has made changes and wants to ensure proper commit organization. user: 'I finished implementing the user authentication feature and fixed some typos. Everything is staged.' assistant: 'Let me use the commit-manager agent to review your staged changes, check if documentation needs updating, create an appropriate commit strategy and initiate commits.' <commentary>User has completed work and staged files, perfect time to use commit-manager for proper commit planning.</commentary></example>
|
||||||
|
tools: Bash, BashOutput, Glob, Grep, Read, WebSearch, WebFetch, TodoWrite, ListMcpResourcesTool, ReadMcpResourceTool, mcp__tavily__tavily-search, mcp__tavily__tavily-extract
|
||||||
|
color: blue
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a Git commit workflow manager, an expert in version control best practices and semantic commit organization. Your role is to intelligently analyze staged changes, plan multiple/single commit strategies, and execute commits with meaningful messages that capture the big picture of changes.
|
||||||
|
|
||||||
|
When activated, follow this precise workflow:
|
||||||
|
|
||||||
|
1. **Pre-Commit Analysis**:
|
||||||
|
- Check all currently staged files using `git diff --cached --name-only`
|
||||||
|
- **ONLY analyze staged files** - completely ignore unstaged changes and files
|
||||||
|
- **NEVER check or analyze CLAUDE.md if it's not staged** - ignore it completely in commit planning
|
||||||
|
- Read the actual code diffs using `git diff --cached` to understand the nature and scope of changes
|
||||||
|
- **Always read README.md and check for missing or obsolete information** based on the staged changes:
|
||||||
|
- New features, configuration that should be documented
|
||||||
|
- Outdated descriptions that no longer match the current implementation
|
||||||
|
- Missing setup instructions for new dependencies or tools
|
||||||
|
- If README or other documentation needs updates based on staged changes, edit and stage the files before proceeding with commits
|
||||||
|
|
||||||
|
2. **Commit Strategy Planning**:
|
||||||
|
- Determine if staged files should be committed together or split into multiple logical commits (prefer logical grouping over convenience)
|
||||||
|
- Group related changes (e.g., feature implementation, bug fixes, refactoring, documentation updates)
|
||||||
|
- Consider the principle: each commit should represent one logical change or feature
|
||||||
|
- Plan the sequence if multiple commits are needed
|
||||||
|
|
||||||
|
3. **Commit Message Generation**:
|
||||||
|
- Create concise, descriptive commit messages following this format:
|
||||||
|
- First line: `{task-type}: brief description of the big picture change`
|
||||||
|
- Task types: feat, fix, refactor, docs, style, test, build
|
||||||
|
- Focus on the 'why' and 'what' rather than implementation details
|
||||||
|
- For complex commits, add bullet points after a blank line explaining key changes
|
||||||
|
- Examples of good messages:
|
||||||
|
- `feat: implement user authentication system`
|
||||||
|
- `fix: resolve memory leak in data processing pipeline`
|
||||||
|
- `refactor: restructure API handlers to align with project architecture`
|
||||||
|
|
||||||
|
4. **Execution**:
|
||||||
|
- Execute commits in the planned sequence using git commands
|
||||||
|
- **For multi-commit scenarios, use precise git operations to avoid file mixups**:
|
||||||
|
- Create a temporary list of all staged files using `git diff --cached --name-only`
|
||||||
|
- For each commit, use `git reset HEAD <file>` to unstage specific files not meant for current commit
|
||||||
|
- Use `git add <file>` to stage only the files intended for the current commit
|
||||||
|
- After each commit, re-stage remaining files for subsequent commits
|
||||||
|
- **CRITICAL**: Always verify the exact files in staging area before each `git commit` command
|
||||||
|
- After committing, push changes to the remote repository
|
||||||
|
|
||||||
|
5. **Quality Assurance**:
|
||||||
|
- Verify each commit was successful
|
||||||
|
- Confirm push completed without errors
|
||||||
|
- Provide a summary of what was committed and pushed
|
||||||
|
|
||||||
|
Key principles:
|
||||||
|
- Always read and understand the actual code changes, not just filenames
|
||||||
|
- Prioritize logical grouping over convenience
|
||||||
|
- Write commit messages that will be meaningful to future developers
|
||||||
|
- Ensure documentation stays synchronized with code changes
|
||||||
|
- Handle git operations safely with proper error checking
|
||||||
103
agents/pr-manager.md
Normal file
103
agents/pr-manager.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
name: pr-manager
|
||||||
|
description: Use this agent when you need to create a complete pull request workflow including branch creation, committing staged changes, and PR submission. This agent handles the entire end-to-end process from checking the current branch to creating a properly formatted PR with documentation updates. Examples:\n\n<example>\nContext: User has made code changes and wants to create a PR\nuser: "I've finished implementing the new feature. Please create a PR for the staged changes only"\nassistant: "I'll use the pr-manager agent to handle the complete PR workflow including branch creation, commits, and PR submission"\n<commentary>\nSince the user wants to create a PR, use the pr-manager agent to handle the entire workflow from branch creation to PR submission.\n</commentary>\n</example>\n\n<example>\nContext: User is on main branch with staged changes\nuser: "Create a PR with my staged changes only"\nassistant: "I'll launch the pr-manager agent to create a feature branch, commit your staged changes only, and submit a PR"\n<commentary>\nThe user needs the full PR workflow, so use pr-manager to handle branch creation, commits, and PR submission.\n</commentary>\n</example>
|
||||||
|
tools: Bash, BashOutput, Glob, Grep, Read, WebSearch, WebFetch, TodoWrite, SlashCommand, ListMcpResourcesTool, ReadMcpResourceTool, mcp__tavily__tavily-search, mcp__tavily__tavily-extract
|
||||||
|
color: cyan
|
||||||
|
---
|
||||||
|
|
||||||
|
You are a Git and GitHub PR workflow automation specialist. Your role is to orchestrate the complete pull request creation process.
|
||||||
|
|
||||||
|
## Workflow Steps:
|
||||||
|
|
||||||
|
1. **Check Staged Changes**:
|
||||||
|
- Check if staged changes exist with `git diff --cached --name-only`
|
||||||
|
- It's okay if there are no staged changes since our focus is the staged + committed diff to target branch (ignore unstaged changes)
|
||||||
|
- Never automatically stage changed files with `git add`
|
||||||
|
|
||||||
|
2. **Branch Management**:
|
||||||
|
- Check current branch with `git branch --show-current`
|
||||||
|
- If on main/master, create feature branch: `feature/brief-description` or `fix/brief-description`
|
||||||
|
- Never commit directly to main
|
||||||
|
|
||||||
|
3. **Commit Staged Changes**:
|
||||||
|
- Use `github-dev:commit-manager` subagent to handle if any staged changes, skip this step if no staged changes exist, ignore unstaged changes
|
||||||
|
- Ensure commits follow project conventions
|
||||||
|
|
||||||
|
4. **Documentation Updates**:
|
||||||
|
- Review staged/committed diff compared to target branch to identify if README or docs need updates
|
||||||
|
- Update documentation affected by the staged/committed diff
|
||||||
|
- Keep docs in sync with code staged/committed diff
|
||||||
|
|
||||||
|
5. **Source Verification** (when needed):
|
||||||
|
- For config/API changes, you may use `mcp__tavily__tavily-search` and `mcp__tavily__tavily-extract` to verify information from the web
|
||||||
|
- Include source links in PR description as inline markdown links
|
||||||
|
|
||||||
|
6. **Create Pull Request**:
|
||||||
|
- **IMPORTANT**: Analyze ALL committed changes in the branch using `git diff <base-branch>...HEAD`
|
||||||
|
- PR message must describe the complete changeset across all commits, not just the latest commit
|
||||||
|
- Focus on what changed (ignore unstaged changes) from the perspective of someone reviewing the entire branch
|
||||||
|
- Create PR with `gh pr create` using:
|
||||||
|
- `-t` or `--title`: Concise title (max 72 chars)
|
||||||
|
- `-b` or `--body`: Description with brief summary (few words or 1 sentence) + few bullet points of changes
|
||||||
|
- `-a @me`: Self-assign (confirmation hook will show actual username)
|
||||||
|
- `-r <reviewer>`: Add reviewer by finding most probable reviewer from recent PRs:
|
||||||
|
- Get current repo: `gh repo view --json nameWithOwner -q .nameWithOwner`
|
||||||
|
- First try: `gh pr list --repo <owner>/<repo> --author @me --limit 5` to find PRs by current author
|
||||||
|
- If no PRs by author, fallback: `gh pr list --repo <owner>/<repo> --limit 5` to get any recent PRs
|
||||||
|
- Extract reviewer username from the PR list
|
||||||
|
- Title should start with capital letter and verb and should not start with conventional commit prefixes (e.g. "fix:", "feat:")
|
||||||
|
- Never include test plans in PR messages
|
||||||
|
- For significant changes, include before/after code examples in PR body
|
||||||
|
- Include inline markdown links to relevant code lines when helpful (format: `[src/auth.py:42](src/auth.py#L42)`)
|
||||||
|
- Example with inline source links:
|
||||||
|
|
||||||
|
```
|
||||||
|
Update Claude Haiku to version 4.5
|
||||||
|
|
||||||
|
- Model ID: claude-3-haiku-20240307 → claude-haiku-4-5-20251001 ([source](https://docs.anthropic.com/en/docs/about-claude/models/overview))
|
||||||
|
- Pricing: $0.80/$4.00 → $1.00/$5.00 per MTok ([source](https://docs.anthropic.com/en/docs/about-claude/pricing))
|
||||||
|
- Max output: 4,096 → 64,000 tokens ([source](https://docs.anthropic.com/en/docs/about-claude/models/overview))
|
||||||
|
```
|
||||||
|
|
||||||
|
- Example with code changes and file links:
|
||||||
|
|
||||||
|
````
|
||||||
|
Refactor authentication to use async context manager
|
||||||
|
|
||||||
|
- Replace synchronous auth flow with async/await pattern in [src/auth.py:15-42](src/auth.py#L15-L42)
|
||||||
|
- Add context manager support for automatic cleanup
|
||||||
|
|
||||||
|
Before:
|
||||||
|
```python
|
||||||
|
def authenticate(token):
|
||||||
|
session = create_session(token)
|
||||||
|
return session
|
||||||
|
````
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def authenticate(token):
|
||||||
|
async with create_session(token) as session:
|
||||||
|
return session
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tool Usage:
|
||||||
|
|
||||||
|
- Use `gh` CLI for all PR operations
|
||||||
|
- Use `mcp__tavily__tavily-search` for web verification
|
||||||
|
- Use `github-dev:commit-manager` subagent for commit creation
|
||||||
|
- Use git commands for branch operations
|
||||||
|
|
||||||
|
## Output:
|
||||||
|
|
||||||
|
Provide clear status updates:
|
||||||
|
|
||||||
|
- Branch creation confirmation
|
||||||
|
- Commit completion status
|
||||||
|
- Documentation updates made
|
||||||
|
- PR URL upon completion
|
||||||
44
commands/clean-gone-branches.md
Normal file
44
commands/clean-gone-branches.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
---
|
||||||
|
description: Clean up local branches deleted from remote
|
||||||
|
---
|
||||||
|
|
||||||
|
# Clean Gone Branches
|
||||||
|
|
||||||
|
Remove local git branches that have been deleted from remote (marked as [gone]).
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
Run the following commands in sequence:
|
||||||
|
|
||||||
|
1. **Update remote references:**
|
||||||
|
```bash
|
||||||
|
git fetch --prune
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **View branches marked as [gone]:**
|
||||||
|
```bash
|
||||||
|
git branch -vv
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **List worktrees (if any):**
|
||||||
|
```bash
|
||||||
|
git worktree list
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Remove worktrees for gone branches (if any):**
|
||||||
|
```bash
|
||||||
|
git branch -vv | grep '\[gone\]' | awk '{print $1}' | sed 's/^[*+]*//' | while read -r branch; do
|
||||||
|
worktree=$(git worktree list | grep "\[$branch\]" | awk '{print $1}')
|
||||||
|
if [ -n "$worktree" ]; then
|
||||||
|
echo "Removing worktree: $worktree"
|
||||||
|
git worktree remove --force "$worktree"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Delete gone branches:**
|
||||||
|
```bash
|
||||||
|
git branch -vv | grep '\[gone\]' | awk '{print $1}' | sed 's/^[*+]*//' | xargs -I {} git branch -D {}
|
||||||
|
```
|
||||||
|
|
||||||
|
Report the results: list of removed worktrees and deleted branches, or notify if no [gone] branches exist.
|
||||||
19
commands/commit-staged.md
Normal file
19
commands/commit-staged.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
allowed-tools: Task, Read, Grep, SlashCommand
|
||||||
|
argument-hint: [context]
|
||||||
|
description: Commit staged changes with optional context
|
||||||
|
---
|
||||||
|
|
||||||
|
# Commit Staged Changes
|
||||||
|
|
||||||
|
Use the commit-manager agent to analyze and commit staged changes with intelligent organization and optimal commit strategy.
|
||||||
|
|
||||||
|
## Additional Context
|
||||||
|
|
||||||
|
$ARGUMENTS
|
||||||
|
|
||||||
|
Task(
|
||||||
|
description: "Analyze and commit staged changes",
|
||||||
|
prompt: "Analyze the staged changes and create appropriate commits. Additional context: $ARGUMENTS",
|
||||||
|
subagent_type: "github-dev:commit-manager"
|
||||||
|
)
|
||||||
19
commands/create-pr.md
Normal file
19
commands/create-pr.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
allowed-tools: Task, Read, Grep, SlashCommand, Bash(git checkout:*), Bash(git -C:* checkout:*)
|
||||||
|
argument-hint: [context]
|
||||||
|
description: Create pull request with optional context
|
||||||
|
---
|
||||||
|
|
||||||
|
# Create Pull Request
|
||||||
|
|
||||||
|
Use the pr-manager agent to handle the complete PR workflow including branch creation, commits, and PR submission.
|
||||||
|
|
||||||
|
## Additional Context
|
||||||
|
|
||||||
|
$ARGUMENTS
|
||||||
|
|
||||||
|
Task(
|
||||||
|
description: "Create pull request",
|
||||||
|
prompt: "Handle the complete PR workflow including branch creation, commits, and PR submission. Additional context: $ARGUMENTS",
|
||||||
|
subagent_type: "github-dev:pr-manager"
|
||||||
|
)
|
||||||
113
commands/update-pr-summary.md
Normal file
113
commands/update-pr-summary.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# Claude Command: Update PR Summary
|
||||||
|
|
||||||
|
Update PR description with automatically generated summary based on complete changeset.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/update-pr-summary <pr_number> # Update PR description
|
||||||
|
/update-pr-summary 131 # Example: update PR #131
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow Steps
|
||||||
|
|
||||||
|
1. **Fetch PR Information**:
|
||||||
|
- Get PR details using `gh pr view <pr_number>` or `mcp__github__pull_request_read`
|
||||||
|
- Identify base branch and head branch from PR metadata
|
||||||
|
|
||||||
|
2. **Analyze Complete Changeset**:
|
||||||
|
- **IMPORTANT**: Analyze ALL committed changes in the branch using `git diff <base-branch>...HEAD`
|
||||||
|
- PR description must describe the complete changeset across all commits, not just the latest commit
|
||||||
|
- Focus on what changed from the perspective of someone reviewing the entire branch
|
||||||
|
- Ignore unstaged changes
|
||||||
|
|
||||||
|
3. **Generate PR Description**:
|
||||||
|
- Create brief summary (1 sentence or few words)
|
||||||
|
- Add few bullet points of key changes
|
||||||
|
- For significant changes, include before/after code examples in PR body
|
||||||
|
- Include inline markdown links to relevant code lines when helpful (format: `[src/auth.py:42](src/auth.py#L42)`)
|
||||||
|
- For config/API changes, use `mcp__tavily__tavily-search` to verify information and include source links inline
|
||||||
|
- Never include test plans in PR descriptions
|
||||||
|
|
||||||
|
4. **Update PR Title** (if needed):
|
||||||
|
- Title should start with capital letter and verb
|
||||||
|
- Should NOT start with conventional commit prefixes (e.g. "fix:", "feat:")
|
||||||
|
|
||||||
|
5. **Update PR**:
|
||||||
|
- Use `gh pr edit <pr_number>` with `--body` (and optionally `--title`) to update the PR
|
||||||
|
- Use HEREDOC for proper formatting:
|
||||||
|
```bash
|
||||||
|
gh pr edit <pr_number> --body "$(cat <<'EOF'
|
||||||
|
[PR description here]
|
||||||
|
EOF
|
||||||
|
)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## PR Description Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[Brief summary in 1 sentence or few words]
|
||||||
|
|
||||||
|
- [Key change 1 with inline code reference if helpful]
|
||||||
|
- [Key change 2 with source link if config/API change]
|
||||||
|
- [Key change 3]
|
||||||
|
|
||||||
|
[Optional: Before/after code examples for significant changes]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Example 1: Config/API Change with Source Links
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Update Claude Haiku to version 4.5
|
||||||
|
|
||||||
|
- Model ID: claude-3-haiku-20240307 → claude-haiku-4-5-20251001 ([source](https://docs.anthropic.com/en/docs/about-claude/models/overview))
|
||||||
|
- Pricing: $0.80/$4.00 → $1.00/$5.00 per MTok ([source](https://docs.anthropic.com/en/docs/about-claude/pricing))
|
||||||
|
- Max output: 4,096 → 64,000 tokens ([source](https://docs.anthropic.com/en/docs/about-claude/models/overview))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Code Changes with File Links
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
Refactor authentication to use async context manager
|
||||||
|
|
||||||
|
- Replace synchronous auth flow with async/await pattern in [src/auth.py:15-42](src/auth.py#L15-L42)
|
||||||
|
- Add context manager support for automatic cleanup
|
||||||
|
|
||||||
|
Before:
|
||||||
|
```python
|
||||||
|
def authenticate(token):
|
||||||
|
session = create_session(token)
|
||||||
|
return session
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
```python
|
||||||
|
async def authenticate(token):
|
||||||
|
async with create_session(token) as session:
|
||||||
|
return session
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
### Example 3: Simple Feature Addition
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Add user profile export functionality
|
||||||
|
|
||||||
|
- Export user data to JSON format in [src/export.py:45-78](src/export.py#L45-L78)
|
||||||
|
- Add CLI command `/export-profile` in [src/cli.py:123](src/cli.py#L123)
|
||||||
|
- Include email, preferences, and activity history in export
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**Pre-Analysis Verification**:
|
||||||
|
- Verify PR exists and is accessible
|
||||||
|
- Check tool availability (GitHub MCP or gh CLI)
|
||||||
|
- Confirm authentication status
|
||||||
|
|
||||||
|
**Common Issues**:
|
||||||
|
- Invalid PR number → List available PRs
|
||||||
|
- Missing tools → Provide setup instructions
|
||||||
|
- Auth issues → Guide through authentication
|
||||||
20
hooks/hooks.json
Normal file
20
hooks/hooks.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"description": "Git workflow confirmation hooks for GitHub operations",
|
||||||
|
"hooks": {
|
||||||
|
"PreToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Bash",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/git_commit_confirm.py"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/gh_pr_create_confirm.py"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
135
hooks/scripts/gh_pr_create_confirm.py
Executable file
135
hooks/scripts/gh_pr_create_confirm.py
Executable file
@@ -0,0 +1,135 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""PreToolUse hook: show confirmation modal before creating GitHub PR via gh CLI."""
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def parse_gh_pr_create(command: str) -> dict[str, str]:
|
||||||
|
"""Parse gh pr create command to extract PR parameters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command (str): The gh pr create command string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(dict): Dictionary with title, body, assignee, reviewer keys
|
||||||
|
"""
|
||||||
|
params = {"title": "", "body": "", "assignee": "", "reviewer": ""}
|
||||||
|
|
||||||
|
# Extract title (-t or --title)
|
||||||
|
title_match = re.search(r'(?:-t|--title)\s+["\']([^"\']+)["\']', command)
|
||||||
|
if title_match:
|
||||||
|
params["title"] = title_match.group(1)
|
||||||
|
|
||||||
|
# Extract body (-b or --body) - handle HEREDOC syntax first, then simple quotes
|
||||||
|
heredoc_match = re.search(
|
||||||
|
r'(?:-b|--body)\s+"?\$\(cat\s+<<["\']?(\w+)["\']?\s+(.*?)\s+\1\s*\)"?',
|
||||||
|
command,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
if heredoc_match:
|
||||||
|
params["body"] = heredoc_match.group(2).strip()
|
||||||
|
else:
|
||||||
|
body_match = re.search(r'(?:-b|--body)\s+"([^"]+)"', command)
|
||||||
|
if body_match:
|
||||||
|
params["body"] = body_match.group(1)
|
||||||
|
|
||||||
|
# Extract assignee (-a or --assignee)
|
||||||
|
assignee_match = re.search(r'(?:-a|--assignee)\s+([^\s]+)', command)
|
||||||
|
if assignee_match:
|
||||||
|
params["assignee"] = assignee_match.group(1)
|
||||||
|
|
||||||
|
# Extract reviewer (-r or --reviewer)
|
||||||
|
reviewer_match = re.search(r'(?:-r|--reviewer)\s+([^\s]+)', command)
|
||||||
|
if reviewer_match:
|
||||||
|
params["reviewer"] = reviewer_match.group(1)
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_username(assignee: str) -> str:
|
||||||
|
"""Resolve @me to actual GitHub username.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
assignee (str): Assignee value from command (may be @me)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str): Resolved username or original value
|
||||||
|
"""
|
||||||
|
if assignee == "@me":
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["gh", "api", "user", "--jq", ".login"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return assignee
|
||||||
|
|
||||||
|
|
||||||
|
def format_confirmation_message(params: dict[str, str]) -> str:
|
||||||
|
"""Format PR parameters into readable confirmation message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
params (dict): Dictionary with title, body, assignee, reviewer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str): Formatted confirmation message
|
||||||
|
"""
|
||||||
|
# Truncate body if too long
|
||||||
|
body = params["body"]
|
||||||
|
if len(body) > 500:
|
||||||
|
body = body[:500] + "..."
|
||||||
|
|
||||||
|
# Resolve assignee
|
||||||
|
assignee = resolve_username(params["assignee"]) if params["assignee"] else "None"
|
||||||
|
|
||||||
|
lines = ["📝 Create Pull Request?", "", f"Title: {params['title']}", ""]
|
||||||
|
|
||||||
|
if body:
|
||||||
|
lines.extend(["Body:", body, ""])
|
||||||
|
|
||||||
|
lines.append(f"Assignee: {assignee}")
|
||||||
|
|
||||||
|
if params["reviewer"]:
|
||||||
|
lines.append(f"Reviewer: {params['reviewer']}")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
input_data = json.load(sys.stdin)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tool_name = input_data.get("tool_name", "")
|
||||||
|
tool_input = input_data.get("tool_input", {})
|
||||||
|
command = tool_input.get("command", "")
|
||||||
|
|
||||||
|
# Only handle gh pr create commands
|
||||||
|
if tool_name != "Bash" or not command.strip().startswith("gh pr create"):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Parse PR parameters
|
||||||
|
params = parse_gh_pr_create(command)
|
||||||
|
|
||||||
|
# Format confirmation message
|
||||||
|
message = format_confirmation_message(params)
|
||||||
|
|
||||||
|
# Return JSON with ask decision
|
||||||
|
output = {
|
||||||
|
"hookSpecificOutput": {
|
||||||
|
"hookEventName": "PreToolUse",
|
||||||
|
"permissionDecision": "ask",
|
||||||
|
"permissionDecisionReason": message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(json.dumps(output))
|
||||||
|
sys.exit(0)
|
||||||
162
hooks/scripts/git_commit_confirm.py
Executable file
162
hooks/scripts/git_commit_confirm.py
Executable file
@@ -0,0 +1,162 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""PreToolUse hook: show confirmation modal before creating git commit."""
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def parse_git_commit_message(command: str) -> dict[str, str]:
|
||||||
|
"""Parse git commit command to extract commit message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command (str): The git commit command string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(dict): Dictionary with message and is_amend keys
|
||||||
|
"""
|
||||||
|
params = {"message": "", "is_amend": False}
|
||||||
|
|
||||||
|
# Check for --amend flag
|
||||||
|
params["is_amend"] = "--amend" in command
|
||||||
|
|
||||||
|
# Try to extract heredoc format: git commit -m "$(cat <<'EOF' ... EOF)"
|
||||||
|
heredoc_match = re.search(r"<<'EOF'\s*\n(.*?)\nEOF", command, re.DOTALL)
|
||||||
|
if heredoc_match:
|
||||||
|
params["message"] = heredoc_match.group(1).strip()
|
||||||
|
return params
|
||||||
|
|
||||||
|
# Try to extract simple -m "message" format
|
||||||
|
simple_matches = re.findall(r'(?:-m|--message)\s+["\']([^"\']+)["\']', command)
|
||||||
|
if simple_matches:
|
||||||
|
# Join multiple -m flags with double newlines
|
||||||
|
params["message"] = "\n\n".join(simple_matches)
|
||||||
|
return params
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
def get_staged_files() -> tuple[list[str], str]:
|
||||||
|
"""Get list of staged files and diff stats.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(tuple): (list of file paths, diff stats string)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Get list of staged files
|
||||||
|
files_result = subprocess.run(
|
||||||
|
["git", "diff", "--cached", "--name-only"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get diff stats
|
||||||
|
stats_result = subprocess.run(
|
||||||
|
["git", "diff", "--cached", "--stat"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
|
||||||
|
files = []
|
||||||
|
if files_result.returncode == 0:
|
||||||
|
files = [f for f in files_result.stdout.strip().split("\n") if f]
|
||||||
|
|
||||||
|
stats = ""
|
||||||
|
if stats_result.returncode == 0:
|
||||||
|
# Get last line which contains the summary
|
||||||
|
stats_lines = stats_result.stdout.strip().split("\n")
|
||||||
|
if stats_lines:
|
||||||
|
stats = stats_lines[-1]
|
||||||
|
|
||||||
|
return files, stats
|
||||||
|
|
||||||
|
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||||
|
return [], ""
|
||||||
|
|
||||||
|
|
||||||
|
def format_confirmation_message(message: str, is_amend: bool, files: list[str], stats: str) -> str:
|
||||||
|
"""Format commit parameters into readable confirmation message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message (str): Commit message
|
||||||
|
is_amend (bool): Whether this is an amend commit
|
||||||
|
files (list): List of staged file paths
|
||||||
|
stats (str): Diff statistics string
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str): Formatted confirmation message
|
||||||
|
"""
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
# Header
|
||||||
|
if is_amend:
|
||||||
|
lines.append("💾 Amend Previous Commit?")
|
||||||
|
else:
|
||||||
|
lines.append("💾 Create Commit?")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Commit message
|
||||||
|
if message:
|
||||||
|
lines.append("Message:")
|
||||||
|
lines.append(message)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Files
|
||||||
|
if files:
|
||||||
|
lines.append(f"Files to be committed ({len(files)}):")
|
||||||
|
# Show first 15 files, truncate if more
|
||||||
|
display_files = files[:15]
|
||||||
|
for f in display_files:
|
||||||
|
lines.append(f"- {f}")
|
||||||
|
if len(files) > 15:
|
||||||
|
lines.append(f"... and {len(files) - 15} more files")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
# Stats
|
||||||
|
if stats:
|
||||||
|
lines.append("Stats:")
|
||||||
|
lines.append(stats)
|
||||||
|
|
||||||
|
# Warning if no files staged
|
||||||
|
if not files:
|
||||||
|
lines.append("⚠️ No files staged for commit")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
input_data = json.load(sys.stdin)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"Error: Invalid JSON input: {e}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tool_name = input_data.get("tool_name", "")
|
||||||
|
tool_input = input_data.get("tool_input", {})
|
||||||
|
command = tool_input.get("command", "")
|
||||||
|
|
||||||
|
# Only handle git commit commands
|
||||||
|
if tool_name != "Bash" or not command.strip().startswith("git commit"):
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Parse commit message
|
||||||
|
params = parse_git_commit_message(command)
|
||||||
|
|
||||||
|
# Get staged files and stats
|
||||||
|
files, stats = get_staged_files()
|
||||||
|
|
||||||
|
# Format confirmation message
|
||||||
|
message = format_confirmation_message(params["message"], params["is_amend"], files, stats)
|
||||||
|
|
||||||
|
# Return JSON with ask decision
|
||||||
|
output = {
|
||||||
|
"hookSpecificOutput": {
|
||||||
|
"hookEventName": "PreToolUse",
|
||||||
|
"permissionDecision": "ask",
|
||||||
|
"permissionDecisionReason": message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(json.dumps(output))
|
||||||
|
sys.exit(0)
|
||||||
85
plugin.lock.json
Normal file
85
plugin.lock.json
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:fcakyon/claude-codex-settings:plugins/github-dev",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "ef76217b2f1fababfb54227aa1d8f866ddd84a94",
|
||||||
|
"treeHash": "13f276403bc0c942cc5a5070b520fe2810489bc73e17e520c925ae3b1835670e",
|
||||||
|
"generatedAt": "2025-11-28T10:16:50.739990Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "github-dev",
|
||||||
|
"description": "Git workflow automation with commit-manager and pr-manager agents. 5 slash commands for commits/PRs/branch cleanup, confirmation hooks, GitHub MCP, and workflow skills.",
|
||||||
|
"version": "1.2.1"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "968660cc2a9e5dc1209c5f9b1c16d8bfc13d967ff477de8b78d881136f8ef1d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "agents/pr-manager.md",
|
||||||
|
"sha256": "7aaf1f1cdc11f280d432b65a2e3f6e9407a61909853fbaf4f7f82b511b9273fc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "agents/commit-manager.md",
|
||||||
|
"sha256": "10b9cbd9556ceec221ff30d1139798fefb5a9b8d9dc5adbe80e80ee860ebc3c8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "hooks/hooks.json",
|
||||||
|
"sha256": "eaed7e6254bd46dc48f60196ddb6c18cf12158292dcd3d48ebd7ec16c6d6ef12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "hooks/scripts/gh_pr_create_confirm.py",
|
||||||
|
"sha256": "8711c1dab20c6b8f33de434974158e564f09599043153d2c1ab59be4393bed7d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "hooks/scripts/git_commit_confirm.py",
|
||||||
|
"sha256": "ed15c5e857cde98dfcfc8705c163c1899aeea3ef249a89f92d7877b9ed6db156"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "c97d01988c542952c52a2b7009daa701ac194bfbdcf2a98984c42aee67fd0853"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/update-pr-summary.md",
|
||||||
|
"sha256": "4ea7ca767dedbc534c5e3acbc3ae0ca5285b69ef952ce0bcdcf3c4c8b39096f2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/create-pr.md",
|
||||||
|
"sha256": "2531b603ba7f072590ae661120ae104a96f783348555b05fc851b0396cd9377c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/clean-gone-branches.md",
|
||||||
|
"sha256": "174bbd588577bf27201f5dc26677830b5105f4f82b398b16f64db9acf8d537fa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "commands/commit-staged.md",
|
||||||
|
"sha256": "6611d1046d311e3ab397c7a12fc32fa4992a255e468011da06c938e133582956"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/commit-workflow/SKILL.md",
|
||||||
|
"sha256": "fa714f071935508b03b686bd46a0adc2584dff4ba164e97f1aa1f521aecc1699"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/pr-workflow/SKILL.md",
|
||||||
|
"sha256": "a6d53a41e9eaf9fb3bb36ec1d9e0b12185666d85c13ed39fb8580d00a874e8ac"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "13f276403bc0c942cc5a5070b520fe2810489bc73e17e520c925ae3b1835670e"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
51
skills/commit-workflow/SKILL.md
Normal file
51
skills/commit-workflow/SKILL.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
name: commit-workflow
|
||||||
|
description: This skill should be used when user asks to "commit these changes", "write commit message", "stage and commit", "create a commit", "commit staged files", or runs /commit-staged or /commit-manager commands.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Commit Workflow
|
||||||
|
|
||||||
|
Complete workflow for creating commits following project standards.
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
1. **Use commit-manager agent**
|
||||||
|
- Run `/commit-staged [context]` for automated commit handling
|
||||||
|
- Or follow manual steps below
|
||||||
|
|
||||||
|
2. **Analyze staged files only**
|
||||||
|
- Check all staged files: `git diff --cached --name-only`
|
||||||
|
- Read diffs: `git diff --cached`
|
||||||
|
- Completely ignore unstaged changes
|
||||||
|
|
||||||
|
3. **Commit message format**
|
||||||
|
- First line: `{task-type}: brief description of the big picture change`
|
||||||
|
- Task types: `feat`, `fix`, `refactor`, `docs`, `style`, `test`, `build`
|
||||||
|
- Focus on 'why' and 'what', not implementation details
|
||||||
|
- For complex changes, add bullet points after blank line
|
||||||
|
|
||||||
|
4. **Message examples**
|
||||||
|
- `feat: implement user authentication system`
|
||||||
|
- `fix: resolve memory leak in data processing pipeline`
|
||||||
|
- `refactor: restructure API handlers to align with project architecture`
|
||||||
|
|
||||||
|
5. **Documentation update**
|
||||||
|
- Check README.md for:
|
||||||
|
- New features that should be documented
|
||||||
|
- Outdated descriptions no longer matching implementation
|
||||||
|
- Missing setup instructions for new dependencies
|
||||||
|
- Update as needed based on staged changes
|
||||||
|
|
||||||
|
6. **Execution**
|
||||||
|
- Commit uses HEREDOC syntax for proper formatting
|
||||||
|
- Verify commit message has correct format
|
||||||
|
- Don't add test plans to commit messages
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Analyze staged files before writing message
|
||||||
|
- Keep first line concise (50 chars recommended)
|
||||||
|
- Use active voice in message
|
||||||
|
- Reference related code if helpful
|
||||||
|
- One logical change per commit
|
||||||
|
- Ensure README reflects implementation
|
||||||
73
skills/pr-workflow/SKILL.md
Normal file
73
skills/pr-workflow/SKILL.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
name: pr-workflow
|
||||||
|
description: This skill should be used when user asks to "create a PR", "make a pull request", "open PR for this branch", "submit changes as PR", "push and create PR", or runs /create-pr or /pr-manager commands.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Pull Request Workflow
|
||||||
|
|
||||||
|
Complete workflow for creating pull requests following project standards.
|
||||||
|
|
||||||
|
## Process
|
||||||
|
|
||||||
|
1. **Verify staged changes** exist with `git diff --cached --name-only`
|
||||||
|
|
||||||
|
2. **Branch setup**
|
||||||
|
- If on main/master, create feature branch first: `feature/brief-description` or `fix/brief-description`
|
||||||
|
- Use `github-dev:commit-manager` subagent to handle staged changes if needed
|
||||||
|
|
||||||
|
3. **Documentation check**
|
||||||
|
- Update README.md or docs based on changes compared to target branch
|
||||||
|
- For config/API changes, use `mcp__tavily__tavily-search` to verify info and include sources
|
||||||
|
|
||||||
|
4. **Analyze all commits**
|
||||||
|
- Use `git diff <base-branch>...HEAD` to review complete changeset
|
||||||
|
- PR message must describe all commits, not just latest
|
||||||
|
- Focus on what changed from reviewer perspective
|
||||||
|
|
||||||
|
5. **Create PR**
|
||||||
|
- Use `/pr-manager` agent or `gh pr create` with parameters:
|
||||||
|
- `-t` (title): Start with capital letter, use verb, NO "fix:" or "feat:" prefix
|
||||||
|
- `-b` (body): Brief summary + bullet points with inline markdown links
|
||||||
|
- `-a @me` (self-assign)
|
||||||
|
- `-r <reviewer>`: Find via `gh pr list --repo <owner>/<repo> --author @me --limit 5`
|
||||||
|
|
||||||
|
6. **PR Body Guidelines**
|
||||||
|
- **Summary**: Few words or 1 sentence describing changes
|
||||||
|
- **Changes**: Bullet points with inline links `[src/auth.py:42](src/auth.py#L42)`
|
||||||
|
- **Examples**: For significant changes, include before/after code examples
|
||||||
|
- **No test plans**: Never mention test procedures in PR
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### With inline source links:
|
||||||
|
|
||||||
|
```
|
||||||
|
Update Claude Haiku to version 4.5
|
||||||
|
|
||||||
|
- Model ID: claude-3-haiku-20240307 → claude-haiku-4-5-20251001 ([source](https://docs.anthropic.com/en/docs/about-claude/models/overview))
|
||||||
|
- Pricing: $0.80/$4.00 → $1.00/$5.00 per MTok ([source](https://docs.anthropic.com/en/docs/about-claude/pricing))
|
||||||
|
- Max output: 4,096 → 64,000 tokens ([source](https://docs.anthropic.com/en/docs/about-claude/models/overview))
|
||||||
|
```
|
||||||
|
|
||||||
|
### With code changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
Refactor authentication to use async context manager
|
||||||
|
|
||||||
|
- Replace synchronous auth flow with async/await pattern in [src/auth.py:15-42](src/auth.py#L15-L42)
|
||||||
|
- Add context manager support for automatic cleanup
|
||||||
|
|
||||||
|
Before:
|
||||||
|
\`\`\`python
|
||||||
|
def authenticate(token):
|
||||||
|
session = create_session(token)
|
||||||
|
return session
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
After:
|
||||||
|
\`\`\`python
|
||||||
|
async def authenticate(token):
|
||||||
|
async with create_session(token) as session:
|
||||||
|
return session
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user