diff --git a/internal/agent/tools/view.go b/internal/agent/tools/view.go index 0a754dcb4fd05cc975f84e85532eeab1525c7002..5d589f74012f91f0401573c2017722a9d48b7677 100644 --- a/internal/agent/tools/view.go +++ b/internal/agent/tools/view.go @@ -17,6 +17,7 @@ import ( "github.com/charmbracelet/crush/internal/filetracker" "github.com/charmbracelet/crush/internal/lsp" "github.com/charmbracelet/crush/internal/permission" + "github.com/charmbracelet/crush/internal/skills" ) //go:embed view.md @@ -34,9 +35,19 @@ type ViewPermissionsParams struct { Limit int `json:"limit"` } +type ViewResourceType string + +const ( + ViewResourceUnset ViewResourceType = "" + ViewResourceSkill ViewResourceType = "skill" +) + type ViewResponseMetadata struct { - FilePath string `json:"file_path"` - Content string `json:"content"` + FilePath string `json:"file_path"` + Content string `json:"content"` + ResourceType ViewResourceType `json:"resource_type,omitempty"` + ResourceName string `json:"resource_name,omitempty"` + ResourceDescription string `json:"resource_description,omitempty"` } const ( @@ -196,12 +207,22 @@ func NewViewTool( output += "\n\n" output += getDiagnostics(filePath, lspManager) filetracker.RecordRead(ctx, sessionID, filePath) + + meta := ViewResponseMetadata{ + FilePath: filePath, + Content: content, + } + if isSkillFile { + if skill, err := skills.Parse(filePath); err == nil { + meta.ResourceType = ViewResourceSkill + meta.ResourceName = skill.Name + meta.ResourceDescription = skill.Description + } + } + return fantasy.WithResponseMetadata( fantasy.NewTextResponse(output), - ViewResponseMetadata{ - FilePath: filePath, - Content: content, - }, + meta, ), nil }) } diff --git a/internal/ui/chat/file.go b/internal/ui/chat/file.go index d558f79d597871bf6074d33c76b44549ee6725d5..3b1fef8530506be70e512a3cb801ed34a81e0c62 100644 --- a/internal/ui/chat/file.go +++ b/internal/ui/chat/file.go @@ -82,6 +82,12 @@ func (v *ViewToolRenderContext) RenderTool(sty *styles.Styles, width int, opts * content = meta.Content } + // Handle skill content. + if meta.ResourceType == tools.ViewResourceSkill { + body := toolOutputSkillContent(sty, meta.ResourceName, meta.ResourceDescription) + return joinToolParts(header, body) + } + if content == "" { return header } diff --git a/internal/ui/chat/tools.go b/internal/ui/chat/tools.go index c0059010cc4efaeb0c883fbf2f050c785f19da2b..17c15911e377318d4c4a1515fca57332f31996e1 100644 --- a/internal/ui/chat/tools.go +++ b/internal/ui/chat/tools.go @@ -625,10 +625,21 @@ func toolOutputImageContent(sty *styles.Styles, data, mediaType string) string { return sty.Tool.Body.Render(fmt.Sprintf( "%s %s %s %s", - sty.Tool.ResourceLoadedText.String(), - sty.Tool.ResourceLoadedIndicator.String(), - sty.Base.Render(mediaType), - sty.Subtle.Render(sizeStr), + sty.Tool.ResourceLoadedText.Render("Loaded Image"), + sty.Tool.ResourceLoadedIndicator.Render(styles.ArrowRightIcon), + sty.Tool.MediaType.Render(mediaType), + sty.Tool.ResourceSize.Render(sizeStr), + )) +} + +// toolOutputSkillContent renders a skill loaded indicator. +func toolOutputSkillContent(sty *styles.Styles, name, description string) string { + return sty.Tool.Body.Render(fmt.Sprintf( + "%s %s %s %s", + sty.Tool.ResourceLoadedText.Render("Loaded Skill"), + sty.Tool.ResourceLoadedIndicator.Render(styles.ArrowRightIcon), + sty.Tool.ResourceName.Render(name), + sty.Tool.ResourceSize.Render(description), )) } diff --git a/internal/ui/styles/styles.go b/internal/ui/styles/styles.go index c2c54156c6facc9fb5ccb32281a1631994e62bad..0870f62674f8241cd874d61532f18c3bc58dc22f 100644 --- a/internal/ui/styles/styles.go +++ b/internal/ui/styles/styles.go @@ -325,6 +325,7 @@ type Styles struct { // Images and external resources ResourceLoadedText lipgloss.Style ResourceLoadedIndicator lipgloss.Style + ResourceName lipgloss.Style ResourceSize lipgloss.Style MediaType lipgloss.Style } @@ -1175,8 +1176,9 @@ func DefaultStyles() Styles { s.Tool.MCPArrow = base.Foreground(blue).SetString(ArrowRightIcon) // Loading indicators for images, skills - s.Tool.ResourceLoadedText = base.Foreground(green).SetString("Loaded") - s.Tool.ResourceLoadedIndicator = base.Foreground(greenDark).SetString(ArrowRightIcon) + s.Tool.ResourceLoadedText = base.Foreground(green) + s.Tool.ResourceLoadedIndicator = base.Foreground(greenDark) + s.Tool.ResourceName = base s.Tool.MediaType = base s.Tool.ResourceSize = base.Foreground(fgMuted)