Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions cmd/aitools/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ type agentEntry struct {
// Detected is the CLI's presence verdict (Agent.IsPreselected), so consumers get
// the same answer the CLI would act on.
Detected bool `json:"detected"`
// SupportsProjectScope mirrors agents.Agent.SupportsProjectScope so consumers can
// offer a project-scoped install without hardcoding which agents allow it.
SupportsProjectScope bool `json:"supports_project_scope"`
// Installed maps CLI scope -> the install found there; always present, empty
// when nothing is installed, matching the skills shape.
Installed map[string]installInfo `json:"installed"`
Expand Down Expand Up @@ -231,10 +234,11 @@ func buildAgentEntries(ctx context.Context, states map[string]*installer.Install
entries := make([]agentEntry, 0, len(agents.Registry))
for _, a := range agents.Registry {
entry := agentEntry{
Name: a.Name,
DisplayName: a.DisplayName,
Managed: a.Plugin != nil,
Detected: a.IsPreselected(ctx),
Name: a.Name,
DisplayName: a.DisplayName,
Managed: a.Plugin != nil,
Detected: a.IsPreselected(ctx),
SupportsProjectScope: a.SupportsProjectScope,
}

// Always emit an installed map, empty when nothing is installed, so the
Expand Down
18 changes: 13 additions & 5 deletions cmd/aitools/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,12 @@ func TestRenderListJSONWithAgents(t *testing.T) {
Summary: map[string]scopeSummary{installer.ScopeGlobal: {Installed: 0, Total: 0}},
Agents: []agentEntry{
{
Name: "claude-code",
DisplayName: "Claude Code",
Managed: true,
Detected: true,
Installed: map[string]installInfo{installer.ScopeGlobal: {Delivery: "plugin", Version: "0.2.6"}},
Name: "claude-code",
DisplayName: "Claude Code",
Managed: true,
Detected: true,
SupportsProjectScope: true,
Installed: map[string]installInfo{installer.ScopeGlobal: {Delivery: "plugin", Version: "0.2.6"}},
},
{
Name: "cursor",
Expand Down Expand Up @@ -146,6 +147,7 @@ func TestRenderListJSONWithAgents(t *testing.T) {
assert.Equal(t, "Claude Code", first["display_name"])
assert.Equal(t, true, first["managed"])
assert.Equal(t, true, first["detected"])
assert.Equal(t, true, first["supports_project_scope"])
installed := first["installed"].(map[string]any)
global := installed["global"].(map[string]any)
assert.Equal(t, "0.2.6", global["version"])
Expand All @@ -156,6 +158,7 @@ func TestRenderListJSONWithAgents(t *testing.T) {
second := agentsRaw[1].(map[string]any)
assert.Contains(t, second, "installed")
assert.Empty(t, second["installed"])
assert.Equal(t, false, second["supports_project_scope"])
}

func TestBuildAgentEntries(t *testing.T) {
Expand All @@ -182,17 +185,22 @@ func TestBuildAgentEntries(t *testing.T) {
assert.Equal(t, "Claude Code", byName["claude-code"].DisplayName)
assert.Equal(t, "0.2.6", byName["claude-code"].Installed[installer.ScopeGlobal].Version)
assert.Equal(t, "databricks plugin · v0.2.6 · up to date", agentStatusLabel(byName["claude-code"], "0.2.6"))
// SupportsProjectScope is wired straight from the registry: claude-code allows
// project scope, whereas the managed/skills-only global-only agents below do not.
assert.True(t, byName["claude-code"].SupportsProjectScope)

require.Contains(t, byName, "codex")
assert.True(t, byName["codex"].Managed)
assert.Equal(t, "0.2.5", byName["codex"].Installed[installer.ScopeGlobal].Version)
assert.Equal(t, "databricks plugin · v0.2.5 · update available", agentStatusLabel(byName["codex"], "0.2.6"))
assert.False(t, byName["codex"].SupportsProjectScope)

// The JSON output carries an entry for every registry agent, including
// skills-only agents like Cursor and managed agents with no recorded install.
require.Contains(t, byName, "cursor")
assert.False(t, byName["cursor"].Managed)
assert.Empty(t, byName["cursor"].Installed)
assert.False(t, byName["cursor"].SupportsProjectScope)

require.Contains(t, byName, "copilot")
assert.True(t, byName["copilot"].Managed)
Expand Down
Loading