autosnippet 2.8.3 → 2.10.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 -5
- package/bin/cli.js +5 -33
- package/config/constitution.yaml +9 -2
- package/dashboard/dist/assets/{icons-B_Xg4B-s.js → icons-BkT3XrKf.js} +105 -100
- package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
- package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/cli/AiScanService.js +13 -11
- package/lib/cli/KnowledgeSyncService.js +343 -0
- package/lib/cli/SetupService.js +9 -27
- package/lib/core/ast/ProjectGraph.js +160 -0
- package/lib/core/gateway/GatewayActionRegistry.js +48 -58
- package/lib/domain/index.js +16 -11
- package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
- package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
- package/lib/domain/knowledge/Lifecycle.js +109 -0
- package/lib/domain/knowledge/index.js +27 -0
- package/lib/domain/knowledge/values/Constraints.js +125 -0
- package/lib/domain/knowledge/values/Content.js +86 -0
- package/lib/domain/knowledge/values/Quality.js +93 -0
- package/lib/domain/knowledge/values/Reasoning.js +69 -0
- package/lib/domain/knowledge/values/Relations.js +168 -0
- package/lib/domain/knowledge/values/Stats.js +87 -0
- package/lib/domain/knowledge/values/index.js +9 -0
- package/lib/external/ai/AiProvider.js +48 -0
- package/lib/external/ai/providers/GoogleGeminiProvider.js +12 -3
- package/lib/external/mcp/McpServer.js +7 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +3 -2
- package/lib/external/mcp/handlers/bootstrap.js +121 -12
- package/lib/external/mcp/handlers/browse.js +77 -73
- package/lib/external/mcp/handlers/candidate.js +29 -276
- package/lib/external/mcp/handlers/guard.js +2 -0
- package/lib/external/mcp/handlers/knowledge.js +205 -0
- package/lib/external/mcp/handlers/skill.js +4 -2
- package/lib/external/mcp/handlers/structure.js +25 -23
- package/lib/external/mcp/handlers/system.js +10 -12
- package/lib/external/mcp/tools.js +125 -138
- package/lib/http/HttpServer.js +4 -8
- package/lib/http/middleware/requestLogger.js +3 -3
- package/lib/http/routes/ai.js +17 -1
- package/lib/http/routes/extract.js +48 -4
- package/lib/http/routes/knowledge.js +246 -0
- package/lib/http/routes/search.js +12 -17
- package/lib/http/routes/skills.js +44 -1
- package/lib/infrastructure/cache/GraphCache.js +143 -0
- package/lib/infrastructure/database/migrations/015_create_token_usage.js +27 -0
- package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
- package/lib/infrastructure/external/XcodeAutomation.js +187 -103
- package/lib/infrastructure/realtime/RealtimeService.js +14 -2
- package/lib/injection/ServiceContainer.js +164 -63
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
- package/lib/repository/token/TokenUsageStore.js +162 -0
- package/lib/service/automation/DirectiveDetector.js +2 -3
- package/lib/service/automation/FileWatcher.js +67 -28
- package/lib/service/automation/XcodeIntegration.js +931 -156
- package/lib/service/automation/handlers/AlinkHandler.js +6 -4
- package/lib/service/automation/handlers/CreateHandler.js +53 -18
- package/lib/service/automation/handlers/GuardHandler.js +183 -20
- package/lib/service/automation/handlers/SearchHandler.js +35 -17
- package/lib/service/chat/AnalystAgent.js +25 -14
- package/lib/service/chat/CandidateGuardrail.js +1 -1
- package/lib/service/chat/ChatAgent.js +280 -48
- package/lib/service/chat/ContextWindow.js +92 -8
- package/lib/service/chat/HandoffProtocol.js +26 -1
- package/lib/service/chat/ProducerAgent.js +11 -9
- package/lib/service/chat/tools.js +298 -194
- package/lib/service/guard/GuardCheckEngine.js +114 -10
- package/lib/service/guard/GuardService.js +59 -48
- package/lib/service/knowledge/ConfidenceRouter.js +159 -0
- package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
- package/lib/service/knowledge/KnowledgeService.js +725 -0
- package/lib/service/search/SearchEngine.js +92 -19
- package/lib/service/skills/SignalCollector.js +15 -9
- package/lib/service/skills/SkillAdvisor.js +13 -11
- package/lib/service/snippet/SnippetFactory.js +5 -5
- package/lib/service/spm/SpmService.js +119 -18
- package/package.json +1 -1
- package/scripts/install-cursor-skill.js +0 -6
- package/scripts/migrate-md-to-knowledge.mjs +364 -0
- package/skills/autosnippet-analysis/SKILL.md +15 -7
- package/skills/autosnippet-candidates/SKILL.md +6 -6
- package/skills/autosnippet-coldstart/SKILL.md +7 -3
- package/skills/autosnippet-concepts/SKILL.md +7 -6
- package/skills/autosnippet-create/SKILL.md +13 -13
- package/skills/autosnippet-intent/SKILL.md +3 -2
- package/skills/autosnippet-lifecycle/SKILL.md +5 -5
- package/skills/autosnippet-recipes/SKILL.md +16 -4
- package/templates/constitution.yaml +1 -1
- package/templates/copilot-instructions.md +6 -6
- package/templates/recipes-setup/README.md +3 -3
- package/dashboard/dist/assets/index-CkIih2CC.css +0 -1
- package/dashboard/dist/assets/index-Duc8Qk-c.js +0 -197
- package/lib/cli/CandidateSyncService.js +0 -261
- package/lib/cli/SyncService.js +0 -356
- package/lib/domain/candidate/Candidate.js +0 -196
- package/lib/domain/candidate/CandidateRepository.js +0 -107
- package/lib/domain/candidate/Reasoning.js +0 -52
- package/lib/domain/recipe/Recipe.js +0 -421
- package/lib/domain/recipe/RecipeRepository.js +0 -54
- package/lib/domain/types/CandidateStatus.js +0 -52
- package/lib/http/routes/candidates.js +0 -559
- package/lib/http/routes/recipes.js +0 -397
- package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
- package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
- package/lib/service/candidate/CandidateAggregator.js +0 -52
- package/lib/service/candidate/CandidateFileWriter.js +0 -383
- package/lib/service/candidate/CandidateService.js +0 -973
- package/lib/service/recipe/RecipeFileWriter.js +0 -514
- package/lib/service/recipe/RecipeService.js +0 -786
- package/lib/service/recipe/RecipeStatsTracker.js +0 -148
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeRepository — 统一知识实体仓储接口
|
|
3
|
+
*
|
|
4
|
+
* 替代 CandidateRepository + RecipeRepository。
|
|
5
|
+
* 实现类见 lib/repository/knowledge/KnowledgeRepository.impl.js
|
|
6
|
+
*/
|
|
7
|
+
export class KnowledgeRepository {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 创建 KnowledgeEntry
|
|
11
|
+
* @param {import('./KnowledgeEntry.js').KnowledgeEntry} entry
|
|
12
|
+
* @returns {Promise<import('./KnowledgeEntry.js').KnowledgeEntry>}
|
|
13
|
+
*/
|
|
14
|
+
async create(entry) {
|
|
15
|
+
throw new Error('Not implemented');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 根据 ID 获取
|
|
20
|
+
* @param {string} id
|
|
21
|
+
* @returns {Promise<import('./KnowledgeEntry.js').KnowledgeEntry|null>}
|
|
22
|
+
*/
|
|
23
|
+
async findById(id) {
|
|
24
|
+
throw new Error('Not implemented');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 分页查询
|
|
29
|
+
* @param {Object} filters - { lifecycle, kind, language, category, knowledge_type, source }
|
|
30
|
+
* @param {Object} options - { page, pageSize, orderBy, order }
|
|
31
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
32
|
+
*/
|
|
33
|
+
async findWithPagination(filters = {}, options = {}) {
|
|
34
|
+
throw new Error('Not implemented');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 根据生命周期状态查询
|
|
39
|
+
* @param {string} lifecycle
|
|
40
|
+
* @param {Object} pagination
|
|
41
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
42
|
+
*/
|
|
43
|
+
async findByLifecycle(lifecycle, pagination = {}) {
|
|
44
|
+
throw new Error('Not implemented');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 根据 kind 查询
|
|
49
|
+
* @param {string} kind - 'rule' | 'pattern' | 'fact'
|
|
50
|
+
* @param {Object} options - { page, pageSize, lifecycle }
|
|
51
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
52
|
+
*/
|
|
53
|
+
async findByKind(kind, options = {}) {
|
|
54
|
+
throw new Error('Not implemented');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 查询所有 active 的 rule 类型(Guard 消费热路径)
|
|
59
|
+
* @returns {Promise<KnowledgeEntry[]>}
|
|
60
|
+
*/
|
|
61
|
+
async findActiveRules() {
|
|
62
|
+
throw new Error('Not implemented');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 根据语言查询
|
|
67
|
+
* @param {string} language
|
|
68
|
+
* @param {Object} pagination
|
|
69
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
70
|
+
*/
|
|
71
|
+
async findByLanguage(language, pagination = {}) {
|
|
72
|
+
throw new Error('Not implemented');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 根据分类查询
|
|
77
|
+
* @param {string} category
|
|
78
|
+
* @param {Object} pagination
|
|
79
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
80
|
+
*/
|
|
81
|
+
async findByCategory(category, pagination = {}) {
|
|
82
|
+
throw new Error('Not implemented');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 搜索 (标题/内容/触发词/标签)
|
|
87
|
+
* @param {string} keyword
|
|
88
|
+
* @param {Object} pagination
|
|
89
|
+
* @returns {Promise<{ data: KnowledgeEntry[], pagination: Object }>}
|
|
90
|
+
*/
|
|
91
|
+
async search(keyword, pagination = {}) {
|
|
92
|
+
throw new Error('Not implemented');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 更新
|
|
97
|
+
* @param {string} id
|
|
98
|
+
* @param {Object} updates - wire format 的部分字段
|
|
99
|
+
* @returns {Promise<import('./KnowledgeEntry.js').KnowledgeEntry>}
|
|
100
|
+
*/
|
|
101
|
+
async update(id, updates) {
|
|
102
|
+
throw new Error('Not implemented');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 删除
|
|
107
|
+
* @param {string} id
|
|
108
|
+
* @returns {Promise<boolean>}
|
|
109
|
+
*/
|
|
110
|
+
async delete(id) {
|
|
111
|
+
throw new Error('Not implemented');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 获取统计信息
|
|
116
|
+
* @returns {Promise<Object>}
|
|
117
|
+
*/
|
|
118
|
+
async getStats() {
|
|
119
|
+
throw new Error('Not implemented');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default KnowledgeRepository;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lifecycle — 知识实体生命周期状态机(3 状态简化版)
|
|
3
|
+
*
|
|
4
|
+
* pending — 待审核(所有新条目初始状态)
|
|
5
|
+
* active — 已发布(可被搜索/Guard/Export 消费)
|
|
6
|
+
* deprecated — 已废弃
|
|
7
|
+
*
|
|
8
|
+
* 仅开发者可执行 pending → active(发布)。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const Lifecycle = {
|
|
12
|
+
/** 待审核 */
|
|
13
|
+
PENDING: 'pending',
|
|
14
|
+
/** 已发布(可被搜索/Guard/Export 消费) */
|
|
15
|
+
ACTIVE: 'active',
|
|
16
|
+
/** 已弃用 */
|
|
17
|
+
DEPRECATED: 'deprecated',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// ── 向后兼容别名(旧数据中仍存在这些值) ──
|
|
21
|
+
export const LEGACY_LIFECYCLE_MAP = {
|
|
22
|
+
draft: Lifecycle.PENDING,
|
|
23
|
+
approved: Lifecycle.PENDING,
|
|
24
|
+
auto_approved: Lifecycle.PENDING,
|
|
25
|
+
rejected: Lifecycle.DEPRECATED,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/** 候选阶段的所有状态 */
|
|
29
|
+
export const CANDIDATE_STATES = [
|
|
30
|
+
Lifecycle.PENDING,
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/** 合法状态转移表 */
|
|
34
|
+
const VALID_TRANSITIONS = {
|
|
35
|
+
[Lifecycle.PENDING]: [Lifecycle.ACTIVE, Lifecycle.DEPRECATED],
|
|
36
|
+
[Lifecycle.ACTIVE]: [Lifecycle.DEPRECATED],
|
|
37
|
+
[Lifecycle.DEPRECATED]: [Lifecycle.PENDING],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 规范化生命周期值(兼容旧数据)
|
|
42
|
+
* @param {string} lifecycle
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
export function normalizeLifecycle(lifecycle) {
|
|
46
|
+
if (Object.values(Lifecycle).includes(lifecycle)) return lifecycle;
|
|
47
|
+
return LEGACY_LIFECYCLE_MAP[lifecycle] || Lifecycle.PENDING;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 检查状态转移是否合法
|
|
52
|
+
* @param {string} from
|
|
53
|
+
* @param {string} to
|
|
54
|
+
* @returns {boolean}
|
|
55
|
+
*/
|
|
56
|
+
export function isValidTransition(from, to) {
|
|
57
|
+
const normalFrom = normalizeLifecycle(from);
|
|
58
|
+
const normalTo = normalizeLifecycle(to);
|
|
59
|
+
const allowed = VALID_TRANSITIONS[normalFrom];
|
|
60
|
+
return Array.isArray(allowed) && allowed.includes(normalTo);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 是否为合法的生命周期值(含旧值兼容)
|
|
65
|
+
* @param {string} lifecycle
|
|
66
|
+
* @returns {boolean}
|
|
67
|
+
*/
|
|
68
|
+
export function isValidLifecycle(lifecycle) {
|
|
69
|
+
return Object.values(Lifecycle).includes(lifecycle) || lifecycle in LEGACY_LIFECYCLE_MAP;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 是否处于候选阶段(待审核)
|
|
74
|
+
* @param {string} lifecycle
|
|
75
|
+
* @returns {boolean}
|
|
76
|
+
*/
|
|
77
|
+
export function isCandidate(lifecycle) {
|
|
78
|
+
const normalized = normalizeLifecycle(lifecycle);
|
|
79
|
+
return normalized === Lifecycle.PENDING;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* ── knowledgeType → kind 映射 ── */
|
|
83
|
+
|
|
84
|
+
const KIND_MAP = {
|
|
85
|
+
'code-standard': 'rule',
|
|
86
|
+
'code-style': 'rule',
|
|
87
|
+
'best-practice': 'rule',
|
|
88
|
+
'boundary-constraint': 'rule',
|
|
89
|
+
'code-pattern': 'pattern',
|
|
90
|
+
'architecture': 'pattern',
|
|
91
|
+
'solution': 'pattern',
|
|
92
|
+
'anti-pattern': 'pattern',
|
|
93
|
+
'code-relation': 'fact',
|
|
94
|
+
'inheritance': 'fact',
|
|
95
|
+
'call-chain': 'fact',
|
|
96
|
+
'data-flow': 'fact',
|
|
97
|
+
'module-dependency': 'fact',
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 从 knowledgeType 推导 kind
|
|
102
|
+
* @param {string} knowledgeType
|
|
103
|
+
* @returns {'rule'|'pattern'|'fact'}
|
|
104
|
+
*/
|
|
105
|
+
export function inferKind(knowledgeType) {
|
|
106
|
+
return KIND_MAP[knowledgeType] || 'pattern';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default Lifecycle;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeEntry 领域层统一导出
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// 实体
|
|
6
|
+
export { KnowledgeEntry } from './KnowledgeEntry.js';
|
|
7
|
+
|
|
8
|
+
// 生命周期
|
|
9
|
+
export {
|
|
10
|
+
Lifecycle,
|
|
11
|
+
isValidTransition,
|
|
12
|
+
isValidLifecycle,
|
|
13
|
+
isCandidate,
|
|
14
|
+
CANDIDATE_STATES,
|
|
15
|
+
inferKind,
|
|
16
|
+
} from './Lifecycle.js';
|
|
17
|
+
|
|
18
|
+
// 值对象
|
|
19
|
+
export { Content } from './values/Content.js';
|
|
20
|
+
export { Relations, RELATION_BUCKETS } from './values/Relations.js';
|
|
21
|
+
export { Constraints } from './values/Constraints.js';
|
|
22
|
+
export { Reasoning } from './values/Reasoning.js';
|
|
23
|
+
export { Quality } from './values/Quality.js';
|
|
24
|
+
export { Stats } from './values/Stats.js';
|
|
25
|
+
|
|
26
|
+
// Repository 接口
|
|
27
|
+
export { KnowledgeRepository } from './KnowledgeRepository.js';
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constraints — 约束值对象
|
|
3
|
+
*
|
|
4
|
+
* 包含 Guard 规则 (regex + ast)、边界约束、前置条件、副作用。
|
|
5
|
+
* Guard 规则预留 AST 类型,为语义规则做前瞻设计。
|
|
6
|
+
*/
|
|
7
|
+
export class Constraints {
|
|
8
|
+
constructor(props = {}) {
|
|
9
|
+
/** @type {Array<Guard>} Guard 规则列表 */
|
|
10
|
+
this.guards = (props.guards || []).map(Constraints._normalizeGuard);
|
|
11
|
+
/** @type {string[]} 边界约束 */
|
|
12
|
+
this.boundaries = props.boundaries || [];
|
|
13
|
+
/** @type {string[]} 前置条件 */
|
|
14
|
+
this.preconditions = props.preconditions || [];
|
|
15
|
+
/** @type {string[]} 副作用 */
|
|
16
|
+
this.sideEffects = props.side_effects ?? props.sideEffects ?? [];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 从任意输入构造 Constraints
|
|
21
|
+
* @param {Constraints|Object|null} input
|
|
22
|
+
* @returns {Constraints}
|
|
23
|
+
*/
|
|
24
|
+
static from(input) {
|
|
25
|
+
if (input instanceof Constraints) return input;
|
|
26
|
+
if (!input) return new Constraints();
|
|
27
|
+
return new Constraints(input);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 标准化 Guard 对象
|
|
32
|
+
* @param {Object} g
|
|
33
|
+
* @returns {Guard}
|
|
34
|
+
*/
|
|
35
|
+
static _normalizeGuard(g) {
|
|
36
|
+
return {
|
|
37
|
+
id: g.id || null,
|
|
38
|
+
type: g.type || (g.ast_query ? 'ast' : 'regex'),
|
|
39
|
+
pattern: g.pattern || null,
|
|
40
|
+
ast_query: g.ast_query || null,
|
|
41
|
+
message: g.message || '',
|
|
42
|
+
severity: g.severity || 'warning',
|
|
43
|
+
fix_suggestion: g.fix_suggestion || null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 获取 regex 类型的 Guard 规则
|
|
49
|
+
* @returns {Array<Guard>}
|
|
50
|
+
*/
|
|
51
|
+
getRegexGuards() {
|
|
52
|
+
return this.guards.filter(g => g.type === 'regex' && g.pattern);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 获取 ast 类型的 Guard 规则
|
|
57
|
+
* @returns {Array<Guard>}
|
|
58
|
+
*/
|
|
59
|
+
getAstGuards() {
|
|
60
|
+
return this.guards.filter(g => g.type === 'ast' && g.ast_query);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 添加 Guard 规则
|
|
65
|
+
* @param {Object} guard
|
|
66
|
+
* @returns {Constraints}
|
|
67
|
+
*/
|
|
68
|
+
addGuard(guard) {
|
|
69
|
+
this.guards.push(Constraints._normalizeGuard(guard));
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* 是否有 Guard 规则
|
|
75
|
+
* @returns {boolean}
|
|
76
|
+
*/
|
|
77
|
+
hasGuards() {
|
|
78
|
+
return this.guards.length > 0;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 是否为空
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
isEmpty() {
|
|
86
|
+
return this.guards.length === 0 &&
|
|
87
|
+
this.boundaries.length === 0 &&
|
|
88
|
+
this.preconditions.length === 0 &&
|
|
89
|
+
this.sideEffects.length === 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* 转换为 wire format JSON
|
|
94
|
+
*/
|
|
95
|
+
toJSON() {
|
|
96
|
+
return {
|
|
97
|
+
guards: this.guards,
|
|
98
|
+
boundaries: this.boundaries,
|
|
99
|
+
preconditions: this.preconditions,
|
|
100
|
+
side_effects: this.sideEffects,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 从 wire format 创建
|
|
106
|
+
* @param {Object} data
|
|
107
|
+
* @returns {Constraints}
|
|
108
|
+
*/
|
|
109
|
+
static fromJSON(data) {
|
|
110
|
+
return Constraints.from(data);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @typedef {Object} Guard
|
|
116
|
+
* @property {?string} id - Guard 唯一标识
|
|
117
|
+
* @property {'regex'|'ast'} type - 类型
|
|
118
|
+
* @property {?string} pattern - regex pattern (type=regex 时)
|
|
119
|
+
* @property {?Object} ast_query - AST 查询 (type=ast 时)
|
|
120
|
+
* @property {string} message - 错误/警告消息
|
|
121
|
+
* @property {'error'|'warning'|'info'} severity - 严重级别
|
|
122
|
+
* @property {?string} fix_suggestion - 关联修复 Recipe 的 trigger
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
export default Constraints;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content — 内容值对象
|
|
3
|
+
*
|
|
4
|
+
* 统一承载代码片段 (pattern) 或 Markdown 全文 (markdown),
|
|
5
|
+
* 以及设计原理、实施步骤、代码变更、验证方式。
|
|
6
|
+
*/
|
|
7
|
+
export class Content {
|
|
8
|
+
constructor(props = {}) {
|
|
9
|
+
/** @type {string} 代码片段 */
|
|
10
|
+
this.pattern = props.pattern ?? '';
|
|
11
|
+
/** @type {string} Markdown 全文(与 pattern 二选一) */
|
|
12
|
+
this.markdown = props.markdown ?? '';
|
|
13
|
+
/** @type {string} 设计原理 */
|
|
14
|
+
this.rationale = props.rationale ?? '';
|
|
15
|
+
/** @type {Array<{title?:string, description?:string, code?:string}>} 实施步骤 */
|
|
16
|
+
this.steps = props.steps ?? [];
|
|
17
|
+
/** @type {Array<{file:string, before:string, after:string, explanation:string}>} 代码变更 */
|
|
18
|
+
this.codeChanges = props.code_changes ?? props.codeChanges ?? [];
|
|
19
|
+
/** @type {?{method?:string, expected_result?:string, test_code?:string}} 验证方式 */
|
|
20
|
+
this.verification = props.verification ?? null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 从任意输入构造 Content
|
|
25
|
+
* @param {Content|Object|null} input
|
|
26
|
+
* @returns {Content}
|
|
27
|
+
*/
|
|
28
|
+
static from(input) {
|
|
29
|
+
if (input instanceof Content) return input;
|
|
30
|
+
if (!input) return new Content();
|
|
31
|
+
return new Content(input);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 从旧 Candidate 的 code + metadata 构建
|
|
36
|
+
* @param {string} code
|
|
37
|
+
* @param {Object} meta
|
|
38
|
+
* @returns {Content}
|
|
39
|
+
*/
|
|
40
|
+
static fromLegacyCandidate(code, meta = {}) {
|
|
41
|
+
const isMarkdown = code && (
|
|
42
|
+
code.includes('— 项目特写') || /^#{1,3}\s/.test(code.trimStart())
|
|
43
|
+
);
|
|
44
|
+
return new Content({
|
|
45
|
+
pattern: isMarkdown ? '' : (code || ''),
|
|
46
|
+
markdown: isMarkdown ? code : '',
|
|
47
|
+
rationale: meta.rationale || '',
|
|
48
|
+
steps: meta.steps || [],
|
|
49
|
+
code_changes: meta.codeChanges || meta.code_changes || [],
|
|
50
|
+
verification: meta.verification || null,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 是否包含有效内容
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
hasContent() {
|
|
59
|
+
return !!(this.pattern || this.markdown || this.rationale || this.steps.length > 0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 转换为 wire format JSON
|
|
64
|
+
*/
|
|
65
|
+
toJSON() {
|
|
66
|
+
return {
|
|
67
|
+
pattern: this.pattern,
|
|
68
|
+
markdown: this.markdown,
|
|
69
|
+
rationale: this.rationale,
|
|
70
|
+
steps: this.steps,
|
|
71
|
+
code_changes: this.codeChanges,
|
|
72
|
+
verification: this.verification,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 从 wire format 创建
|
|
78
|
+
* @param {Object} data
|
|
79
|
+
* @returns {Content}
|
|
80
|
+
*/
|
|
81
|
+
static fromJSON(data) {
|
|
82
|
+
return new Content(data);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export default Content;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quality — 质量值对象
|
|
3
|
+
*
|
|
4
|
+
* 4 维度评分 + 综合分 + 等级。
|
|
5
|
+
*/
|
|
6
|
+
export class Quality {
|
|
7
|
+
constructor(props = {}) {
|
|
8
|
+
/** @type {number} 内容完整度 (0-1) */
|
|
9
|
+
this.completeness = props.completeness ?? 0;
|
|
10
|
+
/** @type {number} 项目适配度 (0-1) */
|
|
11
|
+
this.adaptation = props.adaptation ?? 0;
|
|
12
|
+
/** @type {number} 文档清晰度 (0-1) */
|
|
13
|
+
this.documentation = props.documentation ?? 0;
|
|
14
|
+
/** @type {number} 综合分 (0-1) */
|
|
15
|
+
this.overall = props.overall ?? 0;
|
|
16
|
+
/** @type {string} 等级 A-F */
|
|
17
|
+
this.grade = props.grade || Quality.calcGrade(this.overall);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 从任意输入构造 Quality
|
|
22
|
+
* @param {Quality|Object|null} input
|
|
23
|
+
* @returns {Quality}
|
|
24
|
+
*/
|
|
25
|
+
static from(input) {
|
|
26
|
+
if (input instanceof Quality) return input;
|
|
27
|
+
return new Quality(input || {});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 从旧 Recipe quality 字段映射
|
|
32
|
+
* @param {Object} old { codeCompleteness, projectAdaptation, documentationClarity, overall }
|
|
33
|
+
* @returns {Quality}
|
|
34
|
+
*/
|
|
35
|
+
static fromLegacyRecipe(old) {
|
|
36
|
+
if (!old) return new Quality();
|
|
37
|
+
return new Quality({
|
|
38
|
+
completeness: old.codeCompleteness ?? old.completeness ?? 0,
|
|
39
|
+
adaptation: old.projectAdaptation ?? old.adaptation ?? 0,
|
|
40
|
+
documentation: old.documentationClarity ?? old.documentation ?? 0,
|
|
41
|
+
overall: old.overall ?? 0,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 从 3 维度计算综合分
|
|
47
|
+
* @returns {Quality}
|
|
48
|
+
*/
|
|
49
|
+
recalculate() {
|
|
50
|
+
this.overall = Math.round(
|
|
51
|
+
((this.completeness + this.adaptation + this.documentation) / 3) * 100
|
|
52
|
+
) / 100;
|
|
53
|
+
this.grade = Quality.calcGrade(this.overall);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 根据分数计算等级
|
|
59
|
+
* @param {number} score 0-1
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
static calcGrade(score) {
|
|
63
|
+
if (score >= 0.9) return 'A';
|
|
64
|
+
if (score >= 0.75) return 'B';
|
|
65
|
+
if (score >= 0.6) return 'C';
|
|
66
|
+
if (score >= 0.4) return 'D';
|
|
67
|
+
return 'F';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 转换为 wire format JSON
|
|
72
|
+
*/
|
|
73
|
+
toJSON() {
|
|
74
|
+
return {
|
|
75
|
+
completeness: this.completeness,
|
|
76
|
+
adaptation: this.adaptation,
|
|
77
|
+
documentation: this.documentation,
|
|
78
|
+
overall: this.overall,
|
|
79
|
+
grade: this.grade,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* 从 wire format 创建
|
|
85
|
+
* @param {Object} data
|
|
86
|
+
* @returns {Quality}
|
|
87
|
+
*/
|
|
88
|
+
static fromJSON(data) {
|
|
89
|
+
return Quality.from(data);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export default Quality;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reasoning — 推理值对象(V3 统一版)
|
|
3
|
+
*
|
|
4
|
+
* 与旧 Candidate.Reasoning 逻辑一致,但 wire format 统一为 snake_case。
|
|
5
|
+
*/
|
|
6
|
+
export class Reasoning {
|
|
7
|
+
constructor(props = {}) {
|
|
8
|
+
/** @type {string} 为什么遵循标准 */
|
|
9
|
+
this.whyStandard = props.why_standard ?? props.whyStandard ?? '';
|
|
10
|
+
/** @type {string[]} 来源列表 */
|
|
11
|
+
this.sources = props.sources || [];
|
|
12
|
+
/** @type {number} 置信度 0-1 */
|
|
13
|
+
this.confidence = props.confidence ?? 0.7;
|
|
14
|
+
/** @type {Object.<string, number>} 质量信号 */
|
|
15
|
+
this.qualitySignals = props.quality_signals ?? props.qualitySignals ?? {};
|
|
16
|
+
/** @type {string[]} 备选方案 */
|
|
17
|
+
this.alternatives = props.alternatives || [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 从任意输入构造 Reasoning
|
|
22
|
+
* @param {Reasoning|Object|null} input
|
|
23
|
+
* @returns {Reasoning}
|
|
24
|
+
*/
|
|
25
|
+
static from(input) {
|
|
26
|
+
if (input instanceof Reasoning) return input;
|
|
27
|
+
if (!input) return new Reasoning();
|
|
28
|
+
return new Reasoning(input);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 验证推理信息的完整性
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
isValid() {
|
|
36
|
+
return !!(
|
|
37
|
+
this.whyStandard?.trim() &&
|
|
38
|
+
Array.isArray(this.sources) &&
|
|
39
|
+
this.sources.length > 0 &&
|
|
40
|
+
typeof this.confidence === 'number' &&
|
|
41
|
+
this.confidence >= 0 &&
|
|
42
|
+
this.confidence <= 1
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 转换为 wire format JSON (snake_case)
|
|
48
|
+
*/
|
|
49
|
+
toJSON() {
|
|
50
|
+
return {
|
|
51
|
+
why_standard: this.whyStandard,
|
|
52
|
+
sources: this.sources,
|
|
53
|
+
confidence: this.confidence,
|
|
54
|
+
quality_signals: this.qualitySignals,
|
|
55
|
+
alternatives: this.alternatives,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 从 wire format 创建
|
|
61
|
+
* @param {Object} data
|
|
62
|
+
* @returns {Reasoning}
|
|
63
|
+
*/
|
|
64
|
+
static fromJSON(data) {
|
|
65
|
+
return new Reasoning(data);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default Reasoning;
|