refactor: update git workflow to dev branch model — fix commit handler, prompts, help text, hook redirects

This commit is contained in:
AIOSAI
2026-05-12 21:46:41 -07:00
parent 2eb1d1d3d9
commit 43b360a286
6 changed files with 64 additions and 48 deletions
+8 -6
View File
@@ -75,18 +75,20 @@ TRUSTED_HOOK_EDITORS = ("devpulse", "seedgo")
GIT_REDIRECT = (
"Raw git write commands are blocked. Use drone instead:\n"
' drone @git pr "description" # branch-scoped PR\n'
' drone @git system-pr "description" # devpulse-only system PR\n'
" drone @git smart-sync # fetch + rebase\n"
" drone @git sync # checkout main + pull\n"
" drone @git status # what changed\n"
" drone @git diff # see changes\n"
" drone @git log # commit history\n"
" drone @git commit 'msg' --all # commit all changes (devpulse only)\n"
' drone @git dev-pr "description" # PR dev to main (devpulse only)\n'
" drone @git smart-sync # fetch + rebase\n"
"Read-only git (status, log, diff, show, fetch, ls-files) is allowed."
)
GH_REDIRECT = (
"Raw gh write commands are blocked. Use drone for git ops:\n"
' drone @git pr "description"\n'
" drone @git merge <PR#> # devpulse only, on user request\n"
' drone @git dev-pr "description" # PR dev to main\n'
" drone @git merge <PR#> # merge a PR (devpulse only)\n"
" drone @git issue list/create/view # gh issue passthrough\n"
"Read-only gh (list, view, status, diff, checks, comments) is allowed."
)
@@ -83,11 +83,11 @@ drone @memory search <query> # Search archived memories
### Git Workflow
```
drone @git pr 'description' # Create a pull request
drone @git status # Git status (branch-scoped)
drone @git sync # Sync with main
drone @git lock / unlock # Lock/unlock the repo
drone @git diff # See changes
drone @git log # Commit history
```
You have no git write access. Devpulse handles all commits and PRs.
### Infrastructure
```
@@ -48,11 +48,13 @@ When a task belongs to a specialist's DOMAIN, ask them. You can still investigat
drone @git status # What changed? (all branches can use)
drone @git diff # See the diff (all branches can use)
drone @git log # Recent commits (all branches can use)
drone @git commit "description" # Commit changes (devpulse only)
drone @git branches # List remote branches (all branches can use)
drone @git commit "msg" --all # Commit all changes (devpulse only)
drone @git checkout dev # Switch to dev branch (devpulse only)
drone @git checkout main # Switch to main (devpulse only)
drone @git system-pr "description" # System-wide PR (devpulse only)
drone @git dev-pr "description" # PR dev to main (devpulse only)
drone @git merge <PR#> # Merge a PR (devpulse only, user must request)
drone @git delete-branch <name> # Delete remote branch (devpulse only)
drone @git sync # Pull latest (devpulse only)
drone @git smart-sync # Fetch + rebase (devpulse only)
drone @git fix # Fix broken git states (devpulse only)
@@ -58,13 +58,23 @@ def commit_changes(
branch_dir: Path | None = None,
all_files: bool = False,
) -> dict:
"""Commit staged changes or all changes under branch_dir."""
"""Commit changes. With --all, stages the entire repo (not CWD-scoped).
Post-DPLAN-0173: only devpulse commits, agents don't PR. Repo-wide
staging is the correct default since dispatched agents work across
multiple branch directories.
"""
repo_root = find_repo_root()
if all_files and branch_dir:
stage_result = stage_branch_dir(branch_dir, repo_root)
if not stage_result["success"]:
return {"stdout": "", "stderr": stage_result["message"], "exit_code": 1}
if all_files:
add_result = subprocess.run(
["git", "add", "-A"],
capture_output=True,
text=True,
cwd=str(repo_root),
)
if add_result.returncode != 0:
return {"stdout": "", "stderr": f"Failed to stage: {add_result.stderr.strip()}", "exit_code": 1}
diff_check = subprocess.run(
["git", "diff", "--cached", "--quiet"],
@@ -81,13 +91,6 @@ def commit_changes(
try:
cmd = ["git", "commit", "-m", message]
if branch_dir:
try:
rel_dir = branch_dir.resolve().relative_to(repo_root.resolve())
except ValueError as exc:
logger.warning("commit_changes: branch_dir not relative to repo root: %s", exc)
rel_dir = branch_dir
cmd.extend(["--", str(rel_dir) + "/"])
result = subprocess.run(
cmd,
+31 -22
View File
@@ -462,13 +462,7 @@ def _handle_commit(args: list[str]) -> dict:
"exit_code": 1,
}
branch_dir = None
if all_files:
detected = _detect_branch_dir()
if detected:
_, branch_dir = detected
return commit_handler.commit_changes(message, branch_dir=branch_dir, all_files=all_files)
return commit_handler.commit_changes(message, all_files=all_files)
def _handle_checkout(args: list[str]) -> dict:
@@ -567,11 +561,23 @@ def get_help(command: str | None = None) -> str:
return "git log [count] — Show recent git log entries (default: 10) [global]\n"
if command == "lock":
return "git lock — Check current lock status [global]\n Shows lock holder, age, stale/orphan detection.\n"
if command == "branches":
return "git branches — List all remote branches [global]\n"
if command == "dev-pr":
return (
"git dev-pr <description> — Push dev branch and create PR to main [owner]\n"
" Description becomes the PR title.\n"
)
if command == "delete-branch":
return (
"git delete-branch <name> — Delete a remote branch [owner]\n"
" Protected: main and dev cannot be deleted.\n"
)
if command == "commit":
return (
"git commit <message> [--all] — Commit staged changes [owner]\n"
"git commit <message> [--all] — Commit changes [owner]\n"
" Options:\n"
" --all Stage all changes under your branch directory first.\n"
" --all Stage all repo changes (git add -A) before committing.\n"
)
if command == "checkout":
return "git checkout <main|dev> — Switch branches (main or dev only) [owner]\n"
@@ -610,36 +616,36 @@ def get_help(command: str | None = None) -> str:
)
return (
"git — Tier-based git workflow\n"
"git — Tier-based git workflow (dev branch model)\n"
"\n"
"Global (all branches):\n"
" status Show git status for your branch\n"
" diff [--staged] Show git diff for your branch\n"
" log [count] Show recent git log (default: 10)\n"
" lock Check lock status\n"
" branches List remote branches\n"
" issue [args] Passthrough to gh issue\n"
" run [args] Passthrough to gh run\n"
" workflow [args] Passthrough to gh workflow\n"
"\n"
"Owner (devpulse only):\n"
" commit <msg> [--all] Commit staged changes\n"
" commit <msg> [--all] Commit changes (--all stages entire repo)\n"
" checkout <main|dev> Switch branches\n"
" sync [--autostash] Checkout main and pull\n"
" unlock --force Force-release the PR lock\n"
" system-pr <desc> Create a system-wide PR\n"
" dev-pr <desc> Push dev and create PR to main\n"
" delete-branch <name> Delete a remote branch\n"
" merge <PR#> Merge a PR\n"
" sync [--autostash] Checkout main and pull\n"
" smart-sync Fetch + rebase if behind\n"
" unlock --force Force-release the PR lock\n"
" system-pr <desc> Legacy system-wide PR (use dev-pr)\n"
" fix [--dry-run] Fix broken git states\n"
"\n"
"Deprecated:\n"
" pr Agent PRs removed — devpulse handles git\n"
)
def get_introspective() -> str:
"""Return introspection text showing connected handlers."""
return (
"@git — Tier-based git workflow (v2.0.0)\n"
"@git — Tier-based git workflow, dev branch model (v3.0.0)\n"
"\n"
"Connected Handlers:\n"
" handlers/git/\n"
@@ -647,14 +653,17 @@ def get_introspective() -> str:
" - status_handler.py (get_branch_status — scoped git status)\n"
" - diff_handler.py (get_branch_diff — scoped git diff)\n"
" - log_handler.py (get_git_log — recent log entries)\n"
" - commit_handler.py (commit_changes, stage_branch_dir)\n"
" - commit_handler.py (commit_changes — repo-wide staging with --all)\n"
" - checkout_handler.py (checkout_branch — main/dev only)\n"
" - sync_handler.py (sync_main — safe main synchronization)\n"
" - pr_handler.py (create_pr — DEPRECATED)\n"
" - dev_pr_handler.py (create_dev_pr — push dev, PR to main)\n"
" - branches_handler.py (list_remote_branches)\n"
" - delete_branch_handler.py (delete_remote_branch — protected: main/dev)\n"
" - pr_handler.py (create_pr — DEPRECATED, kept for reference)\n"
"\n"
" plugins/devpulse_ops/\n"
" - auth.py (verify_git_access — tier-based authorization)\n"
" - pr_plugin.py (create_system_pr — system-wide PR workflow)\n"
" - pr_plugin.py (create_system_pr — legacy, use dev-pr instead)\n"
" - merge_plugin.py (merge_pr — merge PR + sync)\n"
" - sync_plugin.py (smart_sync — fetch + rebase if behind)\n"
" - fix_plugin.py (fix_git_state — detect/fix broken states)\n"
@@ -662,7 +671,7 @@ def get_introspective() -> str:
" gh passthrough:\n"
" - issue, run, workflow → subprocess gh <cmd> [args]\n"
"\n"
"Access Tiers: global (status, diff, log, lock, issue, run, workflow) | owner (commit, checkout, sync, unlock, system-pr, merge, smart-sync, fix)\n"
"Access Tiers: global (status, diff, log, lock, branches, issue, run, workflow) | owner (commit, checkout, dev-pr, delete-branch, sync, unlock, system-pr, merge, smart-sync, fix)\n"
)
@@ -86,8 +86,8 @@ def _find_caller() -> str:
def verify_caller() -> str:
"""Verify the calling branch is authorized for devpulse operations.
system-pr, merge, smart-sync, fix are restricted to ALLOWED_CALLERS.
Other branches use drone @git pr for their own branch-scoped PRs.
Owner-tier commands (commit, dev-pr, merge, etc.) are restricted to
ALLOWED_CALLERS. Other branches have read-only access via global tier.
Returns:
The caller's branch name if authorized.
@@ -97,7 +97,7 @@ def verify_caller() -> str:
"""
name = _find_caller()
if name not in ALLOWED_CALLERS:
msg = f"Branch '{name}' is not authorized for this operation. Use 'drone @git pr' for branch-scoped PRs."
msg = f"Branch '{name}' is not authorized for this operation. Only devpulse can use owner-tier commands."
logger.error(msg)
raise PermissionError(msg)
json_handler.log_operation(