| layout | default |
|---|---|
| title | Chapter 6: Skills, Commands, and Workflow Customization |
| nav_order | 6 |
| parent | Crush Tutorial |
Welcome to Chapter 6: Skills, Commands, and Workflow Customization. In this part of Crush Tutorial: Multi-Model Terminal Coding Agent with Strong Extensibility, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.
This chapter turns Crush into a reusable engineering system rather than a one-off assistant.
- use Agent Skills in local and project scopes
- load custom markdown commands from supported directories
- integrate MCP prompts into command workflows
- standardize reusable patterns across teams
Crush can discover skills from:
~/.config/crush/skills(Unix)%LOCALAPPDATA%\crush\skills(Windows)- additional paths in
options.skills_paths
From internal command loading behavior, custom commands are read from:
- XDG config command dir
~/.crush/commands- project command directory under configured data path
This supports personal command libraries plus project-scoped commands.
- encode standards in
SKILL.mdpackages - add repeatable command snippets for frequent tasks
- keep project-specific commands near repository workflows
- review command/tool permissions with every rollout
You now have the building blocks for durable, reusable Crush workflows.
Next: Chapter 7: Logs, Debugging, and Operations
The AddImageURL function in internal/message/content.go handles a key part of this chapter's functionality:
}
func (m *Message) AddImageURL(url, detail string) {
m.Parts = append(m.Parts, ImageURLContent{URL: url, Detail: detail})
}
func (m *Message) AddBinary(mimeType string, data []byte) {
m.Parts = append(m.Parts, BinaryContent{MIMEType: mimeType, Data: data})
}
func PromptWithTextAttachments(prompt string, attachments []Attachment) string {
var sb strings.Builder
sb.WriteString(prompt)
addedAttachments := false
for _, content := range attachments {
if !content.IsText() {
continue
}
if !addedAttachments {
sb.WriteString("\n<system_info>The files below have been attached by the user, consider them in your response</system_info>\n")
addedAttachments = true
}
if content.FilePath != "" {
fmt.Fprintf(&sb, "<file path='%s'>\n", content.FilePath)
} else {
sb.WriteString("<file>\n")
}
sb.WriteString("\n")
sb.Write(content.Content)
sb.WriteString("\n</file>\n")
}
return sb.String()This function is important because it defines how Crush Tutorial: Multi-Model Terminal Coding Agent with Strong Extensibility implements the patterns covered in this chapter.
The AddBinary function in internal/message/content.go handles a key part of this chapter's functionality:
}
func (m *Message) AddBinary(mimeType string, data []byte) {
m.Parts = append(m.Parts, BinaryContent{MIMEType: mimeType, Data: data})
}
func PromptWithTextAttachments(prompt string, attachments []Attachment) string {
var sb strings.Builder
sb.WriteString(prompt)
addedAttachments := false
for _, content := range attachments {
if !content.IsText() {
continue
}
if !addedAttachments {
sb.WriteString("\n<system_info>The files below have been attached by the user, consider them in your response</system_info>\n")
addedAttachments = true
}
if content.FilePath != "" {
fmt.Fprintf(&sb, "<file path='%s'>\n", content.FilePath)
} else {
sb.WriteString("<file>\n")
}
sb.WriteString("\n")
sb.Write(content.Content)
sb.WriteString("\n</file>\n")
}
return sb.String()
}
func (m *Message) ToAIMessage() []fantasy.Message {
var messages []fantasy.MessageThis function is important because it defines how Crush Tutorial: Multi-Model Terminal Coding Agent with Strong Extensibility implements the patterns covered in this chapter.
The PromptWithTextAttachments function in internal/message/content.go handles a key part of this chapter's functionality:
}
func PromptWithTextAttachments(prompt string, attachments []Attachment) string {
var sb strings.Builder
sb.WriteString(prompt)
addedAttachments := false
for _, content := range attachments {
if !content.IsText() {
continue
}
if !addedAttachments {
sb.WriteString("\n<system_info>The files below have been attached by the user, consider them in your response</system_info>\n")
addedAttachments = true
}
if content.FilePath != "" {
fmt.Fprintf(&sb, "<file path='%s'>\n", content.FilePath)
} else {
sb.WriteString("<file>\n")
}
sb.WriteString("\n")
sb.Write(content.Content)
sb.WriteString("\n</file>\n")
}
return sb.String()
}
func (m *Message) ToAIMessage() []fantasy.Message {
var messages []fantasy.Message
switch m.Role {
case User:
var parts []fantasy.MessagePart
text := strings.TrimSpace(m.Content().Text)This function is important because it defines how Crush Tutorial: Multi-Model Terminal Coding Agent with Strong Extensibility implements the patterns covered in this chapter.
The ToAIMessage function in internal/message/content.go handles a key part of this chapter's functionality:
}
func (m *Message) ToAIMessage() []fantasy.Message {
var messages []fantasy.Message
switch m.Role {
case User:
var parts []fantasy.MessagePart
text := strings.TrimSpace(m.Content().Text)
var textAttachments []Attachment
for _, content := range m.BinaryContent() {
if !strings.HasPrefix(content.MIMEType, "text/") {
continue
}
textAttachments = append(textAttachments, Attachment{
FilePath: content.Path,
MimeType: content.MIMEType,
Content: content.Data,
})
}
text = PromptWithTextAttachments(text, textAttachments)
if text != "" {
parts = append(parts, fantasy.TextPart{Text: text})
}
for _, content := range m.BinaryContent() {
// skip text attachements
if strings.HasPrefix(content.MIMEType, "text/") {
continue
}
parts = append(parts, fantasy.FilePart{
Filename: content.Path,
Data: content.Data,
MediaType: content.MIMEType,This function is important because it defines how Crush Tutorial: Multi-Model Terminal Coding Agent with Strong Extensibility implements the patterns covered in this chapter.
flowchart TD
A[AddImageURL]
B[AddBinary]
C[PromptWithTextAttachments]
D[ToAIMessage]
E[NewManager]
A --> B
B --> C
C --> D
D --> E