Skip to content

Fix/project scoped provider skills - #6661

Closed
GenKerensky wants to merge 11 commits into
pingdotgg:mainfrom
GenKerensky:fix/project-scoped-provider-skills
Closed

Fix/project scoped provider skills#6661
GenKerensky wants to merge 11 commits into
pingdotgg:mainfrom
GenKerensky:fix/project-scoped-provider-skills

Conversation

@GenKerensky

@GenKerensky GenKerensky commented Aug 15, 2026

Copy link
Copy Markdown

What Changed

Project skills in the $ picker were effectively environment-global. This PR makes skill discovery multi-workspace on the server, tags each project skill with where it came from, and filters the picker to the active chat checkout on web and mobile.

Server

  • Shared SkillDiscovery walks project skill roots (git-root → cwd ancestors, compatibility dirs, collision precedence) instead of only the process cwd.
  • SkillWorkspaceCwds / ProjectionSnapshotQuery.getActiveWorkspaceCwds collect every registered project root and active worktree so status probes discover across the environment.
  • Claude, Codex, Cursor, Grok, and OpenCode drivers/providers all use that multi-cwd discovery and stamp project skills with sourceCwd.
  • ServerProviderSkill gains optional sourceCwd (user/global skills omit it).

Shared / contracts

  • @t3tools/shared/providerSkills: path normalization + filterProviderSkillsForWorkspace so server stamps and client paths compare cleanly (trailing separators, ./.., mixed slashes, Windows forms).
  • Clients filter with worktreePath ?? project.workspaceRoot, and may pass projectRoot so worktree chats still see project-root-tagged skills before a re-probe re-tags under the worktree.

Clients

  • Web ChatComposer and mobile ThreadComposer / new-task flow scope the $ picker to the active checkout.
  • Timeline / historical skill labels can keep the full inventory so mentions still resolve when switching projects.
  • Minor Android composer theme-color parsing / skill chip color alignment (fuchsia parity with desktop).

Docs

  • Internals note in docs/internals/providers.md and docs/internals/project-scoped-skills.html describing the model and the accepted interim payload cost (full multi-workspace skill union on provider snapshots until a scoped listSkills(cwd) RPC might land later).

Why

With multiple projects or worktrees open, the $ skill picker showed every project skill from every open workspace. That is wrong for isolation: skills from project A should not appear when chatting in project B (or a different worktree), while user/global skills should still be available everywhere.

Discovery had to expand first (probes only saw one cwd), then the wire format needed a stable workspace tag (sourceCwd), then clients needed a shared filter so web and mobile stay consistent. Filtering only on the client without multi-cwd discovery would still miss skills that were never scanned; tagging without filtering would still dump the full union into the picker.

Payload growth with open workspaces is accepted as an interim cost documented in internals; do not collapse by skill name across workspaces.

UI Changes

Behavior change only in the composer $ skill picker (and mobile new-task flow using the same filter):

  • Before: picker listed the full provider skill inventory (all open projects/worktrees + user skills).
  • After: picker lists user/global skills plus project skills whose sourceCwd matches the active chat checkout (worktreePath ?? project.workspaceRoot, with optional project-root fallback).

No layout or animation redesign. Screenshots not attached; happy to add if useful for review.

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

High Risk
Touches every provider probe, the provider snapshot contract, projection reads, and composer pickers. Wrong tagging or filtering would leak or hide skills/commands across projects.

Overview
Scopes $ skills and / slash commands to the active chat checkout instead of dumping every open workspace’s inventory into the picker.

Server: probes now scan all active project roots and worktrees via getActiveWorkspaceCwds, then stamp project entries with sourceCwd. Shared SkillDiscovery walks git-root → cwd ancestors with harness-specific roots. Claude/Codex/Cursor/Grok/OpenCode all use this; Grok prefers inspect + ACP menus, OpenCode queries debug config / SDK command.list. User/global/bundled items stay untagged.

Clients: web ChatComposer and mobile ThreadComposer / new-task flow filter with filterProviderSkillsForWorkspace / filterProviderSlashCommandsForWorkspace (worktreePath ?? project root, with a project-root fallback for worktree chats). Timeline labels still use the full inventory.

Also fixes Android skill-chip colors by parsing #RRGGBBAA (uniwind) instead of Android #AARRGGBB. Snapshots still ship the full multi-workspace union until a scoped list RPC exists.

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

Note

Add project-scoped provider skill and slash command discovery across workspaces

  • Introduces filesystem skill discovery across user and project directories for Claude, Codex, Cursor, Grok, and OpenCode providers, walking ancestors from git root to each workspace cwd and tagging entries with sourceCwd
  • Adds getActiveWorkspaceCwds to ProjectionSnapshotQuery to supply active project roots and thread worktrees; all provider drivers now require this service and pass resolved skillCwds into their status checks
  • Refactors checkClaudeProviderStatus, checkCodexProviderStatus, checkCursorProviderStatus, checkGrokProviderStatus, and checkOpenCodeProviderStatus to accept an options object with skillCwds and environment
  • Adds shared workspace filtering utilities (filterProviderSkillsForWorkspace, filterProviderSlashCommandsForWorkspace) and applies them in web ChatComposer, mobile ThreadComposer, and NewTaskFlowProvider so $ and / pickers show only skills/commands for the current checkout
  • Adds ACP available_commands_update capture in AcpSessionRuntime (via getAvailableCommands) and routes recognized /name args submissions to session.command in OpenCodeAdapter
  • Behavioral Change: ClaudeDriverEnv, CodexDriverEnv, CursorDriverEnv, GrokDriverEnv, and OpenCodeDriverEnv now require ProjectionSnapshotQuery; all test layers updated with stubs. ServerProviderSkill and ServerProviderSlashCommand schemas gain optional sourceCwd. flattenOpenCodeSkills is removed. packages/client-runtime export path changes from ./providerSkills to ./providerSkillDisplay.

Macroscope summarized 536153c.

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

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 Plus

Run ID: 68c342b9-9201-41c1-b4e5-0334b1654663

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

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 15, 2026

@macroscopeapp macroscopeapp 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.

Effect Service Conventions review: one finding — a new service capability is declared as an optional interface member and feature-detected at runtime, with a production fallback that exists only for partial test doubles. Everything else (namespace imports from effect/* subpaths, environment-based dependency acquisition in the drivers, no ManagedRuntime/runPromise in service code, Layer.succeed limited to test seams, error mapping reusing the existing typed persistence errors) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts

@macroscopeapp macroscopeapp 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.

Effect Service Conventions review: one finding — a new service capability is declared as an optional interface member and feature-detected at runtime, with a production fallback that exists only for partial test doubles. Everything else (namespace imports from effect/* subpaths, environment-based dependency acquisition in the drivers, no ManagedRuntime/runPromise in service code, Layer.succeed limited to test seams, error mapping reusing the existing typed persistence errors) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/orchestration/Services/ProjectionSnapshotQuery.ts
Comment thread packages/shared/src/providerSkills.ts Outdated
Comment thread packages/shared/src/providerSkills.ts Outdated
Comment thread apps/server/src/provider/Drivers/ClaudeSkills.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Skipped

Macroscope did not run approvability analysis for this PR. Macroscope could not determine whether this PR modifies its approvability configuration, so the PR was not approved automatically. A PR that may change the rules that govern approval is never approved automatically.

Not approved because:

  • 1 blocking correctness issue found at or above your repo's Minimum Blocking Severity

@GenKerensky
GenKerensky force-pushed the fix/project-scoped-provider-skills branch from abc0ffe to 9233756 Compare August 15, 2026 16:44

@macroscopeapp macroscopeapp 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.

One finding: two ProjectionSnapshotQuery test doubles added for skill workspace resolution still implement only getShellSnapshot, which is no longer the method the production path calls.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/provider/Layers/ProviderRegistry.test.ts Outdated
Comment thread apps/server/src/provider/Layers/ProviderInstanceRegistryLive.test.ts Outdated
Comment thread apps/server/src/provider/Drivers/ClaudeSkills.ts Outdated
Comment thread apps/server/src/provider/acp/AcpSessionRuntime.ts Outdated
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts Outdated
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts Outdated
Comment thread apps/server/src/provider/acp/AcpSessionRuntime.ts Outdated
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts Outdated
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts Outdated
Comment thread apps/server/src/provider/Layers/OpenCodeAdapter.ts Outdated
Comment thread apps/server/src/provider/Drivers/OpenCodeCommands.ts Outdated
Comment thread apps/server/src/provider/Layers/OpenCodeAdapter.ts Outdated
@GenKerensky
GenKerensky force-pushed the fix/project-scoped-provider-skills branch from 39abc3f to 5447a24 Compare August 21, 2026 05:22
Comment thread apps/web/src/components/chat/ChatComposer.tsx Outdated
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts
Comment thread apps/mobile/src/features/threads/new-task-flow-provider.tsx Outdated

@macroscopeapp macroscopeapp 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.

One finding: the new Grok inspect helper puts bare Error values in an Effect failure channel instead of a Schema.TaggedErrorClass with structured attributes. Everything else in scope (new SkillDiscovery / SkillWorkspaceCwds / OpenCodeCommands modules, driver wiring, contract additions, and the updated ProjectionSnapshotQuery shape/test doubles) follows the service, import, and dependency-acquisition conventions.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/provider/Drivers/GrokSkills.ts

@macroscopeapp macroscopeapp 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.

One finding: the / composer menu still builds its skill: entries from the unfiltered provider inventory, so the cross-workspace leak this PR fixes for $ remains on the slash path (and that read is no longer covered by the memo deps).

Posted via Macroscope — UI Consistency

Comment thread apps/web/src/components/chat/ChatComposer.tsx
Comment thread apps/server/src/provider/Drivers/GrokSkills.ts
Comment thread apps/server/src/provider/Layers/OpenCodeAdapter.ts
GenKerensky and others added 10 commits August 22, 2026 14:20
- Add shared skill discovery for Codex, Grok, and OpenCode
- Include project hierarchy, compatibility roots, and collision precedence
- Expose discovered skills in provider snapshots with focused tests
- discover provider skills across registered projects and worktrees
- add Cursor support and align Grok and OpenCode roots with their harnesses
- use a lightweight workspace projection query and cover multi-workspace behavior
Macroscope flagged four issues on the skill discovery work:

- `getActiveWorkspaceCwds` was an optional service member, so
  `resolveSkillWorkspaceCwds` feature-detected it and otherwise fell back
  to the full shell hydration the method exists to avoid. It is now a
  required member; the fallback and the test-side non-null assertion are
  gone, and the projection test doubles declare it.
- `normalizePosixSkillWorkspacePath` rewrote every backslash to `/`, so
  `/projects/foo\bar` and `/projects/foo/bar` shared a skill bag. A
  backslash is a legal POSIX filename character and now stays in the
  segment; Windows forms are already routed to the Windows normalizer.
- `normalizeWindowsSkillWorkspacePath` let `..` pop the drive letter or
  UNC share root, so `C:\..\project` normalized to `project` and never
  matched the server's `sourceCwd`. The root is now a floor.
- `discoverClaudeSkills` resolved a relative `CLAUDE_CONFIG_DIR` against
  the first workspace only, hiding later workspaces' user skills. Each
  workspace now contributes its resolved config root, deduplicated.

Model: Claude Opus 5, harness: Claude Code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up review found two problems with the previous commit.

Macroscope: the `emptySkillProjectionLayer` doubles in
`ProviderRegistry.test.ts` and `ProviderInstanceRegistryLive.test.ts`
still stubbed only `getShellSnapshot`, so once the fallback was removed
the probes called an undefined method and `Effect.ignoreCause` swallowed
the defect — the path was silently dead. Both now stub
`getActiveWorkspaceCwds`, as does the `Layer.mock` in `server.test.ts`.

Bugbot: resolving a relative `CLAUDE_CONFIG_DIR` per workspace added
每 workspace's config dir as an untagged user root, and user skills are
the baseline for every chat — so one project's config skills leaked into
another's picker, and same-named entries collapsed under the shared user
inventory key. A single shared config dir (the common case) stays
untagged and global; when workspaces resolve to different dirs, each root
is tagged with the cwd whose CLI would actually load it.

Model: Claude Opus 5, harness: Claude Code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Grok's $ and / pickers only scanned filesystem skill roots, so bundled
skills and harness features never appeared. Query grok inspect --json
for the skill inventory and capture ACP available_commands_update for
the live slash menu, with filesystem fallback when inspect is missing.

Made-with: Grok 4.6 (grok-build)
@GenKerensky
GenKerensky force-pushed the fix/project-scoped-provider-skills branch from 74a51cb to 9100794 Compare August 22, 2026 18:22
Comment thread apps/server/src/provider/Layers/GrokProvider.ts

const adoptPendingAvailableCommands = (sessionId: string) =>
Effect.gen(function* () {
const pending = yield* Ref.getAndSet(pendingAvailableCommandsRef, new Map());

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 acp/AcpSessionRuntime.ts:385

getAvailableCommands can remain stale after startup: an available_commands_update received between Ref.getAndSet(pendingAvailableCommandsRef, new Map()) and setting startStateRef to Started is buffered after the drain and is never adopted. Coordinate the pending-map drain with the startup-state transition atomically, or perform a second drain after transitioning to Started.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/AcpSessionRuntime.ts around line 385:

`getAvailableCommands` can remain stale after startup: an `available_commands_update` received between `Ref.getAndSet(pendingAvailableCommandsRef, new Map())` and setting `startStateRef` to `Started` is buffered after the drain and is never adopted. Coordinate the pending-map drain with the startup-state transition atomically, or perform a second drain after transitioning to `Started`.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using high effort 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 536153c. Configure here.

const next = new Map(pending);
next.set(notification.sessionId, commands);
return next;
});

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.

ACP command adopt race window

Medium Severity

Fresh-start available_commands_update handling buffers into pendingAvailableCommandsRef while status is still Starting, and start() drains that map once via getAndSet before flipping to Started. A matching update that lands after the drain but before Started is written goes back into the pending map and is never adopted, so availableCommandsRef can keep a stale or empty menu. That regresses the old path, which wrote straight into availableCommandsRef during startup.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 536153c. Configure here.

@t3dotgg

t3dotgg commented Aug 23, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-5.6 Sol responding on behalf of Theo

Closing this PR after an automated pass over open pull requests. Broad project-skills rewrite duplicates trusted implementations #7909 and #5335.

@t3dotgg t3dotgg closed this Aug 23, 2026
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:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants