Skip to content
Development
Command

/ado-clone

Clone Azure DevOps repositories to local workspace. Use after init if cloning was skipped, or to add repos later.

From plugin
specweave
15673 skills20 agents73 commands
Install
> /plugin marketplace add anton-abyzov/specweave
> /plugin install sw@specweave

How it fires

How this command gets triggered: by you, by Claude, or both.

  • Fires itselfClaude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/ado-clone

Context preview

What this command does when you run it.

Clone Azure DevOps repositories to local workspace. Use after init if cloning was skipped, or to add repos later.

Command definition

ado-clone.md
description: Clone Azure DevOps repositories to local workspace. Use after init if cloning was skipped, or to add repos later.

Clone Azure DevOps Repositories Command

You are an Azure DevOps repository cloning expert. Help users clone repositories from ADO projects to their local workspace.

Purpose

This command clones Azure DevOps repositories **after** initial SpecWeave setup (`specweave init`). Use when:

  • User skipped cloning during init
  • Adding repositories from additional projects
  • Re-cloning after cleanup
  • Selective cloning with pattern filtering

Command Syntax

# Interactive mode (prompts for everything)
sw-ado:clone-repos

# With pattern filter
sw-ado:clone-repos --pattern "sw-*"

# Regex pattern
sw-ado:clone-repos --pattern "regex:^api-.*$"

# Specific project only
sw-ado:clone-repos --project "MyProject"

# Dry-run (preview only)
sw-ado:clone-repos --dry-run

Your Task

When the user runs this command:

Step 1: Check Prerequisites

import { readEnvFile, parseEnvFile } from '../../../src/utils/env-file.js';
import chalk from 'chalk';

const projectPath = process.cwd();
const envContent = readEnvFile(projectPath);

if (!envContent) {
  console.log(chalk.red('❌ No .env file found. Run `specweave init` first.'));
  return;
}

const parsed = parseEnvFile(envContent);

if (!parsed.AZURE_DEVOPS_PAT || !parsed.AZURE_DEVOPS_ORG) {
  console.log(chalk.red('❌ Missing Azure DevOps credentials.'));
  console.log(chalk.gray('   Run `specweave init` with Azure DevOps provider.'));
  return;
}

const org = parsed.AZURE_DEVOPS_ORG;
const pat = parsed.AZURE_DEVOPS_PAT;

console.log(chalk.blue('\n📦 ADO Repository Cloning\n'));
console.log(chalk.gray(`   Organization: ${org}`));

Step 2: Get Project Selection

import { AzureDevOpsProvider } from '../../../src/core/repo-structure/providers/azure-devops-provider.js';

const provider = new AzureDevOpsProvider();

// If project specified via CLI, use it
let selectedProjects: string[] = [];

if (args.project) {
  selectedProjects = [args.project];
  console.log(chalk.gray(`   Project: ${args.project} (from CLI)`));
} else {
  // Fetch available projects
  console.log(chalk.gray('\n   Fetching projects...'));

  const response = await fetch(
    `https://dev.azure.com/${org}/_apis/projects?api-version=7.0`,
    {
      headers: {
        'Authorization': `Basic ${Buffer.from(':' + pat).toString('base64')}`,
        'Accept': 'application/json'
      }
    }
  );

  if (!response.ok) {
    console.log(chalk.red(`❌ Failed to fetch projects: ${response.status}`));
    return;
  }

  const data = await response.json();
  const projects = data.value || [];

  if (projects.length === 0) {
    console.log(chalk.yellow('⚠️  No projects found in organization.'));
    return;
  }

  // Prompt for project selection
  const { checkbox } = await import('@inquirer/prompts');

  selectedProjects = await checkbox({
    message: 'Select project(s) to clone repositories from:',
    choices: projects.map(p => ({ name: p.name, value: p.name })),
    required: true
  });

  console.log(chalk.green(`   ✓ ${selectedProjects.length} project(s) selected`));
}

Step 3: Fetch Repositories

const allRepos = [];

for (const project of selectedProjects) {
  console.log(chalk.gray(`\n   Fetching repos from ${project}...`));

  try {
    const repos = await provider.listRepositories(org, project, pat);
    const reposWithProject = repos.map(r => ({ ...r, project }));
    allRepos.push(...reposWithProject);
    console.log(chalk.green(`   ✓ Found ${repos.length} repositories`));
  } catch (error) {
    console.log(chalk.yellow(`   ⚠️ Failed: ${error.message}`));
  }
}

if (allRepos.length === 0) {
  console.log(chalk.yellow('\n⚠️  No repositories found.'));
  return;
}

console.log(chalk.blue(`\n📋 Total: ${allRepos.length} repositories available\n`));

Step 4: Apply Pattern Filter

import { filterRepositoriesByPattern } from '../../../src/cli/helpers/selection-strategy.js';

let filteredRepos = allRepos;
let patternDescription = 'all';

if (args.pattern) {
  // Determine pattern type
  const isRegex = args.pattern.startsWith('regex:');
  const pattern = isRegex ? args.pattern.slice(6) : args.pattern;

  const clonePattern = {
    strategy: isRegex ? 'pattern-regex' : 'pattern-glob',
    pattern: pattern,
    isRegex
  };

  filteredRepos = filterRepositoriesByPattern(allRepos, clonePattern);
  patternDescription = `matching "${pattern}"`;

  console.log(chalk.gray(`   Pattern: ${args.pattern}`));
  console.log(chalk.gray(`   Matched: ${filteredRepos.length} of ${allRepos.length} repos\n`));
} else {
  // Prompt for pattern selection
  const { select, input } = await import('@inquirer/prompts');

  const strategy = await select({
    message: 'How do you want to select repositories?',
    choices: [
      { name: 'All - Clone all repositories', value: 'all' },
      { name: 'Pattern (glob) - e.g., "sw-*", "*-backend"', value: 'pattern-glob' },
      { name: 'Pattern (regex) - e.g., "^api-.*$"', value: 'pattern-regex' }
    ]
  });

  if (strategy !== 'all') {
    const pattern = await input({
      message: 'Enter pattern:',
      validate: v => v.trim() ? true : 'Pattern required'
    });

    const clonePattern = {
      strategy,
      pattern: pattern.trim(),
      isRegex: strategy === 'pattern-regex'
    };

    filteredRepos = filterRepositoriesByPattern(allRepos, clonePattern);
    patternDescription = `matching "${pattern.trim()}"`;
  }
}

if (filteredRepos.length === 0) {
  console.log(chalk.yellow(`⚠️  No repositories ${patternDescription}.`));
  return;
}

Step 5: Preview and Confirm

console.log(chalk.blue(`\n📦 Repositories to clone (${filteredRepos.length}):\n`));

// Show preview (max 20)
filteredRepos.slice(0, 20).forEach(repo => {
  console.log(chalk.gray(`   • ${repo.name} (${repo.project})`));
});

if (filteredRepos.length
Read more
Ships withspecweave

Spec-first AI development: describe a feature → AI creates spec + plan + tasks, builds autonomously, syncs to GitHub/JIRA. Domain-expert skills for PM, Architect, Frontend, QA learn your patterns permanently. Claude Code, Codex, Cursor, Copilot & more.

Get the whole plugin