Skip to content

feat: local dev agents — project detection, agent team, dev server preview#3018

Open
vibegui wants to merge 2 commits intomainfrom
vibegui/local-dev-agents
Open

feat: local dev agents — project detection, agent team, dev server preview#3018
vibegui wants to merge 2 commits intomainfrom
vibegui/local-dev-agents

Conversation

@vibegui
Copy link
Copy Markdown
Contributor

@vibegui vibegui commented Apr 6, 2026

Summary

One command to optimize your site with AI. Run bunx decocms in any project directory → choose Claude Code → a team of AI agents starts working on your project.

What it does

  • Project Scanner — Auto-detects framework (Next.js, Fresh, Astro, Vite, Remix, Nuxt), package manager, dev command, deploy target from filesystem config files
  • Agent Team — Creates specialized agents on startup: Project Hub, Dev Server, Dependencies, Performance, Framework Expert, Deploy
  • Dev Server — Auto-starts the project's dev server as a managed subprocess with start/stop/restart API
  • Live Preview — iframe-based preview panel showing the running dev server, with toolbar (refresh, open external)
  • Topbar — Shows project name, path, and dev server status (green/yellow/red dot) in the center of the topbar
  • Onboarding — Redesigned provider selection: "decocms is ready for agents" with Claude Code/Codex highlighted as "Local models"
  • Sidebar Fix — Server-pinned agents now sync into localStorage so new agents appear without clearing cache

WIP / Next steps

  • Hub agent chat should show setup messages as persistent conversation (agents appearing one by one)
  • Each agent mention in chat should be clickable to navigate
  • Agents should proactively start working (dep audit, perf check, etc.)
  • Setup sequence as real AI conversation, not scripted animation

Test plan

  • Run bun run apps/mesh/src/cli.ts dev --project ~/Projects/decocms from worktree root
  • Verify topbar shows project name with green status dot
  • Click Claude Code → navigates to hub agent with preview
  • Preview iframe loads the running dev server
  • Project agents appear in sidebar (may need to clear localStorage)
  • GET /api/project returns scan result and dev server state

🤖 Generated with Claude Code


Summary by cubic

Adds local dev agents that auto-detect your project, spin up a specialist team, manage your dev server, and show a live preview in-app. Introduces /api/project endpoints, a new --project CLI flag for explicit directory selection, and an Article Writer agent for content-rich projects.

  • New Features

    • Project detection: scans framework, package manager, dev/build commands, deploy target; shows project name/path with a status dot in the topbar.
    • Agent team: auto-creates Overview, Dev Server, Dependencies, Performance, Framework Expert, Deploy, and Article Writer (when content dirs exist) agents when projectDir is set.
    • Article Writer: created when blog/, content/, or docs/ is present; writes/edits markdown with frontmatter and tone-of-voice guidance; live preview via the dev server.
    • Dev server: start/stop/restart with logs; auto-start on bootstrap; iframe preview with refresh/open external; preview proxy under /api/project/preview/* strips frame-blocking headers.
    • API: /api/project (scan + state), /api/project/dev-server (start/stop/restart/logs), lightweight polling for dev server state, and GET /api/config now includes projectDir and projectName.
    • CLI/UX: --project flag and CWD auto-detection; improved provider selection with “Local models”; clearer empty states; project agents prioritized in the sidebar; onboarding setup sequence that plays after provider activation and navigates to the Dev Server agent.
  • Bug Fixes

    • Server-pinned agents now sync into localStorage so new agents appear without clearing cache.

Written for commit 0f6212a. Summary will update on new commits.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

🧪 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 6, 2026

Release Options

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

React with an emoji to override the release type:

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

Current version: 2.252.1

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 30 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/web/views/settings/org-ai-providers.tsx">

<violation number="1" location="apps/mesh/src/web/views/settings/org-ai-providers.tsx:543">
P2: `onActivated` runs before refreshing virtual MCP data, so hub-agent navigation can race against stale cache and miss the newly created project agent.</violation>
</file>

<file name="apps/mesh/src/project/bootstrap.ts">

<violation number="1" location="apps/mesh/src/project/bootstrap.ts:70">
P1: Agent deduplication is organization-wide instead of project-scoped, so bootstrapping one project can suppress agent creation for another project in the same org.</violation>
</file>

<file name="apps/mesh/src/project/scanner.ts">

<violation number="1" location="apps/mesh/src/project/scanner.ts:106">
P2: `detectDeployTarget` misses Deno Deploy configs in `deno.jsonc`, causing false negatives for deploy target detection.</violation>
</file>

<file name="apps/mesh/src/web/hooks/use-pinned-agents.ts">

<violation number="1" location="apps/mesh/src/web/hooks/use-pinned-agents.ts:16">
P2: Reset the sync-tracking ref when the storage key changes; otherwise old org/user IDs can block syncing in the new context.</violation>
</file>

<file name="apps/mesh/src/project/dev-server.ts">

<violation number="1" location="apps/mesh/src/project/dev-server.ts:73">
P2: Return a cloned `logs` array to avoid exposing mutable internal state through `getDevServerState()`.</violation>

<violation number="2" location="apps/mesh/src/project/dev-server.ts:126">
P1: Guard the `exited` callback so only the currently active subprocess can update global dev-server state.</violation>
</file>

<file name="apps/mesh/src/api/routes/project.ts">

<violation number="1" location="apps/mesh/src/api/routes/project.ts:106">
P1: The preview proxy can be escaped to arbitrary hosts via `//`-prefixed paths, creating an open proxy/SSRF vector.</violation>

<violation number="2" location="apps/mesh/src/api/routes/project.ts:114">
P1: Do not forward authentication/session headers to the proxied dev server; this leaks sensitive credentials across origins.</violation>
</file>

<file name="apps/mesh/src/web/components/project-setup-sequence.tsx">

<violation number="1" location="apps/mesh/src/web/components/project-setup-sequence.tsx:206">
P2: This render-time `setTimeout` starts side effects before commit, which can run multiple times on render retries and duplicate the setup sequence.</violation>
</file>

<file name="apps/mesh/src/api/app.ts">

<violation number="1" location="apps/mesh/src/api/app.ts:570">
P1: `/api/project` is mounted as a public route, exposing unauthenticated dev-server start/stop/restart and preview-proxy endpoints.</violation>
</file>

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

typeof agent.metadata === "string"
? JSON.parse(agent.metadata)
: agent.metadata;
if (meta.projectAgentType) {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P1: Agent deduplication is organization-wide instead of project-scoped, so bootstrapping one project can suppress agent creation for another project in the same org.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/project/bootstrap.ts, line 70:

<comment>Agent deduplication is organization-wide instead of project-scoped, so bootstrapping one project can suppress agent creation for another project in the same org.</comment>

<file context>
@@ -0,0 +1,131 @@
+          typeof agent.metadata === "string"
+            ? JSON.parse(agent.metadata)
+            : agent.metadata;
+        if (meta.projectAgentType) {
+          existingTypes.add(meta.projectAgentType);
+          agentIds.push(agent.id);
</file context>
Fix with Cubic

if (child.stderr) pipeOutput(child.stderr as ReadableStream<Uint8Array>);

// Monitor process exit
child.exited.then((code) => {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P1: Guard the exited callback so only the currently active subprocess can update global dev-server state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/project/dev-server.ts, line 126:

<comment>Guard the `exited` callback so only the currently active subprocess can update global dev-server state.</comment>

<file context>
@@ -0,0 +1,189 @@
+    if (child.stderr) pipeOutput(child.stderr as ReadableStream<Uint8Array>);
+
+    // Monitor process exit
+    child.exited.then((code) => {
+      if (_state.status === "running" || _state.status === "starting") {
+        if (code === 0 || code === null) {
</file context>
Fix with Cubic

targetUrl.search = reqUrl.search;

try {
const headers = new Headers(c.req.raw.headers);
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P1: Do not forward authentication/session headers to the proxied dev server; this leaks sensitive credentials across origins.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/routes/project.ts, line 114:

<comment>Do not forward authentication/session headers to the proxied dev server; this leaks sensitive credentials across origins.</comment>

<file context>
@@ -0,0 +1,143 @@
+  targetUrl.search = reqUrl.search;
+
+  try {
+    const headers = new Headers(c.req.raw.headers);
+    // Remove host header to avoid conflicts
+    headers.delete("host");
</file context>
Fix with Cubic

}

// Reconstruct the target URL from the wildcard path
const path = c.req.path.replace(/^\/api\/project\/preview\/?/, "/");
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P1: The preview proxy can be escaped to arbitrary hosts via //-prefixed paths, creating an open proxy/SSRF vector.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/routes/project.ts, line 106:

<comment>The preview proxy can be escaped to arbitrary hosts via `//`-prefixed paths, creating an open proxy/SSRF vector.</comment>

<file context>
@@ -0,0 +1,143 @@
+  }
+
+  // Reconstruct the target URL from the wildcard path
+  const path = c.req.path.replace(/^\/api\/project\/preview\/?/, "/");
+  const targetUrl = new URL(path || "/", devServer.url);
+
</file context>
Fix with Cubic

// Public Configuration (no auth required)
// ============================================================================
app.route("/api/config", publicConfigRoutes);
app.route("/api/project", projectRoutes);
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P1: /api/project is mounted as a public route, exposing unauthenticated dev-server start/stop/restart and preview-proxy endpoints.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/api/app.ts, line 570:

<comment>`/api/project` is mounted as a public route, exposing unauthenticated dev-server start/stop/restart and preview-proxy endpoints.</comment>

<file context>
@@ -566,6 +567,7 @@ export async function createApp(options: CreateAppOptions = {}) {
   // Public Configuration (no auth required)
   // ============================================================================
   app.route("/api/config", publicConfigRoutes);
+  app.route("/api/project", projectRoutes);
 
   // ============================================================================
</file context>
Fix with Cubic

if (await fileExists(join(dir, "wrangler.jsonc"))) return "cloudflare";

// Check deno.json for deploy config
const denoJson = await readJson(join(dir, "deno.json"));
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: detectDeployTarget misses Deno Deploy configs in deno.jsonc, causing false negatives for deploy target detection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/project/scanner.ts, line 106:

<comment>`detectDeployTarget` misses Deno Deploy configs in `deno.jsonc`, causing false negatives for deploy target detection.</comment>

<file context>
@@ -0,0 +1,295 @@
+  if (await fileExists(join(dir, "wrangler.jsonc"))) return "cloudflare";
+
+  // Check deno.json for deploy config
+  const denoJson = await readJson(join(dir, "deno.json"));
+  if (denoJson?.deploy) return "deno-deploy";
+
</file context>
Fix with Cubic

);

// Track which server IDs we've already synced to avoid re-running
const syncedRef = useRef<Set<string>>(new Set(pinnedIds));
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: Reset the sync-tracking ref when the storage key changes; otherwise old org/user IDs can block syncing in the new context.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/hooks/use-pinned-agents.ts, line 16:

<comment>Reset the sync-tracking ref when the storage key changes; otherwise old org/user IDs can block syncing in the new context.</comment>

<file context>
@@ -1,16 +1,37 @@
   );
 
+  // Track which server IDs we've already synced to avoid re-running
+  const syncedRef = useRef<Set<string>>(new Set(pinnedIds));
+
+  // Sync new server-pinned IDs into the local list.
</file context>
Suggested change
const syncedRef = useRef<Set<string>>(new Set(pinnedIds));
const syncedRef = useRef<Set<string>>(new Set(pinnedIds));
const syncedStorageKeyRef = useRef(storageKey);
if (syncedStorageKeyRef.current !== storageKey) {
syncedStorageKeyRef.current = storageKey;
syncedRef.current = new Set(pinnedIds);
}
Fix with Cubic

}

export function getDevServerState(): DevServerState {
return { ..._state };
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: Return a cloned logs array to avoid exposing mutable internal state through getDevServerState().

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/project/dev-server.ts, line 73:

<comment>Return a cloned `logs` array to avoid exposing mutable internal state through `getDevServerState()`.</comment>

<file context>
@@ -0,0 +1,189 @@
+}
+
+export function getDevServerState(): DevServerState {
+  return { ..._state };
+}
+
</file context>
Suggested change
return { ..._state };
return { ..._state, logs: [..._state.logs] };
Fix with Cubic

…eam, start dev server

When running `bunx decocms` from a project directory, the system now:
- Detects the project stack (Next.js, Fresh, Astro, Vite, Remix, Nuxt, etc.)
- Shows project name and path in the topbar with dev server status
- Auto-creates a team of specialized agents (Overview, Dev Server, Dependencies, Performance, Framework Expert, Deploy)
- Starts the project's dev server and shows a live preview in an iframe
- Groups Claude Code / Codex as "Local models" with a highlighted card on the provider selection screen
- Improves onboarding copy: "decocms is ready for agents" instead of "No model provider connected"
- Fixes sidebar agent pinning to sync new server-pinned agents into localStorage

New files: project/scanner.ts, project/agent-templates.ts, project/bootstrap.ts, project/dev-server.ts, project/state.ts, api/routes/project.ts, web/components/preview-panel.tsx, topbar-project-info.tsx, use-project-info.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vibegui vibegui force-pushed the vibegui/local-dev-agents branch from e3fbc41 to 4c4b348 Compare April 9, 2026 21:49
Detects blog/, content/, docs/ directories during project scanning.
When found, creates an Article Writer agent with instructions tailored
to the project's content structure (frontmatter schema, tone of voice,
content pipeline with briefs/drafts/published workflow).

The agent uses Claude Code for actual file editing — no separate MCP
server needed. The dev server preview shows rendered articles in
real-time.

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

2 issues found across 2 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/project/agent-templates.ts">

<violation number="1" location="apps/mesh/src/project/agent-templates.ts:371">
P2: The writer agent is activated for `docs`-only projects even though its instructions only handle blog/content directories, which produces incorrect guidance.</violation>
</file>

<file name="apps/mesh/src/project/scanner.ts">

<violation number="1" location="apps/mesh/src/project/scanner.ts:299">
P2: Check that each content candidate is a directory, not just an existing path, before adding it to `contentDirs`.</violation>
</file>

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

- Tags are lowercase strings
- Slugs are kebab-case`;
},
applicableWhen: (scan) => scan.contentDirs.length > 0,
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: The writer agent is activated for docs-only projects even though its instructions only handle blog/content directories, which produces incorrect guidance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/project/agent-templates.ts, line 371:

<comment>The writer agent is activated for `docs`-only projects even though its instructions only handle blog/content directories, which produces incorrect guidance.</comment>

<file context>
@@ -284,4 +284,90 @@ When the user asks to deploy, guide them through the process step by step.`;
+- Tags are lowercase strings
+- Slugs are kebab-case`;
+    },
+    applicableWhen: (scan) => scan.contentDirs.length > 0,
+  },
 ];
</file context>
Suggested change
applicableWhen: (scan) => scan.contentDirs.length > 0,
applicableWhen: (scan) =>
scan.contentDirs.some(
(d) => d.type === "blog" || d.type === "content",
),
Fix with Cubic

{ dir: "docs", type: "docs" },
];
for (const candidate of contentCandidates) {
if (existsSync(join(projectDir, candidate.dir))) {
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

P2: Check that each content candidate is a directory, not just an existing path, before adding it to contentDirs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/project/scanner.ts, line 299:

<comment>Check that each content candidate is a directory, not just an existing path, before adding it to `contentDirs`.</comment>

<file context>
@@ -280,6 +288,28 @@ export async function scanProject(
+    { dir: "docs", type: "docs" },
+  ];
+  for (const candidate of contentCandidates) {
+    if (existsSync(join(projectDir, candidate.dir))) {
+      const configFile = (await fileExists(
+        join(projectDir, candidate.dir, "config.json"),
</file context>
Fix with Cubic

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