gate hooks on checkpoint policy · Entire

gate hooks on checkpoint policy

Make agent hooks surface unsupported checkpoint policy loudly while Git hooks skip checkpoint work without blocking Git.

Sessions

Changes

9

3 unmodified lines
package cli

import (
    "context"
    "errors"
    "fmt"
    "io"
    "log/slog"
    "strings"

"github.com/entireio/cli/cmd/entire/cli/agent"
    "github.com/entireio/cli/cmd/entire/cli/agent/claudecode"
    "github.com/entireio/cli/cmd/entire/cli/agent/geminicli"
    "github.com/entireio/cli/cmd/entire/cli/agent/types"
    "github.com/entireio/cli/cmd/entire/cli/checkpointpolicy"
    "github.com/entireio/cli/cmd/entire/cli/gitrepo"
    "github.com/entireio/cli/cmd/entire/cli/logging"
    "github.com/entireio/cli/cmd/entire/cli/paths"
    "github.com/entireio/cli/cmd/entire/cli/strategy"
    "github.com/entireio/cli/cmd/entire/cli/versioncheck"
    "github.com/entireio/cli/cmd/entire/cli/versioninfo"
    "github.com/entireio/cli/perf"
    "github.com/spf13/cobra"
)

// their parent command's PersistentPreRunE already handles logging.
func executeAgentHook(cmd *cobra.Command, agentName types.AgentName, hookName string, initLogging bool) error {
    // Skip silently if not in a git repository - hooks shouldn't prevent the agent from working
    if _, err := paths.WorktreeRoot(cmd.Context()); err != nil {
        worktreeRoot, err := paths.WorktreeRoot(cmd.Context())
        if err != nil {
            return nil
        }
    }

// remaining function implementation omitted for brevity
}
func shouldSkipAgentHookForPolicy(ctx context.Context, worktreeRoot string, errW io.Writer, ag agent.Agent, eventType agent.EventType) (skipHook bool, err error) {
    repo, err := gitrepo.OpenPath(worktreeRoot)
    if err != nil {
        logging.Warn(ctx, "checkpoint policy read skipped for agent hook",
            slog.String("error", err.Error()))
        return false, nil
    }
    defer repo.Close()

policy := localCheckpointPolicyForNewCheckpoint(ctx, repo)
    if checkpointpolicy.CanSatisfyPolicy(policy) {
        return false, nil
    }
    if eventType == agent.SessionStart {
        return true, writeUnsupportedPolicySessionStartWarning(errW, ag, sessionStartPolicyWarning(policy))
    }
    fmt.Fprint(errW, agentCheckpointCaptureDisabledMessage(policy))
    return false, NewSilentError(errUnsupportedCheckpointPolicy)
}
func sessionStartPolicyWarning(policy checkpointpolicy.Policy) string {
    message := "Entire CLI is enabled, but this repository's checkpoint policy requires a newer Entire CLI. No Entire checkpoints will be created for this session until you upgrade."
    details := strings.TrimSpace(checkpointpolicy.UnsupportedPolicyMessage(policy, versioncheck.UpdateCommandForCurrentBinary(versioninfo.Version)))
    if details == "" {
        return message
    }
    return message + "\n\n" + details
}
func agentCheckpointCaptureDisabledMessage(policy checkpointpolicy.Policy) string {
    var b strings.Builder
    b.WriteString("[entire] Checkpoint capture is disabled for this repository.\n")
    b.WriteString("[entire] No Entire checkpoints will be created until the CLI is upgraded.\n")
    if details := strings.TrimSpace(checkpointpolicy.UnsupportedPolicyMessage(policy, versioncheck.UpdateCommandForCurrentBinary(versioninfo.Version))); details != "" {
        b.WriteString(details)
        b.WriteByte('\n')
    }
    return b.String()
}
func writeUnsupportedPolicySessionStartWarning(errW io.Writer, ag agent.Agent, message string) error {
    if writer, ok := agent.AsHookResponseWriter(ag); ok {
        if err := writer.WriteHookResponse(message); err != nil {
            return fmt.Errorf("failed to write hook response: %w", err)
        }
        return nil
    }
    fmt.Fprintln(errW, message)
    return nil
}

// Test functions and additional implementations omitted for brevity.