autosnippet 3.0.13 → 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 +24 -19
- package/config/default.json +1 -1
- package/lib/bootstrap.js +4 -4
- package/lib/cli/SetupService.js +29 -29
- package/lib/cli/UpgradeService.js +3 -2
- 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/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 +1 -2
- package/lib/external/mcp/handlers/bootstrap/pipeline/checkpoint.js +7 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +55 -26
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/refine.js +3 -1
- package/lib/external/mcp/handlers/bootstrap.js +4 -10
- package/lib/external/mcp/handlers/browse.js +6 -2
- package/lib/external/mcp/handlers/guard.js +6 -2
- 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 +1 -1
- 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 +34 -11
- 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/XcodeIntegration.js +10 -12
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +6 -1
- 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 +31 -22
- package/lib/service/chat/HandoffProtocol.js +5 -2
- package/lib/service/chat/tools/composite.js +3 -2
- package/lib/service/chat/tools/index.js +60 -71
- package/lib/service/chat/tools/infrastructure.js +9 -4
- package/lib/service/chat/tools/lifecycle.js +22 -5
- package/lib/service/chat/tools/project-access.js +5 -9
- package/lib/service/chat/tools.js +1 -2
- package/lib/service/cursor/AgentInstructionsGenerator.js +33 -15
- package/lib/service/cursor/CursorDeliveryPipeline.js +2 -1
- package/lib/service/cursor/KnowledgeCompressor.js +16 -7
- package/lib/service/guard/ComplianceReporter.js +5 -2
- package/lib/service/guard/GuardCheckEngine.js +53 -26
- package/lib/service/guard/GuardCodeChecks.js +217 -188
- package/lib/service/guard/GuardCrossFileChecks.js +203 -184
- package/lib/service/guard/GuardPatternUtils.js +17 -10
- package/lib/service/module/ModuleService.js +180 -56
- package/lib/service/recipe/RecipeCandidateValidator.js +11 -8
- 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 +54 -36
- package/lib/service/wiki/WikiRenderers.js +105 -80
- package/lib/service/wiki/WikiUtils.js +217 -80
- package/lib/shared/LanguageService.js +111 -53
- 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
|
@@ -238,15 +238,15 @@ const LANG_ALIASES = Object.freeze({
|
|
|
238
238
|
|
|
239
239
|
/** @type {Readonly<Record<string, readonly string[]>>} */
|
|
240
240
|
const ECO_TO_LANGS = Object.freeze({
|
|
241
|
-
spm:
|
|
242
|
-
node:
|
|
243
|
-
go:
|
|
244
|
-
jvm:
|
|
245
|
-
python:
|
|
246
|
-
dart:
|
|
247
|
-
rust:
|
|
248
|
-
dotnet:
|
|
249
|
-
ruby:
|
|
241
|
+
spm: Object.freeze(['swift', 'objectivec']),
|
|
242
|
+
node: Object.freeze(['javascript', 'typescript']),
|
|
243
|
+
go: Object.freeze(['go']),
|
|
244
|
+
jvm: Object.freeze(['java', 'kotlin']),
|
|
245
|
+
python: Object.freeze(['python']),
|
|
246
|
+
dart: Object.freeze(['dart']),
|
|
247
|
+
rust: Object.freeze(['rust']),
|
|
248
|
+
dotnet: Object.freeze(['csharp']),
|
|
249
|
+
ruby: Object.freeze(['ruby']),
|
|
250
250
|
generic: Object.freeze([]),
|
|
251
251
|
});
|
|
252
252
|
|
|
@@ -260,41 +260,59 @@ const ECO_TO_LANGS = Object.freeze({
|
|
|
260
260
|
*/
|
|
261
261
|
const BUILD_SYSTEM_MARKERS = Object.freeze([
|
|
262
262
|
// Apple / iOS
|
|
263
|
-
{ file: 'Package.swift',
|
|
264
|
-
{ file: 'Podfile',
|
|
263
|
+
{ file: 'Package.swift', eco: 'spm', buildTool: 'SPM' },
|
|
264
|
+
{ file: 'Podfile', eco: 'spm', buildTool: 'CocoaPods' },
|
|
265
265
|
// JS / TS (lock files before package.json to detect specific tool)
|
|
266
|
-
{ file: 'yarn.lock',
|
|
267
|
-
{ file: 'pnpm-lock.yaml',
|
|
268
|
-
{ file: 'package.json',
|
|
266
|
+
{ file: 'yarn.lock', eco: 'node', buildTool: 'Yarn' },
|
|
267
|
+
{ file: 'pnpm-lock.yaml', eco: 'node', buildTool: 'pnpm' },
|
|
268
|
+
{ file: 'package.json', eco: 'node', buildTool: 'npm' },
|
|
269
269
|
// Python
|
|
270
|
-
{ file: 'Pipfile',
|
|
271
|
-
{ file: 'pyproject.toml',
|
|
272
|
-
{ file: 'setup.py',
|
|
273
|
-
{ file: 'requirements.txt', eco: 'python',
|
|
270
|
+
{ file: 'Pipfile', eco: 'python', buildTool: 'Pipenv' },
|
|
271
|
+
{ file: 'pyproject.toml', eco: 'python', buildTool: 'Poetry' },
|
|
272
|
+
{ file: 'setup.py', eco: 'python', buildTool: 'setuptools' },
|
|
273
|
+
{ file: 'requirements.txt', eco: 'python', buildTool: 'pip' },
|
|
274
274
|
// Go
|
|
275
|
-
{ file: 'go.mod',
|
|
275
|
+
{ file: 'go.mod', eco: 'go', buildTool: 'Go Modules' },
|
|
276
276
|
// Rust
|
|
277
|
-
{ file: 'Cargo.toml',
|
|
277
|
+
{ file: 'Cargo.toml', eco: 'rust', buildTool: 'Cargo' },
|
|
278
278
|
// JVM
|
|
279
|
-
{ file: 'pom.xml',
|
|
280
|
-
{ file: 'build.gradle',
|
|
281
|
-
{ file: 'build.gradle.kts', eco: 'jvm',
|
|
279
|
+
{ file: 'pom.xml', eco: 'jvm', buildTool: 'Maven' },
|
|
280
|
+
{ file: 'build.gradle', eco: 'jvm', buildTool: 'Gradle' },
|
|
281
|
+
{ file: 'build.gradle.kts', eco: 'jvm', buildTool: 'Gradle (Kotlin)' },
|
|
282
282
|
// Dart / Flutter
|
|
283
|
-
{ file: 'pubspec.yaml',
|
|
284
|
-
{ file: 'melos.yaml',
|
|
283
|
+
{ file: 'pubspec.yaml', eco: 'dart', buildTool: 'Flutter' },
|
|
284
|
+
{ file: 'melos.yaml', eco: 'dart', buildTool: 'Melos' },
|
|
285
285
|
// C# / .NET
|
|
286
|
-
{ file: '*.csproj',
|
|
287
|
-
{ file: '*.sln',
|
|
286
|
+
{ file: '*.csproj', eco: 'dotnet', buildTool: '.NET' },
|
|
287
|
+
{ file: '*.sln', eco: 'dotnet', buildTool: '.NET' },
|
|
288
288
|
// Ruby
|
|
289
|
-
{ file: 'Gemfile',
|
|
289
|
+
{ file: 'Gemfile', eco: 'ruby', buildTool: 'Bundler' },
|
|
290
290
|
]);
|
|
291
291
|
|
|
292
292
|
/** 扫描目录时跳过的标准目录(性能优化) */
|
|
293
|
-
const SCAN_SKIP_DIRS = Object.freeze(
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
293
|
+
const SCAN_SKIP_DIRS = Object.freeze(
|
|
294
|
+
new Set([
|
|
295
|
+
'.git',
|
|
296
|
+
'node_modules',
|
|
297
|
+
'.build',
|
|
298
|
+
'build',
|
|
299
|
+
'dist',
|
|
300
|
+
'target',
|
|
301
|
+
'out',
|
|
302
|
+
'vendor',
|
|
303
|
+
'.cache',
|
|
304
|
+
'Pods',
|
|
305
|
+
'DerivedData',
|
|
306
|
+
'__pycache__',
|
|
307
|
+
'.venv',
|
|
308
|
+
'venv',
|
|
309
|
+
'.gradle',
|
|
310
|
+
'Carthage',
|
|
311
|
+
'.fvm',
|
|
312
|
+
'.dart_tool',
|
|
313
|
+
'.cargo',
|
|
314
|
+
])
|
|
315
|
+
);
|
|
298
316
|
|
|
299
317
|
// ═══════════════════════════════════════════════════════════
|
|
300
318
|
// LanguageService — 静态单例
|
|
@@ -309,7 +327,9 @@ export class LanguageService {
|
|
|
309
327
|
* @returns {string} 语言 ID,如 'swift', 'typescript', 'python', 'unknown'
|
|
310
328
|
*/
|
|
311
329
|
static inferLang(filename) {
|
|
312
|
-
if (!filename || typeof filename !== 'string')
|
|
330
|
+
if (!filename || typeof filename !== 'string') {
|
|
331
|
+
return 'unknown';
|
|
332
|
+
}
|
|
313
333
|
const dot = filename.lastIndexOf('.');
|
|
314
334
|
if (dot === -1) {
|
|
315
335
|
return 'unknown';
|
|
@@ -324,7 +344,9 @@ export class LanguageService {
|
|
|
324
344
|
* @returns {string}
|
|
325
345
|
*/
|
|
326
346
|
static langFromExt(ext) {
|
|
327
|
-
if (!ext || typeof ext !== 'string')
|
|
347
|
+
if (!ext || typeof ext !== 'string') {
|
|
348
|
+
return 'unknown';
|
|
349
|
+
}
|
|
328
350
|
return EXT_TO_LANG[ext.toLowerCase()] || 'unknown';
|
|
329
351
|
}
|
|
330
352
|
|
|
@@ -344,9 +366,13 @@ export class LanguageService {
|
|
|
344
366
|
* @returns {string} 规范化语言 ID
|
|
345
367
|
*/
|
|
346
368
|
static normalize(langId) {
|
|
347
|
-
if (!langId || typeof langId !== 'string')
|
|
369
|
+
if (!langId || typeof langId !== 'string') {
|
|
370
|
+
return 'unknown';
|
|
371
|
+
}
|
|
348
372
|
const lower = langId.toLowerCase().trim();
|
|
349
|
-
if (KNOWN_PROGRAMMING_LANGS.has(lower))
|
|
373
|
+
if (KNOWN_PROGRAMMING_LANGS.has(lower)) {
|
|
374
|
+
return lower;
|
|
375
|
+
}
|
|
350
376
|
return LANG_ALIASES[lower] || lower;
|
|
351
377
|
}
|
|
352
378
|
|
|
@@ -527,10 +553,14 @@ export class LanguageService {
|
|
|
527
553
|
* @returns {string|null} - 如 '.go', '.swift', '.py';未知返回 null
|
|
528
554
|
*/
|
|
529
555
|
static extForLang(langId) {
|
|
530
|
-
if (!langId)
|
|
556
|
+
if (!langId) {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
531
559
|
const lower = langId.toLowerCase();
|
|
532
560
|
for (const [ext, lang] of Object.entries(EXT_TO_LANG)) {
|
|
533
|
-
if (lang === lower)
|
|
561
|
+
if (lang === lower) {
|
|
562
|
+
return ext;
|
|
563
|
+
}
|
|
534
564
|
}
|
|
535
565
|
return null;
|
|
536
566
|
}
|
|
@@ -585,13 +615,17 @@ export class LanguageService {
|
|
|
585
615
|
* @returns {Array<{ eco: string, buildTool: string }>}
|
|
586
616
|
*/
|
|
587
617
|
static matchBuildMarkers(entryNames) {
|
|
588
|
-
if (!Array.isArray(entryNames) || entryNames.length === 0)
|
|
618
|
+
if (!Array.isArray(entryNames) || entryNames.length === 0) {
|
|
619
|
+
return [];
|
|
620
|
+
}
|
|
589
621
|
const nameSet = new Set(entryNames);
|
|
590
622
|
const results = [];
|
|
591
623
|
const seenEco = new Set();
|
|
592
624
|
|
|
593
625
|
for (const marker of BUILD_SYSTEM_MARKERS) {
|
|
594
|
-
if (seenEco.has(marker.eco))
|
|
626
|
+
if (seenEco.has(marker.eco)) {
|
|
627
|
+
continue;
|
|
628
|
+
}
|
|
595
629
|
const isGlob = marker.file.startsWith('*');
|
|
596
630
|
const matched = isGlob
|
|
597
631
|
? entryNames.some((n) => n.endsWith(marker.file.slice(1)))
|
|
@@ -618,7 +652,9 @@ export class LanguageService {
|
|
|
618
652
|
* @returns {string[]} 规范化语言 ID 数组(如 ['rust', 'dart'])
|
|
619
653
|
*/
|
|
620
654
|
static detectProjectLanguages(projectRoot, opts = {}) {
|
|
621
|
-
if (!projectRoot || typeof projectRoot !== 'string')
|
|
655
|
+
if (!projectRoot || typeof projectRoot !== 'string') {
|
|
656
|
+
return [];
|
|
657
|
+
}
|
|
622
658
|
const { discovererIds, maxDepth = 2 } = opts;
|
|
623
659
|
|
|
624
660
|
// ── Path 1: 从 Discoverer ID 映射 ──
|
|
@@ -627,9 +663,13 @@ export class LanguageService {
|
|
|
627
663
|
if (nonGeneric.length > 0) {
|
|
628
664
|
const langSet = new Set();
|
|
629
665
|
for (const did of nonGeneric) {
|
|
630
|
-
for (const lang of ECO_TO_LANGS[did] || [])
|
|
666
|
+
for (const lang of ECO_TO_LANGS[did] || []) {
|
|
667
|
+
langSet.add(lang);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (langSet.size > 0) {
|
|
671
|
+
return [...langSet];
|
|
631
672
|
}
|
|
632
|
-
if (langSet.size > 0) return [...langSet];
|
|
633
673
|
}
|
|
634
674
|
}
|
|
635
675
|
|
|
@@ -639,20 +679,28 @@ export class LanguageService {
|
|
|
639
679
|
const scanDir = (dir) => {
|
|
640
680
|
try {
|
|
641
681
|
for (const marker of BUILD_SYSTEM_MARKERS) {
|
|
642
|
-
if (seenEco.has(marker.eco))
|
|
682
|
+
if (seenEco.has(marker.eco)) {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
643
685
|
const isGlob = marker.file.startsWith('*');
|
|
644
686
|
let matched = false;
|
|
645
687
|
if (isGlob) {
|
|
646
688
|
try {
|
|
647
689
|
const suffix = marker.file.slice(1);
|
|
648
690
|
matched = readdirSync(dir).some((n) => n.endsWith(suffix));
|
|
649
|
-
} catch {
|
|
691
|
+
} catch {
|
|
692
|
+
/* skip */
|
|
693
|
+
}
|
|
650
694
|
} else {
|
|
651
695
|
matched = existsSync(join(dir, marker.file));
|
|
652
696
|
}
|
|
653
|
-
if (matched)
|
|
697
|
+
if (matched) {
|
|
698
|
+
seenEco.add(marker.eco);
|
|
699
|
+
}
|
|
654
700
|
}
|
|
655
|
-
} catch {
|
|
701
|
+
} catch {
|
|
702
|
+
/* skip unreadable dir */
|
|
703
|
+
}
|
|
656
704
|
};
|
|
657
705
|
|
|
658
706
|
// Level 0: 项目根目录
|
|
@@ -664,22 +712,32 @@ export class LanguageService {
|
|
|
664
712
|
const queue = [[projectRoot, 0]];
|
|
665
713
|
while (queue.length > 0) {
|
|
666
714
|
const [dir, depth] = queue.shift();
|
|
667
|
-
if (depth >= maxDepth)
|
|
715
|
+
if (depth >= maxDepth) {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
668
718
|
try {
|
|
669
719
|
for (const ent of readdirSync(dir, { withFileTypes: true })) {
|
|
670
|
-
if (!ent.isDirectory() || ent.name.startsWith('.') || SCAN_SKIP_DIRS.has(ent.name))
|
|
720
|
+
if (!ent.isDirectory() || ent.name.startsWith('.') || SCAN_SKIP_DIRS.has(ent.name)) {
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
671
723
|
const sub = join(dir, ent.name);
|
|
672
724
|
scanDir(sub);
|
|
673
|
-
if (depth + 1 < maxDepth)
|
|
725
|
+
if (depth + 1 < maxDepth) {
|
|
726
|
+
queue.push([sub, depth + 1]);
|
|
727
|
+
}
|
|
674
728
|
}
|
|
675
|
-
} catch {
|
|
729
|
+
} catch {
|
|
730
|
+
/* skip */
|
|
731
|
+
}
|
|
676
732
|
}
|
|
677
733
|
}
|
|
678
734
|
|
|
679
735
|
// ── 将生态 ID 转为语言 ID ──
|
|
680
736
|
const langSet = new Set();
|
|
681
737
|
for (const eco of seenEco) {
|
|
682
|
-
for (const lang of ECO_TO_LANGS[eco] || [])
|
|
738
|
+
for (const lang of ECO_TO_LANGS[eco] || []) {
|
|
739
|
+
langSet.add(lang);
|
|
740
|
+
}
|
|
683
741
|
}
|
|
684
742
|
|
|
685
743
|
// ── 启发式: node 与其他生态共存时,JS/TS 通常只是构建工具,去掉 ──
|
package/lib/shared/PathGuard.js
CHANGED
|
@@ -320,14 +320,6 @@ class PathGuard {
|
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
/**
|
|
324
|
-
* 延迟加载 fs(避免循环依赖)
|
|
325
|
-
*/
|
|
326
|
-
function _await_fs() {
|
|
327
|
-
// biome-ignore lint/security/noGlobalEval: intentional CJS require fallback for avoiding circular deps
|
|
328
|
-
return eval("require('fs')");
|
|
329
|
-
}
|
|
330
|
-
|
|
331
323
|
// 单例 — 整个进程共享
|
|
332
324
|
const pathGuard = new PathGuard();
|
|
333
325
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autosnippet",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Extract code patterns into a knowledge base for AI coding assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/bootstrap.js",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"postinstall": "node scripts/postinstall-safe.js",
|
|
12
|
-
"prepublishOnly": "node scripts/build-native-ui.js",
|
|
12
|
+
"prepublishOnly": "npm run build:dashboard && node scripts/build-native-ui.js",
|
|
13
13
|
"build:native-ui": "swiftc resources/native-ui/main.swift resources/native-ui/combined-window.swift -o resources/native-ui/native-ui -framework AppKit",
|
|
14
14
|
"install:cursor-skill": "node scripts/install-cursor-skill.js",
|
|
15
15
|
"install:cursor-skill:mcp": "node scripts/install-cursor-skill.js --mcp",
|
|
@@ -71,18 +71,14 @@
|
|
|
71
71
|
"url": "git+https://github.com/GxFn/AutoSnippet.git"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@google/generative-ai": "^0.21.0",
|
|
75
74
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
76
|
-
"axios": "^1.6.7",
|
|
77
75
|
"better-sqlite3": "^9.2.2",
|
|
78
|
-
"chalk": "^5.3.0",
|
|
79
76
|
"chokidar": "^3.6.0",
|
|
80
77
|
"commander": "^11.1.0",
|
|
81
78
|
"cors": "^2.8.5",
|
|
82
79
|
"dotenv": "^16.3.1",
|
|
83
80
|
"express": "^4.18.2",
|
|
84
81
|
"helmet": "^7.1.0",
|
|
85
|
-
"inquirer": "^9.2.12",
|
|
86
82
|
"js-yaml": "^4.1.0",
|
|
87
83
|
"minimist": "^1.2.5",
|
|
88
84
|
"open": "^8.0.4",
|
|
@@ -91,8 +87,7 @@
|
|
|
91
87
|
"undici": "^6.23.0",
|
|
92
88
|
"uuid": "^9.0.1",
|
|
93
89
|
"web-tree-sitter": "^0.25.0",
|
|
94
|
-
"winston": "^3.11.0"
|
|
95
|
-
"zod": "^4.3.6"
|
|
90
|
+
"winston": "^3.11.0"
|
|
96
91
|
},
|
|
97
92
|
"bugs": {
|
|
98
93
|
"url": "https://github.com/GxFn/AutoSnippet/issues"
|
|
@@ -122,7 +117,6 @@
|
|
|
122
117
|
"@biomejs/biome": "2.0.0",
|
|
123
118
|
"@types/node": "^20.10.6",
|
|
124
119
|
"jest": "^30.2.0",
|
|
125
|
-
"mocha": "^11.7.5",
|
|
126
120
|
"nodemon": "^3.1.13",
|
|
127
121
|
"prettier": "^3.1.1",
|
|
128
122
|
"supertest": "^6.3.3"
|
|
@@ -177,48 +177,48 @@ async function benchmark() {
|
|
|
177
177
|
results.push(row);
|
|
178
178
|
process.stdout.write(
|
|
179
179
|
`✓ ${name.padEnd(20)} ${row.files.toString().padStart(5)} files | ` +
|
|
180
|
-
|
|
181
|
-
|
|
180
|
+
`detect=${row.detectMs}ms load=${row.loadMs}ms collect=${row.fileCollectMs}ms ` +
|
|
181
|
+
`ast=${row.astMs}ms total=${row.totalMs}ms\n`
|
|
182
182
|
);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
// ── 汇总表 ──────────────────────────────────────────────────────
|
|
186
|
-
process.stdout.write(
|
|
186
|
+
process.stdout.write(`\n${'═'.repeat(120)}\n`);
|
|
187
187
|
process.stdout.write(
|
|
188
188
|
'Project'.padEnd(22) +
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
189
|
+
'Files'.padStart(6) +
|
|
190
|
+
'Disc'.padStart(10) +
|
|
191
|
+
'Detect'.padStart(8) +
|
|
192
|
+
'Load'.padStart(8) +
|
|
193
|
+
'Collect'.padStart(9) +
|
|
194
|
+
'Lang'.padStart(10) +
|
|
195
|
+
'AST'.padStart(8) +
|
|
196
|
+
'Cls'.padStart(5) +
|
|
197
|
+
'Enh'.padStart(8) +
|
|
198
|
+
'DimC'.padStart(6) +
|
|
199
|
+
'Total'.padStart(8) +
|
|
200
|
+
' Packs\n'
|
|
201
201
|
);
|
|
202
|
-
process.stdout.write('─'.repeat(120)
|
|
202
|
+
process.stdout.write(`${'─'.repeat(120)}\n`);
|
|
203
203
|
|
|
204
204
|
for (const r of results) {
|
|
205
205
|
process.stdout.write(
|
|
206
206
|
r.project.padEnd(22) +
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
207
|
+
r.files.toString().padStart(6) +
|
|
208
|
+
r.discoverer.padStart(10) +
|
|
209
|
+
`${r.detectMs}ms`.padStart(8) +
|
|
210
|
+
`${r.loadMs}ms`.padStart(8) +
|
|
211
|
+
`${r.fileCollectMs}ms`.padStart(9) +
|
|
212
|
+
(r.primaryLang || '?').padStart(10) +
|
|
213
|
+
`${r.astMs}ms`.padStart(8) +
|
|
214
|
+
r.astClasses.toString().padStart(5) +
|
|
215
|
+
`${r.enhancementMs}ms`.padStart(8) +
|
|
216
|
+
`${r.dimCopyMs}ms`.padStart(6) +
|
|
217
|
+
`${r.totalMs}ms`.padStart(8) +
|
|
218
|
+
` ${r.enhPacks}\n`
|
|
219
219
|
);
|
|
220
220
|
}
|
|
221
|
-
process.stdout.write('═'.repeat(120)
|
|
221
|
+
process.stdout.write(`${'═'.repeat(120)}\n`);
|
|
222
222
|
|
|
223
223
|
// 性能阈值检查
|
|
224
224
|
let warnings = 0;
|
|
@@ -72,13 +72,20 @@ function readSnippet(filePath, maxChars) {
|
|
|
72
72
|
function detectLanguageByExt(ext) {
|
|
73
73
|
const map = {
|
|
74
74
|
'.swift': 'swift',
|
|
75
|
-
'.m': 'objectivec',
|
|
76
|
-
'.
|
|
77
|
-
'.
|
|
75
|
+
'.m': 'objectivec',
|
|
76
|
+
'.h': 'objectivec',
|
|
77
|
+
'.mm': 'objectivec',
|
|
78
|
+
'.js': 'javascript',
|
|
79
|
+
'.mjs': 'javascript',
|
|
80
|
+
'.jsx': 'javascript',
|
|
81
|
+
'.ts': 'typescript',
|
|
82
|
+
'.tsx': 'typescript',
|
|
78
83
|
'.py': 'python',
|
|
79
|
-
'.java': 'java',
|
|
84
|
+
'.java': 'java',
|
|
85
|
+
'.kt': 'kotlin',
|
|
80
86
|
'.go': 'go',
|
|
81
|
-
'.rs': 'rust',
|
|
87
|
+
'.rs': 'rust',
|
|
88
|
+
'.rb': 'ruby',
|
|
82
89
|
};
|
|
83
90
|
return map[ext] || 'text';
|
|
84
91
|
}
|
|
@@ -109,18 +116,7 @@ function buildRecipe({ title, trigger, language, sourceFile, snippet }) {
|
|
|
109
116
|
|
|
110
117
|
const DEFAULT_EXTS = '.swift,.m,.h,.mm,.js,.mjs,.jsx,.ts,.tsx,.py,.java,.kt,.go';
|
|
111
118
|
|
|
112
|
-
function printUsage() {
|
|
113
|
-
console.log(`Usage: generate-recipe-drafts [options]
|
|
114
|
-
|
|
115
|
-
Options:
|
|
116
|
-
--projectRoot <path> 项目根目录 (默认: 当前目录)
|
|
117
|
-
--targetDir <path> 要扫描的源码目录 (默认: projectRoot)
|
|
118
|
-
--outDir <path> 输出目录 (默认: <projectRoot>/autosnippet-drafts/recipes)
|
|
119
|
-
--exts <list> 扫描的扩展名,逗号分隔 (默认: ${DEFAULT_EXTS})
|
|
120
|
-
--maxChars <n> 单文件最大截取字符数 (默认: 4000)
|
|
121
|
-
--help 显示帮助
|
|
122
|
-
`);
|
|
123
|
-
}
|
|
119
|
+
function printUsage() {}
|
|
124
120
|
|
|
125
121
|
function main() {
|
|
126
122
|
const args = parseArgs(process.argv);
|
|
@@ -130,13 +126,9 @@ function main() {
|
|
|
130
126
|
process.exit(0);
|
|
131
127
|
}
|
|
132
128
|
|
|
133
|
-
const projectRoot = args.projectRoot
|
|
134
|
-
? path.resolve(args.projectRoot)
|
|
135
|
-
: process.cwd();
|
|
129
|
+
const projectRoot = args.projectRoot ? path.resolve(args.projectRoot) : process.cwd();
|
|
136
130
|
|
|
137
|
-
const targetDir = args.targetDir
|
|
138
|
-
? path.resolve(args.targetDir)
|
|
139
|
-
: projectRoot;
|
|
131
|
+
const targetDir = args.targetDir ? path.resolve(args.targetDir) : projectRoot;
|
|
140
132
|
|
|
141
133
|
const outDir = args.outDir
|
|
142
134
|
? path.resolve(args.outDir)
|
|
@@ -159,7 +151,7 @@ function main() {
|
|
|
159
151
|
const files = [];
|
|
160
152
|
walk(targetDir, exts, files);
|
|
161
153
|
|
|
162
|
-
let
|
|
154
|
+
let _count = 0;
|
|
163
155
|
for (const filePath of files) {
|
|
164
156
|
const ext = path.extname(filePath);
|
|
165
157
|
const base = path.basename(filePath, ext);
|
|
@@ -180,10 +172,8 @@ function main() {
|
|
|
180
172
|
const safeName = `${base}${ext}`.replace(/\./g, '_');
|
|
181
173
|
const outFile = path.join(outDir, `${safeName}.md`);
|
|
182
174
|
fs.writeFileSync(outFile, recipe, 'utf8');
|
|
183
|
-
|
|
175
|
+
_count++;
|
|
184
176
|
}
|
|
185
|
-
|
|
186
|
-
console.log(`✅ 生成 ${count} 个 recipe 草稿 → ${outDir}`);
|
|
187
177
|
}
|
|
188
178
|
|
|
189
179
|
main();
|