@xfe-repo/cli-plugin-ai-rules 2.0.7 → 2.0.9
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/CHANGELOG.md +15 -0
- package/dist/formats/mcp-json.d.ts +1 -0
- package/dist/formats/mcp-json.js +61 -11
- package/dist/formats/mcp-toml.d.ts +1 -0
- package/dist/formats/mcp-toml.js +61 -18
- package/dist/index.js +8 -4
- package/dist/pipeline.js +16 -3
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @xfe-repo/cli-plugin-ai-rules
|
|
2
2
|
|
|
3
|
+
## 2.0.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 修复deploy 优化mcp
|
|
8
|
+
|
|
9
|
+
## 2.0.8
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 新增deploy自动部署支持
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @xfe-repo/cli-core@2.1.0
|
|
16
|
+
- @xfe-repo/cli-plugin-git@2.0.8
|
|
17
|
+
|
|
3
18
|
## 2.0.7
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -21,5 +21,6 @@ export interface PatchResult {
|
|
|
21
21
|
hashes: Record<string, string>;
|
|
22
22
|
}
|
|
23
23
|
export declare function patchMcpJsonInjections(config: AiConfig, projectRoot: string, injections: InjectionRegistry): Promise<PatchResult>;
|
|
24
|
+
export declare function stripMcpJsonInjections(content: string): string;
|
|
24
25
|
export declare function getMcpJsonGitignore(target: TargetConfig): string[];
|
|
25
26
|
//# sourceMappingURL=mcp-json.d.ts.map
|
package/dist/formats/mcp-json.js
CHANGED
|
@@ -14,6 +14,7 @@ import { getTarget } from '../targets.js';
|
|
|
14
14
|
import { computeHash } from './shared.js';
|
|
15
15
|
// ─── Constants ──────
|
|
16
16
|
const MCP_SERVER_PREFIX = 'xfe:';
|
|
17
|
+
const getInjectedServerPrefix = (source) => `${MCP_SERVER_PREFIX}${source}:`;
|
|
17
18
|
// ─── Plan ──────
|
|
18
19
|
/**
|
|
19
20
|
* 将 MCP server 项按 target 转换为 PlannedFile
|
|
@@ -23,8 +24,8 @@ const MCP_SERVER_PREFIX = 'xfe:';
|
|
|
23
24
|
export function planMcpJsonFiles(target, items, injections) {
|
|
24
25
|
if (!isMcpJsonTarget(target))
|
|
25
26
|
return [];
|
|
26
|
-
const syncServers =
|
|
27
|
-
const injectionServers = injections ?
|
|
27
|
+
const syncServers = buildSyncServerEntries(items.filter((i) => i.type === SyncItemType.McpServer));
|
|
28
|
+
const injectionServers = injections ? buildInjectedServerEntries(injections) : {};
|
|
28
29
|
const servers = { ...syncServers, ...injectionServers };
|
|
29
30
|
if (Object.keys(servers).length === 0)
|
|
30
31
|
return [];
|
|
@@ -44,10 +45,11 @@ export async function writeMcpJsonFile(file, projectRoot) {
|
|
|
44
45
|
await mkdir(path.dirname(absPath), { recursive: true });
|
|
45
46
|
const merged = await mergeWithExisting(absPath, file.content);
|
|
46
47
|
await writeFile(absPath, merged, 'utf-8');
|
|
47
|
-
return computeHash(merged);
|
|
48
|
+
return computeHash(stripMcpJsonInjections(merged));
|
|
48
49
|
}
|
|
49
50
|
export async function patchMcpJsonInjections(config, projectRoot, injections) {
|
|
50
|
-
const servers =
|
|
51
|
+
const servers = buildInjectedServerEntries(injections);
|
|
52
|
+
const injectedPrefixes = collectInjectedServerPrefixes(injections);
|
|
51
53
|
const isEmpty = Object.keys(servers).length === 0;
|
|
52
54
|
let written = 0;
|
|
53
55
|
let removed = 0;
|
|
@@ -68,28 +70,48 @@ export async function patchMcpJsonInjections(config, projectRoot, injections) {
|
|
|
68
70
|
const rootKey = target.mcpRootKey ?? 'mcpServers';
|
|
69
71
|
const parsed = JSON.parse(content);
|
|
70
72
|
const existing = parsed[rootKey] ?? {};
|
|
71
|
-
const
|
|
73
|
+
const keptServers = filterInjectedServers(existing, injectedPrefixes);
|
|
72
74
|
if (isEmpty) {
|
|
73
|
-
if (Object.keys(existing).
|
|
74
|
-
parsed[rootKey] =
|
|
75
|
+
if (Object.keys(existing).length !== Object.keys(keptServers).length) {
|
|
76
|
+
parsed[rootKey] = keptServers;
|
|
75
77
|
await writeFile(absPath, JSON.stringify(parsed, null, 2) + '\n', 'utf-8');
|
|
76
78
|
removed++;
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
else {
|
|
80
|
-
parsed[rootKey] = { ...
|
|
82
|
+
parsed[rootKey] = { ...keptServers, ...servers };
|
|
81
83
|
await writeFile(absPath, JSON.stringify(parsed, null, 2) + '\n', 'utf-8');
|
|
82
84
|
written++;
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
return { written, removed, hashes: {} };
|
|
86
88
|
}
|
|
89
|
+
export function stripMcpJsonInjections(content) {
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(content);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return content;
|
|
96
|
+
}
|
|
97
|
+
let changed = false;
|
|
98
|
+
for (const [rootKey, value] of Object.entries(parsed)) {
|
|
99
|
+
if (!isRecord(value))
|
|
100
|
+
continue;
|
|
101
|
+
const keptServers = filterInjectedServers(value, []);
|
|
102
|
+
if (Object.keys(keptServers).length === Object.keys(value).length)
|
|
103
|
+
continue;
|
|
104
|
+
parsed[rootKey] = keptServers;
|
|
105
|
+
changed = true;
|
|
106
|
+
}
|
|
107
|
+
return changed ? JSON.stringify(parsed, null, 2) + '\n' : content;
|
|
108
|
+
}
|
|
87
109
|
// ─── Gitignore ──────
|
|
88
110
|
export function getMcpJsonGitignore(target) {
|
|
89
111
|
return isMcpJsonTarget(target) ? [target.mcp] : [];
|
|
90
112
|
}
|
|
91
113
|
// ─── Helpers ──────
|
|
92
|
-
function
|
|
114
|
+
function buildSyncServerEntries(items) {
|
|
93
115
|
const servers = {};
|
|
94
116
|
for (const item of items) {
|
|
95
117
|
if (!item.serverConfig)
|
|
@@ -98,12 +120,23 @@ function buildServerEntries(items) {
|
|
|
98
120
|
}
|
|
99
121
|
return servers;
|
|
100
122
|
}
|
|
123
|
+
function buildInjectedServerEntries(injections) {
|
|
124
|
+
const servers = {};
|
|
125
|
+
for (const [source, items] of injections) {
|
|
126
|
+
for (const item of items) {
|
|
127
|
+
if (item.type !== SyncItemType.McpServer || !item.serverConfig)
|
|
128
|
+
continue;
|
|
129
|
+
servers[`${getInjectedServerPrefix(source)}${item.id}`] = item.serverConfig;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return servers;
|
|
133
|
+
}
|
|
101
134
|
function buildMcpJson(target, servers) {
|
|
102
135
|
const rootKey = target.mcpRootKey ?? 'mcpServers';
|
|
103
136
|
return JSON.stringify({ [rootKey]: servers }, null, 2) + '\n';
|
|
104
137
|
}
|
|
105
|
-
function
|
|
106
|
-
return [...injections.
|
|
138
|
+
function collectInjectedServerPrefixes(injections) {
|
|
139
|
+
return [...injections.keys()].map(getInjectedServerPrefix);
|
|
107
140
|
}
|
|
108
141
|
function filterUserServers(servers) {
|
|
109
142
|
const user = {};
|
|
@@ -113,6 +146,23 @@ function filterUserServers(servers) {
|
|
|
113
146
|
}
|
|
114
147
|
return user;
|
|
115
148
|
}
|
|
149
|
+
function filterInjectedServers(servers, injectedPrefixes) {
|
|
150
|
+
const kept = {};
|
|
151
|
+
for (const [key, value] of Object.entries(servers)) {
|
|
152
|
+
if (isInjectedServerKey(key, injectedPrefixes))
|
|
153
|
+
continue;
|
|
154
|
+
kept[key] = value;
|
|
155
|
+
}
|
|
156
|
+
return kept;
|
|
157
|
+
}
|
|
158
|
+
function isInjectedServerKey(key, injectedPrefixes) {
|
|
159
|
+
if (injectedPrefixes.some((prefix) => key.startsWith(prefix)))
|
|
160
|
+
return true;
|
|
161
|
+
return key.startsWith(MCP_SERVER_PREFIX) && key.slice(MCP_SERVER_PREFIX.length).includes(':');
|
|
162
|
+
}
|
|
163
|
+
function isRecord(value) {
|
|
164
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
165
|
+
}
|
|
116
166
|
async function mergeWithExisting(absPath, newContent) {
|
|
117
167
|
if (!existsSync(absPath))
|
|
118
168
|
return newContent;
|
|
@@ -17,5 +17,6 @@ export interface PatchResult {
|
|
|
17
17
|
hashes: Record<string, string>;
|
|
18
18
|
}
|
|
19
19
|
export declare function patchMcpTomlInjections(config: AiConfig, projectRoot: string, injections: InjectionRegistry): Promise<PatchResult>;
|
|
20
|
+
export declare function stripMcpTomlInjections(content: string): string;
|
|
20
21
|
export declare function getMcpTomlGitignore(target: TargetConfig): string[];
|
|
21
22
|
//# sourceMappingURL=mcp-toml.d.ts.map
|
package/dist/formats/mcp-toml.js
CHANGED
|
@@ -19,6 +19,8 @@ const DEFAULT_SERVER_KEY = 'mcp_server';
|
|
|
19
19
|
const SERVER_KEY_HASH_LENGTH = 8;
|
|
20
20
|
const MANAGED_BLOCK_BEGIN = '# xfe-ai-rules:mcp:begin';
|
|
21
21
|
const MANAGED_BLOCK_END = '# xfe-ai-rules:mcp:end';
|
|
22
|
+
const INJECTED_BLOCK_BEGIN = '# xfe-ai-rules:mcp-injected:begin';
|
|
23
|
+
const INJECTED_BLOCK_END = '# xfe-ai-rules:mcp-injected:end';
|
|
22
24
|
const TOML_BARE_KEY_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
23
25
|
const SERVER_KEY_INVALID_CHARS_PATTERN = /[^a-z0-9]+/g;
|
|
24
26
|
const SERVER_KEY_EDGE_UNDERSCORES_PATTERN = /^_+|_+$/g;
|
|
@@ -28,16 +30,17 @@ export function planMcpTomlFiles(target, items, injections) {
|
|
|
28
30
|
if (!isMcpTomlTarget(target))
|
|
29
31
|
return [];
|
|
30
32
|
const syncServers = buildServerEntries(items.filter((i) => i.type === SyncItemType.McpServer));
|
|
31
|
-
const injectionServers = injections ?
|
|
32
|
-
const
|
|
33
|
-
|
|
33
|
+
const injectionServers = injections ? buildInjectedServerEntries(injections) : {};
|
|
34
|
+
const hasSyncServers = Object.keys(syncServers).length > 0;
|
|
35
|
+
const hasInjectionServers = Object.keys(injectionServers).length > 0;
|
|
36
|
+
if (!hasSyncServers && !hasInjectionServers)
|
|
34
37
|
return [];
|
|
35
38
|
return [
|
|
36
39
|
{
|
|
37
40
|
relativePath: target.mcp,
|
|
38
|
-
content: buildMcpToml(target,
|
|
41
|
+
content: buildMcpToml(target, { syncServers, injectionServers }),
|
|
39
42
|
action: 'write',
|
|
40
|
-
source:
|
|
43
|
+
source: hasSyncServers ? 'sync' : 'injection',
|
|
41
44
|
format: 'mcp-toml',
|
|
42
45
|
},
|
|
43
46
|
];
|
|
@@ -48,10 +51,10 @@ export async function writeMcpTomlFile(file, projectRoot) {
|
|
|
48
51
|
await mkdir(path.dirname(absPath), { recursive: true });
|
|
49
52
|
const merged = await mergeWithExisting(absPath, file.content);
|
|
50
53
|
await writeFile(absPath, merged, 'utf-8');
|
|
51
|
-
return computeHash(merged);
|
|
54
|
+
return computeHash(stripMcpTomlInjections(merged));
|
|
52
55
|
}
|
|
53
56
|
export async function patchMcpTomlInjections(config, projectRoot, injections) {
|
|
54
|
-
const servers =
|
|
57
|
+
const servers = buildInjectedServerEntries(injections);
|
|
55
58
|
const isEmpty = Object.keys(servers).length === 0;
|
|
56
59
|
let written = 0;
|
|
57
60
|
let removed = 0;
|
|
@@ -63,15 +66,13 @@ export async function patchMcpTomlInjections(config, projectRoot, injections) {
|
|
|
63
66
|
if (!existsSync(absPath)) {
|
|
64
67
|
if (!isEmpty) {
|
|
65
68
|
await mkdir(path.dirname(absPath), { recursive: true });
|
|
66
|
-
await writeFile(absPath, buildMcpToml(target,
|
|
69
|
+
await writeFile(absPath, buildMcpToml(target, { syncServers: {}, injectionServers: servers }), 'utf-8');
|
|
67
70
|
written++;
|
|
68
71
|
}
|
|
69
72
|
continue;
|
|
70
73
|
}
|
|
71
74
|
const content = await readFile(absPath, 'utf-8');
|
|
72
|
-
const updated =
|
|
73
|
-
? removeManagedBlock(content)
|
|
74
|
-
: mergeTomlContent(content, buildMcpToml(target, servers, true), getRootKey(target));
|
|
75
|
+
const updated = mergeInjectedTomlContent(content, buildMcpToml(target, { syncServers: {}, injectionServers: servers }), getRootKey(target));
|
|
75
76
|
if (updated === content)
|
|
76
77
|
continue;
|
|
77
78
|
await writeFile(absPath, updated, 'utf-8');
|
|
@@ -79,6 +80,10 @@ export async function patchMcpTomlInjections(config, projectRoot, injections) {
|
|
|
79
80
|
}
|
|
80
81
|
return { written, removed, hashes: {} };
|
|
81
82
|
}
|
|
83
|
+
export function stripMcpTomlInjections(content) {
|
|
84
|
+
const stripped = removeInjectedBlock(content).trimEnd();
|
|
85
|
+
return stripped ? stripped + '\n' : '';
|
|
86
|
+
}
|
|
82
87
|
// ─── Gitignore ──────
|
|
83
88
|
export function getMcpTomlGitignore(target) {
|
|
84
89
|
return isMcpTomlTarget(target) ? [target.mcp] : [];
|
|
@@ -94,10 +99,22 @@ function buildServerEntries(items) {
|
|
|
94
99
|
}
|
|
95
100
|
return servers;
|
|
96
101
|
}
|
|
97
|
-
function buildMcpToml(target,
|
|
102
|
+
function buildMcpToml(target, options) {
|
|
98
103
|
const rootKey = getRootKey(target);
|
|
99
|
-
const
|
|
100
|
-
const
|
|
104
|
+
const blocks = [];
|
|
105
|
+
const syncEntries = Object.entries(options.syncServers);
|
|
106
|
+
const injectionEntries = Object.entries(options.injectionServers);
|
|
107
|
+
if (syncEntries.length > 0) {
|
|
108
|
+
blocks.push(buildMcpTomlBlock(MANAGED_BLOCK_BEGIN, MANAGED_BLOCK_END, rootKey, syncEntries, true));
|
|
109
|
+
}
|
|
110
|
+
if (injectionEntries.length > 0) {
|
|
111
|
+
blocks.push(buildMcpTomlBlock(INJECTED_BLOCK_BEGIN, INJECTED_BLOCK_END, rootKey, injectionEntries, blocks.length === 0));
|
|
112
|
+
}
|
|
113
|
+
return blocks.join('\n');
|
|
114
|
+
}
|
|
115
|
+
function buildMcpTomlBlock(beginMarker, endMarker, rootKey, serverEntries, includeRootTable) {
|
|
116
|
+
const serverBlocks = serverEntries.map(([name, serverConfig]) => buildServerBlock(rootKey, name, serverConfig));
|
|
117
|
+
const lines = [beginMarker, includeRootTable ? `[${formatTomlKey(rootKey)}]` : '', ...serverBlocks, endMarker];
|
|
101
118
|
return normalizeTomlLines(lines);
|
|
102
119
|
}
|
|
103
120
|
function buildServerBlock(rootKey, name, serverConfig) {
|
|
@@ -115,13 +132,30 @@ async function mergeWithExisting(absPath, newContent) {
|
|
|
115
132
|
return mergeTomlContent(content, newContent, DEFAULT_ROOT_KEY);
|
|
116
133
|
}
|
|
117
134
|
function mergeTomlContent(content, newContent, rootKey) {
|
|
118
|
-
const stripped = removeManagedBlock(content);
|
|
135
|
+
const stripped = removeInjectedBlock(removeManagedBlock(content));
|
|
136
|
+
const replacement = hasRootScope(stripped, rootKey) ? removeRootTable(newContent, rootKey) : newContent;
|
|
137
|
+
return appendTomlContent(stripped, replacement);
|
|
138
|
+
}
|
|
139
|
+
function mergeInjectedTomlContent(content, newContent, rootKey) {
|
|
140
|
+
const stripped = removeInjectedBlock(content);
|
|
119
141
|
const replacement = hasRootScope(stripped, rootKey) ? removeRootTable(newContent, rootKey) : newContent;
|
|
120
|
-
return
|
|
142
|
+
return appendTomlContent(stripped, replacement);
|
|
121
143
|
}
|
|
122
144
|
function removeManagedBlock(content) {
|
|
123
145
|
return spliceMarkerBlock(content, MANAGED_BLOCK_BEGIN, MANAGED_BLOCK_END);
|
|
124
146
|
}
|
|
147
|
+
function removeInjectedBlock(content) {
|
|
148
|
+
return spliceMarkerBlock(content, INJECTED_BLOCK_BEGIN, INJECTED_BLOCK_END);
|
|
149
|
+
}
|
|
150
|
+
function appendTomlContent(content, replacement) {
|
|
151
|
+
const base = content.trimEnd();
|
|
152
|
+
const next = replacement.trimEnd();
|
|
153
|
+
if (!next)
|
|
154
|
+
return base ? base + '\n' : '';
|
|
155
|
+
if (!base)
|
|
156
|
+
return next + '\n';
|
|
157
|
+
return `${base}\n\n${next}\n`;
|
|
158
|
+
}
|
|
125
159
|
function removeRootTable(content, rootKey) {
|
|
126
160
|
const lines = content.split('\n');
|
|
127
161
|
const rootTableLine = `[${formatTomlKey(rootKey)}]`;
|
|
@@ -133,8 +167,17 @@ function hasRootScope(content, rootKey) {
|
|
|
133
167
|
const rootScopePattern = new RegExp(`^\\s*\\[\\s*${formattedRootKey}(?:\\s*\\]|\\s*\\.)`, 'm');
|
|
134
168
|
return rootScopePattern.test(content);
|
|
135
169
|
}
|
|
136
|
-
function
|
|
137
|
-
|
|
170
|
+
function buildInjectedServerEntries(injections) {
|
|
171
|
+
const servers = {};
|
|
172
|
+
for (const [source, items] of injections) {
|
|
173
|
+
for (const item of items) {
|
|
174
|
+
if (item.type !== SyncItemType.McpServer || !item.serverConfig)
|
|
175
|
+
continue;
|
|
176
|
+
const serverKey = createUniqueServerKey(`${source}-${item.id}`, servers);
|
|
177
|
+
servers[serverKey] = item.serverConfig;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return servers;
|
|
138
181
|
}
|
|
139
182
|
function createUniqueServerKey(id, servers) {
|
|
140
183
|
const normalizedKey = normalizeServerKey(id);
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,8 @@ export function aiRulesPlugin() {
|
|
|
30
30
|
injectionRegistry.set(source, items);
|
|
31
31
|
},
|
|
32
32
|
removeInjectedRules(source) {
|
|
33
|
-
|
|
33
|
+
// 保留 source tombstone,MCP 热清理需要知道要删除哪个动态前缀。
|
|
34
|
+
injectionRegistry.set(source, []);
|
|
34
35
|
},
|
|
35
36
|
async applyInjections(ctx) {
|
|
36
37
|
const config = parseAiConfig(ctx);
|
|
@@ -48,8 +49,8 @@ export function aiRulesPlugin() {
|
|
|
48
49
|
value: (state) => {
|
|
49
50
|
const s = state['ai-rules'];
|
|
50
51
|
if (!s?.lastSyncTime || s.synced === false)
|
|
51
|
-
return 'Rules
|
|
52
|
-
return 'Rules
|
|
52
|
+
return 'Rules 未同步';
|
|
53
|
+
return 'Rules 已同步';
|
|
53
54
|
},
|
|
54
55
|
color: 'cyan',
|
|
55
56
|
});
|
|
@@ -146,7 +147,7 @@ async function executeSyncCommand(ctx, injections) {
|
|
|
146
147
|
return;
|
|
147
148
|
}
|
|
148
149
|
const hasItems = config.agent || config.subagents.length > 0 || config.rules.length > 0 || config.skills.length > 0 || config.mcpServers.length > 0;
|
|
149
|
-
if (!hasItems && injections
|
|
150
|
+
if (!hasItems && !hasInjectedItems(injections)) {
|
|
150
151
|
ctx.logger.warn('未配置任何 agent/subagents/rules/skills/mcpServers,请先运行 xfe rules add');
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
@@ -340,6 +341,9 @@ function collectSelectedIds(config) {
|
|
|
340
341
|
ids.push(config.agent);
|
|
341
342
|
return new Set(ids);
|
|
342
343
|
}
|
|
344
|
+
function hasInjectedItems(injections) {
|
|
345
|
+
return [...injections.values()].some((items) => items.length > 0);
|
|
346
|
+
}
|
|
343
347
|
function getEntryChoiceDescription(entry) {
|
|
344
348
|
const descriptions = [
|
|
345
349
|
entry.description,
|
package/dist/pipeline.js
CHANGED
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
import { existsSync } from 'node:fs';
|
|
9
9
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
+
import { McpConfigFormat } from './types.js';
|
|
11
12
|
import { getTarget } from './targets.js';
|
|
12
13
|
import { getCachePath, hasCachedRepo } from './repo-cache.js';
|
|
13
14
|
import { computeHash, spliceMarkerBlock } from './formats/shared.js';
|
|
14
15
|
import { planMarkdownFiles, detectMarkdownConflict, writeMarkdownFile, patchMarkdownInjections, getMarkdownGitignore, stripInjectedSections, removeStaleInjectedFiles, } from './formats/agent-md.js';
|
|
15
|
-
import { planMcpJsonFiles, writeMcpJsonFile, patchMcpJsonInjections, getMcpJsonGitignore } from './formats/mcp-json.js';
|
|
16
|
-
import { planMcpTomlFiles, writeMcpTomlFile, patchMcpTomlInjections, getMcpTomlGitignore } from './formats/mcp-toml.js';
|
|
16
|
+
import { planMcpJsonFiles, writeMcpJsonFile, patchMcpJsonInjections, getMcpJsonGitignore, stripMcpJsonInjections } from './formats/mcp-json.js';
|
|
17
|
+
import { planMcpTomlFiles, writeMcpTomlFile, patchMcpTomlInjections, getMcpTomlGitignore, stripMcpTomlInjections } from './formats/mcp-toml.js';
|
|
17
18
|
import { planSkillDirs, writeSkillDir, patchSkillDirInjections } from './formats/skill-dir.js';
|
|
18
19
|
// ─── Constants ──────
|
|
19
20
|
const GITIGNORE_BLOCK_START = '# xfe-ai-rules (auto-generated, do not edit)';
|
|
@@ -138,7 +139,7 @@ export async function checkSyncStatus(ctx, config) {
|
|
|
138
139
|
if (!existsSync(absPath))
|
|
139
140
|
return false;
|
|
140
141
|
const content = await readFile(absPath, 'utf-8');
|
|
141
|
-
if (computeHash(
|
|
142
|
+
if (computeHash(stripRuntimeInjections(config, relativePath, content)) !== expectedHash)
|
|
142
143
|
return false;
|
|
143
144
|
}
|
|
144
145
|
return true;
|
|
@@ -174,4 +175,16 @@ async function updateGitignore(projectRoot, entries) {
|
|
|
174
175
|
const updated = spliceMarkerBlock(content, GITIGNORE_BLOCK_START, GITIGNORE_BLOCK_END, newBlock);
|
|
175
176
|
await writeFile(gitignorePath, updated, 'utf-8');
|
|
176
177
|
}
|
|
178
|
+
function stripRuntimeInjections(config, relativePath, content) {
|
|
179
|
+
for (const targetId of config.targets) {
|
|
180
|
+
const target = getTarget(targetId);
|
|
181
|
+
if (!target || target.mcp !== relativePath)
|
|
182
|
+
continue;
|
|
183
|
+
if ((target.mcpFormat ?? McpConfigFormat.Json) === McpConfigFormat.Json)
|
|
184
|
+
return stripMcpJsonInjections(content);
|
|
185
|
+
if (target.mcpFormat === McpConfigFormat.Toml)
|
|
186
|
+
return stripMcpTomlInjections(content);
|
|
187
|
+
}
|
|
188
|
+
return stripInjectedSections(content);
|
|
189
|
+
}
|
|
177
190
|
//# sourceMappingURL=pipeline.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xfe-repo/cli-plugin-ai-rules",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "XFE AI Rules plugin - sync AI coding rules from remote repo to IDE configs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"fs-extra": "^11.3.0",
|
|
10
10
|
"gray-matter": "^4.0.3",
|
|
11
11
|
"zod": "^4.3.6",
|
|
12
|
-
"@xfe-repo/cli-core": "2.0
|
|
13
|
-
"@xfe-repo/cli-plugin-git": "2.0.
|
|
12
|
+
"@xfe-repo/cli-core": "2.1.0",
|
|
13
|
+
"@xfe-repo/cli-plugin-git": "2.0.8"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/fs-extra": "^11.0.4",
|