mirror of
https://github.com/imjasonh/cnotes
synced 2026-07-07 00:33:04 +00:00
Fix duplicate content in git notes by filtering since last commit
- Add ExtractContextSince method to filter conversation by timestamp - Get previous commit time and only include activity since then - Change labels to 'User prompts since last commit' and 'Tool interactions since last commit' - Each commit now only captures its own unique conversation context - Prevents accumulation of duplicate content across commits in same session
This commit is contained in:
parent
6b338a76be
commit
a6e6602bf8
2 changed files with 55 additions and 16 deletions
29
cmd/root.go
29
cmd/root.go
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -141,9 +142,12 @@ func processGitCommit(ctx context.Context, input HookInput, bashInput BashToolIn
|
|||
return nil
|
||||
}
|
||||
|
||||
// Extract conversation context
|
||||
// Get the timestamp of the previous commit in this session
|
||||
previousCommitTime := getLastCommitTimeForSession(ctx, notesManager, input.SessionID)
|
||||
|
||||
// Extract conversation context since the last commit
|
||||
contextExtractor := conv.NewContextExtractor()
|
||||
conversationContext, err := contextExtractor.ExtractRecentContext(input.TranscriptPath, input.SessionID)
|
||||
conversationContext, err := contextExtractor.ExtractContextSince(input.TranscriptPath, input.SessionID, previousCommitTime)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to extract conversation context: %w", err)
|
||||
}
|
||||
|
|
@ -238,6 +242,27 @@ func contains(slice []string, item string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// getLastCommitTimeForSession finds the most recent commit time for this session
|
||||
func getLastCommitTimeForSession(ctx context.Context, notesManager *notes.NotesManager, sessionID string) time.Time {
|
||||
// For now, let's use a simpler approach - get the time of the previous commit
|
||||
// This works well when commits are made sequentially in a session
|
||||
cmd := exec.Command("git", "log", "-1", "--format=%cI", "HEAD~1")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
// No previous commit or error, return zero time
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
timeStr := strings.TrimSpace(string(output))
|
||||
commitTime, err := time.Parse(time.RFC3339, timeStr)
|
||||
if err != nil {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Add a small buffer to ensure we don't miss events that happened right at commit time
|
||||
return commitTime.Add(-5 * time.Second)
|
||||
}
|
||||
|
||||
// readStdinWithTimeout reads from stdin with a timeout
|
||||
func readStdinWithTimeout(timeout time.Duration) ([]byte, error) {
|
||||
type result struct {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ConversationContext represents relevant conversation context for a commit
|
||||
|
|
@ -48,6 +49,11 @@ func NewContextExtractor() *ContextExtractor {
|
|||
|
||||
// ExtractRecentContext extracts recent conversation context from a transcript file
|
||||
func (ce *ContextExtractor) ExtractRecentContext(transcriptPath string, sessionID string) (*ConversationContext, error) {
|
||||
return ce.ExtractContextSince(transcriptPath, sessionID, time.Time{})
|
||||
}
|
||||
|
||||
// ExtractContextSince extracts conversation context since a given timestamp
|
||||
func (ce *ContextExtractor) ExtractContextSince(transcriptPath string, sessionID string, since time.Time) (*ConversationContext, error) {
|
||||
if transcriptPath == "" {
|
||||
return &ConversationContext{}, nil
|
||||
}
|
||||
|
|
@ -65,7 +71,7 @@ func (ce *ContextExtractor) ExtractRecentContext(transcriptPath string, sessionI
|
|||
}
|
||||
|
||||
// Parse the transcript content
|
||||
context := ce.parseTranscriptContent(string(content), sessionID)
|
||||
context := ce.parseTranscriptContent(string(content), sessionID, since)
|
||||
|
||||
// Apply privacy filters
|
||||
context = ce.filterSensitiveContent(context)
|
||||
|
|
@ -74,7 +80,7 @@ func (ce *ContextExtractor) ExtractRecentContext(transcriptPath string, sessionI
|
|||
}
|
||||
|
||||
// parseTranscriptContent parses transcript content and extracts conversation elements
|
||||
func (ce *ContextExtractor) parseTranscriptContent(content, sessionID string) *ConversationContext {
|
||||
func (ce *ContextExtractor) parseTranscriptContent(content, sessionID string, since time.Time) *ConversationContext {
|
||||
context := &ConversationContext{
|
||||
UserPrompts: []string{},
|
||||
ClaudeResponses: []string{},
|
||||
|
|
@ -98,11 +104,20 @@ func (ce *ContextExtractor) parseTranscriptContent(content, sessionID string) *C
|
|||
|
||||
// Only process entries for the current session
|
||||
entrySessionID, _ := entry["sessionId"].(string)
|
||||
// Debug: log session matching
|
||||
if entrySessionID != "" && entrySessionID != sessionID {
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter by timestamp if provided
|
||||
if !since.IsZero() {
|
||||
if timestampStr, ok := entry["timestamp"].(string); ok {
|
||||
entryTime, err := time.Parse(time.RFC3339, timestampStr)
|
||||
if err == nil && entryTime.Before(since) {
|
||||
continue // Skip entries before the cutoff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract based on type
|
||||
entryType, _ := entry["type"].(string)
|
||||
|
||||
|
|
@ -211,15 +226,10 @@ func (ce *ContextExtractor) sanitizeText(text string) string {
|
|||
func (ce *ContextExtractor) CreateExcerpt(context *ConversationContext) string {
|
||||
var parts []string
|
||||
|
||||
// Include recent user prompts (last 3 to capture more context)
|
||||
// Include all user prompts (they're already filtered by time)
|
||||
if len(context.UserPrompts) > 0 {
|
||||
start := len(context.UserPrompts) - 3
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
parts = append(parts, "Recent user prompts:")
|
||||
for i := start; i < len(context.UserPrompts); i++ {
|
||||
prompt := context.UserPrompts[i]
|
||||
parts = append(parts, "User prompts since last commit:")
|
||||
for _, prompt := range context.UserPrompts {
|
||||
if len(prompt) > 300 {
|
||||
prompt = prompt[:297] + "..."
|
||||
}
|
||||
|
|
@ -227,11 +237,15 @@ func (ce *ContextExtractor) CreateExcerpt(context *ConversationContext) string {
|
|||
}
|
||||
}
|
||||
|
||||
// Include recent tool interactions
|
||||
// Include all tool interactions (they're already filtered by time)
|
||||
if len(context.ToolInteractions) > 0 {
|
||||
parts = append(parts, "\nTool interactions:")
|
||||
parts = append(parts, "\nTool interactions since last commit:")
|
||||
for _, interaction := range context.ToolInteractions {
|
||||
parts = append(parts, fmt.Sprintf("- %s: %s", interaction.Tool, interaction.Input))
|
||||
input := interaction.Input
|
||||
if len(input) > 100 {
|
||||
input = input[:97] + "..."
|
||||
}
|
||||
parts = append(parts, fmt.Sprintf("- %s: %s", interaction.Tool, input))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue