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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,13 @@ does not block the tool result; the hook emits a concise refresh hint with
`coding-ethos-code-intel rebuild-index`. Repositories can disable or cap this
context with `proxy.code_intel_enrichment` in `repo_config.yaml`.

PreToolUse hooks also perform narrow semantic policy injection. Mutating Git
intents such as `policy-git commit` inject the `safe-git-workflow` skill pointer
and policy-git reminder only for that turn. File-target tool calls that name
Python files inject compact Python static-analysis guidance and the relevant
skill pointer instead of front-loading every Python policy into startup context.
Read-only Git inspection commands stay quiet.

Provider-native file read tools are the supported path for reading source.
Claude-style Bash file-tool emulation such as `cat <path>`,
`sed -n '1,20p' <path>`, `awk ... <path>`, `tee <path>`, and `echo`/`printf`
Expand Down
19 changes: 19 additions & 0 deletions docs/AGENT_PROXY.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,25 @@ pagination or cached-read transforms at the provider file-read boundary, but it
must record `file_read` and `cache_hit` events in the provider-neutral proxy
ledger rather than inferring file reads from shell output.

## Semantic Policy Injection

The first just-in-time policy injection slice runs in the local hook path at
`PreToolUse`. It does not load every skill or policy into startup context.
Instead, it emits compact, exact pointers only when the incoming tool call shows
matching intent:

- mutating Git commands, including managed `policy-git commit`, receive the
`safe-git-workflow` skill pointer and policy-git reminder;
- file-target tool calls that name Python files receive compact Python
static-analysis guidance and the relevant Python skill pointer;
- read-only Git inspection commands stay quiet.

This is intentionally rule-based, not vector retrieval. The hook output records
the trigger, reason, skill id, policy scope, and next action so the injected
context is auditable and small. Provider adapters may still suppress pre-tool
advice where a provider does not support allowed `PreToolUse` context; those
capability limits are documented in `docs/PROVIDER_CAPABILITY_MATRIX.md`.

## Startup Repo Map

On `SessionStart`, the hook runtime refreshes the repo-local Tree-sitter index
Expand Down
48 changes: 3 additions & 45 deletions go/internal/hookrunnercli/toolchain_groups.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,25 +212,15 @@ func runGoVet(_ Config, paths []string) int {
return 0
}

worktree, ok := configuredGoWorktreeName()
if !ok {
return 1
}

return runManagedPolicyTool("go-vet", []string{worktree})
return runManagedPolicyTool("go-vet", nil)
}

func runGoTests(_ Config, paths []string) int {
if len(goFiles(existingFiles(paths))) == 0 {
return 0
}

worktree, ok := configuredGoWorktreeName()
if !ok {
return 1
}

return runManagedPolicyTool(goTestToolName, []string{worktree})
return runManagedPolicyTool(goTestToolName, nil)
}

func runGoCoverageThreshold(cfg Config, paths []string) int {
Expand Down Expand Up @@ -760,18 +750,11 @@ func runGolangciLint(cfg Config, paths []string) int {
return 0
}

worktree, ok := configuredGoWorktreeName()
if !ok {
return 1
}

args := []string{}
args := []string{"run"}
if cfg.HookStage == hookStagePreCommit {
args = append(args, "--new-from-rev=HEAD")
}

args = append(args, worktree)

return runManagedPolicyTool("golangci-lint", args)
}

Expand Down Expand Up @@ -857,31 +840,6 @@ func openPolicyBundleFile(path string) (*os.File, error) {
return file, nil
}

func configuredGoWorktreeName() (string, bool) {
_, _, rootConfig, err := loadBundleConsumerAndConfig()
if err != nil {
writef(os.Stderr, "FATAL: %v\n", err)

return "", false
}

worktree := configuredGoWorktreeValue(rootConfig)
path := repoPath(worktree)

info, err := os.Stat(path)
if err != nil || !info.IsDir() {
fmt.Fprintf(
os.Stderr,
"FATAL: go.worktree is set to %q, but that directory does not exist\n",
worktree,
)

return "", false
}

return worktree, true
}

func configuredGoWorktree() (string, bool) {
_, _, rootConfig, err := loadBundleConsumerAndConfig()
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions go/internal/hooks/git_wrapper_enforcement.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func textRequestsGitWork(text string) bool {
normalized := strings.ToLower(text)

if strings.Contains(normalized, "commit") ||
strings.Contains(normalized, "push") ||
strings.Contains(normalized, operationPush) ||
strings.Contains(normalized, "amend") ||
strings.Contains(normalized, "merge") ||
strings.Contains(normalized, "rebase") ||
Expand Down Expand Up @@ -600,7 +600,7 @@ func managedGitSegment(segment []string) bool {
command := segment[0]

commandBase := filepath.Base(command)
if commandBase == "coding-ethos-run" {
if commandBase == wrapperRunnerName {
return isTrustedRunnerCommand(command) &&
len(segment) > 1 &&
segment[1] == "policy-git"
Expand Down
25 changes: 10 additions & 15 deletions go/internal/hooks/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const (
modeRecord = "record"
statusAllowed = "allowed"
statusBlocked = "blocked"
operationPush = "push"
slowHookBudgetMS = int64(2500)
hookDividerWidth = 50
)
Expand Down Expand Up @@ -290,11 +291,13 @@ func hookSpecificOutput(
}, nil
}

if output := advisoryHookOutput(event); output != nil {
return output, nil
}

if output := postEditOutput(bundle, event); output != nil {
if output := mergeHookSpecificOutputs(
semanticPolicyInjectionOutput(bundle, event),
sessionMemoryImportOutput(event),
continuationOutput(event),
lifecycleOutput(event),
postEditOutput(bundle, event),
); output != nil {
return output, nil
}

Expand All @@ -311,14 +314,6 @@ func hookSpecificOutput(
return postToolBashOutput(bundle, event)
}

func advisoryHookOutput(event Event) *HookSpecificOutput {
return mergeHookSpecificOutputs(
lifecycleOutput(event),
continuationOutput(event),
sessionMemoryImportOutput(event),
)
}

func mergeHookSpecificOutputs(outputs ...*HookSpecificOutput) *HookSpecificOutput {
var merged *HookSpecificOutput

Expand Down Expand Up @@ -566,7 +561,7 @@ func hookOperation(command string) string {
operation := "commit"

if strings.Contains(strings.ToLower(command), "git push") {
operation = "push"
operation = operationPush
}

if strings.Contains(strings.ToLower(command), "pre-commit") {
Expand All @@ -591,7 +586,7 @@ func buildHookOutputContextHuman(
reminders []renderedEthosReminder,
) string {
hookType := "PRE-COMMIT"
if operation == "push" {
if operation == operationPush {
hookType = "PRE-PUSH"
}

Expand Down
Loading
Loading