mirror of
https://github.com/imjasonh/cnotes
synced 2026-07-19 07:08:17 +00:00
Add configurable emojis for user and assistant messages
This commit is contained in:
parent
c2927548f9
commit
cc4541b256
6 changed files with 52 additions and 12 deletions
9
.claude/notes.json
Normal file
9
.claude/notes.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"max_excerpt_length": 5000,
|
||||||
|
"max_prompts": 2,
|
||||||
|
"notes_ref": "claude-conversations",
|
||||||
|
"exclude_patterns": ["password", "token", "key", "secret"],
|
||||||
|
"user_emoji": "🧑",
|
||||||
|
"assistant_emoji": "🤖"
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
⭐️ `cnotes` is pronounced like 💵 c-notes or 🏊 [_cenotes_](https://en.wikipedia.org/wiki/Cenote)
|
⭐️ `cnotes` is pronounced like 💵 c-notes or 🏊 [_cenotes_](https://en.wikipedia.org/wiki/Cenote)
|
||||||
|
|
||||||
|
The conversation display now includes configurable emojis for user and assistant messages.
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
||||||
14
cmd/notes.go
14
cmd/notes.go
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/imjasonh/cnotes/internal/config"
|
||||||
"github.com/imjasonh/cnotes/internal/notes"
|
"github.com/imjasonh/cnotes/internal/notes"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
@ -101,7 +102,8 @@ If no commit is specified, shows notes for HEAD.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pretty-print in Markdown format
|
// Pretty-print in Markdown format
|
||||||
printConversationMarkdown(*note, commit)
|
cfg := config.LoadNotesConfig(".")
|
||||||
|
printConversationMarkdown(*note, commit, cfg)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +140,7 @@ var listCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
// printConversationMarkdown formats a conversation note as readable Markdown
|
// printConversationMarkdown formats a conversation note as readable Markdown
|
||||||
func printConversationMarkdown(note notes.ConversationNote, commit string) {
|
func printConversationMarkdown(note notes.ConversationNote, commit string, cfg *config.NotesConfig) {
|
||||||
fmt.Printf("# Claude Conversation Notes\n\n")
|
fmt.Printf("# Claude Conversation Notes\n\n")
|
||||||
|
|
||||||
// Get commit info
|
// Get commit info
|
||||||
|
|
@ -155,7 +157,7 @@ func printConversationMarkdown(note notes.ConversationNote, commit string) {
|
||||||
if note.ConversationExcerpt != "" {
|
if note.ConversationExcerpt != "" {
|
||||||
fmt.Printf("## Conversation Transcript\n\n")
|
fmt.Printf("## Conversation Transcript\n\n")
|
||||||
// Clean up and format the conversation excerpt for better readability
|
// Clean up and format the conversation excerpt for better readability
|
||||||
formatted := formatConversationExcerpt(note.ConversationExcerpt)
|
formatted := formatConversationExcerpt(note.ConversationExcerpt, cfg)
|
||||||
fmt.Printf("%s\n\n", formatted)
|
fmt.Printf("%s\n\n", formatted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,7 +166,7 @@ func printConversationMarkdown(note notes.ConversationNote, commit string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatConversationExcerpt cleans up the conversation excerpt for better readability
|
// formatConversationExcerpt cleans up the conversation excerpt for better readability
|
||||||
func formatConversationExcerpt(excerpt string) string {
|
func formatConversationExcerpt(excerpt string, cfg *config.NotesConfig) string {
|
||||||
// Replace escaped newlines with actual newlines
|
// Replace escaped newlines with actual newlines
|
||||||
formatted := strings.ReplaceAll(excerpt, "\\n", "\n")
|
formatted := strings.ReplaceAll(excerpt, "\\n", "\n")
|
||||||
|
|
||||||
|
|
@ -195,11 +197,11 @@ func formatConversationExcerpt(excerpt string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(line, "User:"):
|
case strings.HasPrefix(line, "User:") || (cfg != nil && cfg.UserEmoji != "" && strings.HasPrefix(line, cfg.UserEmoji)):
|
||||||
// Bold user prompts
|
// Bold user prompts
|
||||||
formattedLines = append(formattedLines, "**"+line+"**")
|
formattedLines = append(formattedLines, "**"+line+"**")
|
||||||
|
|
||||||
case strings.HasPrefix(line, "Claude:"):
|
case strings.HasPrefix(line, "Claude:") || (cfg != nil && cfg.AssistantEmoji != "" && strings.HasPrefix(line, cfg.AssistantEmoji)):
|
||||||
// Keep Claude responses as-is
|
// Keep Claude responses as-is
|
||||||
formattedLines = append(formattedLines, line)
|
formattedLines = append(formattedLines, line)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ func processGitCommit(ctx context.Context, input HookInput, bashInput BashToolIn
|
||||||
return fmt.Errorf("could not extract commit hash")
|
return fmt.Errorf("could not extract commit hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create notes manager
|
// Create notes manager and load config
|
||||||
notesManager := notes.NewNotesManager(input.CWD)
|
notesManager := notes.NewNotesManager(input.CWD)
|
||||||
cfg := config.LoadNotesConfig(input.CWD)
|
cfg := config.LoadNotesConfig(input.CWD)
|
||||||
notesManager.SetNotesRef(cfg.NotesRef)
|
notesManager.SetNotesRef(cfg.NotesRef)
|
||||||
|
|
@ -149,7 +149,7 @@ func processGitCommit(ctx context.Context, input HookInput, bashInput BashToolIn
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
// Extract conversation context since the last commit
|
// Extract conversation context since the last commit
|
||||||
contextExtractor := conv.NewContextExtractor()
|
contextExtractor := conv.NewContextExtractor(cfg)
|
||||||
conversationContext, err := contextExtractor.ExtractContextSince(input.TranscriptPath, input.SessionID, previousCommitTime)
|
conversationContext, err := contextExtractor.ExtractContextSince(input.TranscriptPath, input.SessionID, previousCommitTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to extract conversation context: %w", err)
|
return fmt.Errorf("failed to extract conversation context: %w", err)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ type NotesConfig struct {
|
||||||
IncludeToolOutput bool `json:"include_tool_output"` // Whether to include tool output in notes
|
IncludeToolOutput bool `json:"include_tool_output"` // Whether to include tool output in notes
|
||||||
NotesRef string `json:"notes_ref"` // Git notes reference name
|
NotesRef string `json:"notes_ref"` // Git notes reference name
|
||||||
ExcludePatterns []string `json:"exclude_patterns"` // Patterns to exclude from notes
|
ExcludePatterns []string `json:"exclude_patterns"` // Patterns to exclude from notes
|
||||||
|
UserEmoji string `json:"user_emoji"` // Emoji to use for user messages
|
||||||
|
AssistantEmoji string `json:"assistant_emoji"` // Emoji to use for assistant messages
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultNotesConfig returns the default configuration
|
// DefaultNotesConfig returns the default configuration
|
||||||
|
|
@ -32,6 +34,8 @@ func DefaultNotesConfig() *NotesConfig {
|
||||||
"api_key",
|
"api_key",
|
||||||
"auth",
|
"auth",
|
||||||
},
|
},
|
||||||
|
UserEmoji: "👤",
|
||||||
|
AssistantEmoji: "🤖",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,6 +66,12 @@ func LoadNotesConfig(projectDir string) *NotesConfig {
|
||||||
if config.MaxPrompts <= 0 {
|
if config.MaxPrompts <= 0 {
|
||||||
config.MaxPrompts = 2
|
config.MaxPrompts = 2
|
||||||
}
|
}
|
||||||
|
if config.UserEmoji == "" {
|
||||||
|
config.UserEmoji = "👤"
|
||||||
|
}
|
||||||
|
if config.AssistantEmoji == "" {
|
||||||
|
config.AssistantEmoji = "🤖"
|
||||||
|
}
|
||||||
|
|
||||||
return &config
|
return &config
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/imjasonh/cnotes/internal/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConversationContext represents relevant conversation context for a commit
|
// ConversationContext represents relevant conversation context for a commit
|
||||||
|
|
@ -40,10 +42,11 @@ type ToolInteraction struct {
|
||||||
type ContextExtractor struct {
|
type ContextExtractor struct {
|
||||||
maxExcerptLength int
|
maxExcerptLength int
|
||||||
sensitivePatterns []*regexp.Regexp
|
sensitivePatterns []*regexp.Regexp
|
||||||
|
config *config.NotesConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContextExtractor creates a new context extractor with default settings
|
// NewContextExtractor creates a new context extractor with default settings
|
||||||
func NewContextExtractor() *ContextExtractor {
|
func NewContextExtractor(cfg *config.NotesConfig) *ContextExtractor {
|
||||||
// Patterns to filter out sensitive information
|
// Patterns to filter out sensitive information
|
||||||
sensitivePatterns := []*regexp.Regexp{
|
sensitivePatterns := []*regexp.Regexp{
|
||||||
regexp.MustCompile(`(?i)(password|token|key|secret)[:\s]*[^\s\n]+`),
|
regexp.MustCompile(`(?i)(password|token|key|secret)[:\s]*[^\s\n]+`),
|
||||||
|
|
@ -52,9 +55,15 @@ func NewContextExtractor() *ContextExtractor {
|
||||||
regexp.MustCompile(`[A-Za-z0-9+/]{40,}={0,2}`), // Base64 encoded secrets
|
regexp.MustCompile(`[A-Za-z0-9+/]{40,}={0,2}`), // Base64 encoded secrets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxLength := 5000
|
||||||
|
if cfg != nil && cfg.MaxExcerptLength > 0 {
|
||||||
|
maxLength = cfg.MaxExcerptLength
|
||||||
|
}
|
||||||
|
|
||||||
return &ContextExtractor{
|
return &ContextExtractor{
|
||||||
maxExcerptLength: 5000, // 5KB limit for context
|
maxExcerptLength: maxLength,
|
||||||
sensitivePatterns: sensitivePatterns,
|
sensitivePatterns: sensitivePatterns,
|
||||||
|
config: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,7 +370,11 @@ func (ce *ContextExtractor) CreateExcerpt(context *ConversationContext) string {
|
||||||
if len(content) > 200 {
|
if len(content) > 200 {
|
||||||
content = content[:197] + "..."
|
content = content[:197] + "..."
|
||||||
}
|
}
|
||||||
line = fmt.Sprintf("User: %s", content)
|
emoji := "👤"
|
||||||
|
if ce.config != nil && ce.config.UserEmoji != "" {
|
||||||
|
emoji = ce.config.UserEmoji
|
||||||
|
}
|
||||||
|
line = fmt.Sprintf("%s User: %s", emoji, content)
|
||||||
|
|
||||||
case "assistant":
|
case "assistant":
|
||||||
// Format assistant responses
|
// Format assistant responses
|
||||||
|
|
@ -369,7 +382,11 @@ func (ce *ContextExtractor) CreateExcerpt(context *ConversationContext) string {
|
||||||
if len(content) > 200 {
|
if len(content) > 200 {
|
||||||
content = content[:197] + "..."
|
content = content[:197] + "..."
|
||||||
}
|
}
|
||||||
line = fmt.Sprintf("Claude: %s", content)
|
emoji := "🤖"
|
||||||
|
if ce.config != nil && ce.config.AssistantEmoji != "" {
|
||||||
|
emoji = ce.config.AssistantEmoji
|
||||||
|
}
|
||||||
|
line = fmt.Sprintf("%s Claude: %s", emoji, content)
|
||||||
|
|
||||||
case "tool":
|
case "tool":
|
||||||
// Format tool uses
|
// Format tool uses
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue