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