Emdash provides deep GitHub integration for the full pull request lifecycle: branch naming, PR generation with AI-drafted descriptions, CI/CD check tracking, and merge automation. All GitHub operations use the official gh CLI for reliability and security.
GitHub integration is optional but highly recommended for teams using GitHub-based workflows.
Requirements
GitHub CLI (gh) installed and authenticated
Project must be a GitHub repository
Remote must be named origin and point to GitHub
Install gh CLI
# macOS
brew install gh
# Windows
winget install GitHub.cli
# Linux
sudo apt install gh # Debian/Ubuntu
sudo yum install gh # RHEL/CentOS
Authenticate with GitHub
Or authenticate via Emdash:
Go to Settings → GitHub
Click Connect with GitHub
Follow the OAuth flow in your browser
Verify authentication
You should see: ✓ Logged in to github.com as <username> (oauth_token)
Emdash automatically syncs gh CLI authentication when you connect via OAuth. If gh auth status fails, Emdash will re-authenticate the CLI with your stored token.
Branch naming
Emdash creates isolated branches for each task:
Format : {prefix}/{task-slug}-{hash}
Default prefix : emdash
Customizable : Settings → Repository → Branch Prefix
Example branches:
emdash/fix-login-bug-a3f
emdash/add-dark-mode-7b2
feature/implement-search-9c4 (custom prefix)
Branch lifecycle
Creation : Branch created when task starts (via WorktreeService)
Commits : Agent commits changes to the branch
Push : Emdash pushes to origin when creating a PR
Cleanup : Branch can be deleted after PR merge (manual or via GitHub settings)
PR generation workflow
The complete PR creation flow:
Review changes
Open the File Changes panel to review diffs before creating a PR.
Generate PR content
Click Create PR . Emdash:
Fetches git diff and commit messages
Attempts to generate title/description using an AI agent
Falls back to heuristic generation if agents fail
From PrGenerationService.ts:23-72: async generatePrContent (
taskPath : string ,
baseBranch : string = 'main' ,
preferredProviderId ?: string | null
): Promise < GeneratedPrContent > {
const { diff , commits , changedFiles } = await this.getGitContext( taskPath , baseBranch);
// Try providers in order: preferred → claude → others
const tryOrder: ProviderId [] = [];
if ( preferredProviderId ) tryOrder . push ( preferredProviderId );
tryOrder . push ( 'claude' );
for ( const id of PROVIDER_IDS ) tryOrder . push ( id );
for ( const providerId of tryOrder ) {
const result = await this . generateWithProvider ( providerId , taskPath , diff , commits );
if ( result ) return result;
}
// Fallback to heuristics
return this . generateHeuristicContent ( diff , commits , changedFiles );
}
Edit and refine
Review the generated PR title and description. Edit as needed.
Create PR via gh CLI
Emdash runs: gh pr create \
--title "feat: add dark mode" \
--body "$( cat << 'EOF'
## Summary
- Implemented dark mode toggle
- Added theme persistence
EOF
)" \
--base main \
--head emdash/add-dark-mode-7b2
Track PR status
The task panel now shows:
PR number and link
CI/CD check status
Merge status
PR content generation
AI-generated (preferred)
Emdash tries to use installed CLI agents to generate PR content:
Preferred agent (if task has an agent ID)
Claude Code (if installed) — uses -p print mode with --output-format json
Other agents in registry order
Claude Code example
From PrGenerationService.ts:289-326:
const args : string [] = [];
const isClaudeProvider = providerId === 'claude' ;
if ( isClaudeProvider ) {
// Use -p (print/non-interactive) mode with JSON output
args . push ( '-p' , prompt , '--output-format' , 'json' );
if ( provider ! . autoApproveFlag ) {
args . push ( provider ! . autoApproveFlag );
}
} else {
// Standard agent invocation
if ( provider ! . defaultArgs ?. length ) {
args . push ( ... provider ! . defaultArgs );
}
if ( provider ! . autoApproveFlag ) {
args . push ( provider ! . autoApproveFlag );
}
if ( provider ! . initialPromptFlag !== undefined ) {
if ( provider ! . initialPromptFlag ) {
args . push ( provider ! . initialPromptFlag );
}
args . push ( prompt );
}
}
const child = spawn ( cliCommand , args , { cwd: taskPath });
Generate a concise PR title and description based on these changes:
Commits:
- feat: add dark mode toggle
- fix: theme persistence bug
Diff summary:
src/theme.ts | 45 +++++++++++++++++++++++++++--
src/components/ | 12 +++++---
2 files changed, 52 insertions(+), 5 deletions(-)
Respond with ONLY valid JSON:
{
"title": "feat: add dark mode",
"description": "## Changes\n- Added theme toggle\n- Fixed persistence"
}
Heuristic fallback
If no agents are available or all fail, Emdash generates content from:
First commit message → PR title
All commits → “Changes” section
Changed files → “Files Changed” section
Diff stats → “Summary” section
From PrGenerationService.ts:516-653:
private generateHeuristicContent (
diff : string ,
commits : string [],
changedFiles : string []
): GeneratedPrContent {
let title = 'chore: update code' ;
if ( commits . length > 0 ) {
title = commits [ 0 ]; // Most recent commit
if ( title . length > 72 ) {
title = title . substring ( 0 , 69 ) + '...' ;
}
}
const descriptionParts : string [] = [];
if ( commits . length > 0 ) {
descriptionParts . push ( '## Changes' );
commits . forEach (( commit ) => {
descriptionParts . push ( `- ${ commit } ` );
});
}
if ( changedFiles . length > 0 ) {
descriptionParts . push ( ' \n ## Files Changed' );
changedFiles . slice ( 0 , 20 ). forEach (( file ) => {
descriptionParts . push ( `- \` ${ file } \` ` );
});
}
return { title , description: descriptionParts . join ( ' \n ' ) };
}
CI/CD check tracking
After creating a PR, Emdash can track CI/CD checks:
Fetch check status
Returns: ✓ CI / Build (push) 0s
✓ CI / Test (push) 1m 23s
✓ CI / Lint (push) 45s
Display in UI
The task panel shows:
✅ All checks passed
⏳ Checks in progress
❌ Failed checks with links to logs
Auto-refresh
Emdash polls check status every 30s until all checks complete.
Check tracking works with any CI/CD provider that integrates with GitHub (GitHub Actions, CircleCI, Travis, Jenkins, etc.).
PR review and merge
Viewing PRs
List open PRs for a project:
// From GitHubService.ts:763-802
async getPullRequests ( projectPath : string ): Promise < GitHubPullRequest [] > {
const fields = [
'number' ,
'title' ,
'headRefName' ,
'baseRefName' ,
'url' ,
'isDraft' ,
'updatedAt' ,
'headRefOid' ,
'author' ,
];
const { stdout } = await this.execGH(
`gh pr list --state open --json ${ fields . join ( ',' ) } ` ,
{ cwd : projectPath }
);
return JSON.parse(stdout || '[]' );
}
Checking out PRs
To review a PR locally:
// From GitHubService.ts:808-845
async ensurePullRequestBranch (
projectPath : string ,
prNumber : number ,
branchName : string
): Promise < string > {
await this.execGH(
`gh pr checkout ${ prNumber } --branch ${ branchName } --force` ,
{ cwd : projectPath }
);
return branchName;
}
This creates/updates a local branch that tracks the PR.
Merging PRs
Merge via GitHub web UI or CLI:
gh pr merge < pr-numbe r > --merge # Standard merge
gh pr merge < pr-numbe r > --squash # Squash merge
gh pr merge < pr-numbe r > --rebase # Rebase merge
Emdash does not auto-merge PRs. You must manually approve and merge via GitHub.
Advanced features
Linking issues to PRs
If a task has an attached issue (Linear/Jira/GitHub), Emdash can reference it in the PR description:
## Summary
- Implemented feature X
- Fixed bug Y
**Related** : Fixes #123
GitHub automatically links the PR to the issue.
Draft PRs
Create a draft PR for work-in-progress:
gh pr create --draft --title "WIP: add feature" --body "..."
PR templates
GitHub PR templates (.github/PULL_REQUEST_TEMPLATE.md) are not automatically used by Emdash. To use a template:
Copy template content
Paste into Emdash’s PR description field
Fill in required sections
Repository creation
Create a new GitHub repository from Emdash:
// From GitHubService.ts:965-1001
async createRepository ( params : {
name: string ;
description ?: string ;
owner : string ;
isPrivate : boolean ;
}): Promise < { url : string ; defaultBranch : string ; fullName : string } > {
const { name , description , owner , isPrivate } = params;
const visibilityFlag = isPrivate ? '--private' : '--public' ;
let command = `gh repo create ${ owner } / ${ name } ${ visibilityFlag } --confirm` ;
if ( description && description.trim()) {
command += ` --description ${ JSON . stringify ( description ) } ` ;
}
await this.execGH(command);
const { stdout } = await this.execGH(
`gh repo view ${ owner } / ${ name } --json name,nameWithOwner,url,defaultBranchRef`
);
const repoInfo = JSON.parse(stdout);
return {
url: repoInfo . url ,
defaultBranch: repoInfo . defaultBranchRef ?. name || 'main' ,
fullName: repoInfo . nameWithOwner ,
};
}
Error handling
Automatic re-authentication
If gh CLI authentication expires, Emdash automatically re-authenticates:
// From GitHubService.ts:460-488
private async execGH ( command : string , options ?: any ) {
try {
return await execAsync ( command , { encoding: 'utf8' , ... options });
} catch ( error : any ) {
if ( error . message && error . message . includes ( 'not authenticated' )) {
// Re-authenticate gh CLI with stored token
const token = await this . getStoredToken ();
if ( token ) {
await this . authenticateGHCLI ( token );
// Retry command
return await execAsync ( command , { encoding: 'utf8' , ... options });
}
}
throw error ;
}
}
Common errors
Symptom : “GitHub CLI not installed”Solution : Install gh CLI (see Requirements )
Symptom : “You are not authenticated with GitHub”Solutions :
Run gh auth login in terminal
Or authenticate via Emdash: Settings → GitHub → Connect
PR creation failed: remote branch exists
Symptom : “A branch named ‘emdash/…’ already exists”Solutions :
Delete the remote branch: git push origin --delete emdash/...
Or use a different branch prefix in Settings
Symptom : “There are no commits between main and emdash/…”Solution : Commit changes before creating a PR
Best practices
Review diffs before PR Always review the File Changes panel before creating a PR to catch unintended changes.
Use conventional commits Follow Conventional Commits for consistent PR titles:
feat: New features
fix: Bug fixes
chore: Maintenance
Keep PRs focused One feature or bug fix per PR. Split large tasks into multiple PRs.
Link issues Always link PRs to issues for better traceability.