mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-08 23:39:40 +00:00
77f5f02510
march-improvements
100 lines
2.8 KiB
Go
100 lines
2.8 KiB
Go
// Package main provides the post-tool-use hook entry point.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/lukaszraczylo/claude-mnemonic/pkg/hooks"
|
|
)
|
|
|
|
// Input is the hook input from Claude Code.
|
|
type Input struct {
|
|
hooks.BaseInput
|
|
ToolName string `json:"tool_name"`
|
|
ToolInput interface{} `json:"tool_input"`
|
|
ToolResponse interface{} `json:"tool_response"`
|
|
ToolUseID string `json:"tool_use_id"`
|
|
}
|
|
|
|
// skipTools lists tools that never produce useful observations.
|
|
// Skip the HTTP call entirely for these to reduce overhead during heavy tool usage.
|
|
var skipTools = map[string]bool{
|
|
// Internal tracking tools (but NOT TodoWrite - it captures planned work)
|
|
"Task": true,
|
|
"TaskOutput": true,
|
|
|
|
// File discovery tools (just listings, no insights)
|
|
"Glob": true,
|
|
"ListDir": true,
|
|
"LS": true,
|
|
"KillShell": true,
|
|
|
|
// Question/interaction tools (no code insights)
|
|
"AskUserQuestion": true,
|
|
|
|
// Plan mode tools (planning, not execution)
|
|
"EnterPlanMode": true,
|
|
"ExitPlanMode": true,
|
|
|
|
// Skill/command execution (meta-operations)
|
|
"Skill": true,
|
|
"SlashCommand": true,
|
|
|
|
// Read is high-volume and rarely produces insights worth the overhead
|
|
// The processor would skip most reads anyway after filtering
|
|
"Read": true,
|
|
|
|
// Search tools are for finding, not modifying
|
|
"Grep": true,
|
|
"WebSearch": true,
|
|
}
|
|
|
|
func main() {
|
|
if !hooks.IsWorkerAvailable() {
|
|
hooks.WriteResponse("PostToolUse", true)
|
|
return
|
|
}
|
|
hooks.RunHook("PostToolUse", handlePostToolUse)
|
|
}
|
|
|
|
func handlePostToolUse(ctx *hooks.HookContext, input *Input) (string, error) {
|
|
// Skip HTTP call entirely for tools that never produce useful observations.
|
|
// This significantly reduces overhead during heavy tool usage.
|
|
if skipTools[input.ToolName] {
|
|
return "", nil
|
|
}
|
|
|
|
fmt.Fprintf(os.Stderr, "[post-tool-use] %s\n", input.ToolName)
|
|
|
|
// Fire-and-forget: send the observation without waiting for the response.
|
|
// The worker just queues it -- we don't need the response data.
|
|
// Use a short-lived context to ensure the request body is at least sent
|
|
// before this process exits.
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
sendCtx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
defer cancel()
|
|
_ = hooks.POSTWithContext(sendCtx, ctx.Port, "/api/sessions/observations", map[string]interface{}{
|
|
"claudeSessionId": ctx.SessionID,
|
|
"project": ctx.Project,
|
|
"tool_name": input.ToolName,
|
|
"tool_input": input.ToolInput,
|
|
"tool_response": input.ToolResponse,
|
|
"cwd": ctx.CWD,
|
|
})
|
|
}()
|
|
|
|
// Wait briefly for the TCP connection to be established and request sent,
|
|
// but don't block the hook for the full response.
|
|
select {
|
|
case <-done:
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
|
|
return "", nil
|
|
}
|