Files
Kilo-Org_kilocode/packages/opencode/test/kilocode/paths.test.ts
2026-04-14 03:06:12 -03:00

267 lines
8.0 KiB
TypeScript

import { test, expect, describe } from "bun:test"
import { KilocodePaths } from "../../src/kilocode/paths"
import { tmpdir } from "../fixture/fixture"
import path from "path"
import fs from "fs/promises"
async function withHome<T>(home: string, fn: () => Promise<T>): Promise<T> {
const prev = process.env.HOME
process.env.HOME = home
try {
return await fn()
} finally {
if (prev) process.env.HOME = prev
else delete process.env.HOME
}
}
describe("KilocodePaths", () => {
describe("skillDirectories", () => {
test("discovers skills from .kilo/skills/", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
const skillDir = path.join(dir, ".kilo", "skills", "test-skill")
await fs.mkdir(skillDir, { recursive: true })
await Bun.write(
path.join(skillDir, "SKILL.md"),
`---
name: test-skill
description: A test skill
---
# Test instructions`,
)
},
})
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(1)
expect(result[0]).toEndWith(".kilo")
})
test("returns empty array when no .kilo/skills/ exists", async () => {
await using tmp = await tmpdir()
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(0)
})
test("discovers skills from nested .kilo directories", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
// Root level skill
const rootSkillDir = path.join(dir, ".kilo", "skills", "root-skill")
await fs.mkdir(rootSkillDir, { recursive: true })
await Bun.write(
path.join(rootSkillDir, "SKILL.md"),
`---
name: root-skill
description: Root level skill
---
# Root instructions`,
)
// Nested project skill
const nestedDir = path.join(dir, "packages", "nested")
const nestedSkillDir = path.join(nestedDir, ".kilo", "skills", "nested-skill")
await fs.mkdir(nestedSkillDir, { recursive: true })
await Bun.write(
path.join(nestedSkillDir, "SKILL.md"),
`---
name: nested-skill
description: Nested skill
---
# Nested instructions`,
)
},
})
// Run from nested directory, should find both
const nestedPath = path.join(tmp.path, "packages", "nested")
const result = await KilocodePaths.skillDirectories({
projectDir: nestedPath,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(2)
const nested = path.join("packages", "nested")
expect(result.some((d) => d.includes(nested))).toBe(true)
expect(result.some((d) => !d.includes(nested))).toBe(true)
})
test("handles .kilo directory without skills subdirectory", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
// Create .kilo but not skills/
await fs.mkdir(path.join(dir, ".kilo"), { recursive: true })
await Bun.write(path.join(dir, ".kilo", "config.json"), "{}")
},
})
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(0)
})
test("handles symlinked skill directories", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
// Create actual skill in a different location
const actualDir = path.join(dir, "shared-skills", "my-skill")
await fs.mkdir(actualDir, { recursive: true })
await Bun.write(
path.join(actualDir, "SKILL.md"),
`---
name: my-skill
description: Symlinked skill
---
# Instructions`,
)
// Create .kilo/skills/ and symlink the skill
const skillsDir = path.join(dir, ".kilo", "skills")
await fs.mkdir(skillsDir, { recursive: true })
await fs.symlink(actualDir, path.join(skillsDir, "my-skill"))
},
})
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(1)
expect(result[0]).toEndWith(".kilo")
})
test("discovers skills from legacy .kilocode/skills/", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
const skillDir = path.join(dir, ".kilocode", "skills", "legacy-skill")
await fs.mkdir(skillDir, { recursive: true })
await Bun.write(
path.join(skillDir, "SKILL.md"),
`---
name: legacy-skill
description: A legacy skill
---
# Legacy instructions`,
)
},
})
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(1)
expect(result[0]).toEndWith(".kilocode")
})
test("returns legacy skill dirs before .kilo so .kilo skills win", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
// .kilo skill
const kiloSkillDir = path.join(dir, ".kilo", "skills", "new-skill")
await fs.mkdir(kiloSkillDir, { recursive: true })
await Bun.write(path.join(kiloSkillDir, "SKILL.md"), "# New skill")
// .kilocode skill
const legacySkillDir = path.join(dir, ".kilocode", "skills", "old-skill")
await fs.mkdir(legacySkillDir, { recursive: true })
await Bun.write(path.join(legacySkillDir, "SKILL.md"), "# Old skill")
},
})
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
expect(result).toHaveLength(2)
expect(result[0]).toEndWith(".kilocode")
expect(result[1]).toEndWith(".kilo")
})
test("discovers global skills from ~/.kilo/skills/", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
const skillDir = path.join(dir, ".kilo", "skills", "global-skill")
await fs.mkdir(skillDir, { recursive: true })
await Bun.write(path.join(skillDir, "SKILL.md"), "# Global skill")
await fs.mkdir(path.join(dir, "repo"), { recursive: true })
},
})
const result = await withHome(tmp.path, () =>
KilocodePaths.skillDirectories({
projectDir: path.join(tmp.path, "repo"),
worktreeRoot: path.join(tmp.path, "repo"),
}),
)
expect(result.some((d) => d.endsWith(".kilo"))).toBe(true)
})
test("discovers multiple skills in same directory", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
const skillsDir = path.join(dir, ".kilo", "skills")
// First skill
const skill1 = path.join(skillsDir, "skill-one")
await fs.mkdir(skill1, { recursive: true })
await Bun.write(
path.join(skill1, "SKILL.md"),
`---
name: skill-one
description: First skill
---
# First`,
)
// Second skill
const skill2 = path.join(skillsDir, "skill-two")
await fs.mkdir(skill2, { recursive: true })
await Bun.write(
path.join(skill2, "SKILL.md"),
`---
name: skill-two
description: Second skill
---
# Second`,
)
},
})
const result = await KilocodePaths.skillDirectories({
projectDir: tmp.path,
worktreeRoot: tmp.path,
skipGlobalPaths: true,
})
// Should return the .kilo directory (not skills/ subdirectory)
expect(result).toHaveLength(1)
expect(result[0]).toEndWith(".kilo")
})
})
})