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>
This commit is contained in:
committed by
Steve Yegge
parent
37f465bde5
commit
dcf7b81011
116
internal/cmd/tap_guard.go
Normal file
116
internal/cmd/tap_guard.go
Normal file
@@ -0,0 +1,116 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user