Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions plugins/git-pilot/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
> **Note:** The `git-pilot-workflow` skill contains the definitive behavioral contract for git-pilot. This CLAUDE.md file serves as reinforcement. If both are loaded, the skill takes precedence.

# git-pilot — Git Workflow Autopilot

git-pilot manages the full git workflow lifecycle. You MUST follow these rules throughout every session.
Expand Down
7 changes: 4 additions & 3 deletions plugins/git-pilot/scripts/post-bash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ if echo "$command" | grep -qE 'git\s+commit'; then
unpushed_count=$(echo "$unpushed" | wc -l | tr -d ' ')

# Emit push prompt — CLAUDE.md instructs Claude to act on this with AskUserQuestion
message="[git-pilot] ${unpushed_count} unpushed commit(s) on '${current_branch}'. Push command: git push -u ${remote_name} ${current_branch}"
message="[git-pilot] ${unpushed_count} unpushed commit(s) on '${current_branch}'. You MUST use AskUserQuestion NOW to ask the user whether to push. Push command: git push -u ${remote_name} ${current_branch}. Do not silently skip this prompt."

jq -n --arg msg "$message" '{"continue": true, "systemMessage": $msg}'
exit 0
Expand All @@ -88,11 +88,12 @@ if echo "$command" | grep -qE 'git\s+push'; then
if echo "$stderr" | grep -qiE 'rejected|failed to push|non-fast-forward'; then
current_branch=$(get_current_branch)
remote_name=$(get_config "$config" '.remote.defaultName' 'origin')
message="[git-pilot] Push rejected -- remote '${remote_name}/${current_branch}' has new commits. Prompt the user:
message="[git-pilot] Push rejected remote '${remote_name}/${current_branch}' has new commits. STOP and use AskUserQuestion NOW to present these options:
1. Pull and rebase, then retry push (git pull --rebase && git push)
2. Force push with lease (git push --force-with-lease)
3. Pull and merge (git pull)
4. Cancel"
4. Cancel
Do not attempt any resolution without the user's choice."
jq -n --arg msg "$message" '{"continue": true, "systemMessage": $msg}'
exit 0
fi
Expand Down
6 changes: 3 additions & 3 deletions plugins/git-pilot/scripts/post-write.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ write_state "$state_file" "$reset_state"
# Act based on mode
case "$mode" in
suggest)
message="[git-pilot] You've made ${threshold} file changes since the last commit. Consider committing your progress for easier rollback. Modified files: ${modified_files}"
message="[git-pilot] You have made ${threshold} file changes since the last commit (${modified_files}). You MUST commit your progress now for easier rollback. Use AskUserQuestion to confirm with the user, then commit with a descriptive message following the configured commit format."
;;
auto)
message="[git-pilot] Auto-commit threshold reached. Commit your current changes now with a descriptive message following the commit format: ${commit_pattern}"
message="[git-pilot] Auto-commit threshold reached (${threshold} file changes). You MUST commit your current changes NOW with a descriptive message following the commit format: ${commit_pattern}. Do not continue writing code until this commit is made."
;;
silent)
commit_msg="${wip_prefix}checkpoint after ${threshold} file changes"
Expand All @@ -90,7 +90,7 @@ case "$mode" in
;;
*)
# Unknown mode, fall back to suggest
message="[git-pilot] You've made ${threshold} file changes since the last commit. Consider committing your progress for easier rollback. Modified files: ${modified_files}"
message="[git-pilot] You have made ${threshold} file changes since the last commit (${modified_files}). You MUST commit your progress now for easier rollback. Use AskUserQuestion to confirm with the user, then commit with a descriptive message following the configured commit format."
;;
esac

Expand Down
2 changes: 1 addition & 1 deletion plugins/git-pilot/scripts/prompt-context.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ if [[ "$commit_count" == "0" ]]; then echo '{"continue": true}'; exit 0; fi

branch_purpose=$(derive_branch_purpose "$current_branch")
recent_commits=$(git log "${default_branch}..${current_branch}" --oneline --no-decorate -5 2>/dev/null || true)
message="[git-pilot] Branch context: '${current_branch}' (${branch_purpose}). ${commit_count} commit(s). Recent: ${recent_commits}"
message="[git-pilot] You are on branch '${current_branch}' (purpose: ${branch_purpose}, ${commit_count} commit(s)). Before acting on the user's next request, assess whether the request is related to this branch's purpose. If unrelated, STOP and use AskUserQuestion to suggest creating a new branch. Recent commits: ${recent_commits}"
jq -n --arg msg "$message" '{"continue": true, "systemMessage": $msg}'
16 changes: 8 additions & 8 deletions plugins/git-pilot/scripts/session-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ if is_git_repo; then
| grep -m1 'checkout: moving from' \
| sed 's/checkout: moving from \([^ ]*\) to .*/\1/' || true)
if [[ -n "$prev_branch" ]]; then
messages+=("[git-pilot] Detached HEAD at ${head_sha}. Previous branch was '${prev_branch}'. Prompt the user: return to '${prev_branch}', create a new branch from HEAD, or continue in detached state.")
messages+=("[git-pilot] Detached HEAD at ${head_sha}. You MUST NOT make any code changes in detached HEAD state. Use AskUserQuestion NOW to ask the user to choose: (1) return to '${prev_branch}', (2) create a new branch from HEAD, or (3) continue in detached state. Do not proceed until the user responds.")
else
messages+=("[git-pilot] Detached HEAD at ${head_sha}. Prompt the user: create a new branch from HEAD or continue in detached state.")
messages+=("[git-pilot] Detached HEAD at ${head_sha}. You MUST NOT make any code changes in detached HEAD state. Use AskUserQuestion NOW to ask the user to choose: (1) create a new branch from HEAD, or (2) continue in detached state. Do not proceed until the user responds.")
fi
fi

Expand All @@ -94,16 +94,16 @@ if is_git_repo; then
if git merge --ff-only "${remote_name}/${current_branch}" >/dev/null 2>&1; then
messages+=("[git-pilot] Branch '${current_branch}' was ${behind_count} commit(s) behind '${remote_name}/${current_branch}'. Fast-forwarded to latest.")
else
messages+=("[git-pilot] Branch '${current_branch}' is ${behind_count} commit(s) behind '${remote_name}/${current_branch}' but fast-forward failed. Prompt the user: pull with merge, reset to remote, or continue as-is.")
messages+=("[git-pilot] Branch '${current_branch}' is ${behind_count} commit(s) behind '${remote_name}/${current_branch}' and fast-forward failed. You MUST use AskUserQuestion NOW to ask the user to choose: (1) pull with merge, (2) reset to remote, or (3) continue as-is. Do not proceed with any code changes until this is resolved.")
fi
;;
diverged:*:*)
IFS=':' read -r _ ahead_count behind_count <<< "$tracking_status"
messages+=("[git-pilot] Branch '${current_branch}' has diverged from '${remote_name}/${current_branch}' (${ahead_count} local, ${behind_count} remote). Prompt the user: rebase onto remote, merge remote, reset to remote, or continue.")
messages+=("[git-pilot] Branch '${current_branch}' has diverged from '${remote_name}/${current_branch}' (${ahead_count} local, ${behind_count} remote). You MUST use AskUserQuestion NOW to ask the user to choose: (1) rebase onto remote, (2) merge remote changes, (3) reset to remote, or (4) continue as-is. Do not make any code changes until this is resolved.")
;;
ahead:*)
ahead_count="${tracking_status#ahead:}"
messages+=("[git-pilot] Branch '${current_branch}' is ${ahead_count} commit(s) ahead of '${remote_name}/${current_branch}'. Unpushed changes.")
messages+=("[git-pilot] Branch '${current_branch}' has ${ahead_count} unpushed commit(s). Before continuing, use AskUserQuestion to ask the user whether to push now or continue working.")
;;
# up-to-date and no-remote: no message
esac
Expand All @@ -117,9 +117,9 @@ if is_git_repo; then
branch_types=$(echo "$CONFIG" | jq -r '.branch.types // ["feat","fix","refactor","docs","test","chore","style","perf","build","ci"] | join(", ")')

if has_uncommitted_changes; then
messages+=("[git-pilot] On '${default_branch}' with uncommitted changes. Prompt the user: stash and create branch, commit first, or continue on '${default_branch}'.")
messages+=("[git-pilot] You are on the default branch '${default_branch}' with uncommitted changes. You MUST NOT make any further code changes until this is resolved. Use AskUserQuestion NOW to ask the user to choose: (1) stash changes and create a feature branch, (2) commit current changes first, or (3) continue on '${default_branch}'. Do not proceed until the user responds.")
else
messages+=("[git-pilot] On default branch '${default_branch}'. Prompt the user to create a branch before making changes. Pattern: ${branch_pattern}, types: ${branch_types}.")
messages+=("[git-pilot] You are on the default branch '${default_branch}'. You MUST NOT make any code changes until a feature branch is created. Use AskUserQuestion to ask the user for the branch type and description NOW, before doing anything else. Pattern: ${branch_pattern}, types: ${branch_types}.")
fi
fi
fi
Expand All @@ -131,7 +131,7 @@ if is_git_repo; then
skip_remote=$(get_config "$CONFIG" '.remote.skipRemotePrompt' 'false')

if [[ "$prompt_remote" == "true" ]] && [[ "$skip_remote" == "false" ]]; then
messages+=("[git-pilot] No git remote configured. Prompt the user to add one or skip.")
messages+=("[git-pilot] No git remote configured. Use AskUserQuestion to ask the user whether to add a remote now (provide the command format: git remote add origin <url>) or skip. This affects push and sync capabilities for this session.")
fi
fi
fi
Expand Down
14 changes: 7 additions & 7 deletions plugins/git-pilot/scripts/session-stop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ if [[ "$session_had_changes" == "true" ]] && has_remote; then
case "$drift_status" in
drifted:*)
drift_count="${drift_status#drifted:}"
messages+=("[git-pilot] Base branch '${default_branch}' has ${drift_count} new commit(s). Rebasing '${current_branch}' onto '${remote_name}/${default_branch}'...")
messages+=("[git-pilot] Base branch '${default_branch}' has ${drift_count} new commit(s) since you branched. Rebasing '${current_branch}' onto '${remote_name}/${default_branch}' now. If conflicts arise, you MUST use AskUserQuestion to present resolution options before proceeding.")
rebase_result=$(attempt_rebase "${remote_name}/${default_branch}" || true)
case "$rebase_result" in
success)
Expand All @@ -184,7 +184,7 @@ if [[ "$session_had_changes" == "true" ]] && has_remote; then
conflicts=$(get_conflict_details)
conflict_count=$(echo "$conflicts" | jq 'length')
conflict_files=$(echo "$conflicts" | jq -r '.[].file' | paste -sd', ')
messages+=("[git-pilot] Rebase conflicts in ${conflict_count} file(s): ${conflict_files}. Prompt the user to resolve conflicts, abort rebase, or use merge instead.")
messages+=("[git-pilot] Rebase conflicts in ${conflict_count} file(s): ${conflict_files}. STOP and use AskUserQuestion NOW to ask the user to choose: (1) resolve conflicts manually, (2) abort rebase, or (3) fall back to merge. Do not proceed until the user responds.")
;;
abort)
git rebase --abort 2>/dev/null || true
Expand All @@ -197,7 +197,7 @@ if [[ "$session_had_changes" == "true" ]] && has_remote; then
else
fallback_conflicts=$(get_conflict_details)
fallback_conflict_count=$(echo "$fallback_conflicts" | jq 'length')
messages+=("[git-pilot] Both rebase and merge have conflicts in ${fallback_conflict_count} file(s). Prompt the user to resolve.")
messages+=("[git-pilot] Both rebase and merge have conflicts in ${fallback_conflict_count} file(s). STOP and use AskUserQuestion NOW to present the conflict details and ask the user how to proceed. Do not push or continue until conflicts are resolved.")
fi
;;
esac
Expand All @@ -208,7 +208,7 @@ if [[ "$session_had_changes" == "true" ]] && has_remote; then
# No action needed
;;
no-common-ancestor)
messages+=("[git-pilot] Cannot determine common ancestor between '${current_branch}' and '${default_branch}'. Skipping rebase. Push may require manual review.")
messages+=("[git-pilot] Cannot determine common ancestor between '${current_branch}' and '${default_branch}'. Skipping rebase. Before pushing, use AskUserQuestion to warn the user that the push may require manual review.")
;;
esac
fi
Expand All @@ -235,7 +235,7 @@ if [[ "$session_had_changes" == "true" ]] && has_remote; then

case "$push_on_finish" in
always)
messages+=("[git-pilot] Pushing ${unpushed_count} commit(s) to '${remote_name}/${current_branch}'. Run: git push -u ${remote_name} ${current_branch}")
messages+=("[git-pilot] You MUST push ${unpushed_count} commit(s) to '${remote_name}/${current_branch}' now. Run: git push -u ${remote_name} ${current_branch}. Execute this command immediately.")
;;
*)
# "ask" and "never" are silent in the stop hook.
Expand Down Expand Up @@ -354,7 +354,7 @@ if [[ "$session_had_changes" == "true" ]] && has_remote; then
fi

if [[ -n "$mr_cmd" ]]; then
messages+=("[git-pilot] Creating merge/pull request. Run: ${mr_cmd}")
messages+=("[git-pilot] You MUST create a merge/pull request now. Run: ${mr_cmd}. Execute this command immediately.")
fi
fi
# If CLI tool is missing, stay silent — user can use /finish or create MR manually
Expand Down Expand Up @@ -386,7 +386,7 @@ if [[ "$active_count" -gt 0 ]]; then
fi
remaining=$(list_worktrees | jq '.worktrees | length')
if [[ "$remaining" -gt 0 ]]; then
messages+=("[git-pilot] ${remaining} active worktree(s) remain. Use /worktree to manage them.")
messages+=("[git-pilot] ${remaining} active worktree(s) remain after session cleanup. Before ending, use AskUserQuestion to ask the user whether to clean up remaining worktrees using the /worktree skill.")
fi
fi

Expand Down
3 changes: 2 additions & 1 deletion plugins/git-pilot/skills/branch/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: branch
description: Create a new branch using the configured naming pattern
user-invocable: true
description: "USE THIS SKILL WHEN you are about to start making code changes and you are on the default branch, when the user describes a new feature or fix, or when starting any implementation work. Creates a new branch using the configured naming pattern so work never happens directly on the default branch. Trigger on: user requests a feature, bug fix, refactor, or any code change; user says 'let's work on X'; you detect you are on main/master/default branch before writing code; the git-pilot-workflow skill directs you to branch. Even if the user doesn't explicitly ask for a branch, still use this skill before making changes on the default branch."
---

# /branch
Expand Down
3 changes: 2 additions & 1 deletion plugins/git-pilot/skills/configure/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: configure
description: Configure git-pilot settings using natural language
description: "USE THIS SKILL WHEN the user wants to change any git-pilot behavior, mentions commit format preferences, branch naming conventions, or push/merge request settings. Configures git-pilot settings through natural language. Trigger on phrases like 'stop asking me about X,' 'always do Y,' 'change the commit format,' 'use underscores in branch names,' 'auto-push after commit,' 'disable auto-commit,' or any request to customize git workflow behavior. Even if the user only vaguely mentions wanting something to work differently, still use this skill."
user-invocable: true
---

# /configure
Expand Down
3 changes: 2 additions & 1 deletion plugins/git-pilot/skills/finish/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
name: finish
description: Finish work - commit remaining changes, push, and optionally create a merge request
user-invocable: true
description: "USE THIS SKILL WHEN the user signals that the current unit of work is over. Commits remaining changes, pushes to remote, and optionally creates a merge request to wrap up the session. Trigger on: user says 'done', 'finished', 'that's it', 'ship it', 'wrap it up', 'we're good', 'let's call it', 'I'm done', 'push it', end of session, task completion, or any indication that work is complete. Even if the user only hints at being done or says something casual like 'looks good', still use this skill to ensure nothing is left uncommitted or unpushed."
---

# /finish
Expand Down
Loading