> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ComposioHQ/agent-orchestrator/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracker

> Plugin interface for issue tracking integrations (GitHub Issues, Linear, Jira) with issue fetching, branch naming, and prompt generation

## Overview

The `Tracker` interface defines how the orchestrator integrates with issue tracking systems. Tracker plugins fetch issue details, generate branch names, and create prompts for agents to work on specific issues.

**Plugin Slot:** `tracker`\
**Default Plugin:** `github`

## Interface Definition

```typescript theme={null}
export interface Tracker {
  readonly name: string;
  
  getIssue(identifier: string, project: ProjectConfig): Promise<Issue>;
  isCompleted(identifier: string, project: ProjectConfig): Promise<boolean>;
  issueUrl(identifier: string, project: ProjectConfig): string;
  issueLabel?(url: string, project: ProjectConfig): string;
  branchName(identifier: string, project: ProjectConfig): string;
  generatePrompt(identifier: string, project: ProjectConfig): Promise<string>;
  listIssues?(filters: IssueFilters, project: ProjectConfig): Promise<Issue[]>;
  updateIssue?(identifier: string, update: IssueUpdate, project: ProjectConfig): Promise<void>;
  createIssue?(input: CreateIssueInput, project: ProjectConfig): Promise<Issue>;
}
```

## Methods

<ResponseField name="name" type="string" required>
  Plugin name identifier (e.g. `"github"`, `"linear"`, `"jira"`).
</ResponseField>

<ResponseField name="getIssue" type="(identifier: string, project: ProjectConfig) => Promise<Issue>" required>
  Fetch issue details from the tracker.

  **Parameters:**

  * `identifier` - Issue identifier (e.g. `"42"`, `"INT-1327"`, GitHub URL)
  * `project` - Project configuration

  **Returns:** Issue object with id, title, description, state, etc.
</ResponseField>

<ResponseField name="isCompleted" type="(identifier: string, project: ProjectConfig) => Promise<boolean>" required>
  Check if issue is completed/closed.

  **Parameters:**

  * `identifier` - Issue identifier
  * `project` - Project configuration

  **Returns:** true if issue is closed/done
</ResponseField>

<ResponseField name="issueUrl" type="(identifier: string, project: ProjectConfig) => string" required>
  Generate a URL for the issue.

  **Parameters:**

  * `identifier` - Issue identifier
  * `project` - Project configuration

  **Returns:** Full URL to issue
</ResponseField>

<ResponseField name="issueLabel" type="(url: string, project: ProjectConfig) => string">
  Optional: Extract a human-readable label from an issue URL (e.g. `"INT-1327"`, `"#42"`).

  **Parameters:**

  * `url` - Issue URL
  * `project` - Project configuration

  **Returns:** Short label for display
</ResponseField>

<ResponseField name="branchName" type="(identifier: string, project: ProjectConfig) => string" required>
  Generate a git branch name for the issue.

  **Parameters:**

  * `identifier` - Issue identifier
  * `project` - Project configuration

  **Returns:** Branch name (e.g. `"fix-auth-bug-42"`, `"int-1327-improve-performance"`)
</ResponseField>

<ResponseField name="generatePrompt" type="(identifier: string, project: ProjectConfig) => Promise<string>" required>
  Generate a prompt for the agent to work on this issue.

  **Parameters:**

  * `identifier` - Issue identifier
  * `project` - Project configuration

  **Returns:** Formatted prompt string with issue details
</ResponseField>

<ResponseField name="listIssues" type="(filters: IssueFilters, project: ProjectConfig) => Promise<Issue[]>">
  Optional: List issues with filters.

  **Parameters:**

  * `filters` - Filter criteria (state, labels, assignee, limit)
  * `project` - Project configuration

  **Returns:** Array of issues
</ResponseField>

<ResponseField name="updateIssue" type="(identifier: string, update: IssueUpdate, project: ProjectConfig) => Promise<void>">
  Optional: Update issue state.

  **Parameters:**

  * `identifier` - Issue identifier
  * `update` - Fields to update (state, labels, assignee, comment)
  * `project` - Project configuration
</ResponseField>

<ResponseField name="createIssue" type="(input: CreateIssueInput, project: ProjectConfig) => Promise<Issue>">
  Optional: Create a new issue.

  **Parameters:**

  * `input` - Issue fields (title, description, labels, assignee, priority)
  * `project` - Project configuration

  **Returns:** Created issue
</ResponseField>

## Related Types

### Issue

```typescript theme={null}
export interface Issue {
  id: string;
  title: string;
  description: string;
  url: string;
  state: "open" | "in_progress" | "closed" | "cancelled";
  labels: string[];
  assignee?: string;
  priority?: number;
}
```

<ParamField path="id" type="string" required>
  Unique issue identifier
</ParamField>

<ParamField path="title" type="string" required>
  Issue title
</ParamField>

<ParamField path="description" type="string" required>
  Full issue description (may be markdown)
</ParamField>

<ParamField path="url" type="string" required>
  URL to view issue
</ParamField>

<ParamField path="state" type="string" required>
  Issue state: `"open"`, `"in_progress"`, `"closed"`, or `"cancelled"`
</ParamField>

<ParamField path="labels" type="string[]" required>
  Array of label strings
</ParamField>

<ParamField path="assignee" type="string">
  Username of assignee
</ParamField>

<ParamField path="priority" type="number">
  Priority level (higher = more urgent)
</ParamField>

### IssueFilters

```typescript theme={null}
export interface IssueFilters {
  state?: "open" | "closed" | "all";
  labels?: string[];
  assignee?: string;
  limit?: number;
}
```

### IssueUpdate

```typescript theme={null}
export interface IssueUpdate {
  state?: "open" | "in_progress" | "closed";
  labels?: string[];
  assignee?: string;
  comment?: string;
}
```

### CreateIssueInput

```typescript theme={null}
export interface CreateIssueInput {
  title: string;
  description: string;
  labels?: string[];
  assignee?: string;
  priority?: number;
}
```

## Usage Examples

### Implementing a Tracker Plugin

```typescript theme={null}
import type { Tracker, Issue, ProjectConfig } from "@composio/ao-core";
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);

export function create(): Tracker {
  return {
    name: "github",
    
    async getIssue(identifier: string, project: ProjectConfig): Promise<Issue> {
      // Normalize identifier (handle URLs, #42, or just "42")
      const issueNumber = identifier.replace(/^#/, "").split("/").pop()!;
      
      // Use gh CLI
      const { stdout } = await execFileAsync("gh", [
        "issue",
        "view",
        issueNumber,
        "--repo", project.repo,
        "--json", "number,title,body,url,state,labels,assignees"
      ], { timeout: 30_000 });
      
      const data = JSON.parse(stdout);
      
      return {
        id: data.number.toString(),
        title: data.title,
        description: data.body || "",
        url: data.url,
        state: data.state === "OPEN" ? "open" : "closed",
        labels: data.labels.map((l: any) => l.name),
        assignee: data.assignees[0]?.login
      };
    },
    
    async isCompleted(identifier: string, project: ProjectConfig): Promise<boolean> {
      const issue = await this.getIssue(identifier, project);
      return issue.state === "closed";
    },
    
    issueUrl(identifier: string, project: ProjectConfig): string {
      const issueNumber = identifier.replace(/^#/, "");
      return `https://github.com/${project.repo}/issues/${issueNumber}`;
    },
    
    issueLabel(url: string, project: ProjectConfig): string {
      const match = url.match(/\/(\d+)$/);
      return match ? `#${match[1]}` : url;
    },
    
    branchName(identifier: string, project: ProjectConfig): string {
      const issueNumber = identifier.replace(/^#/, "");
      return `issue-${issueNumber}`;
    },
    
    async generatePrompt(identifier: string, project: ProjectConfig): Promise<string> {
      const issue = await this.getIssue(identifier, project);
      
      return [
        `# ${issue.title}`,
        ``,
        issue.description,
        ``,
        `Issue: ${issue.url}`,
        issue.labels.length > 0 ? `Labels: ${issue.labels.join(", ")}` : ""
      ].filter(Boolean).join("\n");
    },
    
    async listIssues(filters: IssueFilters, project: ProjectConfig): Promise<Issue[]> {
      const args = ["issue", "list", "--repo", project.repo];
      
      if (filters.state && filters.state !== "all") {
        args.push("--state", filters.state);
      }
      
      if (filters.assignee) {
        args.push("--assignee", filters.assignee);
      }
      
      if (filters.labels) {
        for (const label of filters.labels) {
          args.push("--label", label);
        }
      }
      
      if (filters.limit) {
        args.push("--limit", filters.limit.toString());
      }
      
      args.push("--json", "number,title,body,url,state,labels,assignees");
      
      const { stdout } = await execFileAsync("gh", args, { timeout: 30_000 });
      const items = JSON.parse(stdout);
      
      return items.map((data: any) => ({
        id: data.number.toString(),
        title: data.title,
        description: data.body || "",
        url: data.url,
        state: data.state === "OPEN" ? "open" : "closed",
        labels: data.labels.map((l: any) => l.name),
        assignee: data.assignees[0]?.login
      }));
    }
  };
}
```

### Using Tracker in Session Manager

```typescript theme={null}
import type { Tracker } from "@composio/ao-core";

const tracker: Tracker = registry.get("tracker", "github");

// Fetch issue details
const issue = await tracker.getIssue("42", projectConfig);
console.log(`Issue: ${issue.title}`);

// Generate branch name
const branch = tracker.branchName("42", projectConfig);
console.log(`Branch: ${branch}`);

// Generate prompt for agent
const prompt = await tracker.generatePrompt("42", projectConfig);

// Spawn session with issue
await sessionManager.spawn({
  projectId: "my-app",
  issueId: "42",
  prompt
});
```

## Implementation Notes

### Issue Identifier Normalization

Trackers should accept flexible identifier formats:

* GitHub: `"42"`, `"#42"`, `"https://github.com/owner/repo/issues/42"`
* Linear: `"INT-1327"`, `"https://linear.app/team/issue/INT-1327"`
* Jira: `"PROJ-123"`, `"https://company.atlassian.net/browse/PROJ-123"`

### Prompt Generation

The `generatePrompt()` method should:

1. Include issue title and description
2. Add issue URL for reference
3. Include relevant metadata (labels, priority)
4. Format for agent consumption (markdown preferred)

### Authentication

Trackers typically rely on environment variables or CLI tools:

* GitHub: `gh` CLI (uses `~/.config/gh/hosts.yml`)
* Linear: `LINEAR_API_KEY` environment variable
* Jira: `JIRA_TOKEN` + `JIRA_URL`

### Built-in Plugins

* **github** - GitHub Issues (default, uses `gh` CLI)
* **linear** - Linear (uses GraphQL API)

Future plugins could support Jira, Asana, ClickUp, etc.

## See Also

* [Session](/api/session) - Session interface
* [SCM](/api/scm) - Source control and PR lifecycle interface


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.