From dd3f5d7d5f1b8cfa56f009f2eced167b8a897754 Mon Sep 17 00:00:00 2001 From: Tamir Dresher Date: Thu, 5 Mar 2026 00:10:06 +0200 Subject: [PATCH 01/15] feat: add platform adapter for Azure DevOps support Introduce a platform abstraction layer so Squad works with Azure DevOps (Work Items, PRs, Pipelines) in addition to GitHub (Issues, PRs, Actions). Platform module (packages/squad-sdk/src/platform/): - types.ts: PlatformType, WorkItem, PullRequest, PlatformAdapter interfaces - detect.ts: Auto-detect platform from git remote URL (github/ado) - github.ts: GitHubAdapter wrapping gh CLI - azure-devops.ts: AzureDevOpsAdapter wrapping az CLI - ralph-commands.ts: Platform-specific Ralph triage commands - index.ts: Factory createPlatformAdapter() + barrel exports Coordinator prompt: - Add Platform Detection section to squad.agent.md - ADO command mapping table and prerequisites Tests (57 passing): - Platform detection from various remote URLs - GitHub remote parsing (owner/repo extraction) - ADO remote parsing (org/project/repo extraction) - WorkItem/PullRequest type shape validation - Ralph command generation for both platforms - Edge cases (case insensitivity, unknown platforms) Docs: - docs/features/azure-devops.md: User guide - docs/specs/platform-adapter-prd.md: Design spec Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/.first-run | 1 + .squad/config.json | 4 + docs/features/enterprise-platforms.md | 137 +++++ docs/specs/platform-adapter-prd.md | 61 ++ package.json | 2 +- packages/squad-cli/package.json | 2 +- packages/squad-cli/src/cli-entry.ts | 47 +- packages/squad-sdk/package.json | 2 +- packages/squad-sdk/src/index.ts | 1 + .../squad-sdk/src/platform/azure-devops.ts | 248 ++++++++ packages/squad-sdk/src/platform/detect.ts | 140 +++++ packages/squad-sdk/src/platform/github.ts | 191 ++++++ packages/squad-sdk/src/platform/index.ts | 46 ++ packages/squad-sdk/src/platform/planner.ts | 188 ++++++ .../squad-sdk/src/platform/ralph-commands.ts | 93 +++ packages/squad-sdk/src/platform/types.ts | 64 ++ packages/squad-sdk/src/types.ts | 9 + templates/squad.agent.md | 55 ++ test/cli/consult.test.ts | 4 +- test/cli/init.test.ts | 2 +- test/platform-adapter.test.ts | 572 ++++++++++++++++++ 21 files changed, 1845 insertions(+), 24 deletions(-) create mode 100644 .squad/.first-run create mode 100644 .squad/config.json create mode 100644 docs/features/enterprise-platforms.md create mode 100644 docs/specs/platform-adapter-prd.md create mode 100644 packages/squad-sdk/src/platform/azure-devops.ts create mode 100644 packages/squad-sdk/src/platform/detect.ts create mode 100644 packages/squad-sdk/src/platform/github.ts create mode 100644 packages/squad-sdk/src/platform/index.ts create mode 100644 packages/squad-sdk/src/platform/planner.ts create mode 100644 packages/squad-sdk/src/platform/ralph-commands.ts create mode 100644 packages/squad-sdk/src/platform/types.ts create mode 100644 test/platform-adapter.test.ts diff --git a/.squad/.first-run b/.squad/.first-run new file mode 100644 index 00000000..fabbbc86 --- /dev/null +++ b/.squad/.first-run @@ -0,0 +1 @@ +2026-03-04T23:08:20.566Z diff --git a/.squad/config.json b/.squad/config.json new file mode 100644 index 00000000..4bbe45a9 --- /dev/null +++ b/.squad/config.json @@ -0,0 +1,4 @@ +{ + "version": 1, + "teamRoot": "C:\\temp\\squad-streams" +} \ No newline at end of file diff --git a/docs/features/enterprise-platforms.md b/docs/features/enterprise-platforms.md new file mode 100644 index 00000000..33c9b407 --- /dev/null +++ b/docs/features/enterprise-platforms.md @@ -0,0 +1,137 @@ +# Enterprise Platforms + +Squad supports Azure DevOps and Microsoft Planner in addition to GitHub. When your git remote points to Azure DevOps, Squad automatically detects the platform and adapts its commands. For work-item tracking, Squad also supports a hybrid model where code lives in one platform and tasks live in Microsoft Planner. + +## Prerequisites + +1. **Azure CLI** — Install from [https://aka.ms/install-az-cli](https://aka.ms/install-az-cli) +2. **Azure DevOps extension** — `az extension add --name azure-devops` +3. **Login** — `az login` +4. **Set defaults** — `az devops configure --defaults organization=https://dev.azure.com/YOUR_ORG project=YOUR_PROJECT` + +Verify setup: + +```bash +az devops configure --list +# Should show organization and project +``` + +## How It Works + +Squad auto-detects the platform from your git remote URL: + +| Remote URL pattern | Detected platform | +|---|---| +| `github.com` | GitHub | +| `dev.azure.com` | Azure DevOps | +| `*.visualstudio.com` | Azure DevOps | +| `ssh.dev.azure.com` | Azure DevOps | + +## Differences from GitHub + +### Work Items vs Issues + +| GitHub | Azure DevOps | +|---|---| +| Issues | Work Items | +| Labels (e.g., `squad:alice`) | Tags (e.g., `squad:alice`) | +| `gh issue list --label X` | WIQL query via `az boards query` | +| `gh issue edit --add-label` | `az boards work-item update --fields "System.Tags=..."` | + +### Pull Requests + +| GitHub | Azure DevOps | +|---|---| +| `gh pr list` | `az repos pr list` | +| `gh pr create` | `az repos pr create` | +| `gh pr merge` | `az repos pr update --status completed` | +| Review: Approved / Changes Requested | Vote: 10 (approved) / -10 (rejected) | + +### Branch Operations + +Branch operations use the same `git` commands on both platforms. Squad creates branches with the naming convention `squad/{id}-{slug}`. + +## Ralph on Azure DevOps + +Ralph works identically on ADO — he scans for untriaged work items using WIQL queries instead of GitHub label filters: + +``` +# GitHub +gh issue list --label "squad:untriaged" --json number,title,labels + +# Azure DevOps +az boards query --wiql "SELECT [System.Id],[System.Title],[System.Tags] FROM WorkItems WHERE [System.Tags] Contains 'squad:untriaged'" +``` + +Tag assignment uses the same `squad:{member}` convention, stored as ADO work item tags separated by `;`. + +## Configuration + +No additional configuration is needed beyond the `az` CLI setup. Squad reads the git remote URL and automatically selects the correct adapter. + +To explicitly check which platform Squad detects: + +```typescript +import { detectPlatform } from '@bradygaster/squad/platform'; + +const platform = detectPlatform('/path/to/repo'); +// Returns 'github', 'azure-devops', or 'planner' +``` + +--- + +## Microsoft Planner Support (Hybrid Model) + +Squad supports a hybrid model where your **repository** lives in GitHub or Azure DevOps, but **work items** are tracked in Microsoft Planner. This is common in enterprise environments where project management uses Planner while engineering uses ADO or GitHub for code. + +### How It Works + +- Planner **buckets** map to squad assignments: `squad:untriaged`, `squad:riker`, `squad:data`, etc. +- Moving a task between buckets = reassigning to a team member +- Task completion = 100% complete or move to "Done" bucket +- PRs and branches still go through the repo adapter (GitHub or Azure DevOps) + +### Prerequisites + +1. **Azure CLI** — `az login` +2. **Graph API access** — `az account get-access-token --resource-type ms-graph` +3. **Plan ID** — Found in the Planner URL or via Graph API + +### Configuration + +In `squad.config.ts`, specify the hybrid model: + +```typescript +const config: SquadConfig = { + // ... other config + platform: { + repo: 'azure-devops', // where code lives + workItems: 'planner', // where tasks live + planId: 'rYe_WFgqUUqnSTZfpMdKcZUAER1P', + }, +}; +``` + +### Ralph with Planner + +Ralph scans Planner tasks via the Microsoft Graph API instead of GitHub labels or ADO WIQL: + +``` +# List untriaged tasks +GET /planner/plans/{planId}/tasks → filter by "squad:untriaged" bucket + +# Assign to member (move to their bucket) +PATCH /planner/tasks/{taskId} → { "bucketId": "{squad:member bucket ID}" } +``` + +PR operations still use the repo adapter: + +``` +# Repo on Azure DevOps +az repos pr list --status active +az repos pr create --source-branch ... --target-branch ... + +# Repo on GitHub +gh pr list --state open +gh pr create --head ... --base ... +``` diff --git a/docs/specs/platform-adapter-prd.md b/docs/specs/platform-adapter-prd.md new file mode 100644 index 00000000..e892501b --- /dev/null +++ b/docs/specs/platform-adapter-prd.md @@ -0,0 +1,61 @@ +# Platform Adapter — Design Spec + +## Overview + +The Platform Adapter abstraction allows Squad to work with multiple source code hosting platforms (GitHub, Azure DevOps) through a unified interface. This enables Ralph and the coordinator to use the same triage/assignment logic regardless of the underlying platform. + +## Design Decisions + +### 1. Interface-based abstraction + +We use a TypeScript interface (`PlatformAdapter`) rather than an abstract class. This keeps the contract pure and allows each adapter to manage its own dependencies independently. + +### 2. CLI-based implementations + +Both adapters wrap CLI tools (`gh` for GitHub, `az` for ADO) rather than using REST APIs directly. This: +- Leverages existing authentication (users are already logged into `gh`/`az`) +- Avoids managing OAuth tokens, PATs, or refresh flows +- Matches how Squad already interacts with GitHub + +### 3. Auto-detection from git remote + +Platform detection reads the `origin` remote URL. This is zero-config — users don't need to specify which platform they're on. + +### 4. Graceful failure + +If the required CLI is not installed, the adapter throws a descriptive error with installation instructions rather than a cryptic exec failure. + +## Mapping Table + +| Concept | GitHub | Azure DevOps | +|---|---|---| +| Work item | Issue | Work Item | +| Work item query | `gh issue list --label X` | WIQL via `az boards query` | +| Work item tags | Labels | Tags (`;`-separated) | +| Pull request list | `gh pr list` | `az repos pr list` | +| Pull request create | `gh pr create` | `az repos pr create` | +| Pull request merge | `gh pr merge` | `az repos pr update --status completed` | +| Branch create | `git checkout -b` | `git checkout -b` | +| Review status | `reviewDecision` field | `vote` field on reviewers | +| Authentication | `gh auth login` | `az login` | + +## Module Structure + +``` +packages/squad-sdk/src/platform/ +├── types.ts # PlatformType, WorkItem, PullRequest, PlatformAdapter +├── detect.ts # detectPlatform, parseGitHubRemote, parseAzureDevOpsRemote +├── github.ts # GitHubAdapter +├── azure-devops.ts # AzureDevOpsAdapter +├── ralph-commands.ts # getRalphScanCommands +└── index.ts # Factory + barrel exports +``` + +## Future Work + +- **GitLab adapter** — Same interface, wrapping `glab` CLI +- **Bitbucket adapter** — Same interface, wrapping Bitbucket APIs +- **REST API fallback** — Direct API calls when CLI tools aren't available +- **Token-based auth** — Support PAT/token auth for CI environments +- **Pipelines abstraction** — Normalize GitHub Actions and Azure Pipelines +- **Board view** — Normalize GitHub Projects and ADO Boards diff --git a/package.json b/package.json index 4aaf04aa..3555689f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bradygaster/squad", - "version": "0.8.21-preview.1", + "version": "0.8.21-preview.4", "private": true, "description": "Squad — Programmable multi-agent runtime for GitHub Copilot, built on @github/copilot-sdk", "type": "module", diff --git a/packages/squad-cli/package.json b/packages/squad-cli/package.json index 569299fc..df55c88a 100644 --- a/packages/squad-cli/package.json +++ b/packages/squad-cli/package.json @@ -1,6 +1,6 @@ { "name": "@bradygaster/squad-cli", - "version": "0.8.21-preview.1", + "version": "0.8.21-preview.4", "description": "Squad CLI — Command-line interface for the Squad multi-agent runtime", "type": "module", "bin": { diff --git a/packages/squad-cli/src/cli-entry.ts b/packages/squad-cli/src/cli-entry.ts index e0989212..1be85c15 100644 --- a/packages/squad-cli/src/cli-entry.ts +++ b/packages/squad-cli/src/cli-entry.ts @@ -33,27 +33,42 @@ async function main(): Promise { // --help / -h / help if (cmd === '--help' || cmd === '-h' || cmd === 'help') { console.log(`\n${BOLD}squad${RESET} v${VERSION} — Add an AI agent team to any project\n`); - console.log(`Usage: squad [command] [options]\n`); - console.log(`Commands:`); - console.log(` ${BOLD}(default)${RESET} Launch interactive shell (no args)`); - console.log(` Flags: --global (init in personal squad directory)`); + console.log(`Usage: squad [command] [options]`); + console.log(` squad --help\n`); + console.log(`Getting Started:`); console.log(` ${BOLD}init${RESET} Initialize Squad (skip files that already exist)`); console.log(` Flags: --global (init in personal squad directory)`); - console.log(` ${BOLD}upgrade${RESET} Update Squad-owned files to latest version`); - console.log(` Overwrites: squad.agent.md, templates dir (.squad/templates/)`); - console.log(` Never touches: .squad/ or .ai-team/ (your team state)`); - console.log(` Flags: --global (upgrade personal squad), --migrate-directory (rename .ai-team/ → .squad/)`); console.log(` ${BOLD}status${RESET} Show which squad is active and why`); + console.log(` ${BOLD}doctor${RESET} Validate squad setup (check files, config, health)`); + console.log(` ${BOLD}help${RESET} Show this help message`); + console.log(`\nDevelopment:`); + console.log(` ${BOLD}(default)${RESET} Launch interactive shell (no args)`); + console.log(` Flags: --global (init in personal squad directory)`); + console.log(` ${BOLD}consult${RESET} Enter consult mode with your personal squad`); console.log(` ${BOLD}triage${RESET} Scan for work and categorize issues`); console.log(` Usage: triage [--interval ]`); console.log(` Default: checks every 10 minutes (Ctrl+C to stop)`); console.log(` ${BOLD}loop${RESET} Continuous work loop (Ralph mode)`); console.log(` Usage: loop [--filter