Skip to content

feat(init): add SCM selection with Sapling and GitHub support#196

Open
flora131 wants to merge 11 commits intomainfrom
flora131/feature/sapling-integration
Open

feat(init): add SCM selection with Sapling and GitHub support#196
flora131 wants to merge 11 commits intomainfrom
flora131/feature/sapling-integration

Conversation

@flora131
Copy link
Owner

@flora131 flora131 commented Feb 13, 2026

Summary

Adds source control management (SCM) selection to the init flow, enabling users to choose between GitHub/Git and Sapling/Phabricator workflows. The selected SCM type is persisted in .atomic.json and configures SCM-specific commands throughout the application.

Key Changes

Core Features

  • SCM selection prompt during atomic init with support for GitHub/Git and Sapling/Phabricator
  • CLI flag validation for --scm parameter on init command
  • Configuration persistence via new .atomic.json file in project root
  • SCM-aware workflow prompts in ralph that adapt to the selected SCM type

Implementation Details

  • Added SCM_CONFIG in src/config.ts with metadata for each SCM type (display name, CLI tools, review systems)
  • Created src/utils/atomic-config.ts for reading/writing .atomic.json configuration
  • Enhanced src/commands/init.ts to:
    • Prompt for SCM selection (or accept via --scm flag)
    • Copy SCM-specific command templates based on selection
    • Handle Windows-specific Sapling templates (avoids PowerShell sl alias conflict)
    • Validate .arcconfig presence for Phabricator workflows
  • Updated ralph workflow (src/graph/nodes/ralph-nodes.ts) to use SCM-appropriate:
    • History commands (git log vs sl smartlog)
    • Commit command references (/commit with appropriate tool)
  • Modified src/ui/commands/workflow-commands.ts to read SCM config and pass to prompts

Templates

Added SCM-specific command templates for all three agents (Claude, OpenCode, Copilot):

GitHub/Git:

  • /commit → uses git commit
  • /create-gh-pr → uses gh pr create

Sapling/Phabricator:

  • /commit → uses sl commit
  • /submit-diff → uses jf submit for Phabricator diffs

Windows variant: Sapling templates use full path & 'C:\Program Files\Sapling\sl.exe' to avoid PowerShell alias conflicts

Documentation

  • Updated README.md with SCM selection guide, comparison table, and setup instructions
  • Added .atomic.json configuration reference
  • Included Windows-specific notes for Sapling users

Test Coverage

  • CLI flag validation tests for --scm parameter
  • Init command tests for SCM selection flow
  • Atomic config read/write tests
  • Ralph workflow SCM-aware prompt tests
  • Workflow command integration tests

Configuration File

The new .atomic.json file stores:

{
  "version": 1,
  "agent": "claude",
  "scm": "github",
  "lastUpdated": "2026-02-13T..."
}

Breaking Changes

None. Existing projects without .atomic.json default to GitHub/Git workflow.

Add planning documents for extending the init flow to support
source control type selection (GitHub/Git and Sapling+Phabricator):

- Research doc analyzing current command architecture and SCM usage
- Technical design spec with detailed implementation plan
- Sapling SCM reference guide with Git command mappings

Assistant-model: Claude Code
Add support for multiple source control systems (GitHub/Git and
Sapling+Phabricator) as part of the source control type selection feature.

Changes:
- Add SourceControlType union type for supported SCM systems
- Add ScmConfig interface with fields for CLI tools, review systems, etc.
- Add SCM_CONFIG constant with GitHub and Sapling+Phabricator configurations
- Add helper functions: getScmKeys(), isValidScm(), getScmConfig()
- Add comprehensive unit tests for all SCM configuration functions

This lays the foundation for SCM-aware command file copying during
atomic init, enabling users to select their preferred source control
workflow.

Assistant-model: Claude Code
Update the technical design document with refined implementation details
and clarifications for the SCM type selection feature.

Assistant-model: Claude Code
Add utilities for persisting project configuration in .atomic.json file.
This enables storing the user's SCM selection for use by Ralph workflow
and future re-initialization.

Changes:
- Add AtomicConfig interface with version, agent, scm, and lastUpdated fields
- Add readAtomicConfig() to read config from project directory
- Add saveAtomicConfig() to merge updates with existing config
- Add getSelectedScm() convenience function for SCM lookup
- Add comprehensive unit tests with temp directory isolation

The .atomic.json file will store project-level settings after init,
allowing SCM-aware features to determine the user's source control choice.

Assistant-model: Claude Code
Add template directory structure with command files for GitHub and
Sapling+Phabricator source control systems.

Templates added:
- templates/scm/github/ - Git/GitHub commands (commit.md, create-gh-pr.md)
- templates/scm/sapling-phabricator/ - Sapling commands (commit.md, submit-diff.md)
- templates/scm/sapling-phabricator-windows/ - Windows-specific Sapling templates
  with full sl.exe path to avoid PowerShell alias conflict

Each SCM template includes:
- .claude/commands/ - Claude Code command files
- .opencode/command/ - OpenCode command files
- .github/skills/ - GitHub Copilot skill files with full content

The init flow will copy appropriate templates based on user's SCM selection.

Assistant-model: Claude Code
Extend the atomic init command to support source control system selection.
Users can now choose between GitHub/Git and Sapling+Phabricator workflows.

Changes:
- Add preSelectedScm option to InitOptions interface
- Add getScmTemplatePath() to resolve OS-specific templates (Windows handling)
- Add getCommandsSubfolder() to map agent types to command folder names
- Add copyScmCommands() to copy SCM-specific command files
- Add SCM selection prompt after agent selection in init flow
- Save SCM selection to .atomic.json for future reference
- Add Phabricator .arcconfig validation warning when Sapling is selected
- Add -s/--scm CLI flag for non-interactive SCM selection

The init flow now:
1. Selects agent (claude/opencode/copilot)
2. Selects SCM (github/sapling-phabricator)
3. Copies agent config files
4. Copies SCM-specific command files
5. Saves selection to .atomic.json

Assistant-model: Claude Code
Parameterize buildImplementFeaturePrompt with SourceControlType so
ralph uses the correct history and commit commands for the selected
SCM (git log vs sl smartlog, /commit with git vs sl).

workflow-commands reads the SCM choice from .atomic.json at runtime.

Assistant-model: Claude Code
Add isValidScm guard in the init action so invalid --scm values are
rejected with a clear error before the init flow starts. Includes
SCM validation tests and comprehensive init SCM flow tests.

Assistant-model: Claude Code
Document the source control selection flow during atomic init,
Sapling + Phabricator setup steps, and the .atomic.json config
file schema.

Assistant-model: Claude Code
Copilot AI review requested due to automatic review settings February 13, 2026 05:13
@claude claude bot changed the title feat: add Sapling SCM integration with SCM selection flow feat(init): add SCM selection with Sapling and GitHub support Feb 13, 2026
@claude
Copy link

claude bot commented Feb 13, 2026

PR Review: feat: add Sapling SCM integration with SCM selection flow

Overall Assessment

This is a well-structured PR that adds Sapling SCM (Source Control Management) integration with a clean selection flow during atomic init. The implementation follows good patterns established in the codebase and includes comprehensive test coverage.


✅ Strengths

1. Clean Architecture

  • The SCM configuration follows the same pattern as agent configuration (AGENT_CONFIGSCM_CONFIG)
  • Good separation of concerns with atomic-config.ts handling persistence
  • Template directory structure is well-organized (templates/scm/github/, templates/scm/sapling-phabricator/)

2. Comprehensive Test Coverage

  • Tests cover SCM validation, config persistence, init flow logic, and workflow integration
  • Good edge case coverage (empty files, invalid inputs, Windows-specific paths)
  • Tests in tests/config.test.ts, tests/init.test.ts, tests/utils/atomic-config.test.ts, and workflow tests

3. Windows Support

  • Thoughtful handling of Windows PowerShell sl alias conflict with dedicated sapling-phabricator-windows templates
  • getScmTemplatePath() at src/commands/init.ts:57-62 handles OS-specific template selection

4. SCM-Aware Workflow

  • Ralph workflow properly reads SCM selection and uses appropriate commands
  • buildImplementFeaturePrompt() generates Git or Sapling commands based on config
  • Good fallback to GitHub/Git as default

🔶 Suggestions for Improvement

1. Type Safety Enhancement (src/cli.ts:100)

preSelectedScm: localOpts.scm as SourceControlType | undefined,

Consider using a type guard validation instead of direct casting. The validation happens earlier at line 91, but the cast feels fragile if code order changes.

2. Config File Location Inconsistency
The PR summary mentions .atomic/config.json but the code uses .atomic.json at the project root (see src/utils/atomic-config.ts:14). This should be clarified in the README - the current README correctly documents .atomic.json.

3. Error Handling in readAtomicConfig (src/utils/atomic-config.ts:42-47)

try {
  const content = await readFile(configPath, "utf-8");
  return JSON.parse(content) as AtomicConfig;
} catch {
  return null;
}

Silent error handling is fine for missing files, but parsing errors could benefit from logging in DEBUG mode to help users diagnose malformed config files.

4. Template Duplication
The commit templates across agents (.claude/commands/, .opencode/command/, .github/skills/) contain significant duplication. Consider adding a note about maintaining consistency when updating templates, or future refactoring to generate from a single source.

5. SCM Detection for Auto-Init (src/commands/init.ts)
The spec mentions potential auto-detection (looking for .sl vs .git directory). This could be a future enhancement to automatically suggest the appropriate SCM type rather than always prompting.


🟡 Minor Issues

1. Progress.txt File
The progress.txt file appears to be development tracking - should this be committed to the repo, or should it be in .gitignore?

2. Documentation Placement
There are two Sapling reference documents:

  • docs/sapling-reference.md (540 lines)
  • research/docs/sapling-reference.md (331 lines)

Consider consolidating or clarifying the distinction between these.

3. Unused Constant (src/config.ts:137)

export const SCM_SPECIFIC_COMMANDS = ["commit"];

This constant is exported but only used in tests. If it's intended for future use, that's fine, but consider adding a comment explaining its purpose.


🔒 Security Considerations

  • No security concerns identified
  • Config files store non-sensitive data (agent type, SCM type, timestamps)
  • No credential handling in the SCM configuration

📋 Test Plan Verification

The test plan items in the PR description are well-covered:

  • SCM selection prompt - tested in tests/init.test.ts
  • --scm flags validation - tested in tests/cli-commander.test.ts
  • Config persistence - tested in tests/utils/atomic-config.test.ts
  • SCM-aware prompts - tested in tests/graph/nodes/ralph-nodes.test.ts
  • Command templates - templates present in templates/scm/ directories

Verdict

Approve - This is a solid implementation with good architecture and test coverage. The suggestions above are mostly minor improvements that could be addressed in follow-up PRs.

The PR adds valuable functionality for teams using Sapling + Phabricator workflows while maintaining backward compatibility for GitHub/Git users.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive Source Control Management (SCM) selection to the Atomic CLI, enabling users to choose between Git/GitHub and Sapling/Phabricator workflows during initialization. The feature introduces SCM-aware command templates, configuration persistence, and platform-specific support (including Windows-specific Sapling templates to handle PowerShell alias conflicts).

Changes:

  • Added SCM configuration system with support for GitHub/Git and Sapling+Phabricator
  • Implemented .atomic.json config file for persisting SCM and agent selections
  • Created SCM-specific command templates for all supported agents (Claude, OpenCode, Copilot)
  • Updated Ralph workflow to use SCM-aware prompts based on configured SCM type
  • Added comprehensive test coverage for SCM configuration and persistence

Reviewed changes

Copilot reviewed 36 out of 36 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/config.ts Added SCM type definitions, configurations, and helper functions
src/utils/atomic-config.ts New module for reading/writing .atomic.json configuration
src/cli.ts Added --scm flag to init command with validation
src/commands/init.ts Integrated SCM selection prompt and template copying logic
src/graph/nodes/ralph-nodes.ts Made implement-feature prompts SCM-aware (git log vs sl smartlog)
src/ui/commands/workflow-commands.ts Updated Ralph workflow to read SCM from config
templates/scm/github/*.md GitHub/Git command templates for all agents
templates/scm/sapling-phabricator/*.md Sapling+Phabricator templates for Unix/macOS
templates/scm/sapling-phabricator-windows/*.md Windows-specific templates using full sl.exe paths
tests/**/*.test.ts Comprehensive unit and integration tests
README.md Documentation for SCM selection and configuration
specs/source-control-type-selection.md Complete technical specification

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

const preSelectedScm = "sapling-phabricator" as const;

let scmType: string;
let shouldCallSelect = true;
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial value of shouldCallSelect is unused, since it is always overwritten.

Copilot uses AI. Check for mistakes.

describe("SCM_CONFIG", () => {
test("all SCMs have required name field", () => {
for (const [key, config] of Object.entries(SCM_CONFIG)) {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable key.

Copilot uses AI. Check for mistakes.
});

test("all SCMs have required displayName field", () => {
for (const [key, config] of Object.entries(SCM_CONFIG)) {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable key.

Copilot uses AI. Check for mistakes.
});

test("all SCMs have required cliTool field", () => {
for (const [key, config] of Object.entries(SCM_CONFIG)) {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable key.

Copilot uses AI. Check for mistakes.
});

test("all SCMs have required reviewTool field", () => {
for (const [key, config] of Object.entries(SCM_CONFIG)) {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable key.

Copilot uses AI. Check for mistakes.
});

test("all SCMs have required reviewCommandFile field", () => {
for (const [key, config] of Object.entries(SCM_CONFIG)) {
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable key.

Copilot uses AI. Check for mistakes.
*/

import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { test, expect, describe, beforeEach, afterEach, mock, spyOn } from "bun:test";
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused imports mock, spyOn.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,156 @@
import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { mkdtemp, rm, readFile, writeFile, mkdir } from "fs/promises";
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import mkdir.

Copilot uses AI. Check for mistakes.
const scmType = "github";
const isWindowsPlatform = false;

const templatePath = scmType === "sapling-phabricator" && isWindowsPlatform
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expression always evaluates to false.

Copilot uses AI. Check for mistakes.
const scmType = "sapling-phabricator";
const isWindowsPlatform = false;

const templatePath = scmType === "sapling-phabricator" && isWindowsPlatform
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This expression always evaluates to false.

Copilot uses AI. Check for mistakes.
- Fix SDK event handling with proper type assertions for dynamic event data
- Add type-safe clipboard method calls in UI components
- Update subagent tests to use spawnRecords instead of sentMessages
- Configure opus model for codebase analysis and debugging agents
- Fix annotation and init test type annotations

Assistant-model: Claude Code
@flora131
Copy link
Owner Author

@claude

@claude
Copy link

claude bot commented Feb 13, 2026

Claude finished @flora131's task in 20s —— View job


Hello! 👋

  • Understand the trigger comment and request
  • Gather context about the PR

I noticed you mentioned me, but I didn't see a specific request in your comment.

I can help you with:

  • Implementing fixes for the issues identified by Copilot (unused variables, unused imports, expressions that always evaluate to false)
  • Answering questions about the PR or codebase
  • Making additional changes to the PR

Just let me know what you'd like me to do! For example:

  • "Fix the Copilot review comments"
  • "Review the code again"
  • "Help me understand how X works"

@claude
Copy link

claude bot commented Feb 13, 2026

PR Review: feat(init): add SCM selection with Sapling and GitHub support

Overview

This is a substantial PR that adds source control management (SCM) selection to the atomic init flow, enabling users to choose between Git/GitHub and Sapling/Phabricator workflows. The implementation is well-thought-out with extensive documentation and test coverage.


👍 Strengths

Architecture & Design

  • Clean separation of concerns: SCM configuration is centralized in src/config.ts with the new SCM_CONFIG, SourceControlType, and ScmConfig types
  • Template-based approach: Using templates/scm/ directories for SCM-specific commands is extensible and maintainable
  • Windows support: Thoughtful handling of PowerShell's sl alias conflict by using full paths to sl.exe
  • Comprehensive spec document: The specs/source-control-type-selection.md provides excellent context and design rationale

Code Quality

  • Type safety: Proper TypeScript types and type guards (isValidScm, getScmKeys)
  • Backward compatibility: Existing GitHub/Git users are unaffected, with sensible defaults
  • Configuration persistence: The .atomic.json config file is a good approach for storing project settings

Test Coverage

  • New tests in tests/config.test.ts for SCM configuration
  • New tests in tests/utils/atomic-config.test.ts for config persistence
  • Extended tests in tests/init.test.ts for SCM selection flow
  • Extended tests in tests/graph/nodes/ralph-nodes.test.ts for SCM-aware prompts

🔧 Suggestions for Improvement

1. Type Casting in copilot-client.ts (Minor)

// src/sdk/copilot-client.ts:156
const data = event.data as Record<string, unknown>;

While this fixes the immediate type issue, consider if there's a way to improve the SDK types themselves or add a more specific type rather than Record<string, unknown>. This could help catch type errors at compile time.

2. Missing newlines at end of files

Several template files are missing trailing newlines:

  • templates/scm/github/.claude/commands/commit.md
  • templates/scm/github/.claude/commands/create-gh-pr.md
  • templates/scm/github/.opencode/command/commit.md
  • templates/scm/github/.opencode/command/create-gh-pr.md

Consider adding \n at the end for POSIX compliance.

3. progress.txt should be excluded

The file progress.txt appears to be a work-in-progress tracking file and should probably be removed or added to .gitignore rather than committed.

4. Error Handling in copyScmCommands

// src/commands/init.ts:138-145
if (!(await pathExists(srcDir))) {
  if (process.env.DEBUG === "1") {
    console.log(`[DEBUG] SCM template not found: ${srcDir}`);
  }
  return;
}

Consider logging a warning (not just in DEBUG mode) when expected SCM templates are missing, as this could indicate a broken installation.

5. Inconsistent History Command Output

// src/graph/nodes/ralph-nodes.ts:18-20
export function getHistoryCommand(scm: SourceControlType): string {
  return scm === "sapling-phabricator"
    ? "sl smartlog -l 10"
    : "git log --oneline -20";
}

The git version shows 20 commits while Sapling shows 10. Consider making these consistent.


⚠️ Potential Issues

1. Missing .arcconfig Check Timing

The check for .arcconfig happens after SCM selection but the warning might be easy to miss in the output. Consider making this more prominent or offering to create a skeleton .arcconfig.

2. Hardcoded Windows Sapling Path

& 'C:\Program Files\Sapling\sl.exe'

The Sapling installation path is hardcoded. Consider:

  • Adding a note in the README about custom installation paths
  • Or detecting the path from PATH environment variable when possible

3. Research Documents in PR

The research/docs/ files are informative but quite large. Consider if these should be in a separate documentation location or if they're needed in the main codebase.


🔒 Security

No security concerns identified. The implementation:

  • Validates SCM type input with isValidScm()
  • Doesn't handle credentials directly (delegates to gh and .arcrc)
  • Uses local-only configuration storage

📊 Performance

No performance concerns. The SCM selection is done once during initialization and stored in .atomic.json for subsequent runs.


📝 Documentation

The PR includes excellent documentation:

  • Updated README with SCM selection section
  • Comprehensive docs/sapling-reference.md
  • Detailed spec document explaining design decisions

✅ Verdict

This PR is well-implemented and ready for merge with minor suggestions above. The code is clean, well-tested, and follows the existing patterns in the codebase. The extensibility for future SCM types (like Azure DevOps) is built in.

Summary of recommended changes before merge:

  1. Remove progress.txt from the PR
  2. Add trailing newlines to template files
  3. Consider making the history command limits consistent (10 vs 20)

Great work on this feature! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant