Skip to content

fix(server): thread cwd through Claude capability probe (#2048)#2124

Open
mvanhorn wants to merge 2 commits into
pingdotgg:mainfrom
mvanhorn:fix/2048-skill-discovery-cwd
Open

fix(server): thread cwd through Claude capability probe (#2048)#2124
mvanhorn wants to merge 2 commits into
pingdotgg:mainfrom
mvanhorn:fix/2048-skill-discovery-cwd

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

@mvanhorn mvanhorn commented Apr 17, 2026

Summary

probeClaudeCapabilities enables settingSources: ["user", "project", "local"] but never passed a cwd to the Claude Agent SDK, so the SDK resolved the "project" and "local" sources against the T3 server process cwd rather than the user's active project. Project-level .claude/skills/ was invisible in composer autocomplete even though those skills still executed when typed manually (#2048).

Why this matters

From #2048 (@r3xsean, area: apps/server):

Project-level skills from .claude/skills/ are not discovered in the composer autocomplete when using the Claude provider. User-level skills from ~/.claude/skills/ appear correctly. Skills execute fine when typed manually - only the autocomplete/discovery is broken.

probeClaudeCapabilities enables settingSources: ["user", "project", "local"] but does not pass cwd, so the SDK resolves project/local sources relative to the T3 server process cwd.

Tested on 0.0.17-nightly.20260415.45 with 40+ project-level skills.

Changes

apps/server/src/provider/Layers/ClaudeProvider.ts:

  • probeClaudeCapabilities(binaryPath, cwd?) now takes an optional cwd and spreads { cwd } into the claudeQuery options when set. When cwd is undefined the SDK keeps its previous behavior.
  • subscriptionProbeCache key becomes ${binaryPath}|${cwd ?? ""}. Capacity bumped from 1 to 8 so switching between projects doesn't force a re-probe each time (the probe talks to the SDK's init path, which is not free).
  • Cache lookup parses the composite key back into (binaryPath, cwd) before calling the probe.
  • Added a short comment on the probe explaining the cwd contract so future edits don't regress.

What this PR does NOT do

The two arrow functions at lines 781-789 that feed subscriptionType and slashCommands into checkClaudeProviderStatus still receive binaryPath only. The final wire-up threads session.cwd from ProviderService (which already has it on hand at apps/server/src/provider/Layers/ProviderService.ts:110) into those lookups.

I'm happy to follow up with that caller change as a separate PR once this one lands and you confirm the shape of the probe is what you want. Keeping this PR tight to the probe layer so the diff is easy to review. If you'd prefer the wire-up in one PR, let me know and I'll expand.

Testing

cd apps/server && bun run typecheck passes with exit 0.

No behavior change when the caller still passes undefined for cwd - the probe keeps its current fallback path.

Request

@r3xsean on the thread has a 40-skill .claude/skills/ setup and reproduced the bug cleanly. Once the caller is wired, they're a good candidate for verification.

Fixes #2048 (probe layer only - caller wire-up to follow).

This contribution was developed with AI assistance (Claude Code).


Note

Medium Risk
Behavior changes in Claude provider probing/caching (new cwd handling and multi-entry cache) could affect provider status/slash-command discovery if the workspace root or cache key parsing is incorrect.

Overview
Fixes Claude capability probing to be project-aware by passing an optional cwd into probeClaudeCapabilities, so the SDK resolves settingSources: ['project','local'] against the active workspace and includes .claude/skills/ in initialization results.

Updates the probe cache to key by binaryPath|cwd (via new claudeProbeCacheKey), increases cache capacity from 1→8, and adjusts cache lookup to parse the composite key; adds focused tests for the new cache-key behavior.

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

Note

Thread cwd through Claude capability probe and key cache by workspace directory

  • Adds an optional cwd parameter to probeClaudeCapabilities in ClaudeProvider.ts, forwarding it to claudeQuery so the Claude Agent SDK resolves project and local settings relative to the workspace root.
  • Introduces claudeProbeCacheKey(binaryPath, cwd) to build composite cache keys, and increases the probe cache capacity from 1 to 8 entries so results are cached per binary+workspace combination.
  • Updates checkProvider to pass workspaceRoot from ServerConfig when looking up or populating the cache.
  • Adds unit tests for claudeProbeCacheKey covering undefined and defined cwd cases.

Macroscope summarized 614529d.

probeClaudeCapabilities enables settingSources: ['user', 'project',
'local'] but never passed a cwd to the Claude Agent SDK. The SDK then
resolved the 'project' and 'local' sources against the T3 server
process cwd, not the user's active project, so project-level
.claude/skills/ was invisible in composer autocomplete even though
those skills still ran fine when typed manually.

Changes:

- probeClaudeCapabilities(binaryPath, cwd?) now forwards an optional
  cwd into claudeQuery options. When undefined the SDK keeps its
  previous behavior.
- subscriptionProbeCache key becomes '${binaryPath}|${cwd ?? ""}'
  with capacity bumped to 8 so a handful of recent projects stay
  cached without forcing re-probes on every project switch.
- Cache lookup parses the composite key back into (binaryPath, cwd)
  before calling the probe.

The cache and the probe now support per-project cwds. The remaining
wire-up is at the caller of checkClaudeProviderStatus - the
subscriptionType and slashCommands lookup arrow functions still
receive only binaryPath. Threading the active session.cwd from
ProviderService (apps/server/src/provider/Layers/ProviderService.ts
already has session.cwd on hand) into those two lookups completes
the fix; happy to follow up in a separate PR if you'd like this one
kept tight to the probe layer.

Reporter @r3xsean has a 40-skill project-level .claude/skills/
setup and is a good candidate for verification once the caller is
wired through.

apps/server typecheck passes.

Fixes pingdotgg#2048
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 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: d10aff26-45e5-470c-b182-b17199063573

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:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 17, 2026
Comment thread apps/server/src/provider/Layers/ClaudeProvider.ts
@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp Bot commented Apr 17, 2026

Approvability

Verdict: Approved

This is a straightforward bug fix that threads the workspace directory through the Claude capability probe so project-level slash commands resolve correctly. The changes are well-scoped, include clear documentation and unit tests, and don't touch any sensitive code paths.

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

Follow-up to the probe change earlier in this PR. probeKey accepted
an optional cwd but every call site passed undefined, so the cache
key collapsed to `${binaryPath}|` and the SDK never received the
active project directory — defeating the `.claude/skills/`
resolution the PR was supposed to enable.

- ClaudeProviderLive now resolves `cwd` once from `ServerConfig` at
  layer construction and closes over it as `workspaceRoot`.
- Both Cache.get call sites pass `workspaceRoot` through the cache
  key. The cache lookup already parses it back out and forwards it
  into `probeClaudeCapabilities`.
- `probeKey` is renamed and exported as `claudeProbeCacheKey` so
  the per-cwd contract is testable.
- Adds three unit tests covering distinct cwds, undefined/empty
  equivalence, and distinct binaries.

ServerConfig.cwd is the same value serverRuntimeStartup already
treats as `workspaceRoot` (serverRuntimeStartup.ts:199), so no
new service surface is needed.

Refs pingdotgg#2048
@github-actions github-actions Bot added size:M 30-99 changed lines (additions + deletions). and removed size:S 10-29 changed lines (additions + deletions). labels Apr 18, 2026
@mvanhorn
Copy link
Copy Markdown
Contributor Author

Good catch. Fixed in 614529d.

ClaudeProviderLive now resolves cwd from ServerConfig at layer construction and threads it into both Cache.get calls, so the cache key actually reflects the active workspace instead of always being ${binaryPath}|. ServerConfig.cwd is the same value serverRuntimeStartup treats as workspaceRoot (serverRuntimeStartup.ts:199), so no new service surface was needed.

Added three unit tests for the per-cwd key contract (distinct cwds, undefined/empty equivalence, distinct binaries).

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

Labels

size:M 30-99 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.

[Bug]: Claude provider skill discovery missing cwd — project-level .claude/skills/ not discovered in composer autocomplete

1 participant