Files
gastown/internal/cmd/tap_guard.go
beads/crew/emma dcf7b81011 refactor(hooks): rename to gt tap guard pr-workflow
Reorganizes Claude Code hook handlers under `gt tap` namespace:
- gt tap - parent command for all hook handlers
- gt tap guard - subcommand for blocking operations
- gt tap guard pr-workflow - blocks PR creation and feature branches

This structure allows future expansion:
- gt tap audit <x>   - logging/metrics (PostToolUse)
- gt tap inject <x>  - input modification (PreToolUse)
- gt tap check <x>   - validation (PostToolUse)

Replaces the flat gt block-pr-workflow command.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:21:54 -08:00

117 lines
3.9 KiB
Go

package cmd
import (
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
)
var tapGuardCmd = &cobra.Command{
Use: "guard",
Short: "Block forbidden operations (PreToolUse hook)",
Long: `Block forbidden operations via Claude Code PreToolUse hooks.
Guard commands exit with code 2 to BLOCK tool execution when a policy
is violated. They're called before the tool runs, preventing the
forbidden operation entirely.
Available guards:
pr-workflow - Block PR creation and feature branches
Example hook configuration:
{
"PreToolUse": [{
"matcher": "Bash(gh pr create*)",
"hooks": [{"command": "gt tap guard pr-workflow"}]
}]
}`,
}
var tapGuardPRWorkflowCmd = &cobra.Command{
Use: "pr-workflow",
Short: "Block PR creation and feature branches",
Long: `Block PR workflow operations in Gas Town.
Gas Town workers push directly to main. PRs add friction that breaks
the autonomous execution model (GUPP principle).
This guard blocks:
- gh pr create
- git checkout -b (feature branches)
- git switch -c (feature branches)
Exit codes:
0 - Operation allowed (not in Gas Town agent context)
2 - Operation BLOCKED (in agent context)
The guard only blocks when running as a Gas Town agent (crew, polecat,
witness, etc.). Humans running outside Gas Town can still use PRs.`,
RunE: runTapGuardPRWorkflow,
}
func init() {
tapCmd.AddCommand(tapGuardCmd)
tapGuardCmd.AddCommand(tapGuardPRWorkflowCmd)
}
func runTapGuardPRWorkflow(cmd *cobra.Command, args []string) error {
// Check if we're in a Gas Town agent context
if !isGasTownAgentContext() {
// Not in a Gas Town managed context - allow the operation
return nil
}
// We're in a Gas Town context - block PR operations
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, "╔══════════════════════════════════════════════════════════════════╗")
fmt.Fprintln(os.Stderr, "║ ❌ PR WORKFLOW BLOCKED ║")
fmt.Fprintln(os.Stderr, "╠══════════════════════════════════════════════════════════════════╣")
fmt.Fprintln(os.Stderr, "║ Gas Town workers push directly to main. PRs are forbidden. ║")
fmt.Fprintln(os.Stderr, "║ ║")
fmt.Fprintln(os.Stderr, "║ Instead of: gh pr create / git checkout -b / git switch -c ║")
fmt.Fprintln(os.Stderr, "║ Do this: git add . && git commit && git push origin main ║")
fmt.Fprintln(os.Stderr, "║ ║")
fmt.Fprintln(os.Stderr, "║ Why? PRs add friction that breaks autonomous execution. ║")
fmt.Fprintln(os.Stderr, "║ See: ~/gt/docs/PRIMING.md (GUPP principle) ║")
fmt.Fprintln(os.Stderr, "╚══════════════════════════════════════════════════════════════════╝")
fmt.Fprintln(os.Stderr, "")
os.Exit(2) // Exit 2 = BLOCK in Claude Code hooks
return nil
}
// isGasTownAgentContext returns true if we're running as a Gas Town managed agent.
func isGasTownAgentContext() bool {
// Check environment variables set by Gas Town session management
envVars := []string{
"GT_POLECAT",
"GT_CREW",
"GT_WITNESS",
"GT_REFINERY",
"GT_MAYOR",
"GT_DEACON",
}
for _, env := range envVars {
if os.Getenv(env) != "" {
return true
}
}
// Also check if we're in a crew or polecat worktree by path
cwd, err := os.Getwd()
if err != nil {
return false
}
agentPaths := []string{"/crew/", "/polecats/"}
for _, path := range agentPaths {
if strings.Contains(cwd, path) {
return true
}
}
return false
}