Skip to content
Merged
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
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ bunx allagents
allagents workspace init my-workspace
cd my-workspace

# Or initialize from a remote GitHub template
allagents workspace init my-workspace --from owner/repo/path/to/template

# Add a marketplace (or let auto-registration handle it)
allagents plugin marketplace add anthropics/claude-plugins-official

Expand All @@ -71,13 +74,31 @@ allagents workspace plugin add my-plugin@someuser/their-repo
allagents workspace sync
```

### Initialize from Remote Template

Start a new workspace instantly from any GitHub repository containing a `workspace.yaml`:

```bash
# From GitHub URL
allagents workspace init ~/my-project --from https://github.com/myorg/templates/tree/main/nodejs

# From shorthand
allagents workspace init ~/my-project --from myorg/templates/nodejs

# From repo root (looks for .allagents/workspace.yaml or workspace.yaml)
allagents workspace init ~/my-project --from myorg/templates
```

This fetches the workspace configuration directly from GitHub - no cloning required.

## Commands

### Workspace Commands

```bash
# Initialize a new workspace from template
allagents workspace init <path>
allagents workspace init <path> --from <source> # From local path or GitHub URL

# Sync all plugins to workspace (non-destructive)
allagents workspace sync [options]
Expand Down
14 changes: 14 additions & 0 deletions docs/src/content/docs/getting-started/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ allagents workspace init my-workspace
cd my-workspace
```

### Or Start from a Remote Template

Initialize directly from any GitHub repository - no cloning required:

```bash
# From a GitHub URL
allagents workspace init my-workspace --from https://github.com/myorg/templates/tree/main/nodejs

# Or using shorthand
allagents workspace init my-workspace --from myorg/templates/nodejs
```

This fetches the workspace configuration directly from GitHub and sets up your workspace instantly.

## Add Plugins

```bash
Expand Down
19 changes: 18 additions & 1 deletion docs/src/content/docs/guides/workspaces.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,27 @@ If your template only has `AGENTS.md` and `claude` is in your clients list, AllA
# Create from default template
allagents workspace init my-workspace

# Create from existing template
# Create from local template
allagents workspace init my-workspace --from ./path/to/template

# Create from remote GitHub template
allagents workspace init my-workspace --from https://github.com/myorg/templates/tree/main/nodejs
allagents workspace init my-workspace --from myorg/templates/nodejs # shorthand
```

### Remote Templates

You can initialize a workspace directly from any GitHub repository containing a `workspace.yaml` file. AllAgents will:

1. Fetch the `workspace.yaml` from `.allagents/workspace.yaml` or `workspace.yaml` in the target path
2. Convert relative `workspace.source` paths to GitHub URLs so sync works
3. Copy any `AGENTS.md` and `CLAUDE.md` files from the template source

Supported formats:
- Full URL: `https://github.com/owner/repo/tree/branch/path`
- GitHub shorthand: `owner/repo/path`
- Simple repo: `owner/repo` (looks for workspace.yaml in root)

## workspace.yaml

```yaml
Expand Down
17 changes: 16 additions & 1 deletion docs/src/content/docs/reference/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,28 @@ description: Complete reference for AllAgents CLI commands.
## Workspace Commands

```bash
allagents workspace init <path>
allagents workspace init <path> [--from <source>]
allagents workspace sync [--force] [--dry-run]
allagents workspace status
allagents workspace plugin add <plugin@marketplace>
allagents workspace plugin remove <plugin>
```

### workspace init

Initialize a new workspace from a template:

| Flag | Description |
|------|-------------|
| `--from <source>` | Copy workspace.yaml from local path or GitHub URL |

**Source formats:**
- Local path: `./path/to/template` or `/absolute/path`
- GitHub URL: `https://github.com/owner/repo/tree/branch/path`
- GitHub shorthand: `owner/repo/path` or `owner/repo`

When using a GitHub source, AllAgents fetches `workspace.yaml` from `.allagents/workspace.yaml` or `workspace.yaml` in the target path.

### workspace sync

Syncs plugins to the workspace using non-destructive sync:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"docs:install": "bun install --cwd docs",
"docs:build": "bun run --cwd docs build",
"dev": "bun run src/cli/index.ts",
"test": "bun test tests/unit/validators tests/unit/utils tests/unit/models && bun test tests/unit/core/marketplace.test.ts tests/unit/core/sync.test.ts tests/unit/core/transform-glob.test.ts && bun test tests/unit/core/plugin.test.ts",
"test": "bun test tests/unit/validators tests/unit/utils tests/unit/models && bun test tests/unit/core/marketplace.test.ts tests/unit/core/sync.test.ts tests/unit/core/transform-glob.test.ts tests/unit/core/github-fetch.test.ts && bun test tests/unit/core/plugin.test.ts",
"test:watch": "bun test --watch",
"test:coverage": "bun test --coverage",
"test:integration": "bats tests/integration/*.bats",
Expand Down
144 changes: 144 additions & 0 deletions src/core/github-fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { execa } from 'execa';
import { parseGitHubUrl } from '../utils/plugin-path.js';
import { CONFIG_DIR, WORKSPACE_CONFIG_FILE } from '../constants.js';

/**
* Result of fetching workspace from GitHub
*/
export interface FetchWorkspaceResult {
success: boolean;
content?: string;
error?: string;
}

/**
* Fetch a single file from GitHub using the gh CLI
* @param owner - Repository owner
* @param repo - Repository name
* @param path - File path within the repository
* @returns File content or null if not found
*/
async function fetchFileFromGitHub(
owner: string,
repo: string,
path: string,
): Promise<string | null> {
try {
// Use gh api to fetch file contents
// The API returns base64 encoded content
const result = await execa('gh', [
'api',
`repos/${owner}/${repo}/contents/${path}`,
'--jq',
'.content',
]);

if (result.stdout) {
// Decode base64 content
const content = Buffer.from(result.stdout, 'base64').toString('utf-8');
return content;
}
return null;
} catch {
return null;
}
}

/**
* Fetch workspace.yaml from a GitHub URL
*
* Supports:
* - https://github.com/owner/repo (looks for .allagents/workspace.yaml or workspace.yaml)
* - https://github.com/owner/repo/tree/branch/path (looks in path/.allagents/workspace.yaml or path/workspace.yaml)
* - owner/repo (shorthand)
* - owner/repo/path/to/workspace (shorthand with subpath)
*
* @param url - GitHub URL or shorthand
* @returns Result with workspace.yaml content or error
*/
export async function fetchWorkspaceFromGitHub(
url: string,
): Promise<FetchWorkspaceResult> {
const parsed = parseGitHubUrl(url);
if (!parsed) {
return {
success: false,
error: 'Invalid GitHub URL format. Expected: https://github.com/owner/repo',
};
}

const { owner, repo, subpath } = parsed;

// Check if gh CLI is available
try {
await execa('gh', ['--version']);
} catch {
return {
success: false,
error: 'gh CLI not installed. Install from: https://cli.github.com',
};
}

// Check if repository exists
try {
await execa('gh', ['repo', 'view', `${owner}/${repo}`, '--json', 'name']);
} catch (error) {
if (error instanceof Error) {
const errorMessage = error.message.toLowerCase();
if (
errorMessage.includes('not found') ||
errorMessage.includes('404') ||
errorMessage.includes('could not resolve to a repository')
) {
return {
success: false,
error: `Repository not found: ${owner}/${repo}`,
};
}
if (
errorMessage.includes('auth') ||
errorMessage.includes('authentication')
) {
return {
success: false,
error: 'GitHub authentication required. Run: gh auth login',
};
}
}
return {
success: false,
error: `Failed to access repository: ${error instanceof Error ? error.message : String(error)}`,
};
}

// Determine the base path to look for workspace.yaml
const basePath = subpath || '';

// Try to find workspace.yaml in order of preference:
// 1. {basePath}/.allagents/workspace.yaml
// 2. {basePath}/workspace.yaml
const pathsToTry = basePath
? [
`${basePath}/${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE}`,
`${basePath}/${WORKSPACE_CONFIG_FILE}`,
]
: [
`${CONFIG_DIR}/${WORKSPACE_CONFIG_FILE}`,
WORKSPACE_CONFIG_FILE,
];

for (const path of pathsToTry) {
const content = await fetchFileFromGitHub(owner, repo, path);
if (content) {
return {
success: true,
content,
};
}
}

return {
success: false,
error: `No workspace.yaml found in: ${owner}/${repo}${subpath ? `/${subpath}` : ''}\n Expected at: ${pathsToTry.join(' or ')}`,
};
}
Loading