# Implement Pi live model listing for entire review --models

`d91d995`→[main](/content/gh/entireio/cli/commits/main/index.html)·

dipree·1mo ago·3 files·+90 added/-2 removed

Pi has a real model-enumeration command, so ListModels shells out to
`pi --list-models` and parses the table into provider/model entries.
parsePiModelList is split out and unit-tested (no pi binary needed).

## Sessions

ebcc924da0dfView transcript

## Changes

3

- cmd/entire/cli

- agent/pi

- Amodels.go+49

- Amodels\_test.go+35

- review

- Mcmd\_test.go+6/-2

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

package pi

import (
	"bufio"
	"context"
	"fmt"
	"strings"

"github.com/entireio/cli/cmd/entire/cli/agent"
)

var _ agent.ModelLister = (*PiAgent)(nil)

// ListModels returns Pi's live model catalog by shelling out to
// `pi --list-models`. Unlike the curated lists for claude-code/codex/gemini,
// Pi has a real enumeration command spanning every configured provider, so the
// result reflects what this machine/account can actually use.
func (a *PiAgent) ListModels(ctx context.Context) ([]agent.ModelInfo, error) {
	out, err := agent.RunIsolatedTextGeneratorCLI(ctx, nil, "pi", "pi", []string{"--list-models"}, "")
	if err != nil {
		return nil, fmt.Errorf("pi --list-models: %w", err)
	}
	return parsePiModelList(out), nil
}

// parsePiModelList parses the tabular `pi --list-models` output. Each non-header
// row is "<provider> <model> <context> <max-out> <thinking> <images>"; the model
// ID is rendered as "provider/model" (the unambiguous form Pi's --model accepts)
// with the context window kept as a note.
func parsePiModelList(raw string) []agent.ModelInfo {
	var models []agent.ModelInfo
	scanner := bufio.NewScanner(strings.NewReader(raw))
	for scanner.Scan() {
		fields := strings.Fields(scanner.Text())
		if len(fields) < 2 {
			continue
		}
		provider, model := fields[0], fields[1]
		if provider == "provider" && model == "model" {
			continue // header row
		}
		note := ""
		if len(fields) >= 3 {
			note = fields[2] + " ctx"
		}
		models = append(models, agent.ModelInfo{ID: provider + "/" + model, Note: note})
	}
	return models
}
```

Acmd/entire/cli/agent/pi/models.go+49

```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

package pi

import "testing"

func TestParsePiModelList(t *testing.T) {
	raw := "provider      model                       context  max-out  thinking  images\n" +
		"anthropic     claude-opus-4-0             200K     32K      yes       yes   \n" +
		"openai        gpt-5                       400K     128K     yes       no    \n" +
		"\n" +
		"google        gemini-2.5-pro              1M       64K      yes       yes   \n"

got := parsePiModelList(raw)
	if len(got) != 3 {
		t.Fatalf("parsed %d models, want 3: %#v", len(got), got)
	}
	want := []struct{id, note string}{
		{"anthropic/claude-opus-4-0", "200K ctx"},
		{"openai/gpt-5", "400K ctx"},
		{"google/gemini-2.5-pro", "1M ctx"},
	}
	for i, w := range want {
		if got[i].ID != w.id {
			t.Errorf("model[%d].ID = %q, want %q", i, got[i].ID, w.id)
		}
		if got[i].Note != w.note {
			t.Errorf("model[%d].Note = %q, want %q", i, got[i].Note, w.note)
		}
	}
}

func TestParsePiModelList_HeaderAndBlanksSkipped(t *testing.T) {
	if got := parsePiModelList("provider model\n\n   \n"); len(got) != 0 {
		t.Fatalf("expected no models, got %#v", got)
	}
}
```

Acmd/entire/cli/agent/pi/models\_test.go+35

```
142 unmodified lines

143
144
145
146
147
146
147
148
149
150
151
152
153
154

142 unmodified lines

t.Errorf("--models output missing %q:\n%s", want, out)
	}
}
if strings.Contains(out, "gpt-5-codex") {
		t.Errorf("--models should not invent example codex models:\n%s", out)
	// codex has no enumeration command, so its own section must show the
	// no-advertised-models note rather than invented examples. (A substring
	// check would false-positive on Pi's live list, which legitimately
	// includes openai/gpt-5-codex.)
	if !strings.Contains(out, "codex:\n  (no advertised models") {
		t.Errorf("codex section should show no advertised models:\n%s", out)
	}
}
```

Mcmd/entire/cli/review/cmd\_test.go+6/-2
