Skip to content

feat(github): GitHub repo picker with auto-sync instructions and runtime detection#3095

Open
tlgimenes wants to merge 197 commits intomainfrom
tlgimenes/github-repo-picker
Open

feat(github): GitHub repo picker with auto-sync instructions and runtime detection#3095
tlgimenes wants to merge 197 commits intomainfrom
tlgimenes/github-repo-picker

Conversation

@tlgimenes
Copy link
Copy Markdown
Contributor

@tlgimenes tlgimenes commented Apr 10, 2026

What is this contribution about?

Adds full GitHub integration to virtual MCPs: users can connect a GitHub repo via the Device Flow OAuth, and the system automatically syncs instructions from AGENTS.md/CLAUDE.md and detects the package manager to auto-fill install and dev scripts.

Key features:

  • GitHub Device Flow OAuth (no client secret needed, works locally)
  • Repo picker dialog with org/installation selection
  • GITHUB_GET_FILE_CONTENT tool for fetching files from connected repos
  • Auto-sync AGENTS.md > CLAUDE.md into virtual MCP instructions (read-only when connected)
  • Auto-detect package manager (deno > bun > pnpm > yarn > npm) and populate install/dev scripts
  • New "Repository" tab in agent settings showing connected repo, install script, and dev script
  • GitHub repo button in shell header (octocat icon when disconnected, owner/repo link when connected)

Screenshots/Demonstration

After connecting a GitHub repo, the Repository tab auto-fills:

  • Install Script: e.g. npm install
  • Development Script: e.g. npm run dev

Instructions tab is auto-populated from AGENTS.md and becomes read-only.

How to Test

  1. Open any virtual MCP's settings
  2. Click the GitHub button in the header (octocat icon)
  3. Authenticate via Device Flow, select an org, pick a repo
  4. Verify: instructions are populated from AGENTS.md and disabled
  5. Go to Repository tab — verify install/dev scripts are auto-filled
  6. Edit the script fields and blur — values persist

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working
  • Documentation is updated (if needed)
  • No breaking changes

🤖 Generated with Claude Code


Summary by cubic

Adds a GitHub repo picker (Device Flow) and a Preview view that launches per‑user Freestyle VMs with a header‑stripping proxy, live logs, VM file tools, and a visual editor. Connecting a repo auto‑syncs instructions, detects package manager/runtime/dev port, saves to metadata.githubRepo (GithubRepo in @decocms/mesh-sdk), and is gated by the experimental_vibecode preference.

  • New Features

    • GitHub: header button + repo picker using Device Flow with local token cache and auto‑copy; auto‑installs the deco/mcp-github connection if missing; app‑only tools to list user orgs/installations and repos with pagination; stable KEYS for queries; metadata.githubRepo schema and GithubRepo export; preference toggle in Profile → Preferences.
    • Repo sync/detection: auto‑sync AGENTS.md (fallback CLAUDE.md); detect package manager/runtime/dev port via PACKAGE_MANAGER_CONFIG; new Repository tab to view/edit install/dev scripts, package manager, and port; instructions become read‑only when connected.
    • Preview/VM: VM_START/VM_DELETE app‑only tools using freestyle-sandboxes with @freestyle-sh/with-nodejs, @freestyle-sh/with-deno, @freestyle-sh/with-bun; deterministic per‑user .deco.studio domains; in‑VM daemon (reverse proxy + SSE + file ops) with logs in an @xterm/xterm panel; toolbar to reinstall deps, restart dev, and open in a new tab; when a VM is active, VM file tools (read/write/edit/grep/glob/bash) proxy to the daemon and replace the QuickJS sandbox; Preview is available as a main view; visual editor with click‑to‑prompt that reuses the current thread.
  • Bug Fixes

    • GitHub: correct App client ID; resilient Device Flow; pagination to avoid truncated results; stable query keys; ignore stale repo metadata when the connection is removed.
    • VM/Preview: deterministic per‑user domains; reliable start/resume/delete; clear activeVms on repo change; liveness probe and 503 handling to reset stale VMs; consistent iframe preview via header‑stripping proxy; always restart dev server on resume; safer deletion flow.

Written for commit e72e2e3. Summary will update on new commits.

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Benchmark

Should we run the Virtual MCP strategy benchmark for this PR?

React with 👍 to run the benchmark.

Reaction Action
👍 Run quick benchmark (10 & 128 tools)

Benchmark will run on the next push after you react.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 10, 2026

Release Options

Suggested: Minor (2.265.0) — based on feat: prefix

React with an emoji to override the release type:

Reaction Type Next Version
👍 Prerelease 2.264.10-alpha.1
🎉 Patch 2.264.10
❤️ Minor 2.265.0
🚀 Major 3.0.0

Current version: 2.264.9

Note: If multiple reactions exist, the smallest bump wins. If no reactions, the suggested bump is used (default: patch).

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

10 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/tools/github/list-repos.ts">

<violation number="1" location="apps/mesh/src/tools/github/list-repos.ts:44">
P2: This only fetches the first 100 repositories. Add pagination so installations with >100 repos return complete results.</violation>
</file>

<file name="apps/mesh/src/tools/github/list-installations.ts">

<violation number="1" location="apps/mesh/src/tools/github/list-installations.ts:42">
P2: This fetch only returns the first page of installations. Add pagination handling so users with >100 installations can see all repos/installations.</violation>
</file>

<file name="apps/mesh/src/tools/github/device-flow-poll.ts">

<violation number="1" location="apps/mesh/src/tools/github/device-flow-poll.ts:76">
P1: `slow_down` is treated the same as `authorization_pending`, so callers cannot apply the required poll backoff and may keep hitting device-flow rate limits.</violation>
</file>

<file name="apps/mesh/src/web/components/github-repo-button.tsx">

<violation number="1" location="apps/mesh/src/web/components/github-repo-button.tsx:85">
P2: The GitHub auth bootstrap flow is duplicated from `repository.tsx`; extract a shared helper to avoid flow drift between entry points.</violation>
</file>

<file name="apps/mesh/src/tools/registry-metadata.ts">

<violation number="1" location="apps/mesh/src/tools/registry-metadata.ts:179">
P1: `GITHUB_GET_FILE_CONTENT` is missing from registry metadata, so tool metadata is out of sync with `ALL_TOOLS`.</violation>
</file>

<file name="apps/mesh/src/web/components/github-repo-dialog.tsx">

<violation number="1" location="apps/mesh/src/web/components/github-repo-dialog.tsx:82">
P1: Poll interval leaks when the dialog closes. `pollTimerRef` is never cleared on close, so background API calls continue indefinitely. Also, `pollingStartedRef` stays `true`, preventing polling from restarting if the dialog is re-opened.

Wrap `onOpenChange` to clear the interval and reset the ref when the dialog closes.</violation>

<violation number="2" location="apps/mesh/src/web/components/github-repo-dialog.tsx:103">
P2: Mutating a global (`delete window.__decoGithubDeviceFlow`) and calling `navigator.clipboard.writeText` during render violates React's purity requirement. Under strict mode double-render in development, the first invocation deletes the global, so the second invocation silently skips it. Wrap this block in `queueMicrotask` to defer the side effect outside the render phase.</violation>

<violation number="3" location="apps/mesh/src/web/components/github-repo-dialog.tsx:170">
P2: `startPolling` creates a `setInterval` timer directly during render. Unlike state updates, interval creation is an irreversible side effect that React cannot discard. Defer with `queueMicrotask` so the interval is only created after the render commits.</violation>
</file>

<file name="apps/mesh/src/web/views/settings/repository.tsx">

<violation number="1" location="apps/mesh/src/web/views/settings/repository.tsx:129">
P2: Spreading a stale `runtime` snapshot when saving one field can overwrite a previously edited sibling field.</violation>

<violation number="2" location="apps/mesh/src/web/views/settings/repository.tsx:173">
P2: This input uses `defaultValue` with async server data, so it can display stale values after metadata updates.

(Based on your team's feedback about syncing draft input state with async server values.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/mesh/src/tools/github/device-flow-poll.ts Outdated
Comment thread apps/mesh/src/tools/registry-metadata.ts Outdated
Comment thread apps/mesh/src/web/components/github-repo-dialog.tsx Outdated
Comment thread apps/mesh/src/tools/github/list-repos.ts Outdated
Comment thread apps/mesh/src/tools/github/list-installations.ts Outdated
Comment thread apps/mesh/src/web/components/github-repo-button.tsx Outdated
Comment thread apps/mesh/src/web/components/github-repo-dialog.tsx Outdated
Comment thread apps/mesh/src/web/components/github-repo-dialog.tsx Outdated
Comment thread apps/mesh/src/web/views/settings/repository.tsx Outdated
Comment thread apps/mesh/src/web/views/settings/repository.tsx
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

6 issues found across 14 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/web/components/github-repo-dialog.tsx">

<violation number="1" location="apps/mesh/src/web/components/github-repo-dialog.tsx:348">
P2: `deno.jsonc` is parsed with `JSON.parse`, so valid JSONC files can fail runtime/task detection.</violation>

<violation number="2" location="apps/mesh/src/web/components/github-repo-dialog.tsx:356">
P2: Port detection misses `--port 3000` syntax, so the saved runtime port can be incorrect.</violation>
</file>

<file name="apps/mesh/src/tools/registry-metadata.ts">

<violation number="1" location="apps/mesh/src/tools/registry-metadata.ts:36">
P2: VM tools are never returned by `getToolsByCategory` because the new `"VM"` category was added without adding a `VM` bucket to the grouped map.</violation>
</file>

<file name="apps/mesh/src/web/components/vm-preview.tsx">

<violation number="1" location="apps/mesh/src/web/components/vm-preview.tsx:86">
P2: Using `/favicon.ico` as the readiness probe can fail for healthy servers that don't provide that file, so preview readiness may never be detected.</violation>
</file>

<file name="apps/mesh/src/tools/vm/stop.ts">

<violation number="1" location="apps/mesh/src/tools/vm/stop.ts:40">
P1: Do not swallow all VM deletion errors; only treat "already deleted" as success and keep registry removal aligned with successful deletion.</violation>
</file>

<file name="apps/mesh/src/tools/vm/start.ts">

<violation number="1" location="apps/mesh/src/tools/vm/start.ts:119">
P1: Shell injection via unescaped user-controlled `installScript` interpolated into a single-quoted `bash -c` argument. Any script containing a single quote (which is common in shell commands) will break the quoting, causing the systemd service to fail or execute unintended commands. Escape the value or use a different execution strategy (e.g., write the script to a file first, or use double-quoting with proper escaping).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/mesh/src/tools/vm/stop.ts Outdated
Comment thread apps/mesh/src/tools/vm/start.ts Outdated
Comment thread apps/mesh/src/web/components/github-repo-dialog.tsx Outdated
Comment thread apps/mesh/src/web/components/github-repo-dialog.tsx Outdated
Comment thread apps/mesh/src/tools/registry-metadata.ts
Comment thread apps/mesh/src/web/components/vm-preview.tsx Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/tools/vm/start.ts">

<violation number="1" location="apps/mesh/src/tools/vm/start.ts:145">
P2: Use the domain returned by `freestyle.vms.create` when building `previewUrl`; hardcoding the requested domain can return a non-working URL.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/mesh/src/tools/vm/start.ts Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/tools/vm/start.ts">

<violation number="1" location="apps/mesh/src/tools/vm/start.ts:85">
P1: Validate `metadata.runtime.port` before using it as `vmPort`; invalid user-entered values currently become `NaN` and can break VM creation.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/mesh/src/tools/vm/start.ts Outdated
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/tools/vm/start.ts">

<violation number="1" location="apps/mesh/src/tools/vm/start.ts:113">
P1: Avoid hard-coding the proxy to port 9000 without checking the configured dev-server port; this can cause a port collision and prevent preview proxy startup.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/mesh/src/tools/vm/start.ts Outdated
@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai bot commented Apr 10, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

@tlgimenes tlgimenes force-pushed the tlgimenes/github-repo-picker branch 4 times, most recently from 2b578b5 to 0275bc0 Compare April 15, 2026 13:16
tlgimenes and others added 18 commits April 15, 2026 17:12
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wires GITHUB_LIST_INSTALLATIONS and GITHUB_LIST_REPOS into the
centralized tool registry (CORE_TOOLS array and ALL_TOOL_NAMES),
adds the GitHub category to ToolCategory, and fixes TypeScript
strict-null errors in the GitHub tool handlers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix state update during render by deriving effectiveInstallation
- Add per_page=100 to GitHub API calls to avoid truncated results

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Better Auth GitHub OAuth with GitHub Device Flow (public client ID).
Add GITHUB_DEVICE_FLOW_START and GITHUB_DEVICE_FLOW_POLL tools, update
list-installations and list-repos to accept a token input, rewrite the
dialog to use device flow with localStorage token caching, and remove
the unused BetterAuthAccountTable from storage types.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…diate dialog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After connecting a GitHub repo, automatically fetch AGENTS.md (or CLAUDE.md
fallback) to populate instructions, and detect the package manager from repo
files to auto-fill install/dev scripts. Instructions become read-only when
a GitHub repo is connected. Adds Repository tab to agent settings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create VM_START and VM_STOP app-only tools that spin up Freestyle VMs
with the connected GitHub repo, web terminal (read-only), and systemd
services for install + dev scripts. Add Preview panel to the tasks
sidebar with terminal/preview iframe toggle and auto-detection of
server readiness via polling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…e management

The VmWebTerminal integration's ttyd installation was failing inside
Freestyle VMs. Simplified to preview-only mode without terminal iframe.
Fixed error handling to properly detect MCP tool errors, and used refs
to prevent state loss across re-renders.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add in-memory VM registry that tracks active VMs per user and virtual
MCP. VM_START returns the existing VM if one is already running instead
of creating a duplicate. VM_STOP removes the entry from the registry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tlgimenes and others added 22 commits April 15, 2026 17:12
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ctor

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The usePreferences import was dropped during the rebase onto main.
Also renamed the settings page title from "{agent}'s Settings" to "Settings".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add console.log statements throughout VM_START handler for better
debugging. Improve the VM env error boundary with max-width, text
truncation, and a copy-to-clipboard button with toast feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…+ OAuth

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tHub repo picker

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nections

Cross-references metadata.githubRepo.connectionId against the Virtual MCP's
connections array. If the GitHub connection was removed, the repo metadata is
treated as stale and the UI shows the disconnected state. Migrates all 6
consumer files to use the new utility/hook instead of reading metadata directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move useAutoInstallGitHub call into PickerContent and gate on its
status. The picker now stays in the auto-install UI until the hook
reports "ready", preventing the race where connection creation
invalidates queries and InstallationPicker tries to use an
unauthenticated connection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…flag

useQuery with enabled:false was being misused as global state to signal
runtime detection progress between parent and child components. Replaced
with a simple callback prop + useState since they're in a direct
parent-child relationship.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove verbose console.log calls and wrap handler in try/catch
that logs errors before re-throwing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the VM is unreachable, VM tools now return a clear message asking
the user to start the server via the header button. Changed the header
button icon from Terminal to Server01 to match the "server" metaphor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@tlgimenes tlgimenes force-pushed the tlgimenes/github-repo-picker branch from 31dcae2 to 09566c3 Compare April 15, 2026 20:13
tlgimenes and others added 7 commits April 15, 2026 18:54
PreviewContent no longer calls VM_START. Instead it reads
entity.metadata.activeVms[userId] and shows an empty state with a
"Start Server" button that opens the env panel via toggleEnv().

EnvContent now invalidates the VIRTUAL_MCP collection cache after
VM_START and VM_DELETE so the preview picks up changes reactively.

Also includes github-repo-picker improvements: simplified repo search,
SearchInput component, toggleEnv on repo connect, and extracted
useGithubRepoPicker/useToggleEnvPanel hooks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The preview's Start/Resume Server buttons now use openEnv() instead of
toggleEnv() so they always open the env panel rather than toggling it.
Also removes debug console.log statements from the hook.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the user selects text in a terminal tab, an "Add to chat" button
appears in the tab bar. Clicking it sends the selection to chat wrapped
in <server-logs> tags and opens the chat panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous queryKey-based invalidation didn't match collectionItem
keys (which have a client object at position 0), so the entity cache
was never refreshed after VM_START/VM_DELETE.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The preview now always renders the full layout (toolbar + content area).
When no VM is running, the empty state appears as an overlay that
disappears reactively when previewUrl becomes available, preventing
the UI from getting stuck on the empty screen.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The module-level createSSESubscription singleton leaked a subscription
across React StrictMode double-mounts, leaving a stale EventSource that
prevented the daemon from replaying scripts/logs on reconnect.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

3 participants