feat(projects): add "New project" creation (mkdir + git init)#19
feat(projects): add "New project" creation (mkdir + git init)#19writingdeveloper wants to merge 1 commit into
Conversation
Add a "+ New project" button to the projects toolbar that opens a small modal to create a new project folder under a scan root and `git init` it — git init is what makes the scanner discover the folder. On success the new project opens in a terminal with the active agent and the deck refreshes. - shared/projectName.ts: pure, shared name validation reused by main (authoritative) and the renderer (live feedback); allows spaces/dots/ hyphens, rejects illegal/reserved/too-long names, trims canonical name - main/createProject.ts: mkdir + git init guarded by isAllowedPath (same guard as open/openFolder); deps injectable for tests - IPC project:create, preload bridge, global.d.ts type - renderer modal (newProjectModal.ts): scan-root picker + browse fallback, live path preview, inline validation errors; toolbar button + label - i18n ko/en/ja/zh, modal styles, QA harness modal scene - tests +16 (162 total); Playwright QA 0 console/page errors Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 865ecca255
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| const check = validateProjectName(rawName); | ||
| if (!check.ok) return { ok: false, error: check.reason }; | ||
| if (!isAllowedPath(folders, parent)) return { ok: false, error: 'parent_not_allowed' }; |
There was a problem hiding this comment.
Resolve parent symlinks before mkdir
When a configured scan root contains a symlink to another directory, this guard accepts a renderer-supplied parent like <root>/link because isAllowedPath only checks the resolved string prefix, but mkdirSync(join(parent, name)) then follows the symlink and creates the project outside the allowed roots. Since project:create is exposed to the renderer and this path is used for filesystem writes, resolve/realpath the parent or reject symlink parents before creating the directory.
Useful? React with 👍 / 👎.
A "+ New project" button in the projects toolbar opens a small modal to create a new project folder under a scan root and `git init` it — git init is what makes the scanner discover the folder. On success the project opens in a terminal with the active agent and the deck refreshes. - shared/projectName.ts: pure, shared name validation reused by main (authoritative) and the renderer (live feedback); allows spaces/dots/ hyphens, rejects illegal/reserved/too-long names, returns trimmed name - main/createProject.ts: mkdir + git init guarded by isAllowedPath (same guard as open/openFolder); deps injectable for tests - ipc.ts: project:create; preload + global.d.ts bridge/types - renderer/newProjectModal.ts: scan-root picker + browse fallback, live path preview, inline validation errors; toolbar button + localized label - locales: proj.new + newproj.* (ko/en/ja/zh); modal styles; QA modal scene - tests +16 (162 total); Playwright QA 0 console/page errors Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summary
Adds a "+ New project" button to the projects toolbar that creates a new project folder under a scan root and
git inits it, then opens it in a terminal with the active agent and refreshes the deck.git initis not cosmetic: the scanner only surfaces folders containing a.gitdirectory, so initialising the repo is what makes the new project appear in the deck.What's new
shared/projectName.ts— pure, shared name validation reused by the main process (authoritative) and the renderer (instant feedback). Allows spaces/dots/hyphens, rejects illegal/reserved/too-long names, returns a trimmed canonical name.main/createProject.ts—mkdir+git init, guarded byisAllowedPath(the same guard used by open/openFolder, so a compromised renderer can't mkdir anywhere). Deps injectable for tests.project:create+ preload bridge +global.d.tstype.renderer/newProjectModal.ts— small modal: scan-root picker with a browse fallback (a freshly picked dir registers as a scan root), live path preview, inline validation errors. Toolbar button + localized label.Decisions
git init) — fits DevDeck's "command deck" identity; no template/scaffold system.Test plan
tsc+ renderer bundle cleanprojectName10,createProject6), incl. guard/validation/dup/git-unavailable pathsgit initpath verified end-to-end in a temp dir (gitInitialized: true,.gitcreated)🤖 Generated with Claude Code