

























11/** Reads Codex/Claude/Cursor bundle manifests into OpenClaw plugin manifest metadata. */
2-import fs from "node:fs";
32import path from "node:path";
43import {
54normalizeLowercaseStringOrEmpty,
@@ -17,6 +16,7 @@ import {
1716normalizeManifestActivation,
1817PLUGIN_MANIFEST_FILENAME,
1918} from "./manifest.js";
19+import { pluginScanExistsSync } from "./plugin-scan-existence-cache.js";
20202121/** Relative manifest path for Codex-style plugin bundles. */
2222export const CODEX_BUNDLE_MANIFEST_RELATIVE_PATH = ".codex-plugin/plugin.json";
@@ -139,26 +139,26 @@ function resolveCodexSkillDirs(raw: Record<string, unknown>, rootDir: string): s
139139if (declared.length > 0) {
140140return declared;
141141}
142-return fs.existsSync(path.join(rootDir, "skills")) ? ["skills"] : [];
142+return pluginScanExistsSync(path.join(rootDir, "skills")) ? ["skills"] : [];
143143}
144144145145function resolveCodexHookDirs(raw: Record<string, unknown>, rootDir: string): string[] {
146146const declared = normalizeBundlePathList(raw.hooks);
147147if (declared.length > 0) {
148148return declared;
149149}
150-return fs.existsSync(path.join(rootDir, "hooks")) ? ["hooks"] : [];
150+return pluginScanExistsSync(path.join(rootDir, "hooks")) ? ["hooks"] : [];
151151}
152152153153function resolveCursorSkillsRootDirs(raw: Record<string, unknown>, rootDir: string): string[] {
154154const declared = normalizeBundlePathList(raw.skills);
155-const defaults = fs.existsSync(path.join(rootDir, "skills")) ? ["skills"] : [];
155+const defaults = pluginScanExistsSync(path.join(rootDir, "skills")) ? ["skills"] : [];
156156return mergeBundlePathLists(defaults, declared);
157157}
158158159159function resolveCursorCommandRootDirs(raw: Record<string, unknown>, rootDir: string): string[] {
160160const declared = normalizeBundlePathList(raw.commands);
161-const defaults = fs.existsSync(path.join(rootDir, ".cursor", "commands"))
161+const defaults = pluginScanExistsSync(path.join(rootDir, ".cursor", "commands"))
162162 ? [".cursor/commands"]
163163 : [];
164164return mergeBundlePathLists(defaults, declared);
@@ -173,25 +173,31 @@ function resolveCursorSkillDirs(raw: Record<string, unknown>, rootDir: string):
173173174174function resolveCursorAgentDirs(raw: Record<string, unknown>, rootDir: string): string[] {
175175const declared = normalizeBundlePathList(raw.subagents ?? raw.agents);
176-const defaults = fs.existsSync(path.join(rootDir, ".cursor", "agents")) ? [".cursor/agents"] : [];
176+const defaults = pluginScanExistsSync(path.join(rootDir, ".cursor", "agents"))
177+ ? [".cursor/agents"]
178+ : [];
177179return mergeBundlePathLists(defaults, declared);
178180}
179181180182function hasCursorHookCapability(raw: Record<string, unknown>, rootDir: string): boolean {
181183return (
182184hasInlineCapabilityValue(raw.hooks) ||
183-fs.existsSync(path.join(rootDir, ".cursor", "hooks.json"))
185+pluginScanExistsSync(path.join(rootDir, ".cursor", "hooks.json"))
184186);
185187}
186188187189function hasCursorRulesCapability(raw: Record<string, unknown>, rootDir: string): boolean {
188190return (
189-hasInlineCapabilityValue(raw.rules) || fs.existsSync(path.join(rootDir, ".cursor", "rules"))
191+hasInlineCapabilityValue(raw.rules) ||
192+pluginScanExistsSync(path.join(rootDir, ".cursor", "rules"))
190193);
191194}
192195193196function hasCursorMcpCapability(raw: Record<string, unknown>, rootDir: string): boolean {
194-return hasInlineCapabilityValue(raw.mcpServers) || fs.existsSync(path.join(rootDir, ".mcp.json"));
197+return (
198+hasInlineCapabilityValue(raw.mcpServers) ||
199+pluginScanExistsSync(path.join(rootDir, ".mcp.json"))
200+);
195201}
196202197203function resolveClaudeComponentPaths(
@@ -202,7 +208,7 @@ function resolveClaudeComponentPaths(
202208): string[] {
203209const declared = normalizeBundlePathList(raw[key]);
204210const existingDefaults = defaults.filter((candidate) =>
205-fs.existsSync(path.join(rootDir, candidate)),
211+pluginScanExistsSync(path.join(rootDir, candidate)),
206212);
207213return mergeBundlePathLists(existingDefaults, declared);
208214}
@@ -245,7 +251,7 @@ function resolveClaudeOutputStylePaths(raw: Record<string, unknown>, rootDir: st
245251}
246252247253function resolveClaudeSettingsFiles(_raw: Record<string, unknown>, rootDir: string): string[] {
248-return fs.existsSync(path.join(rootDir, "settings.json")) ? ["settings.json"] : [];
254+return pluginScanExistsSync(path.join(rootDir, "settings.json")) ? ["settings.json"] : [];
249255}
250256251257function hasClaudeHookCapability(raw: Record<string, unknown>, rootDir: string): boolean {
@@ -260,10 +266,13 @@ function buildCodexCapabilities(raw: Record<string, unknown>, rootDir: string):
260266if (resolveCodexHookDirs(raw, rootDir).length > 0) {
261267capabilities.push("hooks");
262268}
263-if (hasInlineCapabilityValue(raw.mcpServers) || fs.existsSync(path.join(rootDir, ".mcp.json"))) {
269+if (
270+hasInlineCapabilityValue(raw.mcpServers) ||
271+pluginScanExistsSync(path.join(rootDir, ".mcp.json"))
272+) {
264273capabilities.push("mcpServers");
265274}
266-if (hasInlineCapabilityValue(raw.apps) || fs.existsSync(path.join(rootDir, ".app.json"))) {
275+if (hasInlineCapabilityValue(raw.apps) || pluginScanExistsSync(path.join(rootDir, ".app.json"))) {
267276capabilities.push("apps");
268277}
269278return capabilities;
@@ -416,21 +425,21 @@ export function loadBundleManifest(params: {
416425}
417426418427export function detectBundleManifestFormat(rootDir: string): PluginBundleFormat | null {
419-if (fs.existsSync(path.join(rootDir, CODEX_BUNDLE_MANIFEST_RELATIVE_PATH))) {
428+if (pluginScanExistsSync(path.join(rootDir, CODEX_BUNDLE_MANIFEST_RELATIVE_PATH))) {
420429return "codex";
421430}
422-if (fs.existsSync(path.join(rootDir, CURSOR_BUNDLE_MANIFEST_RELATIVE_PATH))) {
431+if (pluginScanExistsSync(path.join(rootDir, CURSOR_BUNDLE_MANIFEST_RELATIVE_PATH))) {
423432return "cursor";
424433}
425-if (fs.existsSync(path.join(rootDir, CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH))) {
434+if (pluginScanExistsSync(path.join(rootDir, CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH))) {
426435return "claude";
427436}
428-if (fs.existsSync(path.join(rootDir, PLUGIN_MANIFEST_FILENAME))) {
437+if (pluginScanExistsSync(path.join(rootDir, PLUGIN_MANIFEST_FILENAME))) {
429438return null;
430439}
431440if (
432441DEFAULT_PLUGIN_ENTRY_CANDIDATES.some((candidate) =>
433-fs.existsSync(path.join(rootDir, candidate)),
442+pluginScanExistsSync(path.join(rootDir, candidate)),
434443)
435444) {
436445return null;
@@ -444,7 +453,7 @@ export function detectBundleManifestFormat(rootDir: string): PluginBundleFormat
444453path.join(rootDir, ".lsp.json"),
445454path.join(rootDir, "settings.json"),
446455];
447-if (manifestlessClaudeMarkers.some((candidate) => fs.existsSync(candidate))) {
456+if (manifestlessClaudeMarkers.some((candidate) => pluginScanExistsSync(candidate))) {
448457return "claude";
449458}
450459return null;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。