Skip to content

fix(core): handle Windows path separators in workspace skill discovery#17671

Open
Sandy-1711 wants to merge 5 commits into
mastra-ai:mainfrom
Sandy-1711:fix/workspace-skills-windows-paths
Open

fix(core): handle Windows path separators in workspace skill discovery#17671
Sandy-1711 wants to merge 5 commits into
mastra-ai:mainfrom
Sandy-1711:fix/workspace-skills-windows-paths

Conversation

@Sandy-1711

@Sandy-1711 Sandy-1711 commented Jun 8, 2026

Copy link
Copy Markdown

WorkspaceSkills split paths on '/' only, so absolute Windows paths passed via new Workspace({ skills: [...] }) loaded with the wrong name or failed to load, and skills under node_modules were classified as local instead of external.

Description

WorkspaceSkills in @mastra/core hardcoded / as the path separator, so consumer-supplied absolute Windows paths (e.g. C:\Users\me\skills\my-skill) were mishandled:

  • addSkill / #discoverDirectSkill derived dirName via .split('/').pop(), so on Windows it became either the entire path string or unknown. Since the skill name must match its directory name, the skill loaded with the wrong name or failed validation entirely.
  • #getParentPath only looked for /, so the parent directory of a backslash path resolved incorrectly.
  • The endsWith('/SKILL.md') checks never matched a backslash path, so a path pointing directly at a SKILL.md file took the wrong branch.
  • #determineSource split on / for node_modules detection, so external skills were misclassified as local (and the same applied to .mastra managed paths).

The fix keeps workspace-internal paths POSIX and only adds separator tolerance where consumer-supplied absolute paths enter:

  • Added splitPathSegments() (splits on / or \) and isSkillFilePath() (separator-tolerant SKILL.md detection).
  • Made #getParentPath use Math.max(lastIndexOf('/'), lastIndexOf('\\')).
  • Updated #determineSource to segment-match node_modules on both separators and normalize before the .mastra/skills check.

Skills passed by absolute path now resolve identically on Windows and POSIX:

new Workspace({
  skills: ['C:\\Users\\me\\skills\\my-skill'],
});

Related issue(s)

Fixes #17670

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • Test update

Checklist

  • I have linked the related issue(s) in the description above
  • I have made corresponding changes to the documentation (if applicable)
  • I have added tests that prove my fix is effective or that my feature works
  • I have addressed all Coderabbit comments on this PR

Summary by CodeRabbit

  • Bug Fixes

    • Fixed workspace skill discovery on Windows—absolute Windows paths now resolve correctly, preventing skills from loading under incorrect names or failing entirely.
    • Improved classification of skills in node_modules to correctly identify them as external rather than local.
  • Tests

    • Added comprehensive test coverage for Windows path handling in skill discovery.

WorkspaceSkills split paths on '/' only, so absolute Windows paths passed via new Workspace({ skills: [...] }) loaded with the wrong name or failed to load, and skills under node_modules were classified as local instead of external.

Add separator-tolerant path helpers, make getParentPath handle backslashes, and fix the SKILL.md path detection and source classification. Closes mastra-ai#17670.
@changeset-bot

changeset-bot Bot commented Jun 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 62839ab

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 20 packages
Name Type
@mastra/core Patch
mastracode Patch
@mastra/mcp-docs-server Patch
@internal/playground Patch
@mastra/client-js Patch
@mastra/opencode Patch
@mastra/longmemeval Patch
mastra Patch
@mastra/deployer-cloud Patch
@mastra/react Patch
@mastra/playground-ui Patch
@mastra/server Patch
@mastra/deployer Patch
create-mastra Patch
@mastra/express Patch
@mastra/fastify Patch
@mastra/hono Patch
@mastra/koa Patch
@mastra/nestjs Patch
@mastra/temporal Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 8, 2026

Copy link
Copy Markdown

@Sandy-1711 is attempting to deploy a commit to the Mastra Team on Vercel.

A member of the Team first needs to authorize it.

@dane-ai-mastra dane-ai-mastra Bot added the complexity: low Low-complexity PR label Jun 8, 2026
@dane-ai-mastra

dane-ai-mastra Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

PR triage

Linked issue check passed (#17670).

Mastra uses CodeRabbit for automated code reviews. Please address all feedback from CodeRabbit by either making changes to your PR or leaving a comment explaining why you disagree with the feedback. Since CodeRabbit is an AI, it may occasionally provide incorrect feedback.


PR complexity score

Factor Value Score impact
Files changed 3 +6
Lines changed 110 +6
Author merged PRs 0 -0
Test files changed Yes -10
Final score 2

Applied label: complexity: low


Changed test gate

Changed Test Gate is pending. The Changed Test Gate / changed-tests check will update the test label when it completes.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR fixes Windows path separator handling in workspace skill discovery. Workspace skills previously hardcoded / splitting, causing absolute Windows paths to be mishandled during skill registration and source type classification. Changes add separator-tolerant path utilities and update five entry-point call sites in WorkspaceSkillsImpl to use them.

Changes

Windows Path Separator Support

Layer / File(s) Summary
Path separator utility helpers
packages/core/src/workspace/skills/workspace-skills.ts
New internal helpers splitPathSegments() and isSkillFilePath() provide separator-tolerant path operations supporting both forward slashes and backslashes.
Skill path processing and source classification
packages/core/src/workspace/skills/workspace-skills.ts
addSkill(), discoverDirectSkill(), determineSource(), and getParentPath() now use separator-tolerant logic to extract directory names, detect SKILL.md files, classify skill sources, and compute parent paths regardless of input path style.
Test coverage for Windows path handling
packages/core/src/workspace/skills/workspace-skills.test.ts
New test suite validates skill registration and retrieval with Windows directory paths, Windows SKILL.md file paths, node_modules classification as external, and ensures POSIX paths continue working.
Release notes and changelog
.changeset/workspace-skills-windows-paths.md
Documents the Windows path fix, behavioral changes, and includes a code example demonstrating Workspace skill discovery with absolute Windows paths.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • TylerBarnes
  • YujohnNattrass

Poem

🐰 On Windows paths both old and new,
With backslashes instead of /,
The workspace skills now split right through—
Forward, back, and up above.
One regex rule makes all things true,
And node_modules paths move smooth and true! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: fixing Windows path separator handling in workspace skill discovery.
Linked Issues check ✅ Passed The PR changes comprehensively address all five failure modes identified in issue #17670, including path splitting, SKILL.md detection, node_modules classification, and separator tolerance.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing Windows path separator handling in workspace skill discovery, with no unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Stopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a @coderabbit review after the pipeline has finished.


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.

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

🧹 Nitpick comments (2)
packages/core/src/workspace/skills/workspace-skills.test.ts (2)

2980-3036: ⚡ Quick win

Optional: Add test using constructor-level skills config for realism.

The PR objectives specifically mention "when passed to new Workspace({ skills: [...] })", but all four tests use addSkill() instead of the constructor-level skills parameter. Adding at least one test that passes a Windows path via the constructor would validate the full discovery flow including glob resolution and would better match the user-facing API described in the PR objectives.

💡 Suggested test case
+    it('discovers a skill via constructor with Windows directory path', async () => {
+      const filesystem = createMockFilesystem({
+        'C:\\Users\\me\\skills\\my-skill/SKILL.md': MY_SKILL_MD,
+      });
+      const skills = new WorkspaceSkillsImpl({
+        source: filesystem,
+        skills: ['C:\\Users\\me\\skills'],
+      });
+
+      const result = await skills.get('my-skill');
+      expect(result?.name).toBe('my-skill');
+    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/workspace/skills/workspace-skills.test.ts` around lines
2980 - 3036, Add a test that passes a Windows path via the WorkspaceSkillsImpl
constructor-level skills config (e.g., new WorkspaceSkillsImpl({ source:
filesystem, skills: ['C:\\Users\\me\\skills\\my-skill'] })) instead of calling
addSkill(), so the constructor/glob resolution flow is exercised; create a mock
filesystem entry for the Windows SKILL.md (reuse MY_SKILL_MD), instantiate
WorkspaceSkillsImpl with the skills array containing a Windows directory or
SKILL.md path, then assert discovery (e.g., await skills.get('my-skill') and
expect name/source as in the existing tests).

3025-3036: ⚡ Quick win

Optional: Add test for managed source (.mastra) with Windows path.

The tests validate external (node_modules) classification but not managed (.mastra/skills) classification with Windows paths. Based on the upstream contract, #determineSource normalizes backslashes to forward slashes before checking for .mastra/skills, so adding a test case would verify that code path.

💡 Suggested test case
+    it('classifies a Windows .mastra/skills path as a managed skill', async () => {
+      const filesystem = createMockFilesystem({
+        'C:\\.mastra\\skills\\my-skill/SKILL.md': MY_SKILL_MD,
+      });
+      const skills = new WorkspaceSkillsImpl({ source: filesystem, skills: [] });
+
+      await skills.addSkill('C:\\.mastra\\skills\\my-skill');
+
+      const result = await skills.get('my-skill');
+      expect(result?.source.type).toBe('managed');
+    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/workspace/skills/workspace-skills.test.ts` around lines
3025 - 3036, Add a test to cover managed (.mastra) sources with Windows-style
backslashes: create a mock filesystem entry using a Windows path like
'.mastra\\skills\\my-skill\\SKILL.md' (or call addSkill with '
.mastra\\skills\\my-skill'), instantiate WorkspaceSkillsImpl, call addSkill('
.mastra\\skills\\my-skill') and assert get('my-skill') returns the skill; this
verifies determineSource's backslash-to-slash normalization logic used when
classifying managed sources in WorkspaceSkillsImpl (look for determineSource,
WorkspaceSkillsImpl.addSkill, and WorkspaceSkillsImpl.get).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/src/workspace/skills/workspace-skills.test.ts`:
- Around line 2980-3036: Add a test that passes a Windows path via the
WorkspaceSkillsImpl constructor-level skills config (e.g., new
WorkspaceSkillsImpl({ source: filesystem, skills:
['C:\\Users\\me\\skills\\my-skill'] })) instead of calling addSkill(), so the
constructor/glob resolution flow is exercised; create a mock filesystem entry
for the Windows SKILL.md (reuse MY_SKILL_MD), instantiate WorkspaceSkillsImpl
with the skills array containing a Windows directory or SKILL.md path, then
assert discovery (e.g., await skills.get('my-skill') and expect name/source as
in the existing tests).
- Around line 3025-3036: Add a test to cover managed (.mastra) sources with
Windows-style backslashes: create a mock filesystem entry using a Windows path
like '.mastra\\skills\\my-skill\\SKILL.md' (or call addSkill with '
.mastra\\skills\\my-skill'), instantiate WorkspaceSkillsImpl, call addSkill('
.mastra\\skills\\my-skill') and assert get('my-skill') returns the skill; this
verifies determineSource's backslash-to-slash normalization logic used when
classifying managed sources in WorkspaceSkillsImpl (look for determineSource,
WorkspaceSkillsImpl.addSkill, and WorkspaceSkillsImpl.get).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7c6c3cf9-01c6-494a-9106-2ad8cc6d9601

📥 Commits

Reviewing files that changed from the base of the PR and between 2aba4c3 and fbd82b9.

📒 Files selected for processing (3)
  • .changeset/workspace-skills-windows-paths.md
  • packages/core/src/workspace/skills/workspace-skills.test.ts
  • packages/core/src/workspace/skills/workspace-skills.ts

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

If run is green, this works! Thank you for the contribution

@Sandy-1711

Copy link
Copy Markdown
Author

@wardpeet thanks for the review! The required checks (Lint, Lint docs, Merge Test Reports) are stuck on "Expected — waiting for status" because I updated my branch. Could you please approve the workflow runs?

@Sandy-1711

Copy link
Copy Markdown
Author

Rebased on latest main — this now includes #17690 which fixed the harness.tools CI flake. Could a maintainer approve the workflow run?

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

Labels

complexity: low Low-complexity PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] WorkspaceSkills splits absolute paths on '/' only — breaks on Windows

3 participants