Skip to content

Commit 22535a1

Browse files
committed
fix: handle CRLF line endings and Windows path separators in skill/plugin loading
- Fix YAML frontmatter regex in skillParser, skillManager, subagentParser to accept \r\n (CRLF) line endings. On Windows, git checkout with core.autocrlf=true converts LF→CRLF, causing all SKILL.md frontmatter parsing to fail silently — plugin skills were skipped and never registered as slash commands. - Fix skillParser skill type detection to recognize Windows backslash paths (\.wave\skills) in addition to forward slash paths. - Fix fileWatcher path comparison to normalize backslashes before matching, so file watch events are correctly dispatched on Windows. - Fix commandPathResolver to split on both / and \ when generating command IDs from file paths.
1 parent 4df8937 commit 22535a1

5 files changed

Lines changed: 18 additions & 12 deletions

File tree

packages/agent-sdk/src/managers/skillManager.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,9 @@ export class SkillManager extends EventEmitter {
442442
const skillPath = `Base directory for this skill: ${skill.skillPath}\n\n`;
443443

444444
// Extract content after frontmatter
445-
const contentMatch = skill.content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)$/);
445+
const contentMatch = skill.content.match(
446+
/^---\r?\n[\s\S]*?\r?\n---\r?\n([\s\S]*)$/,
447+
);
446448
let mainContent = contentMatch ? contentMatch[1].trim() : skill.content;
447449

448450
// 1. Substitute parameters ($1, $ARGUMENTS, etc.)

packages/agent-sdk/src/services/fileWatcher.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,11 @@ export class FileWatcherService extends EventEmitter {
281281

282282
// Notify all watchers that match the path or are parents of the path
283283
for (const [watchedPath, entry] of this.watchers.entries()) {
284+
const normalizedFilePath = filePath.replace(/\\/g, "/");
285+
const normalizedWatchedPath = watchedPath.replace(/\\/g, "/");
284286
if (
285-
filePath === watchedPath ||
286-
filePath.startsWith(watchedPath + "/") ||
287-
// Handle cases where the watched path might be a file and we get an event for it
288-
// (already covered by filePath === watchedPath)
289-
false
287+
normalizedFilePath === normalizedWatchedPath ||
288+
normalizedFilePath.startsWith(normalizedWatchedPath + "/")
290289
) {
291290
entry.lastEvent = event.timestamp;
292291

packages/agent-sdk/src/utils/commandPathResolver.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ export function generateCommandId(filePath: string, rootDir: string): string {
2626
throw new Error("Command filename cannot be empty");
2727
}
2828

29-
const segments = relativePath.split("/").filter((segment) => segment !== "");
29+
const segments = relativePath
30+
.split(/[\\/]/)
31+
.filter((segment) => segment !== "");
3032

3133
// Remove .md extension from the last segment
3234
const lastSegment = segments[segments.length - 1];

packages/agent-sdk/src/utils/skillParser.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function parseSkillFile(
3535
result.content = content;
3636

3737
// Parse YAML frontmatter
38-
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
38+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
3939
if (!frontmatterMatch) {
4040
result.validationErrors.push("Missing YAML frontmatter");
4141
return result;
@@ -53,9 +53,11 @@ export function parseSkillFile(
5353

5454
// Determine skill type and path
5555
const skillPath = basePath || dirname(filePath);
56-
const skillType = skillPath.includes("/.wave/skills")
57-
? "project"
58-
: "personal";
56+
const skillType =
57+
skillPath.includes("/.wave/skills") ||
58+
skillPath.includes("\\.wave\\skills")
59+
? "project"
60+
: "personal";
5961

6062
// Extract allowed tools
6163
let allowedTools: string[] | undefined;

packages/agent-sdk/src/utils/subagentParser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ function parseFrontmatter(content: string): {
3030
frontmatter: SubagentFrontmatter;
3131
body: string;
3232
} {
33-
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
33+
const frontmatterRegex =
34+
/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*\r?\n([\s\S]*)$/;
3435
const match = content.match(frontmatterRegex);
3536

3637
if (!match) {

0 commit comments

Comments
 (0)