autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
244 lines (243 loc) • 8.26 kB
TypeScript
/**
* SetupService — 项目初始化服务(V2 重构版)
*
* 一键初始化 AutoSnippet V2 工作空间,5 步完成:
*
* Step 1 .autosnippet/ 运行时目录 + config.json + .gitignore
* Step 2 AutoSnippet/ 知识库目录结构 + AutoSnippet/recipes/(有 --repo 则 clone,无则为普通目录)
* Step 3 IDE 集成(VSCode MCP + Cursor MCP + copilot-instructions + cursor-rules
* + skills-template + cursor-workflow + claude-hooks + guard-ci + pre-commit-hook)
* Step 4 SQLite 数据库 + V1 数据迁移
* Step 5 平台相关初始化(macOS → Xcode Snippets)
*
* ═══════════════════════════════════════════════════════════
*
* 数据架构(核心数据在子仓库,受 git 权限保护)
* ─────────────────────────────────────────────
* AutoSnippet/ (知识库根目录)
* ├─ constitution.yaml 权限宪法:角色 + 权限矩阵 + 治理规则 + 能力探测
* ├─ boxspec.json 项目规格定义
* ├─ recipes/ Git 子仓库 = 唯一真实来源 Source of Truth
* │ └─ *.md 统一知识实体(代码规范/模式/架构/调用链/数据流/...)
* ├─ candidates/ 候选知识(待审批)
* ├─ skills/ Project Skills(冷启动自动生成 + 手动创建)
* └─ README.md
*
* .autosnippet/ (运行时缓存,gitignored)
* ├─ config.json 项目配置(含 core.subRepoDir 子仓库路径)
* ├─ autosnippet.db SQLite 运行时缓存(从子仓库同步 + candidates/snippets/audit)
* ├─ context/ 向量索引缓存
* └─ logs/ 运行日志
*
* 数据流
* ─────
* 写入:编辑子仓库文件 → git push(需权限)→ asd sync → 更新 DB 缓存
* 读取:查询 SQLite(快速索引)
* 核心数据(统一 Recipe 实体)修改必须经过 git,普通用户无法绕过
*
* 权限模型(三层架构)
* ──────────────────
* ① 能力层 WriteGuard — git push --dry-run:探测子仓库写权限(物理信号)
* ② 角色层 Permission — constitution.yaml 角色权限矩阵(逻辑裁决)
* ③ 治理层 Constitution — constitution.yaml 优先级规则引擎(业务裁决)
*
* 子仓库 git 权限只是"一种能力(capability)",最终裁决权在 Constitution YAML。
*/
export declare class SetupService {
force: boolean;
projectName: string;
projectRoot: string;
_results: Array<{
step: number;
label: string;
ok: boolean;
error?: string;
}> | null;
candidatesDir: string;
coreDir: string;
dbPath: string;
recipesDir: string;
runtimeDir: string;
seed: boolean;
skillsDir: string;
/** 子仓库相对路径(相对于 projectRoot),如 'AutoSnippet/recipes' */
subRepoDir: string;
/** 子仓库绝对路径 */
subRepoPath: string;
/** 子仓库远程仓库 URL(为空则 recipes/ 作为普通目录随主仓库提交) */
subRepoUrl: string | undefined;
/**
* @param options
*/
constructor(options: {
projectRoot: string;
force?: boolean;
seed?: boolean;
/** 自定义子仓库相对路径(默认 'AutoSnippet/recipes') */
subRepoDir?: string;
/** 子仓库远程仓库 URL(提供则 clone,不提供则 recipes/ 为普通目录) */
subRepoUrl?: string;
});
getSteps(): ({
label: string;
fn: () => {
created: string;
};
} | {
label: string;
fn: () => {
coreInit: boolean;
alreadyRepo: boolean;
subRepoPath: string;
hasUrl: boolean;
};
} | {
label: string;
fn: () => {
configured: string[];
};
} | {
label: string;
fn: () => Promise<{
dbPath: string;
}>;
} | {
label: string;
fn: () => Promise<{
skipped: boolean;
}>;
} | {
label: string;
fn: () => Promise<{
status: string;
reason: string;
hint: string;
indexed?: undefined;
skipped?: undefined;
errors?: undefined;
error?: undefined;
} | {
status: string;
reason: string;
hint?: undefined;
indexed?: undefined;
skipped?: undefined;
errors?: undefined;
error?: undefined;
} | {
status: string;
indexed: number;
skipped: number;
errors: number;
reason?: undefined;
hint?: undefined;
error?: undefined;
} | {
status: string;
error: string;
hint: string;
reason?: undefined;
indexed?: undefined;
skipped?: undefined;
errors?: undefined;
}>;
})[];
run(): Promise<{
step: number;
label: string;
ok: boolean;
error?: string;
}[]>;
/** 格式化步骤结果的简要信息 */
private _formatStepDetail;
printSummary(): void;
stepRuntime(): {
created: string;
};
stepCoreRepo(): {
coreInit: boolean;
alreadyRepo: boolean;
subRepoPath: string;
hasUrl: boolean;
};
/** 写入 constitution.yaml(优先从模板复制) */
private _writeConstitution;
/** 写入 boxspec.json */
private _writeBoxspec;
/** 复制 _template.md 到 recipes/ */
private _copyRecipeTemplate;
/** 复制示例 Recipe(冷启动推荐) */
private _copySeedRecipes;
/** 写入核心目录 README */
private _writeCoreReadme;
stepIDE(): {
configured: string[];
};
stepDatabase(): Promise<{
dbPath: string;
}>;
/**
* 从 AutoSnippet/recipes/*.md + candidates/*.md 同步到 DB 缓存
* 委托 KnowledgeSyncService 执行全字段同步(setup 场景跳过违规记录)
*/
private _syncRecipesToDB;
stepPlatform(): Promise<{
skipped: boolean;
}>;
/**
* 在项目根目录创建 .env 文件(从 .env.example 复制)
* 如果 .env 已存在则跳过并提示用户手动配置。
*/
private _ensureEnvFile;
/** 在指定目录执行 git 命令 */
private _git;
/** 检查目录中是否有文件(排除 . 和 ..) */
private _hasFiles;
/** 确保子仓库的 remote origin 与给定 URL 一致 */
private _ensureRemote;
/**
* 备份已有文件 → clone → 合并回来(不覆盖远端文件)
* 适用于 recipes/ 有模板文件但还不是 git 仓库的场景
*/
private _cloneWithMerge;
/**
* 尝试初始化向量索引: 检查 embedding provider 可用性,
* 若可用则自动构建初始索引;否则提示用户手动运行 asd embed。
*
* 此步骤为 best-effort: 失败不阻塞 setup 流程。
*/
stepVectorIndex(): Promise<{
status: string;
reason: string;
hint: string;
indexed?: undefined;
skipped?: undefined;
errors?: undefined;
error?: undefined;
} | {
status: string;
reason: string;
hint?: undefined;
indexed?: undefined;
skipped?: undefined;
errors?: undefined;
error?: undefined;
} | {
status: string;
indexed: number;
skipped: number;
errors: number;
reason?: undefined;
hint?: undefined;
error?: undefined;
} | {
status: string;
error: string;
hint: string;
reason?: undefined;
indexed?: undefined;
skipped?: undefined;
errors?: undefined;
}>;
}
export default SetupService;