autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
381 lines (380 loc) • 12 kB
TypeScript
/**
* WikiGenerator — Repo Wiki 生成引擎 (V3 Content-First)
*
* 自动分析项目代码结构,生成结构化的项目文档 Wiki。
* 结合 AutoSnippet 的 AST 深度分析能力(ProjectGraph、CodeEntityGraph、SPM 依赖图)
* 做到深层代码洞察。
*
* V3 核心设计: "内容驱动 + AI 优先"
* 1. 数据收集 (Scan → AST → SPM → KB)
* 2. 主题发现 — 分析数据丰富度,动态决定生成哪些文章
* 3. AI 优先撰写 — 直接由 AI 写完整文章,非骨架+润色
* 4. 质量关卡 — 内容不足 MIN_ARTICLE_CHARS 则跳过该文章
* 5. 降级保底 — AI 不可用时使用丰富模板内容
*
* Wiki 文档结构 (动态生成,按项目特征而异):
* AutoSnippet/wiki/
* ├── index.md — 项目概述 (始终生成)
* ├── architecture.md — 架构总览 (多模块项目)
* ├── getting-started.md — 快速上手 (有构建系统时)
* ├── modules/
* │ ├── {ModuleName}.md — 模块深度文档 (仅内容丰富的模块)
* │ └── ...
* ├── patterns.md — 代码模式 (有知识库 Recipe 时)
* ├── patterns/ — 按分类拆分 (Recipe 较多时)
* │ └── {category}.md
* ├── protocols.md — 协议参考 (协议较多时)
* ├── documents/ — 同步的 Cursor 端文档
* └── meta.json — Wiki 元数据
*
* @module WikiGenerator
*/
import { type WikiData } from './WikiRenderers.js';
/** Resolved WikiGenerator options */
interface WikiOptions {
wikiDir: string;
language: string;
maxFiles: number;
includeRecipes: boolean;
includeDepGraph: boolean;
includeComponents: boolean;
[key: string]: unknown;
}
/** WikiGenerator constructor dependencies */
export interface WikiDeps {
projectRoot: string;
moduleService?: WikiModuleService | null;
knowledgeService?: WikiKnowledgeService | null;
projectGraph?: WikiProjectGraph | null;
codeEntityGraph?: Record<string, unknown> | null;
aiProvider?: WikiAiProvider | null;
onProgress?: (phase: string, progress: number, message: string) => void;
options?: Partial<WikiOptions>;
[key: string]: unknown;
}
/** Minimal ProjectGraph interface */
export interface WikiProjectGraph {
getOverview(): Record<string, unknown>;
getAllClassNames(): string[];
getAllProtocolNames(): string[];
getClassInfo(name: string): {
filePath?: string;
} | null;
getProtocolInfo(name: string): {
filePath?: string;
} | null;
}
/** Minimal ModuleService interface */
export interface WikiModuleService {
load(): Promise<void>;
listTargets(): Promise<WikiModuleTarget[]>;
getDependencyGraph?(opts: Record<string, unknown>): Promise<unknown>;
getProjectInfo(): Record<string, unknown>;
}
/** Module target descriptor */
interface WikiModuleTarget {
name: string;
path?: string;
type?: string;
dependencies?: unknown[];
info?: {
dependencies?: unknown[];
path?: string;
[key: string]: unknown;
};
[key: string]: unknown;
}
/** Minimal KnowledgeService interface */
export interface WikiKnowledgeService {
list(filter: Record<string, unknown>): Promise<{
data?: Record<string, unknown>[];
items?: Record<string, unknown>[];
[key: string]: unknown;
}>;
getStats?(): Promise<Record<string, unknown> | null>;
}
/** Minimal AiProvider interface */
export interface WikiAiProvider {
chat(prompt: string, options: Record<string, unknown>): Promise<string>;
}
/** Topic descriptor from _discoverTopics */
interface WikiTopic {
id: string;
path: string;
title: string;
type: string;
priority: number;
_moduleData?: Record<string, unknown>;
_patternData?: Record<string, unknown>;
_folderProfiles?: Record<string, unknown>[];
_folderProfile?: Record<string, unknown>;
_allTopics?: WikiTopic[];
[key: string]: unknown;
}
/** File write result */
interface WikiFileResult {
path: string;
hash: string;
size: number;
source?: string;
polished?: boolean;
}
/** Scan project result */
interface ProjectScanInfo {
name: string;
root: string;
buildSystems: {
eco: string;
buildTool: string;
}[];
sourceFiles: string[];
languages: Record<string, number>;
langProfile: Record<string, unknown> | null;
primaryLanguage: string;
hasPackageSwift: boolean;
hasPodfile: boolean;
hasXcodeproj: boolean;
sourceFilesByModule: Record<string, string[]>;
[key: string]: unknown;
}
export declare const WikiPhase: Readonly<{
INIT: "init";
SCAN: "scan";
AST_ANALYZE: "ast-analyze";
SPM_PARSE: "spm-parse";
KNOWLEDGE: "knowledge";
GENERATE: "generate";
AI_COMPOSE: "ai-compose";
SYNC_DOCS: "sync-docs";
DEDUP: "dedup";
FINALIZE: "finalize";
}>;
export declare class WikiGenerator {
projectRoot: string;
wikiDir: string;
_aborted: boolean;
aiProvider: WikiAiProvider | null;
codeEntityGraph: Record<string, unknown> | null;
knowledgeService: WikiKnowledgeService | null;
metaPath: string;
moduleService: WikiModuleService | null;
onProgress: (phase: string, progress: number, message: string) => void;
options: WikiOptions;
projectGraph: WikiProjectGraph | null;
/**
* @param [deps.spmService] 向后兼容
* @param [deps.onProgress] (phase, progress, message) => void
*/
constructor(deps: WikiDeps);
/** 全量生成 Wiki */
generate(): Promise<{
success: boolean;
error: string;
duration: number;
} | {
success: boolean;
filesGenerated: number;
aiComposed: number;
syncedDocs: number;
dedup: {
removed: string[];
kept: number;
};
duration: number;
wikiDir: string;
meta: {
dedup?: {
removed: string[];
kept: number;
} | undefined;
version: string;
generator: string;
generatedAt: string;
duration: number;
projectRoot: string;
language: string;
files: {
polished?: boolean | undefined;
source?: string | undefined;
path: string;
hash: string;
size: number;
}[];
sourceHash: string;
};
}>;
/** 增量更新 — 仅重新生成变更的部分 */
update(): Promise<{
success: boolean;
error: string;
duration: number;
} | {
success: boolean;
filesGenerated: number;
aiComposed: number;
syncedDocs: number;
dedup: {
removed: string[];
kept: number;
};
duration: number;
wikiDir: string;
meta: {
dedup?: {
removed: string[];
kept: number;
} | undefined;
version: string;
generator: string;
generatedAt: string;
duration: number;
projectRoot: string;
language: string;
files: {
polished?: boolean | undefined;
source?: string | undefined;
path: string;
hash: string;
size: number;
}[];
sourceHash: string;
};
} | {
success: boolean;
filesGenerated: number;
duration: number;
upToDate: boolean;
}>;
/** 中止当前生成 */
abort(): void;
/** 获取当前 Wiki 状态 */
getStatus(): {
exists: boolean;
generatedAt?: undefined;
filesCount?: undefined;
version?: undefined;
hasChanges?: undefined;
} | {
exists: boolean;
generatedAt: any;
filesCount: any;
version: any;
hasChanges: boolean;
};
/** 扫描项目基本信息 */
_scanProject(): Promise<ProjectScanInfo>;
/** AST 分析 — 利用已有 ProjectGraph 或重新构建 */
_analyzeAST(): Promise<{
overview: Record<string, unknown>;
classes: string[];
protocols: string[];
classNamesByModule: Record<string, string[]>;
protocolNamesByModule: Record<string, string[]>;
} | {
overview: null;
classes: never[];
protocols: never[];
classNamesByModule: {};
protocolNamesByModule: {};
}>;
/**
* 模块依赖解析
* 通过 moduleService 统一处理所有语言的模块扫描
*/
_parseModules(): Promise<{
targets: never[];
depGraph: null;
projectInfo?: undefined;
} | {
targets: WikiModuleTarget[];
depGraph: unknown;
projectInfo: Record<string, unknown>;
}>;
/** 整合已有知识库 Recipes */
_integrateKnowledge(): Promise<{
recipes: Record<string, unknown>[];
stats: Record<string, unknown> | null;
}>;
/**
* V3 内容驱动的主题发现
*
* 核心原则:
* - 没有固定的文件列表 — 所有文章都由数据丰富度驱动
* - 跳过数据不足的主题(避免空文档)
* - 不同的项目产出不同数量/类型的文章
*
* @returns >}
*/
_discoverTopics(projectInfo: ProjectScanInfo, astInfo: Record<string, unknown>, moduleInfo: {
targets: WikiModuleTarget[];
depGraph?: unknown;
projectInfo?: Record<string, unknown>;
}, knowledgeInfo: {
recipes: Record<string, unknown>[];
stats: Record<string, unknown> | null;
}): WikiTopic[];
/**
* V3 AI-first 文章撰写
*
* 对每个发现的主题:
* 1. 优先使用 AI 撰写完整文章 (非骨架增强!)
* 2. AI 不可用时使用丰富的模板内容
* 3. 质量关卡: 最终内容不足 MIN_ARTICLE_CHARS 则跳过
*
* @param topics _discoverTopics() 的输出
* @param structuredData { projectInfo, astInfo, moduleInfo, knowledgeInfo }
* @returns >>}
*/
_composeArticles(topics: WikiTopic[], structuredData: WikiData): Promise<WikiFileResult[]>;
/**
* 同步 Cursor 端保存的 MD 到 wiki 目录
*
* 同步源:
* 1. .cursor/skills/autosnippet-devdocs/references/ (*.md) → wiki/documents/
*
* @returns >}
*/
_syncCursorDocs(): WikiFileResult[];
/** 为同步目录生成索引页 */
_generateSyncIndex(synced: WikiFileResult[], isZh: boolean): void;
_emit(phase: string, progress: number, message: string): void;
_ensureDir(dir: string): void;
_writeFile(relativePath: string, content: string): WikiFileResult;
_writeMeta(files: WikiFileResult[], startTime: number, dedupResult: {
removed: string[];
kept: number;
} | null): {
dedup?: {
removed: string[];
kept: number;
} | undefined;
version: string;
generator: string;
generatedAt: string;
duration: number;
projectRoot: string;
language: string;
files: {
polished?: boolean | undefined;
source?: string | undefined;
path: string;
hash: string;
size: number;
}[];
sourceHash: string;
};
_readMeta(): any;
/** 检测源码是否有变更(简化:对比 sourceHash) */
_detectChanges(meta: {
sourceHash?: string;
[key: string]: unknown;
}): boolean;
/** 计算项目源文件的简易 hash(基于文件名列表 + 总大小) */
_computeSourceHash(): string;
_abortedResult(): {
success: boolean;
error: string;
duration: number;
};
}
export default WikiGenerator;