Skip to content

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

Open
mvanhorn wants to merge 1 commit intopingdotgg:mainfrom
mvanhorn:fix/2048-skill-discovery-cwd
Open

fix(server): thread cwd through Claude capability probe (#2048)#2124
mvanhorn wants to merge 1 commit intopingdotgg: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

Low Risk
Low risk: localized changes to Claude provider probing and caching; behavior is unchanged unless a cwd is supplied, with only a small memory/perf tradeoff from a larger cache.

Overview
Updates the Claude provider’s SDK capability probe to accept an optional cwd and pass it into claudeQuery, so project/local setting sources (and project-level slash commands like .claude/skills/) can be discovered correctly.

Adjusts the subscription/slash-command probe cache to key by binaryPath|cwd and increases capacity from 1 to 8, preventing cross-project cache collisions and reducing re-probes when switching between projects.

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

Note

Thread cwd through probeClaudeCapabilities for project-aware capability probing

  • Adds an optional cwd parameter to probeClaudeCapabilities in ClaudeProvider.ts, passing it to the Claude Agent SDK so project/local settings are resolved correctly during the probe.
  • Changes the subscription probe cache key from binaryPath to binaryPath|cwd and increases cache capacity from 1 to 8 to support multiple working directories.
  • Behavioral Change: capability probe results are now scoped per (binaryPath, cwd) pair, so the same binary may be probed multiple times for different working directories.
📊 Macroscope summarized e7bacee. 1 file reviewed, 1 issue evaluated, 0 issues filtered, 1 comment posted

🗂️ Filtered Issues

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: f692283f-1a37-403b-aa98-5f1e4ac7cf58

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

🟢 Low Layers/ClaudeProvider.ts:791

The probeKey function includes cwd in the cache key format ${binaryPath}|${cwd ?? ""}, but the resolve callbacks in checkClaudeProviderStatus only receive binaryPath and never pass cwd. This means cwd is always undefined, the cache key is always ${binaryPath}|, and the SDK never receives the project directory for resolving .claude/skills/ commands despite the comment explaining this is the purpose (#2048).

     const checkProvider = checkClaudeProviderStatus(
       (binaryPath) =>
-        Cache.get(subscriptionProbeCache, probeKey(binaryPath)).pipe(
+        Cache.get(subscriptionProbeCache, probeKey(binaryPath, /* TODO: get cwd from caller */)).pipe(
           Effect.map((probe) => probe?.subscriptionType),
         ),
       (binaryPath) =>
-        Cache.get(subscriptionProbeCache, probeKey(binaryPath)).pipe(
+        Cache.get(subscriptionProbeCache, probeKey(binaryPath, /* TODO: get cwd from caller */)).pipe(
           Effect.map((probe) => probe?.slashCommands),
         ),
     ).pipe(
🤖 Copy this AI Prompt to have your agent fix this:
In file apps/server/src/provider/Layers/ClaudeProvider.ts around lines 791-803:

The `probeKey` function includes `cwd` in the cache key format `${binaryPath}|${cwd ?? ""}`, but the resolve callbacks in `checkClaudeProviderStatus` only receive `binaryPath` and never pass `cwd`. This means `cwd` is always `undefined`, the cache key is always `${binaryPath}|`, and the SDK never receives the project directory for resolving `.claude/skills/` commands despite the comment explaining this is the purpose (#2048).

Evidence trail:
- apps/server/src/provider/Layers/ClaudeProvider.ts line 789: `probeKey` definition includes `cwd` parameter
- apps/server/src/provider/Layers/ClaudeProvider.ts lines 521-525: `checkClaudeProviderStatus` callback signatures only accept `binaryPath`
- apps/server/src/provider/Layers/ClaudeProvider.ts lines 792-796: callbacks call `probeKey(binaryPath)` without `cwd`
- apps/server/src/provider/Layers/ClaudeProvider.ts lines 465-469: comment explains `cwd` is needed for `.claude/skills/` resolution (#2048)

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Apr 17, 2026

Approvability

Verdict: Needs human review

An unresolved review comment identifies that the fix may be incomplete - the cwd parameter is added to the cache key format but never actually passed through the callback sites, potentially defeating the purpose of the change. Human review should verify the implementation is correct.

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

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

Labels

size:S 10-29 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