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.
Files changed (110) hide show
  1. package/README.md +5 -5
  2. package/bin/cli.js +5 -33
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-B_Xg4B-s.js → icons-BkT3XrKf.js} +105 -100
  5. package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
  6. package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/lib/cli/AiScanService.js +13 -11
  9. package/lib/cli/KnowledgeSyncService.js +343 -0
  10. package/lib/cli/SetupService.js +9 -27
  11. package/lib/core/ast/ProjectGraph.js +160 -0
  12. package/lib/core/gateway/GatewayActionRegistry.js +48 -58
  13. package/lib/domain/index.js +16 -11
  14. package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
  15. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  16. package/lib/domain/knowledge/Lifecycle.js +109 -0
  17. package/lib/domain/knowledge/index.js +27 -0
  18. package/lib/domain/knowledge/values/Constraints.js +125 -0
  19. package/lib/domain/knowledge/values/Content.js +86 -0
  20. package/lib/domain/knowledge/values/Quality.js +93 -0
  21. package/lib/domain/knowledge/values/Reasoning.js +69 -0
  22. package/lib/domain/knowledge/values/Relations.js +168 -0
  23. package/lib/domain/knowledge/values/Stats.js +87 -0
  24. package/lib/domain/knowledge/values/index.js +9 -0
  25. package/lib/external/ai/AiProvider.js +48 -0
  26. package/lib/external/ai/providers/GoogleGeminiProvider.js +12 -3
  27. package/lib/external/mcp/McpServer.js +7 -5
  28. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +3 -2
  29. package/lib/external/mcp/handlers/bootstrap.js +121 -12
  30. package/lib/external/mcp/handlers/browse.js +77 -73
  31. package/lib/external/mcp/handlers/candidate.js +29 -276
  32. package/lib/external/mcp/handlers/guard.js +2 -0
  33. package/lib/external/mcp/handlers/knowledge.js +205 -0
  34. package/lib/external/mcp/handlers/skill.js +4 -2
  35. package/lib/external/mcp/handlers/structure.js +25 -23
  36. package/lib/external/mcp/handlers/system.js +10 -12
  37. package/lib/external/mcp/tools.js +125 -138
  38. package/lib/http/HttpServer.js +4 -8
  39. package/lib/http/middleware/requestLogger.js +3 -3
  40. package/lib/http/routes/ai.js +17 -1
  41. package/lib/http/routes/extract.js +48 -4
  42. package/lib/http/routes/knowledge.js +246 -0
  43. package/lib/http/routes/search.js +12 -17
  44. package/lib/http/routes/skills.js +44 -1
  45. package/lib/infrastructure/cache/GraphCache.js +143 -0
  46. package/lib/infrastructure/database/migrations/015_create_token_usage.js +27 -0
  47. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  48. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  49. package/lib/infrastructure/realtime/RealtimeService.js +14 -2
  50. package/lib/injection/ServiceContainer.js +164 -63
  51. package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
  52. package/lib/repository/token/TokenUsageStore.js +162 -0
  53. package/lib/service/automation/DirectiveDetector.js +2 -3
  54. package/lib/service/automation/FileWatcher.js +67 -28
  55. package/lib/service/automation/XcodeIntegration.js +931 -156
  56. package/lib/service/automation/handlers/AlinkHandler.js +6 -4
  57. package/lib/service/automation/handlers/CreateHandler.js +53 -18
  58. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  59. package/lib/service/automation/handlers/SearchHandler.js +35 -17
  60. package/lib/service/chat/AnalystAgent.js +25 -14
  61. package/lib/service/chat/CandidateGuardrail.js +1 -1
  62. package/lib/service/chat/ChatAgent.js +280 -48
  63. package/lib/service/chat/ContextWindow.js +92 -8
  64. package/lib/service/chat/HandoffProtocol.js +26 -1
  65. package/lib/service/chat/ProducerAgent.js +11 -9
  66. package/lib/service/chat/tools.js +298 -194
  67. package/lib/service/guard/GuardCheckEngine.js +114 -10
  68. package/lib/service/guard/GuardService.js +59 -48
  69. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  70. package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
  71. package/lib/service/knowledge/KnowledgeService.js +725 -0
  72. package/lib/service/search/SearchEngine.js +92 -19
  73. package/lib/service/skills/SignalCollector.js +15 -9
  74. package/lib/service/skills/SkillAdvisor.js +13 -11
  75. package/lib/service/snippet/SnippetFactory.js +5 -5
  76. package/lib/service/spm/SpmService.js +119 -18
  77. package/package.json +1 -1
  78. package/scripts/install-cursor-skill.js +0 -6
  79. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  80. package/skills/autosnippet-analysis/SKILL.md +15 -7
  81. package/skills/autosnippet-candidates/SKILL.md +6 -6
  82. package/skills/autosnippet-coldstart/SKILL.md +7 -3
  83. package/skills/autosnippet-concepts/SKILL.md +7 -6
  84. package/skills/autosnippet-create/SKILL.md +13 -13
  85. package/skills/autosnippet-intent/SKILL.md +3 -2
  86. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  87. package/skills/autosnippet-recipes/SKILL.md +16 -4
  88. package/templates/constitution.yaml +1 -1
  89. package/templates/copilot-instructions.md +6 -6
  90. package/templates/recipes-setup/README.md +3 -3
  91. package/dashboard/dist/assets/index-CkIih2CC.css +0 -1
  92. package/dashboard/dist/assets/index-Duc8Qk-c.js +0 -197
  93. package/lib/cli/CandidateSyncService.js +0 -261
  94. package/lib/cli/SyncService.js +0 -356
  95. package/lib/domain/candidate/Candidate.js +0 -196
  96. package/lib/domain/candidate/CandidateRepository.js +0 -107
  97. package/lib/domain/candidate/Reasoning.js +0 -52
  98. package/lib/domain/recipe/Recipe.js +0 -421
  99. package/lib/domain/recipe/RecipeRepository.js +0 -54
  100. package/lib/domain/types/CandidateStatus.js +0 -52
  101. package/lib/http/routes/candidates.js +0 -559
  102. package/lib/http/routes/recipes.js +0 -397
  103. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  104. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  105. package/lib/service/candidate/CandidateAggregator.js +0 -52
  106. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  107. package/lib/service/candidate/CandidateService.js +0 -973
  108. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  109. package/lib/service/recipe/RecipeService.js +0 -786
  110. 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;