autosnippet 1.6.1 → 1.7.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.
- package/README.md +5 -8
- package/bin/api-server.js +84 -0
- package/bin/asd-cli.js +3 -2
- package/bin/cli-commands.js +169 -16
- package/bin/dashboard/routes/recipes.js +45 -0
- package/bin/dashboard/routes/search.js +194 -27
- package/bin/dashboard/routes/snippets.js +28 -0
- package/bin/dashboard/routes/spm.js +44 -12
- package/bin/dashboard-server.js +17 -4
- package/checksums.json +4 -4
- package/dashboard/dist/assets/index-C4kWcLRI.js +62 -0
- package/dashboard/dist/assets/index-DqAMMzYn.css +1 -0
- package/dashboard/dist/assets/{markdown-DqCR-tTD.js → markdown-YurNruXx.js} +1 -1
- package/dashboard/dist/assets/{vendor-DcWukCsG.js → vendor-Bz-TYq6B.js} +64 -69
- package/dashboard/dist/index.html +4 -4
- package/lib/agent/Agent.js +402 -0
- package/lib/agent/AgentCoordinator.js +10 -0
- package/lib/agent/ResultFusion.js +33 -0
- package/lib/agent/Task.js +337 -0
- package/lib/agent/UserPreferenceManager.js +5 -0
- package/lib/ai/providers/ClaudeProvider.js +131 -6
- package/lib/ai/providers/GoogleGeminiProvider.js +153 -28
- package/lib/ai/providers/MockProvider.js +49 -2
- package/lib/ai/providers/OpenAiProvider.js +161 -18
- package/lib/api/APIGateway.js +689 -0
- package/lib/application/services/ContextServiceCompat.js +73 -30
- package/lib/application/services/IntelligentServiceLayer.js +172 -7
- package/lib/application/services/SearchServiceV2.js +198 -41
- package/lib/automation/ActionPipeline.js +13 -0
- package/lib/automation/AutomationOrchestrator.js +23 -0
- package/lib/automation/ContextCollector.js +10 -0
- package/lib/automation/OutputApplier.js +10 -0
- package/lib/automation/TriggerResolver.js +13 -0
- package/lib/business/metrics/MetricsHub.js +442 -0
- package/lib/business/recipe/RecipeHub.js +460 -0
- package/lib/business/search/SearchHub.js +484 -0
- package/lib/candidate/aggregateCandidates.js +79 -0
- package/lib/cli/candidateCommand.js +143 -0
- package/lib/cli/embedCommand.js +47 -0
- package/lib/cli/searchCommand.js +138 -0
- package/lib/cli/statusCommand.js +256 -0
- package/lib/guard/guardViolations.js +1 -1
- package/lib/infrastructure/cache/CacheHub.js +243 -0
- package/lib/infrastructure/error/ErrorManager.js +453 -0
- package/lib/infrastructure/external/OpenBrowser.js +14 -1
- package/lib/infrastructure/external/spm/DepFixer.js +164 -0
- package/lib/infrastructure/external/spm/DepGraphAnalyzer.js +98 -0
- package/lib/infrastructure/external/spm/DepGraphService.js +208 -0
- package/lib/infrastructure/external/spm/DepPolicyEngine.js +49 -0
- package/lib/infrastructure/external/spm/DepReport.js +10 -0
- package/lib/infrastructure/external/spm/SpmDepsServiceV2.js +219 -194
- package/lib/infrastructure/external/spm/spmDepGraphPatch.js +4 -0
- package/lib/infrastructure/external/spm/spmDepMapUpdater.js +10 -0
- package/lib/infrastructure/logging/LogFactory.js +160 -0
- package/lib/infrastructure/notification/ClipboardManager.js +137 -0
- package/lib/infrastructure/notification/NativeUi.js +71 -6
- package/lib/infrastructure/notification/Notifier.js +52 -0
- package/lib/infrastructure/paths/PathFinder.js +19 -110
- package/lib/infrastructure/paths/ProjectStructure.js +222 -0
- package/lib/infrastructure/process/ProcessHub.js +452 -0
- package/lib/injection/DirectiveParserV2.js +33 -17
- package/lib/injection/ImportDecisionEngine.js +39 -0
- package/lib/injection/ImportWriterV2.js +210 -5
- package/lib/injection/ModuleResolverV2.js +93 -17
- package/lib/injection/injectionService.js +172 -21
- package/lib/mcp/envelope.js +21 -0
- package/lib/mcp/schemas.js +46 -0
- package/lib/rateLimit.js +10 -2
- package/lib/recipe/validateRecipeCandidate.js +54 -0
- package/lib/search/indexer.js +6 -1
- package/lib/search/rankingEngine.js +11 -2
- package/lib/search/recallEngine.js +5 -1
- package/lib/search/unifiedSearch.js +245 -0
- package/lib/watch/DirectiveDetector.js +1 -1
- package/lib/watch/FileWatchService.js +31 -0
- package/lib/watch/handlers/AlinkHandler.js +14 -0
- package/lib/watch/handlers/CreateHandler.js +72 -4
- package/lib/watch/handlers/DraftHandler.js +14 -0
- package/lib/watch/handlers/GuardHandler.js +14 -0
- package/lib/watch/handlers/HeaderHandler.js +14 -0
- package/lib/watch/handlers/SearchHandler.js +899 -43
- package/lib/writeGuard.js +10 -15
- package/package.json +9 -5
- package/recipes/README.md +2 -2
- package/resources/native-ui/main.swift +169 -30
- package/scripts/cursor-rules/autosnippet-conventions.mdc +10 -8
- package/scripts/demo-candidates-submit.js +76 -0
- package/scripts/diagnose-mcp.js +74 -0
- package/scripts/generate-checksums.js +16 -0
- package/scripts/generate-recipe-drafts.js +151 -0
- package/scripts/init-xcode-snippets.js +310 -0
- package/scripts/install-cursor-skill.js +13 -20
- package/scripts/install-full.js +2 -11
- package/scripts/mcp-server.js +460 -97
- package/scripts/random-search-test.js +142 -0
- package/scripts/recipe-audit.js +230 -0
- package/scripts/recipe-migration-diagnose.js +0 -11
- package/scripts/test-ai-failure-handling.js +79 -0
- package/scripts/test-google-models.js +85 -0
- package/scripts/test-hybrid-comprehensive.js +120 -0
- package/scripts/test-hybrid-search.js +54 -0
- package/scripts/test-search-modes.js +364 -0
- package/scripts/test-sentence-search.js +80 -0
- package/skills/autosnippet-batch-scan/SKILL.md +4 -2
- package/skills/autosnippet-candidates/SKILL.md +79 -0
- package/skills/autosnippet-candidates/SKILL_REDESIGNED.md +813 -0
- package/skills/autosnippet-concepts/SKILL.md +140 -125
- package/skills/autosnippet-create/SKILL.md +4 -2
- package/skills/autosnippet-dep-graph/SKILL.md +5 -3
- package/skills/autosnippet-guard/SKILL.md +7 -1
- package/skills/autosnippet-intent/SKILL.md +40 -0
- package/skills/autosnippet-recipe-candidates/SKILL.md +4 -2
- package/skills/autosnippet-recipes/SKILL.md +5 -2
- package/skills/autosnippet-search/SKILL.md +5 -3
- package/skills/autosnippet-structure/SKILL.md +34 -0
- package/skills/autosnippet-when/SKILL.md +11 -9
- package/dashboard/dist/assets/index-DIIxAaTf.js +0 -54
- package/dashboard/dist/assets/index-DnrK_geI.css +0 -1
|
@@ -3,6 +3,23 @@ function registerSearchRoutes(app, ctx) {
|
|
|
3
3
|
const Paths = require('../../../lib/infrastructure/config/Paths.js');
|
|
4
4
|
const { analyzeContext } = require('../../../lib/search/contextAnalyzer.js');
|
|
5
5
|
|
|
6
|
+
// ============================================================
|
|
7
|
+
// 调试工具
|
|
8
|
+
// ============================================================
|
|
9
|
+
const DEBUG = process.env.ASD_DEBUG_SEARCH === '1' || process.env.DEBUG?.includes('search');
|
|
10
|
+
|
|
11
|
+
function debug(taskName, step, data) {
|
|
12
|
+
if (!DEBUG) return;
|
|
13
|
+
const timestamp = new Date().toISOString().split('T')[1];
|
|
14
|
+
console.log(`\n[${timestamp}] [Search:${taskName}] ${step}`, data ? JSON.stringify(data, null, 2) : '');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function debugError(taskName, step, error) {
|
|
18
|
+
if (!DEBUG) return;
|
|
19
|
+
const timestamp = new Date().toISOString().split('T')[1];
|
|
20
|
+
console.error(`\n[${timestamp}] [Search:${taskName}] ❌ ${step}`, error?.message || error);
|
|
21
|
+
}
|
|
22
|
+
|
|
6
23
|
// ============================================================
|
|
7
24
|
// 统一的搜索核心函数(共享给所有搜索 API)
|
|
8
25
|
// ============================================================
|
|
@@ -21,33 +38,57 @@ function registerSearchRoutes(app, ctx) {
|
|
|
21
38
|
throw new Error('keyword is required');
|
|
22
39
|
}
|
|
23
40
|
|
|
41
|
+
const taskId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
42
|
+
debug(taskId, 'INIT', {
|
|
43
|
+
keyword,
|
|
44
|
+
targetName,
|
|
45
|
+
currentFile,
|
|
46
|
+
language,
|
|
47
|
+
limit,
|
|
48
|
+
hasFileContent: !!fileContent
|
|
49
|
+
});
|
|
50
|
+
|
|
24
51
|
const { getInstance } = require('../../../lib/context');
|
|
25
52
|
const recipeStats = require('../../../lib/recipe/recipeStats');
|
|
26
53
|
const service = getInstance(projectRoot);
|
|
27
54
|
const rootSpec = require(Paths.getProjectSpecPath(projectRoot));
|
|
28
55
|
|
|
29
56
|
// 1. 分析当前上下文(使用 analyzeContext 深度分析)
|
|
57
|
+
debug(taskId, '1️⃣ Context Analysis Start', { targetName, currentFile, language });
|
|
30
58
|
const contextInfo = await analyzeContext(projectRoot, { targetName, currentFile, language });
|
|
59
|
+
debug(taskId, '1️⃣ Context Analysis Complete', contextInfo);
|
|
31
60
|
|
|
32
61
|
let vectorResults = [];
|
|
33
62
|
let keywordResults = [];
|
|
34
63
|
|
|
35
64
|
// 2. 向量搜索
|
|
65
|
+
debug(taskId, '2️⃣ Vector Search Start', { keyword, limit: limit + 5 });
|
|
36
66
|
try {
|
|
37
67
|
const ai = await AiFactory.getProvider(projectRoot);
|
|
38
68
|
if (ai) {
|
|
39
69
|
vectorResults = await service.search(keyword, { limit: limit + 5, filter: { type: 'recipe' } });
|
|
40
70
|
vectorResults = vectorResults.map(r => ({ ...r, _vectorScore: r.similarity || 0 }));
|
|
71
|
+
debug(taskId, '2️⃣ Vector Search Complete', {
|
|
72
|
+
count: vectorResults.length,
|
|
73
|
+
topResults: vectorResults.slice(0, 3).map(r => ({
|
|
74
|
+
name: r.name,
|
|
75
|
+
score: r._vectorScore
|
|
76
|
+
}))
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
debug(taskId, '2️⃣ Vector Search Skipped', { reason: 'No AI provider' });
|
|
41
80
|
}
|
|
42
81
|
} catch (e) {
|
|
43
|
-
|
|
82
|
+
debugError(taskId, '2️⃣ Vector Search Failed', e);
|
|
44
83
|
}
|
|
45
84
|
|
|
46
85
|
// 3. 关键词搜索
|
|
86
|
+
debug(taskId, '3️⃣ Keyword Search Start', { keyword });
|
|
47
87
|
try {
|
|
48
88
|
const recipesDir = Paths.getProjectRecipesPath(projectRoot, rootSpec);
|
|
49
89
|
if (fs.existsSync(recipesDir)) {
|
|
50
90
|
const keywordTerms = keyword.toLowerCase().split(/\s+/).filter(w => w.length > 1);
|
|
91
|
+
debug(taskId, '3️⃣ Keyword Terms', { keywordTerms });
|
|
51
92
|
|
|
52
93
|
// 从 contextInfo 获取丰富的上下文术语
|
|
53
94
|
const contextTerms = [
|
|
@@ -106,15 +147,27 @@ function registerSearchRoutes(app, ctx) {
|
|
|
106
147
|
});
|
|
107
148
|
}
|
|
108
149
|
} catch (e) {
|
|
109
|
-
|
|
150
|
+
debugError(taskId, '3️⃣ Recipe Parse Error', { file: recipePath, error: e.message });
|
|
110
151
|
}
|
|
111
152
|
}
|
|
153
|
+
debug(taskId, '3️⃣ Keyword Search Complete', {
|
|
154
|
+
count: keywordResults.length,
|
|
155
|
+
topResults: keywordResults.slice(0, 3).map(r => ({
|
|
156
|
+
name: r.name,
|
|
157
|
+
score: r._keywordScore,
|
|
158
|
+
contextMatches: r._contextMatches
|
|
159
|
+
}))
|
|
160
|
+
});
|
|
112
161
|
}
|
|
113
162
|
} catch (e) {
|
|
114
|
-
|
|
163
|
+
debugError(taskId, '3️⃣ Keyword Search Failed', e);
|
|
115
164
|
}
|
|
116
165
|
|
|
117
166
|
// 4. 合并和评分
|
|
167
|
+
debug(taskId, '4️⃣ Merge & Score Start', {
|
|
168
|
+
vectorCount: vectorResults.length,
|
|
169
|
+
keywordCount: keywordResults.length
|
|
170
|
+
});
|
|
118
171
|
const mergedMap = new Map();
|
|
119
172
|
|
|
120
173
|
for (const vr of vectorResults) {
|
|
@@ -161,10 +214,25 @@ function registerSearchRoutes(app, ctx) {
|
|
|
161
214
|
});
|
|
162
215
|
|
|
163
216
|
// 6. 权威分加权排序
|
|
217
|
+
debug(taskId, '5️⃣ Hybrid Score Calculation', {
|
|
218
|
+
count: results.length,
|
|
219
|
+
topResults: results.slice(0, 3).map(r => ({
|
|
220
|
+
name: r.name,
|
|
221
|
+
hybridScore: r._hybridScore,
|
|
222
|
+
vectorScore: r._vectorScore,
|
|
223
|
+
keywordScore: r._keywordScore,
|
|
224
|
+
isContextRelevant: r.isContextRelevant
|
|
225
|
+
}))
|
|
226
|
+
});
|
|
164
227
|
try {
|
|
165
228
|
const stats = recipeStats.getRecipeStats(projectRoot);
|
|
166
229
|
const byFileEntries = Object.values(stats.byFile || {});
|
|
167
230
|
|
|
231
|
+
debug(taskId, '6️⃣ Authority Scoring Start', {
|
|
232
|
+
statsCount: Object.keys(stats.byFile || {}).length,
|
|
233
|
+
totalByFileEntries: byFileEntries.length
|
|
234
|
+
});
|
|
235
|
+
|
|
168
236
|
results = results.map(item => {
|
|
169
237
|
const key = path.basename(item.name);
|
|
170
238
|
const entry = (stats.byFile || {})[key];
|
|
@@ -225,22 +293,37 @@ Recipe: ${results[0]?.name || 'N/A'}
|
|
|
225
293
|
similarity: r.similarity * 0.7 + aiBoost * 0.3
|
|
226
294
|
}));
|
|
227
295
|
|
|
228
|
-
|
|
296
|
+
debug(taskId, '7️⃣ AI Evaluation Complete', {
|
|
297
|
+
aiScore,
|
|
298
|
+
aiBoost: aiBoost.toFixed(2),
|
|
299
|
+
topResultFinalScore: results[0]?.similarity
|
|
300
|
+
});
|
|
229
301
|
}
|
|
230
302
|
} catch (e) {
|
|
231
|
-
|
|
303
|
+
debugError(taskId, '7️⃣ AI Evaluation Failed', e);
|
|
232
304
|
}
|
|
233
305
|
}
|
|
234
306
|
|
|
235
307
|
// 8. 排序和限制
|
|
308
|
+
debug(taskId, '8️⃣ Sort & Filter Start', { beforeCount: results.length, minSimilarity: vectorResults.length > 0 ? 0.3 : 0.2 });
|
|
236
309
|
results.sort((a, b) => b.similarity - a.similarity);
|
|
237
310
|
|
|
238
311
|
// 设置最小相似度阈值
|
|
239
312
|
const minSimilarity = vectorResults.length > 0 ? 0.3 : 0.2;
|
|
240
313
|
results = results.filter(r => r.similarity >= minSimilarity);
|
|
241
314
|
|
|
315
|
+
debug(taskId, '8️⃣ After Filter', { afterCount: results.length, limit });
|
|
242
316
|
results = results.slice(0, limit);
|
|
243
317
|
|
|
318
|
+
debug(taskId, '✅ COMPLETE', {
|
|
319
|
+
finalCount: results.length,
|
|
320
|
+
topResults: results.slice(0, 3).map(r => ({
|
|
321
|
+
name: r.name,
|
|
322
|
+
similarity: r.similarity,
|
|
323
|
+
authority: r.authority
|
|
324
|
+
}))
|
|
325
|
+
});
|
|
326
|
+
|
|
244
327
|
return {
|
|
245
328
|
results,
|
|
246
329
|
contextInfo,
|
|
@@ -410,16 +493,58 @@ Recipe: ${results[0]?.name || 'N/A'}
|
|
|
410
493
|
console.log('[Trigger from Code] Search Results:', JSON.stringify(resultInfo, null, 2));
|
|
411
494
|
|
|
412
495
|
// 5. 清理结果为 Xcode 格式
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
496
|
+
// 5.1. 使用 SearchServiceV2 为结果添加 Agent 评分
|
|
497
|
+
let agentScores = new Map();
|
|
498
|
+
try {
|
|
499
|
+
const SearchServiceV2 = require('../../../lib/application/services/SearchServiceV2');
|
|
500
|
+
const searchServiceV2 = new SearchServiceV2(projectRoot, {
|
|
501
|
+
enableIntelligentLayer: true,
|
|
502
|
+
enableLearning: false
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// 生成 sessionId 和 userId
|
|
506
|
+
const sessionId = `xcode-${filePath || 'general'}-${Date.now()}`;
|
|
507
|
+
const userId = process.env.ASD_USER_ID || process.env.USER || process.env.USERNAME || 'unknown';
|
|
508
|
+
|
|
509
|
+
// 对前 5 个结果调用 SearchServiceV2 以获得 Agent 评分
|
|
510
|
+
const agentResults = await searchServiceV2.search(searchKeyword, {
|
|
511
|
+
limit: 5,
|
|
512
|
+
sessionId,
|
|
513
|
+
userId,
|
|
514
|
+
context: { source: 'dashboard-xcode-watch' }
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// 构建 agentScores map
|
|
518
|
+
if (agentResults && Array.isArray(agentResults)) {
|
|
519
|
+
for (const agentResult of agentResults) {
|
|
520
|
+
if (agentResult.name && agentResult.qualityScore !== undefined) {
|
|
521
|
+
agentScores.set(agentResult.name, {
|
|
522
|
+
qualityScore: agentResult.qualityScore,
|
|
523
|
+
recommendReason: agentResult.recommendReason || undefined
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} catch (e) {
|
|
529
|
+
console.warn('[Trigger from Code] Agent scoring failed, using defaults:', e.message);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// 5.2. 清理结果为 Xcode 格式
|
|
533
|
+
const cleanResults = results.map(r => {
|
|
534
|
+
const agentScore = agentScores.get(r.name);
|
|
535
|
+
return {
|
|
536
|
+
name: r.name,
|
|
537
|
+
snippet: r.content ? r.content.substring(0, 300) : '',
|
|
538
|
+
similarity: Math.round(r.similarity * 100),
|
|
539
|
+
isContextRelevant: r.isContextRelevant,
|
|
540
|
+
authority: Math.round(r.authority * 100) / 100,
|
|
541
|
+
usageCount: r.usageCount,
|
|
542
|
+
stats: r.stats,
|
|
543
|
+
aiRelevanceScore: r._aiRelevanceScore ? Math.round(r._aiRelevanceScore) : undefined,
|
|
544
|
+
qualityScore: agentScore?.qualityScore,
|
|
545
|
+
recommendReason: agentScore?.recommendReason
|
|
546
|
+
};
|
|
547
|
+
});
|
|
423
548
|
|
|
424
549
|
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
425
550
|
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
@@ -498,18 +623,60 @@ Recipe: ${results[0]?.name || 'N/A'}
|
|
|
498
623
|
console.log('[Context-Aware Search] Search Results:', JSON.stringify(resultInfo, null, 2));
|
|
499
624
|
|
|
500
625
|
// 格式化结果供 Dashboard 使用
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
626
|
+
// 使用 SearchServiceV2 为结果添加 Agent 评分
|
|
627
|
+
let agentScores = new Map();
|
|
628
|
+
try {
|
|
629
|
+
const SearchServiceV2 = require('../../../lib/application/services/SearchServiceV2');
|
|
630
|
+
const searchServiceV2 = new SearchServiceV2(projectRoot, {
|
|
631
|
+
enableIntelligentLayer: true,
|
|
632
|
+
enableLearning: false
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// 生成 sessionId 和 userId
|
|
636
|
+
const sessionId = `dashboard-${currentFile || 'general'}-${Date.now()}`;
|
|
637
|
+
const userId = process.env.ASD_USER_ID || process.env.USER || process.env.USERNAME || 'unknown';
|
|
638
|
+
|
|
639
|
+
// 对前 5 个结果调用 SearchServiceV2 以获得 Agent 评分
|
|
640
|
+
const agentResults = await searchServiceV2.search(keyword, {
|
|
641
|
+
limit: 5,
|
|
642
|
+
sessionId,
|
|
643
|
+
userId,
|
|
644
|
+
context: { source: 'dashboard-context-aware' }
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// 构建 agentScores map
|
|
648
|
+
if (agentResults && Array.isArray(agentResults)) {
|
|
649
|
+
for (const agentResult of agentResults) {
|
|
650
|
+
if (agentResult.name && agentResult.qualityScore !== undefined) {
|
|
651
|
+
agentScores.set(agentResult.name, {
|
|
652
|
+
qualityScore: agentResult.qualityScore,
|
|
653
|
+
recommendReason: agentResult.recommendReason || undefined
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
} catch (e) {
|
|
659
|
+
console.warn('[Context-Aware Search] Agent scoring failed, using defaults:', e.message);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// 格式化结果供 Dashboard 使用
|
|
663
|
+
const cleanResults = results.map(r => {
|
|
664
|
+
const agentScore = agentScores.get(r.name);
|
|
665
|
+
return {
|
|
666
|
+
name: r.name,
|
|
667
|
+
content: r.content,
|
|
668
|
+
similarity: Math.round(r.similarity * 100) / 100,
|
|
669
|
+
authority: r.authority,
|
|
670
|
+
usageCount: r.usageCount,
|
|
671
|
+
stats: r.stats,
|
|
672
|
+
isContextRelevant: r.isContextRelevant,
|
|
673
|
+
matchType: r._vectorScore > r._keywordScore ? 'semantic' : 'keyword',
|
|
674
|
+
metadata: r.metadata,
|
|
675
|
+
aiRelevanceScore: r.aiRelevanceScore, // 多标记 AI 评分
|
|
676
|
+
qualityScore: agentScore?.qualityScore,
|
|
677
|
+
recommendReason: agentScore?.recommendReason
|
|
678
|
+
};
|
|
679
|
+
});
|
|
513
680
|
|
|
514
681
|
res.json({
|
|
515
682
|
results: cleanResults,
|
|
@@ -101,6 +101,34 @@ function registerSnippetsRoutes(app, ctx) {
|
|
|
101
101
|
res.status(500).json({ error: err.message });
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
|
+
|
|
105
|
+
// API: 删除所有 Snippets
|
|
106
|
+
app.post('/api/snippets/delete-all', async (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const probe = writeGuard.checkWritePermission(projectRoot);
|
|
109
|
+
if (!probe.ok) {
|
|
110
|
+
return res.status(403).json({ error: probe.error || '没权限', code: 'RECIPE_WRITE_FORBIDDEN' });
|
|
111
|
+
}
|
|
112
|
+
const rootSpecPath = Paths.getProjectSpecPath(projectRoot);
|
|
113
|
+
const spec = specRepository.readSpecFile(rootSpecPath);
|
|
114
|
+
const count = spec?.list?.length || 0;
|
|
115
|
+
|
|
116
|
+
if (count > 0) {
|
|
117
|
+
// 获取所有 snippet identifiers
|
|
118
|
+
const identifiers = spec.list.map(s => s.identifier);
|
|
119
|
+
|
|
120
|
+
// 逐个删除(使用 deleteSnippet 确保清理分体文件和同步)
|
|
121
|
+
for (const identifier of identifiers) {
|
|
122
|
+
await specRepository.deleteSnippet(rootSpecPath, identifier, { syncRoot: true });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
res.json({ success: true, count });
|
|
127
|
+
} catch (err) {
|
|
128
|
+
console.error('[API Error]', err);
|
|
129
|
+
res.status(500).json({ error: err.message });
|
|
130
|
+
}
|
|
131
|
+
});
|
|
104
132
|
}
|
|
105
133
|
|
|
106
134
|
module.exports = {
|
|
@@ -15,6 +15,7 @@ function registerSpmRoutes(app, ctx) {
|
|
|
15
15
|
// API: 获取项目 SPM 依赖关系图(优先读 spmmap 全解析结果,用于前端「依赖关系图」页展示)
|
|
16
16
|
app.get('/api/dep-graph', async (req, res) => {
|
|
17
17
|
try {
|
|
18
|
+
const level = String(req.query?.level || 'package');
|
|
18
19
|
const knowledgeDir = Paths.getProjectKnowledgePath(projectRoot);
|
|
19
20
|
const mapPath = path.join(knowledgeDir, 'AutoSnippet.spmmap.json');
|
|
20
21
|
let graph = null;
|
|
@@ -31,18 +32,48 @@ function registerSpmRoutes(app, ctx) {
|
|
|
31
32
|
if (!graph || !graph.packages) {
|
|
32
33
|
return res.json({ nodes: [], edges: [], projectRoot: null });
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
let nodes = [];
|
|
36
|
+
let edges = [];
|
|
37
|
+
|
|
38
|
+
if (level === 'target') {
|
|
39
|
+
const nodeList = [];
|
|
40
|
+
const baseEdges = [];
|
|
41
|
+
for (const [pkgName, pkgInfo] of Object.entries(graph.packages)) {
|
|
42
|
+
const targetsInfo = pkgInfo?.targetsInfo || {};
|
|
43
|
+
for (const [targetName, info] of Object.entries(targetsInfo)) {
|
|
44
|
+
const id = `${pkgName}::${targetName}`;
|
|
45
|
+
nodeList.push({
|
|
46
|
+
id,
|
|
47
|
+
label: targetName,
|
|
48
|
+
type: 'target',
|
|
49
|
+
packageName: pkgName,
|
|
50
|
+
});
|
|
51
|
+
const deps = info?.dependencies || [];
|
|
52
|
+
for (const d of deps) {
|
|
53
|
+
const depName = d?.name;
|
|
54
|
+
if (!depName) continue;
|
|
55
|
+
const depPkg = d?.package || pkgName;
|
|
56
|
+
const toId = `${depPkg}::${depName}`;
|
|
57
|
+
baseEdges.push({ from: id, to: toId, source: 'base' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
nodes = nodeList;
|
|
63
|
+
edges = baseEdges;
|
|
64
|
+
} else {
|
|
65
|
+
nodes = Object.keys(graph.packages).map((id) => ({
|
|
66
|
+
id,
|
|
67
|
+
label: id,
|
|
68
|
+
type: 'package',
|
|
69
|
+
packageDir: graph.packages[id]?.packageDir,
|
|
70
|
+
packageSwift: graph.packages[id]?.packageSwift,
|
|
71
|
+
targets: graph.packages[id]?.targets,
|
|
72
|
+
}));
|
|
73
|
+
for (const [from, tos] of Object.entries(graph.edges || {})) {
|
|
74
|
+
for (const to of tos || []) {
|
|
75
|
+
edges.push({ from, to, source: 'base' });
|
|
76
|
+
}
|
|
46
77
|
}
|
|
47
78
|
}
|
|
48
79
|
res.json({
|
|
@@ -57,6 +88,7 @@ function registerSpmRoutes(app, ctx) {
|
|
|
57
88
|
}
|
|
58
89
|
});
|
|
59
90
|
|
|
91
|
+
|
|
60
92
|
// API: 获取 Target 将要扫描的文件列表(不调用 AI)。支持 body.target 或 body.targetName(按名称查 target)
|
|
61
93
|
app.post('/api/spm/target-files', async (req, res) => {
|
|
62
94
|
try {
|
package/bin/dashboard-server.js
CHANGED
|
@@ -130,12 +130,25 @@ async function launch(projectRoot, port = 3000, options = {}) {
|
|
|
130
130
|
|
|
131
131
|
const specRepository = new SpecRepositoryV2(projectRoot);
|
|
132
132
|
const forceBuild = options.forceBuild === true || process.env.ASD_UI_BUILD === '1' || process.env.ASD_UI_REBUILD === '1';
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
|
|
134
|
+
// 1. 在后台启动 Watcher(支持调试模式)
|
|
135
|
+
const isDebugMode = process.env.ASD_DEBUG_WATCH === '1' || process.env.ASD_DEBUG_SEARCH === '1';
|
|
136
|
+
if (isDebugMode) {
|
|
137
|
+
console.log(`[Dashboard] 正在启动项目监听器(调试模式)...`);
|
|
138
|
+
} else {
|
|
139
|
+
console.log(`[Dashboard] 正在后台启动项目监听器...`);
|
|
140
|
+
}
|
|
141
|
+
|
|
135
142
|
const rootSpecPath = Paths.getProjectSpecPath(projectRoot);
|
|
136
143
|
try {
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
// 调试模式下不启用 quiet,以便看到详细日志
|
|
145
|
+
watch.watchFileChange(rootSpecPath, projectRoot, { quiet: !isDebugMode });
|
|
146
|
+
if (isDebugMode) {
|
|
147
|
+
console.log(`[Dashboard] ✅ 监听器已就绪(调试模式已启用)`);
|
|
148
|
+
console.log(`[Dashboard] 💡 在 Xcode 中使用 // as:s 将触发搜索`);
|
|
149
|
+
} else {
|
|
150
|
+
console.log(`[Dashboard] ✅ 监听器已就绪`);
|
|
151
|
+
}
|
|
139
152
|
} catch (err) {
|
|
140
153
|
console.error(`[Dashboard] ❌ 监听器启动失败: ${err.message}`);
|
|
141
154
|
}
|
package/checksums.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"bin/asd-cli.js": "
|
|
3
|
-
"bin/dashboard-server.js": "
|
|
4
|
-
"lib/writeGuard.js": "
|
|
5
|
-
"lib/rateLimit.js": "
|
|
2
|
+
"bin/asd-cli.js": "1e62f7b8792dbb0919deaa1db2b2d5e1282f535b220508685d8caff9a79e5d21",
|
|
3
|
+
"bin/dashboard-server.js": "6bcbfffae8fbdba8472653f934177077e8c4e25d95d8c5ae3080698239c3a513",
|
|
4
|
+
"lib/writeGuard.js": "2ec5b4fa958b013702ba4023b1d1d51d93db3e0be1921e814ef2abb032b45775",
|
|
5
|
+
"lib/rateLimit.js": "ad55cc15b3dc01efa6847c405db9cc7cfab1719a4ca9bc2fb183d4d9c8f51baf",
|
|
6
6
|
"lib/services/context/ContextService.js": "e1e87506b1c66da274a068ca078491fdf09e1eea4ffbc22c3409f6f0f2e40a8e",
|
|
7
7
|
"lib/application/services/ContextServiceV2.js": "d42a572e8bea9e002714f23935c6370ed1975904b8ecbc1e18ffad98f99bb79a",
|
|
8
8
|
"lib/context/adapters/MilvusAdapter.js": "b7c11e95c64f8b57f9edf2380b1038e89438fbe2d87d3d831aee9dbeae2afcbd",
|