@intellectronica/ruler 0.3.20 → 0.3.21

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.
@@ -69,17 +69,35 @@ class GeminiCliAgent extends AgentsMdAgent_1.AgentsMdAgent {
69
69
  const mcpEnabled = agentConfig?.mcp?.enabled ?? true;
70
70
  if (mcpEnabled && rulerMcpJson) {
71
71
  const strategy = agentConfig?.mcp?.strategy ?? 'merge';
72
+ // Gemini CLI (since v0.21.0) no longer accepts the "type" field in MCP server entries.
73
+ // Following the MCP spec update from Nov 25, 2025, the transport type is now inferred
74
+ // from the presence of specific keys (command/args -> stdio, url -> sse/http).
75
+ // Strip 'type' field from all incoming servers before merging.
76
+ const stripTypeField = (servers) => {
77
+ const cleaned = {};
78
+ for (const [name, def] of Object.entries(servers)) {
79
+ if (def && typeof def === 'object') {
80
+ const copy = { ...def };
81
+ delete copy.type;
82
+ cleaned[name] = copy;
83
+ }
84
+ else {
85
+ cleaned[name] = def;
86
+ }
87
+ }
88
+ return cleaned;
89
+ };
72
90
  if (strategy === 'overwrite') {
73
91
  // For overwrite, preserve existing settings except MCP servers
74
92
  const incomingServers = rulerMcpJson.mcpServers || {};
75
- updated[this.getMcpServerKey()] = incomingServers;
93
+ updated[this.getMcpServerKey()] = stripTypeField(incomingServers);
76
94
  }
77
95
  else {
78
96
  // For merge strategy, merge with existing MCP servers
79
97
  const baseServers = existingSettings[this.getMcpServerKey()] || {};
80
98
  const incomingServers = rulerMcpJson.mcpServers || {};
81
99
  const mergedServers = { ...baseServers, ...incomingServers };
82
- updated[this.getMcpServerKey()] = mergedServers;
100
+ updated[this.getMcpServerKey()] = stripTypeField(mergedServers);
83
101
  }
84
102
  }
85
103
  await fs_1.promises.mkdir(path.dirname(settingsPath), { recursive: true });
@@ -43,11 +43,20 @@ const os = __importStar(require("os"));
43
43
  const fs = __importStar(require("fs/promises"));
44
44
  const constants_1 = require("../constants");
45
45
  const ConfigLoader_1 = require("../core/ConfigLoader");
46
+ function assertNotInsideRulerDir(projectRoot) {
47
+ const normalized = path.resolve(projectRoot);
48
+ const segments = normalized.split(path.sep);
49
+ if (segments.includes('.ruler')) {
50
+ console.error(`${constants_1.ERROR_PREFIX} Cannot run from inside a .ruler directory. Please run from your project root.`);
51
+ process.exit(1);
52
+ }
53
+ }
46
54
  /**
47
55
  * Handler for the 'apply' command.
48
56
  */
49
57
  async function applyHandler(argv) {
50
58
  const projectRoot = argv['project-root'];
59
+ assertNotInsideRulerDir(projectRoot);
51
60
  const agents = argv.agents
52
61
  ? argv.agents.split(',').map((a) => a.trim())
53
62
  : undefined;
@@ -192,6 +201,7 @@ async function initHandler(argv) {
192
201
  */
193
202
  async function revertHandler(argv) {
194
203
  const projectRoot = argv['project-root'];
204
+ assertNotInsideRulerDir(projectRoot);
195
205
  const agents = argv.agents
196
206
  ? argv.agents.split(',').map((a) => a.trim())
197
207
  : undefined;
@@ -577,7 +577,31 @@ async function applyStandardMcpConfiguration(agent, filteredMcpJson, dest, agent
577
577
  out[serverKey] = cleanedServers;
578
578
  return out;
579
579
  };
580
- const toWrite = sanitizeForFirebase(merged);
580
+ // Gemini CLI (since v0.21.0) no longer accepts the "type" field in MCP server entries.
581
+ // Following the MCP spec update from Nov 25, 2025, the transport type is now inferred
582
+ // from the presence of specific keys (command/args -> stdio, url -> sse/http).
583
+ // Sanitize merged config by stripping 'type' from each server when targeting Gemini.
584
+ const sanitizeForGemini = (obj) => {
585
+ if (agent.getIdentifier() !== 'gemini-cli')
586
+ return obj;
587
+ const out = { ...obj };
588
+ const servers = out[serverKey] || {};
589
+ const cleanedServers = {};
590
+ for (const [name, def] of Object.entries(servers)) {
591
+ if (def && typeof def === 'object') {
592
+ const copy = { ...def };
593
+ delete copy.type;
594
+ cleanedServers[name] = copy;
595
+ }
596
+ else {
597
+ cleanedServers[name] = def;
598
+ }
599
+ }
600
+ out[serverKey] = cleanedServers;
601
+ return out;
602
+ };
603
+ let toWrite = sanitizeForFirebase(merged);
604
+ toWrite = sanitizeForGemini(toWrite);
581
605
  // Only backup and write if content would actually change (idempotent)
582
606
  const currentContent = JSON.stringify(existing, null, 2);
583
607
  const newContent = JSON.stringify(toWrite, null, 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intellectronica/ruler",
3
- "version": "0.3.20",
3
+ "version": "0.3.21",
4
4
  "description": "Ruler — apply the same rules to all coding agents",
5
5
  "main": "dist/lib.js",
6
6
  "scripts": {