Skip to content
Back to skills

Github Lanes Bridge

ASecurity

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 GitHu...

  • 272 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 26, 2026
developmentgitapi

Works with

  • claude code
  • terminal
  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add lanes-sh/app --skill github-lanes-bridge --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Github Lanes Bridge?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Github Lanes Bridge
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lanes-sh-github-lanes-bridge/badge)](https://www.skillsdirectory.com/skills/lanes-sh-github-lanes-bridge)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.

`lanes_start_session` always **adds** a session, and its launch is asynchronous. In the batch flows below, call it exactly once per issue and record the slot it returns. Never re-call it because a session looks missing — an empty status read inside the launch window is expected. See "Starting is asynchronous" in `lanes-sessions`.

## 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-desktop: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:

```json
{
  "provider": "github",
  "externalId": "42",          // issue number as a string
  "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`/`planning` (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.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.