Adding an init target agent

This guide walks through adding support for a new AI agent harness to the init command.

Steps

1. Add the agent definition

Edit agents/registry.go and add an entry to the Registry slice:

{
    ID:          "my-agent",
    DisplayName: "My Agent",
    ProjectPath: ".my-agent/skills/",  // relative to project root
    GlobalPath:  ".my-agent/skills/",  // relative to home directory
},

The Agent struct fields:

Field Description
ID Machine-readable identifier (kebab-case)
DisplayName Human-readable name for --interactive mode
ProjectPath Install path relative to project root
GlobalPath Install path relative to home directory

2. Add a test case

Add a test case to agents/registry_test.go:

func TestRegistry_MyAgent(t *testing.T) {
    a := ByID("my-agent")
    if a == nil {
        t.Fatal("my-agent not found in registry")
    }
    if a.DisplayName != "My Agent" {
        t.Errorf("expected 'My Agent', got %q", a.DisplayName)
    }
}

3. Run tests

go test ./agents/...

No changes to cmd/init.go are needed — the registry drives everything
via ByID, HarnessDirs, and DisplayNames.

How init auto-detects agents

The resolveByHarness function checks whether each agent’s harness root
directory exists under the install target:

func resolveByHarness(baseDir string, global bool) []Agent {
    // For each agent in the registry:
    //   root = first path component of ProjectPath (e.g., ".cursor")
    //   check if root exists under baseDir
    //   if yes, include the agent
}

For example, if .cursor/ exists in the project, Cursor is auto-detected.
If ~/.claude/ exists, Claude Code is auto-detected (with --global).

Harness directory convention

The harness root is always the first path component of ProjectPath/GlobalPath:

ProjectPath: ".cursor/rules/" → harness root: ".cursor"
GlobalPath:  ".cursor/"       → harness root: ".cursor"

This convention means HarnessDirs() returns the set of unique dot-directory
names (e.g., .claude, .cursor, .agents, .windsurf).