| name | github-lanes-bridge |
| description | Use when bridging GitHub and Lanes — importing GitHub issues into Lanes for local Claude Code execution, batch-spawning sessions per ticket, decomposing one GitHub issue into multiple Lanes sub-issues with dependencies, or posting session results (PR links, comments, follow-up issues) back to GitHub. Triggers on phrases like "import from GitHub", "bring this GitHub issue into Lanes", "comment back on the PR/issue", "spin off a GitHub issue from this Lanes ticket", or any mention of both GitHub and Lanes in the same request. Uses the Lanes MCP's built-in lanes_github_* tools — no separate GitHub MCP needed (the user must have connected GitHub in Lanes settings). |
GitHub ↔ Lanes bridge
Use this skill to move work between GitHub (where issues and PRs live) and Lanes (the local agent execution board). Typical flows: pull an issue into Lanes, run a session on it, then push the outcome (a PR-linking comment, or a new follow-up issue) back to GitHub.
This skill assumes the lanes-sessions skill is also available — it covers the lanes_* issue/session tools in detail. This skill focuses on the bridging layer.
When a Lanes issue hosts more than one CLI session, follow the multi-session disambiguation rules in lanes-sessions (pass session on stop / resume / read_terminal). Bridge tools (lanes_github_*) never address sessions directly.
When to use
- Work originates in GitHub and you want Claude Code to do the actual implementation locally.
- You want to fan out one GitHub issue into multiple Claude sessions on sub-tasks.
- You want PR links / progress updates to flow back to GitHub without leaving chat.
Don't use for ad-hoc one-off tasks that didn't start in GitHub; for those, just use Lanes directly.
Prerequisites
- Lanes MCP must be connected — verify with
lanes_list_components. If missing, run /lanes:setup-mcp.
- GitHub must be connected inside Lanes — verify with
lanes_github_list_repos. If it returns a "not connected" error, ask the user to connect GitHub from Lanes → Settings → GitHub (OAuth flow). The token is read from integrations.json on every call. GitHub tokens don't expire, so no refresh logic to worry about.
There is no standalone GitHub MCP required. The same Lanes MCP that exposes lanes_list_issues also exposes lanes_github_list_repos, lanes_github_get_issue, etc. — they're additional tools on the same server.
The GitHub tool surface
| Tool | Purpose |
|---|
lanes_github_list_repos | List repos the user can access (owner, collaborator, or org member). Sorted by most-recently-pushed. Each is { id: "owner/name", name: "owner/name" }. |
lanes_github_list_issues | First 50 open issues in a repo, most-recently-updated first. PRs are filtered out automatically. |
lanes_github_search_issues | Free-text search via the /search/issues endpoint. When the query is a pure number ("#9", "9"), the exact-match issue is prepended. |
lanes_github_get_issue | Single issue by number (passed as a string, e.g. "42"). |
lanes_github_create_issue | Open a new issue. Labels are GitHub label names (not IDs). |
lanes_github_comment_on_issue | Post a comment on an issue or PR. (GitHub uses the same endpoint for both.) |
Returned ExternalIssue shape:
{
"provider": "github",
"externalId": "42",
"externalKey": "#42",
"externalUrl": "https://github.com/owner/name/issues/42",
"title": "...",
"description": "...",
"state": "open",
"updatedAt": 1715600000000
}
Field mapping (GitHub → Lanes)
| GitHub | Lanes | Notes |
|---|
externalKey (e.g. #42) | Title prefix + description marker | Used as the dedup key. |
title | title | Pass through. |
body (markdown) | description | Prepend a marker line: GitHub: owner/name#42 — <externalUrl> so round-tripping is trivial. |
state (open / closed) | step | open → backlog/todo (your call); closed → done if explicitly importing closed issues. |
upstream externalId (issue number) | externalId on lanes_create_issue | Pass through. |
upstream externalUrl | externalUrl on lanes_create_issue | Pass through. |
upstream externalKey | externalKey on lanes_create_issue | Pass through. |
labels | tags | Different ID spaces. Look up Lanes label UUIDs via lanes_list_labels and map by name. Missing Lanes labels: ask user before creating. |
lanes_create_issue accepts externalProvider, externalId, externalKey, externalUrl, and externalSyncedAt — set all of them on import so the Lanes UI shows the badge and the refresh button works.
Workflow patterns
A. Single port (one GitHub issue → one Lanes session)
1. lanes_github_list_repos // (or user supplies "owner/name")
2. lanes_github_search_issues { repo: "owner/name", query: "#42" }
or lanes_github_get_issue { repo, number: "42" }
3. lanes_list_issues { search: "owner/name#42" } // dedup check
4. lanes_list_components // pick componentId
5. lanes_create_issue {
title: "owner/name#42 · <github title>",
description: "GitHub: owner/name#42 — <externalUrl>\n\n<github body>",
componentId,
cwd,
worktreeStrategy: "create",
worktreeName: "gh/owner-name-42",
externalProvider: "github",
externalId: "42",
externalKey: "#42",
externalUrl: "<github url>",
externalSyncedAt: <now epoch ms>
}
6. lanes_start_session { issueId, planMode: true }
Confirm with the user before step 6 if the issue is non-trivial — let them review the issue first.
B. Backlog import (filter GitHub → bulk Lanes issues)
1. lanes_github_list_issues { repo } (capped at 50)
or lanes_github_search_issues { repo, query: "label:p0" }
Show the user the result count BEFORE importing.
2. Confirm scope with the user.
3. lanes_list_components, lanes_list_labels (cache locally)
4. For each GitHub issue:
- dedup via lanes_list_issues { search: "<externalKey>" }
- lanes_create_issue (with marker, mapped labels → tags, external* metadata)
5. (Optional) batch lanes_start_session in plan mode with
flags: [{ flag: "dangerously-skip-permissions", value: "", kind: "flag" }]
For wider sweeps, prefer lanes_github_search_issues with explicit qualifiers (label:foo, assignee:@me, milestone:"v2") over list_issues with no filter — GitHub's search syntax composes naturally inside the query string.
C. Decomposition (one GitHub issue → many Lanes sub-issues)
1. lanes_github_get_issue { repo, number } // big ticket
2. Propose a breakdown to the user (numbered list).
3. After user approves:
a. lanes_create_issue for the parent (the umbrella, with GitHub marker).
b. lanes_create_issue for each sub-task with
dependencies: [parentLanesId].
4. lanes_start_session on the parent in plan mode (optional);
start sub-tasks once their dependencies are unblocked.
If the team needs sub-issues visible upstream too, call lanes_github_create_issue for each and add their #N to each Lanes description.
D. Round-trip (push results back)
When a session is done and there's something concrete to report:
1. lanes_get_issue_changes { id } // confirm there is a diff
2. (User pushes the branch / opens a PR.)
3. lanes_github_comment_on_issue {
repo: "owner/name",
number: "42",
body: "Implemented locally via Lanes #<lanesId>. PR: <PR URL>\n\n<stat summary>"
}
4. (Optional) lanes_github_create_issue for follow-up work surfaced during the
session.
5. lanes_move_issue { id: lanesId, step: "done" }
PRs and issues share the comment endpoint on GitHub — you can use lanes_github_comment_on_issue to comment on a PR by passing its number.
Idempotency
Always include GitHub: owner/name#N in the Lanes description (and ideally in the title prefix). Before importing, run lanes_list_issues { search: "owner/name#N" } and skip if a match is found. For backlog imports, do the dedup check per-issue, not just up-front, so partially-imported batches can resume cleanly.
Common pitfalls
- ❌ Passing the GitHub issue number as a JSON integer. The tool expects a string (e.g.
"42", not 42) so the same parameter shape works for arbitrary external-id forms.
- ❌ Forgetting
external* metadata on import. Without them, the Lanes UI can't show the "open in GitHub" badge or refresh the description.
- ❌ Using
lanes_github_list_issues and expecting PRs. The endpoint mixes issues and PRs, but the tool filters PRs out (matching the renderer). Use the GitHub UI or gh pr list for PRs — there's no lanes_github_list_prs tool today.
- ❌ Passing GitHub label IDs to
lanes_github_create_issue. Labels are by name (string), not ID — GitHub's REST API expects names.
- ❌ Skipping the dedup marker. Without
GitHub: owner/name#N in the description, a re-run double-imports the same ticket.
- ❌ Auto-pushing branches or opening PRs without user consent. Never do remote git operations in Pattern D without the user explicitly asking.
- ❌ Posting back on every session start. Round-trip happens at session end (work landed). Don't spam GitHub comments mid-run.
- ❌ Assuming
lanes_github_* are read-only. lanes_github_create_issue and lanes_github_comment_on_issue write to upstream — always confirm with the user before invoking on production repos.