autosnippet 2.1.0 → 2.5.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 +189 -113
- package/bin/api-server.js +1 -4
- package/bin/cli.js +1 -50
- package/config/constitution.yaml +33 -107
- package/dashboard/dist/assets/{icons-B5rs8uNb.js → icons-Dtm0E6DS.js} +95 -85
- package/dashboard/dist/assets/index-B7VpZOCz.css +1 -0
- package/dashboard/dist/assets/index-D87IZTmZ.js +187 -0
- package/dashboard/dist/assets/{react-markdown-Bp8u1wRC.js → react-markdown-CWxUbOf4.js} +1 -1
- package/dashboard/dist/assets/{syntax-highlighter-C6bvFtpx.js → syntax-highlighter-CJ2drQQb.js} +1 -1
- package/dashboard/dist/assets/{vendor-Cky7Jynh.js → vendor-f83ah6cm.js} +13 -13
- package/dashboard/dist/index.html +6 -6
- package/lib/bootstrap.js +5 -31
- package/lib/cli/SetupService.js +46 -18
- package/lib/core/capability/CapabilityProbe.js +8 -6
- package/lib/core/constitution/Constitution.js +13 -4
- package/lib/core/constitution/ConstitutionValidator.js +106 -211
- package/lib/core/gateway/Gateway.js +34 -98
- package/lib/core/gateway/GatewayActionRegistry.js +12 -1
- package/lib/core/permission/PermissionManager.js +2 -2
- package/lib/external/ai/AiProvider.js +47 -7
- package/lib/external/mcp/McpServer.js +4 -7
- package/lib/external/mcp/handlers/bootstrap.js +13 -1
- package/lib/external/mcp/handlers/browse.js +0 -7
- package/lib/external/mcp/handlers/candidate.js +1 -1
- package/lib/external/mcp/handlers/guard.js +11 -0
- package/lib/external/mcp/handlers/skill.js +186 -18
- package/lib/external/mcp/tools.js +40 -1
- package/lib/http/HttpServer.js +4 -0
- package/lib/http/middleware/roleResolver.js +1 -1
- package/lib/http/routes/auth.js +2 -2
- package/lib/http/routes/monitoring.js +4 -4
- package/lib/http/routes/search.js +0 -17
- package/lib/http/routes/skills.js +73 -0
- package/lib/injection/ServiceContainer.js +21 -40
- package/lib/service/candidate/CandidateService.js +12 -1
- package/lib/service/chat/ChatAgent.js +139 -18
- package/lib/service/chat/Memory.js +104 -0
- package/lib/service/chat/tools.js +244 -10
- package/lib/service/guard/GuardCheckEngine.js +9 -1
- package/lib/service/recipe/RecipeService.js +8 -0
- package/lib/service/skills/SkillHooks.js +126 -0
- package/package.json +1 -1
- package/scripts/init-db.js +1 -2
- package/templates/constitution.yaml +29 -85
- package/dashboard/dist/assets/index-0YzLw2ga.css +0 -1
- package/dashboard/dist/assets/index-DbkbX1c-.js +0 -154
- package/lib/core/session/SessionManager.js +0 -232
- package/lib/infrastructure/logging/ReasoningLogger.js +0 -269
- package/lib/infrastructure/monitoring/RoleDriftMonitor.js +0 -259
- package/lib/infrastructure/quality/ComplianceEvaluator.js +0 -326
|
@@ -5,12 +5,10 @@ import { InternalError } from '../../shared/errors/BaseError.js';
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Gateway - 统一网关
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 4. 会话管理
|
|
13
|
-
* 5. 事件分发
|
|
8
|
+
* 所有操作的唯一入口。
|
|
9
|
+
*
|
|
10
|
+
* Pipeline (4 步):
|
|
11
|
+
* validate → guard → route → audit
|
|
14
12
|
*/
|
|
15
13
|
export class Gateway extends EventEmitter {
|
|
16
14
|
constructor(config) {
|
|
@@ -18,14 +16,12 @@ export class Gateway extends EventEmitter {
|
|
|
18
16
|
this.config = config;
|
|
19
17
|
this.logger = Logger.getInstance();
|
|
20
18
|
this.routes = new Map();
|
|
21
|
-
this.plugins = [];
|
|
22
19
|
|
|
23
20
|
// 依赖注入(稍后设置)
|
|
24
21
|
this.constitution = null;
|
|
25
22
|
this.constitutionValidator = null;
|
|
26
23
|
this.permissionManager = null;
|
|
27
24
|
this.auditLogger = null;
|
|
28
|
-
this.sessionManager = null;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
/**
|
|
@@ -36,13 +32,11 @@ export class Gateway extends EventEmitter {
|
|
|
36
32
|
constitutionValidator,
|
|
37
33
|
permissionManager,
|
|
38
34
|
auditLogger,
|
|
39
|
-
sessionManager,
|
|
40
35
|
}) {
|
|
41
36
|
this.constitution = constitution;
|
|
42
37
|
this.constitutionValidator = constitutionValidator;
|
|
43
38
|
this.permissionManager = permissionManager;
|
|
44
39
|
this.auditLogger = auditLogger;
|
|
45
|
-
this.sessionManager = sessionManager;
|
|
46
40
|
}
|
|
47
41
|
|
|
48
42
|
/**
|
|
@@ -63,14 +57,6 @@ export class Gateway extends EventEmitter {
|
|
|
63
57
|
return [...this.routes.keys()];
|
|
64
58
|
}
|
|
65
59
|
|
|
66
|
-
/**
|
|
67
|
-
* 注册插件
|
|
68
|
-
*/
|
|
69
|
-
use(plugin) {
|
|
70
|
-
this.plugins.push(plugin);
|
|
71
|
-
this.logger.debug(`Plugin registered: ${plugin.name}`);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
60
|
/**
|
|
75
61
|
* 执行操作(主入口)
|
|
76
62
|
*/
|
|
@@ -95,28 +81,18 @@ export class Gateway extends EventEmitter {
|
|
|
95
81
|
});
|
|
96
82
|
|
|
97
83
|
try {
|
|
98
|
-
// 1.
|
|
84
|
+
// 1. validate — 请求格式
|
|
99
85
|
this.validateRequest(request);
|
|
100
86
|
|
|
101
|
-
// 2.
|
|
102
|
-
await this.
|
|
87
|
+
// 2. guard — 权限 + 宪法规则
|
|
88
|
+
await this.guard(context);
|
|
103
89
|
|
|
104
|
-
// 3.
|
|
105
|
-
await this.validateConstitution(context);
|
|
106
|
-
|
|
107
|
-
// 4. 执行插件(pre-hook)
|
|
108
|
-
await this.runPlugins('pre', context);
|
|
109
|
-
|
|
110
|
-
// 5. 路由到处理器
|
|
90
|
+
// 3. route — 路由到处理器
|
|
111
91
|
const result = await this.routeToHandler(context);
|
|
112
92
|
|
|
113
|
-
//
|
|
114
|
-
await this.runPlugins('post', context, result);
|
|
115
|
-
|
|
116
|
-
// 7. 审计日志(成功)
|
|
93
|
+
// 4. audit — 记录成功
|
|
117
94
|
await this.auditSuccess(context, result);
|
|
118
95
|
|
|
119
|
-
// 8. 返回结果
|
|
120
96
|
const duration = Date.now() - startTime;
|
|
121
97
|
this.logger.info('Gateway: Request completed', {
|
|
122
98
|
requestId,
|
|
@@ -130,7 +106,6 @@ export class Gateway extends EventEmitter {
|
|
|
130
106
|
duration,
|
|
131
107
|
};
|
|
132
108
|
} catch (error) {
|
|
133
|
-
// 审计日志(失败)
|
|
134
109
|
await this.auditFailure(context, error);
|
|
135
110
|
|
|
136
111
|
const duration = Date.now() - startTime;
|
|
@@ -155,7 +130,7 @@ export class Gateway extends EventEmitter {
|
|
|
155
130
|
|
|
156
131
|
/**
|
|
157
132
|
* 仅检查权限与宪法(不执行业务逻辑)
|
|
158
|
-
* 用于 MCP Gateway gating
|
|
133
|
+
* 用于 MCP Gateway gating
|
|
159
134
|
*/
|
|
160
135
|
async checkOnly(request) {
|
|
161
136
|
const requestId = uuidv4();
|
|
@@ -173,13 +148,9 @@ export class Gateway extends EventEmitter {
|
|
|
173
148
|
|
|
174
149
|
try {
|
|
175
150
|
this.validateRequest(request);
|
|
176
|
-
await this.
|
|
177
|
-
await this.validateConstitution(context);
|
|
178
|
-
await this.runPlugins('pre', context);
|
|
151
|
+
await this.guard(context);
|
|
179
152
|
|
|
180
|
-
// 记录成功的 checkOnly 审计日志(供 MCP Gateway gating 审计追踪)
|
|
181
153
|
await this.auditSuccess(context, { checkOnly: true });
|
|
182
|
-
|
|
183
154
|
return { success: true, requestId };
|
|
184
155
|
} catch (error) {
|
|
185
156
|
await this.auditFailure(context, error);
|
|
@@ -195,8 +166,10 @@ export class Gateway extends EventEmitter {
|
|
|
195
166
|
}
|
|
196
167
|
}
|
|
197
168
|
|
|
169
|
+
// ─── Pipeline Steps ────────────────────────────────────
|
|
170
|
+
|
|
198
171
|
/**
|
|
199
|
-
* 验证请求格式
|
|
172
|
+
* validate — 验证请求格式
|
|
200
173
|
*/
|
|
201
174
|
validateRequest(request) {
|
|
202
175
|
if (!request.actor) {
|
|
@@ -208,38 +181,27 @@ export class Gateway extends EventEmitter {
|
|
|
208
181
|
}
|
|
209
182
|
|
|
210
183
|
/**
|
|
211
|
-
* 权限检查
|
|
184
|
+
* guard — 权限检查 + 宪法验证
|
|
212
185
|
*/
|
|
213
|
-
async
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
186
|
+
async guard(context) {
|
|
187
|
+
// 权限检查
|
|
188
|
+
if (this.permissionManager) {
|
|
189
|
+
this.permissionManager.enforce(context.actor, context.action, context.resource);
|
|
217
190
|
}
|
|
218
191
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.logger.warn('ConstitutionValidator not set, skipping validation');
|
|
228
|
-
return;
|
|
192
|
+
// 宪法数据完整性规则
|
|
193
|
+
if (this.constitutionValidator) {
|
|
194
|
+
await this.constitutionValidator.enforce({
|
|
195
|
+
actor: context.actor,
|
|
196
|
+
action: context.action,
|
|
197
|
+
resource: context.resource,
|
|
198
|
+
data: context.data,
|
|
199
|
+
});
|
|
229
200
|
}
|
|
230
|
-
|
|
231
|
-
const request = {
|
|
232
|
-
actor: context.actor,
|
|
233
|
-
action: context.action,
|
|
234
|
-
resource: context.resource,
|
|
235
|
-
data: context.data,
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
await this.constitutionValidator.enforce(request);
|
|
239
201
|
}
|
|
240
202
|
|
|
241
203
|
/**
|
|
242
|
-
* 路由到处理器
|
|
204
|
+
* route — 路由到处理器
|
|
243
205
|
*/
|
|
244
206
|
async routeToHandler(context) {
|
|
245
207
|
const handler = this.routes.get(context.action);
|
|
@@ -252,23 +214,10 @@ export class Gateway extends EventEmitter {
|
|
|
252
214
|
}
|
|
253
215
|
|
|
254
216
|
/**
|
|
255
|
-
*
|
|
256
|
-
*/
|
|
257
|
-
async runPlugins(phase, context, result = null) {
|
|
258
|
-
for (const plugin of this.plugins) {
|
|
259
|
-
if (plugin[phase]) {
|
|
260
|
-
await plugin[phase](context, result);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* 审计成功
|
|
217
|
+
* audit — 记录成功
|
|
267
218
|
*/
|
|
268
219
|
async auditSuccess(context, result) {
|
|
269
|
-
if (!this.auditLogger)
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
220
|
+
if (!this.auditLogger) return;
|
|
272
221
|
|
|
273
222
|
await this.auditLogger.log({
|
|
274
223
|
requestId: context.requestId,
|
|
@@ -277,19 +226,15 @@ export class Gateway extends EventEmitter {
|
|
|
277
226
|
resource: context.resource,
|
|
278
227
|
result: 'success',
|
|
279
228
|
duration: Date.now() - context.startTime,
|
|
280
|
-
context: {
|
|
281
|
-
session: context.session,
|
|
282
|
-
},
|
|
229
|
+
context: { session: context.session },
|
|
283
230
|
});
|
|
284
231
|
}
|
|
285
232
|
|
|
286
233
|
/**
|
|
287
|
-
*
|
|
234
|
+
* audit — 记录失败
|
|
288
235
|
*/
|
|
289
236
|
async auditFailure(context, error) {
|
|
290
|
-
if (!this.auditLogger)
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
237
|
+
if (!this.auditLogger) return;
|
|
293
238
|
|
|
294
239
|
await this.auditLogger.log({
|
|
295
240
|
requestId: context.requestId,
|
|
@@ -299,9 +244,7 @@ export class Gateway extends EventEmitter {
|
|
|
299
244
|
result: 'failure',
|
|
300
245
|
error: error.message,
|
|
301
246
|
duration: Date.now() - context.startTime,
|
|
302
|
-
context: {
|
|
303
|
-
session: context.session,
|
|
304
|
-
},
|
|
247
|
+
context: { session: context.session },
|
|
305
248
|
});
|
|
306
249
|
}
|
|
307
250
|
|
|
@@ -311,13 +254,6 @@ export class Gateway extends EventEmitter {
|
|
|
311
254
|
getRoutes() {
|
|
312
255
|
return Array.from(this.routes.keys());
|
|
313
256
|
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* 获取所有插件
|
|
317
|
-
*/
|
|
318
|
-
getPlugins() {
|
|
319
|
-
return this.plugins.map((p) => p.name || 'anonymous');
|
|
320
|
-
}
|
|
321
257
|
}
|
|
322
258
|
|
|
323
259
|
export default Gateway;
|
|
@@ -206,8 +206,19 @@ export function registerGatewayActions(gateway, container) {
|
|
|
206
206
|
|
|
207
207
|
// ========== Search Actions ==========
|
|
208
208
|
|
|
209
|
+
// ========== Candidate Update (enrich/refine) ==========
|
|
210
|
+
|
|
211
|
+
gateway.register('candidate:update', async (ctx) => {
|
|
212
|
+
const service = container.get('candidateService');
|
|
213
|
+
return service.updateCandidate
|
|
214
|
+
? service.updateCandidate(ctx.data.id, ctx.data, { userId: ctx.actor })
|
|
215
|
+
: service.createCandidate(ctx.data, { userId: ctx.actor });
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// ========== Search ==========
|
|
219
|
+
|
|
209
220
|
gateway.register('search:query', async (ctx) => {
|
|
210
|
-
const service = container.get('
|
|
221
|
+
const service = container.get('searchEngine');
|
|
211
222
|
return service.search(ctx.data.keyword, ctx.data.options);
|
|
212
223
|
});
|
|
213
224
|
|
|
@@ -123,7 +123,7 @@ export class PermissionManager {
|
|
|
123
123
|
* 处理多种格式:
|
|
124
124
|
* - read_recipes -> read:recipes
|
|
125
125
|
* - read:recipes -> read:recipes(已规范化)
|
|
126
|
-
* -
|
|
126
|
+
* - perm_external_agent_read_recipes -> read:recipes(测试使用的格式)
|
|
127
127
|
*/
|
|
128
128
|
_normalizeAction(action) {
|
|
129
129
|
// 如果已经包含冒号,直接返回
|
|
@@ -134,7 +134,7 @@ export class PermissionManager {
|
|
|
134
134
|
// 处理测试格式:perm_actor_action_resource -> action:resource
|
|
135
135
|
if (action.startsWith('perm_')) {
|
|
136
136
|
const parts = action.split('_');
|
|
137
|
-
//
|
|
137
|
+
// perm_external_agent_read_recipes -> ['perm', 'cursor', 'agent', 'read', 'recipes']
|
|
138
138
|
// 跳过 'perm' 和 actor 名称部分,从实际的 action 部分开始
|
|
139
139
|
if (parts.length >= 4) {
|
|
140
140
|
// 尝试找到 action 部分(常见的 action 包括 read, create, delete, submit, approve, reject)
|
|
@@ -472,11 +472,25 @@ ${items}`;
|
|
|
472
472
|
|
|
473
473
|
/**
|
|
474
474
|
* 修复被截断的 JSON 数组 — 回收已完成的对象
|
|
475
|
-
*
|
|
475
|
+
* 策略 1(主路径): 字符级解析找到最后一个完整的顶层 {...} 对象
|
|
476
|
+
* 策略 2(回退路径): 正则 + 渐进 JSON.parse 尝试(应对代码段中未转义引号导致 inString 追踪失效)
|
|
476
477
|
*/
|
|
477
478
|
_repairTruncatedArray(text) {
|
|
478
|
-
//
|
|
479
|
-
|
|
479
|
+
// ── 策略 1:字符级深度追踪 ──
|
|
480
|
+
const charResult = this._repairByCharTracking(text);
|
|
481
|
+
if (charResult) return charResult;
|
|
482
|
+
|
|
483
|
+
// ── 策略 2:正则回退 — 找所有 "}," 或 "}\n" 位置,从后向前逐一尝试 JSON.parse ──
|
|
484
|
+
const regexResult = this._repairByRegexFallback(text);
|
|
485
|
+
if (regexResult) return regexResult;
|
|
486
|
+
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* 字符级深度追踪修复(原逻辑,处理标准 JSON)
|
|
492
|
+
*/
|
|
493
|
+
_repairByCharTracking(text) {
|
|
480
494
|
let depth = 0;
|
|
481
495
|
let inString = false;
|
|
482
496
|
let escape = false;
|
|
@@ -500,13 +514,39 @@ ${items}`;
|
|
|
500
514
|
}
|
|
501
515
|
|
|
502
516
|
if (lastCompleteObjEnd === -1) return null;
|
|
517
|
+
return this._tryRepairAt(text, lastCompleteObjEnd);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* 正则回退修复 — 不依赖 inString 追踪
|
|
522
|
+
* 寻找所有 "},\s*{" 或 "}\s*]" 边界,从后往前尝试 JSON.parse
|
|
523
|
+
*/
|
|
524
|
+
_repairByRegexFallback(text) {
|
|
525
|
+
// 收集所有 "}" 后跟 "," 或空白的位置(可能是对象边界)
|
|
526
|
+
const candidates = [];
|
|
527
|
+
const re = /\}[\s,]*(?=\s*[\[{]|$)/g;
|
|
528
|
+
let m;
|
|
529
|
+
while ((m = re.exec(text)) !== null) {
|
|
530
|
+
candidates.push(m.index); // "}" 的位置
|
|
531
|
+
}
|
|
503
532
|
|
|
504
|
-
//
|
|
505
|
-
let
|
|
533
|
+
// 从后往前尝试
|
|
534
|
+
for (let i = candidates.length - 1; i >= 0; i--) {
|
|
535
|
+
const result = this._tryRepairAt(text, candidates[i]);
|
|
536
|
+
if (result) return result;
|
|
537
|
+
}
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* 在指定位置截断并尝试闭合 JSON 数组
|
|
543
|
+
*/
|
|
544
|
+
_tryRepairAt(text, endPos) {
|
|
545
|
+
let repaired = text.slice(0, endPos + 1);
|
|
506
546
|
// 去掉尾逗号
|
|
507
547
|
repaired = repaired.replace(/,\s*$/, '');
|
|
508
548
|
repaired += ']';
|
|
509
|
-
//
|
|
549
|
+
// 修复尾逗号(对象/数组末尾多余逗号)
|
|
510
550
|
repaired = repaired.replace(/,\s*([}\]])/g, '$1');
|
|
511
551
|
|
|
512
552
|
try {
|
|
@@ -515,7 +555,7 @@ ${items}`;
|
|
|
515
555
|
this._log('warn', `[extractJSON] Repaired truncated JSON array: recovered ${result.length} items from truncated response`);
|
|
516
556
|
return result;
|
|
517
557
|
}
|
|
518
|
-
} catch { /*
|
|
558
|
+
} catch { /* this position didn't work, try next */ }
|
|
519
559
|
return null;
|
|
520
560
|
}
|
|
521
561
|
|
|
@@ -61,10 +61,7 @@ export class McpServer {
|
|
|
61
61
|
db: components.db,
|
|
62
62
|
auditLogger: components.auditLogger,
|
|
63
63
|
gateway: components.gateway,
|
|
64
|
-
|
|
65
|
-
roleDriftMonitor: components.roleDriftMonitor,
|
|
66
|
-
complianceEvaluator: components.complianceEvaluator,
|
|
67
|
-
sessionManager: components.sessionManager,
|
|
64
|
+
constitution: components.constitution,
|
|
68
65
|
projectRoot: process.env.ASD_PROJECT_DIR || process.cwd(),
|
|
69
66
|
});
|
|
70
67
|
|
|
@@ -123,7 +120,6 @@ export class McpServer {
|
|
|
123
120
|
case 'autosnippet_list_recipes': return browseHandlers.listRecipes(ctx, args);
|
|
124
121
|
case 'autosnippet_get_recipe': return browseHandlers.getRecipe(ctx, args);
|
|
125
122
|
case 'autosnippet_recipe_insights': return browseHandlers.recipeInsights(ctx, args);
|
|
126
|
-
case 'autosnippet_compliance_report': return browseHandlers.complianceReport(ctx, args);
|
|
127
123
|
case 'autosnippet_confirm_usage': return browseHandlers.confirmUsage(ctx, args);
|
|
128
124
|
// 项目结构 & 图谱
|
|
129
125
|
case 'autosnippet_get_targets': return structureHandlers.getTargets(ctx);
|
|
@@ -147,9 +143,10 @@ export class McpServer {
|
|
|
147
143
|
// Bootstrap 冷启动
|
|
148
144
|
case 'autosnippet_bootstrap_knowledge': return bootstrapHandlers.bootstrapKnowledge(ctx, args);
|
|
149
145
|
case 'autosnippet_bootstrap_refine': return bootstrapHandlers.bootstrapRefine(ctx, args);
|
|
150
|
-
// Skills 加载
|
|
146
|
+
// Skills 加载 & 创建
|
|
151
147
|
case 'autosnippet_list_skills': return skillHandlers.listSkills();
|
|
152
148
|
case 'autosnippet_load_skill': return skillHandlers.loadSkill(ctx, args);
|
|
149
|
+
case 'autosnippet_create_skill': return skillHandlers.createSkill(ctx, args);
|
|
153
150
|
default: throw new Error(`Unknown tool: ${name}`);
|
|
154
151
|
}
|
|
155
152
|
}
|
|
@@ -167,7 +164,7 @@ export class McpServer {
|
|
|
167
164
|
if (!gateway) return; // Gateway 未初始化,降级放行
|
|
168
165
|
|
|
169
166
|
const result = await gateway.checkOnly({
|
|
170
|
-
actor: '
|
|
167
|
+
actor: 'external_agent',
|
|
171
168
|
action: mapping.action,
|
|
172
169
|
resource: mapping.resource,
|
|
173
170
|
data: args || {},
|
|
@@ -664,6 +664,18 @@ export async function bootstrapKnowledge(ctx, args) {
|
|
|
664
664
|
responseData.skillsEnhanced = skillsEnhanced;
|
|
665
665
|
responseData.message = `Bootstrap 完成: ${allFiles.length} files, ${allTargets.length} targets, ${depEdgesWritten} graph edges, ${candidateResults.created} 条单一职责候选已创建${skillsEnhanced ? '(Skill 增强)' : ''}。${skillContext?.loaded?.length ? `已加载 Skills: ${skillContext.loaded.join(', ')}。` : ''}⚠️ 候选为启发式初稿,请务必执行后续 AI 精炼步骤提升质量。`;
|
|
666
666
|
|
|
667
|
+
// ── SkillHooks: onBootstrapComplete (fire-and-forget) ──
|
|
668
|
+
try {
|
|
669
|
+
const skillHooks = ctx.container.get('skillHooks');
|
|
670
|
+
skillHooks.run('onBootstrapComplete', {
|
|
671
|
+
filesScanned: allFiles.length,
|
|
672
|
+
targetsFound: allTargets.length,
|
|
673
|
+
candidatesCreated: candidateResults.created,
|
|
674
|
+
candidatesFailed: candidateResults.failed,
|
|
675
|
+
}, { projectRoot: ctx.container.get('database')?.filename || '' })
|
|
676
|
+
.catch(() => {}); // fire-and-forget
|
|
677
|
+
} catch { /* skillHooks not available */ }
|
|
678
|
+
|
|
667
679
|
return envelope({
|
|
668
680
|
success: true,
|
|
669
681
|
data: responseData,
|
|
@@ -695,7 +707,7 @@ export async function bootstrapRefine(ctx, args) {
|
|
|
695
707
|
const result = await candidateService.refineBootstrapCandidates(
|
|
696
708
|
aiProvider,
|
|
697
709
|
{ candidateIds: args.candidateIds, userPrompt: args.userPrompt, dryRun: args.dryRun },
|
|
698
|
-
{ userId: '
|
|
710
|
+
{ userId: 'external_agent' },
|
|
699
711
|
);
|
|
700
712
|
|
|
701
713
|
return envelope({
|
|
@@ -119,13 +119,6 @@ export async function recipeInsights(ctx, args) {
|
|
|
119
119
|
return envelope({ success: true, data: insights, meta: { tool: 'autosnippet_recipe_insights' } });
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
export async function complianceReport(ctx, args = {}) {
|
|
123
|
-
const evaluator = ctx.container.get('complianceEvaluator');
|
|
124
|
-
if (!evaluator) return envelope({ success: false, message: 'ComplianceEvaluator not available', meta: { tool: 'autosnippet_compliance_report' } });
|
|
125
|
-
const report = await evaluator.evaluate({ period: args.period || 'all' });
|
|
126
|
-
return envelope({ success: true, data: report, meta: { tool: 'autosnippet_compliance_report' } });
|
|
127
|
-
}
|
|
128
|
-
|
|
129
122
|
export async function confirmUsage(ctx, args) {
|
|
130
123
|
if (!args.recipeId) throw new Error('recipeId is required');
|
|
131
124
|
const recipeService = ctx.container.get('recipeService');
|
|
@@ -65,7 +65,7 @@ export function buildCandidateMetadata(obj) {
|
|
|
65
65
|
* 保留此函数作为 MCP handler 层的快捷入口,保持向后兼容。
|
|
66
66
|
*/
|
|
67
67
|
async function _createCandidateItem(candidateService, item, source, extraMeta = {}) {
|
|
68
|
-
return candidateService.createFromToolParams(item, source, extraMeta, { userId: '
|
|
68
|
+
return candidateService.createFromToolParams(item, source, extraMeta, { userId: 'external_agent' });
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// ─── 限流检查 ──────────────────────────────────────────────
|
|
@@ -24,6 +24,17 @@ export async function guardCheck(ctx, args) {
|
|
|
24
24
|
const language = args.language || detectLanguage(args.filePath || '');
|
|
25
25
|
const violations = engine.checkCode(args.code, language);
|
|
26
26
|
|
|
27
|
+
// ── SkillHooks: onGuardCheck — 允许 hooks 修改 violations ──
|
|
28
|
+
try {
|
|
29
|
+
const skillHooks = ctx.container.get('skillHooks');
|
|
30
|
+
if (skillHooks.has('onGuardCheck')) {
|
|
31
|
+
for (let i = 0; i < violations.length; i++) {
|
|
32
|
+
const modified = await skillHooks.run('onGuardCheck', violations[i], { language });
|
|
33
|
+
if (modified && typeof modified === 'object') violations[i] = modified;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch { /* skillHooks not available */ }
|
|
37
|
+
|
|
27
38
|
const warnings = [];
|
|
28
39
|
if (language === 'unknown') {
|
|
29
40
|
warnings.push('未能识别语言,部分语言相关规则可能未执行。建议提供 language 或 filePath 参数。');
|