Skip to content

feat(server): surface portable .agents/skills across provider snapshots - #6382

Open
marcushohlbein wants to merge 2 commits into
pingdotgg:mainfrom
marcushohlbein:feat/cross-agent-skills-discovery
Open

feat(server): surface portable .agents/skills across provider snapshots#6382
marcushohlbein wants to merge 2 commits into
pingdotgg:mainfrom
marcushohlbein:feat/cross-agent-skills-discovery

Conversation

@marcushohlbein

@marcushohlbein marcushohlbein commented Aug 12, 2026

Copy link
Copy Markdown

Problem

Providers without a native skill inventory (Cursor, Grok, OpenCode) never surfaced cross-agent portable skills in the $ picker. A skill placed under ~/.agents/skills or <cwd>/.agents/skills — the portable, cross-agent convention — was invisible to them, even though Claude and Codex already expose skills.

Fix

  • Extract the filesystem scanner (YAML frontmatter parse + best-effort root scan) out of ClaudeSkills.ts into a shared scanFilesystemSkillRoots / discoverAgentSkills module.
  • Claude reuses the shared scanner and layers its vendor roots on top.
  • Cursor / Grok / OpenCode augment their snapshot draft via a new augmentProviderSnapshotWithAgentSkills helper that calls discoverAgentSkills.
  • Codex is intentionally not augmented — it reports skills natively via its app-server.

Resolution rule

Most-specific-wins, extended to two axes:

  • Scope: project beats user (unchanged).
  • Within a scope: for the Claude provider, the Claude-native .claude/skills root beats a portable .agents/skills root of the same name — so a Claude-specific skill keeps running even when a portable namesake exists. (Portable-first would silently hide existing .claude skills the moment a same-named portable one appears.)

Tests

  • New AgentSkills.test.ts and augmentProviderSnapshotWithAgentSkills.test.ts cover the shared scanner and the augment seam (both branches: no-op passthrough returns the same draft reference; populated roots attach skills with project winning on collision).
  • ClaudeSkills.test.ts gains a characterization test pinning the within-scope .claude-over-.agents precedence.
  • Existing Claude collision tests still pass. 14/14 green; vp run --filter t3 typecheck clean for the touched files.

Generated with Claude Code (anthropic) inside the T3 Code harness.


Note

Low Risk
Read-only filesystem scans and snapshot augmentation; new RPC is orchestration read-scoped. Collision rules are explicit and covered by tests; no auth or data mutation paths.

Overview
Adds shared filesystem discovery for portable cross-agent skills under ~/.agents/skills and <cwd>/.agents/skills, and wires them into provider snapshots and a new per-project RPC.

Provider snapshots: Cursor, Grok, and OpenCode now augment their checkProvider draft with augmentProviderSnapshotWithAgentSkills (Codex unchanged—native inventory). Claude refactors onto the shared scanFilesystemSkillRoots scanner and also scans .agents/skills alongside Claude-native roots, with project over user and .claude/skills over .agents/skills within a scope.

Per-project listing: New projects.listAgentSkills RPC (read scope), WorkspaceAgentSkills service, and contracts types list project-scoped skills for a given cwd and provider—Claude includes .claude/skills; other drivers use portable roots only.

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

Note

Surface portable .agents/skills across provider snapshots via a new projects.listAgentSkills RPC

  • Adds filesystem-based discovery of portable agent skills from ~/.agents/skills (user scope) and <workspace>/.agents/skills (project scope), with project entries overriding user entries on name collisions.
  • For Claude, discovery also includes .claude/skills directories; Claude-native skills override portable ones within the same scope.
  • Introduces augmentProviderSnapshotWithAgentSkills to attach discovered skills to provider snapshots for Cursor, Grok, and OpenCode drivers; returns the original draft unchanged when no skills are found.
  • Adds a new WorkspaceAgentSkills service and a projects.listAgentSkills WebSocket RPC (requiring orchestration read scope) that returns project-scoped skills for a given provider and working directory.
  • Skill entries use frontmatter name/description when present, falling back to the directory name; malformed or unreadable entries are silently skipped.

Macroscope summarized 0513515.

Providers without a native skill inventory (Cursor, Grok, OpenCode) did not
surface cross-agent portable skills in the `$` picker, so a skill placed under
`~/.agents/skills` or `<cwd>/.agents/skills` was invisible to them. Extract the
filesystem scanner (frontmatter parse + best-effort root scan) out of
ClaudeSkills.ts into a shared scanFilesystemSkillRoots/discoverAgentSkills
module; Claude reuses it and layers its `.claude` roots, while the three
skill-less providers augment their snapshot draft via a new
augmentProviderSnapshotWithAgentSkills helper. Resolution stays
most-specific-wins, extended to two axes: project beats user, and within a
scope the Claude-native `.claude/skills` root beats a portable `.agents` namesake.

Codex is intentionally not augmented: it reports skills natively via its
app-server.

Generated with Claude Code (anthropic) inside the T3 Code harness.
@coderabbitai

coderabbitai Bot commented Aug 12, 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: 2b96ffed-9f17-47e7-8033-765732204747

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 the size:L 100-499 changed lines (additions + deletions). label Aug 12, 2026
const textGeneration = yield* makeCursorTextGeneration(effectiveConfig, processEnv);

const checkProvider = checkCursorProviderStatus(effectiveConfig, processEnv).pipe(
Effect.flatMap((draft) => augmentProviderSnapshotWithAgentSkills(draft, cwd)),

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.

🟠 High Drivers/CursorDriver.ts:138

Cursor projects opened outside the server startup directory receive the wrong skills inventory: checkProvider advertises skills from ServerConfig.cwd and omits the project's <project>/.agents/skills, while exposing startup-directory skills to every project. Project-scoped discovery needs to use the workspace cwd associated with the project/thread rather than the process startup cwd.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Drivers/CursorDriver.ts around line 138:

Cursor projects opened outside the server startup directory receive the wrong skills inventory: `checkProvider` advertises skills from `ServerConfig.cwd` and omits the project's `<project>/.agents/skills`, while exposing startup-directory skills to every project. Project-scoped discovery needs to use the workspace cwd associated with the project/thread rather than the process startup cwd.

@github-actions github-actions Bot added the vouch:unvouched PR author is not yet trusted in the VOUCHED list. label Aug 12, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. This PR introduces new agent skills discovery capability across multiple providers, including a new RPC endpoint and service. New feature additions of this scope warrant human review. Additionally, a High-severity finding identifies that CursorDriver uses the server startup cwd rather than the project workspace cwd, causing incorrect skill discovery for projects opened elsewhere.

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

The provider snapshot is environment-level, so project-scoped skill discovery
keyed on ServerConfig.cwd (the launch dir) leaks the startup project's skills to
every project and hides each project's own `<root>/.agents/skills`. This adds
the server foundation for per-project resolution (web wiring + snapshot switch
follow in a later change):

- Split discovery into user-only / project-only primitives
  (`discoverUserAgentSkills`, `discoverProjectAgentSkills`,
  `discoverClaudeProjectSkills`) alongside the existing combined scanner.
- New `WorkspaceAgentSkills` service resolves project skills for a workspace
  root + provider (Claude adds `<root>/.claude/skills`; others portable only),
  reusing `WorkspacePaths.normalizeWorkspaceRoot`.
- New `projects.listAgentSkills` RPC (contract + ws handler + auth scope) so the
  client can fetch skills for the active thread's workspace root.
- No behavior change yet: the snapshot still carries skills as before; this RPC
  is additive until the client merges its results and the snapshot drops
  project roots.

Generated with Claude Code (anthropic) inside the T3 Code harness.

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

Reviewed the new Effect service (WorkspaceAgentSkills), the skill-discovery helpers, the driver call sites, and the new RPC contract error.

One convention violation and one suggestion:

  • ProjectListAgentSkillsError takes a pre-formatted message from the RPC handler instead of deriving it from its structural attributes, and it drops the normalized failure context that every sibling project error carries.
  • WorkspaceAgentSkills leaks FileSystem | Path into its service interface requirement channel instead of acquiring them in make.

Everything else looks consistent with the conventions: namespace subpath imports, canonical Context.Service -> make -> layer ordering in WorkspaceAgentSkills.ts, no service-instance injection, no ManagedRuntime/runPromise in service code, and the shared scanFilesystemSkillRoots extraction preserves the original comments and invariants.

Posted via Macroscope — Effect Service Conventions

Comment on lines +32 to +38
readonly list: (
input: ProjectListAgentSkillsInput,
) => Effect.Effect<
ProjectListAgentSkillsResult,
WorkspacePaths.WorkspacePathsError,
FileSystem.FileSystem | Path.Path
>;

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.

The service interface pushes FileSystem.FileSystem | Path.Path onto every consumer's requirement channel; make acquires only WorkspacePaths. Other services in this directory (WorkspaceFileSystem, WorkspaceEntries) acquire their platform dependencies in make so their methods are Effect<A, E> and the layer owns the requirements. Suggest doing the same here: yield* FileSystem.FileSystem / yield* Path.Path in make, provide them to the discovery helpers with Effect.provideService, and drop the R parameter from the interface.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/ws.ts
Comment on lines +1832 to +1836
new ProjectListAgentSkillsError({
cwd: input.cwd,
message: `Failed to list project agent skills in '${input.cwd}'.`,
cause,
}),

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.

Constructing the caller-visible message at the wrapping site duplicates cwd into an unstructured field. Once ProjectListAgentSkillsError derives its message from its attributes (see the contracts comment), drop message here and pass the structural context instead — cwd, provider, and a normalized failure category from cause — matching the neighbouring projectsReadFile / projectsListEntries handlers that spread projectFileFailureContext(cause) / projectEntriesFailureContext(cause).

Posted via Macroscope — Effect Service Conventions

Comment on lines +315 to +322
export class ProjectListAgentSkillsError extends Schema.TaggedErrorClass<ProjectListAgentSkillsError>()(
"ProjectListAgentSkillsError",
{
cwd: Schema.optional(TrimmedNonEmptyString),
message: TrimmedNonEmptyString,
cause: Schema.optional(Schema.Defect()),
},
) {}

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.

message is a required field here but is not derived from the error's own attributes — the RPC handler in ws.ts hand-formats the string and passes it in. Every sibling error in this file (ProjectListEntriesError, ProjectReadFileError, ProjectWriteFileError) derives the message inside the class from cwd and keeps a normalized failure category so callers get structured context rather than a prose blob. Consider mirroring that pattern (and, since the wrapped WorkspacePathsError union distinguishes not-exists / create-failed / stat-failed / not-directory, capturing that category plus the requested provider):

-) {}
+) {
+  // @effect-diagnostics-next-line overriddenSchemaConstructor:off
+  constructor(props: { readonly cwd: string; readonly cause?: unknown }) {
+    super({
+      ...props,
+      message:
+        decodedProjectErrorMessage(props) ??
+        `Failed to list project agent skills in '${props.cwd}'.`,
+    } as any);
+  }
+}

Posted via Macroscope — Effect Service Conventions

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

Labels

size:L 100-499 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