Skip to content

feat(app): search and switch projects from the command palette - #765

Merged
HugoRCD merged 7 commits into
HugoRCD:mainfrom
voidhrithik:fix/command-palette-projects
Aug 11, 2026
Merged

feat(app): search and switch projects from the command palette#765
HugoRCD merged 7 commits into
HugoRCD:mainfrom
voidhrithik:fix/command-palette-projects

Conversation

@voidhrithik

@voidhrithik voidhrithik commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Continues #686 by @onmax, rebased onto main. Their commit is kept as theirs and my changes sit on top.

Resolves #764.

What changed since #686

  • The team name rendered twice per row, once as suffix on the right and once as description under the label. Dropped suffix, which reverts the CommandPalette.vue and packages/types/src/Command.ts edits and brings the diff down to one file. The palette already draws a checkmark on the right for the active item, which is what @onmax suggested in the thread.
  • Removed the rule that hid the Projects group when there is only one project in total. feat: project switching in command palette #686 framed this as project switching, where that made sense. fix: command palette doesn't search projects #764 is about search, and typing the name of your only project to get "No results" is the reported bug in miniature. Happy to put it back if you'd rather keep the switching framing.
  • Wrapped navigateTo in a block so the action matches the void return type on CommandItem. tsc rejects the arrow-return form.
  • Stopped refetching projects on every palette open, see below.

Duplicate fetches

useProjects is a useState with no default, so undefined means never loaded and [] means the team has no projects. The length === 0 check treated both as unloaded. The palette mounts on open (Navbar.vue renders it behind v-if="isSearchActive"), so a team with no projects was refetched every single time you opened the palette.

fetchTeams() also runs twice on mount, once in useAppCommands and once in CommandPalette.vue. Both watcher passes saw undefined and issued the same request.

Measured locally against Postgres with 3 teams, one of them having no projects:

before after
cold load plus one palette open 5 requests 3 requests
4 further opens after that 4 requests 0 requests

After the 4 reopens the palette still listed all 5 projects, so the drop to zero is cache hits rather than the group breaking.

Testing

Ran locally against Postgres 16 with seeded data, 3 teams and 5 projects, one team deliberately empty. Confirmed in the browser:

  • Projects group renders with all 5 projects across teams
  • Cross-team rows show the team name once, under the label
  • The checkmark appears only on the project you are currently in
  • Searching for a project that lives in another team finds it, which is the fix: command palette doesn't search projects #764 report
  • Clicking the result navigates across teams
  • No console errors

The Tests and Check if packages can be built checks both pass on CI for this commit, and pnpm --filter @shelve/app test passes locally too, 117 tests.

Changeset added, @shelve/app minor.

On the failing checks

autofix.ci was failing on 4 lint errors that are already on main, in variables/[variableId]/index.put.ts and test/unit/team-project-context.test.ts. Neither is auto-fixable by eslint, which is how they got through. autofix.yml runs on pull_request only, so it never runs on main, and every PR opened since #757 and #759 inherits the failure, #696 included.

Fixed in its own commit here so the check can go green. Happy to pull it out into a separate PR if you would rather keep this branch to the palette work.

Vercel – shelve-lp and Vercel – shelve-vault still fail with "Authorization required to deploy". That one needs your side.

Summary by CodeRabbit

  • New Features
    • Added projects to the command palette, with search and quick navigation across teams.
    • Added human verification to OTP sign-in requests.
  • Bug Fixes
    • OTP sign-in now works on preview deployments while maintaining bot protection in production.
    • Improved project variable group validation for empty or missing values.
  • Documentation
    • Added release notes for project command-palette support and preview-deployment sign-in behavior.

onmax and others added 4 commits August 11, 2026 19:26
Project entries set both suffix and description to the team name, so it
appeared twice per row. Drop suffix and keep the description; the palette
already renders a checkmark on the right for the active item.

Also stop hiding the group when there is only one project in total, and
wrap navigateTo so the action matches the void return type CommandItem
expects.
@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b283da0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@shelve/app Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

@voidhrithik is attempting to deploy a commit to the HRCD Projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for following the naming conventions! 🙏

@github-actions github-actions Bot added the feature New feature or request label Aug 11, 2026
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The command palette now loads projects across available teams and creates searchable project commands. OTP endpoints use centralized, environment-aware BotID checks. Project variable validation uses explicit null checks, and project service tests update their module import pattern.

Changes

Project command palette

Layer / File(s) Summary
Load projects for available teams
apps/shelve/app/composables/useAppCommands.ts
The composable fetches missing projects concurrently, logs failures, prevents duplicate requests, and watches team availability.
Generate and group project commands
apps/shelve/app/composables/useAppCommands.ts, .changeset/command-palette-projects.md
The command palette adds team-aware project commands with navigation, keywords, active-state detection, and a Projects group. The changeset documents the release.

OTP human verification

Layer / File(s) Summary
Centralize BotID verification
apps/shelve/server/utils/auth.ts, .changeset/botid-preview-bypass.md
The exported requireHuman helper enforces BotID checks in production and supports preview OTP sign-in.
Apply verification to OTP endpoints
apps/shelve/server/api/auth/otp/send.post.ts, apps/shelve/server/api/auth/otp/verify.post.ts
Both OTP endpoints call requireHuman() before processing requests.

Validation and test maintenance

Layer / File(s) Summary
Update validation and service test setup
apps/shelve/server/api/teams/[slug]/projects/[projectId]/variables/[variableId]/index.put.ts, apps/shelve/test/unit/team-project-context.test.ts
The validation guard explicitly checks for undefined and null. Tests instantiate ProjectsService through the module namespace.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OTPClient
  participant OTPEndpoint
  participant requireHuman
  participant BotID
  OTPClient->>OTPEndpoint: Submit OTP request
  OTPEndpoint->>requireHuman: Check request
  requireHuman->>BotID: Classify request
  BotID-->>requireHuman: Return classification
  requireHuman-->>OTPEndpoint: Allow or throw 403
  OTPEndpoint-->>OTPClient: Process OTP or return denial
Loading

Possibly related issues

Suggested reviewers: hugorcd

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the primary change: searching and switching projects from the command palette.
Description check ✅ Passed The description explains the changes, linked issues, testing, performance results, changeset, and known CI limitations in sufficient detail.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@voidhrithik

Copy link
Copy Markdown
Contributor Author

@HugoRCD workflows here are held pending approval, first PR from my fork. Mind clicking approve so the tests can run? No rush on the review.

This is @onmax's #686 with the duplicate team name from your screenshot removed.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/shelve/app/composables/useAppCommands.ts`:
- Around line 20-23: Update the project-loading logic around useProjects to
track loaded and in-flight state per team slug separately from the project array
length. Skip fetching once a team’s response has loaded, including an empty
array, and prevent overlapping watcher invocations from issuing duplicate
requests by marking the team in flight before awaiting $fetch and clearing it
afterward.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 12d53008-bf87-4d5c-855f-ca721ca788c1

📥 Commits

Reviewing files that changed from the base of the PR and between 4af0de2 and b1d5ec2.

📒 Files selected for processing (2)
  • .changeset/command-palette-projects.md
  • apps/shelve/app/composables/useAppCommands.ts

Comment thread apps/shelve/app/composables/useAppCommands.ts Outdated
useProjects is a useState with no default, so undefined means never
loaded and an empty array means the team has no projects. The length
check treated both as unloaded, so a team with no projects refetched
on every teams change.

fetchTeams also runs twice on mount, once from useAppCommands and once
from CommandPalette, so both watcher passes saw undefined and issued
the same request. Guard with an in-flight set.
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
shelve-app Ready Ready Preview, v0 Aug 11, 2026 5:44pm

Request Review

@coderabbitai coderabbitai Bot left a comment

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/shelve/app/composables/useAppCommands.ts (1)

289-304: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not use the first team as the project-description context.

getTeamSlug() falls back to teams.value[0]. On routes without teamSlug, the first team’s projects therefore have no description, while other teams show in <team>. Use only an explicit route team for this comparison, or render the team description for every project.

Suggested fix
-    const currentTeamSlug = getTeamSlug()
+    const currentTeamSlug = typeof route.params.teamSlug === 'string'
+      ? route.params.teamSlug
+      : undefined
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/shelve/app/composables/useAppCommands.ts` around lines 289 - 304, Update
the project description logic in the command-building flow to avoid using
getTeamSlug(), whose fallback treats the first team as explicitly selected.
Compare against only the route’s explicit teamSlug, or consistently include `in
<team>` for every project when no explicit team is present, while preserving the
current-team omission for an explicitly selected team.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/shelve/app/composables/useAppCommands.ts`:
- Around line 289-304: Update the project description logic in the
command-building flow to avoid using getTeamSlug(), whose fallback treats the
first team as explicitly selected. Compare against only the route’s explicit
teamSlug, or consistently include `in <team>` for every project when no explicit
team is present, while preserving the current-team omission for an explicitly
selected team.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7600eb99-112f-46f0-8184-46e8fe66da1c

📥 Commits

Reviewing files that changed from the base of the PR and between b1d5ec2 and 094e57a.

📒 Files selected for processing (1)
  • apps/shelve/app/composables/useAppCommands.ts

Four eslint errors landed on main with HugoRCD#757 and HugoRCD#759, so autofix.ci has
failed on every PR opened since. Nothing else in this branch touches
those files, but the check stays red until they are fixed.

index.put.ts compared a nullish groupId with != null. Now checks
undefined and null explicitly, same as audit.ts already does.

team-project-context.test.ts destructured ProjectsService, which the
variable naming rule rejects. Imports the module namespace instead so
the resetModules and dynamic import setup is unchanged.
@voidhrithik

Copy link
Copy Markdown
Contributor Author

@HugoRCD could you approve the workflow run when you get a chance? The last push needs it.

The new commit fixes 4 eslint errors that have been on main since #757 and #759. autofix.ci has been failing on every PR opened since, #696 included. pnpm run lint is clean across all 5 packages locally.

The three Vercel checks are stuck on "Authorization required to deploy", which needs your side too.

Preview deployments sit behind Vercel Deployment Protection, which redirects the
BotID challenge requests to the SSO login. The challenge can never be solved
there, so checkBotId classified every OTP request as a bot and sign-in returned
403 Access denied.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/botid-preview-bypass.md:
- Line 5: Resolve the MD041 warning for the changeset by adding a top-level
Markdown heading after its required frontmatter, or update the markdownlint
configuration to exclude .changeset/**/*.md if plain release-note text is
intentional.

In `@apps/shelve/server/utils/auth.ts`:
- Line 25: Update the isDevelopment configuration used by requireHuman so only
the explicitly supported non-production environment, preview, enables
development mode; treat unset and unexpected VERCEL_ENV values as
production-like and keep enforcement enabled. Add tests covering unset, preview,
and production values.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 87ea7fbe-c31f-4c98-9f27-ee91b74335a7

📥 Commits

Reviewing files that changed from the base of the PR and between 094e57a and b283da0.

📒 Files selected for processing (6)
  • .changeset/botid-preview-bypass.md
  • apps/shelve/server/api/auth/otp/send.post.ts
  • apps/shelve/server/api/auth/otp/verify.post.ts
  • apps/shelve/server/api/teams/[slug]/projects/[projectId]/variables/[variableId]/index.put.ts
  • apps/shelve/server/utils/auth.ts
  • apps/shelve/test/unit/team-project-context.test.ts

"@shelve/app": patch
---

Only enforce the BotID check on production, so OTP sign-in works on preview deployments.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Resolve the MD041 warning.

markdownlint-cli2 reports that the changeset body has no top-level heading. Exclude .changeset/**/*.md from MD041 if plain release-note text is intentional, or add a heading after the required frontmatter.

🧰 Tools
🪛 markdownlint-cli2 (0.23.2)

[warning] 5-5: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/botid-preview-bypass.md at line 5, Resolve the MD041 warning for
the changeset by adding a top-level Markdown heading after its required
frontmatter, or update the markdownlint configuration to exclude
.changeset/**/*.md if plain release-note text is intentional.

Source: Linters/SAST tools

export async function requireHuman(): Promise<void> {
const verification = await checkBotId({
developmentOptions: {
isDevelopment: process.env.VERCEL_ENV !== 'production',

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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --glob '!node_modules' --glob '!dist' \
  '\bVERCEL_ENV\b|\bNODE_ENV\b' .

Repository: HugoRCD/shelve

Length of output: 1783


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- auth utility ---'
cat -n apps/shelve/server/utils/auth.ts

printf '%s\n' '--- auth utility usages ---'
rg -n -C 5 'checkBotId|isDevelopment|utils/auth' apps/shelve

printf '%s\n' '--- environment and deployment configuration ---'
fd -HI -t f 'vercel|environment|env|deploy|package.json|README' . | sort | head -200
rg -n -C 3 'VERCEL_ENV|NODE_ENV|Vercel|production|preview' \
  apps/shelve package.json vercel.json .github 2>/dev/null || true

printf '%s\n' '--- deterministic value check ---'
python3 - <<'PY'
values = [None, "", "development", "preview", "production", "staging", "PRODUCTION"]
for value in values:
    print(f"{value!r}: isDevelopment={value != 'production'}")
PY

Repository: HugoRCD/shelve

Length of output: 39587


🌐 Web query:

Vercel VERCEL_ENV system environment variable production preview development BotID isDevelopment developmentOptions ignored production

💡 Result:

The VERCEL_ENV environment variable is a system-provided variable in Vercel that indicates the environment in which the application is currently running [1][2]. It is available at both build time and runtime [1][3]. The value of VERCEL_ENV is one of the following three strings [1][2]: - production: Used for the live, user-facing version of the application [4][2]. - preview: Used for deployments created from pull requests or non-production branches [4][2]. - development: Used when running the project locally on your machine via the Vercel CLI (e.g., using vercel dev) [5][4][2]. Regarding the specific terms mentioned: - BotID: There is no standard system environment variable named BotID related to Vercel's environment detection. - isDevelopment: This is not a standard Vercel system environment variable. If needed, you can infer this status in your code by checking if VERCEL_ENV === 'development' [1][2]. - developmentOptions: This is not a standard Vercel configuration or environment variable. - ignored: This does not appear to be a relevant term for Vercel's environment variable system. - production: As noted, this is a valid value for VERCEL_ENV [1][2]. In summary, Vercel automatically manages VERCEL_ENV to reflect your current deployment context, and it does not rely on custom variables like BotID or isDevelopment to determine environment state [1][3]. For local development, Vercel provides the vercel env pull command to download your project's environment variables into a local.env file, though system variables like VERCEL_ENV are typically injected automatically by the platform and the CLI [5][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- requireHuman call sites ---'
rg -n -C 6 '\brequireHuman\b|/otp|OTP|one[- ]time|oneTime' apps/shelve --glob '!node_modules/**' --glob '!dist/**'

printf '%s\n' '--- self-hosting and environment documentation ---'
cat -n apps/lp/content/docs/5.self-hosting/1.vercel.md
cat -n apps/lp/content/docs/5.self-hosting/2.environment-variables.md
cat -n apps/shelve/.env.example

printf '%s\n' '--- environment schema and startup validation ---'
rg -n -C 8 'envSchema|VERCEL|environment variable|runtimeConfig' \
  apps/shelve --glob '!node_modules/**' --glob '!dist/**' \
  -g '*.ts' -g '*.md' -g '*.example'

printf '%s\n' '--- BotID package metadata and implementation references ---'
rg -n -C 5 'developmentOptions|isDevelopment|checkBotId' \
  node_modules/botid node_modules/.pnpm 2>/dev/null | head -240 || true

Repository: HugoRCD/shelve

Length of output: 50372


Fail closed when VERCEL_ENV is unset.

requireHuman() protects both OTP endpoints. The current comparison enables BotID development mode for unset and unexpected values. The repository does not require or validate VERCEL_ENV, so a non-Vercel or misconfigured production deployment can bypass BotID enforcement. Use an explicit non-production allowlist or fail closed for unknown values. Add tests for unset, preview, and production.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/shelve/server/utils/auth.ts` at line 25, Update the isDevelopment
configuration used by requireHuman so only the explicitly supported
non-production environment, preview, enables development mode; treat unset and
unexpected VERCEL_ENV values as production-like and keep enforcement enabled.
Add tests covering unset, preview, and production values.

Source: MCP tools

@HugoRCD
HugoRCD merged commit 903908f into HugoRCD:main Aug 11, 2026
10 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: command palette doesn't search projects

3 participants