autosnippet 3.3.6 → 3.3.7
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/dashboard/dist/assets/icons-FHns2ypa.js +1 -0
- package/dashboard/dist/assets/index-BRJv5Y3r.js +135 -0
- package/dashboard/dist/assets/index-DzoB7kxK.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/cli.js +1 -0
- package/dist/lib/agent/AgentRuntime.d.ts +2 -2
- package/dist/lib/agent/AgentRuntime.js +26 -18
- package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
- package/dist/lib/agent/forced-summary.js +7 -2
- package/dist/lib/cli/AiScanService.js +4 -4
- package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
- package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +30 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1305 -0
- package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
- package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
- package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
- package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
- package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
- package/dist/lib/core/discovery/index.d.ts +2 -0
- package/dist/lib/core/discovery/index.js +4 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
- package/dist/lib/external/ai/AiProvider.d.ts +12 -0
- package/dist/lib/external/ai/AiProvider.js +24 -0
- package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
- package/dist/lib/external/ai/AiProviderManager.js +193 -0
- package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
- package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
- package/dist/lib/external/ai/providers/MockProvider.js +290 -14
- package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
- package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
- package/dist/lib/external/lark/LarkTransport.js +10 -2
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +16 -8
- package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
- package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +2 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
- package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +2 -1
- package/dist/lib/external/mcp/handlers/evolve-external.js +5 -2
- package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
- package/dist/lib/http/routes/ai.js +111 -30
- package/dist/lib/http/routes/candidates.js +11 -4
- package/dist/lib/http/routes/commands.js +10 -1
- package/dist/lib/http/routes/health.js +11 -0
- package/dist/lib/http/routes/modules.js +27 -0
- package/dist/lib/http/routes/recipes.js +7 -0
- package/dist/lib/http/utils/routeHelpers.js +2 -1
- package/dist/lib/injection/ServiceContainer.d.ts +6 -5
- package/dist/lib/injection/ServiceContainer.js +11 -27
- package/dist/lib/injection/ServiceMap.d.ts +2 -0
- package/dist/lib/injection/modules/AiModule.d.ts +6 -9
- package/dist/lib/injection/modules/AiModule.js +82 -39
- package/dist/lib/injection/modules/PanoramaModule.js +1 -1
- package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
- package/dist/lib/service/cleanup/CleanupService.js +284 -37
- package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +6 -0
- package/dist/lib/service/knowledge/CodeEntityGraph.js +16 -0
- package/dist/lib/service/knowledge/KnowledgeService.js +23 -10
- package/dist/lib/service/module/ModuleService.js +10 -19
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +10 -1
- package/dist/lib/service/panorama/CouplingAnalyzer.js +44 -2
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +1 -1
- package/dist/lib/service/panorama/DimensionAnalyzer.js +31 -17
- package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
- package/dist/lib/service/panorama/LayerInferrer.js +118 -1
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +9 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.js +58 -2
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +6 -2
- package/dist/lib/service/panorama/PanoramaAggregator.js +84 -6
- package/dist/lib/service/panorama/PanoramaScanner.js +28 -0
- package/dist/lib/service/panorama/PanoramaService.js +10 -5
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +38 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +2 -0
- package/dist/lib/service/panorama/RoleRefiner.js +41 -0
- package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
- package/dist/lib/service/panorama/TechStackProfiler.js +191 -0
- package/dist/lib/service/skills/SignalCollector.d.ts +1 -0
- package/dist/lib/service/skills/SignalCollector.js +6 -5
- package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
- package/dist/lib/service/vector/ContextualEnricher.js +4 -0
- package/dist/lib/shared/LanguageService.js +3 -0
- package/dist/lib/shared/developer-identity.d.ts +18 -0
- package/dist/lib/shared/developer-identity.js +62 -0
- package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
- package/dist/lib/shared/schemas/http-requests.js +9 -6
- package/dist/lib/types/knowledge-wire.d.ts +1 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
- package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
- package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConfigWatcher — 构建配置文件热更新监听器
|
|
3
|
+
*
|
|
4
|
+
* 监听自研构建系统的配置文件变更,执行增量重解析并通知下游服务。
|
|
5
|
+
*
|
|
6
|
+
* 核心策略:
|
|
7
|
+
* - debounce 3s(配置文件常有连续保存)
|
|
8
|
+
* - MD5 hash 差量检测避免无效重解析
|
|
9
|
+
* - 60s 最大频率保护(防 git checkout 等批量变更风暴)
|
|
10
|
+
* - 增量解析:单文件变更只影响对应模块
|
|
11
|
+
* - 通过 SignalBus 触发 PanoramaService 缓存失效
|
|
12
|
+
* - 通过 RealtimeService 推送 Dashboard WebSocket 事件
|
|
13
|
+
*
|
|
14
|
+
* @module ConfigWatcher
|
|
15
|
+
*/
|
|
16
|
+
import { createHash } from 'node:crypto';
|
|
17
|
+
import { watch } from 'node:fs';
|
|
18
|
+
import { glob, readFile } from 'node:fs/promises';
|
|
19
|
+
import { join, relative } from 'node:path';
|
|
20
|
+
import { shutdown } from '#shared/shutdown.js';
|
|
21
|
+
/* ═══ Types ═══════════════════════════════════════════════ */
|
|
22
|
+
/** 配置文件系统类型 → 需要监听的文件 glob 列表 */
|
|
23
|
+
const WATCH_PATTERNS = {
|
|
24
|
+
easybox: ['Boxfile', 'Boxfile.local', 'Boxfile.overlay', 'LocalModule/**/*.boxspec'],
|
|
25
|
+
tuist: ['Project.swift', 'Tuist/**/*.swift'],
|
|
26
|
+
xcodegen: ['project.yml', 'project.yaml'],
|
|
27
|
+
};
|
|
28
|
+
/* ═══ ConfigWatcher ═══════════════════════════════════════ */
|
|
29
|
+
export class ConfigWatcher {
|
|
30
|
+
#projectRoot;
|
|
31
|
+
#systemId;
|
|
32
|
+
#signalBus;
|
|
33
|
+
#onChange;
|
|
34
|
+
#debounceMs;
|
|
35
|
+
#fullRebuildIntervalMs;
|
|
36
|
+
/** fs.watch 实例列表 */
|
|
37
|
+
#watchers = [];
|
|
38
|
+
/** 已知文件 → hash 映射 */
|
|
39
|
+
#fileHashes = new Map();
|
|
40
|
+
/** debounce 定时器 */
|
|
41
|
+
#debounceTimer = null;
|
|
42
|
+
/** 待处理的变更文件路径(debounce 窗口内合并) */
|
|
43
|
+
#pendingChanges = new Set();
|
|
44
|
+
/** 上次全量重建时间 */
|
|
45
|
+
#lastFullRebuild = 0;
|
|
46
|
+
/** 是否已关闭 */
|
|
47
|
+
#disposed = false;
|
|
48
|
+
constructor(options) {
|
|
49
|
+
this.#projectRoot = options.projectRoot;
|
|
50
|
+
this.#systemId = options.systemId;
|
|
51
|
+
this.#signalBus = options.signalBus ?? null;
|
|
52
|
+
this.#onChange = options.onChange ?? null;
|
|
53
|
+
this.#debounceMs = options.debounceMs ?? 3000;
|
|
54
|
+
this.#fullRebuildIntervalMs = options.fullRebuildIntervalMs ?? 60_000;
|
|
55
|
+
}
|
|
56
|
+
/* ─── Public API ────────────────────────────────── */
|
|
57
|
+
/**
|
|
58
|
+
* 启动文件监听。异步解析初始文件 hash 并注册 fs.watch。
|
|
59
|
+
* 自动注册 shutdown hook 以清理资源。
|
|
60
|
+
*/
|
|
61
|
+
async start() {
|
|
62
|
+
if (this.#disposed) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const patterns = WATCH_PATTERNS[this.#systemId] ?? [];
|
|
66
|
+
if (patterns.length === 0) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// 1. 遍历 glob patterns 收集初始文件
|
|
70
|
+
const resolvedFiles = await this.#resolveWatchFiles(patterns);
|
|
71
|
+
// 2. 计算初始 hash
|
|
72
|
+
for (const file of resolvedFiles) {
|
|
73
|
+
const absPath = join(this.#projectRoot, file.relativePath);
|
|
74
|
+
try {
|
|
75
|
+
const content = await readFile(absPath, 'utf-8');
|
|
76
|
+
file.hash = computeHash(content);
|
|
77
|
+
this.#fileHashes.set(file.relativePath, file);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* 文件可能已删除,跳过 */
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// 3. 对每个 pattern 设置 fs.watch(目录级)
|
|
84
|
+
this.#setupWatchers(patterns);
|
|
85
|
+
// 4. 注册 shutdown hook
|
|
86
|
+
shutdown.register(() => this.dispose(), `ConfigWatcher(${this.#systemId})`);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 停止监听,释放所有 fs.watch 资源
|
|
90
|
+
*/
|
|
91
|
+
dispose() {
|
|
92
|
+
if (this.#disposed) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this.#disposed = true;
|
|
96
|
+
if (this.#debounceTimer) {
|
|
97
|
+
clearTimeout(this.#debounceTimer);
|
|
98
|
+
this.#debounceTimer = null;
|
|
99
|
+
}
|
|
100
|
+
for (const watcher of this.#watchers) {
|
|
101
|
+
try {
|
|
102
|
+
watcher.close();
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* 忽略关闭错误 */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.#watchers.length = 0;
|
|
109
|
+
this.#pendingChanges.clear();
|
|
110
|
+
this.#fileHashes.clear();
|
|
111
|
+
}
|
|
112
|
+
/** 是否正在监听 */
|
|
113
|
+
get active() {
|
|
114
|
+
return !this.#disposed && this.#watchers.length > 0;
|
|
115
|
+
}
|
|
116
|
+
/** 监听的文件数 */
|
|
117
|
+
get watchedFileCount() {
|
|
118
|
+
return this.#fileHashes.size;
|
|
119
|
+
}
|
|
120
|
+
/* ─── Internal ──────────────────────────────────── */
|
|
121
|
+
/**
|
|
122
|
+
* 解析 glob patterns → WatchedFile 列表
|
|
123
|
+
*/
|
|
124
|
+
async #resolveWatchFiles(patterns) {
|
|
125
|
+
const files = [];
|
|
126
|
+
for (const pattern of patterns) {
|
|
127
|
+
const scope = inferChangeScope(pattern, this.#systemId);
|
|
128
|
+
try {
|
|
129
|
+
// Node 22 has native glob in fs/promises
|
|
130
|
+
const matches = glob(pattern, { cwd: this.#projectRoot });
|
|
131
|
+
for await (const match of matches) {
|
|
132
|
+
files.push({
|
|
133
|
+
relativePath: match,
|
|
134
|
+
hash: '',
|
|
135
|
+
scope,
|
|
136
|
+
moduleName: scope === 'module' ? extractModuleName(match, this.#systemId) : undefined,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
/* pattern 不匹配或目录不存在 */
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return files;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 设置 fs.watch 监听器
|
|
148
|
+
*/
|
|
149
|
+
#setupWatchers(patterns) {
|
|
150
|
+
// 收集要监听的目录(去重)
|
|
151
|
+
const watchDirs = new Set();
|
|
152
|
+
watchDirs.add(this.#projectRoot); // 根目录(Boxfile, project.yml 等)
|
|
153
|
+
for (const pattern of patterns) {
|
|
154
|
+
// 对含 ** 的 pattern,监听其父目录
|
|
155
|
+
const slashIdx = pattern.indexOf('/');
|
|
156
|
+
if (slashIdx > 0) {
|
|
157
|
+
const dir = pattern.substring(0, slashIdx);
|
|
158
|
+
const absDir = join(this.#projectRoot, dir);
|
|
159
|
+
watchDirs.add(absDir);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
for (const dir of watchDirs) {
|
|
163
|
+
try {
|
|
164
|
+
const watcher = watch(dir, { recursive: true }, (_eventType, filename) => {
|
|
165
|
+
if (!filename || this.#disposed) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const relPath = dir === this.#projectRoot ? filename : join(relative(this.#projectRoot, dir), filename);
|
|
169
|
+
// 过滤非配置文件
|
|
170
|
+
if (this.#isRelevantFile(relPath, patterns)) {
|
|
171
|
+
this.#scheduleCheck(relPath);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
this.#watchers.push(watcher);
|
|
175
|
+
// 处理 watcher 错误(macOS 上目录被删除等)
|
|
176
|
+
watcher.on('error', () => {
|
|
177
|
+
/* 静默忽略,下次变更无法检测可通过手动 rescan 恢复 */
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
/* 目录不存在或权限不足 */
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* 检查文件路径是否匹配 watch patterns
|
|
187
|
+
*/
|
|
188
|
+
#isRelevantFile(relPath, patterns) {
|
|
189
|
+
for (const pattern of patterns) {
|
|
190
|
+
// 简单匹配:精确文件名 / 扩展名 / 子目录包含
|
|
191
|
+
if (!pattern.includes('*') && !pattern.includes('/')) {
|
|
192
|
+
// 精确文件名匹配
|
|
193
|
+
if (relPath === pattern) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else if (pattern.includes('**')) {
|
|
198
|
+
// glob 通配 — 提取目录前缀和扩展名
|
|
199
|
+
const parts = pattern.split('/');
|
|
200
|
+
const dirPrefix = parts[0];
|
|
201
|
+
const extMatch = pattern.match(/\*(\.\w+)$/);
|
|
202
|
+
if (relPath.startsWith(`${dirPrefix}/`) && extMatch && relPath.endsWith(extMatch[1])) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* debounce 调度:合并多次变更后统一处理
|
|
211
|
+
*/
|
|
212
|
+
#scheduleCheck(relPath) {
|
|
213
|
+
this.#pendingChanges.add(relPath);
|
|
214
|
+
if (this.#debounceTimer) {
|
|
215
|
+
clearTimeout(this.#debounceTimer);
|
|
216
|
+
}
|
|
217
|
+
this.#debounceTimer = setTimeout(() => {
|
|
218
|
+
this.#debounceTimer = null;
|
|
219
|
+
void this.#processChanges();
|
|
220
|
+
}, this.#debounceMs);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* 处理 debounce 窗口内累积的变更
|
|
224
|
+
*/
|
|
225
|
+
async #processChanges() {
|
|
226
|
+
if (this.#disposed || this.#pendingChanges.size === 0) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
// 频率保护:60s 内最多一次全量重建
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
const hasFullScope = [...this.#pendingChanges].some((p) => {
|
|
232
|
+
const watched = this.#fileHashes.get(p);
|
|
233
|
+
return !watched || watched.scope === 'full';
|
|
234
|
+
});
|
|
235
|
+
if (hasFullScope && now - this.#lastFullRebuild < this.#fullRebuildIntervalMs) {
|
|
236
|
+
// 跳过此轮,等待下次
|
|
237
|
+
this.#pendingChanges.clear();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const changedPaths = [...this.#pendingChanges];
|
|
241
|
+
this.#pendingChanges.clear();
|
|
242
|
+
// 逐个检查 hash 变化
|
|
243
|
+
const actualChanges = [];
|
|
244
|
+
for (const relPath of changedPaths) {
|
|
245
|
+
const absPath = join(this.#projectRoot, relPath);
|
|
246
|
+
try {
|
|
247
|
+
const content = await readFile(absPath, 'utf-8');
|
|
248
|
+
const newHash = computeHash(content);
|
|
249
|
+
const existing = this.#fileHashes.get(relPath);
|
|
250
|
+
if (existing && existing.hash === newHash) {
|
|
251
|
+
continue; // hash 未变,跳过
|
|
252
|
+
}
|
|
253
|
+
// 更新 hash
|
|
254
|
+
const scope = existing?.scope ?? inferChangeScope(relPath, this.#systemId);
|
|
255
|
+
const moduleName = scope === 'module'
|
|
256
|
+
? (existing?.moduleName ?? extractModuleName(relPath, this.#systemId))
|
|
257
|
+
: undefined;
|
|
258
|
+
this.#fileHashes.set(relPath, { relativePath: relPath, hash: newHash, scope, moduleName });
|
|
259
|
+
actualChanges.push({ path: relPath, scope, moduleName });
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// 文件被删除 — 也算变更
|
|
263
|
+
if (this.#fileHashes.has(relPath)) {
|
|
264
|
+
const existing = this.#fileHashes.get(relPath);
|
|
265
|
+
this.#fileHashes.delete(relPath);
|
|
266
|
+
actualChanges.push({
|
|
267
|
+
path: relPath,
|
|
268
|
+
scope: existing.scope,
|
|
269
|
+
moduleName: existing.moduleName,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (actualChanges.length === 0) {
|
|
275
|
+
return; // 所有文件 hash 未变
|
|
276
|
+
}
|
|
277
|
+
if (hasFullScope) {
|
|
278
|
+
this.#lastFullRebuild = now;
|
|
279
|
+
}
|
|
280
|
+
// 发射信号 → PanoramaService 缓存失效
|
|
281
|
+
if (this.#signalBus) {
|
|
282
|
+
this.#signalBus.send('lifecycle', 'ConfigWatcher', 1.0, {
|
|
283
|
+
metadata: {
|
|
284
|
+
event: 'config_changed',
|
|
285
|
+
systemId: this.#systemId,
|
|
286
|
+
changedCount: actualChanges.length,
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// 回调通知(RealtimeService 等)
|
|
291
|
+
const event = {
|
|
292
|
+
changedFiles: actualChanges,
|
|
293
|
+
systemId: this.#systemId,
|
|
294
|
+
timestamp: now,
|
|
295
|
+
};
|
|
296
|
+
this.#onChange?.(event);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/* ═══ Helpers ═════════════════════════════════════════════ */
|
|
300
|
+
function computeHash(content) {
|
|
301
|
+
return createHash('md5').update(content).digest('hex');
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* 根据文件 pattern 推断变更影响范围
|
|
305
|
+
*/
|
|
306
|
+
function inferChangeScope(pattern, systemId) {
|
|
307
|
+
if (systemId === 'easybox') {
|
|
308
|
+
if (pattern.includes('.boxspec')) {
|
|
309
|
+
return 'module';
|
|
310
|
+
}
|
|
311
|
+
if (pattern.includes('.local') || pattern.includes('.overlay')) {
|
|
312
|
+
return 'overlay';
|
|
313
|
+
}
|
|
314
|
+
return 'full';
|
|
315
|
+
}
|
|
316
|
+
if (systemId === 'tuist') {
|
|
317
|
+
if (pattern.includes('Tuist/')) {
|
|
318
|
+
return 'full';
|
|
319
|
+
}
|
|
320
|
+
return 'full';
|
|
321
|
+
}
|
|
322
|
+
// xcodegen / 其他
|
|
323
|
+
return 'full';
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 从文件路径提取模块名
|
|
327
|
+
* e.g. "LocalModule/BDPictures/BDPictures.boxspec" → "BDPictures"
|
|
328
|
+
*/
|
|
329
|
+
function extractModuleName(relPath, _systemId) {
|
|
330
|
+
// 通用:取 spec 文件所在目录名
|
|
331
|
+
const parts = relPath.split('/');
|
|
332
|
+
if (parts.length >= 2) {
|
|
333
|
+
return parts[parts.length - 2];
|
|
334
|
+
}
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module CustomConfigDiscoverer
|
|
3
|
+
* @description 自研配置文件发现器 — 识别使用非标准/自研构建系统的项目
|
|
4
|
+
*
|
|
5
|
+
* 两级检测策略:
|
|
6
|
+
* Level 1: 已知自研工具指纹匹配 (confidence 0.70-0.80)
|
|
7
|
+
* Level 2: 启发式目录结构探测 (confidence 0.50-0.65)
|
|
8
|
+
*
|
|
9
|
+
* 当前支持:
|
|
10
|
+
* - Baidu EasyBox (Boxfile + *.boxspec)
|
|
11
|
+
* - Tuist (Project.swift)
|
|
12
|
+
* - XcodeGen (project.yml)
|
|
13
|
+
*
|
|
14
|
+
* 设计文档: docs/copilot/custom-config-discoverer-design.md
|
|
15
|
+
*/
|
|
16
|
+
import { type DependencyGraph, type DiscoveredFile, type DiscoveredTarget, ProjectDiscoverer } from './ProjectDiscoverer.js';
|
|
17
|
+
export declare class CustomConfigDiscoverer extends ProjectDiscoverer {
|
|
18
|
+
#private;
|
|
19
|
+
get id(): string;
|
|
20
|
+
get displayName(): string;
|
|
21
|
+
detect(projectRoot: string): Promise<{
|
|
22
|
+
match: boolean;
|
|
23
|
+
confidence: number;
|
|
24
|
+
reason: string;
|
|
25
|
+
}>;
|
|
26
|
+
load(projectRoot: string): Promise<void>;
|
|
27
|
+
listTargets(): Promise<DiscoveredTarget[]>;
|
|
28
|
+
getTargetFiles(target: DiscoveredTarget): Promise<DiscoveredFile[]>;
|
|
29
|
+
getDependencyGraph(): Promise<DependencyGraph>;
|
|
30
|
+
}
|