Cleanup list navigates; spend bars; office writes the missing scaffold; memory read-out per agent

This commit is contained in:
Sam
2026-11-11 15:05:43 +11:00
parent 3ec960e349
commit 36abdc1f84
13 changed files with 726 additions and 4 deletions

View File

@@ -717,6 +717,9 @@ func (m agentsModel) settingLines(width int) []string {
}
out = append(out, mark+style.Render(fmt.Sprintf("%-14s %s", f.label, value)))
}
out = append(out, "", " "+mutedStyle2().Render("memory and extensions"))
out = append(out, " "+lipgloss.NewStyle().Foreground(lipgloss.Color(active.Text)).Render(m.memoryLine(agent.ID)))
out = append(out, " "+dimStyle().Render(" read from the agent's command and the global pi settings; nothing is set here yet"))
out = append(out, "")
out = append(out, dimStyle().Render(" a model change applies to that agent's next run, never to one in flight."))
out = append(out, dimStyle().Render(" p pause · P resume · w wake: they act on the agent chosen here."))
@@ -1239,3 +1242,57 @@ func (m agentsModel) extensionLines(width int) []string {
out = append(out, dimStyle().Render(" and passed to Paperclip as this agent's command."))
return out
}
// memoryLine says what an agent's context is configured to use: whether it runs
// a wrapper (per-agent extensions), and which memory or compression packages the
// global Pi settings carry. It reads, and claims nothing it has not seen.
func (m agentsModel) memoryLine(agentID string) string {
parts := []string{}
if wrapped := wrapperExtensions(agentID); len(wrapped) > 0 {
var names []string
for name := range wrapped {
names = append(names, name)
}
sort.Strings(names)
parts = append(parts, "extensions: "+strings.Join(names, ", "))
} else {
parts = append(parts, "extensions: the global set")
}
full, err := m.client.AgentRaw(agentID)
if err == nil {
if cfg, ok := full["adapterConfig"].(map[string]any); ok {
if cmd, ok := cfg["command"].(string); ok && cmd != "" && cmd != "pi" {
parts = append(parts, "command: "+filepath.Base(cmd))
}
}
}
home, _ := os.UserHomeDir()
if b, err := os.ReadFile(filepath.Join(home, ".pi", "agent", "settings.json")); err == nil {
var settings struct {
Packages []any `json:"packages"`
}
if json.Unmarshal(b, &settings) == nil {
var mem []string
for _, p := range settings.Packages {
switch v := p.(type) {
case string:
if strings.Contains(v, "memctx") || strings.Contains(v, "engram") || strings.Contains(v, "headroom") {
mem = append(mem, strings.TrimPrefix(v, "npm:"))
}
case map[string]any:
if src, ok := v["source"].(string); ok {
if strings.Contains(src, "engram") || strings.Contains(src, "headroom") {
mem = append(mem, src)
}
}
}
}
if len(mem) > 0 {
parts = append(parts, "memory: "+strings.Join(mem, ", "))
} else {
parts = append(parts, "memory: none in the global pi settings")
}
}
}
return strings.Join(parts, " · ")
}

View File

@@ -383,11 +383,23 @@ func (m projectModel) costLines() []string {
}
total := 0
runs := 0
out := []string{"", dimStyle().Render(fmt.Sprintf("%-10s %-10s %8s %6s", "task", "state", "cents", "runs"))}
peak := 1
for _, c := range m.costs {
if c.Cents > peak {
peak = c.Cents
}
}
out := []string{"", dimStyle().Render(fmt.Sprintf("%-10s %-10s %6s %5s %s", "task", "state", "cents", "runs", "share of the largest"))}
for _, c := range m.costs {
total += c.Cents
runs += c.Runs
out = append(out, fmt.Sprintf("%-10s %-10s %8d %6d", c.Identifier, c.Status, c.Cents, c.Runs))
bars := int(float64(c.Cents) / float64(peak) * 24)
bar := strings.Repeat("█", bars)
if c.Cents > 0 && bars == 0 {
bar = "▏"
}
out = append(out, fmt.Sprintf("%-10s %-10s %6d %5d %s", c.Identifier, c.Status, c.Cents, c.Runs,
lipgloss.NewStyle().Foreground(lipgloss.Color(active.Accent)).Render(bar)))
}
out = append(out, "")
out = append(out, m.row("total, recent", fmt.Sprintf("%d cent(s) over %d run(s)", total, runs)))

View File

@@ -297,9 +297,26 @@ func (m repairModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
return m, nil
case "up", "k":
if m.tab == 3 {
if m.sessAt > 0 {
m.sessAt--
}
m.scroll = max(0, min(m.scroll, m.sessAt-2))
return m, nil
}
m.scroll--
return m, nil
case "down", "j":
if m.tab == 3 {
if m.sessAt < len(m.sessions)-1 {
m.sessAt++
}
visible := max(3, m.height-6)
if m.sessAt > m.scroll+visible-3 {
m.scroll = m.sessAt - visible + 3
}
return m, nil
}
m.scroll++
return m, nil
case "esc":
@@ -507,10 +524,10 @@ func (m repairModel) cleanupLines(width int) []string {
r.lastUsed.Format("2 Jan 15:04"), state)
mark := " "
if m.ticked[r.name] {
mark = "▸ "
mark = "* "
}
if i == m.sessAt {
out = append(out, selectedStyle2().Render(padTo(mark+line, width)))
out = append(out, selectedStyle2().Render(padTo("▸"+line[1:], width)))
continue
}
out = append(out, mark+line)