@mergesafe-io/connect 0.3.0 → 0.4.0
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/README.md +0 -1
- package/lib/actions.js +1 -17
- package/lib/cli.js +3 -9
- package/package.json +1 -1
- package/lib/rules.js +0 -80
package/README.md
CHANGED
|
@@ -56,7 +56,6 @@ destroy them; configure manually). Gemini CLI: use its built-in command:
|
|
|
56
56
|
|---|---|
|
|
57
57
|
| `--key <msk_…>` | Your MergeSafe API key (from app.mergesafe.io → Settings). Prompted if omitted. |
|
|
58
58
|
| `--project` | Write the project-local config instead of the global one (cursor, vscode, roo-code, continue — needs an explicit client) |
|
|
59
|
-
| `--with-rules` | Also write agent rules files (`.cursor/rules/mergesafe.mdc`, `AGENTS.md` block) so the AI runs MergeSafe after coding |
|
|
60
59
|
| `--dry-run` | Print the target paths and resulting files without writing |
|
|
61
60
|
| `--uninstall` | Remove the `mergesafe` entry (no client = every detected client) |
|
|
62
61
|
| `--help` | Usage |
|
package/lib/actions.js
CHANGED
|
@@ -4,9 +4,8 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { SERVER_NAME } from "./clients.js";
|
|
6
6
|
import { applyEntry, removeEntry } from "./config-file.js";
|
|
7
|
-
import { writeRulesFiles } from "./rules.js";
|
|
8
7
|
|
|
9
|
-
export async function handleInstall(client, key, configPath, dryRun
|
|
8
|
+
export async function handleInstall(client, key, configPath, dryRun) {
|
|
10
9
|
const existingText = await readIfExists(configPath);
|
|
11
10
|
|
|
12
11
|
const newText = client.buildFileContent
|
|
@@ -17,9 +16,6 @@ export async function handleInstall(client, key, configPath, dryRun, withRules,
|
|
|
17
16
|
|
|
18
17
|
if (dryRun) {
|
|
19
18
|
console.log(`Would write ${configPath}:\n\n${newText}`);
|
|
20
|
-
if (withRules) {
|
|
21
|
-
console.log("Would also write agent rules files (.cursor/rules/mergesafe.mdc, AGENTS.md).");
|
|
22
|
-
}
|
|
23
19
|
return;
|
|
24
20
|
}
|
|
25
21
|
|
|
@@ -29,10 +25,6 @@ export async function handleInstall(client, key, configPath, dryRun, withRules,
|
|
|
29
25
|
|
|
30
26
|
console.log(`✔ ${client.label} is connected to MergeSafe (${configPath})`);
|
|
31
27
|
console.log(`→ ${client.restartHint}`);
|
|
32
|
-
|
|
33
|
-
if (withRules) {
|
|
34
|
-
await reportRules(cwd);
|
|
35
|
-
}
|
|
36
28
|
}
|
|
37
29
|
|
|
38
30
|
export async function handleUninstall(client, configPath, dryRun) {
|
|
@@ -75,14 +67,6 @@ export async function handleUninstall(client, configPath, dryRun) {
|
|
|
75
67
|
console.log(`→ ${client.restartHint}`);
|
|
76
68
|
}
|
|
77
69
|
|
|
78
|
-
export async function reportRules(cwd) {
|
|
79
|
-
const written = await writeRulesFiles({ cwd });
|
|
80
|
-
for (const file of written) {
|
|
81
|
-
console.log(`✔ Rules written: ${file}`);
|
|
82
|
-
}
|
|
83
|
-
console.log("→ Your AI agent will now run MergeSafe after each coding session.");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
70
|
async function backupIfPresent(existingText, configPath) {
|
|
87
71
|
if (existingText === null) return;
|
|
88
72
|
const backupPath = `${configPath}.bak-${timestamp()}`;
|
package/lib/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import readline from "node:readline/promises";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
|
|
6
|
-
import { handleInstall, handleUninstall
|
|
6
|
+
import { handleInstall, handleUninstall } from "./actions.js";
|
|
7
7
|
import { SERVER_NAME, getClient, listClientIds } from "./clients.js";
|
|
8
8
|
import { detectClients } from "./detect.js";
|
|
9
9
|
|
|
@@ -30,8 +30,6 @@ Options:
|
|
|
30
30
|
prompted interactively if omitted)
|
|
31
31
|
--project Write the project-local config instead of the global one
|
|
32
32
|
(cursor, vscode, roo-code, continue — needs an explicit client)
|
|
33
|
-
--with-rules Also write agent rules files (.cursor/rules/mergesafe.mdc,
|
|
34
|
-
AGENTS.md block) so the AI runs MergeSafe after coding
|
|
35
33
|
--dry-run Print the target path and resulting file without writing
|
|
36
34
|
--uninstall Remove the mergesafe entry (no client = every detected client)
|
|
37
35
|
--help Show usage
|
|
@@ -44,7 +42,6 @@ export async function main(argv) {
|
|
|
44
42
|
options: {
|
|
45
43
|
key: { type: "string" },
|
|
46
44
|
project: { type: "boolean", default: false },
|
|
47
|
-
"with-rules": { type: "boolean", default: false },
|
|
48
45
|
"dry-run": { type: "boolean", default: false },
|
|
49
46
|
uninstall: { type: "boolean", default: false },
|
|
50
47
|
help: { type: "boolean", default: false },
|
|
@@ -77,7 +74,7 @@ export async function main(argv) {
|
|
|
77
74
|
await handleUninstall(client, configPath, values["dry-run"]);
|
|
78
75
|
} else {
|
|
79
76
|
const key = await resolveKey(values.key);
|
|
80
|
-
await handleInstall(client, key, configPath, values["dry-run"]
|
|
77
|
+
await handleInstall(client, key, configPath, values["dry-run"]);
|
|
81
78
|
}
|
|
82
79
|
}
|
|
83
80
|
|
|
@@ -106,7 +103,7 @@ async function runAllDetected(values, ctx) {
|
|
|
106
103
|
if (values.uninstall) {
|
|
107
104
|
await handleUninstall(client, client.configPath(ctx), values["dry-run"]);
|
|
108
105
|
} else {
|
|
109
|
-
await handleInstall(client, key, client.configPath(ctx), values["dry-run"]
|
|
106
|
+
await handleInstall(client, key, client.configPath(ctx), values["dry-run"]);
|
|
110
107
|
}
|
|
111
108
|
} catch (err) {
|
|
112
109
|
failures.push(`${client.label}: ${err.message}`);
|
|
@@ -114,9 +111,6 @@ async function runAllDetected(values, ctx) {
|
|
|
114
111
|
console.log("");
|
|
115
112
|
}
|
|
116
113
|
|
|
117
|
-
if (values["with-rules"] && !values.uninstall && !values["dry-run"]) {
|
|
118
|
-
await reportRules(ctx.cwd);
|
|
119
|
-
}
|
|
120
114
|
reportOutcome(detected.length, failures, values.uninstall);
|
|
121
115
|
}
|
|
122
116
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mergesafe-io/connect",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "One command connects every AI client on your machine to MergeSafe MCP — Claude Code, Claude Desktop, Cursor, Windsurf, VS Code and more, globally.",
|
|
5
5
|
"keywords": ["mergesafe", "mcp", "model-context-protocol", "installer"],
|
|
6
6
|
"homepage": "https://app.mergesafe.io",
|
package/lib/rules.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/** Agent rules files: make the AI call MergeSafe after every coding session.
|
|
2
|
-
*
|
|
3
|
-
* MCP server instructions are honored unevenly across clients; project rules
|
|
4
|
-
* files are the reliable trigger. This module owns:
|
|
5
|
-
* - .cursor/rules/mergesafe.mdc (whole file, alwaysApply)
|
|
6
|
-
* - AGENTS.md / CLAUDE.md (one marked block, upserted in place)
|
|
7
|
-
*/
|
|
8
|
-
import fs from "node:fs/promises";
|
|
9
|
-
import path from "node:path";
|
|
10
|
-
|
|
11
|
-
export const RULES_START = "<!-- mergesafe:start -->";
|
|
12
|
-
export const RULES_END = "<!-- mergesafe:end -->";
|
|
13
|
-
|
|
14
|
-
const RULE_TEXT = `After generating or modifying code in this project, run the MergeSafe MCP tool \`analyze_directory\` on the project root before you conclude, and report its verdict. If \`release_status\` is "blocked", fix the listed blockers (each finding ships a ready-to-use fix prompt) and re-run the check before declaring the work done.`;
|
|
15
|
-
|
|
16
|
-
const CURSOR_RULE = `---
|
|
17
|
-
description: MergeSafe post-generation check
|
|
18
|
-
alwaysApply: true
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
${RULE_TEXT}
|
|
22
|
-
`;
|
|
23
|
-
|
|
24
|
-
const MARKDOWN_BLOCK = `${RULES_START}
|
|
25
|
-
## MergeSafe check
|
|
26
|
-
|
|
27
|
-
${RULE_TEXT}
|
|
28
|
-
${RULES_END}`;
|
|
29
|
-
|
|
30
|
-
/** Upsert the marked MergeSafe block into a markdown document (idempotent). */
|
|
31
|
-
export function applyRulesBlock(existingText) {
|
|
32
|
-
const text = existingText ?? "";
|
|
33
|
-
const start = text.indexOf(RULES_START);
|
|
34
|
-
const end = text.indexOf(RULES_END);
|
|
35
|
-
if (start !== -1 && end !== -1 && end > start) {
|
|
36
|
-
return text.slice(0, start) + MARKDOWN_BLOCK + text.slice(end + RULES_END.length);
|
|
37
|
-
}
|
|
38
|
-
if (!text.trim()) return `${MARKDOWN_BLOCK}\n`;
|
|
39
|
-
return `${text.replace(/\n*$/, "")}\n\n${MARKDOWN_BLOCK}\n`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function readIfExists(filePath) {
|
|
43
|
-
try {
|
|
44
|
-
return await fs.readFile(filePath, "utf8");
|
|
45
|
-
} catch (err) {
|
|
46
|
-
if (err.code === "ENOENT") return null;
|
|
47
|
-
throw err;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Write the rules files for a project.
|
|
53
|
-
*
|
|
54
|
-
* AGENTS.md is always upserted (created if missing — it is the cross-agent
|
|
55
|
-
* standard); CLAUDE.md is only updated when it already exists, so we never
|
|
56
|
-
* plant a client-specific file uninvited.
|
|
57
|
-
*
|
|
58
|
-
* @returns {Promise<string[]>} the paths written.
|
|
59
|
-
*/
|
|
60
|
-
export async function writeRulesFiles({ cwd }) {
|
|
61
|
-
const written = [];
|
|
62
|
-
|
|
63
|
-
const mdcPath = path.join(cwd, ".cursor", "rules", "mergesafe.mdc");
|
|
64
|
-
await fs.mkdir(path.dirname(mdcPath), { recursive: true });
|
|
65
|
-
await fs.writeFile(mdcPath, CURSOR_RULE, "utf8");
|
|
66
|
-
written.push(mdcPath);
|
|
67
|
-
|
|
68
|
-
const agentsPath = path.join(cwd, "AGENTS.md");
|
|
69
|
-
await fs.writeFile(agentsPath, applyRulesBlock(await readIfExists(agentsPath)), "utf8");
|
|
70
|
-
written.push(agentsPath);
|
|
71
|
-
|
|
72
|
-
const claudePath = path.join(cwd, "CLAUDE.md");
|
|
73
|
-
const claudeText = await readIfExists(claudePath);
|
|
74
|
-
if (claudeText !== null) {
|
|
75
|
-
await fs.writeFile(claudePath, applyRulesBlock(claudeText), "utf8");
|
|
76
|
-
written.push(claudePath);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return written;
|
|
80
|
-
}
|