mirror of
https://github.com/automazeio/ccpm.git
synced 2025-10-09 13:41:06 +03:00
Merge branch 'rc' into more_context_langs
This commit is contained in:
@@ -25,6 +25,34 @@ If no tasks found: "❌ No tasks to sync. Run: /pm:epic-decompose $ARGUMENTS"
|
||||
|
||||
## Instructions
|
||||
|
||||
### 0. Check Remote Repository
|
||||
|
||||
Follow `/rules/github-operations.md` to ensure we're not syncing to the CCPM template:
|
||||
|
||||
```bash
|
||||
# Check if remote origin is the CCPM template repository
|
||||
remote_url=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
if [[ "$remote_url" == *"automazeio/ccpm"* ]] || [[ "$remote_url" == *"automazeio/ccpm.git"* ]]; then
|
||||
echo "❌ ERROR: You're trying to sync with the CCPM template repository!"
|
||||
echo ""
|
||||
echo "This repository (automazeio/ccpm) is a template for others to use."
|
||||
echo "You should NOT create issues or PRs here."
|
||||
echo ""
|
||||
echo "To fix this:"
|
||||
echo "1. Fork this repository to your own GitHub account"
|
||||
echo "2. Update your remote origin:"
|
||||
echo " git remote set-url origin https://github.com/YOUR_USERNAME/YOUR_REPO.git"
|
||||
echo ""
|
||||
echo "Or if this is a new project:"
|
||||
echo "1. Create a new repository on GitHub"
|
||||
echo "2. Update your remote origin:"
|
||||
echo " git remote set-url origin https://github.com/YOUR_USERNAME/YOUR_REPO.git"
|
||||
echo ""
|
||||
echo "Current remote: $remote_url"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
### 1. Create Epic Issue
|
||||
|
||||
Strip frontmatter and prepare GitHub issue body:
|
||||
@@ -34,11 +62,11 @@ sed '1,/^---$/d; 1,/^---$/d' .claude/epics/$ARGUMENTS/epic.md > /tmp/epic-body-r
|
||||
|
||||
# Remove "## Tasks Created" section and replace with Stats
|
||||
awk '
|
||||
/^## Tasks Created/ {
|
||||
/^## Tasks Created/ {
|
||||
in_tasks=1
|
||||
next
|
||||
}
|
||||
/^## / && in_tasks {
|
||||
/^## / && in_tasks {
|
||||
in_tasks=0
|
||||
# When we hit the next section after Tasks Created, add Stats
|
||||
if (total_tasks) {
|
||||
@@ -53,10 +81,10 @@ awk '
|
||||
/^Total tasks:/ && in_tasks { total_tasks = $3; next }
|
||||
/^Parallel tasks:/ && in_tasks { parallel_tasks = $3; next }
|
||||
/^Sequential tasks:/ && in_tasks { sequential_tasks = $3; next }
|
||||
/^Estimated total effort:/ && in_tasks {
|
||||
/^Estimated total effort:/ && in_tasks {
|
||||
gsub(/^Estimated total effort: /, "")
|
||||
total_effort = $0
|
||||
next
|
||||
next
|
||||
}
|
||||
!in_tasks { print }
|
||||
END {
|
||||
@@ -112,13 +140,13 @@ if [ "$task_count" -lt 5 ]; then
|
||||
# Create sequentially for small batches
|
||||
for task_file in .claude/epics/$ARGUMENTS/[0-9][0-9][0-9].md; do
|
||||
[ -f "$task_file" ] || continue
|
||||
|
||||
|
||||
# Extract task name from frontmatter
|
||||
task_name=$(grep '^name:' "$task_file" | sed 's/^name: *//')
|
||||
|
||||
|
||||
# Strip frontmatter from task content
|
||||
sed '1,/^---$/d; 1,/^---$/d' "$task_file" > /tmp/task-body.md
|
||||
|
||||
|
||||
# Create sub-issue with labels
|
||||
if [ "$use_subissues" = true ]; then
|
||||
task_number=$(gh sub-issue create \
|
||||
@@ -134,11 +162,11 @@ if [ "$task_count" -lt 5 ]; then
|
||||
--label "task,epic:$ARGUMENTS" \
|
||||
--json number -q .number)
|
||||
fi
|
||||
|
||||
|
||||
# Record mapping for renaming
|
||||
echo "$task_file:$task_number" >> /tmp/task-mapping.txt
|
||||
done
|
||||
|
||||
|
||||
# After creating all issues, update references and rename files
|
||||
# This follows the same process as step 3 below
|
||||
fi
|
||||
@@ -149,14 +177,14 @@ fi
|
||||
```bash
|
||||
if [ "$task_count" -ge 5 ]; then
|
||||
echo "Creating $task_count sub-issues in parallel..."
|
||||
|
||||
|
||||
# Check if gh-sub-issue is available for parallel agents
|
||||
if gh extension list | grep -q "yahsan2/gh-sub-issue"; then
|
||||
subissue_cmd="gh sub-issue create --parent $epic_number"
|
||||
else
|
||||
subissue_cmd="gh issue create"
|
||||
fi
|
||||
|
||||
|
||||
# Batch tasks for parallel processing
|
||||
# Spawn agents to create sub-issues in parallel with proper labels
|
||||
# Each agent must use: --label "task,epic:$ARGUMENTS"
|
||||
@@ -171,24 +199,24 @@ Task:
|
||||
prompt: |
|
||||
Create GitHub sub-issues for tasks in epic $ARGUMENTS
|
||||
Parent epic issue: #$epic_number
|
||||
|
||||
|
||||
Tasks to process:
|
||||
- {list of 3-4 task files}
|
||||
|
||||
|
||||
For each task file:
|
||||
1. Extract task name from frontmatter
|
||||
2. Strip frontmatter using: sed '1,/^---$/d; 1,/^---$/d'
|
||||
3. Create sub-issue using:
|
||||
- If gh-sub-issue available:
|
||||
- If gh-sub-issue available:
|
||||
gh sub-issue create --parent $epic_number --title "$task_name" \
|
||||
--body-file /tmp/task-body.md --label "task,epic:$ARGUMENTS"
|
||||
- Otherwise:
|
||||
- Otherwise:
|
||||
gh issue create --title "$task_name" --body-file /tmp/task-body.md \
|
||||
--label "task,epic:$ARGUMENTS"
|
||||
4. Record: task_file:issue_number
|
||||
|
||||
|
||||
IMPORTANT: Always include --label parameter with "task,epic:$ARGUMENTS"
|
||||
|
||||
|
||||
Return mapping of files to issue numbers.
|
||||
```
|
||||
|
||||
@@ -221,30 +249,30 @@ Then rename files and update all references:
|
||||
# Process each task file
|
||||
while IFS=: read -r task_file task_number; do
|
||||
new_name="$(dirname "$task_file")/${task_number}.md"
|
||||
|
||||
|
||||
# Read the file content
|
||||
content=$(cat "$task_file")
|
||||
|
||||
|
||||
# Update depends_on and conflicts_with references
|
||||
while IFS=: read -r old_num new_num; do
|
||||
# Update arrays like [001, 002] to use new issue numbers
|
||||
content=$(echo "$content" | sed "s/\b$old_num\b/$new_num/g")
|
||||
done < /tmp/id-mapping.txt
|
||||
|
||||
|
||||
# Write updated content to new file
|
||||
echo "$content" > "$new_name"
|
||||
|
||||
|
||||
# Remove old file if different from new
|
||||
[ "$task_file" != "$new_name" ] && rm "$task_file"
|
||||
|
||||
|
||||
# Update github field in frontmatter
|
||||
# Add the GitHub URL to the frontmatter
|
||||
repo=$(gh repo view --json nameWithOwner -q .nameWithOwner)
|
||||
github_url="https://github.com/$repo/issues/$task_number"
|
||||
|
||||
|
||||
# Update frontmatter with GitHub URL and current timestamp
|
||||
current_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
# Use sed to update the github and updated fields
|
||||
sed -i.bak "/^github:/c\github: $github_url" "$new_name"
|
||||
sed -i.bak "/^updated:/c\updated: $current_date" "$new_name"
|
||||
@@ -260,16 +288,16 @@ If NOT using gh-sub-issue, add task list to epic:
|
||||
if [ "$use_subissues" = false ]; then
|
||||
# Get current epic body
|
||||
gh issue view {epic_number} --json body -q .body > /tmp/epic-body.md
|
||||
|
||||
|
||||
# Append task list
|
||||
cat >> /tmp/epic-body.md << 'EOF'
|
||||
|
||||
|
||||
## Tasks
|
||||
- [ ] #{task1_number} {task1_name}
|
||||
- [ ] #{task2_number} {task2_name}
|
||||
- [ ] #{task3_number} {task3_name}
|
||||
EOF
|
||||
|
||||
|
||||
# Update epic issue
|
||||
gh issue edit {epic_number} --body-file /tmp/epic-body.md
|
||||
fi
|
||||
@@ -304,16 +332,16 @@ EOF
|
||||
# Add each task with its real issue number
|
||||
for task_file in .claude/epics/$ARGUMENTS/[0-9]*.md; do
|
||||
[ -f "$task_file" ] || continue
|
||||
|
||||
|
||||
# Get issue number (filename without .md)
|
||||
issue_num=$(basename "$task_file" .md)
|
||||
|
||||
|
||||
# Get task name from frontmatter
|
||||
task_name=$(grep '^name:' "$task_file" | sed 's/^name: *//')
|
||||
|
||||
|
||||
# Get parallel status
|
||||
parallel=$(grep '^parallel:' "$task_file" | sed 's/^parallel: *//')
|
||||
|
||||
|
||||
# Add to tasks section
|
||||
echo "- [ ] #${issue_num} - ${task_name} (parallel: ${parallel})" >> /tmp/tasks-section.md
|
||||
done
|
||||
@@ -336,7 +364,7 @@ cp .claude/epics/$ARGUMENTS/epic.md .claude/epics/$ARGUMENTS/epic.md.backup
|
||||
|
||||
# Use awk to replace the section
|
||||
awk '
|
||||
/^## Tasks Created/ {
|
||||
/^## Tasks Created/ {
|
||||
skip=1
|
||||
while ((getline line < "/tmp/tasks-section.md") > 0) print line
|
||||
close("/tmp/tasks-section.md")
|
||||
@@ -366,10 +394,10 @@ EOF
|
||||
# Add each task mapping
|
||||
for task_file in .claude/epics/$ARGUMENTS/[0-9]*.md; do
|
||||
[ -f "$task_file" ] || continue
|
||||
|
||||
|
||||
issue_num=$(basename "$task_file" .md)
|
||||
task_name=$(grep '^name:' "$task_file" | sed 's/^name: *//')
|
||||
|
||||
|
||||
echo "- #${issue_num}: ${task_name} - https://github.com/${repo}/issues/${issue_num}" >> .claude/epics/$ARGUMENTS/github-mapping.md
|
||||
done
|
||||
|
||||
@@ -424,4 +452,4 @@ If any issue creation fails:
|
||||
- Trust GitHub CLI authentication
|
||||
- Don't pre-check for duplicates
|
||||
- Update frontmatter only after successful creation
|
||||
- Keep operations simple and atomic
|
||||
- Keep operations simple and atomic
|
||||
|
||||
@@ -21,6 +21,17 @@ Push local updates as GitHub issue comments for transparent audit trail.
|
||||
Before proceeding, complete these validation steps.
|
||||
Do not bother the user with preflight checks progress ("I'm not going to ..."). Just do them and move on.
|
||||
|
||||
0. **Repository Protection Check:**
|
||||
Follow `/rules/github-operations.md` - check remote origin:
|
||||
```bash
|
||||
remote_url=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
if [[ "$remote_url" == *"automazeio/ccpm"* ]]; then
|
||||
echo "❌ ERROR: Cannot sync to CCPM template repository!"
|
||||
echo "Update your remote: git remote set-url origin https://github.com/YOUR_USERNAME/YOUR_REPO.git"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
1. **GitHub Authentication:**
|
||||
- Run: `gh auth status`
|
||||
- If not authenticated, tell user: "❌ GitHub CLI not authenticated. Run: gh auth login"
|
||||
|
||||
130
.claude/hooks/README.md
Normal file
130
.claude/hooks/README.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# Claude Hooks Configuration
|
||||
|
||||
## Bash Worktree Fix Hook
|
||||
|
||||
This hook automatically fixes the Bash tool's directory reset issue when working in git worktrees.
|
||||
|
||||
### Problem
|
||||
|
||||
The Bash tool resets to the main project directory after every command, making it impossible to work in worktrees without manually prefixing every command with `cd /path/to/worktree &&`.
|
||||
|
||||
### Solution
|
||||
|
||||
The pre-tool-use hook automatically detects when you're in a worktree and injects the necessary `cd` prefix to all Bash commands.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Detection**: Before any Bash command executes, the hook checks if `.git` is a file (worktree) or directory (main repo)
|
||||
2. **Injection**: If in a worktree, prepends `cd /absolute/path/to/worktree && ` to the command
|
||||
3. **Transparency**: Agents don't need to know about this - it happens automatically
|
||||
|
||||
### Configuration
|
||||
|
||||
Add to your `.claude/settings.json`:
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"pre-tool-use": {
|
||||
"Bash": {
|
||||
"enabled": true,
|
||||
"script": ".claude/hooks/bash-worktree-fix.sh",
|
||||
"apply_to_subagents": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
To test the hook:
|
||||
|
||||
```bash
|
||||
# Enable debug mode
|
||||
export CLAUDE_HOOK_DEBUG=true
|
||||
|
||||
# Test in main repo (should pass through)
|
||||
.claude/hooks/bash-worktree-fix.sh "ls -la"
|
||||
|
||||
# Test in worktree (should inject cd)
|
||||
cd /path/to/worktree
|
||||
.claude/hooks/bash-worktree-fix.sh "npm install"
|
||||
# Output: cd "/path/to/worktree" && npm install
|
||||
```
|
||||
|
||||
### Advanced Features
|
||||
|
||||
The script handles:
|
||||
|
||||
- Background processes (`&`)
|
||||
- Piped commands (`|`)
|
||||
- Environment variable prefixes (`VAR=value command`)
|
||||
- Commands that already have `cd`
|
||||
- Commands using absolute paths
|
||||
- Debug logging with `CLAUDE_HOOK_DEBUG=true`
|
||||
|
||||
### Edge Cases Handled
|
||||
|
||||
1. **Double-prefix prevention**: Won't add prefix if command already starts with `cd`
|
||||
2. **Absolute paths**: Skips injection for commands using absolute paths
|
||||
3. **Special commands**: Skips for `pwd`, `echo`, `export`, etc. that don't need context
|
||||
4. **Background processes**: Correctly handles `&` at the end of commands
|
||||
5. **Pipe chains**: Injects only at the start of pipe chains
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
If the hook isn't working:
|
||||
|
||||
1. **Verify the hook is executable:**
|
||||
```bash
|
||||
chmod +x .claude/hooks/bash-worktree-fix.sh
|
||||
```
|
||||
|
||||
2. **Enable debug logging to see what's happening:**
|
||||
```bash
|
||||
export CLAUDE_HOOK_DEBUG=true
|
||||
```
|
||||
|
||||
3. **Test the hook manually with a sample command:**
|
||||
```bash
|
||||
cd /path/to/worktree
|
||||
.claude/hooks/bash-worktree-fix.sh "npm test"
|
||||
```
|
||||
|
||||
4. **Check that your settings.json is valid JSON:**
|
||||
```bash
|
||||
cat .claude/settings.json | python -m json.tool
|
||||
```
|
||||
|
||||
### Integration with Claude
|
||||
|
||||
Once configured, this hook will:
|
||||
|
||||
- Automatically apply to all Bash tool invocations
|
||||
- Work for both main agent and sub-agents
|
||||
- Be completely transparent to users
|
||||
- Eliminate the need for worktree-specific instructions
|
||||
|
||||
### Result
|
||||
|
||||
With this hook in place, agents can work in worktrees naturally:
|
||||
|
||||
**Agent writes:**
|
||||
|
||||
```bash
|
||||
npm install
|
||||
git status
|
||||
npm run build
|
||||
```
|
||||
|
||||
**Hook transforms to:**
|
||||
|
||||
```bash
|
||||
cd /path/to/my/project/epic-feature && npm install
|
||||
cd /path/to/my/project/epic-feature && git status
|
||||
cd /path/to/my/project/epic-feature && npm run build
|
||||
```
|
||||
|
||||
**Without the agent knowing or caring about the worktree context!**
|
||||
189
.claude/hooks/bash-worktree-fix.sh
Executable file
189
.claude/hooks/bash-worktree-fix.sh
Executable file
@@ -0,0 +1,189 @@
|
||||
#!/bin/sh
|
||||
# POSIX-compliant pre-tool-use hook for Bash tool
|
||||
# If inside a Git *worktree checkout*, prefix the incoming command with:
|
||||
# cd '<worktree_root>' && <original_command>
|
||||
# No sh -c. No tokenization. Quoting preserved. Robust worktree detection.
|
||||
|
||||
DEBUG_MODE="${CLAUDE_HOOK_DEBUG:-false}"
|
||||
|
||||
debug_log() {
|
||||
case "${DEBUG_MODE:-}" in
|
||||
true|TRUE|1|yes|YES)
|
||||
printf '%s\n' "DEBUG [bash-worktree-fix]: $*" >&2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Safely single-quote a string for shell usage: foo'bar -> 'foo'"'"'bar'
|
||||
shell_squote() {
|
||||
printf "%s" "$1" | sed "s/'/'\"'\"'/g"
|
||||
}
|
||||
|
||||
# Detect if CWD is inside a *linked worktree* and print the worktree root.
|
||||
# Returns 0 with path on stdout if yes; 1 otherwise.
|
||||
get_worktree_path() {
|
||||
check_dir="$(pwd)"
|
||||
|
||||
if [ ! -d "${check_dir}" ]; then
|
||||
debug_log "pwd is not a directory: ${check_dir}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
while [ "${check_dir}" != "/" ]; do
|
||||
if [ -f "${check_dir}/.git" ]; then
|
||||
gitdir_content=""
|
||||
if [ -r "${check_dir}/.git" ]; then
|
||||
IFS= read -r gitdir_content < "${check_dir}/.git" || gitdir_content=""
|
||||
# Strip a possible trailing CR (CRLF files)
|
||||
gitdir_content=$(printf %s "$gitdir_content" | tr -d '\r')
|
||||
else
|
||||
debug_log "Unreadable .git file at: ${check_dir}"
|
||||
fi
|
||||
|
||||
case "${gitdir_content}" in
|
||||
gitdir:*)
|
||||
gitdir_path=${gitdir_content#gitdir:}
|
||||
# Trim leading spaces (portable)
|
||||
while [ "${gitdir_path# }" != "${gitdir_path}" ]; do
|
||||
gitdir_path=${gitdir_path# }
|
||||
done
|
||||
# Normalize to absolute
|
||||
case "${gitdir_path}" in
|
||||
/*) abs_gitdir="${gitdir_path}" ;;
|
||||
*) abs_gitdir="${check_dir}/${gitdir_path}" ;;
|
||||
esac
|
||||
if [ -d "${abs_gitdir}" ]; then
|
||||
case "${abs_gitdir}" in
|
||||
*/worktrees/*)
|
||||
debug_log "Detected worktree root: ${check_dir} (gitdir: ${abs_gitdir})"
|
||||
printf '%s\n' "${check_dir}"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
debug_log "Non-worktree .git indirection at: ${check_dir}"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
else
|
||||
debug_log "gitdir path does not exist: ${abs_gitdir}"
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
debug_log "Unknown .git file format at: ${check_dir}"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
elif [ -d "${check_dir}/.git" ]; then
|
||||
# Regular repo with .git directory — not a linked worktree
|
||||
debug_log "Found regular git repo at: ${check_dir}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
check_dir=$(dirname "${check_dir}")
|
||||
done
|
||||
|
||||
debug_log "No git repository found"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Decide whether to skip prefixing.
|
||||
# Returns 0 => SKIP (pass through as-is)
|
||||
# Returns 1 => Prefix with cd
|
||||
should_skip_command() {
|
||||
cmd=$1
|
||||
|
||||
# Empty or whitespace-only?
|
||||
# If there are no non-space characters, skip.
|
||||
if [ -z "${cmd##*[![:space:]]*}" ]; then
|
||||
debug_log "Skipping: empty/whitespace-only command"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Starts with optional spaces then 'cd' (with or without args)?
|
||||
case "${cmd}" in
|
||||
[[:space:]]cd|cd|[[:space:]]cd[[:space:]]*|cd[[:space:]]*)
|
||||
debug_log "Skipping: command already begins with cd"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Builtins / trivial commands that don't require dir context
|
||||
case "${cmd}" in
|
||||
:|[[:space:]]:|true|[[:space:]]true|false|[[:space:]]false|\
|
||||
pwd|[[:space:]]pwd*|\
|
||||
echo|[[:space:]]echo*|\
|
||||
export|[[:space:]]export*|\
|
||||
alias|[[:space:]]alias*|\
|
||||
unalias|[[:space:]]unalias*|\
|
||||
set|[[:space:]]set*|\
|
||||
unset|[[:space:]]unset*|\
|
||||
readonly|[[:space:]]readonly*|\
|
||||
umask|[[:space:]]umask*|\
|
||||
times|[[:space:]]times*|\
|
||||
.|[[:space:]].[[:space:]]*)
|
||||
debug_log "Skipping: trivial/builtin command"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
# Do NOT skip absolute-path commands; many still depend on cwd.
|
||||
# We want: cd '<root>' && /abs/cmd ... to preserve semantics.
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Inject the worktree prefix without changing semantics.
|
||||
# We do NOT wrap in 'sh -c'. We just prepend 'cd ... && '.
|
||||
# We preserve trailing '&' if present as the last non-space char.
|
||||
inject_prefix() {
|
||||
worktree_path=$1
|
||||
command=$2
|
||||
|
||||
qpath=$(shell_squote "${worktree_path}")
|
||||
|
||||
# Right-trim spaces (portable loop)
|
||||
trimmed=${command}
|
||||
while [ "${trimmed% }" != "${trimmed}" ]; do
|
||||
trimmed=${trimmed% }
|
||||
done
|
||||
|
||||
case "${trimmed}" in
|
||||
*"&")
|
||||
cmd_without_bg=${trimmed%&}
|
||||
while [ "${cmd_without_bg% }" != "${cmd_without_bg}" ]; do
|
||||
cmd_without_bg=${cmd_without_bg% }
|
||||
done
|
||||
printf '%s\n' "cd '${qpath}' && ${cmd_without_bg} &"
|
||||
;;
|
||||
*)
|
||||
printf '%s\n' "cd '${qpath}' && ${command}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main() {
|
||||
# Capture the raw command line exactly as provided
|
||||
original_command="$*"
|
||||
|
||||
debug_log "Processing command: ${original_command}"
|
||||
|
||||
# Fast path: if not in a worktree, pass through unchanged
|
||||
if ! worktree_path="$(get_worktree_path)"; then
|
||||
debug_log "Not in worktree, passing through unchanged"
|
||||
printf '%s\n' "${original_command}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if should_skip_command "${original_command}"; then
|
||||
debug_log "Passing through unchanged"
|
||||
printf '%s\n' "${original_command}"
|
||||
else
|
||||
modified_command="$(inject_prefix "${worktree_path}" "${original_command}")"
|
||||
debug_log "Modified command: ${modified_command}"
|
||||
printf '%s\n' "${modified_command}"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -2,6 +2,41 @@
|
||||
|
||||
Standard patterns for GitHub CLI operations across all commands.
|
||||
|
||||
## CRITICAL: Repository Protection
|
||||
|
||||
**Before ANY GitHub operation that creates/modifies issues or PRs:**
|
||||
|
||||
```bash
|
||||
# Check if remote origin is the CCPM template repository
|
||||
remote_url=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
if [[ "$remote_url" == *"automazeio/ccpm"* ]] || [[ "$remote_url" == *"automazeio/ccpm.git"* ]]; then
|
||||
echo "❌ ERROR: You're trying to sync with the CCPM template repository!"
|
||||
echo ""
|
||||
echo "This repository (automazeio/ccpm) is a template for others to use."
|
||||
echo "You should NOT create issues or PRs here."
|
||||
echo ""
|
||||
echo "To fix this:"
|
||||
echo "1. Fork this repository to your own GitHub account"
|
||||
echo "2. Update your remote origin:"
|
||||
echo " git remote set-url origin https://github.com/YOUR_USERNAME/YOUR_REPO.git"
|
||||
echo ""
|
||||
echo "Or if this is a new project:"
|
||||
echo "1. Create a new repository on GitHub"
|
||||
echo "2. Update your remote origin:"
|
||||
echo " git remote set-url origin https://github.com/YOUR_USERNAME/YOUR_REPO.git"
|
||||
echo ""
|
||||
echo "Current remote: $remote_url"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
This check MUST be performed in ALL commands that:
|
||||
- Create issues (`gh issue create`)
|
||||
- Edit issues (`gh issue edit`)
|
||||
- Comment on issues (`gh issue comment`)
|
||||
- Create PRs (`gh pr create`)
|
||||
- Any other operation that modifies the GitHub repository
|
||||
|
||||
## Authentication
|
||||
|
||||
**Don't pre-check authentication.** Just run the command and handle failure:
|
||||
@@ -19,16 +54,19 @@ gh issue view {number} --json state,title,labels,body
|
||||
|
||||
### Create Issue
|
||||
```bash
|
||||
# ALWAYS check remote origin first!
|
||||
gh issue create --title "{title}" --body-file {file} --label "{labels}"
|
||||
```
|
||||
|
||||
### Update Issue
|
||||
```bash
|
||||
# ALWAYS check remote origin first!
|
||||
gh issue edit {number} --add-label "{label}" --add-assignee @me
|
||||
```
|
||||
|
||||
### Add Comment
|
||||
```bash
|
||||
# ALWAYS check remote origin first!
|
||||
gh issue comment {number} --body-file {file}
|
||||
```
|
||||
|
||||
@@ -41,7 +79,8 @@ If any gh command fails:
|
||||
|
||||
## Important Notes
|
||||
|
||||
- **ALWAYS** check remote origin before ANY write operation to GitHub
|
||||
- Trust that gh CLI is installed and authenticated
|
||||
- Use --json for structured output when parsing
|
||||
- Keep operations atomic - one gh command per action
|
||||
- Don't check rate limits preemptively
|
||||
- Don't check rate limits preemptively
|
||||
|
||||
@@ -92,6 +92,19 @@ if git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
if git remote -v | grep -q origin; then
|
||||
remote_url=$(git remote get-url origin)
|
||||
echo " ✅ Remote configured: $remote_url"
|
||||
|
||||
# Check if remote is the CCPM template repository
|
||||
if [[ "$remote_url" == *"automazeio/ccpm"* ]] || [[ "$remote_url" == *"automazeio/ccpm.git"* ]]; then
|
||||
echo ""
|
||||
echo " ⚠️ WARNING: Your remote origin points to the CCPM template repository!"
|
||||
echo " This means any issues you create will go to the template repo, not your project."
|
||||
echo ""
|
||||
echo " To fix this:"
|
||||
echo " 1. Fork the repository or create your own on GitHub"
|
||||
echo " 2. Update your remote:"
|
||||
echo " git remote set-url origin https://github.com/YOUR_USERNAME/YOUR_REPO.git"
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ No remote configured"
|
||||
echo " Add with: git remote add origin <url>"
|
||||
|
||||
12
.claude/settings.json.example
Normal file
12
.claude/settings.json.example
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"hooks": {
|
||||
"pre-tool-use": {
|
||||
"Bash": {
|
||||
"enabled": true,
|
||||
"script": ".claude/hooks/bash-worktree-fix.sh",
|
||||
"description": "Automatically prepends worktree path to Bash commands when in a worktree",
|
||||
"apply_to_subagents": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
.DS_Store
|
||||
.idea/
|
||||
CLAUDE.md
|
||||
|
||||
27
README.md
27
README.md
@@ -346,7 +346,7 @@ Focus on building, not managing. Intelligent prioritization, automatic context l
|
||||
Teams using this system report:
|
||||
- **89% less time** lost to context switching – you'll use `/compact` and `/clear` a LOT less
|
||||
- **5-8 parallel tasks** vs 1 previously – editing/testing multiple files at the same time
|
||||
- **75% reduction** in bug rates – due to the breaking down features into detailed tasks
|
||||
- **75% reduction** in bug rates – due to the breaking down features into detailed tasks
|
||||
- **Up to 3x faster** feature delivery – based on feature size and complexity
|
||||
|
||||
## Example Flow
|
||||
@@ -382,13 +382,26 @@ Teams using this system report:
|
||||
|
||||
### Quick Setup (2 minutes)
|
||||
|
||||
1. **Clone this repository into your project**:
|
||||
1. **Install this repository into your project**:
|
||||
|
||||
#### Unix/Linux/macOS
|
||||
|
||||
```bash
|
||||
cd path/to/your/project/
|
||||
git clone https://github.com/automazeio/ccpm.git .
|
||||
curl -sSL https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.sh | bash
|
||||
# or: wget -qO- https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.sh | bash
|
||||
```
|
||||
|
||||
#### Windows (PowerShell)
|
||||
```bash
|
||||
cd path/to/your/project/
|
||||
iwr -useb https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.bat | iex
|
||||
```
|
||||
> ⚠️ **IMPORTANT**: If you already have a `.claude` directory, clone this repository to a different directory and copy the contents of the cloned `.claude` directory to your project's `.claude` directory.
|
||||
|
||||
See full/other installation options in the [installation guide ›](https://github.com/automazeio/ccpm/tree/main/install)
|
||||
|
||||
|
||||
2. **Initialize the PM system**:
|
||||
```bash
|
||||
/pm:init
|
||||
@@ -460,7 +473,7 @@ Claude Code PM was developed at [Automaze](https://automaze.io) **for developers
|
||||
|
||||
If Claude Code PM helps your team ship better software:
|
||||
|
||||
- ⭐ **[Star this repository](https://github.com/your-username/claude-code-pm)** to show your support
|
||||
- ⭐ **[Star this repository](https://github.com/automazeio/ccpm)** to show your support
|
||||
- 🐦 **[Follow @aroussi on X](https://x.com/aroussi)** for updates and tips
|
||||
|
||||
|
||||
@@ -469,3 +482,9 @@ If Claude Code PM helps your team ship better software:
|
||||
> [!TIP]
|
||||
> **Ship faster with Automaze.** We partner with founders to bring their vision to life, scale their business, and optimize for success.
|
||||
> **[Visit Automaze to book a call with me ›](https://automaze.io)**
|
||||
|
||||
---
|
||||
|
||||
## Star History
|
||||
|
||||

|
||||
|
||||
42
install/README.md
Normal file
42
install/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Quick Install
|
||||
|
||||
## Unix/Linux/macOS
|
||||
|
||||
```bash
|
||||
curl -sSL https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.sh | bash
|
||||
```
|
||||
|
||||
Or with wget:
|
||||
|
||||
```bash
|
||||
wget -qO- https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.sh | bash
|
||||
```
|
||||
|
||||
## Windows (PowerShell)
|
||||
|
||||
```powershell
|
||||
iwr -useb https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.bat | iex
|
||||
```
|
||||
|
||||
Or download and execute:
|
||||
|
||||
```powershell
|
||||
curl -o ccpm.bat https://raw.githubusercontent.com/automazeio/ccpm/main/ccpm.bat && ccpm.bat
|
||||
```
|
||||
|
||||
## One-liner alternatives
|
||||
|
||||
### Unix/Linux/macOS (direct commands)
|
||||
```bash
|
||||
git clone https://github.com/automazeio/ccpm.git . && rm -rf .git
|
||||
```
|
||||
|
||||
### Windows (cmd)
|
||||
```cmd
|
||||
git clone https://github.com/automazeio/ccpm.git . && rmdir /s /q .git
|
||||
```
|
||||
|
||||
### Windows (PowerShell)
|
||||
```powershell
|
||||
git clone https://github.com/automazeio/ccpm.git .; Remove-Item -Recurse -Force .git
|
||||
```
|
||||
18
install/ccpm.bat
Normal file
18
install/ccpm.bat
Normal file
@@ -0,0 +1,18 @@
|
||||
@echo off
|
||||
|
||||
set REPO_URL=https://github.com/automazeio/ccpm.git
|
||||
set TARGET_DIR=.
|
||||
|
||||
echo Cloning repository from %REPO_URL%...
|
||||
git clone %REPO_URL% %TARGET_DIR%
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo Clone successful. Removing .git directory...
|
||||
rmdir /s /q .git 2>nul
|
||||
rmdir /s /q install 2>nul
|
||||
del /q .gitignore 2>nul
|
||||
echo Git directory removed. Repository is now untracked.
|
||||
) else (
|
||||
echo Error: Failed to clone repository.
|
||||
exit /b 1
|
||||
)
|
||||
16
install/ccpm.sh
Normal file
16
install/ccpm.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
REPO_URL="https://github.com/automazeio/ccpm.git"
|
||||
TARGET_DIR="."
|
||||
|
||||
echo "Cloning repository from $REPO_URL..."
|
||||
git clone "$REPO_URL" "$TARGET_DIR"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Clone successful. Removing .git directory..."
|
||||
rm -rf .git .gitignore install
|
||||
echo "Git directory removed. Repository is now untracked."
|
||||
else
|
||||
echo "Error: Failed to clone repository."
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user