-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Summary
Add support for multiple source control systems in atomic, starting with Sapling as an alternative to the current GitHub-only implementation. The architecture should be extensible to support additional providers in the future.
Motivation
Currently, atomic is tightly coupled to GitHub/Git:
- All commands in
.claude/commands/hardcodegitandghCLI commands - GitHub Actions workflows are bound to GitHub-specific events
- No abstraction exists for source control operations
Many developers use alternative source control systems, particularly Sapling (developed by Meta), which offers:
- Stack-based workflows with automatic rebasing
- Visual smartlog for commit management
- Native GitHub PR integration via
sl pr submit
Supporting multiple providers will expand atomic's user base and provide a foundation for future integrations.
Proposed Solution
Architecture Overview
atomic/
├── .atomic/
│ └── config.yaml # Project-level configuration (NEW)
├── src/
│ └── providers/ # Provider system (NEW)
│ ├── index.ts # Provider registry & loader
│ ├── provider.ts # SourceControlProvider interface
│ ├── github.ts # GitHub provider implementation
│ └── sapling.ts # Sapling provider implementation
└── .claude/
└── commands/
├── commit.md # Updated with provider variables
└── create-pr.md # Renamed from create-gh-pr.md
Technical Design
1. Provider Interface
// src/providers/provider.ts
export interface SourceControlProvider {
/** Provider identifier */
name: 'github' | 'sapling';
/** Display name for UI */
displayName: string;
/** Primary CLI tool */
cli: 'git' | 'sl';
/** Command mappings for templates */
commands: {
// Status & info
status: string;
log: string;
diff: string;
branch: string;
// Staging & committing
add: string;
commit: string;
amend: string;
// Remote operations
push: string;
pull: string;
// PR/code review
createPR: string;
listPRs: string;
viewPR: string;
};
/** Allowed tool patterns for YAML frontmatter */
allowedTools: string[];
/** Prerequisites check */
checkPrerequisites(): Promise<PrerequisiteResult>;
}
export interface PrerequisiteResult {
satisfied: boolean;
missing: string[];
installInstructions: Record<string, string>;
}2. Provider Implementations
GitHub Provider
// src/providers/github.ts
export const GitHubProvider: SourceControlProvider = {
name: 'github',
displayName: 'GitHub (Git)',
cli: 'git',
commands: {
status: 'git status --porcelain',
log: 'git log --oneline',
diff: 'git diff',
branch: 'git branch --show-current',
add: 'git add',
commit: 'git commit',
amend: 'git commit --amend',
push: 'git push',
pull: 'git pull',
createPR: 'gh pr create',
listPRs: 'gh pr list',
viewPR: 'gh pr view',
},
allowedTools: [
'Bash(git add:*)',
'Bash(git status:*)',
'Bash(git commit:*)',
'Bash(git diff:*)',
'Bash(git log:*)',
'Bash(git push:*)',
'Bash(git pull:*)',
'Bash(gh pr:*)',
'Bash(gh issue:*)',
],
async checkPrerequisites() {
const missing: string[] = [];
if (!await commandExists('git')) missing.push('git');
if (!await commandExists('gh')) missing.push('gh');
return {
satisfied: missing.length === 0,
missing,
installInstructions: {
git: 'https://git-scm.com/downloads',
gh: 'brew install gh # or: https://cli.github.com/',
},
};
},
};Sapling Provider
// src/providers/sapling.ts
export interface SaplingProviderOptions {
prWorkflow: 'stack' | 'branch';
}
export const SaplingProvider: SourceControlProvider = {
name: 'sapling',
displayName: 'Sapling',
cli: 'sl',
commands: {
status: 'sl status',
log: 'sl log --template "{node|short} {desc|firstline}\\n"',
diff: 'sl diff',
branch: 'sl log -r . --template "{bookmarks}"',
add: 'sl add',
commit: 'sl commit',
amend: 'sl amend',
push: 'sl push --to', // or 'sl pr submit' based on prWorkflow
pull: 'sl pull',
createPR: 'sl pr submit', // Creates stacked PRs
listPRs: 'sl ssl', // Smartlog with PR status
viewPR: 'sl pr',
},
allowedTools: [
'Bash(sl add:*)',
'Bash(sl status:*)',
'Bash(sl commit:*)',
'Bash(sl diff:*)',
'Bash(sl log:*)',
'Bash(sl push:*)',
'Bash(sl pull:*)',
'Bash(sl pr:*)',
'Bash(sl amend:*)',
'Bash(sl goto:*)',
'Bash(sl next:*)',
'Bash(sl prev:*)',
'Bash(gh:*)', // Sapling uses gh for GitHub auth
],
async checkPrerequisites() {
const missing: string[] = [];
if (!await commandExists('sl')) missing.push('sl');
if (!await commandExists('gh')) missing.push('gh'); // Required for GitHub integration
return {
satisfied: missing.length === 0,
missing,
installInstructions: {
sl: 'brew install sapling # or: https://sapling-scm.com/docs/install',
gh: 'brew install gh # Required for GitHub PR integration',
},
};
},
};3. Configuration Schema
File: .atomic/config.yaml
# yaml-language-server: $schema=https://atomic.dev/schema/config.json
version: 1
sourceControl:
# Required: explicitly set by user during 'atomic init'
provider: sapling # 'github' | 'sapling'
# Provider-specific options
sapling:
# How to create PRs
# - 'stack': Uses 'sl pr submit --stack' (one PR per commit)
# - 'branch': Uses 'sl push --to' (traditional branch-based PR)
prWorkflow: stack
github:
# Future: GitHub-specific options (e.g., default base branch)JSON Schema for Validation
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["version", "sourceControl"],
"properties": {
"version": {
"type": "integer",
"const": 1
},
"sourceControl": {
"type": "object",
"required": ["provider"],
"properties": {
"provider": {
"type": "string",
"enum": ["github", "sapling"],
"description": "Source control provider to use"
},
"sapling": {
"type": "object",
"properties": {
"prWorkflow": {
"type": "string",
"enum": ["stack", "branch"],
"default": "stack",
"description": "PR creation strategy"
}
}
},
"github": {
"type": "object",
"properties": {}
}
}
}
}
}4. Template Variable System
Update Markdown command files to use provider-agnostic variables:
Syntax: ${{ provider.<path> }}
Example: .claude/commands/commit.md
---
allowed-tools:
- ${{ provider.allowedTools }}
---
# Commit Changes
Execute the following steps to create a well-formatted commit.
## 1. Check Repository Status
Run status command to see changes:
\`\`\`bash
${{ provider.commands.status }}
\`\`\`
## 2. Review Changes
View the diff:
\`\`\`bash
${{ provider.commands.diff }}
\`\`\`
## 3. Stage and Commit
Stage specific files (prefer explicit file names over -A):
\`\`\`bash
${{ provider.commands.add }} <files>
\`\`\`
Create the commit:
\`\`\`bash
${{ provider.commands.commit }} -m "$(cat <<'EOF'
<commit message>
EOF
)"
\`\`\`Resolution Logic
// src/template/resolver.ts
export async function resolveTemplate(
content: string,
config: AtomicConfig
): Promise<string> {
const provider = getProvider(config.sourceControl.provider);
// Replace ${{ provider.* }} patterns
return content.replace(
/\$\{\{\s*provider\.([a-zA-Z.]+)\s*\}\}/g,
(match, path) => {
const value = getNestedValue(provider, path);
if (Array.isArray(value)) {
// For allowedTools array, format as YAML list
return value.map(t => `- ${t}`).join('\n ');
}
return String(value);
}
);
}User Onboarding Flow
atomic init Interactive Wizard
$ atomic init
┌─────────────────────────────────────────────────────────┐
│ │
│ Welcome to Atomic Configuration │
│ │
└─────────────────────────────────────────────────────────┘
? Select your source control provider:
› GitHub (Git)
Standard Git workflow with GitHub CLI integration.
Uses: git, gh
Sapling
Stack-based workflow with smartlog visualization.
Uses: sl, gh
[Use arrow keys to navigate, Enter to select]
If Sapling selected:
? Select your preferred PR workflow:
› Stack-based (Recommended)
Creates one PR per commit using 'sl pr submit --stack'.
Best viewed with ReviewStack (reviewstack.dev).
Branch-based
Traditional workflow using 'sl push --to <branch>'.
Creates single PR from branch.
[Use arrow keys to navigate, Enter to select]
Prerequisite Check:
Checking prerequisites...
✓ sl (Sapling CLI) found
✓ gh (GitHub CLI) found
✓ gh authenticated
Or if missing:
Checking prerequisites...
✗ sl (Sapling CLI) not found
✓ gh (GitHub CLI) found
Missing prerequisites must be installed:
Sapling:
macOS: brew install sapling
Linux: https://sapling-scm.com/docs/install
Windows: https://sapling-scm.com/docs/install
After installing, run 'atomic init' again.
Success Output:
✓ Created .atomic/config.yaml
✓ Provider set to: Sapling (stack-based workflow)
Configuration saved. Your agent commands will now use Sapling.
Next steps:
1. Run 'atomic sync' to update agent configurations
2. Verify with 'sl' to see your smartlog
Documentation:
• Sapling basics: https://sapling-scm.com/docs/introduction/
• Stacked PRs: https://sapling-scm.com/docs/git/github/
Non-Interactive Mode
# For CI/automation
atomic init --provider=sapling --sapling-pr-workflow=stack
# Show current configuration
atomic config show
# Update specific setting
atomic config set sourceControl.provider github
atomic config set sourceControl.sapling.prWorkflow branchError Handling
No Configuration Found
Error: No source control provider configured
This project hasn't been set up with atomic yet.
Run 'atomic init' to configure your source control provider:
$ atomic init
Or specify a provider directly:
$ atomic init --provider=github
$ atomic init --provider=sapling
Provider CLI Not Found
Error: Sapling CLI 'sl' not found in PATH
Your project is configured to use Sapling, but the CLI is not installed.
Install Sapling:
macOS: brew install sapling
Linux: See https://sapling-scm.com/docs/install
Windows: See https://sapling-scm.com/docs/install
Or switch to GitHub provider:
$ atomic config set sourceControl.provider github
Invalid Configuration
Error: Invalid configuration in .atomic/config.yaml
Line 5: Unknown provider 'gitlab'
Valid providers: github, sapling
Run 'atomic init' to reconfigure, or edit .atomic/config.yaml manually.
Migration Guide
For Existing Users
Users with existing atomic setups need to:
- Run
atomic initto create.atomic/config.yaml - Select their provider (GitHub for existing behavior)
- Run
atomic syncto update command files
Backward Compatibility
- If no
.atomic/config.yamlexists and user runs a command, show helpful error directing toatomic init - The
atomic synccommand will regenerate command files with provider variables resolved
Implementation Phases
Phase 1: Configuration Foundation
- Create
.atomic/config.yamlschema and TypeScript types - Implement
atomic initcommand with interactive wizard - Implement
atomic config showandatomic config setcommands - Add JSON Schema for editor autocomplete
Phase 2: Provider Abstraction
- Define
SourceControlProviderinterface - Implement provider registry and loader
- Implement
GitHubProviderwith current command mappings - Add prerequisite checking system
Phase 3: Sapling Provider
- Implement
SaplingProviderwith command mappings - Add Sapling-specific options (prWorkflow)
- Test with real Sapling repositories
- Document Sapling-specific workflows
Phase 4: Template System
- Implement
${{ provider.* }}variable resolution - Update
.claude/commands/commit.mdwith variables - Update
.claude/commands/create-gh-pr.md→create-pr.md - Update other command files as needed
- Add
atomic synccommand to regenerate from templates
Phase 5: Polish & Documentation
- Comprehensive error messages for all failure modes
- Update README with multi-provider documentation
- Add troubleshooting guide
- Add provider comparison documentation
Acceptance Criteria
Functional Requirements
- User can run
atomic initand select from available providers via interactive prompt - User can run
atomic init --provider=<name>for non-interactive setup - Configuration is persisted in
.atomic/config.yaml - Commands in
.claude/commands/use provider-appropriate CLI commands - Sapling users can create stacked PRs with
sl pr submit - GitHub users experience no change from current behavior
- Missing prerequisites are detected with clear installation instructions
Non-Functional Requirements
- Adding a new provider requires only implementing the
SourceControlProviderinterface - Provider selection is explicit (no auto-detection)
- Configuration is project-level (per-repository)
- Error messages are actionable and include next steps
Sapling Command Reference
For implementers, here's a mapping of key operations:
| Operation | Git/GitHub | Sapling |
|---|---|---|
| Check status | git status |
sl status |
| View log | git log --oneline |
sl log or sl (smartlog) |
| Show diff | git diff |
sl diff |
| Stage files | git add <files> |
sl add <files> |
| Commit | git commit -m "msg" |
sl commit -m "msg" |
| Amend commit | git commit --amend |
sl amend |
| Push | git push |
sl push --to <branch> |
| Pull | git pull |
sl pull (fetch only, use sl goto to update) |
| Create PR | gh pr create |
sl pr submit or sl pr submit --stack |
| View PRs | gh pr list |
sl ssl (smartlog with PR status) |
| Navigate stack | N/A | sl next / sl prev |
| Interactive UI | N/A | sl web (launches ISL) |
Open Questions
-
Workflow files: Should
.github/workflows/also be provider-aware, or remain GitHub-specific? (Recommendation: Keep GitHub-specific, as they only run on GitHub) -
Agent config: Should
src/config.tsAgentConfig interface be extended to include provider info? -
Scope of
atomic sync: Should it also update.github/skills/or just.claude/commands/?