@intellectronica/ruler 0.3.40 → 0.3.42
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 +59 -46
- package/dist/agents/AbstractAgent.d.ts +53 -0
- package/dist/agents/AgentsMdAgent.d.ts +14 -0
- package/dist/agents/AiderAgent.d.ts +14 -0
- package/dist/agents/AiderAgent.js +3 -1
- package/dist/agents/AmazonQCliAgent.d.ts +13 -0
- package/dist/agents/AmpAgent.d.ts +6 -0
- package/dist/agents/AntigravityAgent.d.ts +10 -0
- package/dist/agents/AugmentCodeAgent.d.ts +13 -0
- package/dist/agents/ClaudeAgent.d.ts +13 -0
- package/dist/agents/ClineAgent.d.ts +9 -0
- package/dist/agents/CodexCliAgent.d.ts +31 -0
- package/dist/agents/CopilotAgent.d.ts +20 -0
- package/dist/agents/CrushAgent.d.ts +14 -0
- package/dist/agents/CrushAgent.js +5 -2
- package/dist/agents/CursorAgent.d.ts +17 -0
- package/dist/agents/FactoryDroidAgent.d.ts +13 -0
- package/dist/agents/FirebaseAgent.d.ts +11 -0
- package/dist/agents/FirebenderAgent.d.ts +36 -0
- package/dist/agents/GeminiCliAgent.d.ts +11 -0
- package/dist/agents/GeminiCliAgent.js +2 -2
- package/dist/agents/GooseAgent.d.ts +12 -0
- package/dist/agents/IAgent.d.ts +72 -0
- package/dist/agents/JetBrainsAiAssistantAgent.d.ts +10 -0
- package/dist/agents/JulesAgent.d.ts +5 -0
- package/dist/agents/JunieAgent.d.ts +12 -0
- package/dist/agents/KiloCodeAgent.d.ts +14 -0
- package/dist/agents/KiroAgent.d.ts +8 -0
- package/dist/agents/MistralVibeAgent.d.ts +31 -0
- package/dist/agents/OpenCodeAgent.d.ts +11 -0
- package/dist/agents/OpenCodeAgent.js +14 -9
- package/dist/agents/OpenHandsAgent.d.ts +8 -0
- package/dist/agents/PiAgent.d.ts +9 -0
- package/dist/agents/QwenCodeAgent.d.ts +10 -0
- package/dist/agents/QwenCodeAgent.js +2 -2
- package/dist/agents/RooCodeAgent.d.ts +16 -0
- package/dist/agents/TraeAgent.d.ts +10 -0
- package/dist/agents/WarpAgent.d.ts +12 -0
- package/dist/agents/WindsurfAgent.d.ts +13 -0
- package/dist/agents/ZedAgent.d.ts +21 -0
- package/dist/agents/ZedAgent.js +5 -2
- package/dist/agents/agent-utils.d.ts +5 -0
- package/dist/agents/agent-utils.js +8 -5
- package/dist/agents/index.d.ts +9 -0
- package/dist/cli/commands.d.ts +4 -0
- package/dist/cli/commands.js +2 -3
- package/dist/cli/handlers.d.ts +41 -0
- package/dist/cli/handlers.js +76 -60
- package/dist/cli/index.d.ts +2 -0
- package/dist/constants.d.ts +35 -0
- package/dist/core/ConfigLoader.d.ts +57 -0
- package/dist/core/ConfigLoader.js +123 -41
- package/dist/core/FileSystemUtils.d.ts +51 -0
- package/dist/core/FileSystemUtils.js +37 -17
- package/dist/core/GitignoreUtils.d.ts +15 -0
- package/dist/core/GitignoreUtils.js +32 -1
- package/dist/core/RuleProcessor.d.ts +8 -0
- package/dist/core/SkillsProcessor.d.ts +127 -0
- package/dist/core/SkillsProcessor.js +104 -218
- package/dist/core/SkillsUtils.d.ts +26 -0
- package/dist/core/SubagentsProcessor.d.ts +38 -0
- package/dist/core/SubagentsProcessor.js +68 -22
- package/dist/core/SubagentsUtils.d.ts +34 -0
- package/dist/core/UnifiedConfigLoader.d.ts +10 -0
- package/dist/core/UnifiedConfigLoader.js +61 -31
- package/dist/core/UnifiedConfigTypes.d.ts +95 -0
- package/dist/core/agent-selection.d.ts +12 -0
- package/dist/core/agent-selection.js +11 -3
- package/dist/core/apply-engine.d.ts +69 -0
- package/dist/core/apply-engine.js +57 -50
- package/dist/core/config-utils.d.ts +14 -0
- package/dist/core/config-utils.js +9 -3
- package/dist/core/hash.d.ts +2 -0
- package/dist/core/path-utils.d.ts +1 -0
- package/dist/core/path-utils.js +42 -0
- package/dist/core/revert-engine.d.ts +36 -0
- package/dist/core/revert-engine.js +70 -9
- package/dist/lib.d.ts +13 -0
- package/dist/lib.js +23 -5
- package/dist/mcp/capabilities.d.ts +20 -0
- package/dist/mcp/merge.d.ts +10 -0
- package/dist/mcp/merge.js +19 -1
- package/dist/mcp/propagateOpenCodeMcp.d.ts +2 -0
- package/dist/mcp/propagateOpenCodeMcp.js +21 -9
- package/dist/mcp/propagateOpenHandsMcp.d.ts +2 -0
- package/dist/mcp/propagateOpenHandsMcp.js +31 -15
- package/dist/mcp/validate.d.ts +7 -0
- package/dist/mcp/validate.js +6 -1
- package/dist/paths/mcp.d.ts +8 -0
- package/dist/paths/mcp.js +33 -4
- package/dist/revert.d.ts +6 -0
- package/dist/revert.js +39 -27
- package/dist/types.d.ts +87 -0
- package/dist/vscode/settings.d.ts +40 -0
- package/package.json +7 -4
package/dist/mcp/merge.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mergeMcp = mergeMcp;
|
|
4
|
+
const MCP_SERVER_KEYS = [
|
|
5
|
+
'mcp',
|
|
6
|
+
'mcpServers',
|
|
7
|
+
'servers',
|
|
8
|
+
'mcp_servers',
|
|
9
|
+
'context_servers',
|
|
10
|
+
];
|
|
4
11
|
/**
|
|
5
12
|
* Merge native and incoming MCP server configurations according to strategy.
|
|
6
13
|
* @param base Existing native MCP config object.
|
|
@@ -17,7 +24,14 @@ function mergeMcp(base, incoming, strategy, serverKey) {
|
|
|
17
24
|
incoming.mcpServers ||
|
|
18
25
|
incoming.mcp ||
|
|
19
26
|
{};
|
|
27
|
+
const preservedBase = { ...base };
|
|
28
|
+
for (const key of MCP_SERVER_KEYS) {
|
|
29
|
+
if (key !== serverKey) {
|
|
30
|
+
delete preservedBase[key];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
20
33
|
return {
|
|
34
|
+
...preservedBase,
|
|
21
35
|
[serverKey]: incomingServers,
|
|
22
36
|
};
|
|
23
37
|
}
|
|
@@ -31,7 +45,11 @@ function mergeMcp(base, incoming, strategy, serverKey) {
|
|
|
31
45
|
{};
|
|
32
46
|
const mergedServers = { ...baseServers, ...incomingServers };
|
|
33
47
|
const newBase = { ...base };
|
|
34
|
-
|
|
48
|
+
for (const key of MCP_SERVER_KEYS) {
|
|
49
|
+
if (key !== serverKey) {
|
|
50
|
+
delete newBase[key];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
35
53
|
return {
|
|
36
54
|
...newBase,
|
|
37
55
|
[serverKey]: mergedServers,
|
|
@@ -91,16 +91,26 @@ function transformToOpenCodeFormat(rulerMcp) {
|
|
|
91
91
|
mcp: openCodeServers,
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
|
-
async function propagateMcpToOpenCode(rulerMcpData, openCodeConfigPath, backup = true) {
|
|
94
|
+
async function propagateMcpToOpenCode(rulerMcpData, openCodeConfigPath, backup = true, strategy = 'merge') {
|
|
95
95
|
const rulerMcp = rulerMcpData || {};
|
|
96
96
|
// Read existing OpenCode config if it exists
|
|
97
97
|
let existingConfig = {};
|
|
98
|
+
let existingContent;
|
|
98
99
|
try {
|
|
99
|
-
|
|
100
|
-
existingConfig = JSON.parse(existingContent);
|
|
100
|
+
existingContent = await fs.readFile(openCodeConfigPath, 'utf8');
|
|
101
101
|
}
|
|
102
|
-
catch {
|
|
103
|
-
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error.code !== 'ENOENT') {
|
|
104
|
+
throw new Error(`Could not read OpenCode config at ${openCodeConfigPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (existingContent !== undefined) {
|
|
108
|
+
try {
|
|
109
|
+
existingConfig = JSON.parse(existingContent);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
throw new Error(`Invalid OpenCode config at ${openCodeConfigPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
113
|
+
}
|
|
104
114
|
}
|
|
105
115
|
// Transform ruler MCP to OpenCode format
|
|
106
116
|
const transformedConfig = transformToOpenCodeFormat(rulerMcp);
|
|
@@ -108,10 +118,12 @@ async function propagateMcpToOpenCode(rulerMcpData, openCodeConfigPath, backup =
|
|
|
108
118
|
const finalConfig = {
|
|
109
119
|
...existingConfig,
|
|
110
120
|
$schema: transformedConfig.$schema,
|
|
111
|
-
mcp:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
mcp: strategy === 'overwrite'
|
|
122
|
+
? transformedConfig.mcp
|
|
123
|
+
: {
|
|
124
|
+
...existingConfig.mcp,
|
|
125
|
+
...transformedConfig.mcp,
|
|
126
|
+
},
|
|
115
127
|
};
|
|
116
128
|
await (0, FileSystemUtils_1.ensureDirExists)(path.dirname(openCodeConfigPath));
|
|
117
129
|
if (backup) {
|
|
@@ -89,7 +89,7 @@ function normalizeRemoteServerArray(entries) {
|
|
|
89
89
|
// All entries are strings, keep as is
|
|
90
90
|
return entries;
|
|
91
91
|
}
|
|
92
|
-
async function propagateMcpToOpenHands(rulerMcpData, openHandsConfigPath, backup = true) {
|
|
92
|
+
async function propagateMcpToOpenHands(rulerMcpData, openHandsConfigPath, backup = true, strategy = 'merge') {
|
|
93
93
|
const rulerMcp = rulerMcpData || {};
|
|
94
94
|
// Always use the legacy Ruler MCP config format as input (top-level "mcpServers" key)
|
|
95
95
|
const rulerServers = rulerMcp.mcpServers || {};
|
|
@@ -100,12 +100,22 @@ async function propagateMcpToOpenHands(rulerMcpData, openHandsConfigPath, backup
|
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
102
|
let config = {};
|
|
103
|
+
let tomlContent;
|
|
103
104
|
try {
|
|
104
|
-
|
|
105
|
-
config = (0, toml_1.parse)(tomlContent);
|
|
105
|
+
tomlContent = await fs.readFile(openHandsConfigPath, 'utf8');
|
|
106
106
|
}
|
|
107
|
-
catch {
|
|
108
|
-
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error.code !== 'ENOENT') {
|
|
109
|
+
throw new Error(`Could not read OpenHands config at ${openHandsConfigPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (tomlContent !== undefined) {
|
|
113
|
+
try {
|
|
114
|
+
config = (0, toml_1.parse)(tomlContent);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
throw new Error(`Invalid OpenHands config at ${openHandsConfigPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
118
|
+
}
|
|
109
119
|
}
|
|
110
120
|
if (!config.mcp) {
|
|
111
121
|
config.mcp = {};
|
|
@@ -119,18 +129,24 @@ async function propagateMcpToOpenHands(rulerMcpData, openHandsConfigPath, backup
|
|
|
119
129
|
if (!config.mcp.shttp_servers) {
|
|
120
130
|
config.mcp.shttp_servers = [];
|
|
121
131
|
}
|
|
122
|
-
// Build maps for merging existing servers
|
|
123
|
-
const existingStdioServers = new Map(
|
|
132
|
+
// Build maps for merging existing servers, or start fresh when overwriting.
|
|
133
|
+
const existingStdioServers = new Map(strategy === 'overwrite'
|
|
134
|
+
? []
|
|
135
|
+
: config.mcp.stdio_servers.map((s) => [s.name, s]));
|
|
124
136
|
const existingSseServers = new Map();
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
137
|
+
if (strategy !== 'overwrite') {
|
|
138
|
+
config.mcp.sse_servers.forEach((entry) => {
|
|
139
|
+
const url = typeof entry === 'string' ? entry : entry.url;
|
|
140
|
+
existingSseServers.set(url, entry);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
129
143
|
const existingShttpServers = new Map();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
if (strategy !== 'overwrite') {
|
|
145
|
+
config.mcp.shttp_servers.forEach((entry) => {
|
|
146
|
+
const url = typeof entry === 'string' ? entry : entry.url;
|
|
147
|
+
existingShttpServers.set(url, entry);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
134
150
|
for (const [name, serverDef] of Object.entries(rulerServers)) {
|
|
135
151
|
if (isRulerMcpServer(serverDef)) {
|
|
136
152
|
if (serverDef.command) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate the structure of the Ruler MCP JSON config.
|
|
3
|
+
* Minimal validation: ensure 'mcpServers' property exists and is an object.
|
|
4
|
+
* @param data Parsed JSON object from .ruler/mcp.json.
|
|
5
|
+
* @throws Error if validation fails.
|
|
6
|
+
*/
|
|
7
|
+
export declare function validateMcp(data: unknown): void;
|
package/dist/mcp/validate.js
CHANGED
|
@@ -8,10 +8,15 @@ exports.validateMcp = validateMcp;
|
|
|
8
8
|
* @throws Error if validation fails.
|
|
9
9
|
*/
|
|
10
10
|
function validateMcp(data) {
|
|
11
|
+
const mcpServers = data && typeof data === 'object'
|
|
12
|
+
? data.mcpServers
|
|
13
|
+
: undefined;
|
|
11
14
|
if (!data ||
|
|
12
15
|
typeof data !== 'object' ||
|
|
13
16
|
!('mcpServers' in data) ||
|
|
14
|
-
|
|
17
|
+
!mcpServers ||
|
|
18
|
+
typeof mcpServers !== 'object' ||
|
|
19
|
+
Array.isArray(mcpServers)) {
|
|
15
20
|
throw new Error('[ruler] Invalid MCP config: must contain an object property "mcpServers" (Ruler style)');
|
|
16
21
|
}
|
|
17
22
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Determine the native MCP config path for a given agent. */
|
|
2
|
+
export declare function getNativeMcpPath(adapterName: string, projectRoot: string): Promise<string | null>;
|
|
3
|
+
/** Read native MCP config from disk, or return empty object if missing. */
|
|
4
|
+
export declare function readNativeMcp(filePath: string): Promise<Record<string, unknown>>;
|
|
5
|
+
/** Read native Codex TOML MCP config from disk, or return empty object if missing. */
|
|
6
|
+
export declare function readNativeMcpToml(filePath: string, parseToml: (text: string) => Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
7
|
+
/** Write native MCP config to disk, creating parent directories as needed. */
|
|
8
|
+
export declare function writeNativeMcp(filePath: string, data: unknown): Promise<void>;
|
package/dist/paths/mcp.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getNativeMcpPath = getNativeMcpPath;
|
|
37
37
|
exports.readNativeMcp = readNativeMcp;
|
|
38
|
+
exports.readNativeMcpToml = readNativeMcpToml;
|
|
38
39
|
exports.writeNativeMcp = writeNativeMcp;
|
|
39
40
|
const path = __importStar(require("path"));
|
|
40
41
|
const fs_1 = require("fs");
|
|
@@ -111,14 +112,42 @@ async function getNativeMcpPath(adapterName, projectRoot) {
|
|
|
111
112
|
// default to first candidate if none exist
|
|
112
113
|
return candidates.length > 0 ? candidates[0] : null;
|
|
113
114
|
}
|
|
114
|
-
/** Read native MCP config from disk, or return empty object if missing
|
|
115
|
+
/** Read native MCP config from disk, or return empty object if missing. */
|
|
115
116
|
async function readNativeMcp(filePath) {
|
|
117
|
+
let text;
|
|
118
|
+
try {
|
|
119
|
+
text = await fs_1.promises.readFile(filePath, 'utf8');
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
if (error.code === 'ENOENT') {
|
|
123
|
+
return {};
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`Could not read MCP config at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
126
|
+
}
|
|
116
127
|
try {
|
|
117
|
-
const text = await fs_1.promises.readFile(filePath, 'utf8');
|
|
118
128
|
return JSON.parse(text);
|
|
119
129
|
}
|
|
120
|
-
catch {
|
|
121
|
-
|
|
130
|
+
catch (error) {
|
|
131
|
+
throw new Error(`Invalid MCP config at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/** Read native Codex TOML MCP config from disk, or return empty object if missing. */
|
|
135
|
+
async function readNativeMcpToml(filePath, parseToml) {
|
|
136
|
+
let text;
|
|
137
|
+
try {
|
|
138
|
+
text = await fs_1.promises.readFile(filePath, 'utf8');
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error.code === 'ENOENT') {
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`Could not read MCP config at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return parseToml(text);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
throw new Error(`Invalid MCP config at ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
122
151
|
}
|
|
123
152
|
}
|
|
124
153
|
/** Write native MCP config to disk, creating parent directories as needed. */
|
package/dist/revert.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { allAgents } from './agents';
|
|
2
|
+
export { allAgents };
|
|
3
|
+
/**
|
|
4
|
+
* Reverts ruler configurations for selected AI agents.
|
|
5
|
+
*/
|
|
6
|
+
export declare function revertAllAgentConfigs(projectRoot: string, includedAgents?: string[], configPath?: string, keepBackups?: boolean, verbose?: boolean, dryRun?: boolean, localOnly?: boolean): Promise<void>;
|
package/dist/revert.js
CHANGED
|
@@ -35,7 +35,6 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.allAgents = void 0;
|
|
37
37
|
exports.revertAllAgentConfigs = revertAllAgentConfigs;
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
38
|
const fs_1 = require("fs");
|
|
40
39
|
const FileSystemUtils = __importStar(require("./core/FileSystemUtils"));
|
|
41
40
|
const ConfigLoader_1 = require("./core/ConfigLoader");
|
|
@@ -45,7 +44,11 @@ const constants_1 = require("./constants");
|
|
|
45
44
|
const revert_engine_1 = require("./core/revert-engine");
|
|
46
45
|
const agent_selection_1 = require("./core/agent-selection");
|
|
47
46
|
const config_utils_1 = require("./core/config-utils");
|
|
47
|
+
const GitignoreUtils_1 = require("./core/GitignoreUtils");
|
|
48
48
|
const agents = agents_1.allAgents;
|
|
49
|
+
const RULER_IGNORE_START_MARKER = '# START Ruler Generated Files';
|
|
50
|
+
const RULER_IGNORE_END_MARKER = '# END Ruler Generated Files';
|
|
51
|
+
const MANAGED_IGNORE_FILES = ['.gitignore', '.git/info/exclude'];
|
|
49
52
|
/**
|
|
50
53
|
* Reverts ruler configurations for selected AI agents.
|
|
51
54
|
*/
|
|
@@ -55,6 +58,7 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
|
|
|
55
58
|
projectRoot,
|
|
56
59
|
cliAgents: includedAgents,
|
|
57
60
|
configPath,
|
|
61
|
+
checkGlobal: !localOnly,
|
|
58
62
|
});
|
|
59
63
|
const rulerDir = await FileSystemUtils.findRulerDir(projectRoot, !localOnly);
|
|
60
64
|
if (!rulerDir) {
|
|
@@ -77,18 +81,19 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
|
|
|
77
81
|
// Fall back to the old logic without validation
|
|
78
82
|
if (config.cliAgents && config.cliAgents.length > 0) {
|
|
79
83
|
const filters = config.cliAgents.map((n) => n.toLowerCase());
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
const validAgentIdentifiers = new Set(agents.map((agent) => agent.getIdentifier()));
|
|
85
|
+
selected = agents.filter((agent) => filters.some((f) => (0, agent_selection_1.agentMatchesFilter)(agent, f, validAgentIdentifiers)));
|
|
82
86
|
}
|
|
83
87
|
else if (config.defaultAgents && config.defaultAgents.length > 0) {
|
|
84
88
|
const defaults = config.defaultAgents.map((n) => n.toLowerCase());
|
|
89
|
+
const validAgentIdentifiers = new Set(agents.map((agent) => agent.getIdentifier()));
|
|
85
90
|
selected = agents.filter((agent) => {
|
|
86
91
|
const identifier = agent.getIdentifier();
|
|
87
92
|
const override = config.agentConfigs[identifier]?.enabled;
|
|
88
93
|
if (override !== undefined) {
|
|
89
94
|
return override;
|
|
90
95
|
}
|
|
91
|
-
return defaults.some((d) =>
|
|
96
|
+
return defaults.some((d) => (0, agent_selection_1.agentMatchesFilter)(agent, d, validAgentIdentifiers));
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
99
|
else {
|
|
@@ -118,10 +123,10 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
|
|
|
118
123
|
// Clean up auxiliary files and directories
|
|
119
124
|
const cleanupResult = await (0, revert_engine_1.cleanUpAuxiliaryFiles)(projectRoot, verbose, dryRun);
|
|
120
125
|
totalFilesRemoved += cleanupResult.additionalFilesRemoved;
|
|
121
|
-
// Clean
|
|
122
|
-
const
|
|
123
|
-
? await
|
|
124
|
-
:
|
|
126
|
+
// Clean managed ignore blocks if reverting all agents.
|
|
127
|
+
const cleanedIgnoreFiles = !config.cliAgents || config.cliAgents.length === 0
|
|
128
|
+
? await cleanManagedIgnoreFiles(projectRoot, verbose, dryRun)
|
|
129
|
+
: [];
|
|
125
130
|
// Display summary
|
|
126
131
|
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
127
132
|
if (dryRun) {
|
|
@@ -139,47 +144,54 @@ async function revertAllAgentConfigs(projectRoot, includedAgents, configPath, ke
|
|
|
139
144
|
if (cleanupResult.directoriesRemoved > 0) {
|
|
140
145
|
console.log(` Empty directories removed: ${cleanupResult.directoriesRemoved}`);
|
|
141
146
|
}
|
|
142
|
-
|
|
143
|
-
console.log(`
|
|
147
|
+
for (const ignoreFile of cleanedIgnoreFiles) {
|
|
148
|
+
console.log(` ${ignoreFile} cleaned: yes`);
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
/**
|
|
147
|
-
* Removes the ruler-managed block from
|
|
152
|
+
* Removes the ruler-managed block from ignore files Ruler can update.
|
|
148
153
|
*/
|
|
149
|
-
async function
|
|
150
|
-
const
|
|
154
|
+
async function cleanManagedIgnoreFiles(projectRoot, verbose, dryRun) {
|
|
155
|
+
const cleanedFiles = [];
|
|
156
|
+
for (const ignoreFile of MANAGED_IGNORE_FILES) {
|
|
157
|
+
if (await cleanIgnoreFile(projectRoot, ignoreFile, verbose, dryRun)) {
|
|
158
|
+
cleanedFiles.push(ignoreFile);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return cleanedFiles;
|
|
162
|
+
}
|
|
163
|
+
async function cleanIgnoreFile(projectRoot, ignoreFile, verbose, dryRun) {
|
|
164
|
+
const ignorePath = await (0, GitignoreUtils_1.resolveIgnoreFilePath)(projectRoot, ignoreFile);
|
|
151
165
|
try {
|
|
152
|
-
await fs_1.promises.access(
|
|
166
|
+
await fs_1.promises.access(ignorePath);
|
|
153
167
|
}
|
|
154
168
|
catch {
|
|
155
|
-
(0, constants_1.logVerbose)(
|
|
169
|
+
(0, constants_1.logVerbose)(`No ${ignoreFile} file found`, verbose);
|
|
156
170
|
return false;
|
|
157
171
|
}
|
|
158
|
-
const content = await fs_1.promises.readFile(
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const startIndex = content.indexOf(startMarker);
|
|
162
|
-
const endIndex = content.indexOf(endMarker);
|
|
172
|
+
const content = await fs_1.promises.readFile(ignorePath, 'utf8');
|
|
173
|
+
const startIndex = content.indexOf(RULER_IGNORE_START_MARKER);
|
|
174
|
+
const endIndex = content.indexOf(RULER_IGNORE_END_MARKER);
|
|
163
175
|
if (startIndex === -1 || endIndex === -1) {
|
|
164
|
-
(0, constants_1.logVerbose)(
|
|
176
|
+
(0, constants_1.logVerbose)(`No ruler-managed block found in ${ignoreFile}`, verbose);
|
|
165
177
|
return false;
|
|
166
178
|
}
|
|
167
179
|
const prefix = (0, constants_1.actionPrefix)(dryRun);
|
|
168
180
|
if (dryRun) {
|
|
169
|
-
(0, constants_1.logVerbose)(`${prefix} Would remove ruler block from
|
|
181
|
+
(0, constants_1.logVerbose)(`${prefix} Would remove ruler block from ${ignoreFile}`, verbose);
|
|
170
182
|
}
|
|
171
183
|
else {
|
|
172
184
|
const beforeBlock = content.substring(0, startIndex);
|
|
173
|
-
const afterBlock = content.substring(endIndex +
|
|
185
|
+
const afterBlock = content.substring(endIndex + RULER_IGNORE_END_MARKER.length);
|
|
174
186
|
let newContent = beforeBlock + afterBlock;
|
|
175
187
|
newContent = newContent.replace(/\n{3,}/g, '\n\n'); // Replace 3+ newlines with 2
|
|
176
188
|
if (newContent.trim() === '') {
|
|
177
|
-
await fs_1.promises.unlink(
|
|
178
|
-
(0, constants_1.logVerbose)(`${prefix} Removed empty
|
|
189
|
+
await fs_1.promises.unlink(ignorePath);
|
|
190
|
+
(0, constants_1.logVerbose)(`${prefix} Removed empty ${ignoreFile} file`, verbose);
|
|
179
191
|
}
|
|
180
192
|
else {
|
|
181
|
-
await fs_1.promises.writeFile(
|
|
182
|
-
(0, constants_1.logVerbose)(`${prefix} Removed ruler block from
|
|
193
|
+
await fs_1.promises.writeFile(ignorePath, newContent);
|
|
194
|
+
(0, constants_1.logVerbose)(`${prefix} Removed ruler block from ${ignoreFile}`, verbose);
|
|
183
195
|
}
|
|
184
196
|
}
|
|
185
197
|
return true;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for Model Context Protocol (MCP) server configuration.
|
|
3
|
+
*/
|
|
4
|
+
export type McpStrategy = 'merge' | 'overwrite';
|
|
5
|
+
/** MCP configuration for an agent or global. */
|
|
6
|
+
export interface McpConfig {
|
|
7
|
+
/** Enable or disable MCP propagation (merge or overwrite). */
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
/** Merge strategy: 'merge' to merge servers, 'overwrite' to replace config. */
|
|
10
|
+
strategy?: McpStrategy;
|
|
11
|
+
}
|
|
12
|
+
/** Global MCP configuration section (same as agent-specific config). */
|
|
13
|
+
export type GlobalMcpConfig = McpConfig;
|
|
14
|
+
/** Gitignore configuration for automatic .gitignore file updates. */
|
|
15
|
+
export interface GitignoreConfig {
|
|
16
|
+
/** Enable or disable automatic .gitignore updates. */
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
/** Write managed ignore entries to .git/info/exclude instead of .gitignore. */
|
|
19
|
+
local?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Backup configuration for .bak file generation. */
|
|
22
|
+
export interface BackupConfig {
|
|
23
|
+
/** Enable or disable creation of .bak backup files. */
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Skills configuration for automatic skills distribution. */
|
|
27
|
+
export interface SkillsConfig {
|
|
28
|
+
/** Enable or disable skills support. */
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Information about a discovered skill. */
|
|
32
|
+
export interface SkillInfo {
|
|
33
|
+
/** Name of the skill (directory name). */
|
|
34
|
+
name: string;
|
|
35
|
+
/** Absolute path to the skill directory. */
|
|
36
|
+
path: string;
|
|
37
|
+
/** Whether the directory contains a SKILL.md file. */
|
|
38
|
+
hasSkillMd: boolean;
|
|
39
|
+
/** Whether this is a valid skill. */
|
|
40
|
+
valid: boolean;
|
|
41
|
+
/** Error message if invalid. */
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Subagents configuration for automatic subagent distribution. */
|
|
45
|
+
export interface SubagentsConfig {
|
|
46
|
+
/** Enable or disable subagents support. */
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* When true, Ruler may delete previously generated native subagent
|
|
50
|
+
* directories that are stale (disabled, no source definitions, or
|
|
51
|
+
* deselected targets). Defaults to false (non-destructive).
|
|
52
|
+
*/
|
|
53
|
+
cleanup_orphaned?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* When true, `.ruler/agents/*.md` are also concatenated into the
|
|
56
|
+
* generated top-level rule files (CLAUDE.md, AGENTS.md, Copilot
|
|
57
|
+
* instructions, etc.). When false (default), `.ruler/agents/` is
|
|
58
|
+
* skipped during rule concatenation, mirroring `.ruler/skills/`.
|
|
59
|
+
*/
|
|
60
|
+
include_in_rules?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/** Frontmatter fields recognised on a source subagent definition. */
|
|
63
|
+
export interface SubagentFrontmatter {
|
|
64
|
+
name: string;
|
|
65
|
+
description: string;
|
|
66
|
+
tools?: string[];
|
|
67
|
+
model?: string;
|
|
68
|
+
readonly?: boolean;
|
|
69
|
+
is_background?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** Information about a discovered subagent. */
|
|
72
|
+
export interface SubagentInfo {
|
|
73
|
+
/** Name of the subagent (filename stem and frontmatter `name`). */
|
|
74
|
+
name: string;
|
|
75
|
+
/** Absolute path to the source `.md` file. */
|
|
76
|
+
path: string;
|
|
77
|
+
/** Relative `.md` path under `.ruler/agents/` (preserves nested layout). */
|
|
78
|
+
sourceRelativePath?: string;
|
|
79
|
+
/** Parsed frontmatter (only present when valid). */
|
|
80
|
+
frontmatter?: SubagentFrontmatter;
|
|
81
|
+
/** Body content after the frontmatter delimiter. */
|
|
82
|
+
body?: string;
|
|
83
|
+
/** Whether this subagent passed validation. */
|
|
84
|
+
valid: boolean;
|
|
85
|
+
/** Error message if invalid. */
|
|
86
|
+
error?: string;
|
|
87
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { McpStrategy } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* VSCode settings.json structure for Augment MCP configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface VSCodeSettings {
|
|
6
|
+
'augment.advanced'?: {
|
|
7
|
+
mcpServers?: AugmentMcpServer[];
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
};
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Augment MCP server configuration format
|
|
14
|
+
*/
|
|
15
|
+
export interface AugmentMcpServer {
|
|
16
|
+
name: string;
|
|
17
|
+
command: string;
|
|
18
|
+
args?: string[];
|
|
19
|
+
env?: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read VSCode settings.json file
|
|
23
|
+
*/
|
|
24
|
+
export declare function readVSCodeSettings(settingsPath: string): Promise<VSCodeSettings>;
|
|
25
|
+
/**
|
|
26
|
+
* Write VSCode settings.json file
|
|
27
|
+
*/
|
|
28
|
+
export declare function writeVSCodeSettings(settingsPath: string, settings: VSCodeSettings): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Transform ruler MCP config to Augment MCP server array format
|
|
31
|
+
*/
|
|
32
|
+
export declare function transformRulerToAugmentMcp(rulerMcpJson: Record<string, unknown>): AugmentMcpServer[];
|
|
33
|
+
/**
|
|
34
|
+
* Merge MCP servers into VSCode settings using the specified strategy
|
|
35
|
+
*/
|
|
36
|
+
export declare function mergeAugmentMcpServers(existingSettings: VSCodeSettings, newServers: AugmentMcpServer[], strategy: McpStrategy): VSCodeSettings;
|
|
37
|
+
/**
|
|
38
|
+
* Get the VSCode settings.json path for a project (local)
|
|
39
|
+
*/
|
|
40
|
+
export declare function getVSCodeSettingsPath(projectRoot: string): string;
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intellectronica/ruler",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.42",
|
|
4
4
|
"description": "Ruler — apply the same rules to all coding agents",
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
|
+
"types": "dist/lib.d.ts",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
8
|
-
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
|
9
|
-
"
|
|
9
|
+
"format": "prettier --write package.json package-lock.json tsconfig.json README.md \".github/**/*.yml\" \"src/**/*.{ts,tsx,json,md}\" \"tests/**/*.{ts,tsx,json,md}\"",
|
|
10
|
+
"format:check": "prettier --check package.json package-lock.json tsconfig.json README.md \".github/**/*.yml\" \"src/**/*.{ts,tsx,json,md}\" \"tests/**/*.{ts,tsx,json,md}\"",
|
|
11
|
+
"test": "jest --coverage",
|
|
10
12
|
"test:watch": "jest --watch",
|
|
11
13
|
"test:coverage": "jest --coverage",
|
|
12
14
|
"test:integration": "jest tests/e2e/ruler.integration.test.ts --verbose",
|
|
@@ -58,6 +60,7 @@
|
|
|
58
60
|
"eslint-config-prettier": "^10.1.8",
|
|
59
61
|
"eslint-plugin-prettier": "^5.5.4",
|
|
60
62
|
"jest": "^29.7.0",
|
|
63
|
+
"jest-util": "^29.7.0",
|
|
61
64
|
"prettier": "^3.6.2",
|
|
62
65
|
"ts-jest": "^29.4.5",
|
|
63
66
|
"typescript": "^5.9.3",
|
|
@@ -65,7 +68,7 @@
|
|
|
65
68
|
},
|
|
66
69
|
"dependencies": {
|
|
67
70
|
"@iarna/toml": "^2.2.5",
|
|
68
|
-
"js-yaml": "^4.1.
|
|
71
|
+
"js-yaml": "^4.1.1",
|
|
69
72
|
"yargs": "^18.0.0",
|
|
70
73
|
"zod": "^4.1.12"
|
|
71
74
|
}
|