diff --git a/.changeset/fix-worktree-project-skills.md b/.changeset/fix-worktree-project-skills.md new file mode 100644 index 00000000000..1cdec650cef --- /dev/null +++ b/.changeset/fix-worktree-project-skills.md @@ -0,0 +1,5 @@ +--- +"@kilocode/cli": patch +--- + +Discover project-installed skills in Agent Manager worktree sessions. diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 4c695b1eac4..f6851e4a064 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -765,7 +765,7 @@ export const layer = Layer.effect( if (!Flag.KILO_DISABLE_PROJECT_CONFIG) { // kilocode_change start - also discover kilo.json project files for (const name of ["kilo", "opencode"] as const) { - for (const file of yield* ConfigPaths.files(name, ctx.directory, ctx.worktree).pipe(Effect.orDie)) { + for (const file of yield* ConfigPaths.files(name, ctx.directory, ctx.project.worktree).pipe(Effect.orDie)) { yield* merge( file, yield* loadFile(file).pipe( @@ -785,7 +785,7 @@ export const layer = Layer.effect( result.mode = result.mode || {} result.plugin = result.plugin || [] - const directories = yield* ConfigPaths.directories(ctx.directory, ctx.worktree) + const directories = yield* ConfigPaths.directories(ctx.directory, ctx.project.worktree) // kilocode_change if (Flag.KILO_CONFIG_DIR) { log.debug("loading config from KILO_CONFIG_DIR", { path: Flag.KILO_CONFIG_DIR }) diff --git a/packages/opencode/src/skill/index.ts b/packages/opencode/src/skill/index.ts index a1c1517e7c4..ee46f65ca86 100644 --- a/packages/opencode/src/skill/index.ts +++ b/packages/opencode/src/skill/index.ts @@ -250,7 +250,7 @@ export const layer = Layer.effect( const global = yield* Global.Service const discovered = yield* InstanceState.make( Effect.fn("Skill.discovery")(function* (ctx) { - return yield* discoverSkills(config, discovery, fsys, global, ctx.directory, ctx.worktree) + return yield* discoverSkills(config, discovery, fsys, global, ctx.directory, ctx.project.worktree) // kilocode_change }), ) const state = yield* InstanceState.make( diff --git a/packages/opencode/test/kilocode/worktree-project-skills.test.ts b/packages/opencode/test/kilocode/worktree-project-skills.test.ts new file mode 100644 index 00000000000..d65041b4f6c --- /dev/null +++ b/packages/opencode/test/kilocode/worktree-project-skills.test.ts @@ -0,0 +1,49 @@ +import { $ } from "bun" +import { afterEach, describe, expect } from "bun:test" +import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" +import { Effect, Layer } from "effect" +import path from "path" +import { Skill } from "../../src/skill" +import { disposeAllInstances, provideInstance, tmpdir } from "../fixture/fixture" +import { testEffect } from "../lib/effect" + +const it = testEffect(Layer.mergeAll(Skill.defaultLayer, CrossSpawnSpawner.defaultLayer)) + +afterEach(() => disposeAllInstances()) + +describe("worktree project skills", () => { + it.live("discovers skills installed in the main repository", () => + Effect.acquireUseRelease( + Effect.promise(() => tmpdir({ git: true })), + (tmp) => + Effect.gen(function* () { + const dir = path.join(tmp.path, ".kilo", "worktrees", "feature") + yield* Effect.promise(() => $`git worktree add -b worktree-project-skills ${dir}`.cwd(tmp.path).quiet()) + yield* Effect.promise(() => + Bun.write( + path.join(tmp.path, ".kilo", "skills", "project-skill", "SKILL.md"), + `--- +name: project-skill +description: A skill installed in the main repository. +--- + +# Project Skill +`, + ), + ) + + const list = yield* provideInstance(dir)( + Effect.gen(function* () { + const skill = yield* Skill.Service + return yield* skill.all() + }), + ) + + expect(list.find((item) => item.name === "project-skill")?.location).toBe( + path.join(tmp.path, ".kilo", "skills", "project-skill", "SKILL.md"), + ) + }), + (tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()), + ), + ) +})