@morphllm/morph-setup
Advanced tools
+196
-84
@@ -7,5 +7,5 @@ #!/usr/bin/env node | ||
| import * as p3 from "@clack/prompts"; | ||
| import path6 from "path"; | ||
| import path7 from "path"; | ||
| import { fileURLToPath } from "url"; | ||
| import { createRequire } from "module"; | ||
| import { createRequire as createRequire2 } from "module"; | ||
@@ -520,8 +520,8 @@ // src/source-parser.ts | ||
| // src/mcp-install.ts | ||
| import fs from "fs/promises"; | ||
| import { existsSync as existsSync2 } from "fs"; | ||
| import path2 from "path"; | ||
| import fs2 from "fs/promises"; | ||
| import { existsSync as existsSync3 } from "fs"; | ||
| import path3 from "path"; | ||
| import JSON5 from "json5"; | ||
| import { execFile } from "child_process"; | ||
| import { promisify } from "util"; | ||
| import { execFile as execFile2 } from "child_process"; | ||
| import { promisify as promisify2 } from "util"; | ||
@@ -538,7 +538,7 @@ // src/mcp-client-paths.ts | ||
| function getWindsurfConfigPath() { | ||
| const os = platform2(); | ||
| if (os === "darwin" || os === "linux") { | ||
| const os2 = platform2(); | ||
| if (os2 === "darwin" || os2 === "linux") { | ||
| return expandHome("~/.codeium/windsurf/mcp_config.json"); | ||
| } | ||
| if (os === "win32") { | ||
| if (os2 === "win32") { | ||
| const appData = process.env.APPDATA; | ||
@@ -554,7 +554,7 @@ if (!appData) return null; | ||
| function getAmpSettingsPath() { | ||
| const os = platform2(); | ||
| if (os === "darwin" || os === "linux") { | ||
| const os2 = platform2(); | ||
| if (os2 === "darwin" || os2 === "linux") { | ||
| return path.join(homedir3(), ".config", "amp", "settings.json"); | ||
| } | ||
| if (os === "win32") { | ||
| if (os2 === "win32") { | ||
| return path.join(homedir3(), ".config", "amp", "settings.json"); | ||
@@ -611,4 +611,86 @@ } | ||
| // src/mcp-server-runtime.ts | ||
| import fs from "fs/promises"; | ||
| import { existsSync as existsSync2 } from "fs"; | ||
| import os from "os"; | ||
| import path2 from "path"; | ||
| import { execFile } from "child_process"; | ||
| import { createRequire } from "module"; | ||
| import { promisify } from "util"; | ||
| var execFileAsync = promisify(execFile); | ||
| var MCP_PACKAGE_NAME = "@morphllm/morphmcp"; | ||
| var INSTALL_ATTEMPTS = 3; | ||
| function managedDirFor(version) { | ||
| return path2.join(os.homedir(), ".morph", "mcp", version); | ||
| } | ||
| async function clearManagedMcpInstalls() { | ||
| await fs.rm(path2.join(os.homedir(), ".morph", "mcp"), { recursive: true, force: true }); | ||
| } | ||
| function serverEntryFor(managedDir) { | ||
| return path2.join(managedDir, "node_modules", "@morphllm", "morphmcp", "dist", "index.js"); | ||
| } | ||
| function completionMarkerFor(managedDir) { | ||
| return path2.join(managedDir, ".install-complete"); | ||
| } | ||
| function resolveNodeCommand() { | ||
| const exec = process.execPath; | ||
| const base = path2.basename(exec).toLowerCase(); | ||
| return base.startsWith("node") ? exec : "node"; | ||
| } | ||
| function isRipgrepReady(managedDir) { | ||
| try { | ||
| const req = createRequire(path2.join(managedDir, "package.json")); | ||
| const { rgPath } = req("@vscode/ripgrep"); | ||
| return typeof rgPath === "string" && existsSync2(rgPath); | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| async function runNpmInstall(managedDir, version) { | ||
| await execFileAsync( | ||
| "npm", | ||
| ["install", `${MCP_PACKAGE_NAME}@${version}`, "--no-audit", "--no-fund", "--loglevel=error"], | ||
| { cwd: managedDir, env: process.env, timeout: 5 * 60 * 1e3, maxBuffer: 64 * 1024 * 1024, windowsHide: true } | ||
| ); | ||
| } | ||
| async function ensureMorphMcpInstalled(version, opts = {}) { | ||
| const managedDir = managedDirFor(version); | ||
| const serverEntry = serverEntryFor(managedDir); | ||
| const marker = completionMarkerFor(managedDir); | ||
| const command = resolveNodeCommand(); | ||
| if (!opts.force && existsSync2(marker) && existsSync2(serverEntry)) { | ||
| return { command, serverEntry, managedDir, ripgrepReady: isRipgrepReady(managedDir) }; | ||
| } | ||
| await fs.mkdir(managedDir, { recursive: true }); | ||
| await fs.rm(marker, { force: true }); | ||
| await fs.writeFile( | ||
| path2.join(managedDir, "package.json"), | ||
| JSON.stringify({ name: "morph-mcp-runtime", version: "0.0.0", private: true }, null, 2) + "\n", | ||
| "utf-8" | ||
| ); | ||
| let lastError; | ||
| for (let attempt = 1; attempt <= INSTALL_ATTEMPTS; attempt++) { | ||
| opts.onProgress?.( | ||
| `Installing ${MCP_PACKAGE_NAME}@${version}` + (attempt > 1 ? ` (attempt ${attempt}/${INSTALL_ATTEMPTS})` : "") | ||
| ); | ||
| try { | ||
| await runNpmInstall(managedDir, version); | ||
| if (!existsSync2(serverEntry)) { | ||
| throw new Error(`npm reported success but no server entry at ${serverEntry}`); | ||
| } | ||
| await fs.writeFile(marker, `${version} | ||
| `, "utf-8"); | ||
| const ripgrepReady = isRipgrepReady(managedDir); | ||
| if (!ripgrepReady) opts.onProgress?.("ripgrep binary missing; code search will be unavailable"); | ||
| return { command, serverEntry, managedDir, ripgrepReady }; | ||
| } catch (err) { | ||
| lastError = err; | ||
| } | ||
| } | ||
| const message = lastError instanceof Error ? lastError.message : String(lastError); | ||
| throw new Error(`Managed install of ${MCP_PACKAGE_NAME}@${version} failed after ${INSTALL_ATTEMPTS} attempts: ${message}`); | ||
| } | ||
| // src/mcp-install.ts | ||
| var execFileAsync = promisify(execFile); | ||
| var execFileAsync2 = promisify2(execFile2); | ||
| var MCP_PACKAGE = `@morphllm/morphmcp@${MCP_VERSION}`; | ||
@@ -619,6 +701,6 @@ function isObject(value) { | ||
| async function ensureDirForFile(filePath) { | ||
| await fs.mkdir(path2.dirname(filePath), { recursive: true }); | ||
| await fs2.mkdir(path3.dirname(filePath), { recursive: true }); | ||
| } | ||
| async function readJsonish(filePath) { | ||
| const raw = await fs.readFile(filePath, "utf-8"); | ||
| const raw = await fs2.readFile(filePath, "utf-8"); | ||
| if (raw.trim().length === 0) return null; | ||
@@ -629,3 +711,3 @@ return JSON5.parse(raw); | ||
| await ensureDirForFile(filePath); | ||
| await fs.writeFile(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8"); | ||
| await fs2.writeFile(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8"); | ||
| } | ||
@@ -640,8 +722,12 @@ function tomlEscapeString(value) { | ||
| } | ||
| function buildCodexMorphBlock(input) { | ||
| function buildMorphServerArgs(input, serverEntry) { | ||
| const args = [serverEntry]; | ||
| if (input.apiKey) args.push("--api-key", input.apiKey); | ||
| return args; | ||
| } | ||
| function buildCodexMorphBlock(input, launch) { | ||
| const lines = []; | ||
| lines.push("[mcp_servers.morph-mcp]"); | ||
| lines.push('command = "npx"'); | ||
| const morphArgs = buildMorphArgs(input); | ||
| const argsToml = morphArgs.map((a) => `"${tomlEscapeString(a)}"`).join(", "); | ||
| lines.push(`command = "${tomlEscapeString(launch.command)}"`); | ||
| const argsToml = launch.args.map((a) => `"${tomlEscapeString(a)}"`).join(", "); | ||
| lines.push(`args = [${argsToml}]`); | ||
@@ -657,6 +743,15 @@ lines.push("enabled = true"); | ||
| } | ||
| async function resolveCodexLaunch(input) { | ||
| try { | ||
| const runtime = await ensureMorphMcpInstalled(MCP_VERSION); | ||
| return { command: runtime.command, args: buildMorphServerArgs(input, runtime.serverEntry) }; | ||
| } catch { | ||
| return { command: "npx", args: buildMorphArgs(input) }; | ||
| } | ||
| } | ||
| async function installToCodexConfigToml(configPath, input) { | ||
| const launch = await resolveCodexLaunch(input); | ||
| let raw = ""; | ||
| try { | ||
| raw = await fs.readFile(configPath, "utf-8"); | ||
| raw = await fs2.readFile(configPath, "utf-8"); | ||
| } catch (err) { | ||
@@ -667,3 +762,3 @@ const code = err?.code; | ||
| const existingLines = raw.length > 0 ? raw.split(/\r?\n/) : []; | ||
| const blockLines = buildCodexMorphBlock(input); | ||
| const blockLines = buildCodexMorphBlock(input, launch); | ||
| const header = "[mcp_servers.morph-mcp]"; | ||
@@ -689,3 +784,3 @@ const start = existingLines.findIndex((l) => l.trim() === header); | ||
| await ensureDirForFile(configPath); | ||
| await fs.writeFile(configPath, out, "utf-8"); | ||
| await fs2.writeFile(configPath, out, "utf-8"); | ||
| } | ||
@@ -753,3 +848,3 @@ function buildMorphEnv(input) { | ||
| try { | ||
| await execFileAsync("amp", ["mcp", "add", "morph-mcp", "--", "npx", "--prefer-offline", "-y", MCP_PACKAGE], { | ||
| await execFileAsync2("amp", ["mcp", "add", "morph-mcp", "--", "npx", "--prefer-offline", "-y", MCP_PACKAGE], { | ||
| windowsHide: true | ||
@@ -803,3 +898,3 @@ }); | ||
| if (!configPath) continue; | ||
| if (existsSync2(path2.dirname(configPath))) detected.push(id); | ||
| if (existsSync3(path3.dirname(configPath))) detected.push(id); | ||
| } | ||
@@ -877,19 +972,19 @@ return detected; | ||
| // src/claude-code-plugin.ts | ||
| import fs2 from "fs/promises"; | ||
| import { existsSync as existsSync3 } from "fs"; | ||
| import path3 from "path"; | ||
| import fs3 from "fs/promises"; | ||
| import { existsSync as existsSync4 } from "fs"; | ||
| import path4 from "path"; | ||
| import { homedir as homedir4 } from "os"; | ||
| import { execFile as execFile2 } from "child_process"; | ||
| import { promisify as promisify2 } from "util"; | ||
| import { execFile as execFile3 } from "child_process"; | ||
| import { promisify as promisify3 } from "util"; | ||
| import * as p from "@clack/prompts"; | ||
| import chalk from "chalk"; | ||
| var execFileAsync2 = promisify2(execFile2); | ||
| var MORPH_STATE_DIR = path3.join(homedir4(), ".claude", "morph"); | ||
| var ENV_FILE = path3.join(MORPH_STATE_DIR, ".env"); | ||
| var CLAUDE_MD_PATH = path3.join(homedir4(), ".claude", "CLAUDE.md"); | ||
| var PLUGINS_DIR = path3.join(homedir4(), ".claude", "plugins"); | ||
| var execFileAsync3 = promisify3(execFile3); | ||
| var MORPH_STATE_DIR = path4.join(homedir4(), ".claude", "morph"); | ||
| var ENV_FILE = path4.join(MORPH_STATE_DIR, ".env"); | ||
| var CLAUDE_MD_PATH = path4.join(homedir4(), ".claude", "CLAUDE.md"); | ||
| var PLUGINS_DIR = path4.join(homedir4(), ".claude", "plugins"); | ||
| var PLUGIN_PATHS_TO_CLEAN = [ | ||
| path3.join(PLUGINS_DIR, "cache", "morph"), | ||
| path3.join(PLUGINS_DIR, "repos", "morph"), | ||
| path3.join(PLUGINS_DIR, "installed", "morph-compact") | ||
| path4.join(PLUGINS_DIR, "cache", "morph"), | ||
| path4.join(PLUGINS_DIR, "repos", "morph"), | ||
| path4.join(PLUGINS_DIR, "installed", "morph-compact") | ||
| ]; | ||
@@ -921,3 +1016,3 @@ var COMPACT_BEGIN_MARKER = "<!-- morph-compact:begin -->"; | ||
| try { | ||
| await execFileAsync2("claude", ["--version"]); | ||
| await execFileAsync3("claude", ["--version"]); | ||
| return true; | ||
@@ -930,7 +1025,7 @@ } catch { | ||
| try { | ||
| await execFileAsync2("claude", ["plugin", "uninstall", "morph-compact@morph"]); | ||
| await execFileAsync3("claude", ["plugin", "uninstall", "morph-compact@morph"]); | ||
| } catch { | ||
| } | ||
| try { | ||
| await execFileAsync2("claude", ["plugin", "marketplace", "remove", "morph"]); | ||
| await execFileAsync3("claude", ["plugin", "marketplace", "remove", "morph"]); | ||
| } catch { | ||
@@ -940,3 +1035,3 @@ } | ||
| PLUGIN_PATHS_TO_CLEAN.map( | ||
| (p4) => fs2.rm(p4, { recursive: true, force: true }).catch(() => { | ||
| (p4) => fs3.rm(p4, { recursive: true, force: true }).catch(() => { | ||
| }) | ||
@@ -948,3 +1043,3 @@ ) | ||
| await cleanupExistingPlugin(); | ||
| await execFileAsync2("claude", [ | ||
| await execFileAsync3("claude", [ | ||
| "plugin", | ||
@@ -957,3 +1052,3 @@ "marketplace", | ||
| ]); | ||
| await execFileAsync2("claude", [ | ||
| await execFileAsync3("claude", [ | ||
| "plugin", | ||
@@ -966,3 +1061,3 @@ "install", | ||
| try { | ||
| await execFileAsync2("claude", [ | ||
| await execFileAsync3("claude", [ | ||
| "plugin", | ||
@@ -978,5 +1073,5 @@ "enable", | ||
| async function writeApiKeyFile(apiKey) { | ||
| if (existsSync3(ENV_FILE)) { | ||
| if (existsSync4(ENV_FILE)) { | ||
| try { | ||
| const content = await fs2.readFile(ENV_FILE, "utf-8"); | ||
| const content = await fs3.readFile(ENV_FILE, "utf-8"); | ||
| if (content.includes("MORPH_API_KEY=")) return; | ||
@@ -986,4 +1081,4 @@ } catch { | ||
| } | ||
| await fs2.mkdir(MORPH_STATE_DIR, { recursive: true, mode: 448 }); | ||
| await fs2.writeFile(ENV_FILE, `MORPH_API_KEY=${apiKey} | ||
| await fs3.mkdir(MORPH_STATE_DIR, { recursive: true, mode: 448 }); | ||
| await fs3.writeFile(ENV_FILE, `MORPH_API_KEY=${apiKey} | ||
| `, { encoding: "utf-8", mode: 384 }); | ||
@@ -1003,3 +1098,3 @@ } | ||
| try { | ||
| existing = await fs2.readFile(CLAUDE_MD_PATH, "utf-8"); | ||
| existing = await fs3.readFile(CLAUDE_MD_PATH, "utf-8"); | ||
| } catch (err) { | ||
@@ -1011,3 +1106,3 @@ const code = err?.code; | ||
| if (updated === null) return; | ||
| await fs2.writeFile(CLAUDE_MD_PATH, updated, "utf-8"); | ||
| await fs3.writeFile(CLAUDE_MD_PATH, updated, "utf-8"); | ||
| } | ||
@@ -1047,14 +1142,14 @@ async function installClaudeCodePlugin(apiKey) { | ||
| // src/opencode-plugin.ts | ||
| import fs3 from "fs/promises"; | ||
| import { existsSync as existsSync4 } from "fs"; | ||
| import path4 from "path"; | ||
| import fs4 from "fs/promises"; | ||
| import { existsSync as existsSync5 } from "fs"; | ||
| import path5 from "path"; | ||
| import { homedir as homedir5 } from "os"; | ||
| import { execFile as execFile3 } from "child_process"; | ||
| import { promisify as promisify3 } from "util"; | ||
| import { execFile as execFile4 } from "child_process"; | ||
| import { promisify as promisify4 } from "util"; | ||
| import * as p2 from "@clack/prompts"; | ||
| import chalk2 from "chalk"; | ||
| import JSON52 from "json5"; | ||
| var execFileAsync3 = promisify3(execFile3); | ||
| var OPENCODE_CONFIG_DIR = path4.join(homedir5(), ".config", "opencode"); | ||
| var OPENCODE_CONFIG_PATH = path4.join(OPENCODE_CONFIG_DIR, "opencode.json"); | ||
| var execFileAsync4 = promisify4(execFile4); | ||
| var OPENCODE_CONFIG_DIR = path5.join(homedir5(), ".config", "opencode"); | ||
| var OPENCODE_CONFIG_PATH = path5.join(OPENCODE_CONFIG_DIR, "opencode.json"); | ||
| var PLUGIN_PACKAGE = "@morphllm/opencode-morph-plugin"; | ||
@@ -1067,3 +1162,3 @@ var PLUGIN_INSTRUCTIONS_PATH = "node_modules/@morphllm/opencode-morph-plugin/instructions/morph-tools.md"; | ||
| try { | ||
| await execFileAsync3("bun", ["--version"]); | ||
| await execFileAsync4("bun", ["--version"]); | ||
| return "bun"; | ||
@@ -1076,10 +1171,10 @@ } catch { | ||
| p2.log.info(chalk2.greenBright("Installing Morph plugin for OpenCode...")); | ||
| await fs3.mkdir(OPENCODE_CONFIG_DIR, { recursive: true }); | ||
| const pkgJsonPath = path4.join(OPENCODE_CONFIG_DIR, "package.json"); | ||
| if (!existsSync4(pkgJsonPath)) { | ||
| await fs3.writeFile(pkgJsonPath, JSON.stringify({ private: true }, null, 2) + "\n", "utf-8"); | ||
| await fs4.mkdir(OPENCODE_CONFIG_DIR, { recursive: true }); | ||
| const pkgJsonPath = path5.join(OPENCODE_CONFIG_DIR, "package.json"); | ||
| if (!existsSync5(pkgJsonPath)) { | ||
| await fs4.writeFile(pkgJsonPath, JSON.stringify({ private: true }, null, 2) + "\n", "utf-8"); | ||
| } | ||
| const pm = await detectPackageManager(); | ||
| try { | ||
| await execFileAsync3(pm, ["install", PLUGIN_PACKAGE], { | ||
| await execFileAsync4(pm, ["install", PLUGIN_PACKAGE], { | ||
| cwd: OPENCODE_CONFIG_DIR, | ||
@@ -1098,3 +1193,3 @@ windowsHide: true | ||
| try { | ||
| data = existsSync4(OPENCODE_CONFIG_PATH) ? await JSON52.parse(await fs3.readFile(OPENCODE_CONFIG_PATH, "utf-8")) : {}; | ||
| data = existsSync5(OPENCODE_CONFIG_PATH) ? await JSON52.parse(await fs4.readFile(OPENCODE_CONFIG_PATH, "utf-8")) : {}; | ||
| } catch { | ||
@@ -1114,3 +1209,3 @@ data = {}; | ||
| obj.instructions = instructions; | ||
| await fs3.writeFile(OPENCODE_CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n", "utf-8"); | ||
| await fs4.writeFile(OPENCODE_CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n", "utf-8"); | ||
| p2.log.success(`OpenCode config updated ${chalk2.dim(`\u2192 ${OPENCODE_CONFIG_PATH}`)}`); | ||
@@ -1125,9 +1220,9 @@ } catch (err) { | ||
| // src/mcp-reset.ts | ||
| import fs4 from "fs/promises"; | ||
| import path5 from "path"; | ||
| import fs5 from "fs/promises"; | ||
| import path6 from "path"; | ||
| import { homedir as homedir6 } from "os"; | ||
| import JSON53 from "json5"; | ||
| import { execFile as execFile4 } from "child_process"; | ||
| import { promisify as promisify4 } from "util"; | ||
| var execFileAsync4 = promisify4(execFile4); | ||
| import { execFile as execFile5 } from "child_process"; | ||
| import { promisify as promisify5 } from "util"; | ||
| var execFileAsync5 = promisify5(execFile5); | ||
| function isObject3(value) { | ||
@@ -1138,3 +1233,3 @@ return !!value && typeof value === "object" && !Array.isArray(value); | ||
| try { | ||
| const raw = await fs4.readFile(filePath, "utf-8"); | ||
| const raw = await fs5.readFile(filePath, "utf-8"); | ||
| const parsed = JSON53.parse(raw); | ||
@@ -1147,4 +1242,4 @@ return isObject3(parsed) ? parsed : null; | ||
| async function writeJson2(filePath, data) { | ||
| await fs4.mkdir(path5.dirname(filePath), { recursive: true }); | ||
| await fs4.writeFile(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8"); | ||
| await fs5.mkdir(path6.dirname(filePath), { recursive: true }); | ||
| await fs5.writeFile(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8"); | ||
| } | ||
@@ -1163,3 +1258,3 @@ async function deleteFromJsonConfig(configPath, rootKey) { | ||
| try { | ||
| raw = await fs4.readFile(configPath, "utf-8"); | ||
| raw = await fs5.readFile(configPath, "utf-8"); | ||
| } catch { | ||
@@ -1174,3 +1269,3 @@ return false; | ||
| const out = [...lines.slice(0, start), ...lines.slice(end)].join("\n").replace(/\n{3,}/g, "\n\n").replace(/\n*$/, "\n"); | ||
| await fs4.writeFile(configPath, out, "utf-8"); | ||
| await fs5.writeFile(configPath, out, "utf-8"); | ||
| return true; | ||
@@ -1184,3 +1279,3 @@ } | ||
| try { | ||
| await execFileAsync4("amp", ["mcp", "remove", "morph-mcp"], { windowsHide: true }); | ||
| await execFileAsync5("amp", ["mcp", "remove", "morph-mcp"], { windowsHide: true }); | ||
| } catch { | ||
@@ -1202,5 +1297,5 @@ } | ||
| async function clearClaudePluginCache() { | ||
| const cacheDir = path5.join(homedir6(), ".claude", "plugins", "cache", "morph"); | ||
| const cacheDir = path6.join(homedir6(), ".claude", "plugins", "cache", "morph"); | ||
| try { | ||
| await fs4.rm(cacheDir, { recursive: true, force: true }); | ||
| await fs5.rm(cacheDir, { recursive: true, force: true }); | ||
| return true; | ||
@@ -1214,4 +1309,4 @@ } catch { | ||
| var __filename = fileURLToPath(import.meta.url); | ||
| var __dirname = path6.dirname(__filename); | ||
| var require2 = createRequire(import.meta.url); | ||
| var __dirname = path7.dirname(__filename); | ||
| var require2 = createRequire2(import.meta.url); | ||
| var MORPH_ASCII_LINES = [ | ||
@@ -1247,3 +1342,3 @@ "\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557 ", | ||
| function getBundledSkillsDir() { | ||
| return path6.resolve(__dirname, "..", ".agents", "skills"); | ||
| return path7.resolve(__dirname, "..", ".agents", "skills"); | ||
| } | ||
@@ -1363,2 +1458,15 @@ function getAllAgentTypes() { | ||
| p3.log.info(chalk3.greenBright(`Installing Morph MCP and/or plugin into ${params.clients.length} chosen app(s)...`)); | ||
| if (params.clients.includes("codex")) { | ||
| const spin = p3.spinner(); | ||
| spin.start("Setting up the Morph MCP server (one-time)"); | ||
| try { | ||
| const runtime = await ensureMorphMcpInstalled(MCP_VERSION); | ||
| spin.stop( | ||
| runtime.ripgrepReady ? "Morph MCP server ready" : "Morph MCP server ready (ripgrep missing \u2014 code search unavailable)" | ||
| ); | ||
| } catch (err) { | ||
| spin.stop(chalk3.yellow("Could not pre-install the Morph MCP server; Codex will fall back to npx")); | ||
| p3.log.warn(err instanceof Error ? err.message : String(err)); | ||
| } | ||
| } | ||
| for (const client of params.clients) { | ||
@@ -1434,2 +1542,6 @@ const res = await installMorphMcpToClient(client, params.input); | ||
| } | ||
| if (mcpClients.includes("codex")) { | ||
| await clearManagedMcpInstalls(); | ||
| p3.log.success("Cleared managed Morph MCP server install"); | ||
| } | ||
| } | ||
@@ -1436,0 +1548,0 @@ await installMcp({ clients: mcpClients, input: mcpInput }); |
+1
-1
| { | ||
| "name": "@morphllm/morph-setup", | ||
| "version": "1.0.28", | ||
| "version": "1.0.29", | ||
| "description": "Install Morph MCP and bundled skills onto coding agents", | ||
@@ -5,0 +5,0 @@ "type": "module", |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
71262
6.81%1770
6.69%33
17.86%8
14.29%