autosnippet 2.5.0 → 2.7.0
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/bin/cli.js +35 -0
- package/dashboard/dist/assets/{icons-Dtm0E6DS.js → icons-Cq4-iQhP.js} +152 -87
- package/dashboard/dist/assets/index-DBxH7pVn.css +1 -0
- package/dashboard/dist/assets/index-Dw2F6qAS.js +197 -0
- package/dashboard/dist/assets/{react-markdown-CWxUbOf4.js → react-markdown-BA6FB2NP.js} +1 -1
- package/dashboard/dist/assets/{syntax-highlighter-CJ2drQQb.js → syntax-highlighter-CVLHn9O5.js} +1 -1
- package/dashboard/dist/assets/{vendor-f83ah6cm.js → vendor-BotF760a.js} +61 -61
- package/dashboard/dist/index.html +6 -6
- package/lib/bootstrap.js +1 -1
- package/lib/cli/SetupService.js +33 -8
- package/lib/cli/UpgradeService.js +139 -2
- package/lib/core/ast/ProjectGraph.js +599 -0
- package/lib/core/gateway/Gateway.js +19 -4
- package/lib/core/gateway/GatewayActionRegistry.js +2 -2
- package/lib/domain/recipe/Recipe.js +3 -0
- package/lib/external/ai/AiProvider.js +117 -10
- package/lib/external/ai/providers/ClaudeProvider.js +197 -0
- package/lib/external/ai/providers/GoogleGeminiProvider.js +235 -1
- package/lib/external/ai/providers/OpenAiProvider.js +131 -0
- package/lib/external/mcp/McpServer.js +2 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +216 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +468 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +162 -0
- package/lib/external/mcp/handlers/bootstrap/skills.js +225 -0
- package/lib/external/mcp/handlers/bootstrap.js +151 -1634
- package/lib/external/mcp/handlers/browse.js +1 -1
- package/lib/external/mcp/handlers/candidate.js +1 -33
- package/lib/external/mcp/handlers/skill.js +126 -31
- package/lib/external/mcp/tools.js +25 -3
- package/lib/http/middleware/requestLogger.js +23 -4
- package/lib/http/routes/ai.js +3 -1
- package/lib/http/routes/auth.js +3 -2
- package/lib/http/routes/candidates.js +49 -25
- package/lib/http/routes/commands.js +0 -8
- package/lib/http/routes/guardRules.js +1 -16
- package/lib/http/routes/recipes.js +4 -17
- package/lib/http/routes/search.js +16 -22
- package/lib/http/routes/skills.js +40 -3
- package/lib/http/routes/snippets.js +0 -33
- package/lib/http/routes/spm.js +37 -63
- package/lib/http/utils/routeHelpers.js +31 -0
- package/lib/infrastructure/audit/AuditStore.js +18 -0
- package/lib/infrastructure/config/Paths.js +9 -0
- package/lib/infrastructure/logging/Logger.js +86 -3
- package/lib/infrastructure/realtime/RealtimeService.js +2 -5
- package/lib/infrastructure/vector/JsonVectorAdapter.js +24 -1
- package/lib/injection/ServiceContainer.js +62 -3
- package/lib/service/bootstrap/BootstrapTaskManager.js +400 -0
- package/lib/service/candidate/CandidateFileWriter.js +68 -27
- package/lib/service/candidate/CandidateService.js +156 -10
- package/lib/service/chat/AnalystAgent.js +216 -0
- package/lib/service/chat/CandidateGuardrail.js +134 -0
- package/lib/service/chat/ChatAgent.js +1272 -155
- package/lib/service/chat/ContextWindow.js +730 -0
- package/lib/service/chat/ConversationStore.js +377 -0
- package/lib/service/chat/HandoffProtocol.js +180 -0
- package/lib/service/chat/Memory.js +40 -10
- package/lib/service/chat/ProducerAgent.js +240 -0
- package/lib/service/chat/ToolRegistry.js +149 -5
- package/lib/service/chat/tools.js +1493 -60
- package/lib/service/recipe/RecipeFileWriter.js +12 -1
- package/lib/service/skills/EventAggregator.js +187 -0
- package/lib/service/skills/SignalCollector.js +549 -0
- package/lib/service/skills/SkillAdvisor.js +324 -0
- package/lib/service/skills/SkillHooks.js +13 -5
- package/lib/service/spm/SpmService.js +2 -2
- package/package.json +1 -1
- package/templates/copilot-instructions.md +20 -3
- package/templates/cursor-rules/autosnippet-conventions.mdc +21 -4
- package/templates/cursor-rules/autosnippet-skills.mdc +45 -0
- package/dashboard/dist/assets/index-B7VpZOCz.css +0 -1
- package/dashboard/dist/assets/index-D87IZTmZ.js +0 -187
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>AutoSnippet Dashboard</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Dw2F6qAS.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-BotF760a.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/axios-C0Zqfgkc.js">
|
|
12
|
-
<link rel="modulepreload" crossorigin href="/assets/icons-
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/syntax-highlighter-
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/react-markdown-
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/icons-Cq4-iQhP.js">
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/syntax-highlighter-CVLHn9O5.js">
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/react-markdown-BA6FB2NP.js">
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DBxH7pVn.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="root"></div>
|
package/lib/bootstrap.js
CHANGED
|
@@ -152,7 +152,7 @@ export class Bootstrap {
|
|
|
152
152
|
this.components.auditLogger = auditLogger;
|
|
153
153
|
logger.info('Audit system initialized');
|
|
154
154
|
|
|
155
|
-
// Skill Hooks (扫描 skills/*/hooks.js +
|
|
155
|
+
// Skill Hooks (扫描 skills/*/hooks.js + AutoSnippet/skills/*/hooks.js)
|
|
156
156
|
const skillHooks = new SkillHooks();
|
|
157
157
|
await skillHooks.load();
|
|
158
158
|
this.components.skillHooks = skillHooks;
|
package/lib/cli/SetupService.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* 一键初始化 AutoSnippet V2 工作空间,5 步完成:
|
|
5
5
|
*
|
|
6
6
|
* Step 1 .autosnippet/ 运行时目录 + config.json + .gitignore
|
|
7
|
-
* Step 2 AutoSnippet/ 子仓库(核心数据 +
|
|
8
|
-
* Step 3 IDE 集成(VSCode MCP + Cursor MCP + copilot-instructions + cursor-rules)
|
|
7
|
+
* Step 2 AutoSnippet/ 子仓库(核心数据 + 权限能力 + skills/)
|
|
8
|
+
* Step 3 IDE 集成(VSCode MCP + Cursor MCP + copilot-instructions + cursor-rules + skills-template)
|
|
9
9
|
* Step 4 SQLite 数据库 + V1 数据迁移
|
|
10
10
|
* Step 5 平台相关初始化(macOS → Xcode Snippets)
|
|
11
11
|
*
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* ├─ constitution.yaml 权限宪法:角色 + 权限矩阵 + 治理规则 + 能力探测
|
|
18
18
|
* ├─ boxspec.json 项目规格定义
|
|
19
19
|
* ├─ recipes/*.md 统一知识实体(代码规范/模式/架构/调用链/数据流/...)
|
|
20
|
+
* ├─ skills/ Project Skills(冷启动自动生成 + 手动创建)
|
|
20
21
|
* └─ README.md
|
|
21
22
|
*
|
|
22
23
|
* .autosnippet/ (运行时缓存,gitignored)
|
|
@@ -76,6 +77,7 @@ export class SetupService {
|
|
|
76
77
|
this.coreDir = join(this.projectRoot, 'AutoSnippet');
|
|
77
78
|
this.recipesDir = join(this.coreDir, 'recipes');
|
|
78
79
|
this.candidatesDir = join(this.coreDir, 'candidates');
|
|
80
|
+
this.skillsDir = join(this.coreDir, 'skills');
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/* ═══ 公共入口 ═══════════════════════════════════════ */
|
|
@@ -121,6 +123,7 @@ export class SetupService {
|
|
|
121
123
|
console.log(' ├─ boxspec.json 项目规格');
|
|
122
124
|
console.log(' ├─ recipes/*.md 统一知识实体 ← 受 git push 保护');
|
|
123
125
|
console.log(' ├─ candidates/*.md 候选代码片段 ← 受 git push 保护');
|
|
126
|
+
console.log(' ├─ skills/ Project Skills(冷启动自动生成 + 手动创建)');
|
|
124
127
|
console.log(' └─ *.json 运行数据(统计/反馈/规则学习/排除策略)');
|
|
125
128
|
console.log(' .autosnippet/ 运行时缓存(gitignored)');
|
|
126
129
|
console.log(' ├─ config.json 项目配置');
|
|
@@ -195,7 +198,7 @@ export class SetupService {
|
|
|
195
198
|
const alreadyRepo = existsSync(coreGit);
|
|
196
199
|
|
|
197
200
|
// 创建目录结构
|
|
198
|
-
for (const d of [this.coreDir, this.recipesDir, this.candidatesDir]) {
|
|
201
|
+
for (const d of [this.coreDir, this.recipesDir, this.candidatesDir, this.skillsDir]) {
|
|
199
202
|
mkdirSync(d, { recursive: true });
|
|
200
203
|
}
|
|
201
204
|
|
|
@@ -366,6 +369,8 @@ export class SetupService {
|
|
|
366
369
|
'│ ├── naming-rules.md 代码规范示例',
|
|
367
370
|
'│ ├── mvvm-arch.md 架构模式示例',
|
|
368
371
|
'│ └── ... 代码模式/调用链/数据流/约束/风格/...',
|
|
372
|
+
'├── skills/ Project Skills(冷启动自动生成 + 手动创建)',
|
|
373
|
+
'│ └── <name>/SKILL.md AI Agent 知识增强文档',
|
|
369
374
|
'└── README.md',
|
|
370
375
|
'```',
|
|
371
376
|
'',
|
|
@@ -429,8 +434,9 @@ export class SetupService {
|
|
|
429
434
|
this._configureCursorMCP(mcpServerPath);
|
|
430
435
|
this._copyCopilotInstructions();
|
|
431
436
|
this._copyCursorRules();
|
|
437
|
+
this._copySkillsTemplate();
|
|
432
438
|
|
|
433
|
-
return { configured: ['vscode-mcp', 'cursor-mcp', 'copilot-instructions', 'cursor-rules'] };
|
|
439
|
+
return { configured: ['vscode-mcp', 'cursor-mcp', 'copilot-instructions', 'cursor-rules', 'skills-template'] };
|
|
434
440
|
}
|
|
435
441
|
|
|
436
442
|
/** @private VSCode settings.json → Copilot MCP */
|
|
@@ -519,6 +525,23 @@ export class SetupService {
|
|
|
519
525
|
console.log(' ✅ .cursor/rules/autosnippet-conventions.mdc');
|
|
520
526
|
}
|
|
521
527
|
|
|
528
|
+
/** @private .cursor/rules/autosnippet-skills.mdc — Project Skills 索引模板 */
|
|
529
|
+
_copySkillsTemplate() {
|
|
530
|
+
const src = join(REPO_ROOT, 'templates', 'cursor-rules', 'autosnippet-skills.mdc');
|
|
531
|
+
if (!existsSync(src)) return;
|
|
532
|
+
|
|
533
|
+
const destDir = join(this.projectRoot, '.cursor', 'rules');
|
|
534
|
+
const dest = join(destDir, 'autosnippet-skills.mdc');
|
|
535
|
+
if (existsSync(dest) && !this.force) {
|
|
536
|
+
console.log(' ℹ️ skills template 已存在');
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
mkdirSync(destDir, { recursive: true });
|
|
541
|
+
copyFileSync(src, dest);
|
|
542
|
+
console.log(' ✅ .cursor/rules/autosnippet-skills.mdc');
|
|
543
|
+
}
|
|
544
|
+
|
|
522
545
|
/* ═══ Step 4: 数据库初始化 ═══════════════════════════ */
|
|
523
546
|
|
|
524
547
|
async stepDatabase() {
|
|
@@ -649,11 +672,13 @@ export class SetupService {
|
|
|
649
672
|
console.log(' ✅ .gitignore += !.autosnippet/config.json');
|
|
650
673
|
}
|
|
651
674
|
|
|
652
|
-
//
|
|
653
|
-
|
|
654
|
-
|
|
675
|
+
// Skills 已迁移到 AutoSnippet/skills/(知识库目录内),自动跟随 Git
|
|
676
|
+
|
|
677
|
+
// ── 清理旧版本的 .autosnippet/skills/ negation(已迁移,不再需要)──
|
|
678
|
+
if (content.includes('!.autosnippet/skills/')) {
|
|
679
|
+
content = content.replace(/^!?\.autosnippet\/skills\/.*\n?/gm, '');
|
|
655
680
|
changed = true;
|
|
656
|
-
console.log(' ✅ .gitignore
|
|
681
|
+
console.log(' ✅ .gitignore: 移除旧版 .autosnippet/skills/ 规则(已迁移到 AutoSnippet/skills/)');
|
|
657
682
|
}
|
|
658
683
|
|
|
659
684
|
// ── 必须跟踪:AutoSnippet/(知识库子仓库)──
|
|
@@ -4,14 +4,17 @@
|
|
|
4
4
|
* 当 AutoSnippet 发布新版本后,老用户执行 `asd upgrade` 即可更新:
|
|
5
5
|
* ① MCP 配置(.cursor/mcp.json + .vscode/settings.json)
|
|
6
6
|
* ② Cursor Skills(.cursor/skills/)
|
|
7
|
-
* ③ Cursor Rules(.cursor/rules/autosnippet-conventions.mdc)
|
|
7
|
+
* ③ Cursor Rules(.cursor/rules/autosnippet-conventions.mdc + autosnippet-skills.mdc)
|
|
8
8
|
* ④ Copilot Instructions(.github/copilot-instructions.md)
|
|
9
|
+
* ⑤ Constitution(AutoSnippet/constitution.yaml)
|
|
10
|
+
* ⑥ .gitignore(升级规则 + 清理旧版本残留)
|
|
11
|
+
* ⑦ Skills 路径迁移(.autosnippet/skills/ → AutoSnippet/skills/)
|
|
9
12
|
*
|
|
10
13
|
* 不会重建数据库、子仓库或运行时目录。
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import {
|
|
14
|
-
existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync,
|
|
17
|
+
existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, readdirSync,
|
|
15
18
|
} from 'fs';
|
|
16
19
|
import { join, resolve, dirname } from 'path';
|
|
17
20
|
import { fileURLToPath } from 'url';
|
|
@@ -39,8 +42,12 @@ export class UpgradeService {
|
|
|
39
42
|
}
|
|
40
43
|
if (!skillsOnly && !mcpOnly) {
|
|
41
44
|
results.push(this._upgradeCursorRules());
|
|
45
|
+
results.push(this._upgradeSkillsTemplate());
|
|
42
46
|
results.push(this._upgradeCopilotInstructions());
|
|
43
47
|
results.push(this._upgradeConstitution());
|
|
48
|
+
results.push(this._upgradeGitignore());
|
|
49
|
+
results.push(this._migrateSkillsPath());
|
|
50
|
+
results.push(this._ensureSkillsDir());
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
console.log('');
|
|
@@ -217,6 +224,136 @@ export class UpgradeService {
|
|
|
217
224
|
console.log(' cd AutoSnippet && git add constitution.yaml && git commit -m "Upgrade constitution" && git push');
|
|
218
225
|
}
|
|
219
226
|
}
|
|
227
|
+
/* ═══ Skills Template ════════════════════════════════ */
|
|
228
|
+
|
|
229
|
+
_upgradeSkillsTemplate() {
|
|
230
|
+
console.log('[Skills Template] 更新 autosnippet-skills.mdc...');
|
|
231
|
+
|
|
232
|
+
const src = join(REPO_ROOT, 'templates', 'cursor-rules', 'autosnippet-skills.mdc');
|
|
233
|
+
if (!existsSync(src)) {
|
|
234
|
+
console.log(' ⚠️ 模板不存在,跳过');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const destDir = join(this.projectRoot, '.cursor', 'rules');
|
|
239
|
+
const dest = join(destDir, 'autosnippet-skills.mdc');
|
|
240
|
+
mkdirSync(destDir, { recursive: true });
|
|
241
|
+
copyFileSync(src, dest);
|
|
242
|
+
console.log(' ✅ .cursor/rules/autosnippet-skills.mdc');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/* ═══ .gitignore ════════════════════════════════════ */
|
|
246
|
+
|
|
247
|
+
_upgradeGitignore() {
|
|
248
|
+
console.log('[Gitignore] 更新 .gitignore 规则...');
|
|
249
|
+
|
|
250
|
+
const giPath = join(this.projectRoot, '.gitignore');
|
|
251
|
+
if (!existsSync(giPath)) {
|
|
252
|
+
console.log(' ℹ️ .gitignore 不存在,跳过');
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let content = readFileSync(giPath, 'utf8');
|
|
257
|
+
let changed = false;
|
|
258
|
+
|
|
259
|
+
// v2.4.0 迁移:旧格式 ".autosnippet/" → 新格式 ".autosnippet/*"
|
|
260
|
+
if (content.includes('.autosnippet/') && !content.includes('.autosnippet/*')) {
|
|
261
|
+
content = content.replace(/^\.autosnippet\/$/m, '.autosnippet/*');
|
|
262
|
+
changed = true;
|
|
263
|
+
console.log(' ✅ .autosnippet/ → .autosnippet/*(升级为精细忽略)');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 确保有 .autosnippet/*
|
|
267
|
+
if (!content.includes('.autosnippet/') && !content.includes('.autosnippet/*')) {
|
|
268
|
+
content += `\n# AutoSnippet 运行时缓存(不入库)\n.autosnippet/*\n`;
|
|
269
|
+
changed = true;
|
|
270
|
+
console.log(' ✅ += .autosnippet/*');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 确保 config.json 跟踪
|
|
274
|
+
if (!content.includes('!.autosnippet/config.json')) {
|
|
275
|
+
content += `!.autosnippet/config.json\n`;
|
|
276
|
+
changed = true;
|
|
277
|
+
console.log(' ✅ += !.autosnippet/config.json');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 清理旧版本的 .autosnippet/skills/ negation(已迁移到 AutoSnippet/skills/)
|
|
281
|
+
if (content.includes('!.autosnippet/skills/')) {
|
|
282
|
+
content = content.replace(/^!?\.autosnippet\/skills\/.*\n?/gm, '');
|
|
283
|
+
changed = true;
|
|
284
|
+
console.log(' ✅ 移除旧版 .autosnippet/skills/ 规则(已迁移到 AutoSnippet/skills/)');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// 确保 AutoSnippet/ 不被忽略
|
|
288
|
+
const lines = content.split('\n');
|
|
289
|
+
const hasIgnoreAS = lines.some(l => {
|
|
290
|
+
const t = l.trim();
|
|
291
|
+
return (t === 'AutoSnippet/' || t === 'AutoSnippet') && !t.startsWith('#') && !t.startsWith('!');
|
|
292
|
+
});
|
|
293
|
+
const hasNegation = lines.some(l => l.trim() === '!AutoSnippet/');
|
|
294
|
+
if (hasIgnoreAS && !hasNegation) {
|
|
295
|
+
content += `\n# AutoSnippet 知识库必须入库(取消上方忽略)\n!AutoSnippet/\n`;
|
|
296
|
+
changed = true;
|
|
297
|
+
console.log(' ✅ += !AutoSnippet/ (取消忽略)');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (changed) {
|
|
301
|
+
writeFileSync(giPath, content);
|
|
302
|
+
} else {
|
|
303
|
+
console.log(' ℹ️ .gitignore 已是最新版本');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* ═══ Skills 路径迁移 ═══════════════════════════════ */
|
|
308
|
+
|
|
309
|
+
_migrateSkillsPath() {
|
|
310
|
+
const oldSkillsDir = join(this.projectRoot, '.autosnippet', 'skills');
|
|
311
|
+
const newSkillsDir = join(this.projectRoot, 'AutoSnippet', 'skills');
|
|
312
|
+
|
|
313
|
+
if (!existsSync(oldSkillsDir)) return;
|
|
314
|
+
if (!existsSync(join(this.projectRoot, 'AutoSnippet'))) return;
|
|
315
|
+
|
|
316
|
+
console.log('[Migration] 迁移 Skills: .autosnippet/skills/ → AutoSnippet/skills/...');
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
mkdirSync(newSkillsDir, { recursive: true });
|
|
320
|
+
const entries = readdirSync(oldSkillsDir, { withFileTypes: true });
|
|
321
|
+
let migrated = 0;
|
|
322
|
+
|
|
323
|
+
for (const entry of entries) {
|
|
324
|
+
if (!entry.isDirectory()) continue;
|
|
325
|
+
const src = join(oldSkillsDir, entry.name);
|
|
326
|
+
const dest = join(newSkillsDir, entry.name);
|
|
327
|
+
if (existsSync(dest)) {
|
|
328
|
+
console.log(` ℹ️ ${entry.name} 已存在于新路径,跳过`);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
// 复制目录
|
|
332
|
+
execSync(`cp -r "${src}" "${dest}"`, { stdio: 'pipe' });
|
|
333
|
+
migrated++;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (migrated > 0) {
|
|
337
|
+
console.log(` ✅ 已迁移 ${migrated} 个 Skill 到 AutoSnippet/skills/`);
|
|
338
|
+
console.log(' 💡 确认迁移无误后可删除旧目录: rm -rf .autosnippet/skills/');
|
|
339
|
+
} else {
|
|
340
|
+
console.log(' ℹ️ 无需迁移(所有 Skill 已存在于新路径)');
|
|
341
|
+
}
|
|
342
|
+
} catch (e) {
|
|
343
|
+
console.error(` ❌ 迁移失败: ${e.message}`);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* ═══ 确保 Skills 目录存在 ══════════════════════════ */
|
|
348
|
+
|
|
349
|
+
_ensureSkillsDir() {
|
|
350
|
+
const skillsDir = join(this.projectRoot, 'AutoSnippet', 'skills');
|
|
351
|
+
if (!existsSync(join(this.projectRoot, 'AutoSnippet'))) return;
|
|
352
|
+
if (existsSync(skillsDir)) return;
|
|
353
|
+
|
|
354
|
+
mkdirSync(skillsDir, { recursive: true });
|
|
355
|
+
console.log('[Skills] ✅ 创建 AutoSnippet/skills/ 目录');
|
|
356
|
+
}
|
|
220
357
|
}
|
|
221
358
|
|
|
222
359
|
export default UpgradeService;
|