autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
224 lines (223 loc) • 12.1 kB
JavaScript
/**
* infrastructure.js — 基础设施类工具 (7)
*
* 29. graph_impact_analysis 知识图谱影响分析
* 30. rebuild_index 向量索引重建
* 31. query_audit_log 审计日志查询
* 32. load_skill 加载 Skill 文档
* 33. create_skill 创建项目级 Skill
* 34. suggest_skills 推荐创建 Skill
* 34. bootstrap_knowledge 冷启动知识库
*/
import fs from 'node:fs';
import path from 'node:path';
import Logger from '#infra/logging/Logger.js';
import { PROJECT_SKILLS_DIR, SKILLS_DIR } from './_shared.js';
// ────────────────────────────────────────────────────────────
// 29. graph_impact_analysis
// ────────────────────────────────────────────────────────────
export const graphImpactAnalysis = {
name: 'graph_impact_analysis',
description: '知识图谱影响范围分析 — 查找修改某个 Recipe 后可能受影响的所有下游依赖。',
parameters: {
type: 'object',
properties: {
recipeId: { type: 'string', description: 'Recipe ID' },
maxDepth: { type: 'number', description: '最大深度,默认 3' },
},
required: ['recipeId'],
},
handler: async (params, ctx) => {
const kgService = ctx.container.get('knowledgeGraphService');
const impacted = kgService.getImpactAnalysis(params.recipeId, 'recipe', params.maxDepth || 3);
return { recipeId: params.recipeId, impactedCount: impacted.length, impacted };
},
};
// ────────────────────────────────────────────────────────────
// 30. rebuild_index
// ────────────────────────────────────────────────────────────
export const rebuildIndex = {
name: 'rebuild_index',
description: '向量索引重建 — 重新扫描 Recipe 文件并更新向量索引(用于索引过期或新增大量 Recipe 后)。',
parameters: {
type: 'object',
properties: {
force: { type: 'boolean', description: '强制重建(跳过增量检测),默认 false' },
dryRun: { type: 'boolean', description: '仅预览不实际写入,默认 false' },
},
},
handler: async (params, ctx) => {
const pipeline = ctx.container.get('indexingPipeline');
return pipeline.run({ force: params.force || false, dryRun: params.dryRun || false });
},
};
// ────────────────────────────────────────────────────────────
// 31. query_audit_log
// ────────────────────────────────────────────────────────────
export const queryAuditLog = {
name: 'query_audit_log',
description: '审计日志查询 — 查看系统操作历史(谁在什么时间做了什么操作)。',
parameters: {
type: 'object',
properties: {
action: {
type: 'string',
description: '按操作类型过滤 (create_candidate/approve_candidate/create_guard_rule 等)',
},
actor: { type: 'string', description: '按操作者过滤' },
limit: { type: 'number', description: '返回数量,默认 20' },
},
},
handler: async (params, ctx) => {
const auditLogger = ctx.container.get('auditLogger');
const { action, actor, limit = 20 } = params;
if (actor) {
return auditLogger.getByActor(actor, limit);
}
if (action) {
return auditLogger.getByAction(action, limit);
}
return auditLogger.getStats();
},
};
// ────────────────────────────────────────────────────────────
// 32. load_skill — 按需加载 Agent Skill 文档
// ────────────────────────────────────────────────────────────
export const loadSkill = {
name: 'load_skill',
description: '加载指定的 Agent Skill 文档,获取领域操作指南和最佳实践参考。如知识提交 (autosnippet-create)、规范审计 (autosnippet-guard)、项目标准 (autosnippet-recipes) 等。',
parameters: {
type: 'object',
properties: {
skillName: {
type: 'string',
description: 'Skill 目录名(如 autosnippet-create, autosnippet-guard, autosnippet-recipes 等)',
},
},
required: ['skillName'],
},
handler: async (params) => {
// 项目级 Skills 优先(覆盖同名内置 Skill)
const projectSkillPath = path.join(PROJECT_SKILLS_DIR, params.skillName, 'SKILL.md');
const builtinSkillPath = path.join(SKILLS_DIR, params.skillName, 'SKILL.md');
const skillPath = fs.existsSync(projectSkillPath) ? projectSkillPath : builtinSkillPath;
try {
const content = fs.readFileSync(skillPath, 'utf8');
const source = skillPath === projectSkillPath ? 'project' : 'builtin';
return { skillName: params.skillName, source, content };
}
catch {
const available = new Set();
try {
fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
.filter((d) => d.isDirectory())
.forEach((d) => {
available.add(d.name);
});
}
catch {
/* skip: SKILLS_DIR may not exist */
}
try {
fs.readdirSync(PROJECT_SKILLS_DIR, { withFileTypes: true })
.filter((d) => d.isDirectory())
.forEach((d) => {
available.add(d.name);
});
}
catch {
/* skip: project skills dir may not exist */
}
return { error: `Skill "${params.skillName}" not found`, availableSkills: [...available] };
}
},
};
// ────────────────────────────────────────────────────────────
// 33. create_skill — 创建项目级 Skill
// ────────────────────────────────────────────────────────────
export const createSkillTool = {
name: 'create_skill',
description: '创建项目级 Skill 文档,写入 AutoSnippet/skills/<name>/SKILL.md。Skill 是 Agent 的领域知识增强文档。创建后自动更新编辑器索引。',
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Skill 名称(kebab-case,如 my-auth-guide),3-64 字符',
},
description: { type: 'string', description: 'Skill 一句话描述(写入 frontmatter)' },
content: { type: 'string', description: 'Skill 正文内容(Markdown 格式,不含 frontmatter)' },
overwrite: { type: 'boolean', description: '如果同名 Skill 已存在,是否覆盖(默认 false)' },
},
required: ['name', 'description', 'content'],
},
handler: async (params, ctx) => {
const { createSkill } = await import('#external/mcp/handlers/skill.js');
// 根据 Agent 的 source 推断 createdBy
const createdBy = ctx?.source === 'system' ? 'system-ai' : 'user-ai';
const raw = createSkill(null, { ...params, createdBy });
try {
return JSON.parse(raw);
}
catch {
return { success: false, error: raw };
}
},
};
// ────────────────────────────────────────────────────────────
// 34. suggest_skills — 基于使用模式推荐 Skill 创建
// ────────────────────────────────────────────────────────────
export const suggestSkills = {
name: 'suggest_skills',
description: '基于项目使用模式分析,推荐创建 Skill。分析 Guard 违规频率、Memory 偏好积累、Recipe 分布缺口、候选积压率。返回推荐列表(含 name/description/rationale/priority),可据此直接调用 create_skill 创建。',
parameters: {
type: 'object',
properties: {},
required: [],
},
handler: async (_params, ctx) => {
const { SkillAdvisor } = await import('#service/skills/SkillAdvisor.js');
const projectRoot = ctx?.projectRoot || process.cwd();
const knowledgeRepo = ctx?.container?.get?.('knowledgeRepository') || null;
const auditRepo = ctx?.container?.get?.('auditRepository') || null;
const advisor = new SkillAdvisor(projectRoot, { knowledgeRepo, auditRepo });
return advisor.suggest();
},
};
// ────────────────────────────────────────────────────────────
// 34. bootstrap_knowledge — 冷启动知识库初始化
// ────────────────────────────────────────────────────────────
export const bootstrapKnowledgeTool = {
name: 'bootstrap_knowledge',
description: '冷启动知识库初始化(纯启发式,不使用 AI): SPM Target 扫描 → 依赖图谱 → Guard 审计 → 9 维度 Candidate 自动创建。支持 Skill 增强维度定义。产出为初稿候选,后续由 DAG pipeline 自动编排 AI 增强(enrich → refine)。',
parameters: {
type: 'object',
properties: {
maxFiles: { type: 'number', description: '最大扫描文件数,默认 500' },
skipGuard: { type: 'boolean', description: '是否跳过 Guard 审计,默认 false' },
contentMaxLines: { type: 'number', description: '每文件读取最大行数,默认 120' },
loadSkills: {
type: 'boolean',
description: '是否加载 Skills 增强维度定义(推荐开启),默认 true',
},
skipAsyncFill: {
type: 'boolean',
description: '跳过异步 AI 填充(CLI 非 --wait 模式下使用,避免 DB 断连)',
},
},
},
handler: async (params, ctx) => {
const { bootstrapKnowledge } = await import('#external/mcp/handlers/bootstrap-internal.js');
const logger = Logger.getInstance();
const result = await bootstrapKnowledge({ container: ctx.container, logger }, {
maxFiles: params.maxFiles || 500,
skipGuard: params.skipGuard || false,
contentMaxLines: params.contentMaxLines || 120,
loadSkills: params.loadSkills ?? true,
skipAsyncFill: params.skipAsyncFill || false,
});
// bootstrapKnowledge 返回 envelope JSON string,解析提取 data
const parsed = typeof result === 'string' ? JSON.parse(result) : result;
return parsed?.data || parsed;
},
};