Ryanhub - file viewer
filename: assistant/agent/prompt.go
branch: main
back to repo
package agent

import (
	"fmt"
	"sort"
	"strings"
	"time"

	"assistant/config"
	"assistant/tools"
)

// systemContent returns the final system prompt including enabled tool names.
func systemContent(cfg *config.Config, reg tools.Registry) string {
	names := make([]string, 0, len(reg))
	for n := range reg {
		names = append(names, n)
	}
	sort.Strings(names)
	var sb strings.Builder
	sb.WriteString(strings.TrimSpace(cfg.Agent.SystemPrompt))
	now := time.Now()
	sb.WriteString(fmt.Sprintf("\n\nCurrent date/time context:\n- Local now: %s\n- Local date: %s\n- Weekday: %s",
		now.Format(time.RFC3339),
		now.Format("2006-01-02"),
		now.Format("Monday"),
	))
	sb.WriteString("\n\nBehavior rules:")
	sb.WriteString("\n- Be concise and action-oriented.")
	sb.WriteString("\n- If a user asks for data you can fetch with tools, call the relevant tool(s) immediately.")
	sb.WriteString("\n- If a tool has default parameters, use them unless the user specifies otherwise.")
	sb.WriteString("\n- Do not ask for confirmation to run a tool unless the user asks you to wait.")
	sb.WriteString("\n- When memory tools are available, prefer reading memory before answering preference/context questions.")
	sb.WriteString("\n- Use session history for short-lived conversation context; use long-term memory for durable preferences/facts.")
	sb.WriteString("\n- Never invent article details from headlines alone; fetch article content first when a deeper explanation is requested.")
	if _, ok := reg["scratchpad_read"]; ok {
		sb.WriteString("\n- Scratchpad: shared Markdown file. scratchpad_read loads it. scratchpad_write only appends: one newline then your content at the end (it never replaces the file). The user may edit the full file in the UI or an editor.")
	}
	if _, ok := reg["calendar_propose_change"]; ok {
		sb.WriteString("\n- Calendar: use calendar_list_range to read scheduled items; use calendar_propose_change to queue creates/updates/deletes. " +
			"Queued changes are NOT applied until the user confirms them in the web UI calendar panel.")
		sb.WriteString("\n- Calendar date intent: if the user says 'today', set due_at to today's local date (YYYY-MM-DD) in calendar_propose_change. " +
			"Do not leave due_at empty for 'today' requests.")
		sb.WriteString("\n- Calendar date fidelity: never invent, assume, or infer a date for an item when due_at is missing/empty.")
		sb.WriteString("\n- Calendar summaries: items with no due_at must be labeled as undated (or no date), and must NOT be grouped under any specific day/date heading.")
	}
	if len(names) > 0 {
		sb.WriteString("\n\nEnabled tools:")
		for _, name := range names {
			t := reg[name]
			sb.WriteString(fmt.Sprintf("\n- %s: %s", t.Name, strings.TrimSpace(t.Description)))
		}
	}
	return sb.String()
}