@xfe-repo/cli-plugin-ai-rules 2.0.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.
Files changed (89) hide show
  1. package/dist/block-utils.d.ts +11 -0
  2. package/dist/block-utils.d.ts.map +1 -0
  3. package/dist/block-utils.js +13 -0
  4. package/dist/block-utils.js.map +1 -0
  5. package/dist/formats/agent-md.d.ts +35 -0
  6. package/dist/formats/agent-md.d.ts.map +1 -0
  7. package/dist/formats/agent-md.js +241 -0
  8. package/dist/formats/agent-md.js.map +1 -0
  9. package/dist/formats/markdown.d.ts +35 -0
  10. package/dist/formats/markdown.d.ts.map +1 -0
  11. package/dist/formats/markdown.js +241 -0
  12. package/dist/formats/markdown.js.map +1 -0
  13. package/dist/formats/mcp-json.d.ts +25 -0
  14. package/dist/formats/mcp-json.d.ts.map +1 -0
  15. package/dist/formats/mcp-json.js +132 -0
  16. package/dist/formats/mcp-json.js.map +1 -0
  17. package/dist/formats/shared.d.ts +4 -0
  18. package/dist/formats/shared.d.ts.map +1 -0
  19. package/dist/formats/shared.js +24 -0
  20. package/dist/formats/shared.js.map +1 -0
  21. package/dist/formats/skill-dir.d.ts +17 -0
  22. package/dist/formats/skill-dir.d.ts.map +1 -0
  23. package/dist/formats/skill-dir.js +72 -0
  24. package/dist/formats/skill-dir.js.map +1 -0
  25. package/dist/gitignore.d.ts +12 -0
  26. package/dist/gitignore.d.ts.map +1 -0
  27. package/dist/gitignore.js +32 -0
  28. package/dist/gitignore.js.map +1 -0
  29. package/dist/index.d.ts +18 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +358 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/injection.d.ts +26 -0
  34. package/dist/injection.d.ts.map +1 -0
  35. package/dist/injection.js +169 -0
  36. package/dist/injection.js.map +1 -0
  37. package/dist/mcp-writer.d.ts +23 -0
  38. package/dist/mcp-writer.d.ts.map +1 -0
  39. package/dist/mcp-writer.js +131 -0
  40. package/dist/mcp-writer.js.map +1 -0
  41. package/dist/merge.d.ts +14 -0
  42. package/dist/merge.d.ts.map +1 -0
  43. package/dist/merge.js +35 -0
  44. package/dist/merge.js.map +1 -0
  45. package/dist/pipeline.d.ts +16 -0
  46. package/dist/pipeline.d.ts.map +1 -0
  47. package/dist/pipeline.js +165 -0
  48. package/dist/pipeline.js.map +1 -0
  49. package/dist/pipeline.new.d.ts +11 -0
  50. package/dist/pipeline.new.d.ts.map +1 -0
  51. package/dist/pipeline.new.js +11 -0
  52. package/dist/pipeline.new.js.map +1 -0
  53. package/dist/repo-cache.d.ts +24 -0
  54. package/dist/repo-cache.d.ts.map +1 -0
  55. package/dist/repo-cache.js +51 -0
  56. package/dist/repo-cache.js.map +1 -0
  57. package/dist/scanner.d.ts +12 -0
  58. package/dist/scanner.d.ts.map +1 -0
  59. package/dist/scanner.js +119 -0
  60. package/dist/scanner.js.map +1 -0
  61. package/dist/sync.d.ts +26 -0
  62. package/dist/sync.d.ts.map +1 -0
  63. package/dist/sync.js +172 -0
  64. package/dist/sync.js.map +1 -0
  65. package/dist/targets.d.ts +28 -0
  66. package/dist/targets.d.ts.map +1 -0
  67. package/dist/targets.js +90 -0
  68. package/dist/targets.js.map +1 -0
  69. package/dist/types.d.ts +112 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +53 -0
  72. package/dist/types.js.map +1 -0
  73. package/dist/writers/index.d.ts +15 -0
  74. package/dist/writers/index.d.ts.map +1 -0
  75. package/dist/writers/index.js +23 -0
  76. package/dist/writers/index.js.map +1 -0
  77. package/dist/writers/markdown-writer.d.ts +15 -0
  78. package/dist/writers/markdown-writer.d.ts.map +1 -0
  79. package/dist/writers/markdown-writer.js +249 -0
  80. package/dist/writers/markdown-writer.js.map +1 -0
  81. package/dist/writers/mcp-json-writer.d.ts +11 -0
  82. package/dist/writers/mcp-json-writer.d.ts.map +1 -0
  83. package/dist/writers/mcp-json-writer.js +185 -0
  84. package/dist/writers/mcp-json-writer.js.map +1 -0
  85. package/dist/writers/types.d.ts +29 -0
  86. package/dist/writers/types.d.ts.map +1 -0
  87. package/dist/writers/types.js +8 -0
  88. package/dist/writers/types.js.map +1 -0
  89. package/package.json +27 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-json.d.ts","sourceRoot":"","sources":["../../src/formats/mcp-json.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAGtF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AASjD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,UAAU,CAAC,EAAE,iBAAiB,GAAG,WAAW,EAAE,CAkBxH;AAID,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU9F;AAID,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CA4CvI;AAID,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAElE"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * MCP JSON 格式模块
3
+ *
4
+ * 处理 MCP 配置文件(.vscode/mcp.json、.cursor/mcp.json、.mcp.json)的同步写入:
5
+ * - 使用 `xfe:` 前缀隔离 xfe 管理的 server 条目与用户自定义条目
6
+ * - 写入时保留非 xfe 前缀的用户条目,仅覆盖 xfe 前缀条目
7
+ * - 不做冲突检测(前缀隔离已足够)
8
+ */
9
+ import { existsSync } from 'node:fs';
10
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
11
+ import path from 'node:path';
12
+ import { SyncItemType } from '../types.js';
13
+ import { getTarget } from '../targets.js';
14
+ import { computeHash } from './shared.js';
15
+ // ─── Constants ──────
16
+ const MCP_SERVER_PREFIX = 'xfe:';
17
+ // ─── Plan ──────
18
+ /**
19
+ * 将 MCP server 项按 target 转换为 PlannedFile
20
+ *
21
+ * 可选注入项合并到同一 JSON 文件
22
+ */
23
+ export function planMcpJsonFiles(target, items, injections) {
24
+ if (!target.mcp)
25
+ return [];
26
+ const syncServers = buildServerEntries(items.filter((i) => i.type === SyncItemType.McpServer));
27
+ const injectionServers = injections ? buildServerEntries(collectMcpItems(injections)) : {};
28
+ const servers = { ...syncServers, ...injectionServers };
29
+ if (Object.keys(servers).length === 0)
30
+ return [];
31
+ return [
32
+ {
33
+ relativePath: target.mcp,
34
+ content: buildMcpJson(target, servers),
35
+ action: 'write',
36
+ source: Object.keys(syncServers).length > 0 ? 'sync' : 'injection',
37
+ format: 'mcp-json',
38
+ },
39
+ ];
40
+ }
41
+ // ─── Write ──────
42
+ export async function writeMcpJsonFile(file, projectRoot) {
43
+ const absPath = path.join(projectRoot, file.relativePath);
44
+ await mkdir(path.dirname(absPath), { recursive: true });
45
+ const merged = await mergeWithExisting(absPath, file.content);
46
+ await writeFile(absPath, merged, 'utf-8');
47
+ return computeHash(merged);
48
+ }
49
+ export async function patchMcpJsonInjections(config, projectRoot, injections) {
50
+ const servers = buildServerEntries(collectMcpItems(injections));
51
+ const isEmpty = Object.keys(servers).length === 0;
52
+ let written = 0;
53
+ let removed = 0;
54
+ for (const targetId of config.targets) {
55
+ const target = getTarget(targetId);
56
+ if (!target?.mcp)
57
+ continue;
58
+ const absPath = path.join(projectRoot, target.mcp);
59
+ if (!existsSync(absPath)) {
60
+ if (!isEmpty) {
61
+ await mkdir(path.dirname(absPath), { recursive: true });
62
+ await writeFile(absPath, buildMcpJson(target, servers), 'utf-8');
63
+ written++;
64
+ }
65
+ continue;
66
+ }
67
+ const content = await readFile(absPath, 'utf-8');
68
+ const rootKey = target.mcpRootKey ?? 'mcpServers';
69
+ const parsed = JSON.parse(content);
70
+ const existing = parsed[rootKey] ?? {};
71
+ const userServers = filterUserServers(existing);
72
+ if (isEmpty) {
73
+ if (Object.keys(existing).some((k) => k.startsWith(MCP_SERVER_PREFIX))) {
74
+ parsed[rootKey] = userServers;
75
+ await writeFile(absPath, JSON.stringify(parsed, null, 2) + '\n', 'utf-8');
76
+ removed++;
77
+ }
78
+ }
79
+ else {
80
+ parsed[rootKey] = { ...userServers, ...servers };
81
+ await writeFile(absPath, JSON.stringify(parsed, null, 2) + '\n', 'utf-8');
82
+ written++;
83
+ }
84
+ }
85
+ return { written, removed, hashes: {} };
86
+ }
87
+ // ─── Gitignore ──────
88
+ export function getMcpJsonGitignore(target) {
89
+ return target.mcp ? [target.mcp] : [];
90
+ }
91
+ // ─── Helpers ──────
92
+ function buildServerEntries(items) {
93
+ const servers = {};
94
+ for (const item of items) {
95
+ if (!item.serverConfig)
96
+ continue;
97
+ servers[`${MCP_SERVER_PREFIX}${item.id}`] = item.serverConfig;
98
+ }
99
+ return servers;
100
+ }
101
+ function buildMcpJson(target, servers) {
102
+ const rootKey = target.mcpRootKey ?? 'mcpServers';
103
+ return JSON.stringify({ [rootKey]: servers }, null, 2) + '\n';
104
+ }
105
+ function collectMcpItems(injections) {
106
+ return [...injections.values()].flat().filter((i) => i.type === SyncItemType.McpServer);
107
+ }
108
+ function filterUserServers(servers) {
109
+ const user = {};
110
+ for (const [key, value] of Object.entries(servers)) {
111
+ if (!key.startsWith(MCP_SERVER_PREFIX))
112
+ user[key] = value;
113
+ }
114
+ return user;
115
+ }
116
+ async function mergeWithExisting(absPath, newContent) {
117
+ if (!existsSync(absPath))
118
+ return newContent;
119
+ let existing;
120
+ try {
121
+ existing = JSON.parse(await readFile(absPath, 'utf-8'));
122
+ }
123
+ catch {
124
+ return newContent;
125
+ }
126
+ const newParsed = JSON.parse(newContent);
127
+ const rootKey = Object.keys(newParsed)[0];
128
+ const userServers = filterUserServers(existing[rootKey] ?? {});
129
+ existing[rootKey] = { ...userServers, ...newParsed[rootKey] };
130
+ return JSON.stringify(existing, null, 2) + '\n';
131
+ }
132
+ //# sourceMappingURL=mcp-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-json.js","sourceRoot":"","sources":["../../src/formats/mcp-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** 格式模块共享工具:hash 计算 + 标记块操作 */
2
+ export declare function computeHash(content: string): string;
3
+ export declare function spliceMarkerBlock(content: string, beginMarker: string, endMarker: string, replacement?: string): string;
4
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/formats/shared.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAI/B,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAuBvH"}
@@ -0,0 +1,24 @@
1
+ /** 格式模块共享工具:hash 计算 + 标记块操作 */
2
+ import { createHash } from 'node:crypto';
3
+ export function computeHash(content) {
4
+ return createHash('sha256').update(content).digest('hex');
5
+ }
6
+ export function spliceMarkerBlock(content, beginMarker, endMarker, replacement) {
7
+ const beginIdx = content.indexOf(beginMarker);
8
+ const endIdx = content.indexOf(endMarker);
9
+ const found = beginIdx !== -1 && endIdx !== -1;
10
+ if (!found) {
11
+ if (replacement === undefined)
12
+ return content;
13
+ const sep = content.endsWith('\n') ? '\n' : '\n\n';
14
+ return content + sep + replacement + '\n';
15
+ }
16
+ const before = content.slice(0, beginIdx);
17
+ const after = content.slice(endIdx + endMarker.length);
18
+ if (replacement === undefined) {
19
+ const trimmed = before.trimEnd();
20
+ return trimmed ? trimmed + after.trimStart() : after.trimStart();
21
+ }
22
+ return before + replacement + after;
23
+ }
24
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../src/formats/shared.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC3D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAe,EAAE,WAAmB,EAAE,SAAiB,EAAE,WAAoB;IAC7G,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACzC,MAAM,KAAK,GAAG,QAAQ,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,CAAA;IAE9C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,WAAW,KAAK,SAAS;YAAE,OAAO,OAAO,CAAA;QAE7C,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;QAElD,OAAO,OAAO,GAAG,GAAG,GAAG,WAAW,GAAG,IAAI,CAAA;IAC3C,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACzC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;IAEtD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;QAEhC,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,CAAA;IAClE,CAAC;IAED,OAAO,MAAM,GAAG,WAAW,GAAG,KAAK,CAAA;AACrC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Skill 目录格式模块
3
+ *
4
+ * 处理 skill 文件夹的同步写入:整目录 rm + cp 覆盖,
5
+ * 不做文件级 diff / 冲突检测。merge 型目标跳过 skill 同步。
6
+ */
7
+ import type { AiConfig, InjectionRegistry, SkillDirPlan, SyncEntry } from '../types.js';
8
+ import type { TargetConfig } from '../targets.js';
9
+ export declare function planSkillDirs(target: TargetConfig, items: SyncEntry[]): SkillDirPlan[];
10
+ export declare function writeSkillDir(plan: SkillDirPlan, projectRoot: string): Promise<void>;
11
+ export interface SkillPatchResult {
12
+ written: number;
13
+ dirs: string[];
14
+ }
15
+ export declare function patchSkillDirInjections(config: AiConfig, projectRoot: string, injections: InjectionRegistry): Promise<SkillPatchResult>;
16
+ export declare function removeStaleSkillDirs(projectRoot: string, prevDirs: string[], currentDirs: string[]): Promise<number>;
17
+ //# sourceMappingURL=skill-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-dir.d.ts","sourceRoot":"","sources":["../../src/formats/skill-dir.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAGvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAIjD,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAgBtF;AAID,wBAAsB,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAK1F;AAID,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,EAAE,CAAA;CACf;AAED,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,QAAQ,EAChB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,iBAAiB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CA8B3B;AAID,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgB1H"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Skill 目录格式模块
3
+ *
4
+ * 处理 skill 文件夹的同步写入:整目录 rm + cp 覆盖,
5
+ * 不做文件级 diff / 冲突检测。merge 型目标跳过 skill 同步。
6
+ */
7
+ import { cp, rm } from 'node:fs/promises';
8
+ import path from 'node:path';
9
+ import { SyncItemType } from '../types.js';
10
+ import { getTarget, isIndividualSlot, resolvePathTemplate } from '../targets.js';
11
+ // ─── Plan ──────
12
+ export function planSkillDirs(target, items) {
13
+ const template = resolvePathTemplate(target, SyncItemType.Skill);
14
+ if (!template || !isIndividualSlot(template))
15
+ return [];
16
+ const plans = [];
17
+ for (const item of items) {
18
+ if (item.type !== SyncItemType.Skill || !item.sourcePath)
19
+ continue;
20
+ const targetDir = path.dirname(template.replace('{id}', item.id));
21
+ plans.push({ id: item.id, sourceDir: item.sourcePath, targetDir, source: 'sync' });
22
+ }
23
+ return plans;
24
+ }
25
+ // ─── Write ──────
26
+ export async function writeSkillDir(plan, projectRoot) {
27
+ const absTarget = path.join(projectRoot, plan.targetDir);
28
+ await rm(absTarget, { recursive: true, force: true });
29
+ await cp(plan.sourceDir, absTarget, { recursive: true });
30
+ }
31
+ export async function patchSkillDirInjections(config, projectRoot, injections) {
32
+ const skillItems = [...injections.values()].flat().filter((i) => i.type === SyncItemType.Skill && i.sourcePath);
33
+ if (skillItems.length === 0)
34
+ return { written: 0, dirs: [] };
35
+ let written = 0;
36
+ const dirs = [];
37
+ for (const targetId of config.targets) {
38
+ const target = getTarget(targetId);
39
+ if (!target)
40
+ continue;
41
+ const template = resolvePathTemplate(target, SyncItemType.Skill);
42
+ if (!template || !isIndividualSlot(template))
43
+ continue;
44
+ for (const item of skillItems) {
45
+ const targetDir = path.dirname(template.replace('{id}', item.id));
46
+ const absTarget = path.join(projectRoot, targetDir);
47
+ await rm(absTarget, { recursive: true, force: true });
48
+ await cp(item.sourcePath, absTarget, { recursive: true });
49
+ dirs.push(targetDir);
50
+ written++;
51
+ }
52
+ }
53
+ return { written, dirs };
54
+ }
55
+ // ─── Stale Cleanup ──────
56
+ export async function removeStaleSkillDirs(projectRoot, prevDirs, currentDirs) {
57
+ const currentSet = new Set(currentDirs);
58
+ let removed = 0;
59
+ for (const dir of prevDirs) {
60
+ if (currentSet.has(dir))
61
+ continue;
62
+ try {
63
+ await rm(path.join(projectRoot, dir), { recursive: true, force: true });
64
+ removed++;
65
+ }
66
+ catch {
67
+ // 删除失败时静默跳过
68
+ }
69
+ }
70
+ return removed;
71
+ }
72
+ //# sourceMappingURL=skill-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-dir.js","sourceRoot":"","sources":["../../src/formats/skill-dir.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @xfe-repo/cli-plugin-ai-rules - .gitignore 管理
3
+ *
4
+ * 在项目 .gitignore 中维护 xfe-ai-rules 标记块
5
+ */
6
+ /**
7
+ * 更新项目 .gitignore 中的 xfe-ai-rules 标记块
8
+ *
9
+ * 如果标记块已存在则替换其内容,否则追加到文件末尾
10
+ */
11
+ export declare function updateGitignore(projectRoot: string, entries: string[]): Promise<void>;
12
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../src/gitignore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAe3F"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @xfe-repo/cli-plugin-ai-rules - .gitignore 管理
3
+ *
4
+ * 在项目 .gitignore 中维护 xfe-ai-rules 标记块
5
+ */
6
+ import { existsSync } from 'node:fs';
7
+ import { readFile, writeFile } from 'node:fs/promises';
8
+ import path from 'node:path';
9
+ import { spliceMarkerBlock } from './injection.js';
10
+ // ─── Constants ──────
11
+ const BLOCK_START = '# xfe-ai-rules (auto-generated, do not edit)';
12
+ const BLOCK_END = '# end xfe-ai-rules';
13
+ // ─── Public ──────
14
+ /**
15
+ * 更新项目 .gitignore 中的 xfe-ai-rules 标记块
16
+ *
17
+ * 如果标记块已存在则替换其内容,否则追加到文件末尾
18
+ */
19
+ export async function updateGitignore(projectRoot, entries) {
20
+ if (entries.length === 0)
21
+ return;
22
+ const gitignorePath = path.join(projectRoot, '.gitignore');
23
+ const newBlock = [BLOCK_START, ...entries, BLOCK_END].join('\n');
24
+ if (!existsSync(gitignorePath)) {
25
+ await writeFile(gitignorePath, newBlock + '\n', 'utf-8');
26
+ return;
27
+ }
28
+ const content = await readFile(gitignorePath, 'utf-8');
29
+ const updated = spliceMarkerBlock(content, BLOCK_START, BLOCK_END, newBlock);
30
+ await writeFile(gitignorePath, updated, 'utf-8');
31
+ }
32
+ //# sourceMappingURL=gitignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../src/gitignore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAElD,uBAAuB;AAEvB,MAAM,WAAW,GAAG,8CAA8C,CAAA;AAClE,MAAM,SAAS,GAAG,oBAAoB,CAAA;AAEtC,oBAAoB;AAEpB;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,WAAmB,EAAE,OAAiB;IAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAEhC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,CAAC,WAAW,EAAE,GAAG,OAAO,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEhE,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,MAAM,SAAS,CAAC,aAAa,EAAE,QAAQ,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;QACxD,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;IACtD,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;IAE5E,MAAM,SAAS,CAAC,aAAa,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;AAClD,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @xfe-repo/cli-plugin-ai-rules - 插件入口
3
+ *
4
+ * 声明式 AI 配置同步:从 xfe.json 读取 ai 配置,
5
+ * 拉取远程仓库内容,按 target 注册表分发到项目 IDE 配置
6
+ */
7
+ import type { XfePlugin } from '@xfe-repo/cli-core';
8
+ import type { AiRulesCapability, AiRulesStoreState } from './types.js';
9
+ export { SyncItemType } from './types.js';
10
+ export type { AiRulesCapability, SyncEntry } from './types.js';
11
+ declare const AI_RULES_STORE_INITIAL: AiRulesStoreState;
12
+ declare module '@xfe-repo/cli-core' {
13
+ interface XfePluginRegistry {
14
+ 'ai-rules': XfePlugin<typeof AI_RULES_STORE_INITIAL, AiRulesCapability>;
15
+ }
16
+ }
17
+ export declare function aiRulesPlugin(): XfePlugin;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,oBAAoB,CAAA;AAIlE,OAAO,KAAK,EAAY,iBAAiB,EAAE,iBAAiB,EAAoD,MAAM,YAAY,CAAA;AAelI,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAI9D,QAAA,MAAM,sBAAsB,EAAE,iBAI7B,CAAA;AAED,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,iBAAiB;QACzB,UAAU,EAAE,SAAS,CAAC,OAAO,sBAAsB,EAAE,iBAAiB,CAAC,CAAA;KACxE;CACF;AAID,wBAAgB,aAAa,IAAI,SAAS,CA+FzC"}