Skip to content

Feat: allow user to choose which directory to save Dyad apps in#2875

Merged
wwwillchen merged 39 commits intodyad-sh:mainfrom
RyanGroch:change-dyad-apps-dir
Mar 25, 2026
Merged

Feat: allow user to choose which directory to save Dyad apps in#2875
wwwillchen merged 39 commits intodyad-sh:mainfrom
RyanGroch:change-dyad-apps-dir

Conversation

@RyanGroch
Copy link
Copy Markdown
Collaborator

@RyanGroch RyanGroch commented Mar 3, 2026

Closes #399. Adds a setting (under "General Settings") to select a custom directory to store new apps in, replacing the default dyad-apps folder.

In order to make sure that users don't lose access to older apps, I opted for creating symlinks inside the new folder to the old app locations. I went with symlinking because moving every single app could be an expensive operation depending on how many apps there are, and the user might not want that. However, this is inconsistent with the import apps and move folder features (i.e. #2000), which copy the apps instead of symlinking.

I'm happy to change the approach on request. Some options I've been thinking about:

  • Add a button in the settings which moves all the apps (replacing the symlinks) after the user chooses a new custom directory. This way, the user would get to choose.
  • Convert all of previously-created apps to absolute paths, which avoids all of the symlinking. The only potential issue with this is that if the user wants to move the apps to their new directory after all, they'd have to use the "move app" feature for every single app, otherwise the database wouldn't get updated. As is, they at least could just delete all the symlinks and mass-move all of their apps into the new directory (though they'd currently have to do this outside of Dyad).

Also, since I'm tentatively adding in the use of symlinks, I modified the move/copy/delete app features so that they always target the true location of the app, not the symlink.

I can also write tests if desired.


Open with Devin

@RyanGroch RyanGroch requested a review from a team March 3, 2026 20:35
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances user flexibility by allowing them to define a custom base directory for their Dyad applications. This change addresses the need for personalized storage locations, moving beyond the previously fixed default folder. The implementation carefully handles the transition for existing applications by employing symlinks, which maintain accessibility without forcing a data migration. Furthermore, all app-related file system operations have been updated to intelligently navigate these symlinks, ensuring that the application behaves predictably and reliably with both new and existing app installations.

Highlights

  • Custom Dyad Apps Directory: Introduced a new setting under 'General Settings' allowing users to specify a custom base directory for storing new Dyad applications, replacing the default 'dyad-apps' folder.
  • Symlink Management for Existing Apps: Implemented a strategy to create symlinks for existing applications in the newly chosen custom directory, pointing back to their original locations. This ensures continued access to older apps without requiring a potentially expensive move operation.
  • Robust App Operation Handling: Modified core app management operations (copy, delete, move) to correctly resolve symlinks to their true physical locations before performing file system actions, ensuring consistent behavior regardless of whether an app is accessed via a symlink.
  • User Interface for Directory Selection: Added a dedicated UI component, 'DyadAppsBaseDirectorySelector', to the General Settings page, enabling users to easily select a custom folder, view the current path, and reset to the default directory.
Changelog
  • src/components/DyadAppsBaseDirectorySelector.tsx
    • Added a new React component for selecting, displaying, and managing the Dyad apps base directory.
    • Implemented state management for path selection, current path, and custom path status.
    • Integrated IPC calls for fetching, setting, and resetting the base directory, including success and error toasts.
  • src/ipc/handlers/app_handlers.ts
    • Modified app copy, delete, and move operations to resolve potential symlinks to their real paths before executing file system commands.
    • Added logic to clean up symlinks when an app's path is updated or an app is deleted, preventing dangling links.
  • src/ipc/handlers/dyad_apps_base_directory_handlers.ts
    • Added new IPC handlers for getting the current Dyad apps base directory, opening a dialog to select a new directory, and setting the chosen directory.
    • Implemented logic to create symlinks (or junctions on Windows) for all existing non-absolute apps when the base directory is changed, linking them from the new directory to their original locations.
    • Included robust error handling for directory validation and symlink creation, with logging for debugging.
  • src/ipc/ipc_host.ts
    • Imported the new registerDyadAppsBaseDirectoryHandlers function.
    • Registered the registerDyadAppsBaseDirectoryHandlers to enable the new IPC functionality.
  • src/ipc/types/system.ts
    • Defined new Zod schemas for SelectDyadAppsBaseDirectoryResult and GetDyadAppsBaseDirectoryResult to type IPC responses.
    • Added new IPC contracts (getDyadAppsBaseDirectory, selectDyadAppsBaseDirectory, setDyadAppsBaseDirectory) to the systemContracts object for managing the Dyad apps base directory.
  • src/lib/schemas.ts
    • Extended the BaseUserSettingsFields schema to include customDyadAppsBaseDirectory as an optional nullable string, allowing the setting to be stored.
  • src/main.ts
    • Updated the call to gitAddSafeDirectory to correctly access the path property from the object returned by getDyadAppsBaseDirectory.
  • src/pages/settings.tsx
    • Imported the DyadAppsBaseDirectorySelector component.
    • Integrated the DyadAppsBaseDirectorySelector into the General Settings page, providing the UI for the new feature.
  • src/paths/paths.ts
    • Refactored getDyadAppsBaseDirectory to return an object containing the current path, the default path, and a boolean indicating if a custom path is in use.
    • Modified getDyadAppPath to utilize the path property from the updated getDyadAppsBaseDirectory return value.
    • Added logic to read and write custom directory settings, and to reset to the default path if a configured custom path is invalid or inaccessible.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with πŸ‘ and πŸ‘Ž on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

devin-ai-integration[bot]

This comment was marked as resolved.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 3, 2026

Greptile Summary

This PR adds a General Settings option that lets users choose a custom top-level directory for storing new Dyad apps instead of the default ~/dyad-apps folder. When the directory changes, all existing apps with relative DB paths are converted to absolute paths so they remain accessible regardless of the new base directory.

Key design decisions:

  • Lazy directory creation β€” dyad-apps is only mkdir'd on first use rather than at DB init, and the default dir is never re-created after cache invalidation (by design, respecting the user's potential intent to remove it).
  • Relative β†’ absolute migration β€” setCustomAppsFolder runs a synchronous better-sqlite3 transaction to convert all relative paths to join(prevPath, relativePath) before the new setting is written, keeping the DB consistent across folder changes.
  • Caching β€” getDyadAppsBaseDirectory() caches both the resolved path and the raw settings value (cachedCustomFolderSetting) in a single ??-chained expression; invalidateDyadAppsBaseDirectoryCache() resets both and is called in every mutating IPC handler before reading state.
  • Accessibility guards β€” isAppLocationAccessible is added to createApp, copyApp, import, cloneRepo, and resetAll to fail with a clear error message when the folder is unreachable.

Issues found:

  • customAppsFolder schema uses z.string().optional().nullable(), which permits an empty string "". If stored manually in the settings file, getDyadAppsBaseDirectory() returns "", causing all app paths to be resolved relative to the process working directory.
  • renameApp is the only write-path operation that does not call isAppLocationAccessible before attempting the file move, producing a generic error message instead of pointing the user to the custom folder setting.

Confidence Score: 4/5

  • Safe to merge with minor fixes; no data-loss or security risk in the normal user flow.
  • The core path-resolution, caching, and migration logic is sound and well-tested by the new e2e suite. The two issues found are limited in blast radius: the empty-string schema gap requires deliberate settings-file editing to trigger, and the missing accessibility check in renameApp only degrades the error message quality rather than causing data loss or inconsistency.
  • src/lib/schemas.ts (empty-string schema gap) and src/ipc/handlers/app_handlers.ts (renameApp missing isAppLocationAccessible).

Important Files Changed

Filename Overview
src/paths/paths.ts Core path resolution with caching; new getDyadAppsBaseDirectory correctly caches custom/default path and exposes getCustomFolderCache(); isAppLocationAccessible and isDirectoryAccessible are well-guarded utility additions.
src/ipc/handlers/custom_apps_folder_handlers.ts New IPC handlers for get/select/set custom apps folder; path conversion transaction is correct and isolated; empty-string custom path could slip through schema validation.
src/lib/schemas.ts customAppsFolder field added as z.string().optional().nullable() β€” allows empty string which would cause getDyadAppsBaseDirectory to return "", making app paths relative to CWD.
src/ipc/handlers/app_handlers.ts Adds isAppLocationAccessible guards for createApp, copyApp, and resetAll; resetAll correctly captures basePath before settings deletion and resolves relative/absolute paths; renameApp is missing an analogous accessibility check.
src/components/CustomAppsFolderSelector.tsx Clean React component for folder selection with proper error handling, loading states, and conditional Reset-to-Default button.

Sequence Diagram

sequenceDiagram
    participant UI as CustomAppsFolderSelector
    participant IPC as IPC (custom_apps_folder_handlers)
    participant Paths as paths.ts (cache)
    participant DB as SQLite DB
    participant FS as Filesystem
    participant Settings as user-settings.json

    UI->>IPC: selectCustomAppsFolder()
    IPC->>FS: dialog.showOpenDialog()
    FS-->>IPC: { filePaths, canceled }
    IPC->>FS: isDirectoryAccessible(dirPath)
    IPC-->>UI: { path, canceled }

    UI->>IPC: setCustomAppsFolder(newPath)
    IPC->>Paths: invalidateDyadAppsBaseDirectoryCache()
    IPC->>Paths: getDyadAppsBaseDirectory() β†’ prevPath
    Paths->>Settings: readSettings().customAppsFolder
    Settings-->>Paths: cached & returned
    IPC->>FS: isDirectoryAccessible(newPath)
    Note over IPC,DB: Only if newPath !== prevPath
    IPC->>DB: transaction: convert relative β†’ absolute paths
    DB-->>IPC: done
    IPC->>Settings: writeSettings({ customAppsFolder })
    IPC->>Paths: invalidateDyadAppsBaseDirectoryCache()

    UI->>IPC: getCustomAppsFolder()
    IPC->>Paths: invalidateDyadAppsBaseDirectoryCache()
    IPC->>Paths: getDyadAppsBaseDirectory()
    Paths->>Settings: readSettings().customAppsFolder
    Settings-->>Paths: value (null = default)
    IPC->>FS: isDirectoryAccessible(directory)
    IPC->>Paths: getCustomFolderCache() β†’ null means default
    IPC-->>UI: { path, isPathAvailable, isPathDefault }
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/lib/schemas.ts
Line: 340

Comment:
**Empty string bypasses path resolution**

`z.string().optional().nullable()` accepts `""` as a valid value. If a user manually edits their settings file to set `customAppsFolder: ""`, `getDyadAppsBaseDirectory()` will return `""` (empty string is not nullish, so the `??` chain short-circuits there). `path.join("", "my-app")` then resolves to just `"my-app"` β€” a path relative to the process's working directory β€” silently storing and looking for apps in an unpredictable location.

Adding a minimum length constraint prevents this without affecting normal usage:

```suggestion
  customAppsFolder: z.string().min(1).optional().nullable(),
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/ipc/handlers/app_handlers.ts
Line: 1454-1457

Comment:
**`renameApp` missing accessibility check for the new path**

`createApp`, `copyApp`, `cloneRepo`, and `import` all call `isAppLocationAccessible` before performing write operations to the custom folder. `renameApp` does not. When `app.path` is relative (the normal case for apps that haven't been migrated to an absolute path), `newAppPath = getDyadAppPath(appPath)` resolves inside the current custom folder. If that folder is inaccessible the file move will fail with a generic `"Failed to move app files"` error, giving the user no indication that the root cause is the custom folder setting.

Adding the same guard used elsewhere would produce a consistent, actionable error message:

```ts
if (!isAppLocationAccessible(newAppPath)) {
  throw new Error(
    `The path ${newAppPath} is inaccessible. Please check your custom apps folder setting.`,
  );
}
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "fix: prevent path co..."

greptile-apps[bot]

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@RyanGroch RyanGroch marked this pull request as draft March 5, 2026 05:13
@wwwillchen
Copy link
Copy Markdown
Collaborator

hi @RyanGroch - thanks for the PR. yeah the existing apps makes this issue tricky (why is we haven't tackled it yet :)
i'm a little concerned about symlinks because users like to sometimes manipulate the files outside of Dyad (e.g. open the app in Cursor, or just do copy-paste backups) and symlinks can be quite confusing, especially for non-developers.

agree with the concern about bulk moving files, this could take a very long time since some people have dozens of apps and each app can be a gb+.

what about this: before we let the user change this base directory, we should iterate through all the apps and ensure the path is absolute. if it's already absolute (e.g. import without copy into dyad-apps), leave it alone. if it's a relative path (which assumes it's relative, now, to ~/dyad-apps), we'll turn this into an absolute path. this way all the existing apps will continue to work and stay in the same place.

having an e2e test for this would be good as this is a pretty subtle feature.

Copy link
Copy Markdown
Collaborator

@wwwillchen wwwillchen left a comment

Choose a reason for hiding this comment

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

see comment above

@RyanGroch RyanGroch marked this pull request as ready for review March 8, 2026 04:35
greptile-apps[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

greptile-apps[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

⚠️ 1 issue in files not directly in the diff

⚠️ Missing default value for customAppsFolder in DEFAULT_SETTINGS (src/main/settings.ts:23-47)

Per the mandatory rule in rules/adding-settings.md, when adding a new setting, step 2 requires adding a default value in DEFAULT_SETTINGS in src/main/settings.ts. The customAppsFolder field was added to UserSettingsSchema (src/lib/schemas.ts:340) and to the search index, but no corresponding entry was added to DEFAULT_SETTINGS in src/main/settings.ts:23-47. While the field is optional().nullable() so it won't cause a runtime error, it violates the explicit rule for adding settings.

View 17 additional findings in Devin Review.

Open in Devin Review

Comment on lines +858 to +863
if (!isAppLocationAccessible(newAppPath)) {
throw new Error(
`The path ${newAppPath} is inaccessible. Please check your custom apps folder setting.`,
);
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The isAppLocationAccessible(...) guards are my effort to account for cases where the user's custom folder is unavailable (e.g. maybe on a separate drive that's not plugged in).

I didn't add these guards to every app handler; I focused on adding them in places where the custom apps folder is most likely to be the cause of failure. This primarily includes handlers that create apps (createApp, copyApp, import-app, cloneRepoFromUrl). The main appeal here is having a specific error message for the user, so I wasn't concerned about putting guards in places where the error message might not fit.

Comment on lines +52 to 55
export const SelectCustomAppsFolderResultSchema = z.object({
path: z.string().nullable(),
canceled: z.boolean(),
});
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

SelectAppLocationResultSchema wasn't being used. There's an identical Zod schema in src/ipc/types/app.ts, and that one actually gets used. I've repurposed this one.

}

fs.mkdirSync(getUserDataPath(), { recursive: true });
fs.mkdirSync(getDyadAppPath("."), { recursive: true });
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Previously, we were creating the dyad-apps directory for the first time in the database initialization. I moved it into a function called resolveDefaultDyadAppsDirectory in src/paths/paths.ts so that it gets created the first time that Dyad tries to access it.

The main reason for this is that we probably don't want dyad-apps to get created if the user has chosen a different directory.

Comment on lines +7 to +9
// Cached result of getDyadAppsBaseDirectory
let cachedBaseDirectory: string | null = null;
let cachedCustomFolderSetting: string | null | undefined;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I've changed getDyadAppsBaseDirectory so that it now relies on a settings read. However, to avoid the potentially expensive settings read on every call, I also added a cache for the result here.

I'm actually a bit torn on whether the cache is necessary or a premature optimization (AI reviews asked for it), but I don't think it hurts.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

generally, you don't need to worry about caching read settings since it's very fast (it should be a small file), but seems OK

@RyanGroch
Copy link
Copy Markdown
Collaborator Author

Apologies for taking so long on this; I ended changing my approach a few times (in part to move away from the symlink approach as requested), so the PR is quite different from when I first opened it.

There are still two things that I'm unsure of:

  1. Should I be adding a default value to DEFAULT_SETTINGS in src/main/settings.ts? rules/adding-settings.md says that I should, but the comment above the DEFAULT_SETTINGS object suggests that I probably should not modify it.
  2. Regarding internationalization (i18n) - I can't reliably translate to the other languages Dyad supports, but I could restructure it to have the useTranslation(...)/t(...) calls and add entries (albeit not translated) to the json files in src/i18n/locales if you'd like.

Please let me know if there's anything else you'd like changed. Thanks!

@RyanGroch RyanGroch requested a review from wwwillchen March 22, 2026 14:50
@wwwillchen
Copy link
Copy Markdown
Collaborator

@azizmejri1 could you take a look at this PR first and respond to some of the comments? thanks

@azizmejri1
Copy link
Copy Markdown
Collaborator

@azizmejri1 could you take a look at this PR first and respond to some of the comments? thanks

Sure , I'll take a look

@azizmejri1
Copy link
Copy Markdown
Collaborator

  1. Should I be adding a default value to DEFAULT_SETTINGS in src/main/settings.ts? rules/adding-settings.md says that I should, but the comment above the DEFAULT_SETTINGS object suggests that I probably should not modify it.

I think the comment is meant for modifying existing fields , adding new ones is fine.

Copy link
Copy Markdown
Collaborator

@azizmejri1 azizmejri1 left a comment

Choose a reason for hiding this comment

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

@RyanGroch @wwwillchen Overall this looks good , just left a minor comment regarding a misleading error message.

@github-actions github-actions bot added the needs-human:review-issue ai agent flagged an issue that requires human review label Mar 24, 2026
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

cubic-dev-ai[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

⚠️ 1 issue in files not directly in the diff

⚠️ Missing default value for customAppsFolder in DEFAULT_SETTINGS (src/main/settings.ts:23-47)

Per the mandatory rule in rules/adding-settings.md, when adding a new setting, step 2 requires adding a default value in DEFAULT_SETTINGS in src/main/settings.ts. The customAppsFolder field was added to UserSettingsSchema (src/lib/schemas.ts:340) and to the search index, but no corresponding entry was added to DEFAULT_SETTINGS in src/main/settings.ts:23-47. While the field is optional().nullable() so it won't cause a runtime error, it violates the explicit rule for adding settings.

View 17 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87546e3e24

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1631 to +1634
const resolvedAppPath = path.isAbsolute(appPath)
? appPath
: path.join(basePath, appPath);
await fsPromises.rm(resolvedAppPath, {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid deleting non-Dyad folders during reset-all

resetAll now recursively deletes every apps.path value from the database, including absolute paths (fsPromises.rm(resolvedAppPath, { recursive: true, force: true })). That is unsafe because Dyad can store user-owned absolute paths (for example, local-folder imports with skipCopy=true keep sourcePath instead of copying), so a reset can wipe unrelated project directories outside any Dyad-managed apps root. This is a regression from the previous behavior and can cause irreversible data loss for users who imported existing folders in-place.

Useful? React with πŸ‘Β / πŸ‘Ž.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I agree that this could happen. The warning message on resetAll is pretty explicit though, so I'm not sure how many users would unintentionally lose apps because of this.

It would also probably require a change to the database schema to determine which apps were imported in place and are therefore exempt from deletion. I'd prefer not to make a change like that in this PR. We could treat all absolute paths as exempt, but after this PR that could include a lot of apps.

I'll leave this unresolved because it's worth seeing.

@RyanGroch
Copy link
Copy Markdown
Collaborator Author

@azizmejri1 Thanks for the review. I appreciate the note on the error message; that's been fixed.

I think the comment is meant for modifying existing fields , adding new ones is fine.

Good to know. The default value for customAppsFolder would be undefined anyway, so I suppose it doesn't make a huge difference.

@wwwillchen By the way, don't worry too much about responding to my comments/questions. I leave them mostly for context or to call attention to particular things. Unless there's a change I should make, it's totally fine to ignore or resolve them.

Copy link
Copy Markdown
Collaborator

@wwwillchen wwwillchen left a comment

Choose a reason for hiding this comment

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

thanks @RyanGroch! merging this

@wwwillchen wwwillchen merged commit 583334f into dyad-sh:main Mar 25, 2026
15 of 16 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🎭 Playwright Test Results

❌ Some tests failed

OS Passed Failed Flaky Skipped
🍎 macOS 405 3 2 127
πŸͺŸ Windows 406 4 3 127

Summary: 811 passed, 7 failed, 5 flaky, 254 skipped

Failed Tests

🍎 macOS

  • queued_message.spec.ts > editing queued message restores attachments and selected components
    • Error: expect(locator).toBeVisible() failed
  • queued_message.spec.ts > canceling queued message edit clears restored components
    • Error: expect(locator).toBeVisible() failed
  • setup_flow.spec.ts > Setup Flow > node.js install flow
    • TimeoutError: locator.dispatchEvent: Timeout 30000ms exceeded.

πŸͺŸ Windows

  • concurrent_chat.spec.ts > concurrent chat
    • Error: expect(locator).toBeVisible() failed
  • github.spec.ts > create and sync to new repo
    • Error: expect(locator).toHaveClass(expected) failed
  • github.spec.ts > create and sync to existing repo
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed
  • github.spec.ts > create and sync to existing repo - custom branch
    • Error: expect(locator).toMatchAriaSnapshot(expected) failed

πŸ“‹ Re-run Failing Tests (macOS)

Copy and paste to re-run all failing spec files locally:

npm run e2e \
  e2e-tests/queued_message.spec.ts \
  e2e-tests/setup_flow.spec.ts

⚠️ Flaky Tests

🍎 macOS

  • concurrent_chat.spec.ts > concurrent chat (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > setup banner shows correct state when node.js is installed (passed after 1 retry)

πŸͺŸ Windows

  • chat_mode.spec.ts > chat mode selector - ask mode (passed after 1 retry)
  • edit_code.spec.ts > edit code (passed after 1 retry)
  • setup_flow.spec.ts > Setup Flow > setup banner shows correct state when node.js is installed (passed after 1 retry)

πŸ“Š View full report

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-human:review-issue ai agent flagged an issue that requires human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Select which directory to save Dyad apps

4 participants