From 87da6d373f6bf068dffbb4b0b7111d8f39031087 Mon Sep 17 00:00:00 2001 From: Ogrodev Date: Sun, 14 Jun 2026 20:49:05 -0300 Subject: [PATCH] fix(coding-agent): scope managed skills to profiles --- packages/coding-agent/CHANGELOG.md | 1 + packages/coding-agent/src/autolearn/managed-skills.ts | 8 +++----- packages/coding-agent/src/discovery/builtin.ts | 2 +- packages/coding-agent/test/autolearn-discovery.test.ts | 5 +++++ .../coding-agent/test/autolearn-managed-skills.test.ts | 5 +++++ .../coding-agent/test/autolearn-tools-gating.test.ts | 9 +++++++++ 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 8c23542c7..4cd1a2cf0 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -19,6 +19,7 @@ - Fixed a crash in subagent task execution and extensions when a string (instead of a string array) was returned or set for the system prompt. Gracefully wrap string values in arrays. - Fixed profile bootstrap so an extension-shadowed `--plan` flag no longer swallows a following global `--profile`. - Fixed MCP OAuth URL-keyed credentials to stay profile-scoped under shared auth-broker storage and to clear discovered definition-only server auth during `/mcp unauth`. +- Fixed auto-learn managed skills to use the active profile's agent directory, so authored profile skills keep priority over managed fallbacks. ## [15.13.0] - 2026-06-14 diff --git a/packages/coding-agent/src/autolearn/managed-skills.ts b/packages/coding-agent/src/autolearn/managed-skills.ts index ecfc176c8..fa190a3be 100644 --- a/packages/coding-agent/src/autolearn/managed-skills.ts +++ b/packages/coding-agent/src/autolearn/managed-skills.ts @@ -9,11 +9,9 @@ */ import { constants as fsConstants, type Stats } from "node:fs"; import * as fs from "node:fs/promises"; -import * as os from "node:os"; import * as path from "node:path"; -import { isEnoent } from "@oh-my-pi/pi-utils"; +import { getAgentDir, isEnoent } from "@oh-my-pi/pi-utils"; import { YAML } from "bun"; -import { SOURCE_PATHS } from "../discovery/helpers"; /** Provider id stamped on discovered managed skills (distinguishes them from authored). */ export const MANAGED_SKILLS_PROVIDER_ID = "omp-managed"; @@ -24,8 +22,8 @@ export const MAX_MANAGED_SKILL_BYTES = 64_000; const SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/; /** Resolve the isolated managed-skills directory (`~/.omp/agent/managed-skills`). */ -export function getManagedSkillsDir(home: string = os.homedir()): string { - return path.join(home, SOURCE_PATHS.native.userAgent, "managed-skills"); +export function getManagedSkillsDir(agentDir: string = getAgentDir()): string { + return path.join(agentDir, "managed-skills"); } /** diff --git a/packages/coding-agent/src/discovery/builtin.ts b/packages/coding-agent/src/discovery/builtin.ts index 38bf96b93..94a17e2bc 100644 --- a/packages/coding-agent/src/discovery/builtin.ts +++ b/packages/coding-agent/src/discovery/builtin.ts @@ -303,7 +303,7 @@ async function loadSkills(ctx: LoadContext): Promise> { const MANAGED_SKILLS_PRIORITY = 5; async function loadManagedSkills(ctx: LoadContext): Promise> { return scanSkillsFromDir(ctx, { - dir: getManagedSkillsDir(ctx.home), + dir: getManagedSkillsDir(), providerId: MANAGED_SKILLS_PROVIDER_ID, level: "user", requireDescription: true, diff --git a/packages/coding-agent/test/autolearn-discovery.test.ts b/packages/coding-agent/test/autolearn-discovery.test.ts index caf6ce952..b1541a31f 100644 --- a/packages/coding-agent/test/autolearn-discovery.test.ts +++ b/packages/coding-agent/test/autolearn-discovery.test.ts @@ -5,6 +5,7 @@ import * as path from "node:path"; import { getManagedSkillsDir } from "@oh-my-pi/pi-coding-agent/autolearn/managed-skills"; import "@oh-my-pi/pi-coding-agent/discovery"; import { loadSkills } from "@oh-my-pi/pi-coding-agent/extensibility/skills"; +import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs"; async function writeSkill(dir: string, name: string, description: string): Promise { const file = path.join(dir, name, "SKILL.md"); @@ -18,13 +19,16 @@ describe("managed-skills discovery", () => { let managedDir: string; let authoredDir: string; + let originalAgentDir: string; beforeEach(async () => { + originalAgentDir = getAgentDir(); tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "omp-managed-disco-home-")); // cwd MUST live under the fake home so loadSkills' ancestor walk is bounded // and cannot pick up ambient /tmp/.omp or /.omp fixtures (full-suite-safe). tempCwd = path.join(tempHome, "work"); await fs.mkdir(tempCwd, { recursive: true }); spyOn(os, "homedir").mockReturnValue(tempHome); + setAgentDir(path.join(tempHome, ".omp", "agent")); managedDir = getManagedSkillsDir(); // Authored user skills live in the sibling `skills/` dir under .../agent. authoredDir = path.join(path.dirname(managedDir), "skills"); @@ -32,6 +36,7 @@ describe("managed-skills discovery", () => { afterEach(async () => { spyOn(os, "homedir").mockRestore(); + setAgentDir(originalAgentDir); await fs.rm(tempHome, { recursive: true, force: true }); }); diff --git a/packages/coding-agent/test/autolearn-managed-skills.test.ts b/packages/coding-agent/test/autolearn-managed-skills.test.ts index 292051780..7ce8453e4 100644 --- a/packages/coding-agent/test/autolearn-managed-skills.test.ts +++ b/packages/coding-agent/test/autolearn-managed-skills.test.ts @@ -11,17 +11,22 @@ import { writeManagedSkill, } from "@oh-my-pi/pi-coding-agent/autolearn/managed-skills"; import { parseFrontmatter } from "@oh-my-pi/pi-utils"; +import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs"; describe("managed-skills primitives", () => { let tempHome: string; + let originalAgentDir: string; beforeEach(async () => { + originalAgentDir = getAgentDir(); tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "omp-managed-skills-")); spyOn(os, "homedir").mockReturnValue(tempHome); + setAgentDir(path.join(tempHome, ".omp", "agent")); }); afterEach(async () => { spyOn(os, "homedir").mockRestore(); + setAgentDir(originalAgentDir); await fs.rm(tempHome, { recursive: true, force: true }); }); diff --git a/packages/coding-agent/test/autolearn-tools-gating.test.ts b/packages/coding-agent/test/autolearn-tools-gating.test.ts index e7adfc55a..04c4725b5 100644 --- a/packages/coding-agent/test/autolearn-tools-gating.test.ts +++ b/packages/coding-agent/test/autolearn-tools-gating.test.ts @@ -10,6 +10,7 @@ import type { MnemopiSessionState } from "@oh-my-pi/pi-coding-agent/mnemopi/stat import { createTools, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools"; import { LearnTool } from "@oh-my-pi/pi-coding-agent/tools/learn"; import { ManageSkillTool } from "@oh-my-pi/pi-coding-agent/tools/manage-skill"; +import { getAgentDir, setAgentDir } from "@oh-my-pi/pi-utils/dirs"; function makeSession( settingsOverrides: Partial> = {}, @@ -104,14 +105,18 @@ describe("autolearn tool gating", () => { describe("manage_skill execute", () => { let tempHome: string; + let originalAgentDir: string; beforeEach(async () => { + originalAgentDir = getAgentDir(); tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "omp-manage-skill-")); spyOn(os, "homedir").mockReturnValue(tempHome); + setAgentDir(path.join(tempHome, ".omp", "agent")); }); afterEach(async () => { spyOn(os, "homedir").mockRestore(); + setAgentDir(originalAgentDir); resetActiveSkillsForTests(); await fs.rm(tempHome, { recursive: true, force: true }); }); @@ -178,6 +183,7 @@ describe("manage_skill execute", () => { describe("learn execute", () => { let tempHome: string; let remembered: string[]; + let originalAgentDir: string; function learnSession(): ToolSession { const fakeState = { @@ -195,13 +201,16 @@ describe("learn execute", () => { } beforeEach(async () => { + originalAgentDir = getAgentDir(); tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "omp-learn-")); spyOn(os, "homedir").mockReturnValue(tempHome); + setAgentDir(path.join(tempHome, ".omp", "agent")); remembered = []; }); afterEach(async () => { spyOn(os, "homedir").mockRestore(); + setAgentDir(originalAgentDir); await fs.rm(tempHome, { recursive: true, force: true }); });