Skip to content

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

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

feat(github): GitHub repo picker with auto-sync instructions and runtime detection#3095
tlgimenes wants to merge 109 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 via Device Flow and a rebuilt VM Preview with a VM daemon that streams logs and status over SSE for fast, live feedback. Connecting a repo auto-syncs AGENTS.md, detects the runtime, pre-fills install/dev scripts and port, saves the repo to metadata.githubRepo, and enables an optional visual editor — all behind the “Experimental: VibeCode” preference.

  • New Features

    • GitHub: Device Flow (GITHUB_DEVICE_FLOW_START/GITHUB_DEVICE_FLOW_POLL) with local token cache; list installations/repos (paginated) and fetch file content; persist linked repo in metadata.githubRepo; header button and Repository tab.
    • Auto-sync instructions from AGENTS.md (fallback CLAUDE.md); detect runtime (deno > bun > pnpm > yarn > npm) to pre-fill install/dev and port.
    • VM preview: app-only tools VM_START/VM_EXEC/VM_DELETE using freestyle-sandboxes; VM daemon with iframe proxy that strips X‑Frame‑Options/CSP and SSE /_daemon/events; split preview with resizable logs, open-in-new-tab, vmId badge, actions (Reinstall/Restart), Preview main view, and visual editor click-to-prompt.
  • Bug Fixes

    • Clear activeVms on repo change; sticky persistence with recreate: true; daemon liveness with stale‑VM auto‑reset; replace @freestyle-sh/with-web-terminal with built-in daemon log streaming.
    • GitHub: correct app client ID, add pagination to list calls, start Device Flow on click with auto-copied code, and fix dialog state/query keys.

Written for commit 7c639d9. 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.259.0) — based on feat: prefix

React with an emoji to override the release type:

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

Current version: 2.258.0

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.

};
}

if (data.error === "authorization_pending" || data.error === "slow_down") {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

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.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/tools/github/device-flow-poll.ts, line 76:

<comment>`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.</comment>

<file context>
@@ -0,0 +1,94 @@
+      };
+    }
+
+    if (data.error === "authorization_pending" || data.error === "slow_down") {
+      return { status: "pending" as const, token: null, error: null };
+    }
</file context>
Fix with Cubic

onOpenChange,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

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.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/github-repo-dialog.tsx, line 82:

<comment>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.</comment>

<file context>
@@ -0,0 +1,591 @@
+  onOpenChange,
+}: {
+  open: boolean;
+  onOpenChange: (open: boolean) => void;
+}) {
+  const { org } = useProjectContext();
</file context>
Fix with Cubic

);
}

const handleClick = async () => {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

P2: The GitHub auth bootstrap flow is duplicated from repository.tsx; extract a shared helper to avoid flow drift between entry points.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/github-repo-button.tsx, line 85:

<comment>The GitHub auth bootstrap flow is duplicated from `repository.tsx`; extract a shared helper to avoid flow drift between entry points.</comment>

<file context>
@@ -0,0 +1,145 @@
+    );
+  }
+
+  const handleClick = async () => {
+    // If user already has a token, skip device flow and go straight to repo picker
+    if (getStoredToken()) {
</file context>
Fix with Cubic

};

// Auto-start polling if dialog opened with pre-started device flow data
if (deviceFlow && !pollingStartedRef.current && !polling && !token) {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

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.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/github-repo-dialog.tsx, line 170:

<comment>`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.</comment>

<file context>
@@ -0,0 +1,591 @@
+  };
+
+  // Auto-start polling if dialog opened with pre-started device flow data
+  if (deviceFlow && !pollingStartedRef.current && !polling && !token) {
+    pollingStartedRef.current = true;
+    startPolling(deviceFlow.deviceCode, deviceFlow.interval);
</file context>
Fix with Cubic

const pollingStartedRef = useRef(false);

// Pick up device flow data pre-started by the button when dialog opens
if (open && !deviceFlow && !token && window.__decoGithubDeviceFlow) {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

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.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/github-repo-dialog.tsx, line 103:

<comment>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.</comment>

<file context>
@@ -0,0 +1,591 @@
+  const pollingStartedRef = useRef(false);
+
+  // Pick up device flow data pre-started by the button when dialog opens
+  if (open && !deviceFlow && !token && window.__decoGithubDeviceFlow) {
+    const data = window.__decoGithubDeviceFlow;
+    delete window.__decoGithubDeviceFlow;
</file context>
Fix with Cubic

<Input
id="install-script"
placeholder="e.g. npm install"
defaultValue={runtime?.installScript ?? ""}
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

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

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/views/settings/repository.tsx, line 173:

<comment>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.) </comment>

<file context>
@@ -0,0 +1,213 @@
+          <Input
+            id="install-script"
+            placeholder="e.g. npm install"
+            defaultValue={runtime?.installScript ?? ""}
+            onBlur={(e) => handleScriptUpdate("installScript", e.target.value)}
+          />
</file context>
Fix with Cubic

data: {
metadata: {
runtime: {
...runtime,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

P2: Spreading a stale runtime snapshot when saving one field can overwrite a previously edited sibling field.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/views/settings/repository.tsx, line 129:

<comment>Spreading a stale `runtime` snapshot when saving one field can overwrite a previously edited sibling field.</comment>

<file context>
@@ -0,0 +1,213 @@
+          data: {
+            metadata: {
+              runtime: {
+                ...runtime,
+                [field]: value,
+              },
</file context>
Fix with Cubic

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.

devScript = "deno task dev";
// Try to extract port from the dev task command
const portMatch = tasks.dev.match(
/(?:--port|PORT=|:)(\d{4,5})/,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

P2: Port detection misses --port 3000 syntax, so the saved runtime port can be incorrect.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/github-repo-dialog.tsx, line 356:

<comment>Port detection misses `--port 3000` syntax, so the saved runtime port can be incorrect.</comment>

<file context>
@@ -335,24 +335,57 @@ export function GitHubRepoDialog({
+                    devScript = "deno task dev";
+                    // Try to extract port from the dev task command
+                    const portMatch = tasks.dev.match(
+                      /(?:--port|PORT=|:)(\d{4,5})/,
+                    );
+                    if (portMatch?.[1]) devPort = portMatch[1];
</file context>
Suggested change
/(?:--port|PORT=|:)(\d{4,5})/,
/(?:--port(?:=|\s+)|PORT=|:)(\d{4,5})/,
Fix with Cubic

const data = await checkFileExists(denoFile);
if (data.found && data.content) {
try {
const deno = JSON.parse(data.content) as {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

P2: deno.jsonc is parsed with JSON.parse, so valid JSONC files can fail runtime/task detection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/github-repo-dialog.tsx, line 348:

<comment>`deno.jsonc` is parsed with `JSON.parse`, so valid JSONC files can fail runtime/task detection.</comment>

<file context>
@@ -335,24 +335,57 @@ export function GitHubRepoDialog({
+              const data = await checkFileExists(denoFile);
+              if (data.found && data.content) {
+                try {
+                  const deno = JSON.parse(data.content) as {
+                    tasks?: Record<string, string>;
+                  };
</file context>
Fix with Cubic

| "Registry";
| "Registry"
| "GitHub"
| "VM";
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 10, 2026

Choose a reason for hiding this comment

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

P2: VM tools are never returned by getToolsByCategory because the new "VM" category was added without adding a VM bucket to the grouped map.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/tools/registry-metadata.ts, line 36:

<comment>VM tools are never returned by `getToolsByCategory` because the new `"VM"` category was added without adding a `VM` bucket to the grouped map.</comment>

<file context>
@@ -32,7 +32,8 @@ export type ToolCategory =
   | "Registry"
-  | "GitHub";
+  | "GitHub"
+  | "VM";
 
 /**
</file context>
Fix with Cubic

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.

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.

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.

@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 and others added 22 commits April 10, 2026 22: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>
Add @freestyle-sh/with-deno integration for Deno projects. Port is now
configurable via the Repository tab and auto-detected from package.json
or deno.json scripts. Wrap systemd service commands with explicit PATH
to find runtime binaries (deno, bun, node).

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

Add external link button to preview toolbar for opening the dev server
in a new browser tab. Fall back to {vmId}.freestyle.run when domains
array is empty. Guard against undefined domains with explicit error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch from ports config (which returned empty domains array) to
Freestyle's domains config with *.style.dev subdomains. Domain is
derived from the virtualMcpId for deterministic, reusable URLs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dev servers that bind to localhost only (like Deno Fresh) aren't
accessible from the Freestyle domain. Add a socat proxy service that
forwards from 0.0.0.0:9999 to localhost:PORT, and map the domain to
the proxy port instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tlgimenes and others added 9 commits April 10, 2026 22:12
…d UI issues

- Use @freestyle-sh/with-deno and @freestyle-sh/with-bun integrations via
  `with` property on freestyle.vms.create() instead of manual curl install
  scripts. Runtimes are now pre-installed and cached by Freestyle.
- Fix systemd web-terminal exec: remove bash -c wrapper that caused
  "touch: missing file operand" (systemd doesn't interpret single quotes).
  Log viewer script now creates /tmp/vm.log itself.
- Fix UI: "Reinstall Dependencies" no longer triggers dev server restart.
- Fix UI: pollPreview no longer force-closes terminal when preview loads.
- Remove needsRuntimeInstall flag and /opt/setup-runtime.sh from helpers/exec.
- Refactor ViewModeToggle to use map loop and support tooltips.

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

Removes the ~50-line inline Node.js SSE server (LOG_VIEWER_SCRIPT) and
ensureLogViewer() in favour of VmWebTerminal (ttyd). The terminal domain is
routed post-creation via vm.terminal.logs.route(), which creates a persistent
domain mapping that survives VM resumes. If route() fails the VM is still
persisted with terminalUrl: null so no VM is orphaned.

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

- Flatten withIntegrations into a single `plugins` object with always-present
  runtime (VmNodeJs as default) and terminal keys — removes the ternary spread
- Move PROXY_PORT, BOOTSTRAP_SCRIPT, PROXY_SCRIPT to module scope
- Replace inline Array type with SystemdServiceInput from freestyle-sandboxes
- Add console.log for detected runtime
- Add VmNodeJs mock in start.test.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each user's VM gets its own deterministic domain derived from both the
Virtual MCP ID and the user ID, matching the activeVms[userId] key.
Previously all users of the same Virtual MCP shared the same domain,
causing collisions when multiple users created VMs concurrently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switches from the plain `with: {}` object form to VmSpec chaining, which
is the documented approach and correctly triggers each integration's
configure() hooks. Also fixes the key names to match docs conventions:
"deno", "js" (bun), "node" (node), "terminal". Removes duplicate
with-nodejs mock from tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- VmNodeJs is now always in the base VmSpec since the iframe-proxy
  systemd service runs Node.js on every VM regardless of project runtime
- For npm projects the spec stays as base (node + terminal); for deno/bun
  the corresponding runtime integration is chained on top
- Fix xterm.js initializing at 15px height: the pane containers were
  hidden with `invisible h-0` which collapsed the iframe to 0 before
  ttyd loaded — changed to `invisible overflow-hidden` so the container
  keeps its full dimensions while invisible

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
VmDeno installs to /opt/deno/bin and VmBun to /opt/bun/bin — neither
is in the default PATH for non-interactive bash shells. Running
`deno install` or `bun install` via vm.exec() fails with
"command not found".

Adds runtimeBinPath to resolveRuntimeConfig() and prepends it via
`export PATH=...` before the install and dev scripts in VM_EXEC.
npm projects are unaffected (system node/npm already at /usr/local/bin).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…m.delete()

Removes the DB entry first so the UI transitions to idle immediately,
then fires vm.delete() without awaiting so slow VM teardown doesn't
block the response.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tlgimenes tlgimenes force-pushed the tlgimenes/github-repo-picker branch from d26b573 to 8a5965f Compare April 11, 2026 01:13
tlgimenes and others added 20 commits April 10, 2026 22:28
Move git repo, additionalFiles, and systemd service declarations into
the VmSpec chain instead of passing them as separate create() options.
Pass spec to freestyle.vms.ref() to restore typed helpers on resume.

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

When VM_START returns isNewVm:false (existing VM resumed from suspension),
the preview code assumed the dev server was still running. But nohup
processes may die after suspend/resume, leaving the iframe-proxy and dev
server dead. Freestyle then serves a "Reloading..." placeholder page in
a loop. Now we always run handleExec("dev") + pollPreview() for existing
VMs, matching what handleResume already does.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use bash -lc for iframe-proxy systemd service so NVM's node is on PATH
- Bake upstream port directly into proxy script instead of relying on
  systemd env vars (was falling back to port 3000)
- Add /tmp/vm.log as additionalFile so tail -f doesn't fail on boot
- Fire-and-forget all vm.exec() calls to avoid MCP request timeouts
- Remove pgrep iframe-proxy fallback from VM_EXEC (systemd manages it)
- Add logging to iframe-proxy for debugging
- Await vm.delete() with 10s timeout instead of fire-and-forget

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use wrapper shell script for iframe-proxy to source NVM properly
  (bash -lc quoting was stripped by Freestyle, and NVM profile was missing)
- Add wantedBy + after/requires install-nodejs.service so proxy starts
  after node is installed
- Stop VM before deleting for reliable cleanup
- Add stopping state to preview UI

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VmNodeJs integration creates /etc/profile.d/nvm.sh — use that instead
of guessing NVM_DIR path.

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

Visual editor prompt now sends messages to the existing thread and opens
the chat panel instead of creating a new thread each time. GitHub repo
integration is gated behind an experimental_vibecode preference toggle.

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

Replace three CSS-toggled layout divs (preview-only, split-view, terminal-only)
with a single ResizablePanelGroup. Terminal panel visibility is driven by the
panel's imperative collapse/expand API instead of CSS visibility toggling.
Panels use overflow-hidden and rounded-[inherit] to match parent card styling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MockVmSpec was missing repo(), additionalFiles(), and systemdService() methods
added by the fluent API refactor (7492def). Test assertions updated:
- repos.create is no longer called (repo is now set via VmSpec.repo())
- systemd services are now in spec._services (not createCall.systemd.services)
- additional files are now in spec._files (not createCall.additionalFiles)

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

Add Link header pagination to GITHUB_LIST_INSTALLATIONS and GITHUB_LIST_REPOS
so all results are returned when a user has more than 100 installations or repos.

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

- Clear activeVms when user connects a new GitHub repo so VM_START
  creates a fresh VM with the new repo instead of resuming the old one
- Add console.log for repo/runtime at VM creation
- Replace idleTimeoutSeconds with sticky persistence + recreate:true
- Comment out VmWebTerminal to isolate install-ttyd boot failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iling, liveness probe

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

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>
…ased suspension

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

The injected visual editor script had no cleanup path — switching back to
interactive mode left highlight outlines, badge, and cursor override in the
iframe. Added a `visual-editor::deactivate` postMessage handler that removes
DOM elements and detaches event listeners. Also renamed "Preview" tooltip to
"Interactive".

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>
Replace direct GitHub URL cloning with Freestyle Git repos and GitHub
Sync so that private repositories can be cloned via the GitHub App.

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.

1 participant