Skip to content

Commit ce66ae3

Browse files
committed
fix(xdg): resolve marketplace plugins path when using xdg spec
1 parent ceea681 commit ce66ae3

3 files changed

Lines changed: 24 additions & 4 deletions

File tree

packages/coding-agent/src/discovery/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as os from "node:os";
33
import * as path from "node:path";
44
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
55
import { FileType, glob } from "@oh-my-pi/pi-natives";
6-
import { CONFIG_DIR_NAME, getConfigDirName, getProjectDir, parseFrontmatter, tryParseJson } from "@oh-my-pi/pi-utils";
6+
import { CONFIG_DIR_NAME, getConfigDirName, getProjectDir, getUserPluginsRegistryDir, parseFrontmatter, tryParseJson } from "@oh-my-pi/pi-utils";
77
import type { ExtensionModule } from "../capability/extension-module";
88
import { invalidate as invalidateFsCache, readDirEntries, readFile } from "../capability/fs";
99
import { parseRuleConditionAndScope, type Rule, type RuleFrontmatter } from "../capability/rule";
@@ -805,7 +805,7 @@ export async function listClaudePluginRoots(
805805
// ── OMP installed plugins registry ───────────────────────────────────────
806806
// OMP registry is authoritative: its entries replace Claude's entries for the same plugin ID.
807807
// Path derived from `home` (not os.homedir()) so test isolation works when home is overridden.
808-
const ompRegistryPath = path.join(home, getConfigDirName(), "plugins", "installed_plugins.json");
808+
const ompRegistryPath = path.join(getUserPluginsRegistryDir(home));
809809
const ompContent = await readFile(ompRegistryPath);
810810
if (ompContent) {
811811
const ompRegistry = parseClaudePluginsRegistry(ompContent);

packages/coding-agent/test/discovery/claude-plugins.test.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { discoverAgents } from "@oh-my-pi/pi-coding-agent/task/discovery";
1313
import "@oh-my-pi/pi-coding-agent/discovery/claude-plugins";
1414
import type { Skill } from "@oh-my-pi/pi-coding-agent/capability/skill";
1515
import type { SlashCommand } from "@oh-my-pi/pi-coding-agent/capability/slash-command";
16-
16+
import { getAgentDir, getConfigDirName, setAgentDir } from "@oh-my-pi/pi-utils";
1717
describe("parseClaudePluginsRegistry", () => {
1818
test("parses valid registry", () => {
1919
const content = JSON.stringify({
@@ -60,25 +60,36 @@ describe("parseClaudePluginsRegistry", () => {
6060
describe("listClaudePluginRoots", () => {
6161
let tempDir: string;
6262
let originalHome: string | undefined;
63-
63+
let originalAgentDir: string;
64+
let originalXdgDataHome: string | undefined;
6465
beforeEach(async () => {
6566
clearClaudePluginRootsCache();
6667
clearFsCache();
6768
originalHome = process.env.HOME;
69+
originalAgentDir = getAgentDir();
70+
originalXdgDataHome = process.env.XDG_DATA_HOME;
71+
delete process.env.XDG_DATA_HOME;
6872
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "claude-plugins-test-"));
6973
process.env.HOME = tempDir;
7074
vi.spyOn(os, "homedir").mockReturnValue(tempDir);
75+
setAgentDir(path.join(tempDir, getConfigDirName(), "agent"));
7176
});
7277

7378
afterEach(async () => {
7479
clearClaudePluginRootsCache();
7580
clearFsCache();
7681
vi.restoreAllMocks();
82+
setAgentDir(originalAgentDir);
7783
if (originalHome === undefined) {
7884
delete process.env.HOME;
7985
} else {
8086
process.env.HOME = originalHome;
8187
}
88+
if (originalXdgDataHome === undefined) {
89+
delete process.env.XDG_DATA_HOME;
90+
} else {
91+
process.env.XDG_DATA_HOME = originalXdgDataHome;
92+
}
8293
await fs.rm(tempDir, { recursive: true, force: true });
8394
});
8495

packages/utils/src/dirs.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,15 @@ export function getPluginsDir(): string {
241241
return dirs.rootSubdir("plugins", "data");
242242
}
243243

244+
/** Get the users plugins directory (~/.omp/plugins or ~/.local/share/omp/plugins). */
245+
export function getUserPluginsRegistryDir(home: string): string {
246+
if (home != os.homedir()) {
247+
// If a custom home directory is provided, assume plugins are stored under it without XDG redirection.
248+
return path.join(home, getConfigDirName(), "plugins", "installed_plugins.json");
249+
}
250+
return path.join(getPluginsDir(), "installed_plugins.json")
251+
}
252+
244253
/** Where npm installs packages (~/.omp/plugins/node_modules). */
245254
export function getPluginsNodeModules(): string {
246255
return path.join(getPluginsDir(), "node_modules");

0 commit comments

Comments
 (0)