fix: resolve symlinks in skill config source discovery and test paths
Use fs.realpath() in config-source-discovery to resolve symlinks before loading skills, preventing duplicate/mismatched paths on systems where tmpdir() returns a symlink (e.g., macOS /var → /private/var). Also adds agents-config-dir utility for ~/.agents path resolution.
This commit is contained in:
@@ -53,26 +53,28 @@ async function loadSourcePath(options: {
|
||||
const stat = await fs.stat(absolutePath).catch(() => null)
|
||||
if (!stat) return []
|
||||
|
||||
const realBasePath = await fs.realpath(absolutePath).catch(() => absolutePath)
|
||||
|
||||
if (stat.isFile()) {
|
||||
if (!isMarkdownPath(absolutePath)) return []
|
||||
if (!isMarkdownPath(realBasePath)) return []
|
||||
const loaded = await loadSkillFromPath({
|
||||
skillPath: absolutePath,
|
||||
resolvedPath: dirname(absolutePath),
|
||||
defaultName: inferSkillNameFromFileName(absolutePath),
|
||||
skillPath: realBasePath,
|
||||
resolvedPath: dirname(realBasePath),
|
||||
defaultName: inferSkillNameFromFileName(realBasePath),
|
||||
scope: "config",
|
||||
})
|
||||
if (!loaded) return []
|
||||
return filterByGlob([loaded], dirname(absolutePath), options.globPattern)
|
||||
return filterByGlob([loaded], dirname(realBasePath), options.globPattern)
|
||||
}
|
||||
|
||||
if (!stat.isDirectory()) return []
|
||||
|
||||
const directorySkills = await loadSkillsFromDir({
|
||||
skillsDir: absolutePath,
|
||||
skillsDir: realBasePath,
|
||||
scope: "config",
|
||||
maxDepth: options.recursive ? MAX_RECURSIVE_DEPTH : 0,
|
||||
})
|
||||
return filterByGlob(directorySkills, absolutePath, options.globPattern)
|
||||
return filterByGlob(directorySkills, realBasePath, options.globPattern)
|
||||
}
|
||||
|
||||
export async function discoverConfigSourceSkills(options: {
|
||||
|
||||
14
src/shared/agents-config-dir.test.ts
Normal file
14
src/shared/agents-config-dir.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { describe, test, expect } from "bun:test"
|
||||
import { getAgentsConfigDir } from "./agents-config-dir"
|
||||
|
||||
describe("getAgentsConfigDir", () => {
|
||||
test("returns path ending with .agents", () => {
|
||||
// given agents config dir is requested
|
||||
|
||||
// when getAgentsConfigDir is called
|
||||
const result = getAgentsConfigDir()
|
||||
|
||||
// then returns path ending with .agents
|
||||
expect(result.endsWith(".agents")).toBe(true)
|
||||
})
|
||||
})
|
||||
6
src/shared/agents-config-dir.ts
Normal file
6
src/shared/agents-config-dir.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { homedir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
|
||||
export function getAgentsConfigDir(): string {
|
||||
return join(homedir(), ".agents")
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, it, expect, beforeAll, afterAll } from "bun:test"
|
||||
import { mkdirSync, writeFileSync, symlinkSync, rmSync } from "fs"
|
||||
import { mkdirSync, writeFileSync, symlinkSync, rmSync, realpathSync } from "fs"
|
||||
import { join } from "path"
|
||||
import { tmpdir } from "os"
|
||||
import { resolveSymlink, resolveSymlinkAsync, isSymbolicLink } from "./file-utils"
|
||||
|
||||
const testDir = join(tmpdir(), "file-utils-test-" + Date.now())
|
||||
const testDir = join(realpathSync(tmpdir()), "file-utils-test-" + Date.now())
|
||||
|
||||
// Create a directory structure that mimics the real-world scenario:
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user