vikunja/veans/internal/commands/prime.go

88 lines
2.4 KiB
Go

package commands
import (
_ "embed"
"errors"
"fmt"
"strings"
"text/template"
"github.com/spf13/cobra"
"code.vikunja.io/veans/internal/client"
"code.vikunja.io/veans/internal/config"
)
//go:embed prompt.tmpl
var promptTemplate string
// primeContext is the data passed into the agent prompt template.
type primeContext struct {
Server string
ProjectID int64
ProjectTitle string
ProjectIdentifier string
ViewID int64
Buckets config.Buckets
BotUsername string
TaskIDExample string
}
func newPrimeCmd() *cobra.Command {
return &cobra.Command{
Use: "prime",
Short: "Emit the agent system prompt for this project",
Long: `Renders the embedded prompt template against this repo's .veans.yml and
prints it to stdout. Designed to be wired into Claude Code's SessionStart
and PreCompact hooks (or the OpenCode equivalent) so coding agents always
have an up-to-date Vikunja cheat sheet in context.
If no .veans.yml is found upward from the current directory, prime exits
silently with status 0 — that makes the hook safe to install globally.`,
RunE: func(cmd *cobra.Command, args []string) error {
path, err := config.Find("")
if err != nil {
if errors.Is(err, config.ErrNotFound) {
return nil // silent — globally-installed hook safety
}
return err
}
cfg, err := config.Load(path)
if err != nil {
return err
}
// Fetch the project title for nicer prompt copy. Best-effort —
// if the API call fails (network blip, expired token), we fall
// back to "(unknown)" rather than aborting the prompt render.
projectTitle := "(unknown)"
if rt, err := loadRuntime(); err == nil {
if p, err := rt.client.GetProject(cmd.Context(), cfg.ProjectID); err == nil {
projectTitle = p.Title
}
}
data := primeContext{
Server: cfg.Server,
ProjectID: cfg.ProjectID,
ProjectTitle: projectTitle,
ProjectIdentifier: cfg.ProjectIdentifier,
ViewID: cfg.ViewID,
Buckets: cfg.Buckets,
BotUsername: cfg.Bot.Username,
TaskIDExample: cfg.FormatTaskID(1),
}
tpl, err := template.New("prime").Parse(promptTemplate)
if err != nil {
return fmt.Errorf("parse prompt template: %w", err)
}
return tpl.Execute(cmd.OutOrStdout(), data)
},
}
}
// silence linter noise on unused symbols when wiring hooks.
var _ = client.New
var _ = strings.TrimSpace