autosnippet 3.0.13 → 3.1.1
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/bin/api-server.js +2 -0
- package/bin/cli.js +24 -19
- package/config/default.json +1 -1
- package/lib/bootstrap.js +4 -4
- package/lib/cli/SetupService.js +29 -29
- package/lib/cli/UpgradeService.js +3 -2
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/ast/ensure-grammars.js +1 -1
- package/lib/core/ast/index.js +62 -11
- package/lib/core/ast/lang-dart.js +27 -21
- package/lib/core/ast/lang-go.js +6 -20
- package/lib/core/ast/lang-rust.js +53 -28
- package/lib/core/ast/parser-init.js +9 -5
- package/lib/core/discovery/DartDiscoverer.js +4 -10
- package/lib/core/discovery/GoDiscoverer.js +45 -25
- package/lib/core/discovery/NodeDiscoverer.js +1 -3
- package/lib/core/discovery/PythonDiscoverer.js +7 -1
- package/lib/core/discovery/RustDiscoverer.js +111 -38
- package/lib/core/discovery/index.js +2 -2
- package/lib/core/enhancement/django-enhancement.js +10 -4
- package/lib/core/enhancement/fastapi-enhancement.js +16 -9
- package/lib/core/enhancement/go-grpc-enhancement.js +2 -1
- package/lib/core/enhancement/go-web-enhancement.js +3 -6
- package/lib/core/enhancement/ml-enhancement.js +6 -3
- package/lib/core/enhancement/nextjs-enhancement.js +17 -7
- package/lib/core/enhancement/node-server-enhancement.js +4 -2
- package/lib/core/enhancement/react-enhancement.js +6 -3
- package/lib/core/enhancement/rust-tokio-enhancement.js +6 -2
- package/lib/core/enhancement/rust-web-enhancement.js +13 -7
- package/lib/core/enhancement/vue-enhancement.js +10 -5
- package/lib/external/ai/AiFactory.js +3 -1
- package/lib/external/ai/AiProvider.js +3 -1
- package/lib/external/mcp/McpServer.js +2 -0
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +1 -2
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +55 -26
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/refine.js +3 -1
- package/lib/external/mcp/handlers/bootstrap.js +4 -10
- package/lib/external/mcp/handlers/browse.js +6 -2
- package/lib/external/mcp/handlers/guard.js +6 -2
- package/lib/external/mcp/handlers/skill.js +6 -2
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/routes/candidates.js +3 -1
- package/lib/http/routes/extract.js +4 -5
- package/lib/http/routes/guardRules.js +1 -1
- package/lib/http/routes/modules.js +9 -3
- package/lib/http/routes/skills.js +54 -6
- package/lib/http/routes/violations.js +4 -3
- package/lib/infrastructure/external/ClipboardManager.js +24 -7
- package/lib/infrastructure/external/NativeUi.js +3 -1
- package/lib/infrastructure/external/OpenBrowser.js +1 -0
- package/lib/infrastructure/external/XcodeAutomation.js +5 -5
- package/lib/infrastructure/vector/IndexingPipeline.js +14 -5
- package/lib/injection/ServiceContainer.js +34 -11
- package/lib/platform/ios/index.js +20 -25
- package/lib/platform/ios/routes/spm.js +6 -3
- package/lib/platform/ios/snippet/PlaceholderConverter.js +6 -2
- package/lib/platform/ios/snippet/XcodeCodec.js +4 -2
- package/lib/platform/ios/spm/SpmDiscoverer.js +1 -1
- package/lib/platform/ios/spm/SpmService.js +3 -1
- package/lib/platform/ios/xcode/XcodeIntegration.js +10 -12
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +6 -1
- package/lib/service/automation/FileWatcher.js +1 -3
- package/lib/service/automation/handlers/CreateHandler.js +3 -5
- package/lib/service/automation/handlers/GuardHandler.js +11 -32
- package/lib/service/automation/handlers/SearchHandler.js +9 -9
- package/lib/service/chat/CandidateGuardrail.js +11 -6
- package/lib/service/chat/ChatAgent.js +31 -22
- package/lib/service/chat/HandoffProtocol.js +5 -2
- package/lib/service/chat/tools/composite.js +3 -2
- package/lib/service/chat/tools/index.js +60 -71
- package/lib/service/chat/tools/infrastructure.js +9 -4
- package/lib/service/chat/tools/lifecycle.js +22 -5
- package/lib/service/chat/tools/project-access.js +5 -9
- package/lib/service/chat/tools.js +1 -2
- package/lib/service/cursor/AgentInstructionsGenerator.js +33 -15
- package/lib/service/cursor/CursorDeliveryPipeline.js +2 -1
- package/lib/service/cursor/KnowledgeCompressor.js +16 -7
- package/lib/service/guard/ComplianceReporter.js +5 -2
- package/lib/service/guard/GuardCheckEngine.js +53 -26
- package/lib/service/guard/GuardCodeChecks.js +217 -188
- package/lib/service/guard/GuardCrossFileChecks.js +203 -184
- package/lib/service/guard/GuardPatternUtils.js +17 -10
- package/lib/service/module/ModuleService.js +180 -56
- package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
- package/lib/service/snippet/SnippetFactory.js +3 -3
- package/lib/service/snippet/SnippetInstaller.js +35 -11
- package/lib/service/snippet/codecs/VSCodeCodec.js +2 -2
- package/lib/service/wiki/WikiGenerator.js +67 -40
- package/lib/service/wiki/WikiRenderers.js +105 -80
- package/lib/service/wiki/WikiUtils.js +217 -80
- package/lib/shared/LanguageService.js +111 -53
- package/lib/shared/PathGuard.js +0 -8
- package/package.json +3 -9
- package/scripts/bench-real-projects.mjs +29 -29
- package/scripts/generate-recipe-drafts.js +17 -27
- package/scripts/init-snippets.js +43 -24
- package/scripts/install-vscode-copilot.js +3 -19
- package/scripts/setup-mcp-config.js +0 -4
|
@@ -127,7 +127,9 @@ class RustWebEnhancement extends EnhancementPack {
|
|
|
127
127
|
|
|
128
128
|
// ── Structs used as State / AppData ──
|
|
129
129
|
for (const cls of astSummary.classes || []) {
|
|
130
|
-
if (cls.kind !== 'struct')
|
|
130
|
+
if (cls.kind !== 'struct') {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
131
133
|
const nameLower = cls.name.toLowerCase();
|
|
132
134
|
if (
|
|
133
135
|
nameLower.includes('state') ||
|
|
@@ -164,7 +166,9 @@ class RustWebEnhancement extends EnhancementPack {
|
|
|
164
166
|
|
|
165
167
|
// ── Middleware / Layer structs ──
|
|
166
168
|
for (const cls of astSummary.classes || []) {
|
|
167
|
-
if (cls.kind !== 'struct')
|
|
169
|
+
if (cls.kind !== 'struct') {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
168
172
|
const nameLower = cls.name.toLowerCase();
|
|
169
173
|
if (
|
|
170
174
|
nameLower.includes('middleware') ||
|
|
@@ -185,12 +189,12 @@ class RustWebEnhancement extends EnhancementPack {
|
|
|
185
189
|
|
|
186
190
|
// ── Extractor structs (used as handler params) ──
|
|
187
191
|
for (const cls of astSummary.classes || []) {
|
|
188
|
-
if (cls.kind !== 'struct')
|
|
192
|
+
if (cls.kind !== 'struct') {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
189
195
|
// Derive-heavy DTOs used as Json<T>, Query<T>, Path<T>
|
|
190
196
|
if (cls.derives && cls.derives.length >= 2) {
|
|
191
|
-
const hasSerdeDerive = cls.derives.some(
|
|
192
|
-
(d) => d === 'Deserialize' || d === 'Serialize'
|
|
193
|
-
);
|
|
197
|
+
const hasSerdeDerive = cls.derives.some((d) => d === 'Deserialize' || d === 'Serialize');
|
|
194
198
|
if (hasSerdeDerive) {
|
|
195
199
|
const nameLower = cls.name.toLowerCase();
|
|
196
200
|
if (
|
|
@@ -215,7 +219,9 @@ class RustWebEnhancement extends EnhancementPack {
|
|
|
215
219
|
|
|
216
220
|
// ── Response structs ──
|
|
217
221
|
for (const cls of astSummary.classes || []) {
|
|
218
|
-
if (cls.kind !== 'struct')
|
|
222
|
+
if (cls.kind !== 'struct') {
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
219
225
|
const nameLower = cls.name.toLowerCase();
|
|
220
226
|
if (
|
|
221
227
|
nameLower.includes('response') ||
|
|
@@ -29,7 +29,8 @@ class VueEnhancement extends EnhancementPack {
|
|
|
29
29
|
{
|
|
30
30
|
id: 'composable-scan',
|
|
31
31
|
label: 'Composable 函数分析',
|
|
32
|
-
guide:
|
|
32
|
+
guide:
|
|
33
|
+
'Composable 函数(useXxx)+ 内部 ref/computed/watch 调用、Composable 组合链、provide/inject 依赖注入模式',
|
|
33
34
|
knowledgeTypes: ['code-pattern'],
|
|
34
35
|
skillWorthy: true,
|
|
35
36
|
dualOutput: true,
|
|
@@ -97,7 +98,8 @@ class VueEnhancement extends EnhancementPack {
|
|
|
97
98
|
severity: 'warning',
|
|
98
99
|
languages: ['typescript', 'javascript'],
|
|
99
100
|
pattern: /(?:const|let)\s*\{[^}]+\}\s*=\s*(?:reactive|toRefs)\s*\(/,
|
|
100
|
-
message:
|
|
101
|
+
message:
|
|
102
|
+
'解构 reactive() 对象会失去响应性,使用 toRefs() 或保持引用访问。Vue 3.5 的 Reactive Props Destructure 除外',
|
|
101
103
|
},
|
|
102
104
|
{
|
|
103
105
|
ruleId: 'vue-computed-side-effect',
|
|
@@ -105,8 +107,10 @@ class VueEnhancement extends EnhancementPack {
|
|
|
105
107
|
dimension: 'file',
|
|
106
108
|
severity: 'warning',
|
|
107
109
|
languages: ['typescript', 'javascript'],
|
|
108
|
-
pattern:
|
|
109
|
-
|
|
110
|
+
pattern:
|
|
111
|
+
/computed\s*\(\s*(?:\(\)|function\s*\(\))\s*(?:=>)?\s*\{[^}]*(?:fetch|axios|console\.log|\.value\s*=)/,
|
|
112
|
+
message:
|
|
113
|
+
'computed 中不应有副作用 (网络请求、修改 ref、console.log) — 使用 watch/watchEffect 代替',
|
|
110
114
|
},
|
|
111
115
|
{
|
|
112
116
|
ruleId: 'vue-v-for-with-v-if',
|
|
@@ -115,7 +119,8 @@ class VueEnhancement extends EnhancementPack {
|
|
|
115
119
|
severity: 'warning',
|
|
116
120
|
languages: ['typescript', 'javascript'],
|
|
117
121
|
pattern: /v-for=[\s\S]*?v-if=/,
|
|
118
|
-
message:
|
|
122
|
+
message:
|
|
123
|
+
'避免在同一元素上同时使用 v-for 和 v-if — v-if 优先级高于 v-for(Vue 3),用 computed 过滤或包裹 <template>',
|
|
119
124
|
},
|
|
120
125
|
{
|
|
121
126
|
ruleId: 'vue-no-ref-in-reactive',
|
|
@@ -114,7 +114,9 @@ export function autoDetectProvider() {
|
|
|
114
114
|
return createProvider({ provider: 'deepseek' });
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
logger.info(
|
|
117
|
+
logger.info(
|
|
118
|
+
'[AiFactory] 未找到任何 AI API Key,AI 功能已跳过。请在 .env 中配置 ASD_GOOGLE_API_KEY 等。'
|
|
119
|
+
);
|
|
118
120
|
return createProvider({ provider: 'mock' });
|
|
119
121
|
}
|
|
120
122
|
|
|
@@ -31,8 +31,7 @@ export const baseDimensions = [
|
|
|
31
31
|
dualOutput: true,
|
|
32
32
|
skillMeta: {
|
|
33
33
|
name: 'project-code-standard',
|
|
34
|
-
description:
|
|
35
|
-
'Project coding standards and naming conventions (auto-generated by bootstrap)',
|
|
34
|
+
description: 'Project coding standards and naming conventions (auto-generated by bootstrap)',
|
|
36
35
|
},
|
|
37
36
|
},
|
|
38
37
|
// ② 代码模式(Candidate)
|
|
@@ -22,7 +22,13 @@ const CHECKPOINT_TTL_MS = 3600_000; // 1小时内有效
|
|
|
22
22
|
* @param {object} result — 维度执行结果
|
|
23
23
|
* @param {object} [digest] — DimensionDigest
|
|
24
24
|
*/
|
|
25
|
-
export async function saveDimensionCheckpoint(
|
|
25
|
+
export async function saveDimensionCheckpoint(
|
|
26
|
+
projectRoot,
|
|
27
|
+
sessionId,
|
|
28
|
+
dimId,
|
|
29
|
+
result,
|
|
30
|
+
digest = null
|
|
31
|
+
) {
|
|
26
32
|
try {
|
|
27
33
|
const checkpointDir = path.join(projectRoot, '.autosnippet', 'bootstrap-checkpoint');
|
|
28
34
|
await fs.mkdir(checkpointDir, { recursive: true });
|
|
@@ -25,14 +25,13 @@ const logger = Logger.getInstance();
|
|
|
25
25
|
*/
|
|
26
26
|
export async function runNoAiFallback(fillContext) {
|
|
27
27
|
const {
|
|
28
|
-
ctx
|
|
28
|
+
// ctx and projectRoot are part of fillContext API but unused in fallback path
|
|
29
29
|
dimensions,
|
|
30
30
|
depGraphData,
|
|
31
31
|
guardAudit,
|
|
32
32
|
langStats,
|
|
33
33
|
primaryLang,
|
|
34
34
|
astProjectSummary,
|
|
35
|
-
projectRoot,
|
|
36
35
|
taskManager,
|
|
37
36
|
sessionId,
|
|
38
37
|
} = fillContext;
|
|
@@ -45,7 +44,7 @@ export async function runNoAiFallback(fillContext) {
|
|
|
45
44
|
const report = { dimensionsProcessed: 0, candidatesCreated: 0, skillsCreated: 0, errors: [] };
|
|
46
45
|
|
|
47
46
|
// ── 收集原始数据 ──
|
|
48
|
-
|
|
47
|
+
const allFiles = fillContext.allFiles || [];
|
|
49
48
|
const targetFileMap = fillContext.targetFileMap || {};
|
|
50
49
|
const allTargets = Object.keys(targetFileMap);
|
|
51
50
|
|
|
@@ -160,7 +159,14 @@ export async function runNoAiFallback(fillContext) {
|
|
|
160
159
|
// 维度构建器
|
|
161
160
|
// ═══════════════════════════════════════════════════════════
|
|
162
161
|
|
|
163
|
-
function _buildProjectProfile({
|
|
162
|
+
function _buildProjectProfile({
|
|
163
|
+
langStats,
|
|
164
|
+
primaryLang,
|
|
165
|
+
depGraphData,
|
|
166
|
+
allTargets,
|
|
167
|
+
allFiles,
|
|
168
|
+
astProjectSummary,
|
|
169
|
+
}) {
|
|
164
170
|
const lines = ['## 项目技术画像', ''];
|
|
165
171
|
|
|
166
172
|
// 语言统计
|
|
@@ -183,9 +189,7 @@ function _buildProjectProfile({ langStats, primaryLang, depGraphData, allTargets
|
|
|
183
189
|
lines.push(`### 模块结构`, '');
|
|
184
190
|
lines.push(`项目包含 **${allTargets.length}** 个模块/Target:`, '');
|
|
185
191
|
for (const t of allTargets.slice(0, 15)) {
|
|
186
|
-
const fileCount = Array.isArray(allFiles)
|
|
187
|
-
? allFiles.filter((f) => f.target === t).length
|
|
188
|
-
: 0;
|
|
192
|
+
const fileCount = Array.isArray(allFiles) ? allFiles.filter((f) => f.target === t).length : 0;
|
|
189
193
|
lines.push(`- \`${t}\` (${fileCount} files)`);
|
|
190
194
|
}
|
|
191
195
|
if (allTargets.length > 15) {
|
|
@@ -207,7 +211,9 @@ function _buildProjectProfile({ langStats, primaryLang, depGraphData, allTargets
|
|
|
207
211
|
lines.push(`- 类/结构体: ${astProjectSummary.classes?.length || 0}`);
|
|
208
212
|
lines.push(`- 协议/接口: ${astProjectSummary.protocols?.length || 0}`);
|
|
209
213
|
lines.push(`- 方法总数: ${m.totalMethods || 0}`);
|
|
210
|
-
if (m.maxNestingDepth)
|
|
214
|
+
if (m.maxNestingDepth) {
|
|
215
|
+
lines.push(`- 最大嵌套深度: ${m.maxNestingDepth}`);
|
|
216
|
+
}
|
|
211
217
|
if (m.complexMethods?.length > 0) {
|
|
212
218
|
lines.push(`- 高复杂度方法: ${m.complexMethods.length}`);
|
|
213
219
|
}
|
|
@@ -218,7 +224,9 @@ function _buildProjectProfile({ langStats, primaryLang, depGraphData, allTargets
|
|
|
218
224
|
}
|
|
219
225
|
|
|
220
226
|
const markdown = lines.join('\n');
|
|
221
|
-
if (markdown.length < 50)
|
|
227
|
+
if (markdown.length < 50) {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
222
230
|
|
|
223
231
|
return _makeCandidate({
|
|
224
232
|
title: `项目技术画像 — ${primaryLang}`,
|
|
@@ -231,7 +239,9 @@ function _buildProjectProfile({ langStats, primaryLang, depGraphData, allTargets
|
|
|
231
239
|
}
|
|
232
240
|
|
|
233
241
|
function _buildArchitecture({ depGraphData, allTargets, targetFileMap, primaryLang }) {
|
|
234
|
-
if (!depGraphData?.edges?.length && allTargets.length < 2)
|
|
242
|
+
if (!depGraphData?.edges?.length && allTargets.length < 2) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
235
245
|
|
|
236
246
|
const lines = ['## 模块架构', ''];
|
|
237
247
|
|
|
@@ -260,8 +270,12 @@ function _buildArchitecture({ depGraphData, allTargets, targetFileMap, primaryLa
|
|
|
260
270
|
for (const e of depGraphData.edges) {
|
|
261
271
|
const from = typeof e.from === 'string' ? e.from : e.source;
|
|
262
272
|
const to = typeof e.to === 'string' ? e.to : e.target;
|
|
263
|
-
if (from)
|
|
264
|
-
|
|
273
|
+
if (from) {
|
|
274
|
+
outDeg[from] = (outDeg[from] || 0) + 1;
|
|
275
|
+
}
|
|
276
|
+
if (to) {
|
|
277
|
+
inDeg[to] = (inDeg[to] || 0) + 1;
|
|
278
|
+
}
|
|
265
279
|
}
|
|
266
280
|
|
|
267
281
|
// 核心模块(被依赖最多)
|
|
@@ -288,7 +302,9 @@ function _buildArchitecture({ depGraphData, allTargets, targetFileMap, primaryLa
|
|
|
288
302
|
}
|
|
289
303
|
|
|
290
304
|
const markdown = lines.join('\n');
|
|
291
|
-
if (markdown.length < 80)
|
|
305
|
+
if (markdown.length < 80) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
292
308
|
|
|
293
309
|
return _makeCandidate({
|
|
294
310
|
title: '模块架构与依赖关系',
|
|
@@ -301,7 +317,9 @@ function _buildArchitecture({ depGraphData, allTargets, targetFileMap, primaryLa
|
|
|
301
317
|
}
|
|
302
318
|
|
|
303
319
|
function _buildCodeStandard({ astProjectSummary, primaryLang, allFiles }) {
|
|
304
|
-
if (!astProjectSummary)
|
|
320
|
+
if (!astProjectSummary) {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
305
323
|
|
|
306
324
|
const lines = ['## 代码规范发现', ''];
|
|
307
325
|
|
|
@@ -310,7 +328,9 @@ function _buildCodeStandard({ astProjectSummary, primaryLang, allFiles }) {
|
|
|
310
328
|
// 从 file 级聚合方法
|
|
311
329
|
if (astProjectSummary.files) {
|
|
312
330
|
for (const f of astProjectSummary.files) {
|
|
313
|
-
if (f.methods)
|
|
331
|
+
if (f.methods) {
|
|
332
|
+
methods.push(...f.methods);
|
|
333
|
+
}
|
|
314
334
|
}
|
|
315
335
|
}
|
|
316
336
|
|
|
@@ -389,23 +409,23 @@ function _buildCodeStandard({ astProjectSummary, primaryLang, allFiles }) {
|
|
|
389
409
|
lines.push(`- 高圈复杂度方法: ${metrics.complexMethods.length} 个`);
|
|
390
410
|
for (const m of metrics.complexMethods.slice(0, 5)) {
|
|
391
411
|
lines.push(
|
|
392
|
-
` - \`${m.className ? m.className
|
|
412
|
+
` - \`${m.className ? `${m.className}.` : ''}${m.name}\` — complexity ${m.complexity}`
|
|
393
413
|
);
|
|
394
414
|
}
|
|
395
415
|
}
|
|
396
416
|
if (metrics.longMethods?.length > 0) {
|
|
397
417
|
lines.push(`- 过长方法: ${metrics.longMethods.length} 个`);
|
|
398
418
|
for (const m of metrics.longMethods.slice(0, 5)) {
|
|
399
|
-
lines.push(
|
|
400
|
-
` - \`${m.className ? m.className + '.' : ''}${m.name}\` — ${m.bodyLines} 行`
|
|
401
|
-
);
|
|
419
|
+
lines.push(` - \`${m.className ? `${m.className}.` : ''}${m.name}\` — ${m.bodyLines} 行`);
|
|
402
420
|
}
|
|
403
421
|
}
|
|
404
422
|
lines.push('');
|
|
405
423
|
}
|
|
406
424
|
|
|
407
425
|
const markdown = lines.join('\n');
|
|
408
|
-
if (markdown.length < 80)
|
|
426
|
+
if (markdown.length < 80) {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
409
429
|
|
|
410
430
|
return _makeCandidate({
|
|
411
431
|
title: '代码规范与命名约定',
|
|
@@ -418,7 +438,9 @@ function _buildCodeStandard({ astProjectSummary, primaryLang, allFiles }) {
|
|
|
418
438
|
}
|
|
419
439
|
|
|
420
440
|
function _buildBestPractice({ guardAudit, primaryLang }) {
|
|
421
|
-
if (!guardAudit?.files?.length)
|
|
441
|
+
if (!guardAudit?.files?.length) {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
422
444
|
|
|
423
445
|
// 聚合所有违规
|
|
424
446
|
const ruleStats = {};
|
|
@@ -438,10 +460,11 @@ function _buildBestPractice({ guardAudit, primaryLang }) {
|
|
|
438
460
|
}
|
|
439
461
|
}
|
|
440
462
|
|
|
441
|
-
const sortedRules = Object.entries(ruleStats)
|
|
442
|
-
.sort(([, a], [, b]) => b.count - a.count);
|
|
463
|
+
const sortedRules = Object.entries(ruleStats).sort(([, a], [, b]) => b.count - a.count);
|
|
443
464
|
|
|
444
|
-
if (sortedRules.length === 0)
|
|
465
|
+
if (sortedRules.length === 0) {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
445
468
|
|
|
446
469
|
const lines = ['## 最佳实践(基于 Guard 审计)', ''];
|
|
447
470
|
lines.push(
|
|
@@ -490,7 +513,11 @@ function _buildAgentGuidelines({ guardAudit, primaryLang, astProjectSummary }) {
|
|
|
490
513
|
const ruleStats = {};
|
|
491
514
|
for (const f of guardAudit.files) {
|
|
492
515
|
for (const v of f.violations || []) {
|
|
493
|
-
ruleStats[v.ruleId] = ruleStats[v.ruleId] || {
|
|
516
|
+
ruleStats[v.ruleId] = ruleStats[v.ruleId] || {
|
|
517
|
+
count: 0,
|
|
518
|
+
message: v.message,
|
|
519
|
+
severity: v.severity,
|
|
520
|
+
};
|
|
494
521
|
ruleStats[v.ruleId].count++;
|
|
495
522
|
}
|
|
496
523
|
}
|
|
@@ -540,7 +567,9 @@ function _buildAgentGuidelines({ guardAudit, primaryLang, astProjectSummary }) {
|
|
|
540
567
|
}
|
|
541
568
|
|
|
542
569
|
const markdown = lines.join('\n');
|
|
543
|
-
if (markdown.length < 100)
|
|
570
|
+
if (markdown.length < 100) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
544
573
|
|
|
545
574
|
return _makeCandidate({
|
|
546
575
|
title: 'Agent 开发注意事项',
|
|
@@ -19,14 +19,14 @@ import { EpisodicConsolidator } from '../../../../../service/chat/EpisodicConsol
|
|
|
19
19
|
import { ProducerAgent } from '../../../../../service/chat/ProducerAgent.js';
|
|
20
20
|
import { ProjectSemanticMemory } from '../../../../../service/chat/ProjectSemanticMemory.js';
|
|
21
21
|
import { WorkingMemory } from '../../../../../service/chat/WorkingMemory.js';
|
|
22
|
+
import { clearCheckpoints, loadCheckpoints, saveDimensionCheckpoint } from './checkpoint.js';
|
|
23
|
+
import { buildTierReflection, DIMENSION_CONFIGS_V3 } from './dimension-configs.js';
|
|
22
24
|
import { DimensionContext, parseDimensionDigest } from './dimension-context.js';
|
|
23
25
|
import { EpisodicMemory } from './EpisodicMemory.js';
|
|
24
26
|
import { IncrementalBootstrap } from './IncrementalBootstrap.js';
|
|
25
27
|
import { runNoAiFallback } from './noAiFallback.js';
|
|
26
28
|
import { ToolResultCache } from './ToolResultCache.js';
|
|
27
29
|
import { TierScheduler } from './tier-scheduler.js';
|
|
28
|
-
import { saveDimensionCheckpoint, loadCheckpoints, clearCheckpoints } from './checkpoint.js';
|
|
29
|
-
import { DIMENSION_CONFIGS_V3, buildTierReflection } from './dimension-configs.js';
|
|
30
30
|
|
|
31
31
|
const logger = Logger.getInstance();
|
|
32
32
|
|
|
@@ -555,7 +555,9 @@ export async function fillDimensionsV3(fillContext) {
|
|
|
555
555
|
`[Bootstrap-v3] Producer "${dimId}": ${producerResult.candidateCount} candidates (${Date.now() - dimStartTime}ms total)`
|
|
556
556
|
);
|
|
557
557
|
} catch (producerErr) {
|
|
558
|
-
logger.error(
|
|
558
|
+
logger.error(
|
|
559
|
+
`[Bootstrap-v3] Producer "${dimId}" failed: ${producerErr.message} — Analyst result preserved for Skill generation`
|
|
560
|
+
);
|
|
559
561
|
candidateResults.errors.push({ dimId, error: `Producer: ${producerErr.message}` });
|
|
560
562
|
}
|
|
561
563
|
}
|
|
@@ -741,8 +743,8 @@ export async function fillDimensionsV3(fillContext) {
|
|
|
741
743
|
continue;
|
|
742
744
|
}
|
|
743
745
|
// 2. 重复行检测(AI 陷入循环输出工具提示等)
|
|
744
|
-
const textLines = analysisText.split('\n').filter(l => l.trim().length > 0);
|
|
745
|
-
const uniqueLines = new Set(textLines.map(l => l.trim()));
|
|
746
|
+
const textLines = analysisText.split('\n').filter((l) => l.trim().length > 0);
|
|
747
|
+
const uniqueLines = new Set(textLines.map((l) => l.trim()));
|
|
746
748
|
const uniqueRatio = textLines.length > 0 ? uniqueLines.size / textLines.length : 1;
|
|
747
749
|
if (textLines.length > 20 && uniqueRatio < 0.3) {
|
|
748
750
|
logger.warn(
|
|
@@ -759,9 +761,7 @@ export async function fillDimensionsV3(fillContext) {
|
|
|
759
761
|
/^[-*•]\s/m.test(analysisText) ||
|
|
760
762
|
/```[\s\S]*?```/.test(analysisText);
|
|
761
763
|
if (!hasStructure && analysisText.length < 500) {
|
|
762
|
-
logger.warn(
|
|
763
|
-
`[Bootstrap-v3] Skill "${dim.id}" skipped — no structured content detected`
|
|
764
|
-
);
|
|
764
|
+
logger.warn(`[Bootstrap-v3] Skill "${dim.id}" skipped — no structured content detected`);
|
|
765
765
|
skillResults.failed++;
|
|
766
766
|
skillResults.errors.push({ dimId: dim.id, error: 'no structured content' });
|
|
767
767
|
continue;
|
|
@@ -74,7 +74,9 @@ export async function bootstrapRefine(ctx, args) {
|
|
|
74
74
|
{ page: 1, pageSize: 200 }
|
|
75
75
|
);
|
|
76
76
|
publishedTitles = (published.items || []).map((e) => e.title).filter(Boolean);
|
|
77
|
-
} catch {
|
|
77
|
+
} catch {
|
|
78
|
+
/* ignore */
|
|
79
|
+
}
|
|
78
80
|
|
|
79
81
|
// 3. 逐条 AI 润色
|
|
80
82
|
const results = [];
|
|
@@ -46,11 +46,13 @@ import { DimensionCopy } from '../../../shared/DimensionCopyRegistry.js';
|
|
|
46
46
|
import { LanguageService } from '../../../shared/LanguageService.js';
|
|
47
47
|
import pathGuard from '../../../shared/PathGuard.js';
|
|
48
48
|
import { envelope } from '../envelope.js';
|
|
49
|
+
import { baseDimensions, resolveActiveDimensions } from './bootstrap/base-dimensions.js';
|
|
49
50
|
import {
|
|
50
51
|
clearCheckpoints,
|
|
51
52
|
clearSnapshots,
|
|
52
53
|
fillDimensionsV3,
|
|
53
54
|
} from './bootstrap/pipeline/orchestrator.js';
|
|
55
|
+
import { bootstrapRefine } from './bootstrap/refine.js';
|
|
54
56
|
// ── Sub-modules ──
|
|
55
57
|
import {
|
|
56
58
|
enhanceDimensions,
|
|
@@ -59,8 +61,6 @@ import {
|
|
|
59
61
|
} from './bootstrap/skills.js';
|
|
60
62
|
import { buildLanguageExtension, detectPrimaryLanguage, inferLang } from './LanguageExtensions.js';
|
|
61
63
|
import { inferFilePriority, inferTargetRole } from './TargetClassifier.js';
|
|
62
|
-
import { baseDimensions, resolveActiveDimensions } from './bootstrap/base-dimensions.js';
|
|
63
|
-
import { bootstrapRefine } from './bootstrap/refine.js';
|
|
64
64
|
|
|
65
65
|
// Re-export for external consumers
|
|
66
66
|
export { loadBootstrapSkills };
|
|
@@ -289,9 +289,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
289
289
|
const enhReg = await initEnhancementRegistry();
|
|
290
290
|
const allPacks = enhReg.all();
|
|
291
291
|
// 找到第一个提供 preprocessFile 的增强包
|
|
292
|
-
const preprocessPack = allPacks.find(
|
|
293
|
-
(p) => typeof p.preprocessFile === 'function'
|
|
294
|
-
);
|
|
292
|
+
const preprocessPack = allPacks.find((p) => typeof p.preprocessFile === 'function');
|
|
295
293
|
if (preprocessPack) {
|
|
296
294
|
sfcPreprocessor = preprocessPack.preprocessFile.bind(preprocessPack);
|
|
297
295
|
}
|
|
@@ -510,11 +508,7 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
510
508
|
const detectedFrameworks = allTargets
|
|
511
509
|
.map((t) => (typeof t === 'object' ? t.framework : null))
|
|
512
510
|
.filter(Boolean);
|
|
513
|
-
const activeDimensions = resolveActiveDimensions(
|
|
514
|
-
baseDimensions,
|
|
515
|
-
primaryLang,
|
|
516
|
-
detectedFrameworks
|
|
517
|
-
);
|
|
511
|
+
const activeDimensions = resolveActiveDimensions(baseDimensions, primaryLang, detectedFrameworks);
|
|
518
512
|
|
|
519
513
|
// ── Enhancement Pack 动态追加维度 + Guard 规则 ─────────────
|
|
520
514
|
const enhancementPackInfo = [];
|
|
@@ -18,7 +18,9 @@ import { envelope } from '../envelope.js';
|
|
|
18
18
|
function _buildActionHint(json) {
|
|
19
19
|
const doText = json.doClause || '';
|
|
20
20
|
const whenText = json.whenClause || '';
|
|
21
|
-
if (!doText && !whenText)
|
|
21
|
+
if (!doText && !whenText) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
22
24
|
return `${whenText ? `${whenText} → ` : ''}${doText}`.replace(/ → $/, '');
|
|
23
25
|
}
|
|
24
26
|
|
|
@@ -26,7 +28,9 @@ function _buildActionHint(json) {
|
|
|
26
28
|
* 只保留非空关系桶,压缩 Relations 输出体积。
|
|
27
29
|
*/
|
|
28
30
|
function _compactRelations(relations) {
|
|
29
|
-
if (!relations)
|
|
31
|
+
if (!relations) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
30
34
|
const compact = {};
|
|
31
35
|
for (const [type, list] of Object.entries(relations)) {
|
|
32
36
|
if (Array.isArray(list) && list.length > 0) {
|
|
@@ -285,7 +285,9 @@ export async function scanProject(ctx, args) {
|
|
|
285
285
|
function _getOrCreateEngine(ctx, GuardCheckEngine) {
|
|
286
286
|
try {
|
|
287
287
|
const engine = ctx.container.get('guardCheckEngine');
|
|
288
|
-
if (engine)
|
|
288
|
+
if (engine) {
|
|
289
|
+
return engine;
|
|
290
|
+
}
|
|
289
291
|
} catch {
|
|
290
292
|
/* DI not registered — fall back to new instance */
|
|
291
293
|
}
|
|
@@ -300,7 +302,9 @@ function _getOrCreateEngine(ctx, GuardCheckEngine) {
|
|
|
300
302
|
*/
|
|
301
303
|
async function _injectEnhancementGuardRules(engine, ctx) {
|
|
302
304
|
// 幂等保护: 已注入则跳过
|
|
303
|
-
if (engine.isEpInjected?.())
|
|
305
|
+
if (engine.isEpInjected?.()) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
304
308
|
try {
|
|
305
309
|
const { initEnhancementRegistry } = await import('../../../core/enhancement/index.js');
|
|
306
310
|
const enhReg = await initEnhancementRegistry();
|
|
@@ -240,12 +240,16 @@ export function loadSkill(_ctx, args) {
|
|
|
240
240
|
fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
|
|
241
241
|
.filter((d) => d.isDirectory())
|
|
242
242
|
.forEach((d) => available.add(d.name));
|
|
243
|
-
} catch {
|
|
243
|
+
} catch {
|
|
244
|
+
/* skip: SKILLS_DIR may not exist */
|
|
245
|
+
}
|
|
244
246
|
try {
|
|
245
247
|
fs.readdirSync(_getProjectSkillsDir(), { withFileTypes: true })
|
|
246
248
|
.filter((d) => d.isDirectory())
|
|
247
249
|
.forEach((d) => available.add(d.name));
|
|
248
|
-
} catch {
|
|
250
|
+
} catch {
|
|
251
|
+
/* skip: project skills dir may not exist */
|
|
252
|
+
}
|
|
249
253
|
|
|
250
254
|
return JSON.stringify({
|
|
251
255
|
success: false,
|
package/lib/http/HttpServer.js
CHANGED
|
@@ -29,13 +29,13 @@ import extractRouter from './routes/extract.js';
|
|
|
29
29
|
import guardRuleRouter from './routes/guardRules.js';
|
|
30
30
|
import healthRouter from './routes/health.js';
|
|
31
31
|
import knowledgeRouter from './routes/knowledge.js';
|
|
32
|
+
import modulesRouter from './routes/modules.js';
|
|
32
33
|
import monitoringRouter from './routes/monitoring.js';
|
|
33
34
|
import recipesRouter from './routes/recipes.js';
|
|
34
35
|
import searchRouter from './routes/search.js';
|
|
35
36
|
import skillsRouter from './routes/skills.js';
|
|
36
37
|
import snippetRouter from './routes/snippets.js';
|
|
37
38
|
import spmRouter from './routes/spm.js';
|
|
38
|
-
import modulesRouter from './routes/modules.js';
|
|
39
39
|
import violationsRouter from './routes/violations.js';
|
|
40
40
|
import wikiRouter from './routes/wiki.js';
|
|
41
41
|
|
|
@@ -77,7 +77,9 @@ router.post(
|
|
|
77
77
|
try {
|
|
78
78
|
const chatAgent = container.get('chatAgent');
|
|
79
79
|
lang = chatAgent?.getLang?.() || 'en';
|
|
80
|
-
} catch {
|
|
80
|
+
} catch {
|
|
81
|
+
/* chatAgent not available */
|
|
82
|
+
}
|
|
81
83
|
enriched = await aiProvider.enrichCandidates(candidates, { lang });
|
|
82
84
|
} catch (err) {
|
|
83
85
|
logger.warn('AI enrichCandidates failed', { error: err.message });
|
|
@@ -7,8 +7,8 @@ import { basename } from 'node:path';
|
|
|
7
7
|
import express from 'express';
|
|
8
8
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
9
9
|
import { getServiceContainer } from '../../injection/ServiceContainer.js';
|
|
10
|
-
import { LanguageService } from '../../shared/LanguageService.js';
|
|
11
10
|
import { ValidationError } from '../../shared/errors/index.js';
|
|
11
|
+
import { LanguageService } from '../../shared/LanguageService.js';
|
|
12
12
|
import { asyncHandler } from '../middleware/errorHandler.js';
|
|
13
13
|
|
|
14
14
|
const router = express.Router();
|
|
@@ -143,11 +143,10 @@ router.post(
|
|
|
143
143
|
const chatAgent = container.get('chatAgent');
|
|
144
144
|
if (chatAgent) {
|
|
145
145
|
const lang =
|
|
146
|
-
language ||
|
|
146
|
+
language ||
|
|
147
|
+
(relativePath ? LanguageService.inferLang(relativePath) || 'unknown' : 'unknown');
|
|
147
148
|
const ext = LanguageService.extForLang(lang) || '.txt';
|
|
148
|
-
const fileName = relativePath
|
|
149
|
-
? basename(relativePath)
|
|
150
|
-
: `clipboard${ext}`;
|
|
149
|
+
const fileName = relativePath ? basename(relativePath) : `clipboard${ext}`;
|
|
151
150
|
const aiResult = await chatAgent.executeTool('extract_recipes', {
|
|
152
151
|
targetName: fileName,
|
|
153
152
|
files: [{ name: fileName, content: text }],
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
import express from 'express';
|
|
7
7
|
import { getServiceContainer } from '../../injection/ServiceContainer.js';
|
|
8
|
-
import { LanguageService } from '../../shared/LanguageService.js';
|
|
9
8
|
import { NotFoundError, ValidationError } from '../../shared/errors/index.js';
|
|
9
|
+
import { LanguageService } from '../../shared/LanguageService.js';
|
|
10
10
|
import { asyncHandler } from '../middleware/errorHandler.js';
|
|
11
11
|
import { getContext, safeInt } from '../utils/routeHelpers.js';
|
|
12
12
|
|
|
@@ -81,7 +81,9 @@ router.get(
|
|
|
81
81
|
packageName: pkgName,
|
|
82
82
|
});
|
|
83
83
|
for (const d of info?.dependencies || []) {
|
|
84
|
-
if (!d?.name)
|
|
84
|
+
if (!d?.name) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
85
87
|
const depPkg = d?.package || pkgName;
|
|
86
88
|
edges.push({ from: id, to: `${depPkg}::${d.name}`, source: 'base' });
|
|
87
89
|
}
|
|
@@ -200,7 +202,9 @@ router.post(
|
|
|
200
202
|
const result = await moduleService.scanFolder(folderPath, {
|
|
201
203
|
...options,
|
|
202
204
|
onProgress: (evt) => {
|
|
203
|
-
if (session)
|
|
205
|
+
if (session) {
|
|
206
|
+
session.push(evt);
|
|
207
|
+
}
|
|
204
208
|
},
|
|
205
209
|
});
|
|
206
210
|
|
|
@@ -406,7 +410,9 @@ router.get('/scan/events/:sessionId', (req, res) => {
|
|
|
406
410
|
}
|
|
407
411
|
|
|
408
412
|
function writeEvent(event) {
|
|
409
|
-
if (res.writableEnded)
|
|
413
|
+
if (res.writableEnded) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
410
416
|
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
411
417
|
}
|
|
412
418
|
|