Skip to content

Add per-project settings #2567

Open
shivamhwp wants to merge 18 commits intopingdotgg:mainfrom
shivamhwp:per-project-settings
Open

Add per-project settings #2567
shivamhwp wants to merge 18 commits intopingdotgg:mainfrom
shivamhwp:per-project-settings

Conversation

@shivamhwp
Copy link
Copy Markdown
Collaborator

@shivamhwp shivamhwp commented May 6, 2026

  • add project details/settings RPCs and persistence
  • use project remote overrides for source control detection
  • add project details page and sidebar navigation
  • minor ui fix for the caret icon in the custom header of the diff.

What Changed

Added a per-project settings page that can be opened from the project row in the sidebar.

  • Clicking the project row opens /projects/$projectId
  • Clicking the caret still expands/collapses project threads
  • Added project settings UI for:
    • display name
    • project path
    • detected Git remote
    • effective Git remote
    • Git root and branch
  • Added a manual remote override so users can configure the source control provider/remote URL when auto-detection fails, especially for self-hosted Git remotes.
  • Persisted per-project remote override settings and wired them into source control provider resolution.

Why

Git remote detection can fail for self-hosted providers where the remote URL does not clearly identify the host, such as a self-hosted GitLab instance without gitlab.com in the URL.

This gives users a project-level fallback instead of requiring the app to guess correctly. The project name change is also scoped to T3 Code metadata only; it does not rename or move the directory on disk.

UI Changes

image

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • No video needed; no animation/motion change

Note

Medium Risk
Adds new persisted per-project settings and threads them through source control detection, git status streaming, and provider selection/dispatch validation, which could affect repo/provider resolution and message sending behavior. Changes span both server RPCs and core UI flows, but are mostly additive with tests.

Overview
Adds persisted per-project settings and exposes them via new WS RPCs (projectsGetDetails, projectsUpdateSettings) plus a new /projects/$projectId route; the sidebar now navigates to this page from the project row while a separate caret toggles thread expansion.

Wires project settings into behavior: remote overrides influence SourceControlProviderRegistry.resolveHandle (tracking contextSource) and GitManager provider resolution, project actionEnvironment is injected into setup scripts and terminal launches (with T3CODE_* protected), git status subscriptions/refresh are keyed by projectId, and provider selection/sending is constrained by per-project disabled provider instances (validated server-side on thread.turn.start and enforced in the composer UI).

Reviewed by Cursor Bugbot for commit 0f2ff8c. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add per-project settings including provider policy, action environment, and remote override

  • Introduces ProjectSettings contract with fields for remoteOverride, actionEnvironment, disabledProviderInstanceIds, and automaticGitFetchInterval; server settings now persist a per-project settings map
  • Adds projectsGetDetails and projectsUpdateSettings WebSocket RPC handlers in ws.ts that return git-detected remotes, effective remote, and project settings
  • New projects.$projectId.tsx route renders a settings UI for remote override, model selection, action environment variables, and disabled providers
  • Provider availability in ChatComposer is now filtered by per-project disabled provider list; send is blocked when no providers are allowed for the project
  • Draft model selection is seeded from project.defaultModelSelection instead of a sticky fallback when creating or reusing empty drafts
  • Terminal launches (setup scripts and chat actions) now inject per-project actionEnvironment variables; runtime T3CODE_* vars can no longer be overridden by user env
  • SourceControlProviderRegistry now prefers a settings-based remote override (tagged contextSource='override') over auto-detected remotes
  • Risk: disabling all providers for a project via projectsUpdateSettings is rejected server-side, but existing sessions are not forcibly disconnected until their next turn

Macroscope summarized 0f2ff8c.

- add project details/settings RPCs and persistence
- use project remote overrides for source control detection
- add project details page and sidebar navigation
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 998d5333-a3d6-48d0-86a7-3272ddc4580b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels May 6, 2026
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/routes/projects.$projectId.tsx
Comment thread apps/web/src/routes/projects.$projectId.tsx
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/components/Sidebar.tsx Outdated
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented May 6, 2026

Approvability

Verdict: Needs human review

2 blocking correctness issues found. This PR introduces a substantial new feature (per-project settings) with ~3000 lines of new logic including a new settings page, per-project provider filtering, model defaults, action environment variables, and git fetch intervals. Additionally, open review comments identify two medium-severity bugs (missing projectId parameter, incorrect null vs undefined check) that warrant human attention before merging.

You can customize Macroscope's approvability policy. Learn more.

- Replace sidebar rename action with project settings access
- Show detected remotes and project path in settings
- Extract remote override parsing into shared server helper
@shivamhwp shivamhwp changed the title Add per-project settings and remote overrides Add per-project settings May 7, 2026
@shivamhwp shivamhwp changed the title Add per-project settings Add per-project settings (wip) May 7, 2026
Comment thread apps/server/src/sourceControl/RemoteOverride.ts
Comment thread apps/web/src/routes/projects.$projectId.tsx Outdated
Comment thread apps/server/src/git/GitManager.ts
shivamhwp added 2 commits May 7, 2026 19:10
- Persist project action environment and surface it in setup/runtime
- Seed new threads from project default model selection
- Refine source control detection precedence and project settings UI
Comment thread apps/web/src/routes/projects.$projectId.tsx
Comment thread apps/web/src/routes/projects.$projectId.tsx
Comment thread apps/server/src/ws.ts
Comment thread apps/web/src/hooks/useHandleNewThread.ts
- sync script keybindings against existing local bindings
- preserve sticky draft state when seeding new threads
- reject duplicate action environment keys during normalization
Comment thread apps/web/src/routes/projects.$projectId.tsx Outdated
Comment thread apps/web/src/components/ChatView.tsx Outdated
- Replace the manual remote block with a detected remote summary
- Add provider icons and friendlier labels for Git remotes
- Keep custom remote override controls available when enabled
Comment thread apps/web/src/routes/projects.$projectId.tsx
- Seed new chat drafts from per-project defaults
- Cache project settings edits locally while commits are in flight
- Prevent duplicate action-environment keys
Comment thread apps/web/src/routes/projects.$projectId.tsx
Comment thread apps/web/src/routes/projects.$projectId.tsx
Comment thread apps/server/src/ws.ts Outdated
Comment thread apps/web/src/lib/projectScriptKeybindings.ts Outdated
- update project settings from the latest persisted snapshot
- skip keybinding validation when no server is available
- cover the new atomic update path with tests
Comment thread apps/web/src/lib/projectScriptKeybindings.ts
Comment thread apps/server/src/provider/Layers/ProviderRegistry.test.ts
Comment thread apps/server/src/serverSettings.ts
Comment thread apps/web/src/components/ChatView.tsx
- block action environment keys that start with `T3CODE_`
- keep runtime `T3CODE_*` values owned by T3Code
- add coverage for reserved env validation
Comment thread apps/web/src/components/ChatView.tsx Outdated
- Remove the Electron-only guard around project script keybinding sync
- Pass the local server when available and let the helper handle non-Electron cases
Comment thread apps/web/src/lib/projectScriptKeybindings.ts
Comment thread apps/web/src/components/ChatView.tsx
- Deploy the web app from release workflow
- Replace broad Effect imports with subpath imports
- Add lint rules and diagnostics for cleaner schema usage
@shivamhwp shivamhwp changed the title Add per-project settings (wip) Add per-project settings May 8, 2026
Comment thread apps/server/src/sourceControl/SourceControlProviderRegistry.ts
shivamhwp added 2 commits May 9, 2026 13:28
- Remove detected-remote summary and provider icon usage
- Show effective remote URL inline with external-link opening
- Reuse shared external URL helper for remote actions
- Persist project-level Git fetch refresh settings
- Thread project context through VCS status polling and UI
- Update project settings schema and tests for the new field
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium

const gitStatusQuery = useGitStatus({
environmentId: activeThread?.environmentId ?? null,
cwd: activeCwd ?? null,
});

The call to useGitStatus at lines 221-224 is missing the projectId parameter, which the hook now requires after the changes in gitStatusState.ts. The hook's useEffect passes projectId to watchGitStatus and includes it in the dependency array, but this call site only provides environmentId and cwd. Since activeProjectId is already available at line 211, pass projectId: activeProjectId to ensure the effect dependencies are complete and the watcher is properly scoped.

-  const gitStatusQuery = useGitStatus({
-    environmentId: activeThread?.environmentId ?? null,
-    cwd: activeCwd ?? null,
-  });
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/web/src/components/DiffPanel.tsx around lines 221-224:

The call to `useGitStatus` at lines 221-224 is missing the `projectId` parameter, which the hook now requires after the changes in `gitStatusState.ts`. The hook's `useEffect` passes `projectId` to `watchGitStatus` and includes it in the dependency array, but this call site only provides `environmentId` and `cwd`. Since `activeProjectId` is already available at line 211, pass `projectId: activeProjectId` to ensure the effect dependencies are complete and the watcher is properly scoped.

Evidence trail:
apps/web/src/components/DiffPanel.tsx lines 211, 221-224 (REVIEWED_COMMIT): `activeProjectId` available but not passed to `useGitStatus`.
apps/web/src/lib/gitStatusState.ts lines 37-42 (REVIEWED_COMMIT): `GitStatusTarget` interface with optional `projectId`.
apps/web/src/lib/gitStatusState.ts lines 74-81 (REVIEWED_COMMIT): `getGitStatusTargetKey` includes `projectId` in key.
apps/web/src/lib/gitStatusState.ts lines 172-185 (REVIEWED_COMMIT): `useGitStatus` hook passes `target.projectId` to `watchGitStatus` and includes in deps.
apps/web/src/components/ChatView.tsx line 1632-1636 (REVIEWED_COMMIT): other call site passes `projectId`.
apps/web/src/components/Sidebar.tsx lines 371-375 (REVIEWED_COMMIT): another call site passes `projectId`.
apps/web/src/components/ThreadStatusIndicators.tsx lines 153-157 (REVIEWED_COMMIT): another call site passes `projectId`.

Comment on lines +1992 to +1994
if (isElectron && input.keybinding !== undefined && !keybindingServer) {
throw new Error("Local API unavailable.");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium components/ChatView.tsx:1992

When deleteProjectScript calls persistProjectScripts with keybinding: null, the guard isElectron && input.keybinding !== undefined evaluates to true (since null !== undefined), causing the code to throw "Local API unavailable." if readLocalApi()?.server is falsy. The old code skipped this branch entirely for null keybindings because decodeProjectScriptKeybindingRule returned a null rule. This causes script deletion in Electron to surface a misleading error toast even though the server-side deletion already succeeded. Consider checking input.keybinding != null instead of !== undefined to preserve the original null-skipping behavior.

-      const keybindingServer =
-        isElectron && input.keybinding !== undefined ? readLocalApi()?.server : null;
-      if (isElectron && input.keybinding !== undefined && !keybindingServer) {
+      const keybindingServer =
+        isElectron && input.keybinding != null ? readLocalApi()?.server : null;
+      if (isElectron && input.keybinding != null && !keybindingServer) {
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/web/src/components/ChatView.tsx around lines 1992-1994:

When `deleteProjectScript` calls `persistProjectScripts` with `keybinding: null`, the guard `isElectron && input.keybinding !== undefined` evaluates to `true` (since `null !== undefined`), causing the code to throw "Local API unavailable." if `readLocalApi()?.server` is falsy. The old code skipped this branch entirely for `null` keybindings because `decodeProjectScriptKeybindingRule` returned a null rule. This causes script deletion in Electron to surface a misleading error toast even though the server-side deletion already succeeded. Consider checking `input.keybinding != null` instead of `!== undefined` to preserve the original null-skipping behavior.

Evidence trail:
apps/web/src/components/ChatView.tsx lines 1990-1994 (new guard with `!== undefined`), line 2086 (`keybinding: null` in deleteProjectScript), lines 1983-1988 (dispatchCommand runs before the guard). Diff at MERGE_BASE..REVIEWED_COMMIT showing old code used `isElectron && keybindingRule` which was null for null keybinding. apps/web/src/lib/projectScriptKeybindings.ts lines 94-95 (`syncProjectScriptKeybinding` handles null server with early return). apps/web/src/routes/projects.$projectId.tsx lines 448-453 (parallel implementation without the throwing guard).

Comment thread apps/server/src/vcs/VcsStatusBroadcaster.ts
shivamhwp added 2 commits May 9, 2026 17:37
- Persist disabled provider instance IDs in project settings
- Gate chat and project selection on project-specific provider access
- Update project settings UI and validation
# Conflicts:
#	apps/server/src/provider/Layers/ProviderRegistry.test.ts
#	apps/server/src/serverSettings.ts
#	apps/server/src/ws.ts
#	packages/shared/src/serverSettings.ts
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0f2ff8c. Configure here.

Comment thread apps/server/src/ws.ts
automaticGitFetchInterval: null,
actionEnvironment: {},
disabledProviderInstanceIds: [],
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Duplicated emptyProjectSettings constant across two production files

Low Severity

The emptyProjectSettings constant is defined identically in both serverSettings.ts and ws.ts. Since ws.ts already imports from serverSettings.ts (via ServerSettingsService), exporting the constant from serverSettings.ts and reusing it would eliminate the duplication and reduce the risk of them diverging when ProjectSettings fields change in the future.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0f2ff8c. Configure here.

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

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant