autosnippet 3.0.11 → 3.1.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/api-server.js +2 -0
- package/bin/cli.js +84 -16
- package/config/default.json +10 -1
- package/dashboard/dist/assets/{index-I2ySoCmF.js → index-Bnm26ulL.js} +47 -47
- package/dashboard/dist/index.html +1 -1
- package/lib/bootstrap.js +4 -4
- package/lib/cli/SetupService.js +116 -29
- package/lib/cli/UpgradeService.js +16 -6
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/ast/ensure-grammars.js +1 -1
- package/lib/core/ast/index.js +62 -11
- package/lib/core/ast/lang-dart.js +27 -21
- package/lib/core/ast/lang-go.js +6 -20
- package/lib/core/ast/lang-rust.js +53 -28
- package/lib/core/ast/parser-init.js +9 -5
- package/lib/core/discovery/DartDiscoverer.js +4 -10
- package/lib/core/discovery/GenericDiscoverer.js +4 -28
- package/lib/core/discovery/GoDiscoverer.js +45 -25
- package/lib/core/discovery/NodeDiscoverer.js +1 -3
- package/lib/core/discovery/PythonDiscoverer.js +7 -1
- package/lib/core/discovery/RustDiscoverer.js +111 -38
- package/lib/core/discovery/index.js +2 -2
- package/lib/core/enhancement/django-enhancement.js +10 -4
- package/lib/core/enhancement/fastapi-enhancement.js +16 -9
- package/lib/core/enhancement/go-grpc-enhancement.js +2 -1
- package/lib/core/enhancement/go-web-enhancement.js +3 -6
- package/lib/core/enhancement/ml-enhancement.js +6 -3
- package/lib/core/enhancement/nextjs-enhancement.js +17 -7
- package/lib/core/enhancement/node-server-enhancement.js +4 -2
- package/lib/core/enhancement/react-enhancement.js +6 -3
- package/lib/core/enhancement/rust-tokio-enhancement.js +6 -2
- package/lib/core/enhancement/rust-web-enhancement.js +13 -7
- package/lib/core/enhancement/vue-enhancement.js +10 -5
- package/lib/external/ai/AiFactory.js +3 -1
- package/lib/external/ai/AiProvider.js +3 -1
- package/lib/external/mcp/McpServer.js +2 -0
- package/lib/external/mcp/handlers/bootstrap/base-dimensions.js +245 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +86 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +275 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +629 -0
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +131 -348
- package/lib/external/mcp/handlers/bootstrap/refine.js +364 -0
- package/lib/external/mcp/handlers/bootstrap.js +7 -597
- package/lib/external/mcp/handlers/browse.js +123 -9
- package/lib/external/mcp/handlers/guard.js +29 -6
- package/lib/external/mcp/handlers/search.js +56 -24
- package/lib/external/mcp/handlers/skill.js +6 -2
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/routes/candidates.js +3 -1
- package/lib/http/routes/extract.js +4 -5
- package/lib/http/routes/guardRules.js +9 -17
- package/lib/http/routes/modules.js +9 -3
- package/lib/http/routes/skills.js +54 -6
- package/lib/http/routes/violations.js +4 -3
- package/lib/infrastructure/external/ClipboardManager.js +24 -7
- package/lib/infrastructure/external/NativeUi.js +3 -1
- package/lib/infrastructure/external/OpenBrowser.js +1 -0
- package/lib/infrastructure/external/XcodeAutomation.js +5 -5
- package/lib/infrastructure/vector/IndexingPipeline.js +14 -5
- package/lib/injection/ServiceContainer.js +45 -13
- package/lib/platform/ios/index.js +20 -25
- package/lib/platform/ios/routes/spm.js +6 -3
- package/lib/platform/ios/snippet/PlaceholderConverter.js +6 -2
- package/lib/platform/ios/snippet/XcodeCodec.js +4 -2
- package/lib/platform/ios/spm/SpmDiscoverer.js +1 -1
- package/lib/platform/ios/spm/SpmService.js +3 -1
- package/lib/platform/ios/xcode/XcodeImportResolver.js +434 -0
- package/lib/platform/ios/xcode/XcodeIntegration.js +43 -664
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +225 -0
- package/lib/service/automation/FileWatcher.js +1 -3
- package/lib/service/automation/handlers/CreateHandler.js +3 -5
- package/lib/service/automation/handlers/GuardHandler.js +11 -32
- package/lib/service/automation/handlers/SearchHandler.js +9 -9
- package/lib/service/chat/CandidateGuardrail.js +11 -6
- package/lib/service/chat/ChatAgent.js +51 -421
- package/lib/service/chat/ChatAgentPrompts.js +149 -0
- package/lib/service/chat/ChatAgentTasks.js +297 -0
- package/lib/service/chat/HandoffProtocol.js +5 -2
- package/lib/service/chat/tools/_shared.js +61 -0
- package/lib/service/chat/tools/ai-analysis.js +284 -0
- package/lib/service/chat/tools/ast-graph.js +681 -0
- package/lib/service/chat/tools/composite.js +497 -0
- package/lib/service/chat/tools/guard.js +265 -0
- package/lib/service/chat/tools/index.js +239 -0
- package/lib/service/chat/tools/infrastructure.js +227 -0
- package/lib/service/chat/tools/knowledge-graph.js +234 -0
- package/lib/service/chat/tools/lifecycle.js +486 -0
- package/lib/service/chat/tools/project-access.js +919 -0
- package/lib/service/chat/tools/query.js +264 -0
- package/lib/service/chat/tools.js +13 -3994
- package/lib/service/cursor/AgentInstructionsGenerator.js +413 -0
- package/lib/service/cursor/CursorDeliveryPipeline.js +71 -11
- package/lib/service/cursor/FileProtection.js +116 -0
- package/lib/service/cursor/KnowledgeCompressor.js +70 -11
- package/lib/service/cursor/SkillsSyncer.js +5 -3
- package/lib/service/cursor/TopicClassifier.js +19 -3
- package/lib/service/guard/ComplianceReporter.js +5 -2
- package/lib/service/guard/ExclusionManager.js +26 -2
- package/lib/service/guard/GuardCheckEngine.js +83 -388
- package/lib/service/guard/GuardCodeChecks.js +391 -0
- package/lib/service/guard/GuardCrossFileChecks.js +326 -0
- package/lib/service/guard/GuardPatternUtils.js +187 -0
- package/lib/service/guard/GuardService.js +80 -38
- package/lib/service/module/ModuleService.js +181 -56
- package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
- package/lib/service/search/SearchEngine.js +10 -2
- package/lib/service/snippet/SnippetFactory.js +3 -3
- package/lib/service/snippet/SnippetInstaller.js +35 -11
- package/lib/service/snippet/codecs/VSCodeCodec.js +2 -2
- package/lib/service/wiki/WikiGenerator.js +247 -1535
- package/lib/service/wiki/WikiRenderers.js +1903 -0
- package/lib/service/wiki/WikiUtils.js +1044 -0
- package/lib/shared/LanguageService.js +359 -2
- package/lib/shared/PathGuard.js +0 -8
- package/package.json +3 -9
- package/scripts/bench-real-projects.mjs +29 -29
- package/scripts/generate-recipe-drafts.js +17 -27
- package/scripts/init-snippets.js +43 -24
- package/scripts/install-vscode-copilot.js +3 -19
- package/scripts/setup-mcp-config.js +0 -4
|
@@ -5,7 +5,7 @@
|
|
|
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-Bnm26ulL.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-CEnWn7aV.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/axios-C0Zqfgkc.js">
|
package/lib/bootstrap.js
CHANGED
|
@@ -57,7 +57,7 @@ export class Bootstrap {
|
|
|
57
57
|
// 2. 初始化日志系统
|
|
58
58
|
await this.initializeLogger();
|
|
59
59
|
|
|
60
|
-
this.components.logger.info('AutoSnippet
|
|
60
|
+
this.components.logger.info('AutoSnippet - Starting initialization...');
|
|
61
61
|
|
|
62
62
|
// 3. 连接数据库
|
|
63
63
|
await this.initializeDatabase();
|
|
@@ -75,7 +75,7 @@ export class Bootstrap {
|
|
|
75
75
|
// await this.registerRoutes();
|
|
76
76
|
|
|
77
77
|
const duration = Date.now() - startTime;
|
|
78
|
-
this.components.logger.info(`AutoSnippet
|
|
78
|
+
this.components.logger.info(`AutoSnippet initialized successfully (${duration}ms)`);
|
|
79
79
|
|
|
80
80
|
return this.components;
|
|
81
81
|
} catch (error) {
|
|
@@ -200,14 +200,14 @@ export class Bootstrap {
|
|
|
200
200
|
* 关闭应用程序
|
|
201
201
|
*/
|
|
202
202
|
async shutdown() {
|
|
203
|
-
this.components.logger?.info('AutoSnippet
|
|
203
|
+
this.components.logger?.info('AutoSnippet - Shutting down...');
|
|
204
204
|
|
|
205
205
|
// 关闭数据库连接
|
|
206
206
|
if (this.components.db) {
|
|
207
207
|
await this.components.db.close();
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
this.components.logger?.info('AutoSnippet
|
|
210
|
+
this.components.logger?.info('AutoSnippet - Shutdown complete');
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
/**
|
package/lib/cli/SetupService.js
CHANGED
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
} from 'node:fs';
|
|
54
54
|
import { dirname, join, resolve } from 'node:path';
|
|
55
55
|
import { fileURLToPath } from 'node:url';
|
|
56
|
+
import { checkWriteSafety } from '../service/cursor/FileProtection.js';
|
|
56
57
|
|
|
57
58
|
const __filename = fileURLToPath(import.meta.url);
|
|
58
59
|
const __dirname = dirname(__filename);
|
|
@@ -102,21 +103,15 @@ export class SetupService {
|
|
|
102
103
|
const results = [];
|
|
103
104
|
const total = steps.length;
|
|
104
105
|
|
|
105
|
-
console.log('');
|
|
106
|
-
console.log(` ⚙ AutoSnippet Setup — ${this.projectName}`);
|
|
107
|
-
console.log(` ${'─'.repeat(44)}`);
|
|
108
|
-
|
|
109
106
|
for (let i = 0; i < total; i++) {
|
|
110
107
|
const { label, fn } = steps[i];
|
|
111
108
|
const tag = `[${i + 1}/${total}]`;
|
|
112
109
|
process.stdout.write(` ${tag} ${label}...`);
|
|
113
110
|
try {
|
|
114
111
|
const r = await fn();
|
|
115
|
-
const
|
|
116
|
-
console.log(` ✅${detail}`);
|
|
112
|
+
const _detail = this._formatStepDetail(r);
|
|
117
113
|
results.push({ step: i + 1, label, ok: true, ...(r || {}) });
|
|
118
114
|
} catch (err) {
|
|
119
|
-
console.log(` ❌`);
|
|
120
115
|
console.error(` ${err.message}`);
|
|
121
116
|
results.push({ step: i + 1, label, ok: false, error: err.message });
|
|
122
117
|
}
|
|
@@ -128,7 +123,9 @@ export class SetupService {
|
|
|
128
123
|
|
|
129
124
|
/** @private 格式化步骤结果的简要信息 */
|
|
130
125
|
_formatStepDetail(r) {
|
|
131
|
-
if (!r)
|
|
126
|
+
if (!r) {
|
|
127
|
+
return '';
|
|
128
|
+
}
|
|
132
129
|
const parts = [];
|
|
133
130
|
if (r.configured) {
|
|
134
131
|
parts.push(r.configured.join(', '));
|
|
@@ -144,17 +141,8 @@ export class SetupService {
|
|
|
144
141
|
|
|
145
142
|
printSummary() {
|
|
146
143
|
const results = this._results || [];
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
console.log(` ${'─'.repeat(44)}`);
|
|
151
|
-
console.log(` ✨ Setup 完成: ${ok} 成功${fail > 0 ? `, ${fail} 失败` : ''}`);
|
|
152
|
-
console.log('');
|
|
153
|
-
console.log(' 后续操作:');
|
|
154
|
-
console.log(' asd coldstart 扫描项目、AI 生成知识库');
|
|
155
|
-
console.log(' asd ui 启动 Dashboard + API Server');
|
|
156
|
-
console.log(' asd watch 启动 Xcode 文件监听');
|
|
157
|
-
console.log('');
|
|
144
|
+
const _ok = results.filter((r) => r.ok).length;
|
|
145
|
+
const _fail = results.filter((r) => !r.ok).length;
|
|
158
146
|
}
|
|
159
147
|
|
|
160
148
|
/* ═══ Step 1: 运行时目录与配置 ═══════════════════════ */
|
|
@@ -451,10 +439,10 @@ export class SetupService {
|
|
|
451
439
|
this._configureVSCodeMCP(mcpServerPath);
|
|
452
440
|
this._configureCursorMCP(mcpServerPath);
|
|
453
441
|
this._copyCopilotInstructions();
|
|
442
|
+
this._generateAgentInstructionFiles();
|
|
454
443
|
this._copyCursorRules();
|
|
455
444
|
this._copySkillsTemplate();
|
|
456
|
-
|
|
457
|
-
this._mirrorCursorToIDE('.trae');
|
|
445
|
+
// NOTE: .qoder/ .trae/ 不再自动创建,用户可通过 `asd mirror` 按需生成
|
|
458
446
|
|
|
459
447
|
const extResult = this._installVSCodeExtension();
|
|
460
448
|
|
|
@@ -462,10 +450,9 @@ export class SetupService {
|
|
|
462
450
|
'vscode-mcp',
|
|
463
451
|
'cursor-mcp',
|
|
464
452
|
'copilot-instructions',
|
|
453
|
+
'agent-instructions',
|
|
465
454
|
'cursor-rules',
|
|
466
455
|
'skills-template',
|
|
467
|
-
'qoder-rules',
|
|
468
|
-
'trae-rules',
|
|
469
456
|
];
|
|
470
457
|
if (extResult) {
|
|
471
458
|
configured.push(...extResult);
|
|
@@ -577,7 +564,12 @@ export class SetupService {
|
|
|
577
564
|
const whichCmd = process.platform === 'win32' ? 'where' : 'which';
|
|
578
565
|
for (const cmd of ['code', 'cursor', 'codex', 'code-insiders']) {
|
|
579
566
|
try {
|
|
580
|
-
const p = execSync(`${whichCmd} ${cmd}`, {
|
|
567
|
+
const p = execSync(`${whichCmd} ${cmd}`, {
|
|
568
|
+
encoding: 'utf8',
|
|
569
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
570
|
+
})
|
|
571
|
+
.trim()
|
|
572
|
+
.split(/\r?\n/)[0];
|
|
581
573
|
if (p) {
|
|
582
574
|
candidates.push({ name: cmd, cli: p });
|
|
583
575
|
}
|
|
@@ -591,7 +583,11 @@ export class SetupService {
|
|
|
591
583
|
// macOS: /Applications/xxx.app/Contents/Resources/app/bin/
|
|
592
584
|
const appPaths = [
|
|
593
585
|
{ name: 'vscode', app: '/Applications/Visual Studio Code.app', bin: 'code' },
|
|
594
|
-
{
|
|
586
|
+
{
|
|
587
|
+
name: 'vscode-insiders',
|
|
588
|
+
app: '/Applications/Visual Studio Code - Insiders.app',
|
|
589
|
+
bin: 'code-insiders',
|
|
590
|
+
},
|
|
595
591
|
{ name: 'cursor', app: '/Applications/Cursor.app', bin: 'cursor' },
|
|
596
592
|
{ name: 'codex', app: '/Applications/Codex.app', bin: 'codex' },
|
|
597
593
|
];
|
|
@@ -603,10 +599,15 @@ export class SetupService {
|
|
|
603
599
|
}
|
|
604
600
|
} else if (process.platform === 'win32') {
|
|
605
601
|
// Windows: %LOCALAPPDATA%\Programs\xxx
|
|
606
|
-
const localAppData =
|
|
602
|
+
const localAppData =
|
|
603
|
+
process.env.LOCALAPPDATA || join(process.env.USERPROFILE || '', 'AppData', 'Local');
|
|
607
604
|
const winPaths = [
|
|
608
605
|
{ name: 'vscode', dir: 'Microsoft VS Code', bin: 'bin/code.cmd' },
|
|
609
|
-
{
|
|
606
|
+
{
|
|
607
|
+
name: 'vscode-insiders',
|
|
608
|
+
dir: 'Microsoft VS Code Insiders',
|
|
609
|
+
bin: 'bin/code-insiders.cmd',
|
|
610
|
+
},
|
|
610
611
|
{ name: 'cursor', dir: 'cursor', bin: 'cursor.exe' },
|
|
611
612
|
];
|
|
612
613
|
for (const { name, dir, bin } of winPaths) {
|
|
@@ -639,7 +640,9 @@ export class SetupService {
|
|
|
639
640
|
} catch {
|
|
640
641
|
/* use as-is */
|
|
641
642
|
}
|
|
642
|
-
if (seen.has(realPath))
|
|
643
|
+
if (seen.has(realPath)) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
643
646
|
seen.add(realPath);
|
|
644
647
|
return true;
|
|
645
648
|
});
|
|
@@ -706,7 +709,7 @@ export class SetupService {
|
|
|
706
709
|
writeFileSync(configPath, JSON.stringify(existing, null, 2));
|
|
707
710
|
}
|
|
708
711
|
|
|
709
|
-
/** @private .github/copilot-instructions.md */
|
|
712
|
+
/** @private .github/copilot-instructions.md (static template fallback) */
|
|
710
713
|
_copyCopilotInstructions() {
|
|
711
714
|
const src = join(REPO_ROOT, 'templates', 'copilot-instructions.md');
|
|
712
715
|
if (!existsSync(src)) {
|
|
@@ -719,10 +722,94 @@ export class SetupService {
|
|
|
719
722
|
return;
|
|
720
723
|
}
|
|
721
724
|
|
|
725
|
+
// 即使 --force,也不覆盖用户原有的非 AutoSnippet 文件
|
|
726
|
+
const { canWrite } = checkWriteSafety(dest);
|
|
727
|
+
if (!canWrite) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
722
731
|
mkdirSync(destDir, { recursive: true });
|
|
723
732
|
copyFileSync(src, dest);
|
|
724
733
|
}
|
|
725
734
|
|
|
735
|
+
/**
|
|
736
|
+
* @private 生成 AGENTS.md + CLAUDE.md 静态骨架
|
|
737
|
+
* setup 阶段没有知识库数据,所以只生成指向 MCP 的基础版本。
|
|
738
|
+
* bootstrap / delivery pipeline 完成后会用动态版本覆盖。
|
|
739
|
+
*/
|
|
740
|
+
_generateAgentInstructionFiles() {
|
|
741
|
+
const projectName = this.projectRoot.split('/').pop();
|
|
742
|
+
|
|
743
|
+
// AGENTS.md
|
|
744
|
+
const agentsPath = join(this.projectRoot, 'AGENTS.md');
|
|
745
|
+
if (!existsSync(agentsPath) || this.force) {
|
|
746
|
+
const { canWrite } = checkWriteSafety(agentsPath);
|
|
747
|
+
if (canWrite) {
|
|
748
|
+
const agentsContent = [
|
|
749
|
+
`# ${projectName} — Agent Instructions`,
|
|
750
|
+
'',
|
|
751
|
+
'> Auto-generated by AutoSnippet. Will be enriched after `asd bootstrap`.',
|
|
752
|
+
'',
|
|
753
|
+
'## Project Knowledge Base',
|
|
754
|
+
'',
|
|
755
|
+
'This project uses **AutoSnippet** for knowledge management.',
|
|
756
|
+
'Run `asd bootstrap` to populate the knowledge base, then this file will be',
|
|
757
|
+
'regenerated with coding standards, patterns, and tool references.',
|
|
758
|
+
'',
|
|
759
|
+
'## MCP Tools',
|
|
760
|
+
'',
|
|
761
|
+
'Use these MCP tools to access the full knowledge base:',
|
|
762
|
+
'- `autosnippet_search` — Search knowledge (mode: auto/context/keyword/semantic)',
|
|
763
|
+
'- `autosnippet_knowledge` — Browse/get recipes (operation: list/get/insights)',
|
|
764
|
+
'- `autosnippet_submit_knowledge` — Submit candidate (strict validation)',
|
|
765
|
+
'- `autosnippet_guard` — Code compliance check',
|
|
766
|
+
'- `autosnippet_health` — Service health & KB stats',
|
|
767
|
+
'',
|
|
768
|
+
'## Mandatory Constraints',
|
|
769
|
+
'',
|
|
770
|
+
'1. **Do NOT modify** knowledge base files directly (`AutoSnippet/`, `.autosnippet/`).',
|
|
771
|
+
'2. Create or update knowledge **only** through MCP tools.',
|
|
772
|
+
'3. **Prefer Recipes** as project standards; source code is supplementary.',
|
|
773
|
+
'',
|
|
774
|
+
].join('\n');
|
|
775
|
+
writeFileSync(agentsPath, agentsContent);
|
|
776
|
+
} else {
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// CLAUDE.md
|
|
781
|
+
const claudePath = join(this.projectRoot, 'CLAUDE.md');
|
|
782
|
+
if (!existsSync(claudePath) || this.force) {
|
|
783
|
+
const { canWrite } = checkWriteSafety(claudePath);
|
|
784
|
+
if (canWrite) {
|
|
785
|
+
const claudeContent = [
|
|
786
|
+
`# ${projectName} — Claude Code Instructions`,
|
|
787
|
+
'',
|
|
788
|
+
'> Auto-generated by AutoSnippet. Will be enriched after `asd bootstrap`.',
|
|
789
|
+
'',
|
|
790
|
+
'## MCP Integration',
|
|
791
|
+
'',
|
|
792
|
+
'This project has an AutoSnippet MCP server configured.',
|
|
793
|
+
'Use the following tools to access project knowledge:',
|
|
794
|
+
'- `autosnippet_search` — Search knowledge (mode: auto/context/keyword/semantic)',
|
|
795
|
+
'- `autosnippet_knowledge` — Browse/get recipes (operation: list/get/insights)',
|
|
796
|
+
'- `autosnippet_submit_knowledge` — Submit candidate (strict validation)',
|
|
797
|
+
'- `autosnippet_guard` — Code compliance check',
|
|
798
|
+
'- `autosnippet_health` — Service health & KB stats',
|
|
799
|
+
'',
|
|
800
|
+
'## Mandatory Constraints',
|
|
801
|
+
'',
|
|
802
|
+
'1. **Do NOT modify** knowledge base files directly (`AutoSnippet/`, `.autosnippet/`).',
|
|
803
|
+
'2. Create or update knowledge **only** through MCP tools.',
|
|
804
|
+
'3. **Prefer Recipes** as project standards; source code is supplementary.',
|
|
805
|
+
'',
|
|
806
|
+
].join('\n');
|
|
807
|
+
writeFileSync(claudePath, claudeContent);
|
|
808
|
+
} else {
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
726
813
|
/** @private .cursor/rules/autosnippet-conventions.mdc */
|
|
727
814
|
_copyCursorRules() {
|
|
728
815
|
const src = join(REPO_ROOT, 'templates', 'cursor-rules', 'autosnippet-conventions.mdc');
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ① MCP 配置(.cursor/mcp.json + .vscode/mcp.json)
|
|
6
6
|
* ② Cursor Skills(.cursor/skills/)
|
|
7
7
|
* ③ Cursor Rules(.cursor/rules/autosnippet-conventions.mdc + autosnippet-skills.mdc)
|
|
8
|
-
* ④
|
|
8
|
+
* ④ Agent Instructions(AGENTS.md + CLAUDE.md + .github/copilot-instructions.md — 通过 Channel F 动态生成)
|
|
9
9
|
* ⑤ Constitution(AutoSnippet/constitution.yaml)
|
|
10
10
|
* ⑥ .gitignore(升级规则 + 清理旧版本残留)
|
|
11
11
|
* ⑦ Skills 路径迁移(.autosnippet/skills/ → AutoSnippet/skills/)
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from 'node:fs';
|
|
25
25
|
import { dirname, join, resolve } from 'node:path';
|
|
26
26
|
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { safeCopyFile } from '../service/cursor/FileProtection.js';
|
|
27
28
|
|
|
28
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
30
|
const __dirname = dirname(__filename);
|
|
@@ -47,8 +48,7 @@ export class UpgradeService {
|
|
|
47
48
|
}
|
|
48
49
|
if (!skillsOnly && !mcpOnly) {
|
|
49
50
|
results.push(this._upgradeCursorRules());
|
|
50
|
-
|
|
51
|
-
results.push(this._upgradeMirrorIDE('.trae'));
|
|
51
|
+
// NOTE: .qoder/ .trae/ 不再自动镜像,用户可通过 `asd mirror` 按需同步
|
|
52
52
|
results.push(this._upgradeSkillsTemplate());
|
|
53
53
|
results.push(this._upgradeCopilotInstructions());
|
|
54
54
|
results.push(this._upgradeConstitution());
|
|
@@ -169,6 +169,7 @@ export class UpgradeService {
|
|
|
169
169
|
|
|
170
170
|
/**
|
|
171
171
|
* 触发 Cursor Delivery Pipeline 动态生成
|
|
172
|
+
* 包含 Channel A-D + Channel F (AGENTS.md / CLAUDE.md / copilot-instructions)
|
|
172
173
|
* 非阻塞 — 失败不影响 upgrade 流程
|
|
173
174
|
*/
|
|
174
175
|
_triggerCursorDelivery() {
|
|
@@ -180,7 +181,9 @@ export class UpgradeService {
|
|
|
180
181
|
pipeline
|
|
181
182
|
.deliver()
|
|
182
183
|
.then((_result) => {})
|
|
183
|
-
.catch((_err) => {
|
|
184
|
+
.catch((_err) => {
|
|
185
|
+
/* fire-and-forget: delivery failure is non-critical during upgrade */
|
|
186
|
+
});
|
|
184
187
|
}
|
|
185
188
|
})
|
|
186
189
|
.catch(() => {
|
|
@@ -247,8 +250,13 @@ export class UpgradeService {
|
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
|
|
250
|
-
/* ═══ Copilot Instructions
|
|
253
|
+
/* ═══ Copilot Instructions (fallback static copy) ══════════════════ */
|
|
251
254
|
|
|
255
|
+
/**
|
|
256
|
+
* 静态模板回退 — 当 _triggerCursorDelivery() 无法运行时
|
|
257
|
+
* (无 DB 环境),至少保证有一份基础模板。
|
|
258
|
+
* Channel F 动态生成会覆盖此文件。
|
|
259
|
+
*/
|
|
252
260
|
_upgradeCopilotInstructions() {
|
|
253
261
|
const src = join(REPO_ROOT, 'templates', 'copilot-instructions.md');
|
|
254
262
|
if (!existsSync(src)) {
|
|
@@ -258,7 +266,9 @@ export class UpgradeService {
|
|
|
258
266
|
const destDir = join(this.projectRoot, '.github');
|
|
259
267
|
const dest = join(destDir, 'copilot-instructions.md');
|
|
260
268
|
mkdirSync(destDir, { recursive: true });
|
|
261
|
-
|
|
269
|
+
const { written } = safeCopyFile(src, dest);
|
|
270
|
+
if (!written) {
|
|
271
|
+
}
|
|
262
272
|
}
|
|
263
273
|
|
|
264
274
|
/* ═══ Constitution ══════════════════════════════════ */
|
package/lib/core/AstAnalyzer.js
CHANGED
|
@@ -128,7 +128,7 @@ function analyzeProject(files, lang, options) {
|
|
|
128
128
|
|
|
129
129
|
// SFC 预处理: .vue / .svelte 等文件 → 提取 <script> 块再交给 AST
|
|
130
130
|
if (preprocessFile) {
|
|
131
|
-
const ext = file.name ?
|
|
131
|
+
const ext = file.name ? `.${file.name.split('.').pop()}` : '';
|
|
132
132
|
const result = preprocessFile(content, ext);
|
|
133
133
|
if (result) {
|
|
134
134
|
content = result.content;
|
|
@@ -88,7 +88,7 @@ export async function ensureGrammars(detectedLanguages, options = {}) {
|
|
|
88
88
|
if (result.failed.length > 0) {
|
|
89
89
|
logger?.warn?.(
|
|
90
90
|
`[ensure-grammars] ${result.failed.length} grammar(s) missing. ` +
|
|
91
|
-
|
|
91
|
+
`Expected in: ${GRAMMARS_DIR}`
|
|
92
92
|
);
|
|
93
93
|
} else {
|
|
94
94
|
logger?.info?.('[ensure-grammars] All required grammar .wasm files available');
|
package/lib/core/ast/index.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { registerLanguage } from '../AstAnalyzer.js';
|
|
23
|
-
import { initParser,
|
|
23
|
+
import { initParser, isParserReady, loadLanguageWasm } from './parser-init.js';
|
|
24
24
|
|
|
25
25
|
let _loaded = false;
|
|
26
26
|
|
|
@@ -36,17 +36,68 @@ export function _resetForReload() {
|
|
|
36
36
|
* 语言注册表 — langId → { wasmFile, module, setGrammarFn, langId, tsxWasmFile?, setTsxGrammarFn? }
|
|
37
37
|
*/
|
|
38
38
|
const LANG_REGISTRY = [
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
|
|
39
|
+
{
|
|
40
|
+
langId: 'objectivec',
|
|
41
|
+
wasmFile: 'tree-sitter-objc.wasm',
|
|
42
|
+
module: './lang-objc.js',
|
|
43
|
+
setFn: 'setGrammar',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
langId: 'swift',
|
|
47
|
+
wasmFile: 'tree-sitter-swift.wasm',
|
|
48
|
+
module: './lang-swift.js',
|
|
49
|
+
setFn: 'setGrammar',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
langId: 'typescript',
|
|
53
|
+
wasmFile: 'tree-sitter-typescript.wasm',
|
|
54
|
+
module: './lang-typescript.js',
|
|
55
|
+
setFn: 'setGrammar',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
langId: 'tsx',
|
|
59
|
+
wasmFile: 'tree-sitter-tsx.wasm',
|
|
60
|
+
module: './lang-typescript.js',
|
|
61
|
+
setFn: 'setTsxGrammar',
|
|
62
|
+
pluginKey: 'tsxPlugin',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
langId: 'javascript',
|
|
66
|
+
wasmFile: 'tree-sitter-javascript.wasm',
|
|
67
|
+
module: './lang-javascript.js',
|
|
68
|
+
setFn: 'setGrammar',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
langId: 'python',
|
|
72
|
+
wasmFile: 'tree-sitter-python.wasm',
|
|
73
|
+
module: './lang-python.js',
|
|
74
|
+
setFn: 'setGrammar',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
langId: 'java',
|
|
78
|
+
wasmFile: 'tree-sitter-java.wasm',
|
|
79
|
+
module: './lang-java.js',
|
|
80
|
+
setFn: 'setGrammar',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
langId: 'kotlin',
|
|
84
|
+
wasmFile: 'tree-sitter-kotlin.wasm',
|
|
85
|
+
module: './lang-kotlin.js',
|
|
86
|
+
setFn: 'setGrammar',
|
|
87
|
+
},
|
|
47
88
|
{ langId: 'go', wasmFile: 'tree-sitter-go.wasm', module: './lang-go.js', setFn: 'setGrammar' },
|
|
48
|
-
{
|
|
49
|
-
|
|
89
|
+
{
|
|
90
|
+
langId: 'dart',
|
|
91
|
+
wasmFile: 'tree-sitter-dart.wasm',
|
|
92
|
+
module: './lang-dart.js',
|
|
93
|
+
setFn: 'setGrammar',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
langId: 'rust',
|
|
97
|
+
wasmFile: 'tree-sitter-rust.wasm',
|
|
98
|
+
module: './lang-rust.js',
|
|
99
|
+
setFn: 'setGrammar',
|
|
100
|
+
},
|
|
50
101
|
];
|
|
51
102
|
|
|
52
103
|
/**
|
|
@@ -146,8 +146,12 @@ function _parseClassDef(node, ctx) {
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
let kind = 'class';
|
|
149
|
-
if (isAbstract)
|
|
150
|
-
|
|
149
|
+
if (isAbstract) {
|
|
150
|
+
kind = 'abstract-class';
|
|
151
|
+
}
|
|
152
|
+
if (isSealed) {
|
|
153
|
+
kind = 'sealed-class';
|
|
154
|
+
}
|
|
151
155
|
|
|
152
156
|
ctx.classes.push({
|
|
153
157
|
name,
|
|
@@ -227,7 +231,9 @@ function _parseMixinDecl(node, ctx) {
|
|
|
227
231
|
);
|
|
228
232
|
const name = nameNode?.text || 'Unknown';
|
|
229
233
|
|
|
230
|
-
const onClause = node.namedChildren.find(
|
|
234
|
+
const onClause = node.namedChildren.find(
|
|
235
|
+
(c) => c.type === 'on_clause' || c.type === 'superclass'
|
|
236
|
+
);
|
|
231
237
|
const constraints = [];
|
|
232
238
|
if (onClause) {
|
|
233
239
|
for (let i = 0; i < onClause.namedChildCount; i++) {
|
|
@@ -262,9 +268,7 @@ function _parseExtensionDecl(node, ctx) {
|
|
|
262
268
|
const name = nameNode?.text || 'anonymous_extension';
|
|
263
269
|
|
|
264
270
|
// on Type
|
|
265
|
-
const onType = node.namedChildren.find(
|
|
266
|
-
(c) => c.type === 'type_identifier' && c !== nameNode
|
|
267
|
-
);
|
|
271
|
+
const onType = node.namedChildren.find((c) => c.type === 'type_identifier' && c !== nameNode);
|
|
268
272
|
|
|
269
273
|
ctx.categories.push({
|
|
270
274
|
name,
|
|
@@ -273,7 +277,9 @@ function _parseExtensionDecl(node, ctx) {
|
|
|
273
277
|
endLine: node.endPosition.row + 1,
|
|
274
278
|
});
|
|
275
279
|
|
|
276
|
-
const body = node.namedChildren.find(
|
|
280
|
+
const body = node.namedChildren.find(
|
|
281
|
+
(c) => c.type === 'class_body' || c.type === 'extension_body'
|
|
282
|
+
);
|
|
277
283
|
if (body) {
|
|
278
284
|
_walkClassBody(body, ctx, name);
|
|
279
285
|
}
|
|
@@ -310,9 +316,7 @@ function _parseFunctionDef(node, className) {
|
|
|
310
316
|
const isAsync = node.text.includes('async') || node.text.includes('async*');
|
|
311
317
|
const isOverride = node.text.includes('@override');
|
|
312
318
|
|
|
313
|
-
const body = node.namedChildren.find(
|
|
314
|
-
(c) => c.type === 'function_body' || c.type === 'block'
|
|
315
|
-
);
|
|
319
|
+
const body = node.namedChildren.find((c) => c.type === 'function_body' || c.type === 'block');
|
|
316
320
|
const bodyLines = body ? body.endPosition.row - body.startPosition.row + 1 : 0;
|
|
317
321
|
const complexity = body ? _estimateComplexity(body) : 1;
|
|
318
322
|
const nestingDepth = body ? _maxNesting(body, 0) : 0;
|
|
@@ -372,13 +376,17 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
|
|
|
372
376
|
const classPropMap = {};
|
|
373
377
|
for (const m of methods) {
|
|
374
378
|
if (m.className) {
|
|
375
|
-
if (!classMethodMap[m.className])
|
|
379
|
+
if (!classMethodMap[m.className]) {
|
|
380
|
+
classMethodMap[m.className] = [];
|
|
381
|
+
}
|
|
376
382
|
classMethodMap[m.className].push(m);
|
|
377
383
|
}
|
|
378
384
|
}
|
|
379
385
|
for (const p of properties) {
|
|
380
386
|
if (p.className) {
|
|
381
|
-
if (!classPropMap[p.className])
|
|
387
|
+
if (!classPropMap[p.className]) {
|
|
388
|
+
classPropMap[p.className] = [];
|
|
389
|
+
}
|
|
382
390
|
classPropMap[p.className].push(p);
|
|
383
391
|
}
|
|
384
392
|
}
|
|
@@ -430,10 +438,7 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
|
|
|
430
438
|
}
|
|
431
439
|
|
|
432
440
|
// ChangeNotifier (Provider pattern)
|
|
433
|
-
if (
|
|
434
|
-
cls.superclass === 'ChangeNotifier' ||
|
|
435
|
-
(cls.mixins && cls.mixins.includes('ChangeNotifier'))
|
|
436
|
-
) {
|
|
441
|
+
if (cls.superclass === 'ChangeNotifier' || cls.mixins?.includes('ChangeNotifier')) {
|
|
437
442
|
patterns.push({
|
|
438
443
|
type: 'change-notifier',
|
|
439
444
|
className: cls.name,
|
|
@@ -448,9 +453,7 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
|
|
|
448
453
|
const hasPrivateConstructor = classMethods.some(
|
|
449
454
|
(m) => m.kind === 'constructor' && m.name.startsWith('_')
|
|
450
455
|
);
|
|
451
|
-
const hasStaticInstance = classProps.some(
|
|
452
|
-
(p) => p.isStatic && (p.isFinal || p.isConst)
|
|
453
|
-
);
|
|
456
|
+
const hasStaticInstance = classProps.some((p) => p.isStatic && (p.isFinal || p.isConst));
|
|
454
457
|
const hasFactoryConstructor = classMethods.some((m) => m.kind === 'factory');
|
|
455
458
|
|
|
456
459
|
if (hasPrivateConstructor && (hasStaticInstance || hasFactoryConstructor)) {
|
|
@@ -493,7 +496,7 @@ function detectDartPatterns(root, lang, methods, properties, classes) {
|
|
|
493
496
|
}
|
|
494
497
|
|
|
495
498
|
// Freezed pattern — @freezed/@Freezed annotation + with _$ClassName mixin
|
|
496
|
-
if (cls.mixins
|
|
499
|
+
if (cls.mixins?.some((m) => m.startsWith('_$'))) {
|
|
497
500
|
patterns.push({
|
|
498
501
|
type: 'freezed',
|
|
499
502
|
className: cls.name,
|
|
@@ -548,7 +551,10 @@ function _detectStreamUsage(root, patterns) {
|
|
|
548
551
|
if (node.type === 'type_identifier' && node.text === 'Stream') {
|
|
549
552
|
streamCount++;
|
|
550
553
|
}
|
|
551
|
-
if (
|
|
554
|
+
if (
|
|
555
|
+
node.type === 'identifier' &&
|
|
556
|
+
(node.text === 'StreamController' || node.text === 'StreamSubscription')
|
|
557
|
+
) {
|
|
552
558
|
streamCount++;
|
|
553
559
|
}
|
|
554
560
|
for (let i = 0; i < node.namedChildCount; i++) {
|
package/lib/core/ast/lang-go.js
CHANGED
|
@@ -39,9 +39,7 @@ function walkGo(root, ctx) {
|
|
|
39
39
|
// 单行 import
|
|
40
40
|
const spec = child.namedChildren.find((c) => c.type === 'import_spec');
|
|
41
41
|
if (spec) {
|
|
42
|
-
const strLit = spec.namedChildren.find(
|
|
43
|
-
(c) => c.type === 'interpreted_string_literal'
|
|
44
|
-
);
|
|
42
|
+
const strLit = spec.namedChildren.find((c) => c.type === 'interpreted_string_literal');
|
|
45
43
|
if (strLit) {
|
|
46
44
|
ctx.imports.push(strLit.text.replace(/"/g, ''));
|
|
47
45
|
}
|
|
@@ -117,7 +115,7 @@ function _walkTypeDeclaration(node, ctx) {
|
|
|
117
115
|
|
|
118
116
|
function _parseStruct(name, structNode, specNode, ctx) {
|
|
119
117
|
const fields = [];
|
|
120
|
-
|
|
118
|
+
const embeddedTypes = [];
|
|
121
119
|
|
|
122
120
|
const fieldList = structNode.namedChildren.find((c) => c.type === 'field_declaration_list');
|
|
123
121
|
if (fieldList) {
|
|
@@ -326,14 +324,9 @@ function detectGoPatterns(root, lang, methods, properties, classes) {
|
|
|
326
324
|
continue;
|
|
327
325
|
}
|
|
328
326
|
// 检查是否有 package-level var 指向此 struct
|
|
329
|
-
const hasPackageVar = properties.some(
|
|
330
|
-
(p) => !p.className && !p.isConst && !p.isExported
|
|
331
|
-
);
|
|
327
|
+
const hasPackageVar = properties.some((p) => !p.className && !p.isConst && !p.isExported);
|
|
332
328
|
const hasNewFunc = methods.some(
|
|
333
|
-
(m) =>
|
|
334
|
-
!m.className &&
|
|
335
|
-
/^(?:New|Get|Default)/.test(m.name) &&
|
|
336
|
-
m.isExported
|
|
329
|
+
(m) => !m.className && /^(?:New|Get|Default)/.test(m.name) && m.isExported
|
|
337
330
|
);
|
|
338
331
|
if (hasPackageVar && hasNewFunc) {
|
|
339
332
|
patterns.push({
|
|
@@ -346,11 +339,7 @@ function detectGoPatterns(root, lang, methods, properties, classes) {
|
|
|
346
339
|
|
|
347
340
|
// Factory: New* / Create* package-level functions
|
|
348
341
|
for (const m of methods) {
|
|
349
|
-
if (
|
|
350
|
-
!m.className &&
|
|
351
|
-
m.isExported &&
|
|
352
|
-
/^(?:New|Create|Make|Build|Open|Connect)/.test(m.name)
|
|
353
|
-
) {
|
|
342
|
+
if (!m.className && m.isExported && /^(?:New|Create|Make|Build|Open|Connect)/.test(m.name)) {
|
|
354
343
|
patterns.push({
|
|
355
344
|
type: 'factory',
|
|
356
345
|
methodName: m.name,
|
|
@@ -446,10 +435,7 @@ function _countParams(paramList) {
|
|
|
446
435
|
let count = 0;
|
|
447
436
|
for (let i = 0; i < paramList.namedChildCount; i++) {
|
|
448
437
|
const child = paramList.namedChild(i);
|
|
449
|
-
if (
|
|
450
|
-
child.type === 'parameter_declaration' ||
|
|
451
|
-
child.type === 'variadic_parameter_declaration'
|
|
452
|
-
) {
|
|
438
|
+
if (child.type === 'parameter_declaration' || child.type === 'variadic_parameter_declaration') {
|
|
453
439
|
// Each identifier in the same declaration is a parameter
|
|
454
440
|
const ids = child.namedChildren.filter((c) => c.type === 'identifier');
|
|
455
441
|
count += Math.max(ids.length, 1);
|