@within-7/minto 0.3.9 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agents/AgentsCommand.js +459 -655
- package/dist/commands/agents/AgentsCommand.js.map +2 -2
- package/dist/commands/agents/types.js +1 -0
- package/dist/commands/agents/types.js.map +2 -2
- package/dist/commands/agents/utils/fileOperations.js +96 -36
- package/dist/commands/agents/utils/fileOperations.js.map +3 -3
- package/dist/commands/agents/utils/index.js +3 -1
- package/dist/commands/agents/utils/index.js.map +2 -2
- package/dist/commands/context.js +54 -23
- package/dist/commands/context.js.map +2 -2
- package/dist/commands/export.js +673 -93
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/language.js +19 -46
- package/dist/commands/language.js.map +2 -2
- package/dist/commands/mcp-interactive.js +419 -217
- package/dist/commands/mcp-interactive.js.map +2 -2
- package/dist/commands/model.js +415 -66
- package/dist/commands/model.js.map +2 -2
- package/dist/commands/permissions.js +75 -49
- package/dist/commands/permissions.js.map +2 -2
- package/dist/commands/plugin.js +882 -185
- package/dist/commands/plugin.js.map +3 -3
- package/dist/commands/resume.js +1 -1
- package/dist/commands/resume.js.map +1 -1
- package/dist/commands/sandbox.js +168 -70
- package/dist/commands/sandbox.js.map +2 -2
- package/dist/commands/setup.js +593 -107
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/stats.js +188 -131
- package/dist/commands/stats.js.map +2 -2
- package/dist/commands/status.js +75 -13
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +138 -174
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands.js.map +1 -1
- package/dist/components/Help.js +165 -32
- package/dist/components/Help.js.map +2 -2
- package/dist/components/InfoPanel/InfoPanel.js +123 -0
- package/dist/components/InfoPanel/InfoPanel.js.map +7 -0
- package/dist/components/InfoPanel/index.js +5 -0
- package/dist/components/InfoPanel/index.js.map +7 -0
- package/dist/components/InfoPanel/types.js +1 -0
- package/dist/components/InfoPanel/types.js.map +7 -0
- package/dist/components/ModelSelector/BrandTextInput.js +43 -0
- package/dist/components/ModelSelector/BrandTextInput.js.map +7 -0
- package/dist/components/ModelSelector/ModelSelector.js +419 -501
- package/dist/components/ModelSelector/ModelSelector.js.map +2 -2
- package/dist/components/ModelSelector/WizardContainer.js +45 -0
- package/dist/components/ModelSelector/WizardContainer.js.map +7 -0
- package/dist/components/ModelSelector/index.js +1 -3
- package/dist/components/ModelSelector/index.js.map +2 -2
- package/dist/components/PromptInput.js +5 -5
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SimpleSelector/SimpleSelector.js +154 -0
- package/dist/components/SimpleSelector/SimpleSelector.js.map +7 -0
- package/dist/components/SimpleSelector/index.js +5 -0
- package/dist/components/SimpleSelector/index.js.map +7 -0
- package/dist/components/SimpleSelector/types.js +1 -0
- package/dist/components/SimpleSelector/types.js.map +7 -0
- package/dist/components/StatusOverlayContent.js +21 -0
- package/dist/components/StatusOverlayContent.js.map +7 -0
- package/dist/components/TabbedListView/ScrollableList.js +31 -5
- package/dist/components/TabbedListView/ScrollableList.js.map +2 -2
- package/dist/components/TabbedListView/TabbedListView.js +122 -47
- package/dist/components/TabbedListView/TabbedListView.js.map +2 -2
- package/dist/core/backupHook.js +29 -0
- package/dist/core/backupHook.js.map +7 -0
- package/dist/core/config/defaults.js +8 -2
- package/dist/core/config/defaults.js.map +2 -2
- package/dist/core/config/schema.js +14 -2
- package/dist/core/config/schema.js.map +2 -2
- package/dist/core/costTracker.js +0 -16
- package/dist/core/costTracker.js.map +2 -2
- package/dist/cost-tracker.js +0 -16
- package/dist/cost-tracker.js.map +2 -2
- package/dist/entrypoints/bootstrap.js +3 -1
- package/dist/entrypoints/bootstrap.js.map +2 -2
- package/dist/entrypoints/cli.js +32 -0
- package/dist/entrypoints/cli.js.map +2 -2
- package/dist/i18n/locales/en.js +300 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +301 -2
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/services/customCommands.js +30 -8
- package/dist/services/customCommands.js.map +2 -2
- package/dist/services/plugins/lspServers.js +1 -1
- package/dist/services/plugins/lspServers.js.map +2 -2
- package/dist/services/plugins/pluginRuntime.js +2 -1
- package/dist/services/plugins/pluginRuntime.js.map +2 -2
- package/dist/services/plugins/pluginValidation.js +10 -3
- package/dist/services/plugins/pluginValidation.js.map +2 -2
- package/dist/services/plugins/skillMarketplace.js +16 -8
- package/dist/services/plugins/skillMarketplace.js.map +2 -2
- package/dist/services/systemReminder.js +17 -6
- package/dist/services/systemReminder.js.map +2 -2
- package/dist/tools/FileEditTool/FileEditTool.js +7 -0
- package/dist/tools/FileEditTool/FileEditTool.js.map +2 -2
- package/dist/tools/FileWriteTool/FileWriteTool.js +7 -0
- package/dist/tools/FileWriteTool/FileWriteTool.js.map +2 -2
- package/dist/tools/MultiEditTool/MultiEditTool.js +7 -0
- package/dist/tools/MultiEditTool/MultiEditTool.js.map +2 -2
- package/dist/tools/NotebookEditTool/NotebookEditTool.js +2 -0
- package/dist/tools/NotebookEditTool/NotebookEditTool.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +9 -6
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/types/PermissionMode.js.map +1 -1
- package/dist/types/plugin.js +2 -4
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentHookExecutor.js +1 -4
- package/dist/utils/agentHookExecutor.js.map +2 -2
- package/dist/utils/agentLoader.js +67 -13
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentMemory.js.map +2 -2
- package/dist/utils/claudeCodeSync.js +439 -0
- package/dist/utils/claudeCodeSync.js.map +7 -0
- package/dist/utils/config.js +1 -23
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/execFileNoThrow.js +2 -1
- package/dist/utils/execFileNoThrow.js.map +2 -2
- package/dist/utils/marketplaceManager.js +80 -43
- package/dist/utils/marketplaceManager.js.map +2 -2
- package/dist/utils/messages.js +2 -2
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/pluginInstaller.js +34 -24
- package/dist/utils/pluginInstaller.js.map +2 -2
- package/dist/utils/pluginLoader.js +48 -25
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/repoFetcher.js +110 -0
- package/dist/utils/repoFetcher.js.map +7 -0
- package/dist/utils/skillLoader.js +18 -6
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/stringSubstitution.js +4 -5
- package/dist/utils/stringSubstitution.js.map +2 -2
- package/dist/utils/teamConfig.js +153 -13
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/terminal.js +1 -1
- package/dist/utils/terminal.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +6 -6
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import { join, resolve } from "path";
|
|
11
11
|
import { homedir } from "os";
|
|
12
12
|
import { EventEmitter } from "events";
|
|
13
|
-
import {
|
|
13
|
+
import { fetchRepo } from "./repoFetcher.js";
|
|
14
14
|
import { getCwd } from "./state.js";
|
|
15
15
|
import {
|
|
16
16
|
PluginManifestSchema,
|
|
@@ -111,9 +111,9 @@ class PluginInstaller extends EventEmitter {
|
|
|
111
111
|
this.saveRegistry(filtered, global);
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* Fetch a git repository to a temporary directory using repoFetcher
|
|
115
115
|
*/
|
|
116
|
-
async
|
|
116
|
+
async fetchToTempDir(source) {
|
|
117
117
|
const tempDir = join(
|
|
118
118
|
homedir(),
|
|
119
119
|
".minto",
|
|
@@ -123,21 +123,13 @@ class PluginInstaller extends EventEmitter {
|
|
|
123
123
|
);
|
|
124
124
|
this.tempDirs.push(tempDir);
|
|
125
125
|
mkdirSync(tempDir, { recursive: true });
|
|
126
|
+
this.emitProgress("cloning" /* CLONING */, `Fetching repository...`, 20);
|
|
126
127
|
try {
|
|
127
|
-
|
|
128
|
-
if (ref) {
|
|
129
|
-
args.push("--branch", ref);
|
|
130
|
-
}
|
|
131
|
-
args.push(url, tempDir);
|
|
132
|
-
this.emitProgress("cloning" /* CLONING */, `Cloning repository: ${url}`, 20);
|
|
133
|
-
const result = await execFileNoThrow("git", args);
|
|
134
|
-
if (result.code !== 0) {
|
|
135
|
-
throw new Error(`Git clone failed: ${result.stderr || result.stdout}`);
|
|
136
|
-
}
|
|
128
|
+
await fetchRepo(source, tempDir);
|
|
137
129
|
return tempDir;
|
|
138
130
|
} catch (error) {
|
|
139
131
|
throw new PluginError(
|
|
140
|
-
`Failed to
|
|
132
|
+
`Failed to fetch repository: ${error instanceof Error ? error.message : String(error)}`,
|
|
141
133
|
PluginErrorCode.PERMISSION_DENIED,
|
|
142
134
|
void 0,
|
|
143
135
|
error
|
|
@@ -281,9 +273,25 @@ ${missing.join("\n")}`);
|
|
|
281
273
|
0
|
|
282
274
|
);
|
|
283
275
|
switch (source.type) {
|
|
284
|
-
case "git":
|
|
285
|
-
|
|
276
|
+
case "git": {
|
|
277
|
+
const ghMatch = source.repo.match(
|
|
278
|
+
/github\.com[/:]([^/]+\/[^/.]+?)(?:\.git)?$/
|
|
279
|
+
);
|
|
280
|
+
if (ghMatch) {
|
|
281
|
+
sourceDir = await this.fetchToTempDir({
|
|
282
|
+
type: "github",
|
|
283
|
+
repo: ghMatch[1],
|
|
284
|
+
ref: source.ref
|
|
285
|
+
});
|
|
286
|
+
} else {
|
|
287
|
+
sourceDir = await this.fetchToTempDir({
|
|
288
|
+
type: "url",
|
|
289
|
+
url: source.repo,
|
|
290
|
+
ref: source.ref
|
|
291
|
+
});
|
|
292
|
+
}
|
|
286
293
|
break;
|
|
294
|
+
}
|
|
287
295
|
case "local":
|
|
288
296
|
sourceDir = resolve(source.path);
|
|
289
297
|
if (!existsSync(sourceDir)) {
|
|
@@ -313,15 +321,17 @@ ${missing.join("\n")}`);
|
|
|
313
321
|
PluginErrorCode.MANIFEST_NOT_FOUND
|
|
314
322
|
);
|
|
315
323
|
} else if (plugin.source.source === "github") {
|
|
316
|
-
sourceDir = await this.
|
|
317
|
-
|
|
318
|
-
plugin.source.
|
|
319
|
-
|
|
324
|
+
sourceDir = await this.fetchToTempDir({
|
|
325
|
+
type: "github",
|
|
326
|
+
repo: plugin.source.repo,
|
|
327
|
+
ref: plugin.source.ref
|
|
328
|
+
});
|
|
320
329
|
} else if (plugin.source.source === "url") {
|
|
321
|
-
sourceDir = await this.
|
|
322
|
-
|
|
323
|
-
plugin.source.
|
|
324
|
-
|
|
330
|
+
sourceDir = await this.fetchToTempDir({
|
|
331
|
+
type: "url",
|
|
332
|
+
url: plugin.source.url,
|
|
333
|
+
ref: plugin.source.ref
|
|
334
|
+
});
|
|
325
335
|
} else {
|
|
326
336
|
throw new PluginError(
|
|
327
337
|
"Unsupported marketplace plugin source type",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/pluginInstaller.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Plugin Installer\n *\n * Comprehensive plugin installation utility with proper error handling,\n * progress tracking, and cleanup on failure.\n *\n * Supports installation from:\n * - Git repositories (GitHub shorthand, full URLs)\n * - Local directories (copy or symlink)\n * - NPM packages (future)\n * - Marketplaces (via marketplaceManager)\n */\n\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n rmSync,\n cpSync,\n symlinkSync,\n statSync,\n readdirSync,\n} from 'fs'\nimport { join, resolve, basename } from 'path'\nimport { homedir } from 'os'\nimport { EventEmitter } from 'events'\nimport { execFileNoThrow } from './execFileNoThrow'\nimport { getCwd } from './state'\nimport {\n PluginManifest,\n PluginManifestSchema,\n PluginSource,\n PluginError,\n PluginErrorCode,\n} from '../types/plugin'\nimport { findPlugin } from './marketplaceManager'\n\n/**\n * Installation options\n */\nexport interface InstallOptions {\n /** Target directory (default: ~/.minto/plugins/<name> or ./.minto/plugins/<name>) */\n targetDir?: string\n\n /** Overwrite existing installation */\n force?: boolean\n\n /** Create symlink instead of copy (for local development) */\n dev?: boolean\n\n /** Install globally (~/minto/plugins/) vs project-level (./.minto/plugins/) */\n global?: boolean\n\n /** Silent mode (no progress events) */\n silent?: boolean\n\n /** Validation mode: strict (require all components) or loose */\n validation?: 'strict' | 'loose'\n}\n\n/**\n * Installation progress phases\n */\nexport enum InstallPhase {\n VALIDATING_SOURCE = 'validating_source',\n CLONING = 'cloning',\n COPYING = 'copying',\n VALIDATING_MANIFEST = 'validating_manifest',\n CHECKING_DEPENDENCIES = 'checking_dependencies',\n INSTALLING = 'installing',\n REGISTERING = 'registering',\n CLEANUP = 'cleanup',\n COMPLETE = 'complete',\n}\n\n/**\n * Installation progress event\n */\nexport interface InstallProgress {\n phase: InstallPhase\n message: string\n percentage: number\n pluginName?: string\n}\n\n/**\n * Plugin registry entry\n */\ninterface PluginRegistryEntry {\n name: string\n version: string\n source: PluginSource\n installedAt: string\n location: string\n}\n\n/**\n * Plugin installer with event-based progress tracking\n */\nexport class PluginInstaller extends EventEmitter {\n private tempDirs: string[] = []\n\n /**\n * Emit progress event\n */\n private emitProgress(\n phase: InstallPhase,\n message: string,\n percentage: number,\n pluginName?: string,\n ): void {\n this.emit('progress', {\n phase,\n message,\n percentage,\n pluginName,\n } as InstallProgress)\n }\n\n /**\n * Get plugin installation directory\n */\n private getInstallDir(pluginName: string, options?: InstallOptions): string {\n if (options?.targetDir) {\n return resolve(options.targetDir)\n }\n\n const baseDir = options?.global ? homedir() : getCwd()\n return join(baseDir, '.minto', 'plugins', pluginName)\n }\n\n /**\n * Get plugin registry path\n */\n private getRegistryPath(global: boolean): string {\n const baseDir = global ? homedir() : getCwd()\n return join(baseDir, '.minto', 'plugins', 'registry.json')\n }\n\n /**\n * Load plugin registry\n */\n private loadRegistry(global: boolean): PluginRegistryEntry[] {\n const registryPath = this.getRegistryPath(global)\n\n if (!existsSync(registryPath)) {\n return []\n }\n\n try {\n const content = readFileSync(registryPath, 'utf-8')\n return JSON.parse(content)\n } catch (error) {\n console.error('Error loading plugin registry:', error)\n return []\n }\n }\n\n /**\n * Save plugin registry\n */\n private saveRegistry(entries: PluginRegistryEntry[], global: boolean): void {\n const registryPath = this.getRegistryPath(global)\n const registryDir = join(registryPath, '..')\n\n // Ensure directory exists\n if (!existsSync(registryDir)) {\n mkdirSync(registryDir, { recursive: true })\n }\n\n writeFileSync(registryPath, JSON.stringify(entries, null, 2), 'utf-8')\n }\n\n /**\n * Register plugin in registry\n */\n private registerPlugin(\n manifest: PluginManifest,\n source: PluginSource,\n location: string,\n global: boolean,\n ): void {\n const registry = this.loadRegistry(global)\n\n // Remove existing entry if present\n const filtered = registry.filter(e => e.name !== manifest.name)\n\n // Add new entry\n filtered.push({\n name: manifest.name,\n version: manifest.version,\n source,\n installedAt: new Date().toISOString(),\n location,\n })\n\n this.saveRegistry(filtered, global)\n }\n\n /**\n * Unregister plugin from registry\n */\n private unregisterPlugin(pluginName: string, global: boolean): void {\n const registry = this.loadRegistry(global)\n const filtered = registry.filter(e => e.name !== pluginName)\n this.saveRegistry(filtered, global)\n }\n\n /**\n * Clone git repository to temporary directory\n */\n private async cloneGitRepo(url: string, ref?: string): Promise<string> {\n const tempDir = join(\n homedir(),\n '.minto',\n 'temp',\n 'plugins',\n Date.now().toString(),\n )\n\n this.tempDirs.push(tempDir)\n mkdirSync(tempDir, { recursive: true })\n\n try {\n // Build git clone arguments\n const args = ['clone', '--depth', '1']\n\n if (ref) {\n args.push('--branch', ref)\n }\n\n args.push(url, tempDir)\n\n this.emitProgress(InstallPhase.CLONING, `Cloning repository: ${url}`, 20)\n\n // Clone repository\n const result = await execFileNoThrow('git', args)\n\n if (result.code !== 0) {\n throw new Error(`Git clone failed: ${result.stderr || result.stdout}`)\n }\n\n return tempDir\n } catch (error) {\n throw new PluginError(\n `Failed to clone repository: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n undefined,\n error,\n )\n }\n }\n\n /**\n * Validate plugin manifest exists and is valid\n */\n private validateManifest(pluginPath: string): PluginManifest {\n const manifestPath = join(pluginPath, 'plugin.json')\n\n if (!existsSync(manifestPath)) {\n throw new PluginError(\n `Plugin manifest (plugin.json) not found in ${pluginPath}`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n try {\n const content = readFileSync(manifestPath, 'utf-8')\n const data = JSON.parse(content)\n\n // Validate with Zod schema\n return PluginManifestSchema.parse(data)\n } catch (error) {\n throw new PluginError(\n `Invalid plugin manifest: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.MANIFEST_INVALID,\n undefined,\n error,\n )\n }\n }\n\n /**\n * Check if plugin is already installed\n */\n private isPluginInstalled(pluginName: string, global: boolean): boolean {\n const registry = this.loadRegistry(global)\n return registry.some(e => e.name === pluginName)\n }\n\n /**\n * Validate plugin components exist\n */\n private validateComponents(\n pluginPath: string,\n manifest: PluginManifest,\n strict: boolean,\n ): void {\n const missing: string[] = []\n\n // Check agents\n for (const agentPath of manifest.agents || []) {\n const fullPath = join(pluginPath, agentPath)\n if (!existsSync(fullPath)) {\n missing.push(`Agent: ${agentPath}`)\n }\n }\n\n // Check commands\n for (const commandPath of manifest.commands || []) {\n const fullPath = join(pluginPath, commandPath)\n if (!existsSync(fullPath)) {\n missing.push(`Command: ${commandPath}`)\n }\n }\n\n // Check skills\n for (const skillPath of manifest.skills || []) {\n const fullPath = join(pluginPath, skillPath)\n if (!existsSync(fullPath)) {\n missing.push(`Skill: ${skillPath}`)\n }\n }\n\n if (missing.length > 0 && strict) {\n throw new PluginError(\n `Missing plugin components:\\n${missing.join('\\n')}`,\n PluginErrorCode.COMPONENT_NOT_FOUND,\n manifest.name,\n )\n } else if (missing.length > 0) {\n console.warn(`Warning: Missing plugin components:\\n${missing.join('\\n')}`)\n }\n }\n\n /**\n * Check disk space availability\n */\n private checkDiskSpace(targetDir: string): void {\n try {\n // Create parent directory if it doesn't exist\n const parentDir = join(targetDir, '..')\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true })\n }\n } catch (error) {\n throw new PluginError(\n `Insufficient permissions or disk space: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n undefined,\n error,\n )\n }\n }\n\n /**\n * Install plugin from source directory to target directory\n */\n private installToTarget(\n sourceDir: string,\n targetDir: string,\n options?: InstallOptions,\n ): void {\n this.emitProgress(InstallPhase.INSTALLING, `Installing to ${targetDir}`, 60)\n\n // Check if target exists\n if (existsSync(targetDir)) {\n if (options?.force) {\n rmSync(targetDir, { recursive: true, force: true })\n } else {\n throw new PluginError(\n `Plugin directory already exists: ${targetDir}. Use --force to overwrite.`,\n PluginErrorCode.ALREADY_INSTALLED,\n )\n }\n }\n\n // Create parent directory\n const parentDir = join(targetDir, '..')\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true })\n }\n\n // Install: symlink for dev mode, copy otherwise\n if (options?.dev) {\n symlinkSync(sourceDir, targetDir, 'dir')\n } else {\n cpSync(sourceDir, targetDir, { recursive: true })\n }\n }\n\n /**\n * Cleanup temporary directories\n */\n private cleanup(): void {\n for (const tempDir of this.tempDirs) {\n try {\n if (existsSync(tempDir)) {\n rmSync(tempDir, { recursive: true, force: true })\n }\n } catch (error) {\n console.error(`Error cleaning up temp directory ${tempDir}:`, error)\n }\n }\n\n this.tempDirs = []\n }\n\n /**\n * Install plugin from any source\n */\n async installPlugin(\n source: PluginSource,\n options?: InstallOptions,\n ): Promise<string> {\n let sourceDir: string | undefined\n let manifest: PluginManifest | undefined\n\n try {\n // Phase 1: Validate and fetch source\n this.emitProgress(\n InstallPhase.VALIDATING_SOURCE,\n 'Validating plugin source',\n 0,\n )\n\n switch (source.type) {\n case 'git':\n sourceDir = await this.cloneGitRepo(source.repo, source.ref)\n break\n\n case 'local':\n sourceDir = resolve(source.path)\n if (!existsSync(sourceDir)) {\n throw new PluginError(\n `Local plugin path does not exist: ${sourceDir}`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n break\n\n case 'npm':\n throw new PluginError(\n 'NPM package installation not yet supported',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n\n case 'marketplace':\n // Find plugin in marketplace\n const found = findPlugin(source.name, source.marketplace)\n\n if (!found) {\n throw new PluginError(\n source.marketplace\n ? `Plugin \"${source.name}\" not found in marketplace \"${source.marketplace}\"`\n : `Plugin \"${source.name}\" not found in any marketplace`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n const { plugin } = found\n\n // Recursively install from plugin's source\n if (typeof plugin.source === 'string') {\n throw new PluginError(\n 'Relative path plugins not yet supported',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n } else if (plugin.source.source === 'github') {\n sourceDir = await this.cloneGitRepo(\n `https://github.com/${plugin.source.repo}.git`,\n plugin.source.ref,\n )\n } else if (plugin.source.source === 'url') {\n sourceDir = await this.cloneGitRepo(\n plugin.source.url,\n plugin.source.ref,\n )\n } else {\n throw new PluginError(\n 'Unsupported marketplace plugin source type',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n break\n\n default:\n throw new PluginError(\n 'Unknown plugin source type',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n // Phase 2: Validate manifest\n this.emitProgress(\n InstallPhase.VALIDATING_MANIFEST,\n 'Validating plugin manifest',\n 30,\n )\n\n manifest = this.validateManifest(sourceDir)\n\n // Check if already installed\n const global = options?.global ?? false\n if (this.isPluginInstalled(manifest.name, global) && !options?.force) {\n throw new PluginError(\n `Plugin \"${manifest.name}\" is already installed. Use --force to overwrite.`,\n PluginErrorCode.ALREADY_INSTALLED,\n manifest.name,\n )\n }\n\n // Phase 3: Validate components\n this.emitProgress(\n InstallPhase.VALIDATING_MANIFEST,\n 'Validating plugin components',\n 40,\n )\n\n const strict = options?.validation === 'strict'\n this.validateComponents(sourceDir, manifest, strict)\n\n // Phase 4: Check dependencies (placeholder)\n this.emitProgress(\n InstallPhase.CHECKING_DEPENDENCIES,\n 'Checking dependencies',\n 50,\n )\n\n // TODO: Check engine versions, dependencies, etc.\n\n // Phase 5: Determine target directory\n const targetDir = this.getInstallDir(manifest.name, options)\n\n // Check disk space and permissions\n this.checkDiskSpace(targetDir)\n\n // Phase 6: Install to target\n this.installToTarget(sourceDir, targetDir, options)\n\n // Phase 7: Register plugin\n this.emitProgress(InstallPhase.REGISTERING, 'Registering plugin', 80)\n\n this.registerPlugin(manifest, source, targetDir, global)\n\n // Phase 8: Complete\n this.emitProgress(\n InstallPhase.COMPLETE,\n `Successfully installed ${manifest.name}`,\n 100,\n manifest.name,\n )\n\n return targetDir\n } catch (error) {\n // Cleanup on error\n this.emitProgress(\n InstallPhase.CLEANUP,\n 'Installation failed, cleaning up',\n 0,\n )\n\n throw error\n } finally {\n // Always cleanup temp directories\n this.cleanup()\n }\n }\n\n /**\n * Uninstall a plugin by name\n */\n async uninstallPlugin(\n pluginName: string,\n options?: { global?: boolean },\n ): Promise<void> {\n const global = options?.global ?? false\n const registry = this.loadRegistry(global)\n\n // Find plugin in registry\n const entry = registry.find(e => e.name === pluginName)\n\n if (!entry) {\n throw new PluginError(\n `Plugin \"${pluginName}\" is not installed`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n // Remove plugin directory\n if (existsSync(entry.location)) {\n try {\n rmSync(entry.location, { recursive: true, force: true })\n } catch (error) {\n throw new PluginError(\n `Failed to remove plugin directory: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n pluginName,\n error,\n )\n }\n }\n\n // Unregister from registry\n this.unregisterPlugin(pluginName, global)\n }\n\n /**\n * Update a plugin (re-install from source)\n */\n async updatePlugin(\n pluginName: string,\n options?: { global?: boolean },\n ): Promise<void> {\n const global = options?.global ?? false\n const registry = this.loadRegistry(global)\n\n // Find plugin in registry\n const entry = registry.find(e => e.name === pluginName)\n\n if (!entry) {\n throw new PluginError(\n `Plugin \"${pluginName}\" is not installed`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n // Uninstall and reinstall\n await this.uninstallPlugin(pluginName, { global })\n await this.installPlugin(entry.source, { global, force: true })\n }\n}\n\n/**\n * Convenience function: Install plugin from source\n */\nexport async function installPlugin(\n source: PluginSource,\n options?: InstallOptions,\n): Promise<string> {\n const installer = new PluginInstaller()\n\n // Setup progress listener if not silent\n if (!options?.silent) {\n installer.on('progress', (progress: InstallProgress) => {\n console.log(\n `[${progress.percentage}%] ${progress.phase}: ${progress.message}`,\n )\n })\n }\n\n return installer.installPlugin(source, options)\n}\n\n/**\n * Convenience function: Uninstall plugin by name\n */\nexport async function uninstallPlugin(\n name: string,\n options?: { global?: boolean },\n): Promise<void> {\n const installer = new PluginInstaller()\n return installer.uninstallPlugin(name, options)\n}\n\n/**\n * Convenience function: Update plugin by name\n */\nexport async function updatePlugin(\n name: string,\n options?: { global?: boolean },\n): Promise<void> {\n const installer = new PluginInstaller()\n return installer.updatePlugin(name, options)\n}\n\n/**\n * List all installed plugins\n */\nexport function listInstalledPlugins(global?: boolean): PluginRegistryEntry[] {\n const installer = new PluginInstaller()\n const globalRegistry = installer['loadRegistry'](true)\n const projectRegistry = installer['loadRegistry'](false)\n\n if (global === true) {\n return globalRegistry\n } else if (global === false) {\n return projectRegistry\n } else {\n // Return both, with project plugins overriding global\n const combined = new Map<string, PluginRegistryEntry>()\n\n for (const entry of globalRegistry) {\n combined.set(entry.name, entry)\n }\n\n for (const entry of projectRegistry) {\n combined.set(entry.name, entry)\n }\n\n return Array.from(combined.values())\n }\n}\n"],
|
|
5
|
-
"mappings": "AAaA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AACP,SAAS,MAAM,eAAyB;AACxC,SAAS,eAAe;AACxB,SAAS,oBAAoB;AAC7B,SAAS,
|
|
4
|
+
"sourcesContent": ["/**\n * Plugin Installer\n *\n * Comprehensive plugin installation utility with proper error handling,\n * progress tracking, and cleanup on failure.\n *\n * Supports installation from:\n * - Git repositories (GitHub shorthand, full URLs)\n * - Local directories (copy or symlink)\n * - NPM packages (future)\n * - Marketplaces (via marketplaceManager)\n */\n\nimport {\n existsSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n rmSync,\n cpSync,\n symlinkSync,\n statSync,\n readdirSync,\n} from 'fs'\nimport { join, resolve, basename } from 'path'\nimport { homedir } from 'os'\nimport { EventEmitter } from 'events'\nimport { fetchRepo, RepoSource } from './repoFetcher'\nimport { getCwd } from './state'\nimport {\n PluginManifest,\n PluginManifestSchema,\n PluginSource,\n PluginError,\n PluginErrorCode,\n} from '../types/plugin'\nimport { findPlugin } from './marketplaceManager'\n\n/**\n * Installation options\n */\nexport interface InstallOptions {\n /** Target directory (default: ~/.minto/plugins/<name> or ./.minto/plugins/<name>) */\n targetDir?: string\n\n /** Overwrite existing installation */\n force?: boolean\n\n /** Create symlink instead of copy (for local development) */\n dev?: boolean\n\n /** Install globally (~/minto/plugins/) vs project-level (./.minto/plugins/) */\n global?: boolean\n\n /** Silent mode (no progress events) */\n silent?: boolean\n\n /** Validation mode: strict (require all components) or loose */\n validation?: 'strict' | 'loose'\n}\n\n/**\n * Installation progress phases\n */\nexport enum InstallPhase {\n VALIDATING_SOURCE = 'validating_source',\n CLONING = 'cloning',\n COPYING = 'copying',\n VALIDATING_MANIFEST = 'validating_manifest',\n CHECKING_DEPENDENCIES = 'checking_dependencies',\n INSTALLING = 'installing',\n REGISTERING = 'registering',\n CLEANUP = 'cleanup',\n COMPLETE = 'complete',\n}\n\n/**\n * Installation progress event\n */\nexport interface InstallProgress {\n phase: InstallPhase\n message: string\n percentage: number\n pluginName?: string\n}\n\n/**\n * Plugin registry entry\n */\ninterface PluginRegistryEntry {\n name: string\n version: string\n source: PluginSource\n installedAt: string\n location: string\n}\n\n/**\n * Plugin installer with event-based progress tracking\n */\nexport class PluginInstaller extends EventEmitter {\n private tempDirs: string[] = []\n\n /**\n * Emit progress event\n */\n private emitProgress(\n phase: InstallPhase,\n message: string,\n percentage: number,\n pluginName?: string,\n ): void {\n this.emit('progress', {\n phase,\n message,\n percentage,\n pluginName,\n } as InstallProgress)\n }\n\n /**\n * Get plugin installation directory\n */\n private getInstallDir(pluginName: string, options?: InstallOptions): string {\n if (options?.targetDir) {\n return resolve(options.targetDir)\n }\n\n const baseDir = options?.global ? homedir() : getCwd()\n return join(baseDir, '.minto', 'plugins', pluginName)\n }\n\n /**\n * Get plugin registry path\n */\n private getRegistryPath(global: boolean): string {\n const baseDir = global ? homedir() : getCwd()\n return join(baseDir, '.minto', 'plugins', 'registry.json')\n }\n\n /**\n * Load plugin registry\n */\n private loadRegistry(global: boolean): PluginRegistryEntry[] {\n const registryPath = this.getRegistryPath(global)\n\n if (!existsSync(registryPath)) {\n return []\n }\n\n try {\n const content = readFileSync(registryPath, 'utf-8')\n return JSON.parse(content)\n } catch (error) {\n console.error('Error loading plugin registry:', error)\n return []\n }\n }\n\n /**\n * Save plugin registry\n */\n private saveRegistry(entries: PluginRegistryEntry[], global: boolean): void {\n const registryPath = this.getRegistryPath(global)\n const registryDir = join(registryPath, '..')\n\n // Ensure directory exists\n if (!existsSync(registryDir)) {\n mkdirSync(registryDir, { recursive: true })\n }\n\n writeFileSync(registryPath, JSON.stringify(entries, null, 2), 'utf-8')\n }\n\n /**\n * Register plugin in registry\n */\n private registerPlugin(\n manifest: PluginManifest,\n source: PluginSource,\n location: string,\n global: boolean,\n ): void {\n const registry = this.loadRegistry(global)\n\n // Remove existing entry if present\n const filtered = registry.filter(e => e.name !== manifest.name)\n\n // Add new entry\n filtered.push({\n name: manifest.name,\n version: manifest.version,\n source,\n installedAt: new Date().toISOString(),\n location,\n })\n\n this.saveRegistry(filtered, global)\n }\n\n /**\n * Unregister plugin from registry\n */\n private unregisterPlugin(pluginName: string, global: boolean): void {\n const registry = this.loadRegistry(global)\n const filtered = registry.filter(e => e.name !== pluginName)\n this.saveRegistry(filtered, global)\n }\n\n /**\n * Fetch a git repository to a temporary directory using repoFetcher\n */\n private async fetchToTempDir(source: RepoSource): Promise<string> {\n const tempDir = join(\n homedir(),\n '.minto',\n 'temp',\n 'plugins',\n Date.now().toString(),\n )\n\n this.tempDirs.push(tempDir)\n mkdirSync(tempDir, { recursive: true })\n\n this.emitProgress(InstallPhase.CLONING, `Fetching repository...`, 20)\n\n try {\n await fetchRepo(source, tempDir)\n return tempDir\n } catch (error) {\n throw new PluginError(\n `Failed to fetch repository: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n undefined,\n error,\n )\n }\n }\n\n /**\n * Validate plugin manifest exists and is valid\n */\n private validateManifest(pluginPath: string): PluginManifest {\n const manifestPath = join(pluginPath, 'plugin.json')\n\n if (!existsSync(manifestPath)) {\n throw new PluginError(\n `Plugin manifest (plugin.json) not found in ${pluginPath}`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n try {\n const content = readFileSync(manifestPath, 'utf-8')\n const data = JSON.parse(content)\n\n // Validate with Zod schema\n return PluginManifestSchema.parse(data)\n } catch (error) {\n throw new PluginError(\n `Invalid plugin manifest: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.MANIFEST_INVALID,\n undefined,\n error,\n )\n }\n }\n\n /**\n * Check if plugin is already installed\n */\n private isPluginInstalled(pluginName: string, global: boolean): boolean {\n const registry = this.loadRegistry(global)\n return registry.some(e => e.name === pluginName)\n }\n\n /**\n * Validate plugin components exist\n */\n private validateComponents(\n pluginPath: string,\n manifest: PluginManifest,\n strict: boolean,\n ): void {\n const missing: string[] = []\n\n // Check agents\n for (const agentPath of manifest.agents || []) {\n const fullPath = join(pluginPath, agentPath)\n if (!existsSync(fullPath)) {\n missing.push(`Agent: ${agentPath}`)\n }\n }\n\n // Check commands\n for (const commandPath of manifest.commands || []) {\n const fullPath = join(pluginPath, commandPath)\n if (!existsSync(fullPath)) {\n missing.push(`Command: ${commandPath}`)\n }\n }\n\n // Check skills\n for (const skillPath of manifest.skills || []) {\n const fullPath = join(pluginPath, skillPath)\n if (!existsSync(fullPath)) {\n missing.push(`Skill: ${skillPath}`)\n }\n }\n\n if (missing.length > 0 && strict) {\n throw new PluginError(\n `Missing plugin components:\\n${missing.join('\\n')}`,\n PluginErrorCode.COMPONENT_NOT_FOUND,\n manifest.name,\n )\n } else if (missing.length > 0) {\n console.warn(`Warning: Missing plugin components:\\n${missing.join('\\n')}`)\n }\n }\n\n /**\n * Check disk space availability\n */\n private checkDiskSpace(targetDir: string): void {\n try {\n // Create parent directory if it doesn't exist\n const parentDir = join(targetDir, '..')\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true })\n }\n } catch (error) {\n throw new PluginError(\n `Insufficient permissions or disk space: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n undefined,\n error,\n )\n }\n }\n\n /**\n * Install plugin from source directory to target directory\n */\n private installToTarget(\n sourceDir: string,\n targetDir: string,\n options?: InstallOptions,\n ): void {\n this.emitProgress(InstallPhase.INSTALLING, `Installing to ${targetDir}`, 60)\n\n // Check if target exists\n if (existsSync(targetDir)) {\n if (options?.force) {\n rmSync(targetDir, { recursive: true, force: true })\n } else {\n throw new PluginError(\n `Plugin directory already exists: ${targetDir}. Use --force to overwrite.`,\n PluginErrorCode.ALREADY_INSTALLED,\n )\n }\n }\n\n // Create parent directory\n const parentDir = join(targetDir, '..')\n if (!existsSync(parentDir)) {\n mkdirSync(parentDir, { recursive: true })\n }\n\n // Install: symlink for dev mode, copy otherwise\n if (options?.dev) {\n symlinkSync(sourceDir, targetDir, 'dir')\n } else {\n cpSync(sourceDir, targetDir, { recursive: true })\n }\n }\n\n /**\n * Cleanup temporary directories\n */\n private cleanup(): void {\n for (const tempDir of this.tempDirs) {\n try {\n if (existsSync(tempDir)) {\n rmSync(tempDir, { recursive: true, force: true })\n }\n } catch (error) {\n console.error(`Error cleaning up temp directory ${tempDir}:`, error)\n }\n }\n\n this.tempDirs = []\n }\n\n /**\n * Install plugin from any source\n */\n async installPlugin(\n source: PluginSource,\n options?: InstallOptions,\n ): Promise<string> {\n let sourceDir: string | undefined\n let manifest: PluginManifest | undefined\n\n try {\n // Phase 1: Validate and fetch source\n this.emitProgress(\n InstallPhase.VALIDATING_SOURCE,\n 'Validating plugin source',\n 0,\n )\n\n switch (source.type) {\n case 'git': {\n // Detect GitHub URLs and use tarball; otherwise use URL source\n const ghMatch = source.repo.match(\n /github\\.com[/:]([^/]+\\/[^/.]+?)(?:\\.git)?$/,\n )\n if (ghMatch) {\n sourceDir = await this.fetchToTempDir({\n type: 'github',\n repo: ghMatch[1],\n ref: source.ref,\n })\n } else {\n sourceDir = await this.fetchToTempDir({\n type: 'url',\n url: source.repo,\n ref: source.ref,\n })\n }\n break\n }\n\n case 'local':\n sourceDir = resolve(source.path)\n if (!existsSync(sourceDir)) {\n throw new PluginError(\n `Local plugin path does not exist: ${sourceDir}`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n break\n\n case 'npm':\n throw new PluginError(\n 'NPM package installation not yet supported',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n\n case 'marketplace':\n // Find plugin in marketplace\n const found = findPlugin(source.name, source.marketplace)\n\n if (!found) {\n throw new PluginError(\n source.marketplace\n ? `Plugin \"${source.name}\" not found in marketplace \"${source.marketplace}\"`\n : `Plugin \"${source.name}\" not found in any marketplace`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n const { plugin } = found\n\n // Recursively install from plugin's source\n if (typeof plugin.source === 'string') {\n throw new PluginError(\n 'Relative path plugins not yet supported',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n } else if (plugin.source.source === 'github') {\n sourceDir = await this.fetchToTempDir({\n type: 'github',\n repo: plugin.source.repo,\n ref: plugin.source.ref,\n })\n } else if (plugin.source.source === 'url') {\n sourceDir = await this.fetchToTempDir({\n type: 'url',\n url: plugin.source.url,\n ref: plugin.source.ref,\n })\n } else {\n throw new PluginError(\n 'Unsupported marketplace plugin source type',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n break\n\n default:\n throw new PluginError(\n 'Unknown plugin source type',\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n // Phase 2: Validate manifest\n this.emitProgress(\n InstallPhase.VALIDATING_MANIFEST,\n 'Validating plugin manifest',\n 30,\n )\n\n manifest = this.validateManifest(sourceDir)\n\n // Check if already installed\n const global = options?.global ?? false\n if (this.isPluginInstalled(manifest.name, global) && !options?.force) {\n throw new PluginError(\n `Plugin \"${manifest.name}\" is already installed. Use --force to overwrite.`,\n PluginErrorCode.ALREADY_INSTALLED,\n manifest.name,\n )\n }\n\n // Phase 3: Validate components\n this.emitProgress(\n InstallPhase.VALIDATING_MANIFEST,\n 'Validating plugin components',\n 40,\n )\n\n const strict = options?.validation === 'strict'\n this.validateComponents(sourceDir, manifest, strict)\n\n // Phase 4: Check dependencies (placeholder)\n this.emitProgress(\n InstallPhase.CHECKING_DEPENDENCIES,\n 'Checking dependencies',\n 50,\n )\n\n // TODO: Check engine versions, dependencies, etc.\n\n // Phase 5: Determine target directory\n const targetDir = this.getInstallDir(manifest.name, options)\n\n // Check disk space and permissions\n this.checkDiskSpace(targetDir)\n\n // Phase 6: Install to target\n this.installToTarget(sourceDir, targetDir, options)\n\n // Phase 7: Register plugin\n this.emitProgress(InstallPhase.REGISTERING, 'Registering plugin', 80)\n\n this.registerPlugin(manifest, source, targetDir, global)\n\n // Phase 8: Complete\n this.emitProgress(\n InstallPhase.COMPLETE,\n `Successfully installed ${manifest.name}`,\n 100,\n manifest.name,\n )\n\n return targetDir\n } catch (error) {\n // Cleanup on error\n this.emitProgress(\n InstallPhase.CLEANUP,\n 'Installation failed, cleaning up',\n 0,\n )\n\n throw error\n } finally {\n // Always cleanup temp directories\n this.cleanup()\n }\n }\n\n /**\n * Uninstall a plugin by name\n */\n async uninstallPlugin(\n pluginName: string,\n options?: { global?: boolean },\n ): Promise<void> {\n const global = options?.global ?? false\n const registry = this.loadRegistry(global)\n\n // Find plugin in registry\n const entry = registry.find(e => e.name === pluginName)\n\n if (!entry) {\n throw new PluginError(\n `Plugin \"${pluginName}\" is not installed`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n // Remove plugin directory\n if (existsSync(entry.location)) {\n try {\n rmSync(entry.location, { recursive: true, force: true })\n } catch (error) {\n throw new PluginError(\n `Failed to remove plugin directory: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n pluginName,\n error,\n )\n }\n }\n\n // Unregister from registry\n this.unregisterPlugin(pluginName, global)\n }\n\n /**\n * Update a plugin (re-install from source)\n */\n async updatePlugin(\n pluginName: string,\n options?: { global?: boolean },\n ): Promise<void> {\n const global = options?.global ?? false\n const registry = this.loadRegistry(global)\n\n // Find plugin in registry\n const entry = registry.find(e => e.name === pluginName)\n\n if (!entry) {\n throw new PluginError(\n `Plugin \"${pluginName}\" is not installed`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n // Uninstall and reinstall\n await this.uninstallPlugin(pluginName, { global })\n await this.installPlugin(entry.source, { global, force: true })\n }\n}\n\n/**\n * Convenience function: Install plugin from source\n */\nexport async function installPlugin(\n source: PluginSource,\n options?: InstallOptions,\n): Promise<string> {\n const installer = new PluginInstaller()\n\n // Setup progress listener if not silent\n if (!options?.silent) {\n installer.on('progress', (progress: InstallProgress) => {\n console.log(\n `[${progress.percentage}%] ${progress.phase}: ${progress.message}`,\n )\n })\n }\n\n return installer.installPlugin(source, options)\n}\n\n/**\n * Convenience function: Uninstall plugin by name\n */\nexport async function uninstallPlugin(\n name: string,\n options?: { global?: boolean },\n): Promise<void> {\n const installer = new PluginInstaller()\n return installer.uninstallPlugin(name, options)\n}\n\n/**\n * Convenience function: Update plugin by name\n */\nexport async function updatePlugin(\n name: string,\n options?: { global?: boolean },\n): Promise<void> {\n const installer = new PluginInstaller()\n return installer.updatePlugin(name, options)\n}\n\n/**\n * List all installed plugins\n */\nexport function listInstalledPlugins(global?: boolean): PluginRegistryEntry[] {\n const installer = new PluginInstaller()\n const globalRegistry = installer['loadRegistry'](true)\n const projectRegistry = installer['loadRegistry'](false)\n\n if (global === true) {\n return globalRegistry\n } else if (global === false) {\n return projectRegistry\n } else {\n // Return both, with project plugins overriding global\n const combined = new Map<string, PluginRegistryEntry>()\n\n for (const entry of globalRegistry) {\n combined.set(entry.name, entry)\n }\n\n for (const entry of projectRegistry) {\n combined.set(entry.name, entry)\n }\n\n return Array.from(combined.values())\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["InstallPhase"]
|
|
7
7
|
}
|
|
@@ -18,8 +18,12 @@ function getPluginDirectories() {
|
|
|
18
18
|
const cwd = getCwd();
|
|
19
19
|
const home = homedir();
|
|
20
20
|
return [
|
|
21
|
+
join(home, ".claude", "plugins"),
|
|
22
|
+
// User global (legacy)
|
|
21
23
|
join(home, ".minto", "plugins"),
|
|
22
24
|
// User global
|
|
25
|
+
join(cwd, ".claude", "plugins"),
|
|
26
|
+
// Project (legacy)
|
|
23
27
|
join(cwd, ".minto", "plugins")
|
|
24
28
|
// Project (highest priority)
|
|
25
29
|
];
|
|
@@ -39,8 +43,13 @@ function discoverPluginPaths() {
|
|
|
39
43
|
".minto-plugin",
|
|
40
44
|
"plugin.json"
|
|
41
45
|
);
|
|
46
|
+
const claudeManifestPath = join(
|
|
47
|
+
pluginPath,
|
|
48
|
+
".claude-plugin",
|
|
49
|
+
"plugin.json"
|
|
50
|
+
);
|
|
42
51
|
const rootManifestPath = join(pluginPath, "plugin.json");
|
|
43
|
-
if (!existsSync(mintoManifestPath) && !existsSync(rootManifestPath))
|
|
52
|
+
if (!existsSync(mintoManifestPath) && !existsSync(claudeManifestPath) && !existsSync(rootManifestPath))
|
|
44
53
|
continue;
|
|
45
54
|
pluginPaths.set(entry, pluginPath);
|
|
46
55
|
}
|
|
@@ -50,35 +59,35 @@ function discoverPluginPaths() {
|
|
|
50
59
|
return pluginPaths;
|
|
51
60
|
}
|
|
52
61
|
function loadManifest(pluginPath) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
if (!manifestPath) {
|
|
62
|
+
const candidates = [
|
|
63
|
+
join(pluginPath, ".minto-plugin", "plugin.json"),
|
|
64
|
+
join(pluginPath, ".claude-plugin", "plugin.json"),
|
|
65
|
+
join(pluginPath, "plugin.json")
|
|
66
|
+
];
|
|
67
|
+
const existing = candidates.filter((p) => existsSync(p));
|
|
68
|
+
if (existing.length === 0) {
|
|
62
69
|
throw new PluginError(
|
|
63
70
|
`Plugin manifest not found. Tried:
|
|
64
|
-
- ${
|
|
65
|
-
- ${rootManifest}`,
|
|
71
|
+
${candidates.map((p) => ` - ${p}`).join("\n")}`,
|
|
66
72
|
PluginErrorCode.MANIFEST_NOT_FOUND
|
|
67
73
|
);
|
|
68
74
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
void 0,
|
|
79
|
-
error
|
|
80
|
-
);
|
|
75
|
+
let lastError = null;
|
|
76
|
+
for (const manifestPath of existing) {
|
|
77
|
+
try {
|
|
78
|
+
const manifestContent = readFileSync(manifestPath, "utf-8");
|
|
79
|
+
const manifestData = JSON.parse(manifestContent);
|
|
80
|
+
return PluginManifestSchema.parse(manifestData);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
lastError = error;
|
|
83
|
+
}
|
|
81
84
|
}
|
|
85
|
+
throw new PluginError(
|
|
86
|
+
`Invalid plugin manifest at ${existing[0]}: ${lastError instanceof Error ? lastError.message : String(lastError)}`,
|
|
87
|
+
PluginErrorCode.MANIFEST_INVALID,
|
|
88
|
+
void 0,
|
|
89
|
+
lastError
|
|
90
|
+
);
|
|
82
91
|
}
|
|
83
92
|
function loadAgents(pluginPath, manifest) {
|
|
84
93
|
const agents = [];
|
|
@@ -324,10 +333,12 @@ function loadHooks(pluginPath, manifest) {
|
|
|
324
333
|
function expandEnvVars(value, pluginPath) {
|
|
325
334
|
let expanded = value.replace(/\$\{MINTO_PLUGIN_ROOT\}/g, pluginPath);
|
|
326
335
|
expanded = expanded.replace(/\$MINTO_PLUGIN_ROOT(?![A-Za-z_])/g, pluginPath);
|
|
336
|
+
expanded = expanded.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginPath);
|
|
337
|
+
expanded = expanded.replace(/\$CLAUDE_PLUGIN_ROOT(?![A-Za-z_])/g, pluginPath);
|
|
327
338
|
expanded = expanded.replace(
|
|
328
339
|
/\$\{([^}:]+)(?::-([^}]*))?\}/g,
|
|
329
340
|
(match, varName, defaultValue) => {
|
|
330
|
-
if (varName === "MINTO_PLUGIN_ROOT") {
|
|
341
|
+
if (varName === "MINTO_PLUGIN_ROOT" || varName === "CLAUDE_PLUGIN_ROOT") {
|
|
331
342
|
return match;
|
|
332
343
|
}
|
|
333
344
|
return process.env[varName] || defaultValue || "";
|
|
@@ -425,6 +436,18 @@ function loadMCPServers(pluginPath, manifest) {
|
|
|
425
436
|
function determinePluginSource(pluginPath) {
|
|
426
437
|
const home = homedir();
|
|
427
438
|
const cwd = getCwd();
|
|
439
|
+
const ccSyncPath = join(pluginPath, ".cc-sync.json");
|
|
440
|
+
if (existsSync(ccSyncPath)) {
|
|
441
|
+
try {
|
|
442
|
+
const ccMeta = JSON.parse(readFileSync(ccSyncPath, "utf-8"));
|
|
443
|
+
return {
|
|
444
|
+
type: "claude-code",
|
|
445
|
+
marketplace: ccMeta.marketplace || "",
|
|
446
|
+
name: ccMeta.name || basename(pluginPath)
|
|
447
|
+
};
|
|
448
|
+
} catch {
|
|
449
|
+
}
|
|
450
|
+
}
|
|
428
451
|
const marketplaceMetaPath = join(pluginPath, ".marketplace-meta.json");
|
|
429
452
|
if (existsSync(marketplaceMetaPath)) {
|
|
430
453
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/utils/pluginLoader.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Plugin Loader\n *\n * Discovers and loads plugins from Minto plugin directories.\n *\n * Directory Priority (later overrides earlier):\n * 1. ~/.minto/plugins/ (user global)\n * 2. ./.minto/plugins/ (project - highest priority)\n */\n\nimport {\n existsSync,\n readFileSync,\n readdirSync,\n statSync,\n writeFileSync,\n mkdirSync,\n} from 'fs'\nimport { join, resolve, basename } from 'path'\nimport { homedir } from 'os'\nimport matter from 'gray-matter'\nimport {\n PluginManifest,\n PluginManifestSchema,\n LoadedPlugin,\n LoadedAgent,\n LoadedCommand,\n LoadedSkill,\n LoadedHook,\n LoadedMCPServer,\n PluginError,\n PluginErrorCode,\n PluginSource,\n} from '../types/plugin'\nimport { getCwd } from './state'\n\n/**\n * Plugin discovery directories in priority order\n */\nfunction getPluginDirectories(): string[] {\n const cwd = getCwd()\n const home = homedir()\n\n return [\n join(home, '.minto', 'plugins'), // User global\n join(cwd, '.minto', 'plugins'), // Project (highest priority)\n ]\n}\n\n/**\n * Find all plugin directories across all sources\n */\nfunction discoverPluginPaths(): Map<string, string> {\n const pluginPaths = new Map<string, string>() // name -> path\n const directories = getPluginDirectories()\n\n for (const dir of directories) {\n if (!existsSync(dir)) continue\n\n try {\n const entries = readdirSync(dir)\n\n for (const entry of entries) {\n const pluginPath = join(dir, entry)\n\n // Must be a directory\n if (!statSync(pluginPath).isDirectory()) continue\n\n // Must have plugin.json in .minto-plugin/ or root\n const mintoManifestPath = join(\n pluginPath,\n '.minto-plugin',\n 'plugin.json',\n )\n const rootManifestPath = join(pluginPath, 'plugin.json')\n if (!existsSync(mintoManifestPath) && !existsSync(rootManifestPath))\n continue\n\n // Later directories override earlier ones\n pluginPaths.set(entry, pluginPath)\n }\n } catch (error) {\n // Silently ignore errors\n }\n }\n\n return pluginPaths\n}\n\n/**\n * Load and validate plugin manifest\n *\n * Manifest locations (priority order):\n * 1. .minto-plugin/plugin.json\n * 2. plugin.json (root fallback)\n */\nfunction loadManifest(pluginPath: string): PluginManifest {\n const mintoPluginManifest = join(pluginPath, '.minto-plugin', 'plugin.json')\n const rootManifest = join(pluginPath, 'plugin.json')\n\n let manifestPath: string | null = null\n\n if (existsSync(mintoPluginManifest)) {\n manifestPath = mintoPluginManifest\n } else if (existsSync(rootManifest)) {\n manifestPath = rootManifest\n }\n\n if (!manifestPath) {\n throw new PluginError(\n `Plugin manifest not found. Tried:\\n - ${mintoPluginManifest}\\n - ${rootManifest}`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n try {\n const manifestContent = readFileSync(manifestPath, 'utf-8')\n const manifestData = JSON.parse(manifestContent)\n\n // Validate with Zod schema\n const manifest = PluginManifestSchema.parse(manifestData)\n\n return manifest\n } catch (error) {\n throw new PluginError(\n `Invalid plugin manifest at ${manifestPath}: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.MANIFEST_INVALID,\n undefined,\n error,\n )\n }\n}\n\n/**\n * Load agents from plugin\n */\nfunction loadAgents(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedAgent[] {\n const agents: LoadedAgent[] = []\n const agentsDir = join(pluginPath, 'agents')\n\n // Load agents listed in manifest\n for (const agentPath of manifest.agents || []) {\n const fullPath = join(pluginPath, agentPath)\n\n if (!existsSync(fullPath)) {\n console.warn(`Agent file not found: ${fullPath}`)\n continue\n }\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: promptContent } = matter(content)\n\n agents.push({\n name: data.name || basename(agentPath, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(agentPath, '.md'),\n description: data.description || '',\n tools: data.tools,\n model: data.model,\n content: promptContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading agent ${agentPath}:`, error)\n }\n }\n\n // Also scan agents/ directory if it exists\n if (existsSync(agentsDir) && statSync(agentsDir).isDirectory()) {\n const agentFiles = readdirSync(agentsDir).filter(f => f.endsWith('.md'))\n\n for (const file of agentFiles) {\n const fullPath = join(agentsDir, file)\n\n // Skip if already loaded from manifest\n if (agents.some(a => a.filePath === fullPath)) continue\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: promptContent } = matter(content)\n\n agents.push({\n name: data.name || basename(file, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(file, '.md'),\n description: data.description || '',\n tools: data.tools,\n model: data.model,\n content: promptContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading agent ${file}:`, error)\n }\n }\n }\n\n return agents\n}\n\n/**\n * Load commands from plugin\n */\nfunction loadCommands(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedCommand[] {\n const commands: LoadedCommand[] = []\n const commandsDir = join(pluginPath, 'commands')\n\n // Load commands listed in manifest\n for (const commandPath of manifest.commands || []) {\n const fullPath = join(pluginPath, commandPath)\n\n if (!existsSync(fullPath)) {\n console.warn(`Command file not found: ${fullPath}`)\n continue\n }\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: commandContent } = matter(content)\n\n commands.push({\n name: data.name || basename(commandPath, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(commandPath, '.md'),\n description: data.description,\n aliases: data.aliases,\n enabled: data.enabled !== false,\n hidden: data.hidden === true,\n progressMessage: data.progressMessage,\n argNames: data.argNames,\n 'allowed-tools': data['allowed-tools'],\n content: commandContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading command ${commandPath}:`, error)\n }\n }\n\n // Also scan commands/ directory if it exists\n if (existsSync(commandsDir) && statSync(commandsDir).isDirectory()) {\n const commandFiles = readdirSync(commandsDir).filter(f => f.endsWith('.md'))\n\n for (const file of commandFiles) {\n const fullPath = join(commandsDir, file)\n\n // Skip if already loaded from manifest\n if (commands.some(c => c.filePath === fullPath)) continue\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: commandContent } = matter(content)\n\n commands.push({\n name: data.name || basename(file, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(file, '.md'),\n description: data.description,\n aliases: data.aliases,\n enabled: data.enabled !== false,\n hidden: data.hidden === true,\n progressMessage: data.progressMessage,\n argNames: data.argNames,\n 'allowed-tools': data['allowed-tools'],\n content: commandContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading command ${file}:`, error)\n }\n }\n }\n\n return commands\n}\n\n/**\n * Load skills from plugin\n *\n * Supports two patterns:\n * - Subdirectory: skills/skill-name/SKILL.md\n * - Flat: skills/skill-name.md\n */\nfunction loadSkills(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedSkill[] {\n const skills: LoadedSkill[] = []\n const skillsDir = join(pluginPath, 'skills')\n\n // Load skills listed in manifest\n for (const skillPath of manifest.skills || []) {\n const fullPath = join(pluginPath, skillPath)\n\n if (!existsSync(fullPath)) {\n console.warn(`Skill file not found: ${fullPath}`)\n continue\n }\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: skillContent } = matter(content)\n\n skills.push({\n name: data.name || basename(skillPath, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(skillPath, '.md'),\n description: data.description || '',\n content: skillContent.trim(),\n },\n pluginName: manifest.name,\n source: 'plugin',\n })\n } catch (error) {\n console.error(`Error loading skill ${skillPath}:`, error)\n }\n }\n\n // Auto-discover skills from skills/ directory if it exists\n if (existsSync(skillsDir) && statSync(skillsDir).isDirectory()) {\n const entries = readdirSync(skillsDir, { withFileTypes: true })\n\n for (const entry of entries) {\n // Subdirectory pattern: skill-name/SKILL.md\n if (entry.isDirectory()) {\n const skillMdPath = join(skillsDir, entry.name, 'SKILL.md')\n\n if (existsSync(skillMdPath)) {\n // Skip if already loaded from manifest\n if (skills.some(s => s.filePath === skillMdPath)) continue\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8')\n const { data, content: skillContent } = matter(content)\n\n skills.push({\n name: data.name || entry.name,\n filePath: skillMdPath,\n config: {\n name: data.name || entry.name,\n description: data.description || '',\n content: skillContent.trim(),\n },\n pluginName: manifest.name,\n source: 'plugin',\n })\n } catch (error) {\n console.error(`Error loading skill from ${skillMdPath}:`, error)\n }\n }\n }\n // Flat pattern: .md files directly in skills/\n else if (entry.isFile() && entry.name.endsWith('.md')) {\n const fullPath = join(skillsDir, entry.name)\n\n // Skip if already loaded from manifest\n if (skills.some(s => s.filePath === fullPath)) continue\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: skillContent } = matter(content)\n\n skills.push({\n name: data.name || basename(entry.name, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(entry.name, '.md'),\n description: data.description || '',\n content: skillContent.trim(),\n },\n pluginName: manifest.name,\n source: 'plugin',\n })\n } catch (error) {\n console.error(`Error loading skill ${entry.name}:`, error)\n }\n }\n }\n }\n\n return skills\n}\n\n/**\n * Load hooks from plugin\n *\n * Uses hooks/hooks.json format\n */\nfunction loadHooks(pluginPath: string, manifest: PluginManifest): LoadedHook[] {\n const hooks: LoadedHook[] = []\n const hooksDir = join(pluginPath, 'hooks')\n const hooksJsonPath = join(hooksDir, 'hooks.json')\n\n // Check if hooks.json exists\n if (!existsSync(hooksJsonPath)) {\n return hooks\n }\n\n try {\n const content = readFileSync(hooksJsonPath, 'utf-8')\n const hooksConfig = JSON.parse(content)\n\n // Validate structure (basic validation, full validation in hook executor)\n if (!hooksConfig.hooks || typeof hooksConfig.hooks !== 'object') {\n console.warn(`Invalid hooks.json in ${pluginPath}: missing \"hooks\" field`)\n return hooks\n }\n\n // Process each hook event\n for (const [eventName, matchers] of Object.entries(\n hooksConfig.hooks as Record<string, any[]>,\n )) {\n if (!Array.isArray(matchers)) continue\n\n for (const matcher of matchers) {\n if (!matcher.hooks || !Array.isArray(matcher.hooks)) continue\n\n for (const hookDef of matcher.hooks) {\n hooks.push({\n name: `${manifest.name}:${eventName}:${hooks.length}`,\n filePath: hooksJsonPath,\n config: {\n event: eventName as any,\n matcher: matcher.matcher,\n type: hookDef.type || 'command',\n command: hookDef.command,\n message: hookDef.prompt, // Claude Code uses \"prompt\", we use \"message\" internally\n blocking: hookDef.type === 'prompt',\n timeout: hookDef.timeout || 60,\n },\n pluginName: manifest.name,\n event: eventName as any,\n matcher: matcher.matcher,\n })\n }\n }\n }\n } catch (error) {\n console.error(`Error loading hooks from ${hooksJsonPath}:`, error)\n }\n\n return hooks\n}\n\n/**\n * Expand environment variables in a string\n * Supports:\n * - ${VAR} - expands to environment variable VAR\n * - ${VAR:-default} - expands to VAR or default if not set\n * - ${MINTO_PLUGIN_ROOT} - expands to plugin directory path\n */\nfunction expandEnvVars(value: string, pluginPath: string): string {\n // Replace ${MINTO_PLUGIN_ROOT} with plugin path\n let expanded = value.replace(/\\$\\{MINTO_PLUGIN_ROOT\\}/g, pluginPath)\n\n // Also support $MINTO_PLUGIN_ROOT without braces\n expanded = expanded.replace(/\\$MINTO_PLUGIN_ROOT(?![A-Za-z_])/g, pluginPath)\n\n // Replace ${VAR} or ${VAR:-default} for other environment variables\n expanded = expanded.replace(\n /\\$\\{([^}:]+)(?::-([^}]*))?\\}/g,\n (match, varName, defaultValue) => {\n // Skip already processed plugin root variable\n if (varName === 'MINTO_PLUGIN_ROOT') {\n return match\n }\n return process.env[varName] || defaultValue || ''\n },\n )\n\n return expanded\n}\n\n/**\n * Expand environment variables in MCP server configuration\n */\nfunction expandServerConfig(config: any, pluginPath: string): any {\n const expanded: any = { ...config }\n\n // Expand string fields\n if (expanded.command) {\n expanded.command = expandEnvVars(expanded.command, pluginPath)\n }\n\n if (expanded.url) {\n expanded.url = expandEnvVars(expanded.url, pluginPath)\n }\n\n // Expand args array\n if (expanded.args && Array.isArray(expanded.args)) {\n expanded.args = expanded.args.map((arg: string) =>\n expandEnvVars(arg, pluginPath),\n )\n }\n\n // Expand env object\n if (expanded.env && typeof expanded.env === 'object') {\n expanded.env = Object.fromEntries(\n Object.entries(expanded.env).map(([k, v]) => [\n k,\n expandEnvVars(String(v), pluginPath),\n ]),\n )\n }\n\n // Expand headers object\n if (expanded.headers && typeof expanded.headers === 'object') {\n expanded.headers = Object.fromEntries(\n Object.entries(expanded.headers).map(([k, v]) => [\n k,\n expandEnvVars(String(v), pluginPath),\n ]),\n )\n }\n\n return expanded\n}\n\n/**\n * Load MCP servers from plugin\n *\n * Two configuration methods:\n * 1. .mcp.json file at plugin root\n * 2. Inline mcpServers in plugin.json manifest\n *\n * Inline configurations override .mcp.json for same server name.\n */\nfunction loadMCPServers(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedMCPServer[] {\n const mcpServers: LoadedMCPServer[] = []\n\n // Step 1: Load .mcp.json if it exists\n const mcpJsonPath = join(pluginPath, '.mcp.json')\n let mcpJsonServers: Record<string, any> = {}\n\n if (existsSync(mcpJsonPath)) {\n try {\n const content = readFileSync(mcpJsonPath, 'utf-8')\n const parsed = JSON.parse(content)\n\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {\n mcpJsonServers = parsed.mcpServers\n }\n } catch (error) {\n console.error(`Error loading .mcp.json from ${pluginPath}:`, error)\n }\n }\n\n // Step 2: Get inline MCP servers from manifest\n let inlineServers: Record<string, any> = {}\n\n if (\n manifest.mcpServers &&\n typeof manifest.mcpServers === 'object' &&\n !Array.isArray(manifest.mcpServers)\n ) {\n inlineServers = manifest.mcpServers as Record<string, any>\n }\n\n // Step 3: Merge (inline overrides .mcp.json)\n const allServers = { ...mcpJsonServers, ...inlineServers }\n\n // Step 4: Convert to LoadedMCPServer format\n for (const [name, config] of Object.entries(allServers)) {\n try {\n // Expand environment variables\n const expandedConfig = expandServerConfig(config, pluginPath)\n\n // Validate required fields based on server type\n const serverType = expandedConfig.type || 'stdio'\n\n if (serverType === 'stdio' && !expandedConfig.command) {\n console.warn(\n `MCP server \"${name}\" in ${manifest.name} is missing required \"command\" field for stdio type`,\n )\n continue\n }\n\n if (\n (serverType === 'http' || serverType === 'sse') &&\n !expandedConfig.url\n ) {\n console.warn(\n `MCP server \"${name}\" in ${manifest.name} is missing required \"url\" field for ${serverType} type`,\n )\n continue\n }\n\n mcpServers.push({\n name,\n filePath: existsSync(mcpJsonPath)\n ? mcpJsonPath\n : join(pluginPath, 'plugin.json'),\n config: {\n command: expandedConfig.command || '',\n args: expandedConfig.args || [],\n env: expandedConfig.env || {},\n timeout: expandedConfig.timeout,\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(\n `Error loading MCP server \"${name}\" from ${manifest.name}:`,\n error,\n )\n }\n }\n\n return mcpServers\n}\n\n/**\n * Determine plugin source from path\n * Checks for marketplace metadata file to identify marketplace-installed plugins\n */\nfunction determinePluginSource(pluginPath: string): PluginSource {\n const home = homedir()\n const cwd = getCwd()\n\n // Check for marketplace metadata file\n const marketplaceMetaPath = join(pluginPath, '.marketplace-meta.json')\n if (existsSync(marketplaceMetaPath)) {\n try {\n const metaContent = readFileSync(marketplaceMetaPath, 'utf-8')\n const meta = JSON.parse(metaContent)\n if (meta.marketplace && meta.plugin) {\n return {\n type: 'marketplace',\n marketplace: meta.marketplace,\n name: meta.plugin,\n }\n }\n } catch (error) {\n // If metadata file is corrupted, fall through to local detection\n console.warn(\n `Failed to read marketplace metadata from ${marketplaceMetaPath}:`,\n error,\n )\n }\n }\n\n // Determine local source based on path\n if (pluginPath.startsWith(join(home, '.minto', 'plugins'))) {\n return { type: 'local', path: 'user-global' }\n } else if (pluginPath.startsWith(join(cwd, '.minto', 'plugins'))) {\n return { type: 'local', path: 'project' }\n } else {\n return { type: 'local', path: pluginPath }\n }\n}\n\n/**\n * Load a single plugin from a directory\n */\nexport function loadPlugin(pluginPath: string): LoadedPlugin {\n const manifest = loadManifest(pluginPath)\n const source = determinePluginSource(pluginPath)\n const pluginConfig = loadPluginConfig(pluginPath)\n\n const agents = loadAgents(pluginPath, manifest)\n const commands = loadCommands(pluginPath, manifest)\n const skills = loadSkills(pluginPath, manifest)\n const hooks = loadHooks(pluginPath, manifest)\n const mcpServers = loadMCPServers(pluginPath, manifest)\n\n return {\n manifest,\n name: manifest.name,\n location: pluginPath,\n source,\n agents,\n commands,\n skills,\n hooks,\n mcpServers,\n enabled: pluginConfig.enabled,\n config: pluginConfig.config,\n }\n}\n\n/**\n * Load all plugins from all discovery directories\n */\nexport function loadAllPlugins(): LoadedPlugin[] {\n const pluginPaths = discoverPluginPaths()\n const plugins: LoadedPlugin[] = []\n\n for (const [name, path] of pluginPaths) {\n try {\n const plugin = loadPlugin(path)\n plugins.push(plugin)\n } catch (error) {\n if (error instanceof PluginError) {\n console.error(`Error loading plugin ${name}:`, error.message)\n } else {\n console.error(`Unexpected error loading plugin ${name}:`, error)\n }\n }\n }\n\n return plugins\n}\n\n/**\n * Get plugin by name\n */\nexport function getPlugin(name: string): LoadedPlugin | undefined {\n const pluginPaths = discoverPluginPaths()\n const pluginPath = pluginPaths.get(name)\n\n if (!pluginPath) return undefined\n\n try {\n return loadPlugin(pluginPath)\n } catch (error) {\n console.error(`Error loading plugin ${name}:`, error)\n return undefined\n }\n}\n\n/**\n * List all installed plugins\n */\nexport function listPlugins(): Array<{\n name: string\n path: string\n manifest?: PluginManifest\n}> {\n const pluginPaths = discoverPluginPaths()\n const plugins: Array<{\n name: string\n path: string\n manifest?: PluginManifest\n }> = []\n\n for (const [name, path] of pluginPaths) {\n try {\n const manifest = loadManifest(path)\n plugins.push({ name, path, manifest })\n } catch (error) {\n plugins.push({ name, path })\n }\n }\n\n return plugins\n}\n\n/**\n * Get plugin config file path\n */\nfunction getPluginConfigPath(pluginPath: string): string {\n return join(pluginPath, '.plugin-config.json')\n}\n\n/**\n * Load plugin configuration\n */\nfunction loadPluginConfig(pluginPath: string): {\n enabled: boolean\n config: Record<string, any>\n} {\n const configPath = getPluginConfigPath(pluginPath)\n\n if (!existsSync(configPath)) {\n return { enabled: true, config: {} }\n }\n\n try {\n const content = readFileSync(configPath, 'utf-8')\n const data = JSON.parse(content)\n return {\n enabled: data.enabled !== false,\n config: data.config || {},\n }\n } catch (error) {\n console.error(`Error loading plugin config from ${configPath}:`, error)\n return { enabled: true, config: {} }\n }\n}\n\n/**\n * Save plugin configuration\n */\nfunction savePluginConfig(\n pluginPath: string,\n enabled: boolean,\n config: Record<string, any>,\n): void {\n const configPath = getPluginConfigPath(pluginPath)\n\n try {\n const data = { enabled, config }\n writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8')\n } catch (error) {\n throw new PluginError(\n `Failed to save plugin config: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n undefined,\n error,\n )\n }\n}\n\n/**\n * Enable a plugin\n */\nexport function enablePlugin(pluginName: string): void {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n savePluginConfig(plugin.location, true, plugin.config || {})\n}\n\n/**\n * Disable a plugin\n */\nexport function disablePlugin(pluginName: string): void {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n savePluginConfig(plugin.location, false, plugin.config || {})\n}\n\n/**\n * Toggle plugin enabled state\n */\nexport function togglePluginEnabled(pluginName: string): boolean {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n const newState = !plugin.enabled\n savePluginConfig(plugin.location, newState, plugin.config || {})\n return newState\n}\n\n/**\n * Update plugin configuration\n */\nexport function updatePluginConfig(\n pluginName: string,\n config: Record<string, any>,\n): void {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n savePluginConfig(plugin.location, plugin.enabled, config)\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Plugin Loader\n *\n * Discovers and loads plugins from Minto plugin directories.\n *\n * Directory Priority (later overrides earlier):\n * 1. ~/.minto/plugins/ (user global)\n * 2. ./.minto/plugins/ (project - highest priority)\n */\n\nimport {\n existsSync,\n readFileSync,\n readdirSync,\n statSync,\n writeFileSync,\n mkdirSync,\n} from 'fs'\nimport { join, resolve, basename } from 'path'\nimport { homedir } from 'os'\nimport matter from 'gray-matter'\nimport {\n PluginManifest,\n PluginManifestSchema,\n LoadedPlugin,\n LoadedAgent,\n LoadedCommand,\n LoadedSkill,\n LoadedHook,\n LoadedMCPServer,\n PluginError,\n PluginErrorCode,\n PluginSource,\n} from '../types/plugin'\nimport { getCwd } from './state'\n\n/**\n * Plugin discovery directories in priority order\n */\nfunction getPluginDirectories(): string[] {\n const cwd = getCwd()\n const home = homedir()\n\n return [\n join(home, '.claude', 'plugins'), // User global (legacy)\n join(home, '.minto', 'plugins'), // User global\n join(cwd, '.claude', 'plugins'), // Project (legacy)\n join(cwd, '.minto', 'plugins'), // Project (highest priority)\n ]\n}\n\n/**\n * Find all plugin directories across all sources\n */\nfunction discoverPluginPaths(): Map<string, string> {\n const pluginPaths = new Map<string, string>() // name -> path\n const directories = getPluginDirectories()\n\n for (const dir of directories) {\n if (!existsSync(dir)) continue\n\n try {\n const entries = readdirSync(dir)\n\n for (const entry of entries) {\n const pluginPath = join(dir, entry)\n\n // Must be a directory\n if (!statSync(pluginPath).isDirectory()) continue\n\n // Must have plugin.json in .minto-plugin/, .claude-plugin/, or root\n const mintoManifestPath = join(\n pluginPath,\n '.minto-plugin',\n 'plugin.json',\n )\n const claudeManifestPath = join(\n pluginPath,\n '.claude-plugin',\n 'plugin.json',\n )\n const rootManifestPath = join(pluginPath, 'plugin.json')\n if (\n !existsSync(mintoManifestPath) &&\n !existsSync(claudeManifestPath) &&\n !existsSync(rootManifestPath)\n )\n continue\n\n // Later directories override earlier ones\n pluginPaths.set(entry, pluginPath)\n }\n } catch (error) {\n // Silently ignore errors\n }\n }\n\n return pluginPaths\n}\n\n/**\n * Load and validate plugin manifest\n *\n * Manifest locations (priority order):\n * 1. .minto-plugin/plugin.json\n * 2. plugin.json (root fallback)\n */\nfunction loadManifest(pluginPath: string): PluginManifest {\n const candidates = [\n join(pluginPath, '.minto-plugin', 'plugin.json'),\n join(pluginPath, '.claude-plugin', 'plugin.json'),\n join(pluginPath, 'plugin.json'),\n ]\n\n const existing = candidates.filter(p => existsSync(p))\n\n if (existing.length === 0) {\n throw new PluginError(\n `Plugin manifest not found. Tried:\\n${candidates.map(p => ` - ${p}`).join('\\n')}`,\n PluginErrorCode.MANIFEST_NOT_FOUND,\n )\n }\n\n let lastError: unknown = null\n\n // Try each candidate in priority order; fall through on validation failure\n for (const manifestPath of existing) {\n try {\n const manifestContent = readFileSync(manifestPath, 'utf-8')\n const manifestData = JSON.parse(manifestContent)\n return PluginManifestSchema.parse(manifestData)\n } catch (error) {\n lastError = error\n // Continue to next candidate\n }\n }\n\n // All candidates failed\n throw new PluginError(\n `Invalid plugin manifest at ${existing[0]}: ${lastError instanceof Error ? lastError.message : String(lastError)}`,\n PluginErrorCode.MANIFEST_INVALID,\n undefined,\n lastError,\n )\n}\n\n/**\n * Load agents from plugin\n */\nfunction loadAgents(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedAgent[] {\n const agents: LoadedAgent[] = []\n const agentsDir = join(pluginPath, 'agents')\n\n // Load agents listed in manifest\n for (const agentPath of manifest.agents || []) {\n const fullPath = join(pluginPath, agentPath)\n\n if (!existsSync(fullPath)) {\n console.warn(`Agent file not found: ${fullPath}`)\n continue\n }\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: promptContent } = matter(content)\n\n agents.push({\n name: data.name || basename(agentPath, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(agentPath, '.md'),\n description: data.description || '',\n tools: data.tools,\n model: data.model,\n content: promptContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading agent ${agentPath}:`, error)\n }\n }\n\n // Also scan agents/ directory if it exists\n if (existsSync(agentsDir) && statSync(agentsDir).isDirectory()) {\n const agentFiles = readdirSync(agentsDir).filter(f => f.endsWith('.md'))\n\n for (const file of agentFiles) {\n const fullPath = join(agentsDir, file)\n\n // Skip if already loaded from manifest\n if (agents.some(a => a.filePath === fullPath)) continue\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: promptContent } = matter(content)\n\n agents.push({\n name: data.name || basename(file, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(file, '.md'),\n description: data.description || '',\n tools: data.tools,\n model: data.model,\n content: promptContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading agent ${file}:`, error)\n }\n }\n }\n\n return agents\n}\n\n/**\n * Load commands from plugin\n */\nfunction loadCommands(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedCommand[] {\n const commands: LoadedCommand[] = []\n const commandsDir = join(pluginPath, 'commands')\n\n // Load commands listed in manifest\n for (const commandPath of manifest.commands || []) {\n const fullPath = join(pluginPath, commandPath)\n\n if (!existsSync(fullPath)) {\n console.warn(`Command file not found: ${fullPath}`)\n continue\n }\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: commandContent } = matter(content)\n\n commands.push({\n name: data.name || basename(commandPath, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(commandPath, '.md'),\n description: data.description,\n aliases: data.aliases,\n enabled: data.enabled !== false,\n hidden: data.hidden === true,\n progressMessage: data.progressMessage,\n argNames: data.argNames,\n 'allowed-tools': data['allowed-tools'],\n content: commandContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading command ${commandPath}:`, error)\n }\n }\n\n // Also scan commands/ directory if it exists\n if (existsSync(commandsDir) && statSync(commandsDir).isDirectory()) {\n const commandFiles = readdirSync(commandsDir).filter(f => f.endsWith('.md'))\n\n for (const file of commandFiles) {\n const fullPath = join(commandsDir, file)\n\n // Skip if already loaded from manifest\n if (commands.some(c => c.filePath === fullPath)) continue\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: commandContent } = matter(content)\n\n commands.push({\n name: data.name || basename(file, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(file, '.md'),\n description: data.description,\n aliases: data.aliases,\n enabled: data.enabled !== false,\n hidden: data.hidden === true,\n progressMessage: data.progressMessage,\n argNames: data.argNames,\n 'allowed-tools': data['allowed-tools'],\n content: commandContent.trim(),\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(`Error loading command ${file}:`, error)\n }\n }\n }\n\n return commands\n}\n\n/**\n * Load skills from plugin\n *\n * Supports two patterns:\n * - Subdirectory: skills/skill-name/SKILL.md\n * - Flat: skills/skill-name.md\n */\nfunction loadSkills(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedSkill[] {\n const skills: LoadedSkill[] = []\n const skillsDir = join(pluginPath, 'skills')\n\n // Load skills listed in manifest\n for (const skillPath of manifest.skills || []) {\n const fullPath = join(pluginPath, skillPath)\n\n if (!existsSync(fullPath)) {\n console.warn(`Skill file not found: ${fullPath}`)\n continue\n }\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: skillContent } = matter(content)\n\n skills.push({\n name: data.name || basename(skillPath, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(skillPath, '.md'),\n description: data.description || '',\n content: skillContent.trim(),\n },\n pluginName: manifest.name,\n source: 'plugin',\n })\n } catch (error) {\n console.error(`Error loading skill ${skillPath}:`, error)\n }\n }\n\n // Auto-discover skills from skills/ directory if it exists\n if (existsSync(skillsDir) && statSync(skillsDir).isDirectory()) {\n const entries = readdirSync(skillsDir, { withFileTypes: true })\n\n for (const entry of entries) {\n // Subdirectory pattern: skill-name/SKILL.md\n if (entry.isDirectory()) {\n const skillMdPath = join(skillsDir, entry.name, 'SKILL.md')\n\n if (existsSync(skillMdPath)) {\n // Skip if already loaded from manifest\n if (skills.some(s => s.filePath === skillMdPath)) continue\n\n try {\n const content = readFileSync(skillMdPath, 'utf-8')\n const { data, content: skillContent } = matter(content)\n\n skills.push({\n name: data.name || entry.name,\n filePath: skillMdPath,\n config: {\n name: data.name || entry.name,\n description: data.description || '',\n content: skillContent.trim(),\n },\n pluginName: manifest.name,\n source: 'plugin',\n })\n } catch (error) {\n console.error(`Error loading skill from ${skillMdPath}:`, error)\n }\n }\n }\n // Flat pattern: .md files directly in skills/\n else if (entry.isFile() && entry.name.endsWith('.md')) {\n const fullPath = join(skillsDir, entry.name)\n\n // Skip if already loaded from manifest\n if (skills.some(s => s.filePath === fullPath)) continue\n\n try {\n const content = readFileSync(fullPath, 'utf-8')\n const { data, content: skillContent } = matter(content)\n\n skills.push({\n name: data.name || basename(entry.name, '.md'),\n filePath: fullPath,\n config: {\n name: data.name || basename(entry.name, '.md'),\n description: data.description || '',\n content: skillContent.trim(),\n },\n pluginName: manifest.name,\n source: 'plugin',\n })\n } catch (error) {\n console.error(`Error loading skill ${entry.name}:`, error)\n }\n }\n }\n }\n\n return skills\n}\n\n/**\n * Load hooks from plugin\n *\n * Uses hooks/hooks.json format\n */\nfunction loadHooks(pluginPath: string, manifest: PluginManifest): LoadedHook[] {\n const hooks: LoadedHook[] = []\n const hooksDir = join(pluginPath, 'hooks')\n const hooksJsonPath = join(hooksDir, 'hooks.json')\n\n // Check if hooks.json exists\n if (!existsSync(hooksJsonPath)) {\n return hooks\n }\n\n try {\n const content = readFileSync(hooksJsonPath, 'utf-8')\n const hooksConfig = JSON.parse(content)\n\n // Validate structure (basic validation, full validation in hook executor)\n if (!hooksConfig.hooks || typeof hooksConfig.hooks !== 'object') {\n console.warn(`Invalid hooks.json in ${pluginPath}: missing \"hooks\" field`)\n return hooks\n }\n\n // Process each hook event\n for (const [eventName, matchers] of Object.entries(\n hooksConfig.hooks as Record<string, any[]>,\n )) {\n if (!Array.isArray(matchers)) continue\n\n for (const matcher of matchers) {\n if (!matcher.hooks || !Array.isArray(matcher.hooks)) continue\n\n for (const hookDef of matcher.hooks) {\n hooks.push({\n name: `${manifest.name}:${eventName}:${hooks.length}`,\n filePath: hooksJsonPath,\n config: {\n event: eventName as any,\n matcher: matcher.matcher,\n type: hookDef.type || 'command',\n command: hookDef.command,\n message: hookDef.prompt, // Claude Code uses \"prompt\", we use \"message\" internally\n blocking: hookDef.type === 'prompt',\n timeout: hookDef.timeout || 60,\n },\n pluginName: manifest.name,\n event: eventName as any,\n matcher: matcher.matcher,\n })\n }\n }\n }\n } catch (error) {\n console.error(`Error loading hooks from ${hooksJsonPath}:`, error)\n }\n\n return hooks\n}\n\n/**\n * Expand environment variables in a string\n * Supports:\n * - ${VAR} - expands to environment variable VAR\n * - ${VAR:-default} - expands to VAR or default if not set\n * - ${MINTO_PLUGIN_ROOT} - expands to plugin directory path\n */\nfunction expandEnvVars(value: string, pluginPath: string): string {\n // Replace ${MINTO_PLUGIN_ROOT} with plugin path\n let expanded = value.replace(/\\$\\{MINTO_PLUGIN_ROOT\\}/g, pluginPath)\n\n // Also support $MINTO_PLUGIN_ROOT without braces\n expanded = expanded.replace(/\\$MINTO_PLUGIN_ROOT(?![A-Za-z_])/g, pluginPath)\n\n // Support ${CLAUDE_PLUGIN_ROOT} for CC-synced plugins\n expanded = expanded.replace(/\\$\\{CLAUDE_PLUGIN_ROOT\\}/g, pluginPath)\n expanded = expanded.replace(/\\$CLAUDE_PLUGIN_ROOT(?![A-Za-z_])/g, pluginPath)\n\n // Replace ${VAR} or ${VAR:-default} for other environment variables\n expanded = expanded.replace(\n /\\$\\{([^}:]+)(?::-([^}]*))?\\}/g,\n (match, varName, defaultValue) => {\n // Skip already processed plugin root variables\n if (varName === 'MINTO_PLUGIN_ROOT' || varName === 'CLAUDE_PLUGIN_ROOT') {\n return match\n }\n return process.env[varName] || defaultValue || ''\n },\n )\n\n return expanded\n}\n\n/**\n * Expand environment variables in MCP server configuration\n */\nfunction expandServerConfig(config: any, pluginPath: string): any {\n const expanded: any = { ...config }\n\n // Expand string fields\n if (expanded.command) {\n expanded.command = expandEnvVars(expanded.command, pluginPath)\n }\n\n if (expanded.url) {\n expanded.url = expandEnvVars(expanded.url, pluginPath)\n }\n\n // Expand args array\n if (expanded.args && Array.isArray(expanded.args)) {\n expanded.args = expanded.args.map((arg: string) =>\n expandEnvVars(arg, pluginPath),\n )\n }\n\n // Expand env object\n if (expanded.env && typeof expanded.env === 'object') {\n expanded.env = Object.fromEntries(\n Object.entries(expanded.env).map(([k, v]) => [\n k,\n expandEnvVars(String(v), pluginPath),\n ]),\n )\n }\n\n // Expand headers object\n if (expanded.headers && typeof expanded.headers === 'object') {\n expanded.headers = Object.fromEntries(\n Object.entries(expanded.headers).map(([k, v]) => [\n k,\n expandEnvVars(String(v), pluginPath),\n ]),\n )\n }\n\n return expanded\n}\n\n/**\n * Load MCP servers from plugin\n *\n * Two configuration methods:\n * 1. .mcp.json file at plugin root\n * 2. Inline mcpServers in plugin.json manifest\n *\n * Inline configurations override .mcp.json for same server name.\n */\nfunction loadMCPServers(\n pluginPath: string,\n manifest: PluginManifest,\n): LoadedMCPServer[] {\n const mcpServers: LoadedMCPServer[] = []\n\n // Step 1: Load .mcp.json if it exists\n const mcpJsonPath = join(pluginPath, '.mcp.json')\n let mcpJsonServers: Record<string, any> = {}\n\n if (existsSync(mcpJsonPath)) {\n try {\n const content = readFileSync(mcpJsonPath, 'utf-8')\n const parsed = JSON.parse(content)\n\n if (parsed.mcpServers && typeof parsed.mcpServers === 'object') {\n mcpJsonServers = parsed.mcpServers\n }\n } catch (error) {\n console.error(`Error loading .mcp.json from ${pluginPath}:`, error)\n }\n }\n\n // Step 2: Get inline MCP servers from manifest\n let inlineServers: Record<string, any> = {}\n\n if (\n manifest.mcpServers &&\n typeof manifest.mcpServers === 'object' &&\n !Array.isArray(manifest.mcpServers)\n ) {\n inlineServers = manifest.mcpServers as Record<string, any>\n }\n\n // Step 3: Merge (inline overrides .mcp.json)\n const allServers = { ...mcpJsonServers, ...inlineServers }\n\n // Step 4: Convert to LoadedMCPServer format\n for (const [name, config] of Object.entries(allServers)) {\n try {\n // Expand environment variables\n const expandedConfig = expandServerConfig(config, pluginPath)\n\n // Validate required fields based on server type\n const serverType = expandedConfig.type || 'stdio'\n\n if (serverType === 'stdio' && !expandedConfig.command) {\n console.warn(\n `MCP server \"${name}\" in ${manifest.name} is missing required \"command\" field for stdio type`,\n )\n continue\n }\n\n if (\n (serverType === 'http' || serverType === 'sse') &&\n !expandedConfig.url\n ) {\n console.warn(\n `MCP server \"${name}\" in ${manifest.name} is missing required \"url\" field for ${serverType} type`,\n )\n continue\n }\n\n mcpServers.push({\n name,\n filePath: existsSync(mcpJsonPath)\n ? mcpJsonPath\n : join(pluginPath, 'plugin.json'),\n config: {\n command: expandedConfig.command || '',\n args: expandedConfig.args || [],\n env: expandedConfig.env || {},\n timeout: expandedConfig.timeout,\n },\n pluginName: manifest.name,\n })\n } catch (error) {\n console.error(\n `Error loading MCP server \"${name}\" from ${manifest.name}:`,\n error,\n )\n }\n }\n\n return mcpServers\n}\n\n/**\n * Determine plugin source from path\n * Checks for marketplace metadata file to identify marketplace-installed plugins\n */\nfunction determinePluginSource(pluginPath: string): PluginSource {\n const home = homedir()\n const cwd = getCwd()\n\n // Check for CC sync metadata (Claude Code synced plugins)\n const ccSyncPath = join(pluginPath, '.cc-sync.json')\n if (existsSync(ccSyncPath)) {\n try {\n const ccMeta = JSON.parse(readFileSync(ccSyncPath, 'utf-8'))\n return {\n type: 'claude-code',\n marketplace: ccMeta.marketplace || '',\n name: ccMeta.name || basename(pluginPath),\n }\n } catch {\n // Fall through to other detection methods\n }\n }\n\n // Check for marketplace metadata file\n const marketplaceMetaPath = join(pluginPath, '.marketplace-meta.json')\n if (existsSync(marketplaceMetaPath)) {\n try {\n const metaContent = readFileSync(marketplaceMetaPath, 'utf-8')\n const meta = JSON.parse(metaContent)\n if (meta.marketplace && meta.plugin) {\n return {\n type: 'marketplace',\n marketplace: meta.marketplace,\n name: meta.plugin,\n }\n }\n } catch (error) {\n // If metadata file is corrupted, fall through to local detection\n console.warn(\n `Failed to read marketplace metadata from ${marketplaceMetaPath}:`,\n error,\n )\n }\n }\n\n // Determine local source based on path\n if (pluginPath.startsWith(join(home, '.minto', 'plugins'))) {\n return { type: 'local', path: 'user-global' }\n } else if (pluginPath.startsWith(join(cwd, '.minto', 'plugins'))) {\n return { type: 'local', path: 'project' }\n } else {\n return { type: 'local', path: pluginPath }\n }\n}\n\n/**\n * Load a single plugin from a directory\n */\nexport function loadPlugin(pluginPath: string): LoadedPlugin {\n const manifest = loadManifest(pluginPath)\n const source = determinePluginSource(pluginPath)\n const pluginConfig = loadPluginConfig(pluginPath)\n\n const agents = loadAgents(pluginPath, manifest)\n const commands = loadCommands(pluginPath, manifest)\n const skills = loadSkills(pluginPath, manifest)\n const hooks = loadHooks(pluginPath, manifest)\n const mcpServers = loadMCPServers(pluginPath, manifest)\n\n return {\n manifest,\n name: manifest.name,\n location: pluginPath,\n source,\n agents,\n commands,\n skills,\n hooks,\n mcpServers,\n enabled: pluginConfig.enabled,\n config: pluginConfig.config,\n }\n}\n\n/**\n * Load all plugins from all discovery directories\n */\nexport function loadAllPlugins(): LoadedPlugin[] {\n const pluginPaths = discoverPluginPaths()\n const plugins: LoadedPlugin[] = []\n\n for (const [name, path] of pluginPaths) {\n try {\n const plugin = loadPlugin(path)\n plugins.push(plugin)\n } catch (error) {\n if (error instanceof PluginError) {\n console.error(`Error loading plugin ${name}:`, error.message)\n } else {\n console.error(`Unexpected error loading plugin ${name}:`, error)\n }\n }\n }\n\n return plugins\n}\n\n/**\n * Get plugin by name\n */\nexport function getPlugin(name: string): LoadedPlugin | undefined {\n const pluginPaths = discoverPluginPaths()\n const pluginPath = pluginPaths.get(name)\n\n if (!pluginPath) return undefined\n\n try {\n return loadPlugin(pluginPath)\n } catch (error) {\n console.error(`Error loading plugin ${name}:`, error)\n return undefined\n }\n}\n\n/**\n * List all installed plugins\n */\nexport function listPlugins(): Array<{\n name: string\n path: string\n manifest?: PluginManifest\n}> {\n const pluginPaths = discoverPluginPaths()\n const plugins: Array<{\n name: string\n path: string\n manifest?: PluginManifest\n }> = []\n\n for (const [name, path] of pluginPaths) {\n try {\n const manifest = loadManifest(path)\n plugins.push({ name, path, manifest })\n } catch (error) {\n plugins.push({ name, path })\n }\n }\n\n return plugins\n}\n\n/**\n * Get plugin config file path\n */\nfunction getPluginConfigPath(pluginPath: string): string {\n return join(pluginPath, '.plugin-config.json')\n}\n\n/**\n * Load plugin configuration\n */\nfunction loadPluginConfig(pluginPath: string): {\n enabled: boolean\n config: Record<string, any>\n} {\n const configPath = getPluginConfigPath(pluginPath)\n\n if (!existsSync(configPath)) {\n return { enabled: true, config: {} }\n }\n\n try {\n const content = readFileSync(configPath, 'utf-8')\n const data = JSON.parse(content)\n return {\n enabled: data.enabled !== false,\n config: data.config || {},\n }\n } catch (error) {\n console.error(`Error loading plugin config from ${configPath}:`, error)\n return { enabled: true, config: {} }\n }\n}\n\n/**\n * Save plugin configuration\n */\nfunction savePluginConfig(\n pluginPath: string,\n enabled: boolean,\n config: Record<string, any>,\n): void {\n const configPath = getPluginConfigPath(pluginPath)\n\n try {\n const data = { enabled, config }\n writeFileSync(configPath, JSON.stringify(data, null, 2), 'utf-8')\n } catch (error) {\n throw new PluginError(\n `Failed to save plugin config: ${error instanceof Error ? error.message : String(error)}`,\n PluginErrorCode.PERMISSION_DENIED,\n undefined,\n error,\n )\n }\n}\n\n/**\n * Enable a plugin\n */\nexport function enablePlugin(pluginName: string): void {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n savePluginConfig(plugin.location, true, plugin.config || {})\n}\n\n/**\n * Disable a plugin\n */\nexport function disablePlugin(pluginName: string): void {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n savePluginConfig(plugin.location, false, plugin.config || {})\n}\n\n/**\n * Toggle plugin enabled state\n */\nexport function togglePluginEnabled(pluginName: string): boolean {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n const newState = !plugin.enabled\n savePluginConfig(plugin.location, newState, plugin.config || {})\n return newState\n}\n\n/**\n * Update plugin configuration\n */\nexport function updatePluginConfig(\n pluginName: string,\n config: Record<string, any>,\n): void {\n const plugin = getPlugin(pluginName)\n\n if (!plugin) {\n throw new PluginError(\n `Plugin \"${pluginName}\" not found`,\n PluginErrorCode.NOT_INSTALLED,\n pluginName,\n )\n }\n\n savePluginConfig(plugin.location, plugin.enabled, config)\n}\n"],
|
|
5
|
+
"mappings": "AAUA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,MAAe,gBAAgB;AACxC,SAAS,eAAe;AACxB,OAAO,YAAY;AACnB;AAAA,EAEE;AAAA,EAOA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,cAAc;AAKvB,SAAS,uBAAiC;AACxC,QAAM,MAAM,OAAO;AACnB,QAAM,OAAO,QAAQ;AAErB,SAAO;AAAA,IACL,KAAK,MAAM,WAAW,SAAS;AAAA;AAAA,IAC/B,KAAK,MAAM,UAAU,SAAS;AAAA;AAAA,IAC9B,KAAK,KAAK,WAAW,SAAS;AAAA;AAAA,IAC9B,KAAK,KAAK,UAAU,SAAS;AAAA;AAAA,EAC/B;AACF;AAKA,SAAS,sBAA2C;AAClD,QAAM,cAAc,oBAAI,IAAoB;AAC5C,QAAM,cAAc,qBAAqB;AAEzC,aAAW,OAAO,aAAa;AAC7B,QAAI,CAAC,WAAW,GAAG,EAAG;AAEtB,QAAI;AACF,YAAM,UAAU,YAAY,GAAG;AAE/B,iBAAW,SAAS,SAAS;AAC3B,cAAM,aAAa,KAAK,KAAK,KAAK;AAGlC,YAAI,CAAC,SAAS,UAAU,EAAE,YAAY,EAAG;AAGzC,cAAM,oBAAoB;AAAA,UACxB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,cAAM,qBAAqB;AAAA,UACzB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,cAAM,mBAAmB,KAAK,YAAY,aAAa;AACvD,YACE,CAAC,WAAW,iBAAiB,KAC7B,CAAC,WAAW,kBAAkB,KAC9B,CAAC,WAAW,gBAAgB;AAE5B;AAGF,oBAAY,IAAI,OAAO,UAAU;AAAA,MACnC;AAAA,IACF,SAAS,OAAO;AAAA,IAEhB;AAAA,EACF;AAEA,SAAO;AACT;AASA,SAAS,aAAa,YAAoC;AACxD,QAAM,aAAa;AAAA,IACjB,KAAK,YAAY,iBAAiB,aAAa;AAAA,IAC/C,KAAK,YAAY,kBAAkB,aAAa;AAAA,IAChD,KAAK,YAAY,aAAa;AAAA,EAChC;AAEA,QAAM,WAAW,WAAW,OAAO,OAAK,WAAW,CAAC,CAAC;AAErD,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI;AAAA,MACR;AAAA,EAAsC,WAAW,IAAI,OAAK,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,MAChF,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,MAAI,YAAqB;AAGzB,aAAW,gBAAgB,UAAU;AACnC,QAAI;AACF,YAAM,kBAAkB,aAAa,cAAc,OAAO;AAC1D,YAAM,eAAe,KAAK,MAAM,eAAe;AAC/C,aAAO,qBAAqB,MAAM,YAAY;AAAA,IAChD,SAAS,OAAO;AACd,kBAAY;AAAA,IAEd;AAAA,EACF;AAGA,QAAM,IAAI;AAAA,IACR,8BAA8B,SAAS,CAAC,CAAC,KAAK,qBAAqB,QAAQ,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,IAChH,gBAAgB;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,WACP,YACA,UACe;AACf,QAAM,SAAwB,CAAC;AAC/B,QAAM,YAAY,KAAK,YAAY,QAAQ;AAG3C,aAAW,aAAa,SAAS,UAAU,CAAC,GAAG;AAC7C,UAAM,WAAW,KAAK,YAAY,SAAS;AAE3C,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,cAAQ,KAAK,yBAAyB,QAAQ,EAAE;AAChD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,YAAM,EAAE,MAAM,SAAS,cAAc,IAAI,OAAO,OAAO;AAEvD,aAAO,KAAK;AAAA,QACV,MAAM,KAAK,QAAQ,SAAS,WAAW,KAAK;AAAA,QAC5C,UAAU;AAAA,QACV,QAAQ;AAAA,UACN,MAAM,KAAK,QAAQ,SAAS,WAAW,KAAK;AAAA,UAC5C,aAAa,KAAK,eAAe;AAAA,UACjC,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK;AAAA,UACZ,SAAS,cAAc,KAAK;AAAA,QAC9B;AAAA,QACA,YAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,uBAAuB,SAAS,KAAK,KAAK;AAAA,IAC1D;AAAA,EACF;AAGA,MAAI,WAAW,SAAS,KAAK,SAAS,SAAS,EAAE,YAAY,GAAG;AAC9D,UAAM,aAAa,YAAY,SAAS,EAAE,OAAO,OAAK,EAAE,SAAS,KAAK,CAAC;AAEvE,eAAW,QAAQ,YAAY;AAC7B,YAAM,WAAW,KAAK,WAAW,IAAI;AAGrC,UAAI,OAAO,KAAK,OAAK,EAAE,aAAa,QAAQ,EAAG;AAE/C,UAAI;AACF,cAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,cAAM,EAAE,MAAM,SAAS,cAAc,IAAI,OAAO,OAAO;AAEvD,eAAO,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,SAAS,MAAM,KAAK;AAAA,UACvC,UAAU;AAAA,UACV,QAAQ;AAAA,YACN,MAAM,KAAK,QAAQ,SAAS,MAAM,KAAK;AAAA,YACvC,aAAa,KAAK,eAAe;AAAA,YACjC,OAAO,KAAK;AAAA,YACZ,OAAO,KAAK;AAAA,YACZ,SAAS,cAAc,KAAK;AAAA,UAC9B;AAAA,UACA,YAAY,SAAS;AAAA,QACvB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,uBAAuB,IAAI,KAAK,KAAK;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,aACP,YACA,UACiB;AACjB,QAAM,WAA4B,CAAC;AACnC,QAAM,cAAc,KAAK,YAAY,UAAU;AAG/C,aAAW,eAAe,SAAS,YAAY,CAAC,GAAG;AACjD,UAAM,WAAW,KAAK,YAAY,WAAW;AAE7C,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,cAAQ,KAAK,2BAA2B,QAAQ,EAAE;AAClD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,YAAM,EAAE,MAAM,SAAS,eAAe,IAAI,OAAO,OAAO;AAExD,eAAS,KAAK;AAAA,QACZ,MAAM,KAAK,QAAQ,SAAS,aAAa,KAAK;AAAA,QAC9C,UAAU;AAAA,QACV,QAAQ;AAAA,UACN,MAAM,KAAK,QAAQ,SAAS,aAAa,KAAK;AAAA,UAC9C,aAAa,KAAK;AAAA,UAClB,SAAS,KAAK;AAAA,UACd,SAAS,KAAK,YAAY;AAAA,UAC1B,QAAQ,KAAK,WAAW;AAAA,UACxB,iBAAiB,KAAK;AAAA,UACtB,UAAU,KAAK;AAAA,UACf,iBAAiB,KAAK,eAAe;AAAA,UACrC,SAAS,eAAe,KAAK;AAAA,QAC/B;AAAA,QACA,YAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,yBAAyB,WAAW,KAAK,KAAK;AAAA,IAC9D;AAAA,EACF;AAGA,MAAI,WAAW,WAAW,KAAK,SAAS,WAAW,EAAE,YAAY,GAAG;AAClE,UAAM,eAAe,YAAY,WAAW,EAAE,OAAO,OAAK,EAAE,SAAS,KAAK,CAAC;AAE3E,eAAW,QAAQ,cAAc;AAC/B,YAAM,WAAW,KAAK,aAAa,IAAI;AAGvC,UAAI,SAAS,KAAK,OAAK,EAAE,aAAa,QAAQ,EAAG;AAEjD,UAAI;AACF,cAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,cAAM,EAAE,MAAM,SAAS,eAAe,IAAI,OAAO,OAAO;AAExD,iBAAS,KAAK;AAAA,UACZ,MAAM,KAAK,QAAQ,SAAS,MAAM,KAAK;AAAA,UACvC,UAAU;AAAA,UACV,QAAQ;AAAA,YACN,MAAM,KAAK,QAAQ,SAAS,MAAM,KAAK;AAAA,YACvC,aAAa,KAAK;AAAA,YAClB,SAAS,KAAK;AAAA,YACd,SAAS,KAAK,YAAY;AAAA,YAC1B,QAAQ,KAAK,WAAW;AAAA,YACxB,iBAAiB,KAAK;AAAA,YACtB,UAAU,KAAK;AAAA,YACf,iBAAiB,KAAK,eAAe;AAAA,YACrC,SAAS,eAAe,KAAK;AAAA,UAC/B;AAAA,UACA,YAAY,SAAS;AAAA,QACvB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,yBAAyB,IAAI,KAAK,KAAK;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AASA,SAAS,WACP,YACA,UACe;AACf,QAAM,SAAwB,CAAC;AAC/B,QAAM,YAAY,KAAK,YAAY,QAAQ;AAG3C,aAAW,aAAa,SAAS,UAAU,CAAC,GAAG;AAC7C,UAAM,WAAW,KAAK,YAAY,SAAS;AAE3C,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,cAAQ,KAAK,yBAAyB,QAAQ,EAAE;AAChD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,YAAM,EAAE,MAAM,SAAS,aAAa,IAAI,OAAO,OAAO;AAEtD,aAAO,KAAK;AAAA,QACV,MAAM,KAAK,QAAQ,SAAS,WAAW,KAAK;AAAA,QAC5C,UAAU;AAAA,QACV,QAAQ;AAAA,UACN,MAAM,KAAK,QAAQ,SAAS,WAAW,KAAK;AAAA,UAC5C,aAAa,KAAK,eAAe;AAAA,UACjC,SAAS,aAAa,KAAK;AAAA,QAC7B;AAAA,QACA,YAAY,SAAS;AAAA,QACrB,QAAQ;AAAA,MACV,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,uBAAuB,SAAS,KAAK,KAAK;AAAA,IAC1D;AAAA,EACF;AAGA,MAAI,WAAW,SAAS,KAAK,SAAS,SAAS,EAAE,YAAY,GAAG;AAC9D,UAAM,UAAU,YAAY,WAAW,EAAE,eAAe,KAAK,CAAC;AAE9D,eAAW,SAAS,SAAS;AAE3B,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,cAAc,KAAK,WAAW,MAAM,MAAM,UAAU;AAE1D,YAAI,WAAW,WAAW,GAAG;AAE3B,cAAI,OAAO,KAAK,OAAK,EAAE,aAAa,WAAW,EAAG;AAElD,cAAI;AACF,kBAAM,UAAU,aAAa,aAAa,OAAO;AACjD,kBAAM,EAAE,MAAM,SAAS,aAAa,IAAI,OAAO,OAAO;AAEtD,mBAAO,KAAK;AAAA,cACV,MAAM,KAAK,QAAQ,MAAM;AAAA,cACzB,UAAU;AAAA,cACV,QAAQ;AAAA,gBACN,MAAM,KAAK,QAAQ,MAAM;AAAA,gBACzB,aAAa,KAAK,eAAe;AAAA,gBACjC,SAAS,aAAa,KAAK;AAAA,cAC7B;AAAA,cACA,YAAY,SAAS;AAAA,cACrB,QAAQ;AAAA,YACV,CAAC;AAAA,UACH,SAAS,OAAO;AACd,oBAAQ,MAAM,4BAA4B,WAAW,KAAK,KAAK;AAAA,UACjE;AAAA,QACF;AAAA,MACF,WAES,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,GAAG;AACrD,cAAM,WAAW,KAAK,WAAW,MAAM,IAAI;AAG3C,YAAI,OAAO,KAAK,OAAK,EAAE,aAAa,QAAQ,EAAG;AAE/C,YAAI;AACF,gBAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,gBAAM,EAAE,MAAM,SAAS,aAAa,IAAI,OAAO,OAAO;AAEtD,iBAAO,KAAK;AAAA,YACV,MAAM,KAAK,QAAQ,SAAS,MAAM,MAAM,KAAK;AAAA,YAC7C,UAAU;AAAA,YACV,QAAQ;AAAA,cACN,MAAM,KAAK,QAAQ,SAAS,MAAM,MAAM,KAAK;AAAA,cAC7C,aAAa,KAAK,eAAe;AAAA,cACjC,SAAS,aAAa,KAAK;AAAA,YAC7B;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,QAAQ;AAAA,UACV,CAAC;AAAA,QACH,SAAS,OAAO;AACd,kBAAQ,MAAM,uBAAuB,MAAM,IAAI,KAAK,KAAK;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOA,SAAS,UAAU,YAAoB,UAAwC;AAC7E,QAAM,QAAsB,CAAC;AAC7B,QAAM,WAAW,KAAK,YAAY,OAAO;AACzC,QAAM,gBAAgB,KAAK,UAAU,YAAY;AAGjD,MAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,eAAe,OAAO;AACnD,UAAM,cAAc,KAAK,MAAM,OAAO;AAGtC,QAAI,CAAC,YAAY,SAAS,OAAO,YAAY,UAAU,UAAU;AAC/D,cAAQ,KAAK,yBAAyB,UAAU,yBAAyB;AACzE,aAAO;AAAA,IACT;AAGA,eAAW,CAAC,WAAW,QAAQ,KAAK,OAAO;AAAA,MACzC,YAAY;AAAA,IACd,GAAG;AACD,UAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAE9B,iBAAW,WAAW,UAAU;AAC9B,YAAI,CAAC,QAAQ,SAAS,CAAC,MAAM,QAAQ,QAAQ,KAAK,EAAG;AAErD,mBAAW,WAAW,QAAQ,OAAO;AACnC,gBAAM,KAAK;AAAA,YACT,MAAM,GAAG,SAAS,IAAI,IAAI,SAAS,IAAI,MAAM,MAAM;AAAA,YACnD,UAAU;AAAA,YACV,QAAQ;AAAA,cACN,OAAO;AAAA,cACP,SAAS,QAAQ;AAAA,cACjB,MAAM,QAAQ,QAAQ;AAAA,cACtB,SAAS,QAAQ;AAAA,cACjB,SAAS,QAAQ;AAAA;AAAA,cACjB,UAAU,QAAQ,SAAS;AAAA,cAC3B,SAAS,QAAQ,WAAW;AAAA,YAC9B;AAAA,YACA,YAAY,SAAS;AAAA,YACrB,OAAO;AAAA,YACP,SAAS,QAAQ;AAAA,UACnB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,aAAa,KAAK,KAAK;AAAA,EACnE;AAEA,SAAO;AACT;AASA,SAAS,cAAc,OAAe,YAA4B;AAEhE,MAAI,WAAW,MAAM,QAAQ,4BAA4B,UAAU;AAGnE,aAAW,SAAS,QAAQ,qCAAqC,UAAU;AAG3E,aAAW,SAAS,QAAQ,6BAA6B,UAAU;AACnE,aAAW,SAAS,QAAQ,sCAAsC,UAAU;AAG5E,aAAW,SAAS;AAAA,IAClB;AAAA,IACA,CAAC,OAAO,SAAS,iBAAiB;AAEhC,UAAI,YAAY,uBAAuB,YAAY,sBAAsB;AACvE,eAAO;AAAA,MACT;AACA,aAAO,QAAQ,IAAI,OAAO,KAAK,gBAAgB;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,mBAAmB,QAAa,YAAyB;AAChE,QAAM,WAAgB,EAAE,GAAG,OAAO;AAGlC,MAAI,SAAS,SAAS;AACpB,aAAS,UAAU,cAAc,SAAS,SAAS,UAAU;AAAA,EAC/D;AAEA,MAAI,SAAS,KAAK;AAChB,aAAS,MAAM,cAAc,SAAS,KAAK,UAAU;AAAA,EACvD;AAGA,MAAI,SAAS,QAAQ,MAAM,QAAQ,SAAS,IAAI,GAAG;AACjD,aAAS,OAAO,SAAS,KAAK;AAAA,MAAI,CAAC,QACjC,cAAc,KAAK,UAAU;AAAA,IAC/B;AAAA,EACF;AAGA,MAAI,SAAS,OAAO,OAAO,SAAS,QAAQ,UAAU;AACpD,aAAS,MAAM,OAAO;AAAA,MACpB,OAAO,QAAQ,SAAS,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM;AAAA,QAC3C;AAAA,QACA,cAAc,OAAO,CAAC,GAAG,UAAU;AAAA,MACrC,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,SAAS,WAAW,OAAO,SAAS,YAAY,UAAU;AAC5D,aAAS,UAAU,OAAO;AAAA,MACxB,OAAO,QAAQ,SAAS,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM;AAAA,QAC/C;AAAA,QACA,cAAc,OAAO,CAAC,GAAG,UAAU;AAAA,MACrC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAWA,SAAS,eACP,YACA,UACmB;AACnB,QAAM,aAAgC,CAAC;AAGvC,QAAM,cAAc,KAAK,YAAY,WAAW;AAChD,MAAI,iBAAsC,CAAC;AAE3C,MAAI,WAAW,WAAW,GAAG;AAC3B,QAAI;AACF,YAAM,UAAU,aAAa,aAAa,OAAO;AACjD,YAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,UAAI,OAAO,cAAc,OAAO,OAAO,eAAe,UAAU;AAC9D,yBAAiB,OAAO;AAAA,MAC1B;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,gCAAgC,UAAU,KAAK,KAAK;AAAA,IACpE;AAAA,EACF;AAGA,MAAI,gBAAqC,CAAC;AAE1C,MACE,SAAS,cACT,OAAO,SAAS,eAAe,YAC/B,CAAC,MAAM,QAAQ,SAAS,UAAU,GAClC;AACA,oBAAgB,SAAS;AAAA,EAC3B;AAGA,QAAM,aAAa,EAAE,GAAG,gBAAgB,GAAG,cAAc;AAGzD,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACvD,QAAI;AAEF,YAAM,iBAAiB,mBAAmB,QAAQ,UAAU;AAG5D,YAAM,aAAa,eAAe,QAAQ;AAE1C,UAAI,eAAe,WAAW,CAAC,eAAe,SAAS;AACrD,gBAAQ;AAAA,UACN,eAAe,IAAI,QAAQ,SAAS,IAAI;AAAA,QAC1C;AACA;AAAA,MACF;AAEA,WACG,eAAe,UAAU,eAAe,UACzC,CAAC,eAAe,KAChB;AACA,gBAAQ;AAAA,UACN,eAAe,IAAI,QAAQ,SAAS,IAAI,wCAAwC,UAAU;AAAA,QAC5F;AACA;AAAA,MACF;AAEA,iBAAW,KAAK;AAAA,QACd;AAAA,QACA,UAAU,WAAW,WAAW,IAC5B,cACA,KAAK,YAAY,aAAa;AAAA,QAClC,QAAQ;AAAA,UACN,SAAS,eAAe,WAAW;AAAA,UACnC,MAAM,eAAe,QAAQ,CAAC;AAAA,UAC9B,KAAK,eAAe,OAAO,CAAC;AAAA,UAC5B,SAAS,eAAe;AAAA,QAC1B;AAAA,QACA,YAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ;AAAA,QACN,6BAA6B,IAAI,UAAU,SAAS,IAAI;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,SAAS,sBAAsB,YAAkC;AAC/D,QAAM,OAAO,QAAQ;AACrB,QAAM,MAAM,OAAO;AAGnB,QAAM,aAAa,KAAK,YAAY,eAAe;AACnD,MAAI,WAAW,UAAU,GAAG;AAC1B,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AAC3D,aAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa,OAAO,eAAe;AAAA,QACnC,MAAM,OAAO,QAAQ,SAAS,UAAU;AAAA,MAC1C;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,sBAAsB,KAAK,YAAY,wBAAwB;AACrE,MAAI,WAAW,mBAAmB,GAAG;AACnC,QAAI;AACF,YAAM,cAAc,aAAa,qBAAqB,OAAO;AAC7D,YAAM,OAAO,KAAK,MAAM,WAAW;AACnC,UAAI,KAAK,eAAe,KAAK,QAAQ;AACnC,eAAO;AAAA,UACL,MAAM;AAAA,UACN,aAAa,KAAK;AAAA,UAClB,MAAM,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAEd,cAAQ;AAAA,QACN,4CAA4C,mBAAmB;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,MAAI,WAAW,WAAW,KAAK,MAAM,UAAU,SAAS,CAAC,GAAG;AAC1D,WAAO,EAAE,MAAM,SAAS,MAAM,cAAc;AAAA,EAC9C,WAAW,WAAW,WAAW,KAAK,KAAK,UAAU,SAAS,CAAC,GAAG;AAChE,WAAO,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,EAC1C,OAAO;AACL,WAAO,EAAE,MAAM,SAAS,MAAM,WAAW;AAAA,EAC3C;AACF;AAKO,SAAS,WAAW,YAAkC;AAC3D,QAAM,WAAW,aAAa,UAAU;AACxC,QAAM,SAAS,sBAAsB,UAAU;AAC/C,QAAM,eAAe,iBAAiB,UAAU;AAEhD,QAAM,SAAS,WAAW,YAAY,QAAQ;AAC9C,QAAM,WAAW,aAAa,YAAY,QAAQ;AAClD,QAAM,SAAS,WAAW,YAAY,QAAQ;AAC9C,QAAM,QAAQ,UAAU,YAAY,QAAQ;AAC5C,QAAM,aAAa,eAAe,YAAY,QAAQ;AAEtD,SAAO;AAAA,IACL;AAAA,IACA,MAAM,SAAS;AAAA,IACf,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,aAAa;AAAA,IACtB,QAAQ,aAAa;AAAA,EACvB;AACF;AAKO,SAAS,iBAAiC;AAC/C,QAAM,cAAc,oBAAoB;AACxC,QAAM,UAA0B,CAAC;AAEjC,aAAW,CAAC,MAAM,IAAI,KAAK,aAAa;AACtC,QAAI;AACF,YAAM,SAAS,WAAW,IAAI;AAC9B,cAAQ,KAAK,MAAM;AAAA,IACrB,SAAS,OAAO;AACd,UAAI,iBAAiB,aAAa;AAChC,gBAAQ,MAAM,wBAAwB,IAAI,KAAK,MAAM,OAAO;AAAA,MAC9D,OAAO;AACL,gBAAQ,MAAM,mCAAmC,IAAI,KAAK,KAAK;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,UAAU,MAAwC;AAChE,QAAM,cAAc,oBAAoB;AACxC,QAAM,aAAa,YAAY,IAAI,IAAI;AAEvC,MAAI,CAAC,WAAY,QAAO;AAExB,MAAI;AACF,WAAO,WAAW,UAAU;AAAA,EAC9B,SAAS,OAAO;AACd,YAAQ,MAAM,wBAAwB,IAAI,KAAK,KAAK;AACpD,WAAO;AAAA,EACT;AACF;AAKO,SAAS,cAIb;AACD,QAAM,cAAc,oBAAoB;AACxC,QAAM,UAID,CAAC;AAEN,aAAW,CAAC,MAAM,IAAI,KAAK,aAAa;AACtC,QAAI;AACF,YAAM,WAAW,aAAa,IAAI;AAClC,cAAQ,KAAK,EAAE,MAAM,MAAM,SAAS,CAAC;AAAA,IACvC,SAAS,OAAO;AACd,cAAQ,KAAK,EAAE,MAAM,KAAK,CAAC;AAAA,IAC7B;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBAAoB,YAA4B;AACvD,SAAO,KAAK,YAAY,qBAAqB;AAC/C;AAKA,SAAS,iBAAiB,YAGxB;AACA,QAAM,aAAa,oBAAoB,UAAU;AAEjD,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAO,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;AAAA,EACrC;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,YAAY,OAAO;AAChD,UAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,WAAO;AAAA,MACL,SAAS,KAAK,YAAY;AAAA,MAC1B,QAAQ,KAAK,UAAU,CAAC;AAAA,IAC1B;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,oCAAoC,UAAU,KAAK,KAAK;AACtE,WAAO,EAAE,SAAS,MAAM,QAAQ,CAAC,EAAE;AAAA,EACrC;AACF;AAKA,SAAS,iBACP,YACA,SACA,QACM;AACN,QAAM,aAAa,oBAAoB,UAAU;AAEjD,MAAI;AACF,UAAM,OAAO,EAAE,SAAS,OAAO;AAC/B,kBAAc,YAAY,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,OAAO;AAAA,EAClE,SAAS,OAAO;AACd,UAAM,IAAI;AAAA,MACR,iCAAiC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MACvF,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAKO,SAAS,aAAa,YAA0B;AACrD,QAAM,SAAS,UAAU,UAAU;AAEnC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,WAAW,UAAU;AAAA,MACrB,gBAAgB;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,mBAAiB,OAAO,UAAU,MAAM,OAAO,UAAU,CAAC,CAAC;AAC7D;AAKO,SAAS,cAAc,YAA0B;AACtD,QAAM,SAAS,UAAU,UAAU;AAEnC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,WAAW,UAAU;AAAA,MACrB,gBAAgB;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,mBAAiB,OAAO,UAAU,OAAO,OAAO,UAAU,CAAC,CAAC;AAC9D;AAKO,SAAS,oBAAoB,YAA6B;AAC/D,QAAM,SAAS,UAAU,UAAU;AAEnC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,WAAW,UAAU;AAAA,MACrB,gBAAgB;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,CAAC,OAAO;AACzB,mBAAiB,OAAO,UAAU,UAAU,OAAO,UAAU,CAAC,CAAC;AAC/D,SAAO;AACT;AAKO,SAAS,mBACd,YACA,QACM;AACN,QAAM,SAAS,UAAU,UAAU;AAEnC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,WAAW,UAAU;AAAA,MACrB,gBAAgB;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,mBAAiB,OAAO,UAAU,OAAO,SAAS,MAAM;AAC1D;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|