Skip to content

feat: cache provider slash commands on project creation and session start#1583

Open
deyvid-bardarov wants to merge 5 commits intopingdotgg:mainfrom
deyvid-bardarov:feat/cache-provider-slash-commands
Open

feat: cache provider slash commands on project creation and session start#1583
deyvid-bardarov wants to merge 5 commits intopingdotgg:mainfrom
deyvid-bardarov:feat/cache-provider-slash-commands

Conversation

@deyvid-bardarov
Copy link
Copy Markdown

@deyvid-bardarov deyvid-bardarov commented Mar 30, 2026

Discover provider slash commands eagerly on project creation via a lightweight Claude SDK probe query, and update the cache when a session reports its commands via session.configured. Commands are persisted server-side in the project projection (SQLite) and served to the web client through the orchestration snapshot, enabling a 3-tier fallback: session commands → project-level cache → localStorage cache.

What Changed

  • Added a discoverSlashCommands method to the provider adapter interface. On project creation, ProviderCommandReactor listens for project.created events and fires a lightweight Claude SDK probe query (permissionMode: "plan", immediately closed) to call supportedCommands(), retrieving rich command metadata (name, description, argumentHint).
  • Introduced a new ProviderSlashCommandInfo schema in contracts and a project.provider-slash-commands.set command/event pair in the event-sourcing pipeline to persist discovered commands on the project aggregate.
  • Added two SQLite migrations: one for providerSlashCommands on thread sessions, and one for cached_provider_slash_commands_json on the projects projection table.
  • On session.configured, ProviderRuntimeIngestion now extracts slash command names from the init message and updates both the thread session and the project-level cache.
  • The web client uses a 3-tier fallback for slash commands: active session commands → project-level cached commands → localStorage cache. The composer command menu now shows rich descriptions from the project cache when available.

Why

By discovering commands eagerly on project creation (in a background fiber that never blocks), the menu is populated immediately. The project-level cache also survives across sessions and browser refreshes, so commands are always available without waiting for a session round-trip.

UI Changes

N/A — No visual changes. The command menu items now appear sooner (before session start) and show richer descriptions when available.

Screenshots

image

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • I included before/after screenshots for any UI changes
  • I included a video for animation/interaction changes

Note

Medium Risk
Adds new event-sourced command/event plus SQLite migrations and projection changes to persist slash-command data, which could impact read model hydration and DB compatibility. Runtime ingestion and project-creation background discovery introduce new asynchronous flows that need validation across providers.

Overview
Adds provider slash-command caching end-to-end. Introduces discoverSlashCommands on provider adapters/services (implemented via a timed Claude probe; Codex/test adapters stubbed) and a new project.provider-slash-commands.set command/event to persist discovered commands on the project aggregate.

Persists and hydrates cached commands. Updates projections/snapshot query and adds SQLite migrations to store (1) cached_provider_slash_commands_json on projection_projects and (2) provider_slash_commands_json on projection_thread_sessions, wiring these fields through the projection repositories and snapshot decoding.

Populates cache from runtime + surfaces in UI. ProviderCommandReactor now listens for project.created and asynchronously discovers/caches commands, while ProviderRuntimeIngestion extracts slash_commands from session.configured to update both the thread session and project cache. The web composer consumes session → project cache → localStorage fallback, expands trigger detection to include provider commands, and renders provider slash commands in the menu (with provider icons/descriptions when available).

Written by Cursor Bugbot for commit 37aa10f. This will update automatically on new commits. Configure here.

Note

Cache provider slash commands on project creation and session start

  • Adds discoverSlashCommands() to the Claude and Codex provider adapters; Claude probes the runtime with a 15-second timeout and returns an empty list on failure.
  • On project.created, ProviderCommandReactor calls discoverSlashCommands for each supported provider and dispatches project.provider-slash-commands.set to cache results at the project level.
  • On session.configured, ProviderRuntimeIngestion extracts slash commands from the session config and persists them to both the thread session and the project cache.
  • The web ChatView reads cached commands from the session or project state (falling back to a localStorage cache) and surfaces them in the composer slash menu with provider icons and descriptions.
  • Two new DB migrations add provider_slash_commands_json to projection_thread_sessions and cached_provider_slash_commands_json to projection_projects.

Macroscope summarized 37aa10f.

…tart

Discover provider slash commands eagerly on project creation via a
lightweight Claude SDK probe query, and update the cache when a session
reports its commands via session.configured. Commands are persisted
server-side in the project projection (SQLite) and served to the web
client through the orchestration snapshot, enabling a 3-tier fallback:
session commands → project-level cache → localStorage cache.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 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: c0fa00ff-e8b9-45fb-ac52-0bef29f2c284

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

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 size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 30, 2026
return names;
}
return providerSlashCommandsCache.get(selectedProvider) ?? EMPTY_PROVIDER_SLASH_COMMANDS;
}, [sessionSlashCommands, projectCachedCommands, selectedProvider]);
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.

Side effects inside useMemo violate React purity

Low Severity

The useMemo callback mutates a module-level Map (providerSlashCommandsCache) and writes to localStorage via persistProviderSlashCommandsCache. React requires useMemo callbacks to be pure. In Strict Mode, React may invoke this callback twice per render, causing redundant localStorage writes. A useEffect is the appropriate place for these side effects.

Fix in Cursor Fix in Web

The slash command extraction from session.configured events was
unreachable because it was nested inside the assistantDelta check,
which is only true for content.delta events.
Read claudeSettings.binaryPath from serverSettingsService instead of
hardcoding "claude", matching startSession behavior.
…worker

Prevents a malformed project.created payload from killing the entire
stream with an uncaught defect. Decode errors are now logged and the
worker continues processing subsequent events.
Lowercase the command name before startsWith comparison so mixed-case
provider commands like "CustomCmd" match when the user types /customcmd.
} as SDKUserMessage;
}

return yield* Effect.tryPromise({
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 Layers/ClaudeAdapter.ts:3058

When probeQuery.supportedCommands() hangs past the 15-second timeout, probeQuery.close() is never called and the resource leaks. The Effect.timeout fails the Effect with TimeoutError, but the underlying Promise keeps running — the finally block at line 3079 only runs when the Promise settles, not when the timeout fires. Consider restructuring to ensure close() is always invoked, for example by using Effect.acquireUseRelease or Effect.onInterrupt to trigger cleanup when the Effect is interrupted.

🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/ClaudeAdapter.ts around line 3058:

When `probeQuery.supportedCommands()` hangs past the 15-second timeout, `probeQuery.close()` is never called and the resource leaks. The `Effect.timeout` fails the Effect with `TimeoutError`, but the underlying Promise keeps running — the `finally` block at line 3079 only runs when the Promise settles, not when the timeout fires. Consider restructuring to ensure `close()` is always invoked, for example by using `Effect.acquireUseRelease` or `Effect.onInterrupt` to trigger cleanup when the Effect is interrupted.

Evidence trail:
apps/server/src/provider/Layers/ClaudeAdapter.ts lines 3058-3092 (REVIEWED_COMMIT): Shows `Effect.tryPromise` with inner `try/finally` block where `probeQuery.close()` is in the `finally`, wrapped by `Effect.timeout(Duration.seconds(15))`. The `finally` block at line ~3079 only runs when the Promise settles, not when Effect.timeout fires the interruption.

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 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

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

import Migration0017 from "./Migrations/017_ProjectionThreadsArchivedAt.ts";
import Migration0018 from "./Migrations/018_ProjectionThreadsArchivedAtIndex.ts";
import Migration0019 from "./Migrations/016_ProjectionThreadSessionSlashCommands.ts";
import Migration0020 from "./Migrations/017_ProjectionProjectSlashCommandsCache.ts";
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.

Migration files named with wrong numeric prefix

Low Severity

The new migration files are named 016_ProjectionThreadSessionSlashCommands.ts and 017_ProjectionProjectSlashCommandsCache.ts, but they are registered as migration IDs 19 and 20. Files 016_CanonicalizeModelSelections.ts and 017_ProjectionThreadsArchivedAt.ts already exist with the same numeric prefixes. This duplication in naming conventions makes it easy for a future contributor to misidentify migration ordering or accidentally reuse an ID.

Fix in Cursor Fix in Web


export type ComposerTriggerKind = "path" | "slash-command" | "slash-model";
export type ComposerSlashCommand = "model" | "plan" | "default";
export type ComposerProviderSlashCommand = string;
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.

Exported type ComposerProviderSlashCommand is never used

Low Severity

The newly exported type ComposerProviderSlashCommand (aliased to string) is defined but never imported or referenced anywhere in the codebase. This is dead code that adds noise without providing value.

Fix in Cursor Fix in Web

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

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant