Skip to content

Commit 4fc8262

Browse files
committed
Color-code four autonomy tiers in footer and tier messages
1 parent a73b161 commit 4fc8262

5 files changed

Lines changed: 76 additions & 7 deletions

File tree

cmd/autonomy_tiers.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package cmd
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/GrayCodeAI/hawk/internal/engine"
8+
"github.com/charmbracelet/lipgloss"
79
)
810

911
// Four container autonomy tiers (Inspect → Edit → Run → Trust).
@@ -61,14 +63,52 @@ func autonomyTierHint(level engine.AutonomyLevel) string {
6163
}
6264
}
6365

66+
func autonomyTierColor(level engine.AutonomyLevel) lipgloss.Color {
67+
switch level {
68+
case engine.AutonomyBasic:
69+
return tierInspect
70+
case engine.AutonomySemi:
71+
return tierEdit
72+
case engine.AutonomyFull:
73+
return tierRun
74+
case engine.AutonomyYOLO:
75+
return tierTrust
76+
default:
77+
return tierEdit
78+
}
79+
}
80+
81+
func autonomyTierStyle(level engine.AutonomyLevel) lipgloss.Style {
82+
return lipgloss.NewStyle().Foreground(autonomyTierColor(level)).Bold(true).Inline(true)
83+
}
84+
85+
func renderAutonomyTierLabel(level engine.AutonomyLevel) string {
86+
return autonomyTierStyle(level).Render(autonomyTierName(level))
87+
}
88+
6489
func formatAutonomyTierMessage(level engine.AutonomyLevel) string {
65-
name := autonomyTierName(level)
90+
name := renderAutonomyTierLabel(level)
6691
if hint := autonomyTierHint(level); hint != "" {
6792
return fmt.Sprintf("Autonomy → %s (%s)", name, hint)
6893
}
6994
return fmt.Sprintf("Autonomy → %s", name)
7095
}
7196

97+
func autonomyLevelForTierName(name string) engine.AutonomyLevel {
98+
switch strings.TrimSpace(name) {
99+
case "Inspect":
100+
return engine.AutonomyBasic
101+
case "Edit":
102+
return engine.AutonomySemi
103+
case "Run":
104+
return engine.AutonomyFull
105+
case "Trust":
106+
return engine.AutonomyYOLO
107+
default:
108+
return DefaultContainerAutonomy
109+
}
110+
}
111+
72112
// autonomyFromSettings maps settings.json autonomy (1–4) to a tier level.
73113
// 0 or unset leaves session default until the container is ready.
74114
func autonomyFromSettings(n int) engine.AutonomyLevel {

cmd/autonomy_tiers_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"testing"
55

66
"github.com/GrayCodeAI/hawk/internal/engine"
7+
"github.com/charmbracelet/lipgloss"
78
)
89

910
func TestAutonomyTierNames(t *testing.T) {
@@ -37,4 +38,21 @@ func TestAutonomyFromSettings(t *testing.T) {
3738
if autonomyFromSettings(0) != 0 {
3839
t.Fatal("settings 0 should leave unset")
3940
}
41+
}
42+
43+
func TestAutonomyTierColorsDistinct(t *testing.T) {
44+
levels := []engine.AutonomyLevel{
45+
engine.AutonomyBasic,
46+
engine.AutonomySemi,
47+
engine.AutonomyFull,
48+
engine.AutonomyYOLO,
49+
}
50+
seen := make(map[lipgloss.Color]bool)
51+
for _, l := range levels {
52+
c := autonomyTierColor(l)
53+
if seen[c] {
54+
t.Fatalf("duplicate color for tier %v", l)
55+
}
56+
seen[c] = true
57+
}
4058
}

cmd/chat.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@ func (m chatModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
11081108
if m.session.Autonomy == 0 {
11091109
m.session.Autonomy = DefaultContainerAutonomy
11101110
}
1111-
m.messages = append(m.messages, displayMsg{role: "system", content: "Sandbox ready · default autonomy " + autonomyTierName(m.session.Autonomy) + " (ctrl+L to change)"})
1111+
m.messages = append(m.messages, displayMsg{role: "system", content: "Sandbox ready · default autonomy " + renderAutonomyTierLabel(m.session.Autonomy) + " (ctrl+L to change)"})
11121112
m.invalidateConnStatus()
11131113
}
11141114
if msg.err != nil {

cmd/statusbar.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,25 +154,30 @@ func renderContainerFooterLeft(m chatModel) (rendered string, visLen int) {
154154
return containerErrStyle.Bold(true).Render(bold) + containerErrStyle.Render(dim), visLen
155155
}
156156
if m.containerEnabled {
157-
return containerLabelStyle.Render(bold) + renderContainerFooterDetail(dim), visLen
157+
return containerLabelStyle.Render(bold) + renderContainerFooterDetail(dim, m.session), visLen
158158
}
159159

160160
labelStyle := lipgloss.NewStyle().Foreground(warnAmber).Bold(true)
161161
return labelStyle.Render(bold) + dimStyle.Render(dim), visLen
162162
}
163163

164-
func renderContainerFooterDetail(detail string) string {
164+
func renderContainerFooterDetail(detail string, sess *engine.Session) string {
165165
if detail == "" {
166166
return ""
167167
}
168168
statusStyle := lipgloss.NewStyle().Foreground(textPlaceholder).Inline(true)
169-
okStyle := lipgloss.NewStyle().Foreground(doneGreen).Inline(true)
170169
sep := " · "
171-
status, ok, found := strings.Cut(detail, sep)
170+
status, tierPart, found := strings.Cut(detail, sep)
172171
if !found {
173172
return statusStyle.Render(detail)
174173
}
175-
return statusStyle.Render(status) + configMutedStyle().Inline(true).Render(sep) + okStyle.Render(ok)
174+
level := DefaultContainerAutonomy
175+
if sess != nil && sess.Autonomy != 0 {
176+
level = sess.Autonomy
177+
} else {
178+
level = autonomyLevelForTierName(tierPart)
179+
}
180+
return statusStyle.Render(status) + configMutedStyle().Inline(true).Render(sep) + autonomyTierStyle(level).Render(strings.TrimSpace(tierPart))
176181
}
177182

178183
// containerFooterLeft is the bold + dim text on the top footer row (left side).

cmd/theme.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ var doneGreen = lipgloss.Color("#4CAF50")
7373
// container output reads as its own zone in the status footer.
7474
var containerBlue = lipgloss.Color("#3BAADA")
7575

76+
// Autonomy tier colors (Inspect → Edit → Run → Trust), coolest to hottest.
77+
var tierInspect = lipgloss.Color("#75B1E2") // infoSky — read-only
78+
var tierEdit = lipgloss.Color("#4ECDC4") // successTeal — default work
79+
var tierRun = lipgloss.Color("#FFB347") // warnAmber — shell
80+
var tierTrust = lipgloss.Color("#FF6B6B") // errorCoral — minimal gates
81+
7682
// ---------------------------------------------------------------------------
7783
// 5. HUD overlay
7884
// ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)