autosnippet 3.0.11 → 3.0.13

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 (56) hide show
  1. package/bin/cli.js +64 -1
  2. package/config/default.json +9 -0
  3. package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
  4. package/dashboard/dist/index.html +1 -1
  5. package/lib/cli/SetupService.js +92 -5
  6. package/lib/cli/UpgradeService.js +14 -5
  7. package/lib/core/discovery/GenericDiscoverer.js +4 -28
  8. package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +246 -0
  9. package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +80 -0
  10. package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
  11. package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +600 -0
  12. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +125 -342
  13. package/lib/external/mcp/handlers/bootstrap/refine.js +362 -0
  14. package/lib/external/mcp/handlers/bootstrap.js +6 -590
  15. package/lib/external/mcp/handlers/browse.js +119 -9
  16. package/lib/external/mcp/handlers/guard.js +25 -6
  17. package/lib/external/mcp/handlers/search.js +56 -24
  18. package/lib/http/routes/guardRules.js +9 -17
  19. package/lib/injection/ServiceContainer.js +12 -3
  20. package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
  21. package/lib/platform/ios/xcode/XcodeIntegration.js +40 -659
  22. package/lib/platform/ios/xcode/XcodeWriteUtils.js +220 -0
  23. package/lib/service/chat/ChatAgent.js +39 -418
  24. package/lib/service/chat/ChatAgentPrompts.js +149 -0
  25. package/lib/service/chat/ChatAgentTasks.js +297 -0
  26. package/lib/service/chat/tools/_shared.js +61 -0
  27. package/lib/service/chat/tools/ai-analysis.js +284 -0
  28. package/lib/service/chat/tools/ast-graph.js +681 -0
  29. package/lib/service/chat/tools/composite.js +496 -0
  30. package/lib/service/chat/tools/guard.js +265 -0
  31. package/lib/service/chat/tools/index.js +250 -0
  32. package/lib/service/chat/tools/infrastructure.js +222 -0
  33. package/lib/service/chat/tools/knowledge-graph.js +234 -0
  34. package/lib/service/chat/tools/lifecycle.js +469 -0
  35. package/lib/service/chat/tools/project-access.js +923 -0
  36. package/lib/service/chat/tools/query.js +264 -0
  37. package/lib/service/chat/tools.js +14 -3994
  38. package/lib/service/cursor/AgentInstructionsGenerator.js +395 -0
  39. package/lib/service/cursor/CursorDeliveryPipeline.js +70 -11
  40. package/lib/service/cursor/FileProtection.js +116 -0
  41. package/lib/service/cursor/KnowledgeCompressor.js +61 -11
  42. package/lib/service/cursor/SkillsSyncer.js +5 -3
  43. package/lib/service/cursor/TopicClassifier.js +19 -3
  44. package/lib/service/guard/ExclusionManager.js +26 -2
  45. package/lib/service/guard/GuardCheckEngine.js +38 -370
  46. package/lib/service/guard/GuardCodeChecks.js +362 -0
  47. package/lib/service/guard/GuardCrossFileChecks.js +307 -0
  48. package/lib/service/guard/GuardPatternUtils.js +180 -0
  49. package/lib/service/guard/GuardService.js +80 -38
  50. package/lib/service/module/ModuleService.js +1 -0
  51. package/lib/service/search/SearchEngine.js +10 -2
  52. package/lib/service/wiki/WikiGenerator.js +226 -1532
  53. package/lib/service/wiki/WikiRenderers.js +1878 -0
  54. package/lib/service/wiki/WikiUtils.js +907 -0
  55. package/lib/shared/LanguageService.js +299 -0
  56. package/package.json +1 -1
@@ -0,0 +1,222 @@
1
+ /**
2
+ * infrastructure.js — 基础设施类工具 (7)
3
+ *
4
+ * 29. graph_impact_analysis 知识图谱影响分析
5
+ * 30. rebuild_index 向量索引重建
6
+ * 31. query_audit_log 审计日志查询
7
+ * 32. load_skill 加载 Skill 文档
8
+ * 33. create_skill 创建项目级 Skill
9
+ * 34. suggest_skills 推荐创建 Skill
10
+ * 34. bootstrap_knowledge 冷启动知识库
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import Logger from '../../../infrastructure/logging/Logger.js';
16
+ import { SKILLS_DIR, PROJECT_SKILLS_DIR } from './_shared.js';
17
+
18
+ // ────────────────────────────────────────────────────────────
19
+ // 29. graph_impact_analysis
20
+ // ────────────────────────────────────────────────────────────
21
+ export const graphImpactAnalysis = {
22
+ name: 'graph_impact_analysis',
23
+ description: '知识图谱影响范围分析 — 查找修改某个 Recipe 后可能受影响的所有下游依赖。',
24
+ parameters: {
25
+ type: 'object',
26
+ properties: {
27
+ recipeId: { type: 'string', description: 'Recipe ID' },
28
+ maxDepth: { type: 'number', description: '最大深度,默认 3' },
29
+ },
30
+ required: ['recipeId'],
31
+ },
32
+ handler: async (params, ctx) => {
33
+ const kgService = ctx.container.get('knowledgeGraphService');
34
+ const impacted = kgService.getImpactAnalysis(params.recipeId, 'recipe', params.maxDepth || 3);
35
+ return { recipeId: params.recipeId, impactedCount: impacted.length, impacted };
36
+ },
37
+ };
38
+
39
+ // ────────────────────────────────────────────────────────────
40
+ // 30. rebuild_index
41
+ // ────────────────────────────────────────────────────────────
42
+ export const rebuildIndex = {
43
+ name: 'rebuild_index',
44
+ description:
45
+ '向量索引重建 — 重新扫描 Recipe 文件并更新向量索引(用于索引过期或新增大量 Recipe 后)。',
46
+ parameters: {
47
+ type: 'object',
48
+ properties: {
49
+ force: { type: 'boolean', description: '强制重建(跳过增量检测),默认 false' },
50
+ dryRun: { type: 'boolean', description: '仅预览不实际写入,默认 false' },
51
+ },
52
+ },
53
+ handler: async (params, ctx) => {
54
+ const pipeline = ctx.container.get('indexingPipeline');
55
+ return pipeline.run({ force: params.force || false, dryRun: params.dryRun || false });
56
+ },
57
+ };
58
+
59
+ // ────────────────────────────────────────────────────────────
60
+ // 31. query_audit_log
61
+ // ────────────────────────────────────────────────────────────
62
+ export const queryAuditLog = {
63
+ name: 'query_audit_log',
64
+ description: '审计日志查询 — 查看系统操作历史(谁在什么时间做了什么操作)。',
65
+ parameters: {
66
+ type: 'object',
67
+ properties: {
68
+ action: {
69
+ type: 'string',
70
+ description: '按操作类型过滤 (create_candidate/approve_candidate/create_guard_rule 等)',
71
+ },
72
+ actor: { type: 'string', description: '按操作者过滤' },
73
+ limit: { type: 'number', description: '返回数量,默认 20' },
74
+ },
75
+ },
76
+ handler: async (params, ctx) => {
77
+ const auditLogger = ctx.container.get('auditLogger');
78
+ const { action, actor, limit = 20 } = params;
79
+
80
+ if (actor) {
81
+ return auditLogger.getByActor(actor, limit);
82
+ }
83
+ if (action) {
84
+ return auditLogger.getByAction(action, limit);
85
+ }
86
+ return auditLogger.getStats();
87
+ },
88
+ };
89
+
90
+ // ────────────────────────────────────────────────────────────
91
+ // 32. load_skill — 按需加载 Agent Skill 文档
92
+ // ────────────────────────────────────────────────────────────
93
+ export const loadSkill = {
94
+ name: 'load_skill',
95
+ description:
96
+ '加载指定的 Agent Skill 文档,获取领域操作指南和最佳实践参考。可用于冷启动指南 (autosnippet-coldstart)、语言参考 (autosnippet-reference-swift/objc/jsts/python/java/kotlin/go/dart/rust) 等。',
97
+ parameters: {
98
+ type: 'object',
99
+ properties: {
100
+ skillName: {
101
+ type: 'string',
102
+ description: 'Skill 目录名(如 autosnippet-coldstart, autosnippet-reference-swift, autosnippet-reference-go 等)',
103
+ },
104
+ },
105
+ required: ['skillName'],
106
+ },
107
+ handler: async (params) => {
108
+ // 项目级 Skills 优先(覆盖同名内置 Skill)
109
+ const projectSkillPath = path.join(PROJECT_SKILLS_DIR, params.skillName, 'SKILL.md');
110
+ const builtinSkillPath = path.join(SKILLS_DIR, params.skillName, 'SKILL.md');
111
+ const skillPath = fs.existsSync(projectSkillPath) ? projectSkillPath : builtinSkillPath;
112
+ try {
113
+ const content = fs.readFileSync(skillPath, 'utf8');
114
+ const source = skillPath === projectSkillPath ? 'project' : 'builtin';
115
+ return { skillName: params.skillName, source, content };
116
+ } catch {
117
+ const available = new Set();
118
+ try {
119
+ fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
120
+ .filter((d) => d.isDirectory())
121
+ .forEach((d) => available.add(d.name));
122
+ } catch {}
123
+ try {
124
+ fs.readdirSync(PROJECT_SKILLS_DIR, { withFileTypes: true })
125
+ .filter((d) => d.isDirectory())
126
+ .forEach((d) => available.add(d.name));
127
+ } catch {}
128
+ return { error: `Skill "${params.skillName}" not found`, availableSkills: [...available] };
129
+ }
130
+ },
131
+ };
132
+
133
+ // ────────────────────────────────────────────────────────────
134
+ // 33. create_skill — 创建项目级 Skill
135
+ // ────────────────────────────────────────────────────────────
136
+ export const createSkillTool = {
137
+ name: 'create_skill',
138
+ description:
139
+ '创建项目级 Skill 文档,写入 AutoSnippet/skills/<name>/SKILL.md。Skill 是 Agent 的领域知识增强文档。创建后自动更新编辑器索引。',
140
+ parameters: {
141
+ type: 'object',
142
+ properties: {
143
+ name: {
144
+ type: 'string',
145
+ description: 'Skill 名称(kebab-case,如 my-auth-guide),3-64 字符',
146
+ },
147
+ description: { type: 'string', description: 'Skill 一句话描述(写入 frontmatter)' },
148
+ content: { type: 'string', description: 'Skill 正文内容(Markdown 格式,不含 frontmatter)' },
149
+ overwrite: { type: 'boolean', description: '如果同名 Skill 已存在,是否覆盖(默认 false)' },
150
+ },
151
+ required: ['name', 'description', 'content'],
152
+ },
153
+ handler: async (params, ctx) => {
154
+ const { createSkill } = await import('../../../external/mcp/handlers/skill.js');
155
+ // 根据 ChatAgent 的 source 推断 createdBy
156
+ const createdBy = ctx?.source === 'system' ? 'system-ai' : 'user-ai';
157
+ const raw = createSkill(null, { ...params, createdBy });
158
+ try {
159
+ return JSON.parse(raw);
160
+ } catch {
161
+ return { success: false, error: raw };
162
+ }
163
+ },
164
+ };
165
+
166
+ // ────────────────────────────────────────────────────────────
167
+ // 34. suggest_skills — 基于使用模式推荐 Skill 创建
168
+ // ────────────────────────────────────────────────────────────
169
+ export const suggestSkills = {
170
+ name: 'suggest_skills',
171
+ description:
172
+ '基于项目使用模式分析,推荐创建 Skill。分析 Guard 违规频率、Memory 偏好积累、Recipe 分布缺口、候选积压率。返回推荐列表(含 name/description/rationale/priority),可据此直接调用 create_skill 创建。',
173
+ parameters: {
174
+ type: 'object',
175
+ properties: {},
176
+ required: [],
177
+ },
178
+ handler: async (_params, ctx) => {
179
+ const { SkillAdvisor } = await import('../../skills/SkillAdvisor.js');
180
+ const database = ctx?.container?.get?.('database') || null;
181
+ const projectRoot = ctx?.projectRoot || process.cwd();
182
+ const advisor = new SkillAdvisor(projectRoot, { database });
183
+ return advisor.suggest();
184
+ },
185
+ };
186
+
187
+ // ────────────────────────────────────────────────────────────
188
+ // 34. bootstrap_knowledge — 冷启动知识库初始化
189
+ // ────────────────────────────────────────────────────────────
190
+ export const bootstrapKnowledgeTool = {
191
+ name: 'bootstrap_knowledge',
192
+ description:
193
+ '冷启动知识库初始化(纯启发式,不使用 AI): SPM Target 扫描 → 依赖图谱 → Guard 审计 → 9 维度 Candidate 自动创建。支持 Skill 增强维度定义。产出为初稿候选,后续由 DAG pipeline 自动编排 AI 增强(enrich → refine)。',
194
+ parameters: {
195
+ type: 'object',
196
+ properties: {
197
+ maxFiles: { type: 'number', description: '最大扫描文件数,默认 500' },
198
+ skipGuard: { type: 'boolean', description: '是否跳过 Guard 审计,默认 false' },
199
+ contentMaxLines: { type: 'number', description: '每文件读取最大行数,默认 120' },
200
+ loadSkills: {
201
+ type: 'boolean',
202
+ description: '是否加载 Skills 增强维度定义(推荐开启),默认 true',
203
+ },
204
+ },
205
+ },
206
+ handler: async (params, ctx) => {
207
+ const { bootstrapKnowledge } = await import('../../../external/mcp/handlers/bootstrap.js');
208
+ const logger = Logger.getInstance();
209
+ const result = await bootstrapKnowledge(
210
+ { container: ctx.container, logger },
211
+ {
212
+ maxFiles: params.maxFiles || 500,
213
+ skipGuard: params.skipGuard || false,
214
+ contentMaxLines: params.contentMaxLines || 120,
215
+ loadSkills: params.loadSkills ?? true,
216
+ }
217
+ );
218
+ // bootstrapKnowledge 返回 envelope JSON string,解析提取 data
219
+ const parsed = typeof result === 'string' ? JSON.parse(result) : result;
220
+ return parsed?.data || parsed;
221
+ },
222
+ };
@@ -0,0 +1,234 @@
1
+ /**
2
+ * knowledge-graph.js — 知识图谱类工具 (3)
3
+ *
4
+ * 10. check_duplicate 候选查重
5
+ * 11. discover_relations AI 关系发现
6
+ * 12. add_graph_edge 手动添加图谱边
7
+ */
8
+
9
+ import { findSimilarRecipes } from '../../candidate/SimilarityService.js';
10
+
11
+ // ────────────────────────────────────────────────────────────
12
+ // 10. check_duplicate
13
+ // ────────────────────────────────────────────────────────────
14
+ export const checkDuplicate = {
15
+ name: 'check_duplicate',
16
+ description:
17
+ '候选查重 — 检测候选代码是否与已有 Recipe 重复(基于标题/摘要/代码的 Jaccard 相似度)。',
18
+ parameters: {
19
+ type: 'object',
20
+ properties: {
21
+ candidate: { type: 'object', description: '候选对象 { title, summary, code, usageGuide }' },
22
+ candidateId: { type: 'string', description: '或提供候选 ID,从数据库读取' },
23
+ projectRoot: { type: 'string', description: '项目根目录(可选,默认当前项目)' },
24
+ threshold: { type: 'number', description: '相似度阈值,默认 0.5' },
25
+ },
26
+ },
27
+ handler: async (params, ctx) => {
28
+ let cand = params.candidate;
29
+ const projectRoot = params.projectRoot || ctx.projectRoot;
30
+ const threshold = params.threshold ?? 0.5;
31
+
32
+ // 如果提供 candidateId,从数据库读取条目信息
33
+ if (!cand && params.candidateId) {
34
+ try {
35
+ const knowledgeService = ctx.container.get('knowledgeService');
36
+ const found = await knowledgeService.get(params.candidateId);
37
+ if (found) {
38
+ const json = typeof found.toJSON === 'function' ? found.toJSON() : found;
39
+ cand = {
40
+ title: json.title || '',
41
+ summary: json.description || '',
42
+ code: json.content?.pattern || '',
43
+ usageGuide: '',
44
+ };
45
+ }
46
+ } catch {
47
+ /* ignore */
48
+ }
49
+ }
50
+
51
+ if (!cand) {
52
+ return { similar: [], message: 'No candidate provided' };
53
+ }
54
+
55
+ const similar = findSimilarRecipes(projectRoot, cand, {
56
+ threshold,
57
+ topK: 10,
58
+ });
59
+
60
+ return {
61
+ similar,
62
+ hasDuplicate: similar.some((s) => s.similarity >= 0.7),
63
+ highestSimilarity: similar.length > 0 ? similar[0].similarity : 0,
64
+ _meta: {
65
+ confidence: similar.length === 0 ? 'none' : similar[0].similarity >= 0.7 ? 'high' : 'low',
66
+ hint:
67
+ similar.length === 0
68
+ ? '未发现相似 Recipe,可放心提交。'
69
+ : similar[0].similarity >= 0.7
70
+ ? '发现高度相似 Recipe,建议人工审核是否重复。'
71
+ : '有低相似度匹配,大概率不是重复。',
72
+ },
73
+ };
74
+ },
75
+ };
76
+
77
+ // ────────────────────────────────────────────────────────────
78
+ // 11. discover_relations
79
+ // ────────────────────────────────────────────────────────────
80
+ export const discoverRelations = {
81
+ name: 'discover_relations',
82
+ description:
83
+ 'AI 知识图谱关系发现 — 分析 Recipe 对之间的潜在关系(requires/extends/enforces/calls 等),并自动写入知识图谱。',
84
+ parameters: {
85
+ type: 'object',
86
+ properties: {
87
+ recipePairs: {
88
+ type: 'array',
89
+ items: {
90
+ type: 'object',
91
+ properties: {
92
+ a: {
93
+ type: 'object',
94
+ properties: {
95
+ id: { type: 'string' },
96
+ title: { type: 'string' },
97
+ category: { type: 'string' },
98
+ code: { type: 'string' },
99
+ },
100
+ },
101
+ b: {
102
+ type: 'object',
103
+ properties: {
104
+ id: { type: 'string' },
105
+ title: { type: 'string' },
106
+ category: { type: 'string' },
107
+ code: { type: 'string' },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ description:
113
+ 'Recipe 对数组 [{ a: {id, title, category, code}, b: {id, title, category, code} }]',
114
+ },
115
+ dryRun: { type: 'boolean', description: '仅分析不写入,默认 false' },
116
+ },
117
+ required: ['recipePairs'],
118
+ },
119
+ handler: async (params, ctx) => {
120
+ if (!ctx.aiProvider) {
121
+ return { error: 'AI provider not available' };
122
+ }
123
+
124
+ const { recipePairs, dryRun = false } = params;
125
+ if (!recipePairs || recipePairs.length === 0) {
126
+ return { relations: [] };
127
+ }
128
+
129
+ // 构建 LLM prompt
130
+ const pairsText = recipePairs
131
+ .map(
132
+ (p, i) => `
133
+ --- Pair #${i + 1} ---
134
+ Recipe A [${p.a.id}]: ${p.a.title} (${p.a.category}/${p.a.language || ''})
135
+ ${p.a.code ? `Code: ${p.a.code.substring(0, 300)}` : ''}
136
+
137
+ Recipe B [${p.b.id}]: ${p.b.title} (${p.b.category}/${p.b.language || ''})
138
+ ${p.b.code ? `Code: ${p.b.code.substring(0, 300)}` : ''}`
139
+ )
140
+ .join('\n');
141
+
142
+ const prompt = `# Role
143
+ You are a Software Architect analyzing relationships between code recipes (knowledge units).
144
+
145
+ # Goal
146
+ For each Recipe pair below, determine if there is a meaningful relationship.
147
+
148
+ # Relationship Types
149
+ - requires: A needs B to function
150
+ - extends: A builds upon / enriches B
151
+ - enforces: A enforces rules defined in B
152
+ - depends_on: A depends on B
153
+ - inherits: A inherits from B (class/protocol)
154
+ - implements: A implements interface/protocol defined in B
155
+ - calls: A calls API defined in B
156
+ - prerequisite: B must be learned/applied before A
157
+ - none: No meaningful relationship
158
+
159
+ # Output
160
+ Return a JSON array. For each pair with a relationship (skip "none"):
161
+ { "index": 0, "from_id": "...", "to_id": "...", "relation": "requires", "confidence": 0.85, "reason": "A uses the network client defined in B" }
162
+
163
+ Return ONLY a JSON array. No markdown, no extra text. Return [] if no relationships found.
164
+
165
+ # Recipe Pairs
166
+ ${pairsText}`;
167
+
168
+ const parsed = await ctx.aiProvider.chatWithStructuredOutput(prompt, {
169
+ openChar: '[',
170
+ closeChar: ']',
171
+ temperature: 0.2,
172
+ });
173
+ const relations = Array.isArray(parsed) ? parsed : [];
174
+
175
+ // 写入知识图谱(除非 dryRun)
176
+ if (!dryRun && relations.length > 0) {
177
+ try {
178
+ const kgService = ctx.container.get('knowledgeGraphService');
179
+ for (const rel of relations) {
180
+ if (rel.from_id && rel.to_id && rel.relation && rel.relation !== 'none') {
181
+ kgService.addEdge(rel.from_id, 'recipe', rel.to_id, 'recipe', rel.relation, {
182
+ confidence: rel.confidence || 0.5,
183
+ reason: rel.reason || '',
184
+ source: 'ai-discovery',
185
+ });
186
+ }
187
+ }
188
+ } catch {
189
+ /* KG not available */
190
+ }
191
+ }
192
+
193
+ return {
194
+ analyzed: recipePairs.length,
195
+ relations: relations.filter((r) => r.relation !== 'none'),
196
+ written: dryRun ? 0 : relations.filter((r) => r.relation !== 'none').length,
197
+ };
198
+ },
199
+ };
200
+
201
+ // ────────────────────────────────────────────────────────────
202
+ // 12. add_graph_edge
203
+ // ────────────────────────────────────────────────────────────
204
+ export const addGraphEdge = {
205
+ name: 'add_graph_edge',
206
+ description: '手动添加知识图谱关系边(从 A 到 B 的关系)。',
207
+ parameters: {
208
+ type: 'object',
209
+ properties: {
210
+ fromId: { type: 'string', description: '源节点 ID' },
211
+ fromType: { type: 'string', description: '源节点类型 (recipe/candidate)' },
212
+ toId: { type: 'string', description: '目标节点 ID' },
213
+ toType: { type: 'string', description: '目标节点类型 (recipe/candidate)' },
214
+ relation: {
215
+ type: 'string',
216
+ description:
217
+ '关系类型 (requires/extends/enforces/depends_on/inherits/implements/calls/prerequisite)',
218
+ },
219
+ weight: { type: 'number', description: '权重 0-1,默认 1.0' },
220
+ },
221
+ required: ['fromId', 'fromType', 'toId', 'toType', 'relation'],
222
+ },
223
+ handler: async (params, ctx) => {
224
+ const kgService = ctx.container.get('knowledgeGraphService');
225
+ return kgService.addEdge(
226
+ params.fromId,
227
+ params.fromType,
228
+ params.toId,
229
+ params.toType,
230
+ params.relation,
231
+ { weight: params.weight || 1.0, source: 'manual' }
232
+ );
233
+ },
234
+ };