Skip to content

Commit 28e9865

Browse files
committed
feat: add support for .agents/skills directory
This change adds support for discovering skills from the .agents/skills directory, following the Agent Skills convention for sharing skills across different AI coding tools. Priority order (later entries override earlier ones): 1. Global ~/.agents/skills (shared across AI coding tools, lowest priority) 2. Project .agents/skills 3. Global ~/.roo/skills (Roo-specific) 4. Project .roo/skills (highest priority) Changes: - Add getGlobalAgentsDirectory() and getProjectAgentsDirectoryForCwd() functions to roo-config - Update SkillsManager.getSkillsDirectories() to include .agents/skills - Update SkillsManager.setupFileWatchers() to watch .agents/skills - Add tests for new functionality
1 parent 7f6272a commit 28e9865

4 files changed

Lines changed: 330 additions & 11 deletions

File tree

src/services/roo-config/__tests__/index.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ vi.mock("../../search/file-search", () => ({
2828

2929
import {
3030
getGlobalRooDirectory,
31+
getGlobalAgentsDirectory,
3132
getProjectRooDirectoryForCwd,
33+
getProjectAgentsDirectoryForCwd,
3234
directoryExists,
3335
fileExists,
3436
readFileIfExists,
@@ -70,6 +72,27 @@ describe("RooConfigService", () => {
7072
})
7173
})
7274

75+
describe("getGlobalAgentsDirectory", () => {
76+
it("should return correct path for global .agents directory", () => {
77+
const result = getGlobalAgentsDirectory()
78+
expect(result).toBe(path.join("/mock/home", ".agents"))
79+
})
80+
81+
it("should handle different home directories", () => {
82+
mockHomedir.mockReturnValue("/different/home")
83+
const result = getGlobalAgentsDirectory()
84+
expect(result).toBe(path.join("/different/home", ".agents"))
85+
})
86+
})
87+
88+
describe("getProjectAgentsDirectoryForCwd", () => {
89+
it("should return correct path for given cwd", () => {
90+
const cwd = "/custom/project/path"
91+
const result = getProjectAgentsDirectoryForCwd(cwd)
92+
expect(result).toBe(path.join(cwd, ".agents"))
93+
})
94+
})
95+
7396
describe("directoryExists", () => {
7497
it("should return true for existing directory", async () => {
7598
mockStat.mockResolvedValue({ isDirectory: () => true } as any)

src/services/roo-config/index.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,50 @@ export function getGlobalRooDirectory(): string {
2828
return path.join(homeDir, ".roo")
2929
}
3030

31+
/**
32+
* Gets the global .agents directory path based on the current platform.
33+
* This is a shared directory for agent skills across different AI coding tools.
34+
*
35+
* @returns The absolute path to the global .agents directory
36+
*
37+
* @example Platform-specific paths:
38+
* ```
39+
* // macOS/Linux: ~/.agents/
40+
* // Example: /Users/john/.agents
41+
*
42+
* // Windows: %USERPROFILE%\.agents\
43+
* // Example: C:\Users\john\.agents
44+
* ```
45+
*
46+
* @example Usage:
47+
* ```typescript
48+
* const globalAgentsDir = getGlobalAgentsDirectory()
49+
* // Returns: "/Users/john/.agents" (on macOS/Linux)
50+
* // Returns: "C:\\Users\\john\\.agents" (on Windows)
51+
* ```
52+
*/
53+
export function getGlobalAgentsDirectory(): string {
54+
const homeDir = os.homedir()
55+
return path.join(homeDir, ".agents")
56+
}
57+
58+
/**
59+
* Gets the project-local .agents directory path for a given cwd.
60+
* This is a shared directory for agent skills across different AI coding tools.
61+
*
62+
* @param cwd - Current working directory (project path)
63+
* @returns The absolute path to the project-local .agents directory
64+
*
65+
* @example
66+
* ```typescript
67+
* const projectAgentsDir = getProjectAgentsDirectoryForCwd('/Users/john/my-project')
68+
* // Returns: "/Users/john/my-project/.agents"
69+
* ```
70+
*/
71+
export function getProjectAgentsDirectoryForCwd(cwd: string): string {
72+
return path.join(cwd, ".agents")
73+
}
74+
3175
/**
3276
* Gets the project-local .roo directory path for a given cwd
3377
*

src/services/skills/SkillsManager.ts

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import * as vscode from "vscode"
55
import matter from "gray-matter"
66

77
import type { ClineProvider } from "../../core/webview/ClineProvider"
8-
import { getGlobalRooDirectory } from "../roo-config"
8+
import { getGlobalRooDirectory, getGlobalAgentsDirectory, getProjectAgentsDirectoryForCwd } from "../roo-config"
99
import { directoryExists, fileExists } from "../roo-config"
1010
import { SkillMetadata, SkillContent } from "../../shared/skills"
1111
import { modes, getAllModes } from "../../shared/modes"
@@ -590,19 +590,41 @@ Add your skill instructions here.
590590
> {
591591
const dirs: Array<{ dir: string; source: "global" | "project"; mode?: string }> = []
592592
const globalRooDir = getGlobalRooDirectory()
593+
const globalAgentsDir = getGlobalAgentsDirectory()
593594
const provider = this.providerRef.deref()
594595
const projectRooDir = provider?.cwd ? path.join(provider.cwd, ".roo") : null
596+
const projectAgentsDir = provider?.cwd ? getProjectAgentsDirectoryForCwd(provider.cwd) : null
595597

596598
// Get list of modes to check for mode-specific skills
597599
const modesList = await this.getAvailableModes()
598600

599-
// Global directories
601+
// Priority order (later entries override earlier ones with same skill name):
602+
// 1. Global .agents/skills (shared across AI coding tools, lowest priority)
603+
// 2. Project .agents/skills
604+
// 3. Global .roo/skills (Roo-specific)
605+
// 4. Project .roo/skills (highest priority)
606+
607+
// Global .agents directories (lowest priority - shared across agents)
608+
dirs.push({ dir: path.join(globalAgentsDir, "skills"), source: "global" })
609+
for (const mode of modesList) {
610+
dirs.push({ dir: path.join(globalAgentsDir, `skills-${mode}`), source: "global", mode })
611+
}
612+
613+
// Project .agents directories
614+
if (projectAgentsDir) {
615+
dirs.push({ dir: path.join(projectAgentsDir, "skills"), source: "project" })
616+
for (const mode of modesList) {
617+
dirs.push({ dir: path.join(projectAgentsDir, `skills-${mode}`), source: "project", mode })
618+
}
619+
}
620+
621+
// Global .roo directories (Roo-specific, higher priority than .agents)
600622
dirs.push({ dir: path.join(globalRooDir, "skills"), source: "global" })
601623
for (const mode of modesList) {
602624
dirs.push({ dir: path.join(globalRooDir, `skills-${mode}`), source: "global", mode })
603625
}
604626

605-
// Project directories
627+
// Project .roo directories (highest priority)
606628
if (projectRooDir) {
607629
dirs.push({ dir: path.join(projectRooDir, "skills"), source: "project" })
608630
for (const mode of modesList) {
@@ -647,20 +669,32 @@ Add your skill instructions here.
647669
if (!provider?.cwd) return
648670

649671
// Watch for changes in skills directories
650-
const globalSkillsDir = path.join(getGlobalRooDirectory(), "skills")
651-
const projectSkillsDir = path.join(provider.cwd, ".roo", "skills")
672+
const globalRooDir = getGlobalRooDirectory()
673+
const globalAgentsDir = getGlobalAgentsDirectory()
674+
const projectRooDir = path.join(provider.cwd, ".roo")
675+
const projectAgentsDir = getProjectAgentsDirectoryForCwd(provider.cwd)
676+
677+
// Watch global .roo skills directory
678+
this.watchDirectory(path.join(globalRooDir, "skills"))
679+
680+
// Watch global .agents skills directory
681+
this.watchDirectory(path.join(globalAgentsDir, "skills"))
652682

653-
// Watch global skills directory
654-
this.watchDirectory(globalSkillsDir)
683+
// Watch project .roo skills directory
684+
this.watchDirectory(path.join(projectRooDir, "skills"))
655685

656-
// Watch project skills directory
657-
this.watchDirectory(projectSkillsDir)
686+
// Watch project .agents skills directory
687+
this.watchDirectory(path.join(projectAgentsDir, "skills"))
658688

659689
// Watch mode-specific directories for all available modes
660690
const modesList = await this.getAvailableModes()
661691
for (const mode of modesList) {
662-
this.watchDirectory(path.join(getGlobalRooDirectory(), `skills-${mode}`))
663-
this.watchDirectory(path.join(provider.cwd, ".roo", `skills-${mode}`))
692+
// .roo mode-specific
693+
this.watchDirectory(path.join(globalRooDir, `skills-${mode}`))
694+
this.watchDirectory(path.join(projectRooDir, `skills-${mode}`))
695+
// .agents mode-specific
696+
this.watchDirectory(path.join(globalAgentsDir, `skills-${mode}`))
697+
this.watchDirectory(path.join(projectAgentsDir, `skills-${mode}`))
664698
}
665699
}
666700

0 commit comments

Comments
 (0)