Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@morphllm/morph-setup

Package Overview
Dependencies
Maintainers
2
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@morphllm/morph-setup - npm Package Compare versions

Comparing version
1.0.28
to
1.0.29
+196
-84
dist/index.js

@@ -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 });

{
"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",