feat(local-dev): native folder picker and simplified project creation#2691
feat(local-dev): native folder picker and simplified project creation#2691
Conversation
🧪 BenchmarkShould we run the Virtual MCP strategy benchmark for this PR? React with 👍 to run the benchmark.
Benchmark will run on the next push after you react. |
Release OptionsShould a new version be published when this PR is merged? React with an emoji to vote on the release type:
Current version: Deployment
|
There was a problem hiding this comment.
7 issues found across 671 files
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
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/docs/client/src/content/2025-10-10/pt-br/no-code-guides/creating-agents.mdx">
<violation number="1" location="apps/docs/client/src/content/2025-10-10/pt-br/no-code-guides/creating-agents.mdx:93">
P2: The PT-BR refund limit guidance is inconsistent with the English source (R$500 vs $100), which can lead to different agent behavior depending on locale.
(Based on your team's feedback about treating English documentation as the authoritative source.) [FEEDBACK_USED]</violation>
</file>
<file name=".github/workflows/release.yaml">
<violation number="1" location=".github/workflows/release.yaml:70">
P1: The npm version check queries the wrong package (`decocms` instead of `@decocms/mesh`), which can incorrectly force publish attempts for already-released versions.</violation>
</file>
<file name=".cursor/skills/respond-to-pr-review/SKILL.md">
<violation number="1" location=".cursor/skills/respond-to-pr-review/SKILL.md:52">
P2: Filter replies by the authenticated user before considering a comment addressed; otherwise replies from other users incorrectly suppress unaddressed review items.
(Based on your team's feedback about only treating comments as addressed when the current authenticated user replied.) [FEEDBACK_USED]</violation>
</file>
<file name="apps/docs/astro.config.mjs">
<violation number="1" location="apps/docs/astro.config.mjs:16">
P2: `publicDir` is relative to the configured `root`. With `root: "client"`, setting `publicDir: "client/public"` resolves to `client/client/public` and will break static asset resolution. Use `public` (relative to root) or an absolute URL if you meant the repo root.</violation>
</file>
<file name="apps/docs/client/src/content/2026-03-10/pt-br/mcp-mesh/self-hosting/deploy/kubernetes.mdx">
<violation number="1" location="apps/docs/client/src/content/2026-03-10/pt-br/mcp-mesh/self-hosting/deploy/kubernetes.mdx:37">
P2: The architecture image URL is broken due to an invalid GitHub owner path (`deco CMS` with a space).</violation>
</file>
<file name="apps/docs/client/src/layouts/DocsLayout.astro">
<violation number="1" location="apps/docs/client/src/layouts/DocsLayout.astro:22">
P2: Avoid hardcoding the fallback docs version; use a non-date alias/source of truth so this doesn’t drift on future version bumps.</violation>
</file>
<file name=".claude/settings.json">
<violation number="1" location=".claude/settings.json:8">
P1: Validate `name` before building `DIR`/`BRANCH`; currently path segments like `../` can escape the intended `.claude/worktrees` directory.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| # Check if this specific version already exists in npm | ||
| # This works for both stable and prerelease versions (unlike checking 'latest' tag) | ||
| if npm view @decocms/mesh@$CURRENT_VERSION version >/dev/null 2>&1; then | ||
| if npm view decocms@$CURRENT_VERSION version >/dev/null 2>&1; then |
There was a problem hiding this comment.
P1: The npm version check queries the wrong package (decocms instead of @decocms/mesh), which can incorrectly force publish attempts for already-released versions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yaml, line 70:
<comment>The npm version check queries the wrong package (`decocms` instead of `@decocms/mesh`), which can incorrectly force publish attempts for already-released versions.</comment>
<file context>
@@ -66,7 +67,7 @@ jobs:
# Check if this specific version already exists in npm
# This works for both stable and prerelease versions (unlike checking 'latest' tag)
- if npm view @decocms/mesh@$CURRENT_VERSION version >/dev/null 2>&1; then
+ if npm view decocms@$CURRENT_VERSION version >/dev/null 2>&1; then
echo "version-changed=false" >> $GITHUB_OUTPUT
echo "⏭️ Version $CURRENT_VERSION already published, skipping publish"
</file context>
| if npm view decocms@$CURRENT_VERSION version >/dev/null 2>&1; then | |
| if npm view @decocms/mesh@$CURRENT_VERSION version >/dev/null 2>&1; then |
| "hooks": [ | ||
| { | ||
| "type": "command", | ||
| "command": "sh -c 'INPUT=$(cat); NAME=$(echo \"$INPUT\" | jq -r .name); CWD=$(echo \"$INPUT\" | jq -r .cwd); DIR=\"$CWD/.claude/worktrees/$NAME\"; BRANCH=\"claude/$NAME\"; git -C \"$CWD\" worktree add -b \"$BRANCH\" \"$DIR\" HEAD >&2; cp \"$CWD/apps/mesh/.env\" \"$DIR/apps/mesh/.env\" 2>/dev/null >&2 || true; echo \"$DIR\"'" |
There was a problem hiding this comment.
P1: Validate name before building DIR/BRANCH; currently path segments like ../ can escape the intended .claude/worktrees directory.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/settings.json, line 8:
<comment>Validate `name` before building `DIR`/`BRANCH`; currently path segments like `../` can escape the intended `.claude/worktrees` directory.</comment>
<file context>
@@ -0,0 +1,14 @@
+ "hooks": [
+ {
+ "type": "command",
+ "command": "sh -c 'INPUT=$(cat); NAME=$(echo \"$INPUT\" | jq -r .name); CWD=$(echo \"$INPUT\" | jq -r .cwd); DIR=\"$CWD/.claude/worktrees/$NAME\"; BRANCH=\"claude/$NAME\"; git -C \"$CWD\" worktree add -b \"$BRANCH\" \"$DIR\" HEAD >&2; cp \"$CWD/apps/mesh/.env\" \"$DIR/apps/mesh/.env\" 2>/dev/null >&2 || true; echo \"$DIR\"'"
+ }
+ ]
</file context>
| Você pode processar reembolsos de até R$500. Para valores maiores, abra um ticket | ||
| e informe o cliente que um gerente fará a revisão. | ||
| Você pode: | ||
| - Processar reembolsos abaixo de R$500 usando @PROCESS_REFUND |
There was a problem hiding this comment.
P2: The PT-BR refund limit guidance is inconsistent with the English source (R$500 vs $100), which can lead to different agent behavior depending on locale.
(Based on your team's feedback about treating English documentation as the authoritative source.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/client/src/content/2025-10-10/pt-br/no-code-guides/creating-agents.mdx, line 93:
<comment>The PT-BR refund limit guidance is inconsistent with the English source (R$500 vs $100), which can lead to different agent behavior depending on locale.
(Based on your team's feedback about treating English documentation as the authoritative source.) </comment>
<file context>
@@ -79,89 +73,114 @@ O system prompt define as características do seu agent:
-Você pode processar reembolsos de até R$500. Para valores maiores, abra um ticket
-e informe o cliente que um gerente fará a revisão.
+Você pode:
+- Processar reembolsos abaixo de R$500 usando @PROCESS_REFUND
+- Redefinir senhas usando @RESET_PASSWORD
+- Consultar status da conta usando @GET_ACCOUNT_STATUS
</file context>
| # Get IDs that have been replied to | ||
| REPLIED_IDS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \ | ||
| --paginate \ | ||
| --jq '[.[] | select(.in_reply_to_id != null) | .in_reply_to_id] | unique') |
There was a problem hiding this comment.
P2: Filter replies by the authenticated user before considering a comment addressed; otherwise replies from other users incorrectly suppress unaddressed review items.
(Based on your team's feedback about only treating comments as addressed when the current authenticated user replied.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .cursor/skills/respond-to-pr-review/SKILL.md, line 52:
<comment>Filter replies by the authenticated user before considering a comment addressed; otherwise replies from other users incorrectly suppress unaddressed review items.
(Based on your team's feedback about only treating comments as addressed when the current authenticated user replied.) </comment>
<file context>
@@ -0,0 +1,122 @@
+# Get IDs that have been replied to
+REPLIED_IDS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \
+ --paginate \
+ --jq '[.[] | select(.in_reply_to_id != null) | .in_reply_to_id] | unique')
+```
+
</file context>
| "/": "/latest/en/introduction", | ||
| }, | ||
| outDir: "dist/client/", | ||
| publicDir: "client/public", |
There was a problem hiding this comment.
P2: publicDir is relative to the configured root. With root: "client", setting publicDir: "client/public" resolves to client/client/public and will break static asset resolution. Use public (relative to root) or an absolute URL if you meant the repo root.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/astro.config.mjs, line 16:
<comment>`publicDir` is relative to the configured `root`. With `root: "client"`, setting `publicDir: "client/public"` resolves to `client/client/public` and will break static asset resolution. Use `public` (relative to root) or an absolute URL if you meant the repo root.</comment>
<file context>
@@ -1,52 +1,21 @@
- "/": "/latest/en/introduction",
- },
outDir: "dist/client/",
+ publicDir: "client/public",
srcDir: "client/src",
- integrations: [mdx(), react(), patchCsrRedirect()],
</file context>
| publicDir: "client/public", | |
| publicDir: "public", |
| ### Arquitetura | ||
|
|
||
|  | ||
|  |
There was a problem hiding this comment.
P2: The architecture image URL is broken due to an invalid GitHub owner path (deco CMS with a space).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/client/src/content/2026-03-10/pt-br/mcp-mesh/self-hosting/deploy/kubernetes.mdx, line 37:
<comment>The architecture image URL is broken due to an invalid GitHub owner path (`deco CMS` with a space).</comment>
<file context>
@@ -1,103 +1,103 @@
### Arquitetura
-
+
## Pré-requisitos
</file context>
|  | |
|  |
dcc6353 to
1026dcf
Compare
41a1cfe to
421ad07
Compare
… flow Simplify the local folder project creation with native macOS folder picker (osascript) instead of custom directory browser. Add git panel with branch management and commit history, file watching via SSE, and integration with object-storage and preview plugins. Local connections now use empty connection_url instead of null to satisfy DB constraints. - Add /api/local-dev/pick-folder endpoint (native Finder dialog via osascript) - Simplify FolderStep UI to single dashed card for folder selection - Add GitPanel component with branch switching, commit history, and diff viewing - Add file watch support via SSE (useConnectionWatch hook) - Add git operation helpers (gitStatus, gitCommit, gitBranch, gitLog) - Fix MeshContext middleware ordering (local-dev routes mounted after context injection) - Adapt git-panel AI features to new AI provider system (useAiProviderKeyList) - Add query keys for git operations (KEYS.gitBranch, gitStatus, etc.) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
421ad07 to
7476141
Compare
…d MCP App preview - From Folder click directly opens OS picker and auto-creates project (no modal) - Add connection to project via projectConnections.add() so it appears in settings - Preserve _meta field when saving tools so preview UI resource URI is discoverable - Rewrite preview tool with proper MCP ext-apps protocol (JSON-RPC handshake) - Server-side preview detection: static sites (index.html), dev servers, manual config - Remove FolderStep dialog component (replaced by inline pickFolderAndCreate flow) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
4 issues found across 4 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/routes/projects-list.tsx">
<violation number="1" location="apps/mesh/src/web/routes/projects-list.tsx:52">
P2: Show the picker error before returning when `/pick-folder` fails without a path.</violation>
<violation number="2" location="apps/mesh/src/web/routes/projects-list.tsx:173">
P2: Selecting "Blank Project" from the empty-state cards now reopens the selection step instead of the blank-project form.</violation>
</file>
<file name="apps/mesh/src/web/components/create-project-dialog.tsx">
<violation number="1" location="apps/mesh/src/web/components/create-project-dialog.tsx:90">
P1: `From Folder` becomes a no-op in local mode when this dialog is opened from callers that do not pass `onPickFolder`.</violation>
</file>
<file name="apps/mesh/src/mcp-clients/local/index.ts">
<violation number="1" location="apps/mesh/src/mcp-clients/local/index.ts:394">
P2: Script injection via unescaped `JSON.stringify` inside `<script>` tag. `JSON.stringify` doesn't escape `</` sequences, so a crafted `rootPath` or `detection.command` (from `.deco/preview.json`) can close the script tag and inject arbitrary HTML/JS. Use `.replace(/</g, '\\u003c')` after stringifying to neutralize the sequence.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| }; | ||
|
|
||
| const handleSelectFolder = () => { | ||
| if (onPickFolder) { |
There was a problem hiding this comment.
P1: From Folder becomes a no-op in local mode when this dialog is opened from callers that do not pass onPickFolder.
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/create-project-dialog.tsx, line 90:
<comment>`From Folder` becomes a no-op in local mode when this dialog is opened from callers that do not pass `onPickFolder`.</comment>
<file context>
@@ -109,22 +86,22 @@ export function CreateProjectDialog({
};
+ const handleSelectFolder = () => {
+ if (onPickFolder) {
+ handleOpenChange(false);
+ onPickFolder();
</file context>
| }); | ||
| const pick: { path?: string; cancelled?: boolean; error?: string } = | ||
| await pickRes.json(); | ||
| if (!pick.path) return; |
There was a problem hiding this comment.
P2: Show the picker error before returning when /pick-folder fails without a path.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/projects-list.tsx, line 52:
<comment>Show the picker error before returning when `/pick-folder` fails without a path.</comment>
<file context>
@@ -27,16 +28,109 @@ export default function ProjectsListPage() {
+ });
+ const pick: { path?: string; cancelled?: boolean; error?: string } =
+ await pickRes.json();
+ if (!pick.path) return;
+
+ // 2. Validate folder (gets name/slug, checks for existing project)
</file context>
| if (!pick.path) return; | |
| if (!pick.path) { | |
| if (pick.error) { | |
| toast.error(pick.error); | |
| } | |
| return; | |
| } |
| </div> | ||
| <ModeSelectionCards | ||
| onSelectFolder={pickFolderAndCreate} | ||
| onSelectBlank={() => setCreateDialogOpen(true)} |
There was a problem hiding this comment.
P2: Selecting "Blank Project" from the empty-state cards now reopens the selection step instead of the blank-project form.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/projects-list.tsx, line 173:
<comment>Selecting "Blank Project" from the empty-state cards now reopens the selection step instead of the blank-project form.</comment>
<file context>
@@ -75,24 +169,16 @@ export default function ProjectsListPage() {
- setCreateDialogOpen(true);
- }}
+ onSelectFolder={pickFolderAndCreate}
+ onSelectBlank={() => setCreateDialogOpen(true)}
+ folderPicking={folderPicking}
/>
</file context>
| var ROOT = ${JSON.stringify(rootPath)}; | ||
| var BASE_FILE_URL = ${JSON.stringify(baseFileUrl)}; | ||
| var detection = ${JSON.stringify(detection)}; |
There was a problem hiding this comment.
P2: Script injection via unescaped JSON.stringify inside <script> tag. JSON.stringify doesn't escape </ sequences, so a crafted rootPath or detection.command (from .deco/preview.json) can close the script tag and inject arbitrary HTML/JS. Use .replace(/</g, '\\u003c') after stringifying to neutralize the sequence.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/mcp-clients/local/index.ts, line 394:
<comment>Script injection via unescaped `JSON.stringify` inside `<script>` tag. `JSON.stringify` doesn't escape `</` sequences, so a crafted `rootPath` or `detection.command` (from `.deco/preview.json`) can close the script tag and inject arbitrary HTML/JS. Use `.replace(/</g, '\\u003c')` after stringifying to neutralize the sequence.</comment>
<file context>
@@ -192,46 +302,117 @@ function getPreviewHtml(rootPath: string): string {
+// ---- Preview state ----
+
+var ROOT = ${JSON.stringify(rootPath)};
+var BASE_FILE_URL = ${JSON.stringify(baseFileUrl)};
+var detection = ${JSON.stringify(detection)};
</file context>
| var ROOT = ${JSON.stringify(rootPath)}; | |
| var BASE_FILE_URL = ${JSON.stringify(baseFileUrl)}; | |
| var detection = ${JSON.stringify(detection)}; | |
| var ROOT = ${JSON.stringify(rootPath).replace(/</g, "\\u003c")}; | |
| var BASE_FILE_URL = ${JSON.stringify(baseFileUrl).replace(/</g, "\\u003c")}; | |
| var detection = ${JSON.stringify(detection).replace(/</g, "\\u003c")}; |
What is this contribution about?
Simplifies local folder project creation from complex daemon discovery to a single-click native folder picker. Users can now:
Includes a complete Git Panel for branch management, commit history, diffs, and AI-powered commit messages. File watching via SSE, file serving for preview/storage plugins, and bash tool integration.
Screenshots/Demonstration
How to Test
MESH_LOCAL_MODE=true bun run dev~/my-project)Migration Notes
metadata: { sourceProvider: "local-filesystem", localDevRoot: "/path" }connection_url: ""(not null) for local connections/api/local-dev/files/:connectionId/*Review Checklist
🤖 Generated with Claude Code
Summary by cubic
Adds a one-click “From Folder” flow with a native macOS folder picker that auto-creates the project, connection, and in‑process local MCP for fast, zero‑config dev. Also ships a Git panel, live SSE file watching, secure local file serving, and an improved preview and file viewer experience.
New Features
/api/local-dev/pick-folder,/validate-folder,/create-project, SSE watch at/api/local-dev/watch/:connectionId, and presigned file serving at/api/local-dev/files/:connectionId/*with path traversal protection.@decocms/local-dev): filesystem + object storage tools andbash, SSE file watch, secure path resolution; clients are created in‑process for zero overhead.bash; preserves_metaso the UI can discover the preview resource URI./viewer?key=...) with Markdown preview and image rendering; file browser row click opens the viewer.localModeto show local‑only UI like “From Folder”; login supports a localhost‑onlyredirectTofor CLI auth.DECOCMS_HOME/assetsin local mode; project deletion cleans up unused localhost connections and virtual MCPs.Dependencies
@decocms/local-dev; includesmesh-plugin-preview.Written for commit e825a5b. Summary will update on new commits.