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.31
to
1.0.32
+215
-11
dist/index.js

@@ -7,3 +7,3 @@ #!/usr/bin/env node

import * as p3 from "@clack/prompts";
import path7 from "path";
import path8 from "path";
import { fileURLToPath } from "url";

@@ -518,2 +518,52 @@ import { createRequire as createRequire2 } from "module";

}
async function removeSkillForAgent(skillName, agentType, options = {}) {
let target;
try {
target = getInstallPath(skillName, agentType, options);
} catch {
return false;
}
try {
await lstat(target);
} catch {
return false;
}
await rm2(target, { recursive: true, force: true });
return true;
}
async function removeCanonicalSkill(skillName, options = {}) {
let target;
try {
target = getCanonicalPath(skillName, options);
} catch {
return false;
}
try {
await lstat(target);
} catch {
return false;
}
await rm2(target, { recursive: true, force: true });
return true;
}
function getInstallPath(skillName, agentType, options = {}) {
const agent = agents[agentType];
const cwd = options.cwd || process.cwd();
const sanitized = sanitizeName(skillName);
const targetBase = options.global ? agent.globalSkillsDir : join4(cwd, agent.skillsDir);
const installPath = join4(targetBase, sanitized);
if (!isPathSafe(targetBase, installPath)) {
throw new Error("Invalid skill name: potential path traversal detected");
}
return installPath;
}
function getCanonicalPath(skillName, options = {}) {
const sanitized = sanitizeName(skillName);
const canonicalBase = getCanonicalSkillsDir(options.global ?? false, options.cwd);
const canonicalPath = join4(canonicalBase, sanitized);
if (!isPathSafe(canonicalBase, canonicalPath)) {
throw new Error("Invalid skill name: potential path traversal detected");
}
return canonicalPath;
}

@@ -538,7 +588,7 @@ // src/mcp-install.ts

function getWindsurfConfigPath() {
const os2 = platform2();
if (os2 === "darwin" || os2 === "linux") {
const os3 = platform2();
if (os3 === "darwin" || os3 === "linux") {
return expandHome("~/.codeium/windsurf/mcp_config.json");
}
if (os2 === "win32") {
if (os3 === "win32") {
const appData = process.env.APPDATA;

@@ -554,7 +604,7 @@ if (!appData) return null;

function getAmpSettingsPath() {
const os2 = platform2();
if (os2 === "darwin" || os2 === "linux") {
const os3 = platform2();
if (os3 === "darwin" || os3 === "linux") {
return path.join(homedir3(), ".config", "amp", "settings.json");
}
if (os2 === "win32") {
if (os3 === "win32") {
return path.join(homedir3(), ".config", "amp", "settings.json");

@@ -1077,2 +1127,11 @@ }

}
function computeStrippedClaudeMd(existing) {
let stripped = existing;
for (const pattern of LEGACY_BLOCK_PATTERNS) {
stripped = stripped.replace(pattern, "");
}
stripped = stripped.replace(/\n{3,}/g, "\n\n").replace(/\s+$/, "");
const next = stripped.length === 0 ? "" : stripped + "\n";
return next === existing ? null : next;
}
async function injectCompactInstructions() {

@@ -1121,2 +1180,25 @@ let existing = "";

}
async function uninstallClaudeCodePlugin() {
const pluginRemoved = PLUGIN_PATHS_TO_CLEAN.some((dir) => existsSync4(dir));
await cleanupExistingPlugin();
let claudeMdCleaned = false;
try {
const existing = await fs3.readFile(CLAUDE_MD_PATH, "utf-8");
const next = computeStrippedClaudeMd(existing);
if (next !== null) {
await fs3.writeFile(CLAUDE_MD_PATH, next, "utf-8");
claudeMdCleaned = true;
}
} catch {
}
let stateRemoved = false;
if (existsSync4(MORPH_STATE_DIR)) {
try {
await fs3.rm(MORPH_STATE_DIR, { recursive: true, force: true });
stateRemoved = true;
} catch {
}
}
return { pluginRemoved, claudeMdCleaned, stateRemoved };
}

@@ -1195,2 +1277,32 @@ // src/opencode-plugin.ts

}
async function uninstallOpenCodePlugin() {
if (!existsSync5(OPENCODE_CONFIG_PATH)) return false;
let data;
try {
data = JSON52.parse(await fs4.readFile(OPENCODE_CONFIG_PATH, "utf-8"));
} catch {
return false;
}
if (!isObject2(data)) return false;
const obj = data;
let changed = false;
if (Array.isArray(obj.plugin)) {
const next = obj.plugin.filter((x) => x !== PLUGIN_PACKAGE);
if (next.length !== obj.plugin.length) {
obj.plugin = next;
changed = true;
}
}
if (Array.isArray(obj.instructions)) {
const next = obj.instructions.filter((x) => x !== PLUGIN_INSTRUCTIONS_PATH);
if (next.length !== obj.instructions.length) {
obj.instructions = next;
changed = true;
}
}
if (changed) {
await fs4.writeFile(OPENCODE_CONFIG_PATH, JSON.stringify(obj, null, 2) + "\n", "utf-8");
}
return changed;
}

@@ -1278,5 +1390,45 @@ // src/mcp-reset.ts

// src/uninstall.ts
import { existsSync as existsSync6 } from "fs";
import os2 from "os";
import path7 from "path";
function managedMcpRoot() {
return path7.join(os2.homedir(), ".morph", "mcp");
}
async function uninstallMorph(opts) {
const global = opts.global ?? true;
const mcpRemoved = [];
for (const client of opts.clients) {
if (await resetMorphMcpFromClient(client)) mcpRemoved.push(client);
}
const skillNames = (await discoverSkills(opts.bundledSkillsDir)).map(getSkillDisplayName);
const skillsRemoved = [];
for (const agent of opts.agents) {
for (const skill of skillNames) {
if (await removeSkillForAgent(skill, agent, { global })) {
skillsRemoved.push({ agent, skill });
}
}
}
const canonicalSkillsRemoved = [];
for (const skill of skillNames) {
if (await removeCanonicalSkill(skill, { global })) canonicalSkillsRemoved.push(skill);
}
const claudePlugin = opts.clients.includes("claude-code") ? await uninstallClaudeCodePlugin() : void 0;
const openCodePluginRemoved = opts.clients.includes("opencode") ? await uninstallOpenCodePlugin() : false;
const managedInstallRemoved = existsSync6(managedMcpRoot());
await clearManagedMcpInstalls();
return {
mcpRemoved,
skillsRemoved,
canonicalSkillsRemoved,
claudePlugin,
openCodePluginRemoved,
managedInstallRemoved
};
}
// src/index.ts
var __filename = fileURLToPath(import.meta.url);
var __dirname = path7.dirname(__filename);
var __dirname = path8.dirname(__filename);
var require2 = createRequire2(import.meta.url);

@@ -1313,3 +1465,3 @@ var MORPH_ASCII_LINES = [

function getBundledSkillsDir() {
return path7.resolve(__dirname, "..", ".agents", "skills");
return path8.resolve(__dirname, "..", ".agents", "skills");
}

@@ -1319,5 +1471,9 @@ function getAllAgentTypes() {

}
function getAllPlatforms() {
const supportedMcpIds = new Set(listSupportedMcpClients().map((c) => c.id));
return getAllAgentTypes().filter((id) => supportedMcpIds.has(id));
}
async function selectPlatforms(opts) {
const supportedMcpIds = new Set(listSupportedMcpClients().map((c) => c.id));
const allPlatforms = getAllAgentTypes().filter((id) => supportedMcpIds.has(id));
const allPlatforms = getAllPlatforms();
if (opts.cliAgents.length > 0) return opts.cliAgents;

@@ -1452,2 +1608,43 @@ if (opts.yes) {

}
async function runUninstall(opts) {
p3.intro(chalk3.greenBright(" Morph Uninstall "));
const platforms = opts.cliAgents.length > 0 ? opts.cliAgents : getAllPlatforms();
if (!opts.yes) {
const labels = platforms.map((id) => agents[id]?.displayName ?? id).join(", ");
const ok = await p3.confirm({
message: `Remove Morph (MCP servers, skills, and plugins) from: ${labels}?`,
initialValue: true
});
if (p3.isCancel(ok) || !ok) {
p3.outro("Nothing removed.");
return;
}
}
const spin = p3.spinner();
spin.start("Removing Morph from your agents");
const report = await uninstallMorph({
clients: platforms,
agents: platforms,
bundledSkillsDir: getBundledSkillsDir(),
global: true
});
spin.stop("Removed Morph");
if (report.mcpRemoved.length > 0) {
p3.log.success(`MCP config removed from: ${chalk3.dim(report.mcpRemoved.join(", "))}`);
}
if (report.skillsRemoved.length > 0) {
const perAgent = /* @__PURE__ */ new Map();
for (const { agent } of report.skillsRemoved) perAgent.set(agent, (perAgent.get(agent) ?? 0) + 1);
const summary = [...perAgent].map(([a, n]) => `${a} (${n})`).join(", ");
p3.log.success(`Skills removed from: ${chalk3.dim(summary)}`);
}
if (report.claudePlugin?.pluginRemoved) p3.log.success("Claude Code plugin (morph-compact) removed");
if (report.claudePlugin?.claudeMdCleaned) p3.log.success("Compact instructions stripped from ~/.claude/CLAUDE.md");
if (report.openCodePluginRemoved) p3.log.success("OpenCode plugin deregistered");
if (report.managedInstallRemoved) p3.log.success("Managed Morph MCP server install removed (~/.morph/mcp)");
const nothing = report.mcpRemoved.length === 0 && report.skillsRemoved.length === 0 && !report.claudePlugin?.pluginRemoved && !report.claudePlugin?.claudeMdCleaned && !report.openCodePluginRemoved && !report.managedInstallRemoved;
if (nothing) p3.log.info("No Morph artifacts were found to remove.");
track({ event: "uninstall", agents: platforms.join(",") });
p3.outro(chalk3.greenBright("Morph removed."));
}
async function main() {

@@ -1457,3 +1654,3 @@ const pkg = require2("../package.json");

await showMorphSplash();
const program = new Command().name("morph-setup").description("Install Morph MCP and bundled skills onto coding agents").argument("[source]", "Optional skills source (repo/path). Bundled skills are always installed.").option("--morph-api-key <key>", "MORPH_API_KEY to write into MCP config (overrides env)").option("-g, --global", "Install skills globally (default)").option("-a, --agent <agents...>", "Target specific agents (repeatable)").option("-s, --skill <skills...>", "Install specific skills by name (only affects <source> skills)").option("-l, --list", "List available skills without installing").option("-y, --yes", "Skip all confirmation prompts").option("--exclude-tools <tools...>", "Disable specific MCP tools (e.g. --exclude-tools edit_file)").option("--fresh", "Remove existing Morph configs and npx cache before installing (clean slate)").option("--a2a-review", "Set up your AI code review twin").option("--github-username <username>", "GitHub username (for --a2a-review, skips auto-detection)").option("--claude-md <paths...>", "Paths to CLAUDE.md files (for --a2a-review, skips auto-scan)").version(pkg.version);
const program = new Command().name("morph-setup").description("Install Morph MCP and bundled skills onto coding agents").argument("[source]", "Optional skills source (repo/path). Bundled skills are always installed.").option("--morph-api-key <key>", "MORPH_API_KEY to write into MCP config (overrides env)").option("-g, --global", "Install skills globally (default)").option("-a, --agent <agents...>", "Target specific agents (repeatable)").option("-s, --skill <skills...>", "Install specific skills by name (only affects <source> skills)").option("-l, --list", "List available skills without installing").option("-y, --yes", "Skip all confirmation prompts").option("--exclude-tools <tools...>", "Disable specific MCP tools (e.g. --exclude-tools edit_file)").option("--fresh", "Remove existing Morph configs and npx cache before installing (clean slate)").option("--uninstall", "Remove Morph (MCP servers, skills, plugins) from all agents (or -a <agents>)").option("--a2a-review", "Set up your AI code review twin").option("--github-username <username>", "GitHub username (for --a2a-review, skips auto-detection)").option("--claude-md <paths...>", "Paths to CLAUDE.md files (for --a2a-review, skips auto-scan)").version(pkg.version);
const argv = process.argv.slice();

@@ -1463,2 +1660,9 @@ if (argv[2] === "--") argv.splice(2, 1);

const opts = program.opts();
if (opts.uninstall) {
await runUninstall({
yes: !!opts.yes,
cliAgents: (opts.agent ?? []).map((a) => a)
});
return;
}
if (opts.a2aReview) {

@@ -1465,0 +1669,0 @@ const { runA2aReviewSetup } = await import("./a2a-review-GIEEOXF6.js");

+1
-1
{
"name": "@morphllm/morph-setup",
"version": "1.0.31",
"version": "1.0.32",
"description": "Install Morph MCP and bundled skills onto coding agents",

@@ -5,0 +5,0 @@ "type": "module",