autosnippet 3.3.4 → 3.3.6
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.
- package/README.md +174 -83
- package/config/constitution.yaml +2 -0
- package/dashboard/dist/assets/icons-D1aVZYFW.js +1 -0
- package/dashboard/dist/assets/index-CxHOu8Hd.css +1 -0
- package/dashboard/dist/assets/index-DDdAOpYT.js +128 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/api-server.js +1 -0
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +136 -9
- package/dist/lib/agent/AgentFactory.d.ts +0 -17
- package/dist/lib/agent/AgentFactory.js +1 -25
- package/dist/lib/agent/capabilities.d.ts +11 -0
- package/dist/lib/agent/capabilities.js +29 -5
- package/dist/lib/agent/context/ExplorationTracker.js +10 -1
- package/dist/lib/agent/context/exploration/ExplorationStrategies.d.ts +2 -0
- package/dist/lib/agent/context/exploration/ExplorationStrategies.js +2 -2
- package/dist/lib/agent/domain/insight-analyst.d.ts +47 -3
- package/dist/lib/agent/domain/insight-analyst.js +111 -11
- package/dist/lib/agent/domain/insight-evolver.d.ts +69 -0
- package/dist/lib/agent/domain/insight-evolver.js +230 -0
- package/dist/lib/agent/domain/insight-gate.d.ts +42 -0
- package/dist/lib/agent/domain/insight-gate.js +41 -0
- package/dist/lib/agent/domain/insight-producer.d.ts +27 -2
- package/dist/lib/agent/domain/insight-producer.js +60 -5
- package/dist/lib/agent/domain/scan-prompts.js +10 -7
- package/dist/lib/agent/memory/ActiveContext.d.ts +2 -28
- package/dist/lib/agent/memory/MemoryCoordinator.d.ts +2 -2
- package/dist/lib/agent/memory/SessionStore.d.ts +6 -12
- package/dist/lib/agent/memory/SessionStore.js +9 -15
- package/dist/lib/agent/memory/memory-flush-contract.d.ts +49 -0
- package/dist/lib/agent/memory/memory-flush-contract.js +16 -0
- package/dist/lib/agent/memory/session-store-schema.d.ts +20 -0
- package/dist/lib/agent/memory/session-store-schema.js +41 -0
- package/dist/lib/agent/presets.d.ts +89 -1
- package/dist/lib/agent/presets.js +53 -5
- package/dist/lib/agent/tools/_shared.d.ts +7 -15
- package/dist/lib/agent/tools/_shared.js +20 -21
- package/dist/lib/agent/tools/composite.d.ts +25 -22
- package/dist/lib/agent/tools/composite.js +108 -109
- package/dist/lib/agent/tools/evolution-tools.d.ts +145 -0
- package/dist/lib/agent/tools/evolution-tools.js +161 -0
- package/dist/lib/agent/tools/index.d.ts +163 -92
- package/dist/lib/agent/tools/index.js +9 -1
- package/dist/lib/agent/tools/lifecycle.d.ts +7 -1
- package/dist/lib/agent/tools/lifecycle.js +59 -75
- package/dist/lib/cli/AiScanService.js +1 -1
- package/dist/lib/cli/KnowledgeSyncService.d.ts +5 -1
- package/dist/lib/cli/KnowledgeSyncService.js +6 -3
- package/dist/lib/core/AstAnalyzer.d.ts +1 -0
- package/dist/lib/{service/bootstrap/DimensionCopyRegistry.d.ts → domain/dimension/DimensionCopy.d.ts} +2 -2
- package/dist/lib/{service/bootstrap/DimensionCopyRegistry.js → domain/dimension/DimensionCopy.js} +22 -72
- package/dist/lib/domain/dimension/DimensionRegistry.d.ts +54 -0
- package/dist/lib/domain/dimension/DimensionRegistry.js +620 -0
- package/dist/lib/domain/dimension/DimensionSop.d.ts +55 -0
- package/dist/lib/domain/dimension/DimensionSop.js +1604 -0
- package/dist/lib/domain/dimension/UnifiedDimension.d.ts +61 -0
- package/dist/lib/domain/dimension/UnifiedDimension.js +53 -0
- package/dist/lib/domain/dimension/index.d.ts +10 -0
- package/dist/lib/domain/dimension/index.js +9 -0
- package/dist/lib/domain/knowledge/FieldSpec.d.ts +1 -1
- package/dist/lib/domain/knowledge/FieldSpec.js +29 -16
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +33 -111
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +27 -6
- package/dist/lib/domain/knowledge/KnowledgeRepository.d.ts +1 -0
- package/dist/lib/domain/knowledge/KnowledgeRepository.js +3 -0
- package/dist/lib/domain/knowledge/Lifecycle.js +1 -1
- package/dist/lib/domain/knowledge/StyleGuide.d.ts +1 -1
- package/dist/lib/domain/knowledge/StyleGuide.js +1 -1
- package/dist/lib/domain/knowledge/UnifiedValidator.js +15 -0
- package/dist/lib/domain/knowledge/values/Stats.d.ts +1 -1
- package/dist/lib/domain/knowledge/values/Stats.js +2 -2
- package/dist/lib/external/mcp/McpServer.js +4 -0
- package/dist/lib/external/mcp/handlers/TargetClassifier.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/BootstrapSession.d.ts +8 -16
- package/dist/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +10 -10
- package/dist/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.d.ts +7 -0
- package/dist/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +20 -0
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +52 -132
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +204 -17
- package/dist/lib/external/mcp/handlers/bootstrap/base-dimensions.d.ts +11 -75
- package/dist/lib/external/mcp/handlers/bootstrap/base-dimensions.js +40 -191
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.d.ts +13 -78
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +30 -52
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.d.ts +0 -1
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.d.ts +99 -12
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +172 -161
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +7 -17
- package/dist/lib/external/mcp/handlers/bootstrap/shared/async-fill-helpers.d.ts +46 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/async-fill-helpers.js +58 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/audit-helpers.d.ts +25 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/audit-helpers.js +47 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +50 -12
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +30 -10
- package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-text.js +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/handler-types.d.ts +24 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/handler-types.js +14 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/panorama-utils.d.ts +14 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/panorama-utils.js +48 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/session-helpers.d.ts +21 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/session-helpers.js +45 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/skill-generator.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/target-file-map.d.ts +27 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/target-file-map.js +44 -0
- package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +14 -10
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +39 -51
- package/dist/lib/external/mcp/handlers/bootstrap-internal.d.ts +2 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +115 -82
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +4 -4
- package/dist/lib/external/mcp/handlers/consolidated.js +115 -162
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +69 -1
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +54 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +226 -0
- package/dist/lib/external/mcp/handlers/knowledge.js +26 -2
- package/dist/lib/external/mcp/handlers/rescan-external.d.ts +76 -0
- package/dist/lib/external/mcp/handlers/rescan-external.js +335 -0
- package/dist/lib/external/mcp/handlers/rescan-internal.d.ts +120 -0
- package/dist/lib/external/mcp/handlers/rescan-internal.js +359 -0
- package/dist/lib/external/mcp/handlers/search.d.ts +6 -5
- package/dist/lib/external/mcp/handlers/search.js +6 -5
- package/dist/lib/external/mcp/handlers/types.d.ts +2 -1
- package/dist/lib/external/mcp/handlers/wiki-external.js +2 -2
- package/dist/lib/external/mcp/tools.d.ts +8 -18
- package/dist/lib/external/mcp/tools.js +60 -3
- package/dist/lib/http/routes/knowledge.js +122 -1
- package/dist/lib/http/routes/modules.js +25 -3
- package/dist/lib/http/routes/panorama.js +16 -4
- package/dist/lib/infrastructure/cache/CacheCoordinator.d.ts +41 -0
- package/dist/lib/infrastructure/cache/CacheCoordinator.js +105 -0
- package/dist/lib/infrastructure/database/migrations/006_lifecycle_transition_events.d.ts +7 -0
- package/dist/lib/infrastructure/database/migrations/006_lifecycle_transition_events.js +28 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +1 -1
- package/dist/lib/injection/ServiceContainer.js +55 -0
- package/dist/lib/injection/ServiceMap.d.ts +8 -1
- package/dist/lib/injection/modules/InfraModule.js +4 -1
- package/dist/lib/injection/modules/KnowledgeModule.js +38 -1
- package/dist/lib/repository/evolution/ProposalRepository.d.ts +99 -0
- package/dist/lib/repository/evolution/ProposalRepository.js +255 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +4 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +16 -1
- package/dist/lib/service/bootstrap/BootstrapEventEmitter.d.ts +3 -2
- package/dist/lib/service/bootstrap/BootstrapEventEmitter.js +1 -1
- package/dist/lib/service/bootstrap/DeliveryVerifier.d.ts +51 -0
- package/dist/lib/service/bootstrap/DeliveryVerifier.js +163 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +22 -4
- package/dist/lib/service/bootstrap/UiStartupTasks.js +73 -5
- package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +54 -0
- package/dist/lib/service/bootstrap/bootstrap-event-types.js +10 -0
- package/dist/lib/service/cleanup/CleanupService.d.ts +85 -0
- package/dist/lib/service/cleanup/CleanupService.js +324 -0
- package/dist/lib/service/delivery/AgentInstructionsGenerator.js +39 -43
- package/dist/lib/service/delivery/FileProtection.d.ts +20 -0
- package/dist/lib/service/delivery/FileProtection.js +54 -0
- package/dist/lib/service/delivery/SkillsSyncer.js +16 -21
- package/dist/lib/service/evolution/ContentPatcher.d.ts +44 -0
- package/dist/lib/service/evolution/ContentPatcher.js +310 -0
- package/dist/lib/service/evolution/DecayDetector.d.ts +4 -3
- package/dist/lib/service/evolution/DecayDetector.js +97 -22
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -2
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +29 -2
- package/dist/lib/service/evolution/ProposalExecutor.d.ts +66 -0
- package/dist/lib/service/evolution/ProposalExecutor.js +424 -0
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +64 -0
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +458 -0
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +89 -0
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +492 -0
- package/dist/lib/service/evolution/StagingManager.js +5 -3
- package/dist/lib/service/evolution/createSupersedeProposal.d.ts +44 -0
- package/dist/lib/service/evolution/createSupersedeProposal.js +81 -0
- package/dist/lib/service/guard/ComplianceReporter.d.ts +4 -0
- package/dist/lib/service/guard/ComplianceReporter.js +51 -0
- package/dist/lib/service/guard/GuardCheckEngine.js +5 -4
- package/dist/lib/service/guard/GuardCrossFileChecks.js +2 -0
- package/dist/lib/service/guard/ReverseGuard.d.ts +1 -1
- package/dist/lib/service/guard/ReverseGuard.js +32 -2
- package/dist/lib/service/knowledge/ConfidenceRouter.js +1 -1
- package/dist/lib/service/knowledge/KnowledgeService.d.ts +11 -1
- package/dist/lib/service/knowledge/KnowledgeService.js +44 -4
- package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +225 -0
- package/dist/lib/service/knowledge/RecipeProductionGateway.js +384 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +2 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +48 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +3 -2
- package/dist/lib/service/panorama/DimensionAnalyzer.js +15 -140
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/SearchEngine.d.ts +11 -10
- package/dist/lib/service/search/SearchEngine.js +38 -36
- package/dist/lib/service/search/SearchTypes.d.ts +14 -8
- package/dist/lib/service/search/SearchTypes.js +1 -1
- package/dist/lib/service/search/tokenizer.d.ts +1 -1
- package/dist/lib/service/search/tokenizer.js +2 -2
- package/dist/lib/shared/schemas/common.d.ts +4 -4
- package/dist/lib/shared/schemas/http-requests.d.ts +12 -1
- package/dist/lib/shared/schemas/http-requests.js +8 -0
- package/dist/lib/shared/schemas/mcp-tools.d.ts +33 -2
- package/dist/lib/shared/schemas/mcp-tools.js +42 -0
- package/dist/lib/types/evolution.d.ts +135 -0
- package/dist/lib/types/evolution.js +6 -0
- package/dist/lib/types/graph-shared.d.ts +25 -0
- package/dist/lib/types/graph-shared.js +7 -0
- package/dist/lib/types/knowledge-wire.d.ts +131 -0
- package/dist/lib/types/knowledge-wire.js +7 -0
- package/dist/lib/types/project-snapshot-builder.d.ts +19 -0
- package/dist/lib/types/project-snapshot-builder.js +189 -0
- package/dist/lib/types/project-snapshot.d.ts +399 -0
- package/dist/lib/types/project-snapshot.js +17 -0
- package/dist/lib/types/search-wire.d.ts +46 -0
- package/dist/lib/types/search-wire.js +7 -0
- package/dist/lib/types/snapshot-views.d.ts +58 -0
- package/dist/lib/types/snapshot-views.js +103 -0
- package/package.json +1 -1
- package/skills/autosnippet-recipes/SKILL.md +1 -1
- package/templates/instructions/agent-static.md +2 -0
- package/templates/instructions/conventions.md +3 -1
- package/templates/recipes-setup/README.md +2 -2
- package/dashboard/dist/assets/icons-BJ2mUBi8.js +0 -1
- package/dashboard/dist/assets/index-B659K9t5.js +0 -128
- package/dashboard/dist/assets/index-NCm40PMD.css +0 -1
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.d.ts +0 -169
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +0 -727
- package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.d.ts +0 -370
- package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +0 -821
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CleanupService — 统一数据清理策略
|
|
3
|
+
*
|
|
4
|
+
* 提供两种清理模式:
|
|
5
|
+
* - fullReset(): 全量清理(删除一切知识/缓存/衍生数据),用于 bootstrap 冷启动
|
|
6
|
+
* - rescanClean(): Rescan 清理(保留 Recipe,清除衍生缓存),用于增量知识更新
|
|
7
|
+
* - snapshotRecipes(): 快照当前活跃 Recipe 信息
|
|
8
|
+
*
|
|
9
|
+
* 设计原则:
|
|
10
|
+
* - 配置数据 (config.json, constitution.yaml, boxspec.json) 永不清理
|
|
11
|
+
* - IDE 集成配置 (.vscode/, .cursor/, .github/) 永不清理
|
|
12
|
+
* - 交付物 (.cursor/rules/autosnippet-*) 由 R4 重建,不在此清理
|
|
13
|
+
*
|
|
14
|
+
* @module service/cleanup/CleanupService
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'node:fs';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import { CANDIDATES_DIR } from '#infra/config/Defaults.js';
|
|
19
|
+
import { getContextIndexPath, getProjectKnowledgePath, getProjectRecipesPath, getProjectSkillsPath, } from '#infra/config/Paths.js';
|
|
20
|
+
// ── 常量 ────────────────────────────────────────────────────
|
|
21
|
+
/** fullReset 时清除的所有 DB 表(不含 schema_migrations) */
|
|
22
|
+
const ALL_DATA_TABLES = [
|
|
23
|
+
'knowledge_entries',
|
|
24
|
+
'knowledge_edges',
|
|
25
|
+
'guard_violations',
|
|
26
|
+
'audit_logs',
|
|
27
|
+
'sessions',
|
|
28
|
+
'token_usage',
|
|
29
|
+
'semantic_memories',
|
|
30
|
+
'bootstrap_snapshots',
|
|
31
|
+
'bootstrap_dim_files',
|
|
32
|
+
'code_entities',
|
|
33
|
+
'remote_commands',
|
|
34
|
+
'remote_state',
|
|
35
|
+
'evolution_proposals',
|
|
36
|
+
'recipe_source_refs',
|
|
37
|
+
];
|
|
38
|
+
/** rescanClean 时清除的 DB 表(保留知识/进化相关表) */
|
|
39
|
+
const RESCAN_CLEAN_TABLES = [
|
|
40
|
+
'code_entities',
|
|
41
|
+
'guard_violations',
|
|
42
|
+
'bootstrap_snapshots',
|
|
43
|
+
'bootstrap_dim_files',
|
|
44
|
+
'semantic_memories',
|
|
45
|
+
'sessions',
|
|
46
|
+
'audit_logs',
|
|
47
|
+
'token_usage',
|
|
48
|
+
'remote_commands',
|
|
49
|
+
'remote_state',
|
|
50
|
+
'recipe_source_refs',
|
|
51
|
+
];
|
|
52
|
+
// ── CleanupService ──────────────────────────────────────────
|
|
53
|
+
export class CleanupService {
|
|
54
|
+
#projectRoot;
|
|
55
|
+
#logger;
|
|
56
|
+
#db;
|
|
57
|
+
constructor(opts) {
|
|
58
|
+
this.#projectRoot = opts.projectRoot;
|
|
59
|
+
this.#logger = opts.logger || { info() { }, warn() { } };
|
|
60
|
+
this.#db = opts.db
|
|
61
|
+
? typeof opts.db?.getDb === 'function'
|
|
62
|
+
? opts.db.getDb()
|
|
63
|
+
: opts.db
|
|
64
|
+
: null;
|
|
65
|
+
}
|
|
66
|
+
/** 更新 DB 引用(fullReset 后重连时调用) */
|
|
67
|
+
setDb(db) {
|
|
68
|
+
this.#db = db
|
|
69
|
+
? typeof db?.getDb === 'function'
|
|
70
|
+
? db.getDb()
|
|
71
|
+
: db
|
|
72
|
+
: null;
|
|
73
|
+
}
|
|
74
|
+
// ─── 需求 A:全量清理(删除一切) ─────────────────────
|
|
75
|
+
/**
|
|
76
|
+
* 全量清理 — 用于 bootstrap 冷启动
|
|
77
|
+
*
|
|
78
|
+
* 清除: DB 所有数据表、candidates/、recipes/、skills/、wiki/、
|
|
79
|
+
* 向量索引、bootstrap-report.json、logs/signals/
|
|
80
|
+
* 保留: config.json、constitution.yaml、boxspec.json、IDE 配置
|
|
81
|
+
*/
|
|
82
|
+
async fullReset() {
|
|
83
|
+
const result = {
|
|
84
|
+
deletedFiles: 0,
|
|
85
|
+
clearedTables: [],
|
|
86
|
+
preservedRecipes: 0,
|
|
87
|
+
errors: [],
|
|
88
|
+
};
|
|
89
|
+
this.#logger.info('[CleanupService] Starting fullReset...');
|
|
90
|
+
// 1. 清除 DB 所有数据表
|
|
91
|
+
if (this.#db) {
|
|
92
|
+
for (const table of ALL_DATA_TABLES) {
|
|
93
|
+
try {
|
|
94
|
+
this.#db.exec(`DELETE FROM ${table}`);
|
|
95
|
+
result.clearedTables.push(table);
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
99
|
+
// 表可能不存在(未 migrate),跳过
|
|
100
|
+
if (!msg.includes('no such table')) {
|
|
101
|
+
result.errors.push(`Failed to clear ${table}: ${msg}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 也清除 tasks 相关表(来自 migration 002)
|
|
106
|
+
for (const table of ['tasks', 'task_dependencies', 'task_events']) {
|
|
107
|
+
try {
|
|
108
|
+
this.#db.exec(`DELETE FROM ${table}`);
|
|
109
|
+
result.clearedTables.push(table);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* table may not exist */
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// 2. 清空 candidates/ 目录
|
|
117
|
+
result.deletedFiles += this.#clearDirectory(path.join(this.#projectRoot, CANDIDATES_DIR));
|
|
118
|
+
// 3. 清空 recipes/ 目录
|
|
119
|
+
result.deletedFiles += this.#clearDirectory(getProjectRecipesPath(this.#projectRoot));
|
|
120
|
+
// 4. 清空 skills/ 目录
|
|
121
|
+
result.deletedFiles += this.#clearDirectory(getProjectSkillsPath(this.#projectRoot));
|
|
122
|
+
// 5. 清空 wiki/ 目录
|
|
123
|
+
result.deletedFiles += this.#clearDirectory(path.join(getProjectKnowledgePath(this.#projectRoot), 'wiki'));
|
|
124
|
+
// 6. 删除向量索引
|
|
125
|
+
result.deletedFiles += this.#clearDirectory(getContextIndexPath(this.#projectRoot));
|
|
126
|
+
// 7. 删除 bootstrap-report.json
|
|
127
|
+
result.deletedFiles += this.#deleteFile(path.join(getProjectKnowledgePath(this.#projectRoot), '.autosnippet', 'bootstrap-report.json'));
|
|
128
|
+
// 8. 清除 logs/signals/
|
|
129
|
+
result.deletedFiles += this.#clearDirectory(path.join(getProjectKnowledgePath(this.#projectRoot), '.autosnippet', 'logs', 'signals'));
|
|
130
|
+
this.#logger.info('[CleanupService] fullReset complete', {
|
|
131
|
+
tables: result.clearedTables.length,
|
|
132
|
+
files: result.deletedFiles,
|
|
133
|
+
errors: result.errors.length,
|
|
134
|
+
});
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
// ─── 需求 B:Rescan 清理(保留 Recipe) ───────────────
|
|
138
|
+
/**
|
|
139
|
+
* Rescan 清理 — 保留 Recipe,清除衍生缓存
|
|
140
|
+
*
|
|
141
|
+
* 清除: 衍生 DB 表、pending/rejected/deprecated 知识条目、
|
|
142
|
+
* candidates/、skills/、wiki/、向量索引、bootstrap-report
|
|
143
|
+
* 保留: recipes/、active/published/staging/evolving 知识条目、
|
|
144
|
+
* knowledge_edges、evolution_proposals
|
|
145
|
+
*/
|
|
146
|
+
async rescanClean() {
|
|
147
|
+
const result = {
|
|
148
|
+
deletedFiles: 0,
|
|
149
|
+
clearedTables: [],
|
|
150
|
+
preservedRecipes: 0,
|
|
151
|
+
errors: [],
|
|
152
|
+
};
|
|
153
|
+
this.#logger.info('[CleanupService] Starting rescanClean...');
|
|
154
|
+
// 1. 清除衍生 DB 表
|
|
155
|
+
if (this.#db) {
|
|
156
|
+
for (const table of RESCAN_CLEAN_TABLES) {
|
|
157
|
+
try {
|
|
158
|
+
this.#db.exec(`DELETE FROM ${table}`);
|
|
159
|
+
result.clearedTables.push(table);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
163
|
+
if (!msg.includes('no such table')) {
|
|
164
|
+
result.errors.push(`Failed to clear ${table}: ${msg}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// 清除旧候选/废弃条目,保留活跃知识
|
|
169
|
+
try {
|
|
170
|
+
this.#db.exec(`DELETE FROM knowledge_entries WHERE lifecycle IN ('pending', 'rejected', 'deprecated')`);
|
|
171
|
+
result.clearedTables.push('knowledge_entries (pending/rejected/deprecated)');
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
175
|
+
result.errors.push(`Failed to clean old entries: ${msg}`);
|
|
176
|
+
}
|
|
177
|
+
// 也清除 tasks 相关表
|
|
178
|
+
for (const table of ['tasks', 'task_dependencies', 'task_events']) {
|
|
179
|
+
try {
|
|
180
|
+
this.#db.exec(`DELETE FROM ${table}`);
|
|
181
|
+
result.clearedTables.push(table);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
/* table may not exist */
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// 2. 清空 candidates/ 目录
|
|
189
|
+
result.deletedFiles += this.#clearDirectory(path.join(this.#projectRoot, CANDIDATES_DIR));
|
|
190
|
+
// 3. 清空 skills/ 目录
|
|
191
|
+
result.deletedFiles += this.#clearDirectory(getProjectSkillsPath(this.#projectRoot));
|
|
192
|
+
// 4. 清空 wiki/ 目录
|
|
193
|
+
result.deletedFiles += this.#clearDirectory(path.join(getProjectKnowledgePath(this.#projectRoot), 'wiki'));
|
|
194
|
+
// 5. 删除向量索引
|
|
195
|
+
result.deletedFiles += this.#clearDirectory(getContextIndexPath(this.#projectRoot));
|
|
196
|
+
// 6. 删除 bootstrap-report.json
|
|
197
|
+
result.deletedFiles += this.#deleteFile(path.join(getProjectKnowledgePath(this.#projectRoot), '.autosnippet', 'bootstrap-report.json'));
|
|
198
|
+
this.#logger.info('[CleanupService] rescanClean complete', {
|
|
199
|
+
tables: result.clearedTables.length,
|
|
200
|
+
files: result.deletedFiles,
|
|
201
|
+
errors: result.errors.length,
|
|
202
|
+
});
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
// ─── 快照当前 Recipe ──────────────────────────────────
|
|
206
|
+
/**
|
|
207
|
+
* 快照当前活跃 Recipe 信息
|
|
208
|
+
* 用于 rescan 前记录保留的知识条目
|
|
209
|
+
*/
|
|
210
|
+
async snapshotRecipes() {
|
|
211
|
+
if (!this.#db) {
|
|
212
|
+
return { count: 0, entries: [], coverageByDimension: {} };
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
const rows = this.#db
|
|
216
|
+
.prepare(`SELECT id, title, trigger, category, knowledgeType, doClause,
|
|
217
|
+
sourceFile, lifecycle, content, json_extract(reasoning, '$.sources') AS sourceRefsJson
|
|
218
|
+
FROM knowledge_entries
|
|
219
|
+
WHERE lifecycle IN ('active', 'staging', 'evolving')`)
|
|
220
|
+
.all();
|
|
221
|
+
const entries = rows.map((r) => {
|
|
222
|
+
let parsedContent;
|
|
223
|
+
try {
|
|
224
|
+
parsedContent = r.content
|
|
225
|
+
? JSON.parse(r.content)
|
|
226
|
+
: undefined;
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
parsedContent = undefined;
|
|
230
|
+
}
|
|
231
|
+
let parsedSourceRefs;
|
|
232
|
+
try {
|
|
233
|
+
parsedSourceRefs = r.sourceRefsJson
|
|
234
|
+
? JSON.parse(r.sourceRefsJson)
|
|
235
|
+
: undefined;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
parsedSourceRefs = undefined;
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
id: r.id,
|
|
242
|
+
title: r.title || '',
|
|
243
|
+
trigger: r.trigger || '',
|
|
244
|
+
category: r.category || '',
|
|
245
|
+
knowledgeType: r.knowledgeType || 'code-pattern',
|
|
246
|
+
doClause: r.doClause || '',
|
|
247
|
+
sourceFile: r.sourceFile || undefined,
|
|
248
|
+
lifecycle: r.lifecycle,
|
|
249
|
+
content: parsedContent,
|
|
250
|
+
sourceRefs: parsedSourceRefs,
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
// 按维度统计覆盖度 (使用 knowledgeType = 维度 id)
|
|
254
|
+
const coverageByDimension = {};
|
|
255
|
+
for (const entry of entries) {
|
|
256
|
+
const dim = entry.knowledgeType || 'unknown';
|
|
257
|
+
coverageByDimension[dim] = (coverageByDimension[dim] || 0) + 1;
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
count: entries.length,
|
|
261
|
+
entries,
|
|
262
|
+
coverageByDimension,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
catch (err) {
|
|
266
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
267
|
+
this.#logger.warn(`[CleanupService] snapshotRecipes failed: ${msg}`);
|
|
268
|
+
return { count: 0, entries: [], coverageByDimension: {} };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// ─── 内部工具方法 ─────────────────────────────────────
|
|
272
|
+
/**
|
|
273
|
+
* 清空目录内容(保留目录本身)
|
|
274
|
+
* @returns 删除的文件数
|
|
275
|
+
*/
|
|
276
|
+
#clearDirectory(dirPath) {
|
|
277
|
+
let count = 0;
|
|
278
|
+
try {
|
|
279
|
+
if (!fs.existsSync(dirPath)) {
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
const entries = fs.readdirSync(dirPath);
|
|
283
|
+
for (const entry of entries) {
|
|
284
|
+
const fullPath = path.join(dirPath, entry);
|
|
285
|
+
try {
|
|
286
|
+
const stat = fs.statSync(fullPath);
|
|
287
|
+
if (stat.isDirectory()) {
|
|
288
|
+
fs.rmSync(fullPath, { recursive: true });
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
fs.unlinkSync(fullPath);
|
|
292
|
+
}
|
|
293
|
+
count++;
|
|
294
|
+
}
|
|
295
|
+
catch (err) {
|
|
296
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
297
|
+
this.#logger.warn(`[CleanupService] Failed to delete ${entry}: ${msg}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
303
|
+
this.#logger.warn(`[CleanupService] clearDirectory failed for ${dirPath}: ${msg}`);
|
|
304
|
+
}
|
|
305
|
+
return count;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* 删除单个文件
|
|
309
|
+
* @returns 1 if deleted, 0 otherwise
|
|
310
|
+
*/
|
|
311
|
+
#deleteFile(filePath) {
|
|
312
|
+
try {
|
|
313
|
+
if (fs.existsSync(filePath)) {
|
|
314
|
+
fs.unlinkSync(filePath);
|
|
315
|
+
return 1;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
320
|
+
this.#logger.warn(`[CleanupService] Failed to delete file ${filePath}: ${msg}`);
|
|
321
|
+
}
|
|
322
|
+
return 0;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import fs from 'node:fs';
|
|
16
16
|
import path from 'node:path';
|
|
17
17
|
import { TEMPLATES_DIR } from '../../shared/package-root.js';
|
|
18
|
-
import {
|
|
18
|
+
import { mergeSection } from './FileProtection.js';
|
|
19
19
|
import { estimateTokens } from './TokenBudget.js';
|
|
20
20
|
/**
|
|
21
21
|
* Agent 指令文件 token 预算
|
|
@@ -49,6 +49,14 @@ const MCP_TOOLS_SUMMARY = [
|
|
|
49
49
|
{ name: 'autosnippet_graph', desc: 'Knowledge graph query (query/impact/path/stats)' },
|
|
50
50
|
{ name: 'autosnippet_skill', desc: 'Skill management (list/load/create/update/delete)' },
|
|
51
51
|
{ name: 'autosnippet_bootstrap', desc: 'Project cold-start & scan' },
|
|
52
|
+
{
|
|
53
|
+
name: 'autosnippet_rescan',
|
|
54
|
+
desc: 'Incremental rescan: preserves Recipes, cleans caches, re-analyzes project, runs relevance audit',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'autosnippet_evolve',
|
|
58
|
+
desc: 'Batch Recipe evolution decisions (propose_evolution/confirm_deprecation/skip), used per-dimension during rescan or standalone',
|
|
59
|
+
},
|
|
52
60
|
{
|
|
53
61
|
name: 'autosnippet_panorama',
|
|
54
62
|
desc: 'Project panorama (operation: overview/module/gaps/health)',
|
|
@@ -95,8 +103,8 @@ export class AgentInstructionsGenerator {
|
|
|
95
103
|
const filesWritten = allResults.filter((r) => !r.skipped).length;
|
|
96
104
|
const skippedFiles = allResults.filter((r) => r.skipped);
|
|
97
105
|
if (skippedFiles.length > 0) {
|
|
98
|
-
this.logger.info?.(`[AgentInstructions] Skipped ${skippedFiles.length} file(s)
|
|
99
|
-
|
|
106
|
+
this.logger.info?.(`[AgentInstructions] Skipped ${skippedFiles.length} file(s): ` +
|
|
107
|
+
skippedFiles.map((f) => f.filePath).join(', '));
|
|
100
108
|
}
|
|
101
109
|
this.logger.info?.(`[AgentInstructions] Generated ${filesWritten} files in ${duration}ms — ` +
|
|
102
110
|
`AGENTS.md: ${agents.tokensUsed}t, CLAUDE.md: ${claude.tokensUsed}t, ` +
|
|
@@ -159,7 +167,8 @@ export class AgentInstructionsGenerator {
|
|
|
159
167
|
}
|
|
160
168
|
// ─── AGENTS.md ─────────────────────────────────────
|
|
161
169
|
_writeAgentsMd(sections) {
|
|
162
|
-
|
|
170
|
+
// 文件头部(仅用于新建/旧版重写场景)
|
|
171
|
+
const header = [
|
|
163
172
|
`# ${this.projectName} — Agent Instructions`,
|
|
164
173
|
'',
|
|
165
174
|
'> Auto-generated by [AutoSnippet](https://github.com/GxFn/AutoSnippet). Do not edit manually.',
|
|
@@ -167,31 +176,28 @@ export class AgentInstructionsGenerator {
|
|
|
167
176
|
'This project uses **AutoSnippet** for knowledge management.',
|
|
168
177
|
'Access the knowledge base through MCP tools.',
|
|
169
178
|
'',
|
|
170
|
-
];
|
|
171
|
-
//
|
|
179
|
+
].join('\n');
|
|
180
|
+
// 动态区段内容(始终在 markers 内管理)
|
|
181
|
+
const sectionLines = [];
|
|
172
182
|
if (sections.ruleLines.length > 0) {
|
|
173
|
-
|
|
183
|
+
sectionLines.push('## Coding Standards', '', ...sections.ruleLines, '');
|
|
174
184
|
}
|
|
175
|
-
// Architecture Patterns
|
|
176
185
|
if (sections.patternRows.length > 0) {
|
|
177
|
-
|
|
186
|
+
sectionLines.push('## Architecture Patterns', '', '| Trigger | When | Do |', '|---------|------|----|', ...sections.patternRows, '');
|
|
178
187
|
}
|
|
179
|
-
|
|
180
|
-
lines.push('## MCP Tools', '', ...sections.toolLines, '');
|
|
181
|
-
// Skills
|
|
188
|
+
sectionLines.push('## MCP Tools', '', ...sections.toolLines, '');
|
|
182
189
|
if (sections.skillLines.length > 0) {
|
|
183
|
-
|
|
190
|
+
sectionLines.push('## Skills', '', 'Load with `autosnippet_skill({ operation: "load", name: "<skill>" })`:', '', ...sections.skillLines, '');
|
|
184
191
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const content = `${lines.join('\n')}\n`;
|
|
192
|
+
sectionLines.push('## Constraints', '', '1. Do NOT modify knowledge base files directly (`AutoSnippet/recipes/`, `.autosnippet/`).', '2. Prefer Recipes as project standards; source code is supplementary.', '3. Create or update knowledge only through MCP tools.', '');
|
|
193
|
+
const sectionContent = sectionLines.join('\n');
|
|
188
194
|
const filePath = path.join(this.projectRoot, 'AGENTS.md');
|
|
189
|
-
const result =
|
|
190
|
-
return { filePath, tokensUsed: estimateTokens(
|
|
195
|
+
const result = mergeSection(filePath, sectionContent, { header, logger: this.logger });
|
|
196
|
+
return { filePath, tokensUsed: estimateTokens(sectionContent), skipped: !result.written };
|
|
191
197
|
}
|
|
192
198
|
// ─── CLAUDE.md ─────────────────────────────────────
|
|
193
199
|
_writeClaudeMd(sections) {
|
|
194
|
-
const
|
|
200
|
+
const header = [
|
|
195
201
|
`# ${this.projectName} — Claude Code Instructions`,
|
|
196
202
|
'',
|
|
197
203
|
'> Auto-generated by AutoSnippet. Regenerated when knowledge base changes.',
|
|
@@ -199,27 +205,23 @@ export class AgentInstructionsGenerator {
|
|
|
199
205
|
'This project uses **AutoSnippet** for knowledge management.',
|
|
200
206
|
'Access the knowledge base through MCP tools.',
|
|
201
207
|
'',
|
|
202
|
-
];
|
|
203
|
-
|
|
208
|
+
].join('\n');
|
|
209
|
+
const sectionLines = [];
|
|
204
210
|
if (sections.ruleLines.length > 0) {
|
|
205
|
-
|
|
211
|
+
sectionLines.push('## Coding Standards', '', ...sections.ruleLines, '');
|
|
206
212
|
}
|
|
207
|
-
// Patterns
|
|
208
213
|
if (sections.patternRows.length > 0) {
|
|
209
|
-
|
|
214
|
+
sectionLines.push('## Key Patterns', '', '| Trigger | When | Do |', '|---------|------|----|', ...sections.patternRows, '');
|
|
210
215
|
}
|
|
211
|
-
|
|
212
|
-
lines.push('## MCP Tools', '', ...sections.toolLines, '');
|
|
213
|
-
// Skills
|
|
216
|
+
sectionLines.push('## MCP Tools', '', ...sections.toolLines, '');
|
|
214
217
|
if (sections.skillLines.length > 0) {
|
|
215
|
-
|
|
218
|
+
sectionLines.push('## Skills', '', ...sections.skillLines, '');
|
|
216
219
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const content = `${lines.join('\n')}\n`;
|
|
220
|
+
sectionLines.push('', '## Constraints', '', '1. Do NOT modify knowledge base files directly (`AutoSnippet/recipes/`, `.autosnippet/`).', '2. Prefer Recipes as project standards; source code is supplementary.', '3. Create or update knowledge only through MCP tools.', '');
|
|
221
|
+
const sectionContent = sectionLines.join('\n');
|
|
220
222
|
const filePath = path.join(this.projectRoot, 'CLAUDE.md');
|
|
221
|
-
const result =
|
|
222
|
-
return { filePath, tokensUsed: estimateTokens(
|
|
223
|
+
const result = mergeSection(filePath, sectionContent, { header, logger: this.logger });
|
|
224
|
+
return { filePath, tokensUsed: estimateTokens(sectionContent), skipped: !result.written };
|
|
223
225
|
}
|
|
224
226
|
// ─── copilot-instructions.md ───────────────────────
|
|
225
227
|
/**
|
|
@@ -228,7 +230,7 @@ export class AgentInstructionsGenerator {
|
|
|
228
230
|
*/
|
|
229
231
|
_writeCopilotInstructions(_sections) {
|
|
230
232
|
const body = this._loadConventionsTemplate();
|
|
231
|
-
const
|
|
233
|
+
const section = [
|
|
232
234
|
'<!-- autosnippet:begin -->',
|
|
233
235
|
'',
|
|
234
236
|
'# AutoSnippet Conventions',
|
|
@@ -236,16 +238,10 @@ export class AgentInstructionsGenerator {
|
|
|
236
238
|
body,
|
|
237
239
|
'',
|
|
238
240
|
'<!-- autosnippet:end -->',
|
|
239
|
-
'',
|
|
240
241
|
].join('\n');
|
|
241
|
-
const
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
if (canWrite) {
|
|
245
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
246
|
-
}
|
|
247
|
-
const result = safeWriteFile(filePath, content, { logger: this.logger });
|
|
248
|
-
return { filePath, tokensUsed: estimateTokens(content), skipped: !result.written };
|
|
242
|
+
const filePath = path.join(this.projectRoot, '.github', 'copilot-instructions.md');
|
|
243
|
+
const result = mergeSection(filePath, section, { logger: this.logger });
|
|
244
|
+
return { filePath, tokensUsed: estimateTokens(section), skipped: !result.written };
|
|
249
245
|
}
|
|
250
246
|
// ─── 模板读取 ──────────────────────────────────────
|
|
251
247
|
/**
|
|
@@ -61,3 +61,23 @@ export declare function safeCopyFile(srcPath: string, destPath: string, options?
|
|
|
61
61
|
reason: string;
|
|
62
62
|
filePath: string;
|
|
63
63
|
};
|
|
64
|
+
export type MergeStrategy = 'create' | 'replace-section' | 'rewrite-legacy' | 'append-section';
|
|
65
|
+
/**
|
|
66
|
+
* 智能合并 AutoSnippet 管理区段到目标文件
|
|
67
|
+
*
|
|
68
|
+
* 四种场景:
|
|
69
|
+
* 1. 文件不存在 → 创建完整文件(header + markers)
|
|
70
|
+
* 2. 文件有 begin/end 标记 → 仅替换标记区段(增量更新)
|
|
71
|
+
* 3. 文件有旧版 AutoSnippet 签名但无标记 → 全量重写并加标记(旧版迁移)
|
|
72
|
+
* 4. 文件无签名无标记(用户文件)→ 追加标记区段到末尾(共存)
|
|
73
|
+
*/
|
|
74
|
+
export declare function mergeSection(filePath: string, section: string, options?: {
|
|
75
|
+
header?: string;
|
|
76
|
+
logger?: {
|
|
77
|
+
info?: (...args: unknown[]) => void;
|
|
78
|
+
};
|
|
79
|
+
}): {
|
|
80
|
+
written: boolean;
|
|
81
|
+
strategy: MergeStrategy;
|
|
82
|
+
filePath: string;
|
|
83
|
+
};
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* - "auto-generated by autosnippet" (case-insensitive)
|
|
13
13
|
*/
|
|
14
14
|
import fs from 'node:fs';
|
|
15
|
+
import path from 'node:path';
|
|
15
16
|
/**
|
|
16
17
|
* AutoSnippet 文件签名模式(case-insensitive)
|
|
17
18
|
* 检查文件前 1024 字节即可——签名总在文件头部
|
|
@@ -96,3 +97,56 @@ export function safeCopyFile(srcPath, destPath, options = {}) {
|
|
|
96
97
|
logger?.info?.(`[FileProtection] Skipped "${destPath}" — ${reason} (file exists and is not AutoSnippet-generated)`);
|
|
97
98
|
return { written: false, reason, filePath: destPath };
|
|
98
99
|
}
|
|
100
|
+
// ─── Section Merge ──────────────────────────────────
|
|
101
|
+
const SECTION_BEGIN = '<!-- autosnippet:begin -->';
|
|
102
|
+
const SECTION_END = '<!-- autosnippet:end -->';
|
|
103
|
+
const SECTION_PATTERN = /<!-- autosnippet:begin -->[\s\S]*?<!-- autosnippet:end -->/;
|
|
104
|
+
/**
|
|
105
|
+
* 智能合并 AutoSnippet 管理区段到目标文件
|
|
106
|
+
*
|
|
107
|
+
* 四种场景:
|
|
108
|
+
* 1. 文件不存在 → 创建完整文件(header + markers)
|
|
109
|
+
* 2. 文件有 begin/end 标记 → 仅替换标记区段(增量更新)
|
|
110
|
+
* 3. 文件有旧版 AutoSnippet 签名但无标记 → 全量重写并加标记(旧版迁移)
|
|
111
|
+
* 4. 文件无签名无标记(用户文件)→ 追加标记区段到末尾(共存)
|
|
112
|
+
*/
|
|
113
|
+
export function mergeSection(filePath, section, options = {}) {
|
|
114
|
+
const { header = '', logger } = options;
|
|
115
|
+
const fileName = path.basename(filePath);
|
|
116
|
+
// 确保 section 包含 begin/end 标记
|
|
117
|
+
const wrappedSection = section.includes(SECTION_BEGIN)
|
|
118
|
+
? section
|
|
119
|
+
: `${SECTION_BEGIN}\n\n${section}\n${SECTION_END}`;
|
|
120
|
+
// Case 1: 文件不存在 → 创建完整文件
|
|
121
|
+
if (!fs.existsSync(filePath)) {
|
|
122
|
+
const content = header ? `${header}\n${wrappedSection}\n` : `${wrappedSection}\n`;
|
|
123
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
124
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
125
|
+
logger?.info?.(`[FileProtection] Created "${fileName}" (new file)`);
|
|
126
|
+
return { written: true, strategy: 'create', filePath };
|
|
127
|
+
}
|
|
128
|
+
const existing = fs.readFileSync(filePath, 'utf8');
|
|
129
|
+
// Case 2: 文件已有 begin/end 标记 → 仅替换标记区段
|
|
130
|
+
if (SECTION_PATTERN.test(existing)) {
|
|
131
|
+
const updated = existing.replace(SECTION_PATTERN, wrappedSection);
|
|
132
|
+
if (updated === existing) {
|
|
133
|
+
logger?.info?.(`[FileProtection] "${fileName}" — section unchanged, skipped write`);
|
|
134
|
+
return { written: false, strategy: 'replace-section', filePath };
|
|
135
|
+
}
|
|
136
|
+
fs.writeFileSync(filePath, updated, 'utf8');
|
|
137
|
+
logger?.info?.(`[FileProtection] Updated "${fileName}" (replaced marker section)`);
|
|
138
|
+
return { written: true, strategy: 'replace-section', filePath };
|
|
139
|
+
}
|
|
140
|
+
// Case 3: 文件有 AutoSnippet 签名但无标记 → 旧版格式,重写并加标记
|
|
141
|
+
if (SIGNATURE_PATTERN.test(existing.slice(0, 1024))) {
|
|
142
|
+
const content = header ? `${header}\n${wrappedSection}\n` : `${wrappedSection}\n`;
|
|
143
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
144
|
+
logger?.info?.(`[FileProtection] Rewrote "${fileName}" (legacy → marker format)`);
|
|
145
|
+
return { written: true, strategy: 'rewrite-legacy', filePath };
|
|
146
|
+
}
|
|
147
|
+
// Case 4: 用户自有文件 → 追加标记区段到末尾
|
|
148
|
+
const separator = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
149
|
+
fs.writeFileSync(filePath, `${existing}${separator}${wrappedSection}\n`, 'utf8');
|
|
150
|
+
logger?.info?.(`[FileProtection] Appended AutoSnippet section to "${fileName}" (user-owned file preserved)`);
|
|
151
|
+
return { written: true, strategy: 'append-section', filePath };
|
|
152
|
+
}
|
|
@@ -19,36 +19,31 @@ import { SKILLS_DIR as BUILTIN_SKILLS_DIR } from '../../shared/package-root.js';
|
|
|
19
19
|
*/
|
|
20
20
|
const SKILL_NAME_MAP = {
|
|
21
21
|
'project-architecture': 'autosnippet-architecture',
|
|
22
|
-
'project-
|
|
23
|
-
'project-profile': 'autosnippet-profile',
|
|
22
|
+
'project-coding-standards': 'autosnippet-coding-standards',
|
|
24
23
|
'project-agent-guidelines': 'autosnippet-guidelines',
|
|
25
|
-
'project-event-
|
|
26
|
-
'project-
|
|
27
|
-
'project-
|
|
28
|
-
'project-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'project-
|
|
32
|
-
'project-framework-conventions': 'autosnippet-framework-conventions',
|
|
24
|
+
'project-data-event-flow': 'autosnippet-data-flow',
|
|
25
|
+
'project-design-patterns': 'autosnippet-design-patterns',
|
|
26
|
+
'project-error-resilience': 'autosnippet-error-resilience',
|
|
27
|
+
'project-swift-objc-idiom': 'autosnippet-swift-objc-idiom',
|
|
28
|
+
// 语言维度
|
|
29
|
+
'project-ts-js-module': 'autosnippet-ts-js-module',
|
|
30
|
+
'project-react-patterns': 'autosnippet-react-patterns',
|
|
33
31
|
'project-python-structure': 'autosnippet-python-structure',
|
|
34
|
-
'project-jvm-
|
|
32
|
+
'project-jvm-annotation': 'autosnippet-jvm-annotation',
|
|
35
33
|
};
|
|
36
34
|
/** 用途描述模板(英文,Cursor 优先) */
|
|
37
35
|
const SKILL_DESC_MAP = {
|
|
38
36
|
'autosnippet-architecture': 'Architecture patterns, module boundaries, and dependency rules for {project}. Use when creating new modules, reviewing architecture, or understanding dependencies.',
|
|
39
|
-
'autosnippet-
|
|
40
|
-
'autosnippet-profile': 'Project overview and profile for {project}. Use when needing background on the project, its tech stack, or structure.',
|
|
37
|
+
'autosnippet-coding-standards': 'Coding standards and style conventions for {project}. Use when writing new code, reviewing formatting, or enforcing naming conventions.',
|
|
41
38
|
'autosnippet-guidelines': 'Agent interaction guidelines for {project}. Use when understanding how to work with this specific project.',
|
|
42
39
|
'autosnippet-data-flow': 'Event and data flow patterns for {project}. Use when working with events, state management, or data pipelines.',
|
|
43
|
-
'autosnippet-
|
|
44
|
-
'autosnippet-
|
|
45
|
-
'autosnippet-
|
|
46
|
-
'autosnippet-
|
|
47
|
-
|
|
48
|
-
'autosnippet-module-exports': 'Module export structure, barrel exports, and public API surface for {project}. Use when working with imports/exports or module boundaries.',
|
|
49
|
-
'autosnippet-framework-conventions': 'Framework-specific conventions (component structure, routing, state management) for {project}. Use when following framework patterns.',
|
|
40
|
+
'autosnippet-design-patterns': 'Common code patterns and idioms for {project}. Use when implementing features following project conventions.',
|
|
41
|
+
'autosnippet-error-resilience': 'Error handling, resilience patterns and defensive coding for {project}. Use when making design decisions or code review.',
|
|
42
|
+
'autosnippet-swift-objc-idiom': 'Swift/ObjC idioms, categories, method swizzling and interop for {project}. Use when working with Swift or Objective-C code.',
|
|
43
|
+
'autosnippet-ts-js-module': 'Module export structure, barrel exports, and public API surface for {project}. Use when working with imports/exports or module boundaries.',
|
|
44
|
+
'autosnippet-react-patterns': 'React component patterns, state management conventions and routing for {project}. Use when following framework patterns.',
|
|
50
45
|
'autosnippet-python-structure': 'Python package structure, __init__.py exports, import patterns and type hint coverage for {project}. Use when working with Python modules.',
|
|
51
|
-
'autosnippet-jvm-
|
|
46
|
+
'autosnippet-jvm-annotation': 'Annotation patterns (DI, ORM, API, custom) and meta-programming for {project}. Use when working with Spring, Jakarta, or framework annotations.',
|
|
52
47
|
};
|
|
53
48
|
export class SkillsSyncer {
|
|
54
49
|
knowledgeService;
|