feat: add template system for role contexts and messages

Implements gt-u1j.20: Prompt templates using go:embed.

- Add internal/templates package with embedded .md.tmpl files
- Role templates: mayor, witness, refinery, polecat, crew
- Message templates: spawn, nudge, escalation, handoff
- Update gt prime to use templates with fallback to hardcoded output
- Add crew role detection for <rig>/crew/<name>/ paths
- Include Gas Town architecture overview in all role contexts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-17 16:59:40 -08:00
parent f7b0c11157
commit f04cc6fe15
12 changed files with 974 additions and 0 deletions

View File

@@ -8,6 +8,7 @@ import (
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/templates"
"github.com/steveyegge/gastown/internal/workspace"
)
@@ -19,6 +20,7 @@ const (
RoleWitness Role = "witness"
RoleRefinery Role = "refinery"
RolePolecat Role = "polecat"
RoleCrew Role = "crew"
RoleUnknown Role = "unknown"
)
@@ -126,11 +128,63 @@ func detectRole(cwd, townRoot string) RoleContext {
return ctx
}
// Check for crew: <rig>/crew/<name>/
if len(parts) >= 3 && parts[1] == "crew" {
ctx.Role = RoleCrew
ctx.Polecat = parts[2] // Use Polecat field for crew member name
return ctx
}
// Default: could be rig root - treat as unknown
return ctx
}
func outputPrimeContext(ctx RoleContext) error {
// Try to use templates first
tmpl, err := templates.New()
if err != nil {
// Fall back to hardcoded output if templates fail
return outputPrimeContextFallback(ctx)
}
// Map role to template name
var roleName string
switch ctx.Role {
case RoleMayor:
roleName = "mayor"
case RoleWitness:
roleName = "witness"
case RoleRefinery:
roleName = "refinery"
case RolePolecat:
roleName = "polecat"
case RoleCrew:
roleName = "crew"
default:
// Unknown role - use fallback
return outputPrimeContextFallback(ctx)
}
// Build template data
data := templates.RoleData{
Role: roleName,
RigName: ctx.Rig,
TownRoot: ctx.TownRoot,
WorkDir: ctx.WorkDir,
Polecat: ctx.Polecat,
}
// Render and output
output, err := tmpl.RenderRole(roleName, data)
if err != nil {
return fmt.Errorf("rendering template: %w", err)
}
fmt.Print(output)
return nil
}
func outputPrimeContextFallback(ctx RoleContext) error {
switch ctx.Role {
case RoleMayor:
outputMayorContext(ctx)
@@ -140,6 +194,8 @@ func outputPrimeContext(ctx RoleContext) error {
outputRefineryContext(ctx)
case RolePolecat:
outputPolecatContext(ctx)
case RoleCrew:
outputCrewContext(ctx)
default:
outputUnknownContext(ctx)
}
@@ -217,6 +273,25 @@ func outputPolecatContext(ctx RoleContext) {
style.Dim.Render(ctx.Polecat), style.Dim.Render(ctx.Rig))
}
func outputCrewContext(ctx RoleContext) {
fmt.Printf("%s\n\n", style.Bold.Render("# Crew Worker Context"))
fmt.Printf("You are crew worker **%s** in rig: %s\n\n",
style.Bold.Render(ctx.Polecat), style.Bold.Render(ctx.Rig))
fmt.Println("## About Crew Workers")
fmt.Println("- Persistent workspace (not auto-garbage-collected)")
fmt.Println("- User-managed (not Witness-monitored)")
fmt.Println("- Long-lived identity across sessions")
fmt.Println()
fmt.Println("## Key Commands")
fmt.Println("- `gt mail inbox` - Check your inbox")
fmt.Println("- `bd ready` - Available issues")
fmt.Println("- `bd show <issue>` - View issue details")
fmt.Println("- `bd close <issue>` - Mark issue complete")
fmt.Println()
fmt.Printf("Crew: %s | Rig: %s\n",
style.Dim.Render(ctx.Polecat), style.Dim.Render(ctx.Rig))
}
func outputUnknownContext(ctx RoleContext) {
fmt.Printf("%s\n\n", style.Bold.Render("# Gas Town Context"))
fmt.Println("Could not determine specific role from current directory.")