autosnippet 3.2.22 → 3.3.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 (102) hide show
  1. package/dashboard/dist/assets/{icons-C1dUryS-.js → icons-BofcEZ3f.js} +1 -1
  2. package/dashboard/dist/assets/index-SiN1GChm.js +128 -0
  3. package/dashboard/dist/index.html +2 -2
  4. package/dist/bin/cli.d.ts +0 -1
  5. package/dist/bin/cli.js +0 -133
  6. package/dist/lib/cli/SetupService.d.ts +46 -2
  7. package/dist/lib/cli/SetupService.js +2 -27
  8. package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
  9. package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
  10. package/dist/lib/core/discovery/index.d.ts +1 -1
  11. package/dist/lib/core/discovery/index.js +2 -2
  12. package/dist/lib/external/mcp/handlers/guard.js +6 -3
  13. package/dist/lib/http/HttpServer.js +0 -6
  14. package/dist/lib/http/routes/commands.d.ts +1 -1
  15. package/dist/lib/http/routes/commands.js +1 -66
  16. package/dist/lib/http/routes/remote.js +0 -5
  17. package/dist/lib/injection/ServiceMap.d.ts +0 -9
  18. package/dist/lib/injection/modules/AppModule.d.ts +2 -3
  19. package/dist/lib/injection/modules/AppModule.js +3 -30
  20. package/dist/lib/service/module/ModuleService.js +3 -13
  21. package/dist/lib/service/search/SearchEngine.js +1 -1
  22. package/dist/lib/shared/constants.d.ts +0 -15
  23. package/dist/lib/shared/constants.js +0 -10
  24. package/dist/scripts/release.js +2 -10
  25. package/package.json +4 -19
  26. package/dashboard/dist/assets/index-DdvZE4Yd.js +0 -128
  27. package/dist/lib/http/routes/snippets.d.ts +0 -6
  28. package/dist/lib/http/routes/snippets.js +0 -49
  29. package/dist/lib/platform/ClipboardManager.d.ts +0 -24
  30. package/dist/lib/platform/ClipboardManager.js +0 -142
  31. package/dist/lib/platform/NativeUi.d.ts +0 -53
  32. package/dist/lib/platform/NativeUi.js +0 -284
  33. package/dist/lib/platform/ios/index.d.ts +0 -38
  34. package/dist/lib/platform/ios/index.js +0 -42
  35. package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
  36. package/dist/lib/platform/ios/routes/spm.js +0 -371
  37. package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
  38. package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
  39. package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
  40. package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
  41. package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
  42. package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
  43. package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
  44. package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
  45. package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
  46. package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
  47. package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
  48. package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
  49. package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
  50. package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
  51. package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
  52. package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
  53. package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
  54. package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
  55. package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
  56. package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
  57. package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
  58. package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
  59. package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
  60. package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
  61. package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
  62. package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
  63. package/dist/lib/service/automation/ActionPipeline.d.ts +0 -34
  64. package/dist/lib/service/automation/ActionPipeline.js +0 -53
  65. package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
  66. package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
  67. package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
  68. package/dist/lib/service/automation/ContextCollector.js +0 -35
  69. package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
  70. package/dist/lib/service/automation/DirectiveDetector.js +0 -112
  71. package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
  72. package/dist/lib/service/automation/FileWatcher.js +0 -366
  73. package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
  74. package/dist/lib/service/automation/TriggerResolver.js +0 -62
  75. package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
  76. package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
  77. package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
  78. package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
  79. package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
  80. package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
  81. package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
  82. package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
  83. package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
  84. package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
  85. package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
  86. package/dist/lib/service/snippet/SnippetFactory.js +0 -145
  87. package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
  88. package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
  89. package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
  90. package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
  91. package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
  92. package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
  93. package/dist/scripts/build-native-ui.d.ts +0 -3
  94. package/dist/scripts/build-native-ui.js +0 -62
  95. package/dist/scripts/init-snippets.d.ts +0 -30
  96. package/dist/scripts/init-snippets.js +0 -298
  97. package/dist/scripts/install-full.d.ts +0 -7
  98. package/dist/scripts/install-full.js +0 -38
  99. package/resources/native-ui/README.md +0 -29
  100. package/resources/native-ui/combined-window.swift +0 -494
  101. package/resources/native-ui/main.swift +0 -598
  102. package/scripts/postinstall-safe.mjs +0 -89
@@ -1,278 +0,0 @@
1
- /** SearchHandler — 处理 // as:s 指令 */
2
- import Logger from '#infra/logging/Logger.js';
3
- export async function handleSearch(watcher, fullPath, relativePath, searchLine) {
4
- const query = searchLine.replace(/^\/\/\s*(?:autosnippet|as):(?:search|s)\s*/, '').trim();
5
- if (!query) {
6
- return;
7
- }
8
- let searchResult = null;
9
- try {
10
- const { ServiceContainer } = await import('../../../injection/ServiceContainer.js');
11
- const container = ServiceContainer.getInstance();
12
- const searchEngine = container.get('searchEngine');
13
- // 确保索引已构建
14
- searchEngine.ensureIndex();
15
- // auto (BM25+semantic 融合 + Ranking Pipeline) → keyword (SQL LIKE) 降级链
16
- // Xcode/IDE 场景: 传递 generate intent,让排序器使用代码生成权重
17
- try {
18
- searchResult = await searchEngine.search(query, {
19
- limit: 10,
20
- mode: 'auto',
21
- rank: true,
22
- context: { intent: 'generate' },
23
- });
24
- // auto 零结果 → keyword (SQL LIKE) 兜底
25
- const resultItems = Array.isArray(searchResult)
26
- ? searchResult
27
- : searchResult?.items || [];
28
- if (resultItems.length === 0) {
29
- searchResult = await searchEngine.search(query, { limit: 10, mode: 'keyword' });
30
- }
31
- }
32
- catch {
33
- try {
34
- searchResult = await searchEngine.search(query, { limit: 10, mode: 'keyword' });
35
- }
36
- catch {
37
- /* 全部失败 */
38
- }
39
- }
40
- }
41
- catch (err) {
42
- Logger.getInstance().warn('搜索失败', { query, error: err.message });
43
- watcher._notify(`搜索「${query}」失败: ${err.message}`);
44
- return;
45
- }
46
- const items = normalizeSearchResults(searchResult);
47
- // Xcode 代码插入场景: 有实际代码的结果优先展示
48
- items.sort((a, b) => {
49
- const aHasCode = a.code && a.code !== '(无预览内容)' && a.code.length > 30 ? 1 : 0;
50
- const bHasCode = b.code && b.code !== '(无预览内容)' && b.code.length > 30 ? 1 : 0;
51
- return bHasCode - aHasCode;
52
- });
53
- if (items.length === 0) {
54
- watcher._notify(`未找到「${query}」的相关结果`);
55
- return;
56
- }
57
- // NativeUI 交互选择
58
- const NU = await import('../../../platform/NativeUi.js');
59
- const selectedIndex = NU.showCombinedWindow(items, query);
60
- if (selectedIndex < 0 || selectedIndex >= items.length) {
61
- return;
62
- }
63
- const selected = items[selectedIndex];
64
- // 如果 selected 没有 moduleName,尝试从当前文件路径推断
65
- if (!selected.moduleName && selected.headers && selected.headers.length > 0) {
66
- try {
67
- const HeaderResolver = await import('../../../platform/ios/xcode/HeaderResolver.js');
68
- const resolved = await HeaderResolver.resolveHeadersForText(watcher.projectRoot, relativePath, (await import('node:fs')).readFileSync(fullPath, 'utf8'));
69
- if (resolved?.moduleName) {
70
- selected.moduleName = resolved.moduleName;
71
- }
72
- }
73
- catch {
74
- /* 解析失败不阻塞 */
75
- }
76
- }
77
- // Xcode 代码自动插入(osascript 跳转 + 粘贴)
78
- const { insertCodeToXcode } = await import('../../../platform/ios/xcode/XcodeIntegration.js');
79
- await insertCodeToXcode(watcher, fullPath, selected, searchLine);
80
- }
81
- /** 将搜索结果标准化为 NativeUI 可展示格式 */
82
- export function normalizeSearchResults(results) {
83
- if (!results) {
84
- return [];
85
- }
86
- const arr = Array.isArray(results)
87
- ? results
88
- : results.items || [];
89
- return arr
90
- .map((r) => {
91
- let code = '';
92
- let explanation = '';
93
- let headers = [];
94
- if (r.content) {
95
- try {
96
- const content = typeof r.content === 'string' ? JSON.parse(r.content) : r.content;
97
- // 注意: 不直接使用 content.markdown —— markdown 是完整文档,不是纯代码
98
- code =
99
- content.code ||
100
- content.pattern ||
101
- content.content ||
102
- content.body ||
103
- content.snippet ||
104
- content.solution ||
105
- content.example ||
106
- '';
107
- explanation =
108
- content.rationale ||
109
- content.description ||
110
- content.summary ||
111
- content.explanation ||
112
- '';
113
- if (Array.isArray(content.headers) && content.headers.length > 0) {
114
- headers = content.headers;
115
- }
116
- // 如果主字段为空,尝试从 Markdown 内容提取代码块
117
- if (!code && content.markdown) {
118
- code = _extractCodeFromMarkdown(content.markdown);
119
- // 若 markdown 中提取不出代码,且 explanation 为空,用 markdown 生成摘要
120
- if (!code && !explanation) {
121
- explanation = _stripMarkdownFormatting(content.markdown).substring(0, 500);
122
- }
123
- }
124
- }
125
- catch {
126
- // content 不是 JSON,可能是纯文本/代码 — 直接使用
127
- if (typeof r.content === 'string' && r.content.length > 10) {
128
- code = r.content.substring(0, 2000);
129
- }
130
- }
131
- }
132
- // 如果 Ranking Pipeline 已提取 code 字段,优先使用
133
- if (!code && r.code && r.code.length > 5) {
134
- code = r.code;
135
- }
136
- // V3: headers 是独立 JSON 列(字符串),优先解析
137
- if (headers.length === 0 && r.headers) {
138
- try {
139
- const parsed = typeof r.headers === 'string' ? JSON.parse(r.headers) : r.headers;
140
- if (Array.isArray(parsed) && parsed.length > 0) {
141
- headers = parsed;
142
- }
143
- }
144
- catch {
145
- /* ignore */
146
- }
147
- }
148
- // moduleName: 优先从独立列取
149
- let moduleName = r.moduleName || null;
150
- if (!moduleName && r.content) {
151
- try {
152
- const content = typeof r.content === 'string' ? JSON.parse(r.content) : r.content;
153
- moduleName = content.moduleName || null;
154
- }
155
- catch {
156
- /* ignore */
157
- }
158
- }
159
- if (!moduleName) {
160
- moduleName = r.moduleName || null;
161
- }
162
- // ── 从 code 中分离 #import / @import / import 行,归入 headers ──
163
- const finalCode = code || r.code || r.description || r.trigger || '(无预览内容)';
164
- const { cleanedCode, extractedHeaders } = _separateImportsFromCode(String(finalCode));
165
- if (extractedHeaders.length > 0) {
166
- for (const h of extractedHeaders) {
167
- if (!headers.some((existing) => existing.trim() === h.trim())) {
168
- headers.push(h);
169
- }
170
- }
171
- }
172
- return {
173
- title: r.title || r.name || r.id || 'Recipe',
174
- code: cleanedCode || '(无预览内容)',
175
- explanation: explanation || r.summary || r.description || '',
176
- headers,
177
- moduleName,
178
- trigger: r.trigger || r.completionKey || '',
179
- };
180
- })
181
- .filter((item) => item.title);
182
- }
183
- /**
184
- * 从代码文本中分离出 import/include 行
185
- *
186
- * 只提取位于代码开头的连续 import 块(含中间空行),
187
- * 代码正文中的 import(如注释或字符串里的)不做处理。
188
- *
189
- * 支持: #import, @import, #include, import (Swift)
190
- */
191
- function _separateImportsFromCode(code) {
192
- if (!code || code === '(无预览内容)') {
193
- return { cleanedCode: code, extractedHeaders: [] };
194
- }
195
- const lines = code.split(/\r?\n/);
196
- const importRe = /^\s*(#import\s|@import\s|#include\s|import\s)/;
197
- const extractedHeaders = [];
198
- let lastImportIdx = -1;
199
- // 从开头扫描连续 import 块(允许中间有空行)
200
- for (let i = 0; i < lines.length; i++) {
201
- const trimmed = lines[i].trim();
202
- if (!trimmed) {
203
- // 空行:如果前面已有 import,继续扫描
204
- if (lastImportIdx >= 0) {
205
- continue;
206
- }
207
- // 前面没 import,遇到前导空行也继续
208
- continue;
209
- }
210
- if (importRe.test(trimmed)) {
211
- extractedHeaders.push(trimmed);
212
- lastImportIdx = i;
213
- }
214
- else {
215
- // 遇到非 import 非空行,停止扫描
216
- break;
217
- }
218
- }
219
- if (extractedHeaders.length === 0) {
220
- return { cleanedCode: code, extractedHeaders: [] };
221
- }
222
- // 移除开头的 import 行和紧随的空行
223
- const remaining = lines.slice(lastImportIdx + 1);
224
- // 去掉残留的前导空行
225
- while (remaining.length > 0 && !remaining[0].trim()) {
226
- remaining.shift();
227
- }
228
- const cleanedCode = remaining.join('\n').trim();
229
- return { cleanedCode, extractedHeaders };
230
- }
231
- /**
232
- * 从 Markdown 文本中提取所有 fenced code blocks,合并为纯代码
233
- *
234
- * 支持 ```lang\n...\n``` 格式,提取多个代码块并用空行分隔。
235
- * 如果没有找到代码块,返回空字符串。
236
- *
237
- * @param md Markdown 文本
238
- * @returns 提取出的纯代码,或空字符串
239
- */
240
- function _extractCodeFromMarkdown(md) {
241
- if (!md) {
242
- return '';
243
- }
244
- const fencedRe = /```[\w]*\n([\s\S]*?)```/g;
245
- const blocks = [];
246
- let match;
247
- while ((match = fencedRe.exec(md)) !== null) {
248
- const block = match[1].trim();
249
- if (block) {
250
- blocks.push(block);
251
- }
252
- }
253
- return blocks.join('\n\n');
254
- }
255
- /**
256
- * 移除 Markdown 格式标记,返回纯文本摘要
257
- *
258
- * 用于在无法提取代码时,从 markdown 生成 explanation 文本。
259
- *
260
- * @param md Markdown 文本
261
- * @returns 纯文本
262
- */
263
- function _stripMarkdownFormatting(md) {
264
- if (!md) {
265
- return '';
266
- }
267
- return md
268
- .replace(/```[\w]*\n[\s\S]*?```/g, '') // 移除代码块
269
- .replace(/^#{1,6}\s+/gm, '') // 移除标题标记
270
- .replace(/\*\*([^*]+)\*\*/g, '$1') // 移除粗体
271
- .replace(/\*([^*]+)\*/g, '$1') // 移除斜体
272
- .replace(/`([^`]+)`/g, '$1') // 移除行内代码
273
- .replace(/^\s*[-*+]\s+/gm, '') // 移除列表标记
274
- .replace(/^\s*\d+\.\s+/gm, '') // 移除有序列表
275
- .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // 移除链接,保留文字
276
- .replace(/\n{3,}/g, '\n\n') // 压缩多余空行
277
- .trim();
278
- }
@@ -1,101 +0,0 @@
1
- /**
2
- * SnippetFactory — IDE 无关的 Snippet 生成工厂
3
- *
4
- * 职责:
5
- * 1. Recipe → SnippetSpec (IDE 无关的中间表示)
6
- * 2. 查询/列表操作 (listSnippets, getSnippet)
7
- * 3. 委托 Codec 生成最终 IDE 格式 (generate, generateBatch)
8
- *
9
- * Codec 注册:
10
- * factory.registerCodec(codec) — 注册 XcodeCodec / VSCodeCodec
11
- * factory.generate(spec, 'xcode') — 按 target 生成
12
- */
13
- import type { SnippetCodec, SnippetSpec } from './codecs/SnippetCodec.js';
14
- interface RecipeLike {
15
- id?: string;
16
- title: string;
17
- trigger?: string;
18
- code: string;
19
- description?: string;
20
- summary?: string;
21
- language?: string;
22
- [key: string]: unknown;
23
- }
24
- interface KnowledgeRepository {
25
- search(keyword: string, pagination: {
26
- page: number;
27
- pageSize: number;
28
- }): Promise<{
29
- data?: RecipeLike[];
30
- items?: RecipeLike[];
31
- }>;
32
- findWithPagination(filters: Record<string, unknown>, pagination: {
33
- page: number;
34
- pageSize: number;
35
- }): Promise<{
36
- data?: RecipeLike[];
37
- items?: RecipeLike[];
38
- }>;
39
- findById(id: string): Promise<RecipeLike | null>;
40
- }
41
- interface ListFilters {
42
- language?: string;
43
- category?: string;
44
- keyword?: string;
45
- }
46
- export declare class SnippetFactory {
47
- #private;
48
- _recipeRepo: KnowledgeRepository | null;
49
- /** @param [knowledgeRepository] KnowledgeRepositoryImpl(可选) */
50
- constructor(knowledgeRepository?: KnowledgeRepository | null);
51
- /** 注册一个 IDE codec */
52
- registerCodec(codec: SnippetCodec): void;
53
- /**
54
- * 获取已注册的 codec
55
- * @param target 'xcode' | 'vscode'
56
- */
57
- getCodec(target: string): SnippetCodec | undefined;
58
- /** 获取所有已注册 codec 的 ID 列表 */
59
- getRegisteredTargets(): string[];
60
- /** 运行时注入 knowledgeRepository(用于延迟绑定场景) */
61
- setKnowledgeRepository(repo: KnowledgeRepository): void;
62
- /**
63
- * 从 Recipe 列表实时生成 Snippet 列表
64
- * @param [filters] { language, category, keyword }
65
- */
66
- listSnippets(filters?: ListFilters, pagination?: {
67
- page: number;
68
- pageSize: number;
69
- }): Promise<SnippetSpec[]>;
70
- /** 从单个 Recipe ID 实时生成 Snippet */
71
- getSnippet(recipeId: string): Promise<SnippetSpec | null>;
72
- /**
73
- * 使用指定 codec 从 spec 生成 IDE 格式内容
74
- * @param spec SnippetSpec
75
- * @param [target='xcode'] codec ID
76
- */
77
- generate(spec: SnippetSpec, target?: string): string;
78
- /**
79
- * 批量生成 (委托 codec)
80
- * @returns > | { filename: string, content: string, specs: object[] }}
81
- */
82
- generateBatch(recipes: RecipeLike[], target?: string): {
83
- filename: string;
84
- content: string;
85
- spec: SnippetSpec;
86
- }[] | {
87
- filename: string;
88
- content: string | {
89
- filename: string;
90
- content: string;
91
- }[];
92
- specs: SnippetSpec[];
93
- };
94
- /**
95
- * 从 Recipe/Candidate 生成 IDE 无关的 snippet spec
96
- * @param recipe { id, title, trigger, code, description, language }
97
- * @returns SnippetSpec
98
- */
99
- fromRecipe(recipe: RecipeLike): SnippetSpec;
100
- }
101
- export {};
@@ -1,145 +0,0 @@
1
- /**
2
- * SnippetFactory — IDE 无关的 Snippet 生成工厂
3
- *
4
- * 职责:
5
- * 1. Recipe → SnippetSpec (IDE 无关的中间表示)
6
- * 2. 查询/列表操作 (listSnippets, getSnippet)
7
- * 3. 委托 Codec 生成最终 IDE 格式 (generate, generateBatch)
8
- *
9
- * Codec 注册:
10
- * factory.registerCodec(codec) — 注册 XcodeCodec / VSCodeCodec
11
- * factory.generate(spec, 'xcode') — 按 target 生成
12
- */
13
- export class SnippetFactory {
14
- _recipeRepo;
15
- #codecs = new Map();
16
- /** @param [knowledgeRepository] KnowledgeRepositoryImpl(可选) */
17
- constructor(knowledgeRepository) {
18
- this._recipeRepo = knowledgeRepository || null;
19
- }
20
- // ─────────────── Codec 注册 ───────────────
21
- /** 注册一个 IDE codec */
22
- registerCodec(codec) {
23
- this.#codecs.set(codec.id, codec);
24
- }
25
- /**
26
- * 获取已注册的 codec
27
- * @param target 'xcode' | 'vscode'
28
- */
29
- getCodec(target) {
30
- return this.#codecs.get(target);
31
- }
32
- /** 获取所有已注册 codec 的 ID 列表 */
33
- getRegisteredTargets() {
34
- return [...this.#codecs.keys()];
35
- }
36
- // ─────────────── 依赖注入 ───────────────
37
- /** 运行时注入 knowledgeRepository(用于延迟绑定场景) */
38
- setKnowledgeRepository(repo) {
39
- this._recipeRepo = repo;
40
- }
41
- // ─────────────── Recipe → Snippet 查询 ───────────────
42
- /**
43
- * 从 Recipe 列表实时生成 Snippet 列表
44
- * @param [filters] { language, category, keyword }
45
- */
46
- async listSnippets(filters = {}, pagination = { page: 1, pageSize: 50 }) {
47
- if (!this._recipeRepo) {
48
- return [];
49
- }
50
- const dbFilters = { status: 'active' };
51
- if (filters.language) {
52
- dbFilters.language = filters.language;
53
- }
54
- if (filters.category) {
55
- dbFilters.category = filters.category;
56
- }
57
- let result;
58
- if (filters.keyword) {
59
- result = await this._recipeRepo.search(filters.keyword, pagination);
60
- }
61
- else {
62
- result = await this._recipeRepo.findWithPagination(dbFilters, pagination);
63
- }
64
- const recipes = result?.data || result?.items || [];
65
- return recipes.map((r) => this.fromRecipe(r));
66
- }
67
- /** 从单个 Recipe ID 实时生成 Snippet */
68
- async getSnippet(recipeId) {
69
- if (!this._recipeRepo) {
70
- return null;
71
- }
72
- const recipe = await this._recipeRepo.findById(recipeId);
73
- if (!recipe) {
74
- return null;
75
- }
76
- return this.fromRecipe(recipe);
77
- }
78
- // ─────────────── Codec 委托生成 ───────────────
79
- /**
80
- * 使用指定 codec 从 spec 生成 IDE 格式内容
81
- * @param spec SnippetSpec
82
- * @param [target='xcode'] codec ID
83
- */
84
- generate(spec, target = 'xcode') {
85
- const codec = this.#resolveCodec(target);
86
- return codec.generate(spec);
87
- }
88
- /**
89
- * 批量生成 (委托 codec)
90
- * @returns > | { filename: string, content: string, specs: object[] }}
91
- */
92
- generateBatch(recipes, target = 'xcode') {
93
- const codec = this.#resolveCodec(target);
94
- const specs = recipes.map((r) => this.fromRecipe(r));
95
- const bundleFilename = codec.getBundleFilename();
96
- if (bundleFilename) {
97
- // VSCode 模式: 单 bundle 文件
98
- return {
99
- filename: bundleFilename,
100
- content: codec.generateBundle(specs),
101
- specs,
102
- };
103
- }
104
- // Xcode 模式: 每个 snippet 一个文件
105
- return specs.map((spec) => ({
106
- filename: `${spec.identifier}${codec.fileExtension}`,
107
- content: codec.generate(spec),
108
- spec,
109
- }));
110
- }
111
- // ─────────────── Recipe → SnippetSpec ───────────────
112
- /**
113
- * 从 Recipe/Candidate 生成 IDE 无关的 snippet spec
114
- * @param recipe { id, title, trigger, code, description, language }
115
- * @returns SnippetSpec
116
- */
117
- fromRecipe(recipe) {
118
- return {
119
- identifier: `com.autosnippet.${recipe.id || this.#slugify(recipe.title)}`,
120
- title: recipe.title,
121
- completion: recipe.trigger || this.#slugify(recipe.title),
122
- summary: recipe.description || recipe.summary || '',
123
- code: recipe.code,
124
- language: recipe.language || 'unknown',
125
- };
126
- }
127
- // ─────────────── Private ───────────────
128
- #resolveCodec(target) {
129
- const codec = this.#codecs.get(target);
130
- if (!codec) {
131
- throw new Error(`No codec registered for target "${target}". Available: [${this.getRegisteredTargets().join(', ')}]`);
132
- }
133
- return codec;
134
- }
135
- #slugify(str) {
136
- if (!str) {
137
- return 'unnamed';
138
- }
139
- return str
140
- .toLowerCase()
141
- .replace(/[^a-z0-9]+/g, '-')
142
- .replace(/(^-|-$)/g, '')
143
- .slice(0, 50);
144
- }
145
- }
@@ -1,91 +0,0 @@
1
- /**
2
- * SnippetInstaller — Codec 驱动的 Snippet 安装器
3
- *
4
- * 支持:
5
- * - Xcode: 每个 snippet 一个 .codesnippet 文件
6
- * - VSCode: 所有 snippets 合并为单个 .code-snippets JSON 文件
7
- *
8
- * 行为由注入的 SnippetCodec 决定。
9
- */
10
- import type { SnippetCodec, SnippetSpec } from './codecs/SnippetCodec.js';
11
- import type { SnippetFactory } from './SnippetFactory.js';
12
- interface RecipeLike {
13
- id?: string;
14
- title: string;
15
- trigger?: string;
16
- code: string;
17
- description?: string;
18
- summary?: string;
19
- language?: string;
20
- [key: string]: unknown;
21
- }
22
- interface InstallerOptions {
23
- codec?: SnippetCodec | null;
24
- snippetFactory?: SnippetFactory | null;
25
- snippetsDir?: string | null;
26
- }
27
- export declare class SnippetInstaller {
28
- #private;
29
- /**
30
- * @param options.codec IDE codec
31
- * @param [options.snippetsDir] 覆盖 codec 默认目录
32
- */
33
- constructor(options?: InstallerOptions);
34
- /** codec ID ('xcode' | 'vscode') */
35
- get target(): string;
36
- /** 当前安装目录 */
37
- get snippetsDir(): string;
38
- setSnippetFactory(factory: SnippetFactory): void;
39
- setCodec(codec: SnippetCodec): void;
40
- /**
41
- * 安装单个 snippet spec
42
- * @param spec SnippetSpec
43
- * @param [projectRoot] VSCode 需要 projectRoot 确定 .vscode/ 路径
44
- * @returns }
45
- */
46
- install(spec: SnippetSpec, projectRoot?: string): {
47
- success: boolean;
48
- path: string;
49
- message: string;
50
- };
51
- /**
52
- * 从 Recipe 批量安装
53
- * @returns }
54
- */
55
- installFromRecipes(recipes: RecipeLike[], projectRoot?: string): {
56
- success: boolean;
57
- count: number;
58
- successCount: number;
59
- errorCount: number;
60
- details: {
61
- success: boolean;
62
- path: string;
63
- message: string;
64
- }[];
65
- };
66
- /**
67
- * 列出已安装的 AutoSnippet 管理的 snippet
68
- * @returns >}
69
- */
70
- listInstalled(projectRoot?: string): {
71
- filename: string;
72
- path: string;
73
- }[];
74
- /**
75
- * 卸载指定 snippet
76
- * @returns }
77
- */
78
- uninstall(identifier: string, projectRoot?: string): {
79
- success: boolean;
80
- message: string;
81
- };
82
- /**
83
- * 清除所有 AutoSnippet 管理的 snippet
84
- * @returns }
85
- */
86
- cleanAll(projectRoot?: string): {
87
- success: boolean;
88
- removed: number;
89
- };
90
- }
91
- export {};