autosnippet 3.0.1 → 3.0.3
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 +231 -324
- package/bin/api-server.js +1 -1
- package/bin/cli.js +204 -244
- package/bin/mcp-server.js +17 -4
- package/config/knowledge-base.config.js +132 -132
- package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
- package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
- package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/bootstrap.js +8 -8
- package/lib/cli/AiScanService.js +86 -40
- package/lib/cli/KnowledgeSyncService.js +113 -74
- package/lib/cli/SetupService.js +441 -282
- package/lib/cli/UpgradeService.js +68 -107
- package/lib/core/AstAnalyzer.js +276 -597
- package/lib/core/ast/ProjectGraph.js +101 -40
- package/lib/core/ast/ensure-grammars.js +232 -0
- package/lib/core/ast/index.js +115 -0
- package/lib/core/ast/lang-dart.js +661 -0
- package/lib/core/ast/lang-go.js +530 -0
- package/lib/core/ast/lang-java.js +435 -0
- package/lib/core/ast/lang-javascript.js +272 -0
- package/lib/core/ast/lang-kotlin.js +423 -0
- package/lib/core/ast/lang-objc.js +388 -0
- package/lib/core/ast/lang-python.js +371 -0
- package/lib/core/ast/lang-swift.js +337 -0
- package/lib/core/ast/lang-typescript.js +503 -0
- package/lib/core/capability/CapabilityProbe.js +18 -9
- package/lib/core/constitution/Constitution.js +2 -3
- package/lib/core/constitution/ConstitutionValidator.js +65 -24
- package/lib/core/discovery/DartDiscoverer.js +534 -0
- package/lib/core/discovery/DiscovererRegistry.js +83 -0
- package/lib/core/discovery/GenericDiscoverer.js +225 -0
- package/lib/core/discovery/GoDiscoverer.js +541 -0
- package/lib/core/discovery/JvmDiscoverer.js +506 -0
- package/lib/core/discovery/NodeDiscoverer.js +466 -0
- package/lib/core/discovery/ProjectDiscoverer.js +93 -0
- package/lib/core/discovery/PythonDiscoverer.js +338 -0
- package/lib/core/discovery/SpmDiscoverer.js +5 -0
- package/lib/core/discovery/index.js +53 -0
- package/lib/core/enhancement/EnhancementPack.js +71 -0
- package/lib/core/enhancement/EnhancementRegistry.js +47 -0
- package/lib/core/enhancement/android-enhancement.js +102 -0
- package/lib/core/enhancement/django-enhancement.js +70 -0
- package/lib/core/enhancement/fastapi-enhancement.js +63 -0
- package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
- package/lib/core/enhancement/go-web-enhancement.js +201 -0
- package/lib/core/enhancement/index.js +65 -0
- package/lib/core/enhancement/node-server-enhancement.js +88 -0
- package/lib/core/enhancement/react-enhancement.js +86 -0
- package/lib/core/enhancement/spring-enhancement.js +112 -0
- package/lib/core/enhancement/vue-enhancement.js +96 -0
- package/lib/core/gateway/Gateway.js +8 -9
- package/lib/core/gateway/GatewayActionRegistry.js +1 -1
- package/lib/core/permission/PermissionManager.js +12 -8
- package/lib/domain/index.js +13 -9
- package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
- package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
- package/lib/domain/knowledge/Lifecycle.js +22 -22
- package/lib/domain/knowledge/index.js +9 -12
- package/lib/domain/knowledge/values/Constraints.js +31 -21
- package/lib/domain/knowledge/values/Content.js +21 -13
- package/lib/domain/knowledge/values/Quality.js +31 -18
- package/lib/domain/knowledge/values/Reasoning.js +20 -12
- package/lib/domain/knowledge/values/Relations.js +37 -25
- package/lib/domain/knowledge/values/Stats.js +18 -12
- package/lib/domain/knowledge/values/index.js +4 -3
- package/lib/domain/snippet/Snippet.js +35 -10
- package/lib/external/ai/AiFactory.js +48 -16
- package/lib/external/ai/AiProvider.js +184 -90
- package/lib/external/ai/providers/ClaudeProvider.js +25 -12
- package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
- package/lib/external/ai/providers/MockProvider.js +9 -3
- package/lib/external/ai/providers/OpenAiProvider.js +51 -29
- package/lib/external/mcp/McpServer.js +66 -36
- package/lib/external/mcp/errorHandler.js +23 -11
- package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
- package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
- package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
- package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
- package/lib/external/mcp/handlers/bootstrap.js +691 -168
- package/lib/external/mcp/handlers/browse.js +66 -22
- package/lib/external/mcp/handlers/candidate.js +118 -35
- package/lib/external/mcp/handlers/consolidated.js +49 -17
- package/lib/external/mcp/handlers/guard.js +104 -39
- package/lib/external/mcp/handlers/knowledge.js +60 -36
- package/lib/external/mcp/handlers/search.js +43 -14
- package/lib/external/mcp/handlers/skill.js +120 -45
- package/lib/external/mcp/handlers/structure.js +240 -86
- package/lib/external/mcp/handlers/system.js +42 -12
- package/lib/external/mcp/handlers/wiki.js +58 -33
- package/lib/external/mcp/tools.js +306 -123
- package/lib/http/HttpServer.js +72 -47
- package/lib/http/middleware/RateLimiter.js +5 -3
- package/lib/http/middleware/errorHandler.js +6 -1
- package/lib/http/middleware/requestLogger.js +14 -3
- package/lib/http/middleware/roleResolver.js +30 -23
- package/lib/http/routes/ai.js +387 -265
- package/lib/http/routes/auth.js +81 -61
- package/lib/http/routes/candidates.js +430 -320
- package/lib/http/routes/commands.js +289 -189
- package/lib/http/routes/extract.js +158 -125
- package/lib/http/routes/guardRules.js +309 -217
- package/lib/http/routes/knowledge.js +213 -154
- package/lib/http/routes/modules.js +578 -0
- package/lib/http/routes/monitoring.js +6 -6
- package/lib/http/routes/recipes.js +104 -93
- package/lib/http/routes/search.js +361 -305
- package/lib/http/routes/skills.js +145 -98
- package/lib/http/routes/snippets.js +42 -30
- package/lib/http/routes/spm.js +3 -405
- package/lib/http/routes/violations.js +113 -93
- package/lib/http/routes/wiki.js +211 -170
- package/lib/http/utils/routeHelpers.js +3 -1
- package/lib/http/utils/sse-sessions.js +16 -6
- package/lib/http/utils/sse.js +15 -5
- package/lib/infrastructure/audit/AuditLogger.js +5 -2
- package/lib/infrastructure/audit/AuditStore.js +10 -7
- package/lib/infrastructure/cache/CacheService.js +3 -1
- package/lib/infrastructure/cache/GraphCache.js +8 -4
- package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
- package/lib/infrastructure/config/ConfigLoader.js +9 -5
- package/lib/infrastructure/config/Defaults.js +30 -10
- package/lib/infrastructure/config/Paths.js +28 -8
- package/lib/infrastructure/config/TriggerSymbol.js +22 -10
- package/lib/infrastructure/database/DatabaseConnection.js +15 -10
- package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
- package/lib/infrastructure/external/ClipboardManager.js +6 -2
- package/lib/infrastructure/external/NativeUi.js +50 -43
- package/lib/infrastructure/external/OpenBrowser.js +14 -17
- package/lib/infrastructure/external/XcodeAutomation.js +14 -258
- package/lib/infrastructure/logging/Logger.js +46 -30
- package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
- package/lib/infrastructure/paths/HeaderResolver.js +25 -9
- package/lib/infrastructure/paths/PathFinder.js +34 -12
- package/lib/infrastructure/plugin/PluginManager.js +26 -8
- package/lib/infrastructure/realtime/RealtimeService.js +2 -2
- package/lib/infrastructure/vector/Chunker.js +22 -7
- package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
- package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
- package/lib/infrastructure/vector/VectorStore.js +28 -10
- package/lib/injection/ServiceContainer.js +247 -93
- package/lib/platform/ios/index.js +63 -0
- package/lib/platform/ios/routes/spm.js +437 -0
- package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
- package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
- package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
- package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
- package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
- package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
- package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
- package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
- package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
- package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
- package/lib/repository/base/BaseRepository.js +7 -9
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
- package/lib/repository/token/TokenUsageStore.js +4 -2
- package/lib/service/automation/ActionPipeline.js +1 -1
- package/lib/service/automation/AutomationOrchestrator.js +8 -4
- package/lib/service/automation/ContextCollector.js +7 -5
- package/lib/service/automation/DirectiveDetector.js +23 -16
- package/lib/service/automation/FileWatcher.js +112 -56
- package/lib/service/automation/TriggerResolver.js +6 -4
- package/lib/service/automation/handlers/AlinkHandler.js +24 -12
- package/lib/service/automation/handlers/CreateHandler.js +19 -20
- package/lib/service/automation/handlers/DraftHandler.js +14 -8
- package/lib/service/automation/handlers/GuardHandler.js +93 -63
- package/lib/service/automation/handlers/HeaderHandler.js +1 -6
- package/lib/service/automation/handlers/SearchHandler.js +155 -88
- package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
- package/lib/service/candidate/SimilarityService.js +25 -9
- package/lib/service/chat/AnalystAgent.js +50 -24
- package/lib/service/chat/CandidateGuardrail.js +143 -17
- package/lib/service/chat/ChatAgent.js +655 -260
- package/lib/service/chat/ContextWindow.js +116 -71
- package/lib/service/chat/ConversationStore.js +77 -36
- package/lib/service/chat/EpisodicConsolidator.js +47 -23
- package/lib/service/chat/HandoffProtocol.js +98 -22
- package/lib/service/chat/Memory.js +34 -14
- package/lib/service/chat/ProducerAgent.js +40 -20
- package/lib/service/chat/ProjectSemanticMemory.js +109 -78
- package/lib/service/chat/ReasoningLayer.js +148 -70
- package/lib/service/chat/ReasoningTrace.js +44 -32
- package/lib/service/chat/TaskPipeline.js +39 -19
- package/lib/service/chat/ToolRegistry.js +48 -29
- package/lib/service/chat/WorkingMemory.js +44 -18
- package/lib/service/chat/tools.js +1096 -494
- package/lib/service/context/RecipeExtractor.js +132 -51
- package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
- package/lib/service/cursor/KnowledgeCompressor.js +25 -22
- package/lib/service/cursor/RulesGenerator.js +13 -7
- package/lib/service/cursor/SkillsSyncer.js +77 -27
- package/lib/service/cursor/TokenBudget.js +2 -2
- package/lib/service/cursor/TopicClassifier.js +54 -20
- package/lib/service/guard/ComplianceReporter.js +55 -43
- package/lib/service/guard/ExclusionManager.js +67 -29
- package/lib/service/guard/GuardCheckEngine.js +381 -86
- package/lib/service/guard/GuardFeedbackLoop.js +22 -10
- package/lib/service/guard/GuardService.js +29 -19
- package/lib/service/guard/RuleLearner.js +55 -23
- package/lib/service/guard/SourceFileCollector.js +27 -20
- package/lib/service/guard/ViolationsStore.js +43 -38
- package/lib/service/knowledge/CodeEntityGraph.js +147 -82
- package/lib/service/knowledge/ConfidenceRouter.js +12 -10
- package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
- package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
- package/lib/service/knowledge/KnowledgeService.js +222 -112
- package/lib/service/module/ModuleService.js +969 -0
- package/lib/service/quality/FeedbackCollector.js +27 -15
- package/lib/service/quality/QualityScorer.js +78 -24
- package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
- package/lib/service/recipe/RecipeParser.js +78 -45
- package/lib/service/search/CoarseRanker.js +43 -28
- package/lib/service/search/CrossEncoderReranker.js +32 -21
- package/lib/service/search/InvertedIndex.js +21 -7
- package/lib/service/search/MultiSignalRanker.js +90 -28
- package/lib/service/search/RetrievalFunnel.js +45 -24
- package/lib/service/search/SearchEngine.js +255 -103
- package/lib/service/skills/EventAggregator.js +32 -15
- package/lib/service/skills/SignalCollector.js +140 -64
- package/lib/service/skills/SkillAdvisor.js +79 -42
- package/lib/service/skills/SkillHooks.js +16 -14
- package/lib/service/snippet/PlaceholderConverter.js +5 -0
- package/lib/service/snippet/SnippetFactory.js +116 -99
- package/lib/service/snippet/SnippetInstaller.js +234 -62
- package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
- package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
- package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
- package/lib/service/wiki/WikiGenerator.js +637 -263
- package/lib/shared/DimensionCopyRegistry.js +472 -0
- package/lib/shared/LanguageService.js +399 -0
- package/lib/shared/PathGuard.js +45 -28
- package/lib/shared/RecipeReadinessChecker.js +72 -12
- package/lib/shared/constants.js +41 -41
- package/lib/shared/errors/BaseError.js +2 -2
- package/lib/shared/errors/index.js +4 -4
- package/lib/shared/similarity.js +25 -8
- package/lib/shared/token-utils.js +6 -2
- package/lib/shared/utils/common.js +12 -4
- package/package.json +49 -13
- package/scripts/bench-real-projects.mjs +256 -0
- package/scripts/build-native-ui.js +30 -30
- package/scripts/clear-old-vector-index.js +5 -35
- package/scripts/clear-vector-cache.js +7 -37
- package/scripts/collect-test-project-stats.mjs +160 -0
- package/scripts/diagnose-mcp.js +41 -32
- package/scripts/ensure-parse-package.js +6 -9
- package/scripts/generate-recipe-drafts.js +116 -77
- package/scripts/init-db.js +3 -20
- package/scripts/init-snippets.js +305 -0
- package/scripts/init-vector-db.js +173 -170
- package/scripts/install-cursor-skill.js +148 -104
- package/scripts/install-full.js +8 -21
- package/scripts/install-vscode-copilot.js +106 -170
- package/scripts/migrate-md-to-knowledge.mjs +139 -151
- package/scripts/postinstall-safe.js +5 -17
- package/scripts/recipe-audit.js +106 -82
- package/scripts/release.js +283 -323
- package/scripts/setup-mcp-config.js +62 -72
- package/scripts/verify-context-api.js +20 -20
- package/skills/autosnippet-analysis/SKILL.md +10 -6
- package/skills/autosnippet-candidates/SKILL.md +27 -26
- package/skills/autosnippet-coldstart/SKILL.md +555 -38
- package/skills/autosnippet-concepts/SKILL.md +349 -337
- package/skills/autosnippet-create/SKILL.md +5 -5
- package/skills/autosnippet-reference-dart/SKILL.md +543 -0
- package/skills/autosnippet-reference-go/SKILL.md +539 -0
- package/skills/autosnippet-reference-java/SKILL.md +534 -0
- package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
- package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
- package/skills/autosnippet-reference-objc/SKILL.md +29 -6
- package/skills/autosnippet-reference-python/SKILL.md +800 -0
- package/skills/autosnippet-reference-swift/SKILL.md +70 -14
- package/skills/autosnippet-structure/SKILL.md +4 -4
- package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
- package/templates/recipes-setup/README.md +2 -2
- package/templates/recipes-setup/_template.md +1 -1
- package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
- package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
- package/resources/asd-entry/main.swift +0 -159
- package/scripts/build-asd-entry.js +0 -51
- package/scripts/init-xcode-snippets.js +0 -311
- package/template.json +0 -39
package/bin/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* AutoSnippet V2 CLI
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* Usage:
|
|
7
7
|
* asd setup - 初始化项目
|
|
8
8
|
* asd coldstart - 冷启动知识库(9 维度分析 + AI 填充)
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
* asd ui - 启动 Dashboard UI
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
20
|
+
import { dirname, join, resolve } from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
19
22
|
import { Command } from 'commander';
|
|
20
|
-
import { readFileSync, existsSync } from 'fs';
|
|
21
|
-
import { join, resolve, dirname, basename } from 'path';
|
|
22
|
-
import { fileURLToPath } from 'url';
|
|
23
23
|
|
|
24
24
|
const __filename = fileURLToPath(import.meta.url);
|
|
25
25
|
const __dirname = dirname(__filename);
|
|
@@ -29,7 +29,9 @@ const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, 'utf8')) : {
|
|
|
29
29
|
// ─── 进程级错误兜底 ────────────────────────────────────
|
|
30
30
|
process.on('uncaughtException', (error) => {
|
|
31
31
|
process.stderr.write(`[asd] Uncaught Exception: ${error.message}\n`);
|
|
32
|
-
if (error.stack)
|
|
32
|
+
if (error.stack) {
|
|
33
|
+
process.stderr.write(`${error.stack}\n`);
|
|
34
|
+
}
|
|
33
35
|
process.exit(1);
|
|
34
36
|
});
|
|
35
37
|
|
|
@@ -45,13 +47,10 @@ const handleSignal = (signal) => {
|
|
|
45
47
|
process.exit(0);
|
|
46
48
|
};
|
|
47
49
|
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
|
48
|
-
process.on('SIGINT',
|
|
50
|
+
process.on('SIGINT', () => handleSignal('SIGINT'));
|
|
49
51
|
|
|
50
52
|
const program = new Command();
|
|
51
|
-
program
|
|
52
|
-
.name('asd')
|
|
53
|
-
.description('AutoSnippet V2 - AI 知识库管理工具')
|
|
54
|
-
.version(pkg.version);
|
|
53
|
+
program.name('asd').description('AutoSnippet V2 - AI 知识库管理工具').version(pkg.version);
|
|
55
54
|
|
|
56
55
|
// ─────────────────────────────────────────────────────
|
|
57
56
|
// setup 命令
|
|
@@ -64,11 +63,11 @@ program
|
|
|
64
63
|
.option('--seed', '预置示例 Recipe(冷启动推荐)')
|
|
65
64
|
.action(async (opts) => {
|
|
66
65
|
const { SetupService } = await import('../lib/cli/SetupService.js');
|
|
67
|
-
const service = new SetupService({
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const service = new SetupService({
|
|
67
|
+
projectRoot: resolve(opts.dir),
|
|
68
|
+
force: opts.force,
|
|
69
|
+
seed: opts.seed,
|
|
70
|
+
});
|
|
72
71
|
|
|
73
72
|
await service.run();
|
|
74
73
|
service.printSummary();
|
|
@@ -88,12 +87,8 @@ program
|
|
|
88
87
|
.option('--json', '以 JSON 格式输出结果')
|
|
89
88
|
.action(async (opts) => {
|
|
90
89
|
const projectRoot = resolve(opts.dir);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
console.log(` 路径: ${projectRoot}`);
|
|
94
|
-
console.log(` 最大文件数: ${opts.maxFiles}`);
|
|
95
|
-
if (opts.skipGuard) console.log(' Guard 审计: 跳过');
|
|
96
|
-
console.log('');
|
|
90
|
+
if (opts.skipGuard) {
|
|
91
|
+
}
|
|
97
92
|
|
|
98
93
|
try {
|
|
99
94
|
const { bootstrap, container } = await initContainer({ projectRoot });
|
|
@@ -114,71 +109,45 @@ program
|
|
|
114
109
|
spinner.stop();
|
|
115
110
|
|
|
116
111
|
if (opts.json) {
|
|
117
|
-
console.log(JSON.stringify(result, null, 2));
|
|
118
112
|
} else {
|
|
119
113
|
// 输出骨架报告
|
|
120
114
|
const report = result.report || {};
|
|
121
|
-
const
|
|
115
|
+
const _targets = result.targets || [];
|
|
122
116
|
const langStats = result.languageStats || {};
|
|
123
117
|
const guardSummary = result.guardSummary;
|
|
124
118
|
const astSummary = result.astSummary;
|
|
125
|
-
const
|
|
119
|
+
const _bootstrapCandidates = result.bootstrapCandidates || {};
|
|
126
120
|
const framework = result.analysisFramework || {};
|
|
127
|
-
|
|
128
|
-
console.log('✅ 冷启动骨架已创建\n');
|
|
129
|
-
|
|
130
|
-
// 项目概况
|
|
131
|
-
console.log('📊 项目概况');
|
|
132
|
-
console.log(` Targets: ${targets.length}`);
|
|
133
|
-
console.log(` 文件: ${report.totals?.files || 0}`);
|
|
134
|
-
console.log(` 主语言: ${result.primaryLanguage || 'unknown'}`);
|
|
135
121
|
if (Object.keys(langStats).length > 0) {
|
|
136
|
-
const
|
|
122
|
+
const _langParts = Object.entries(langStats)
|
|
137
123
|
.sort((a, b) => b[1] - a[1])
|
|
138
124
|
.slice(0, 5)
|
|
139
125
|
.map(([ext, count]) => `${ext}(${count})`);
|
|
140
|
-
console.log(` 语言分布: ${langParts.join(', ')}`);
|
|
141
126
|
}
|
|
142
127
|
|
|
143
128
|
// AST 分析
|
|
144
129
|
if (astSummary) {
|
|
145
|
-
console.log(`\n🔬 AST 分析`);
|
|
146
|
-
console.log(` 类: ${astSummary.classes}, 协议: ${astSummary.protocols}, Category: ${astSummary.categories}`);
|
|
147
130
|
if (astSummary.metrics) {
|
|
148
|
-
console.log(` 方法总数: ${astSummary.metrics.totalMethods}, 复杂方法: ${astSummary.metrics.complexMethods}`);
|
|
149
131
|
}
|
|
150
132
|
}
|
|
151
133
|
|
|
152
134
|
// SPM 依赖
|
|
153
135
|
if (report.phases?.spmDependencyGraph) {
|
|
154
|
-
console.log(`\n📦 SPM 依赖`);
|
|
155
|
-
console.log(` 依赖边: ${report.phases.spmDependencyGraph.edgesWritten}`);
|
|
156
136
|
}
|
|
157
137
|
|
|
158
138
|
// Guard 审计
|
|
159
139
|
if (guardSummary) {
|
|
160
|
-
console.log(`\n🛡️ Guard 审计`);
|
|
161
|
-
console.log(` 违规: ${guardSummary.totalViolations} (${guardSummary.errors} errors, ${guardSummary.warnings} warnings)`);
|
|
162
140
|
}
|
|
163
141
|
|
|
164
142
|
// 维度分析框架
|
|
165
143
|
if (framework.dimensions) {
|
|
166
|
-
console.log(`\n📋 分析维度: ${framework.dimensions.length} 个`);
|
|
167
144
|
for (const dim of framework.dimensions) {
|
|
168
|
-
const
|
|
169
|
-
console.log(` • ${dim.label} [${type}]`);
|
|
145
|
+
const _type = dim.skillWorthy ? (dim.dualOutput ? 'Dual' : 'Skill') : 'Candidate';
|
|
170
146
|
}
|
|
171
147
|
}
|
|
172
|
-
|
|
173
|
-
// AI 异步填充状态
|
|
174
|
-
console.log(`\n🤖 AI 异步填充: ${result.asyncFill ? '已启动' : '未启用'}`);
|
|
175
148
|
if (result.bootstrapSession) {
|
|
176
|
-
const
|
|
177
|
-
console.log(` 会话: ${session.id}`);
|
|
178
|
-
console.log(` 任务: ${session.tasks?.length || 0} 个维度`);
|
|
149
|
+
const _session = result.bootstrapSession;
|
|
179
150
|
}
|
|
180
|
-
|
|
181
|
-
console.log('');
|
|
182
151
|
}
|
|
183
152
|
|
|
184
153
|
// 等待模式: 轮询 BootstrapTaskManager 直到所有维度完成
|
|
@@ -190,18 +159,22 @@ program
|
|
|
190
159
|
const maxAttempts = 600; // 最多等 10 分钟(每秒轮询)
|
|
191
160
|
|
|
192
161
|
while (attempts < maxAttempts) {
|
|
193
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
162
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
194
163
|
attempts++;
|
|
195
164
|
|
|
196
165
|
try {
|
|
197
166
|
const taskManager = container.get('bootstrapTaskManager');
|
|
198
167
|
const sessionStatus = taskManager.getSessionStatus();
|
|
199
168
|
|
|
200
|
-
if (!sessionStatus || !sessionStatus.tasks)
|
|
169
|
+
if (!sessionStatus || !sessionStatus.tasks) {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
201
172
|
|
|
202
173
|
const total = sessionStatus.tasks.length;
|
|
203
|
-
const done = sessionStatus.tasks.filter(
|
|
204
|
-
|
|
174
|
+
const done = sessionStatus.tasks.filter(
|
|
175
|
+
(t) => t.status === 'done' || t.status === 'error'
|
|
176
|
+
).length;
|
|
177
|
+
const current = sessionStatus.tasks.find((t) => t.status === 'running');
|
|
205
178
|
const statusText = current
|
|
206
179
|
? `[${done}/${total}] 正在处理: ${current.meta?.label || current.id}`
|
|
207
180
|
: `[${done}/${total}] 等待中...`;
|
|
@@ -216,14 +189,11 @@ program
|
|
|
216
189
|
|
|
217
190
|
// 输出各维度结果
|
|
218
191
|
if (!opts.json) {
|
|
219
|
-
const
|
|
220
|
-
const
|
|
221
|
-
console.log(` ✅ 成功: ${succeeded}, ❌ 失败: ${failed}`);
|
|
192
|
+
const _succeeded = sessionStatus.tasks.filter((t) => t.status === 'done').length;
|
|
193
|
+
const _failed = sessionStatus.tasks.filter((t) => t.status === 'error').length;
|
|
222
194
|
for (const t of sessionStatus.tasks) {
|
|
223
|
-
const
|
|
224
|
-
console.log(` ${icon} ${t.meta?.label || t.id}`);
|
|
195
|
+
const _icon = t.status === 'done' ? '✅' : '❌';
|
|
225
196
|
}
|
|
226
|
-
console.log('');
|
|
227
197
|
}
|
|
228
198
|
break;
|
|
229
199
|
}
|
|
@@ -236,16 +206,14 @@ program
|
|
|
236
206
|
waitSpinner.warn('AI 填充超时(10 分钟),可通过 asd ui 查看进度');
|
|
237
207
|
}
|
|
238
208
|
} else if (!opts.json) {
|
|
239
|
-
console.log('💡 后台 AI 正在逐维度填充知识,可通过以下方式查看进度:');
|
|
240
|
-
console.log(' • asd ui -d . → Dashboard 实时查看');
|
|
241
|
-
console.log(' • 再次运行 asd coldstart --wait 等待完成');
|
|
242
|
-
console.log('');
|
|
243
209
|
}
|
|
244
210
|
|
|
245
211
|
await bootstrap.shutdown();
|
|
246
212
|
} catch (err) {
|
|
247
213
|
console.error(`\n❌ ${err.message}`);
|
|
248
|
-
if (process.env.ASD_DEBUG === '1')
|
|
214
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
215
|
+
console.error(err.stack);
|
|
216
|
+
}
|
|
249
217
|
process.exit(1);
|
|
250
218
|
}
|
|
251
219
|
});
|
|
@@ -262,12 +230,10 @@ program
|
|
|
262
230
|
.option('--json', '以 JSON 格式输出')
|
|
263
231
|
.action(async (target, opts) => {
|
|
264
232
|
const projectRoot = resolve(opts.dir);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
if (opts.dryRun) console.log(' 模式: dry-run(仅预览)');
|
|
270
|
-
console.log('');
|
|
233
|
+
if (target) {
|
|
234
|
+
}
|
|
235
|
+
if (opts.dryRun) {
|
|
236
|
+
}
|
|
271
237
|
|
|
272
238
|
try {
|
|
273
239
|
const { bootstrap, container } = await initContainer({ projectRoot });
|
|
@@ -286,28 +252,23 @@ program
|
|
|
286
252
|
spinner.stop();
|
|
287
253
|
|
|
288
254
|
if (opts.json) {
|
|
289
|
-
console.log(JSON.stringify(report, null, 2));
|
|
290
255
|
} else {
|
|
291
|
-
console.log(`\n✅ AI 扫描完成`);
|
|
292
|
-
console.log(` 扫描文件: ${report.files}`);
|
|
293
|
-
console.log(` 跳过: ${report.skipped}`);
|
|
294
|
-
console.log(` 发布 Recipe: ${report.published}`);
|
|
295
256
|
if (report.errors.length > 0) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
257
|
+
for (const _err of report.errors.slice(0, 10)) {
|
|
258
|
+
}
|
|
259
|
+
if (report.errors.length > 10) {
|
|
299
260
|
}
|
|
300
|
-
if (report.errors.length > 10) console.log(` ... 及其他 ${report.errors.length - 10} 个`);
|
|
301
261
|
}
|
|
302
262
|
if (!opts.dryRun && report.published > 0) {
|
|
303
|
-
console.log(`\n📋 Recipe 已发布,可通过 asd search 或 Cursor Rules 使用`);
|
|
304
263
|
}
|
|
305
264
|
}
|
|
306
265
|
|
|
307
266
|
await bootstrap.shutdown();
|
|
308
267
|
} catch (err) {
|
|
309
268
|
console.error(`\n❌ ${err.message}`);
|
|
310
|
-
if (process.env.ASD_DEBUG === '1')
|
|
269
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
270
|
+
console.error(err.stack);
|
|
271
|
+
}
|
|
311
272
|
process.exit(1);
|
|
312
273
|
}
|
|
313
274
|
});
|
|
@@ -332,14 +293,12 @@ program
|
|
|
332
293
|
});
|
|
333
294
|
|
|
334
295
|
if (results.items.length === 0) {
|
|
335
|
-
console.log('No results found.');
|
|
336
296
|
} else {
|
|
337
|
-
console.log(`\n🔍 Found ${results.total} results (${results.mode} mode):\n`);
|
|
338
297
|
for (const item of results.items) {
|
|
339
|
-
const
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
298
|
+
const _badge = item.type === 'recipe' ? '📘' : item.type === 'solution' ? '💡' : '🛡️';
|
|
299
|
+
const _score = item.score ? ` [${item.score}]` : '';
|
|
300
|
+
if (item.description) {
|
|
301
|
+
}
|
|
343
302
|
}
|
|
344
303
|
}
|
|
345
304
|
|
|
@@ -375,23 +334,19 @@ program
|
|
|
375
334
|
const violations = engine.checkCode(code, language, { scope: opts.scope });
|
|
376
335
|
|
|
377
336
|
if (opts.json) {
|
|
378
|
-
console.log(JSON.stringify({ file: filePath, language, violations }, null, 2));
|
|
379
337
|
} else if (violations.length === 0) {
|
|
380
|
-
console.log(`✅ No violations found in ${file} (${language})`);
|
|
381
338
|
} else {
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
console.log(`\n🛡️ ${file} (${language}): ${violations.length} violations`);
|
|
385
|
-
console.log(` ${errors.length} errors, ${warnings.length} warnings\n`);
|
|
339
|
+
const _errors = violations.filter((v) => v.severity === 'error');
|
|
340
|
+
const _warnings = violations.filter((v) => v.severity === 'warning');
|
|
386
341
|
for (const v of violations) {
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
342
|
+
const _icon = v.severity === 'error' ? '❌' : '⚠️';
|
|
343
|
+
if (v.snippet) {
|
|
344
|
+
}
|
|
390
345
|
}
|
|
391
346
|
}
|
|
392
347
|
|
|
393
348
|
await bootstrap.shutdown();
|
|
394
|
-
process.exit(violations.some(v => v.severity === 'error') ? 1 : 0);
|
|
349
|
+
process.exit(violations.some((v) => v.severity === 'error') ? 1 : 0);
|
|
395
350
|
} catch (err) {
|
|
396
351
|
console.error('Error:', err.message);
|
|
397
352
|
process.exit(1);
|
|
@@ -430,11 +385,9 @@ program
|
|
|
430
385
|
if (opts.report === 'json') {
|
|
431
386
|
const output = JSON.stringify(report, null, 2);
|
|
432
387
|
if (opts.output) {
|
|
433
|
-
const { writeFileSync } = await import('fs');
|
|
388
|
+
const { writeFileSync } = await import('node:fs');
|
|
434
389
|
writeFileSync(opts.output, output, 'utf8');
|
|
435
|
-
console.log(`Report written to ${opts.output}`);
|
|
436
390
|
} else {
|
|
437
|
-
console.log(output);
|
|
438
391
|
}
|
|
439
392
|
} else {
|
|
440
393
|
reporter.printReport(report, { format: opts.report });
|
|
@@ -442,9 +395,8 @@ program
|
|
|
442
395
|
|
|
443
396
|
// 如果也要写文件(非 JSON 格式)
|
|
444
397
|
if (opts.output && opts.report !== 'json') {
|
|
445
|
-
const { writeFileSync } = await import('fs');
|
|
398
|
+
const { writeFileSync } = await import('node:fs');
|
|
446
399
|
writeFileSync(opts.output, JSON.stringify(report, null, 2), 'utf8');
|
|
447
|
-
console.log(`Report data written to ${opts.output}`);
|
|
448
400
|
}
|
|
449
401
|
|
|
450
402
|
await bootstrap.shutdown();
|
|
@@ -456,7 +408,9 @@ program
|
|
|
456
408
|
process.exit(0);
|
|
457
409
|
} catch (err) {
|
|
458
410
|
console.error('Error:', err.message);
|
|
459
|
-
if (process.env.ASD_DEBUG === '1')
|
|
411
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
412
|
+
console.error(err.stack);
|
|
413
|
+
}
|
|
460
414
|
process.exit(1);
|
|
461
415
|
}
|
|
462
416
|
});
|
|
@@ -471,36 +425,40 @@ program
|
|
|
471
425
|
.option('--json', '以 JSON 格式输出')
|
|
472
426
|
.action(async (opts) => {
|
|
473
427
|
try {
|
|
474
|
-
const { execSync } = await import('child_process');
|
|
428
|
+
const { execSync } = await import('node:child_process');
|
|
475
429
|
|
|
476
430
|
// 获取 staged 文件列表
|
|
477
431
|
let stagedFiles;
|
|
478
432
|
try {
|
|
479
|
-
stagedFiles = execSync('git diff --cached --name-only --diff-filter=ACM', {
|
|
480
|
-
|
|
481
|
-
|
|
433
|
+
stagedFiles = execSync('git diff --cached --name-only --diff-filter=ACM', {
|
|
434
|
+
encoding: 'utf8',
|
|
435
|
+
})
|
|
436
|
+
.trim()
|
|
437
|
+
.split('\n')
|
|
438
|
+
.filter(Boolean);
|
|
439
|
+
} catch (_err) {
|
|
482
440
|
console.error('❌ 无法获取 git staged 文件(是否在 git 仓库中?)');
|
|
483
441
|
process.exit(1);
|
|
484
442
|
}
|
|
485
443
|
|
|
486
444
|
if (stagedFiles.length === 0) {
|
|
487
|
-
console.log('✅ No staged files');
|
|
488
445
|
process.exit(0);
|
|
489
446
|
}
|
|
490
447
|
|
|
491
448
|
// 过滤源文件
|
|
492
449
|
const { SOURCE_EXTS } = await import('../lib/service/guard/SourceFileCollector.js');
|
|
493
|
-
const { extname: _extname } = await import('path');
|
|
494
|
-
const sourceFiles = stagedFiles.filter(f => SOURCE_EXTS.has(_extname(f).toLowerCase()));
|
|
450
|
+
const { extname: _extname } = await import('node:path');
|
|
451
|
+
const sourceFiles = stagedFiles.filter((f) => SOURCE_EXTS.has(_extname(f).toLowerCase()));
|
|
495
452
|
|
|
496
453
|
if (sourceFiles.length === 0) {
|
|
497
|
-
console.log('✅ No source files in staged changes');
|
|
498
454
|
process.exit(0);
|
|
499
455
|
}
|
|
500
456
|
|
|
501
457
|
const { bootstrap, container } = await initContainer();
|
|
502
458
|
const engine = container.get('guardCheckEngine');
|
|
503
|
-
const { detectLanguage } = await import(
|
|
459
|
+
const { detectLanguage: _detectLanguage } = await import(
|
|
460
|
+
'../lib/service/guard/GuardCheckEngine.js'
|
|
461
|
+
);
|
|
504
462
|
|
|
505
463
|
// 读取文件内容并检查
|
|
506
464
|
const files = [];
|
|
@@ -515,21 +473,15 @@ program
|
|
|
515
473
|
const { summary } = result;
|
|
516
474
|
|
|
517
475
|
if (opts.json) {
|
|
518
|
-
console.log(JSON.stringify(result, null, 2));
|
|
519
476
|
} else if (summary.totalViolations === 0) {
|
|
520
|
-
console.log(`✅ ${summary.filesChecked} staged files passed Guard check`);
|
|
521
477
|
} else {
|
|
522
|
-
|
|
523
|
-
console.log(` ${summary.totalErrors} errors, ${summary.totalViolations - summary.totalErrors} warnings\n`);
|
|
524
|
-
|
|
525
|
-
const filesWithIssues = result.files.filter(f => f.summary.total > 0);
|
|
478
|
+
const filesWithIssues = result.files.filter((f) => f.summary.total > 0);
|
|
526
479
|
for (const file of filesWithIssues.slice(0, 10)) {
|
|
527
|
-
console.log(` 📄 ${basename(file.filePath)} (${file.summary.errors}E / ${file.summary.warnings}W)`);
|
|
528
480
|
for (const v of file.violations.slice(0, 5)) {
|
|
529
|
-
const
|
|
530
|
-
|
|
481
|
+
const _icon = v.severity === 'error' ? '❌' : '⚠️';
|
|
482
|
+
}
|
|
483
|
+
if (file.violations.length > 5) {
|
|
531
484
|
}
|
|
532
|
-
if (file.violations.length > 5) console.log(` ... ${file.violations.length - 5} more`);
|
|
533
485
|
}
|
|
534
486
|
}
|
|
535
487
|
|
|
@@ -537,7 +489,9 @@ program
|
|
|
537
489
|
process.exit(summary.totalErrors > 0 ? 1 : 0);
|
|
538
490
|
} catch (err) {
|
|
539
491
|
console.error('Error:', err.message);
|
|
540
|
-
if (process.env.ASD_DEBUG === '1')
|
|
492
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
493
|
+
console.error(err.stack);
|
|
494
|
+
}
|
|
541
495
|
process.exit(1);
|
|
542
496
|
}
|
|
543
497
|
});
|
|
@@ -547,17 +501,13 @@ program
|
|
|
547
501
|
// ─────────────────────────────────────────────────────
|
|
548
502
|
program
|
|
549
503
|
.command('watch')
|
|
550
|
-
|
|
504
|
+
|
|
551
505
|
.option('-d, --dir <path>', '监控目录', '.')
|
|
552
|
-
.option('-e, --ext <exts>', '
|
|
506
|
+
.option('-e, --ext <exts>', '文件扩展名(逗号分隔,留空则自动检测)')
|
|
553
507
|
.option('--guard', '自动运行 Guard 检查', true)
|
|
554
508
|
.action(async (opts) => {
|
|
555
509
|
try {
|
|
556
510
|
const dir = resolve(opts.dir);
|
|
557
|
-
console.log(`👁️ Watching ${dir} for changes...`);
|
|
558
|
-
console.log(` Extensions: ${opts.ext}`);
|
|
559
|
-
console.log(` Directives: // as:c (create), // as:s (search), // as:a (audit)`);
|
|
560
|
-
console.log(' Press Ctrl+C to stop\n');
|
|
561
511
|
|
|
562
512
|
let bootstrap;
|
|
563
513
|
try {
|
|
@@ -571,8 +521,14 @@ program
|
|
|
571
521
|
const Paths = await import('../lib/infrastructure/config/Paths.js');
|
|
572
522
|
const specPath = Paths.getProjectSpecPath(dir);
|
|
573
523
|
|
|
524
|
+
// IDE + 扩展名自动检测
|
|
525
|
+
let exts = null;
|
|
526
|
+
if (opts.ext) {
|
|
527
|
+
exts = opts.ext.split(',').map((e) => e.trim());
|
|
528
|
+
}
|
|
529
|
+
// 不指定 --ext 时,FileWatcher 内部根据 IDE 检测结果使用默认模式
|
|
530
|
+
|
|
574
531
|
const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
|
|
575
|
-
const exts = opts.ext.split(',').map(e => e.trim());
|
|
576
532
|
const watcher = new FileWatcher(specPath, dir, {
|
|
577
533
|
quiet: false,
|
|
578
534
|
exts,
|
|
@@ -581,14 +537,15 @@ program
|
|
|
581
537
|
|
|
582
538
|
// 优雅退出
|
|
583
539
|
process.on('SIGINT', async () => {
|
|
584
|
-
console.log('\n🛑 Stopping watcher...');
|
|
585
540
|
await watcher.stop();
|
|
586
541
|
await bootstrap.shutdown();
|
|
587
542
|
process.exit(0);
|
|
588
543
|
});
|
|
589
544
|
} catch (err) {
|
|
590
545
|
console.error('Error:', err.message);
|
|
591
|
-
if (process.env.ASD_DEBUG === '1')
|
|
546
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
547
|
+
console.error(err.stack);
|
|
548
|
+
}
|
|
592
549
|
process.exit(1);
|
|
593
550
|
}
|
|
594
551
|
});
|
|
@@ -602,7 +559,6 @@ program
|
|
|
602
559
|
.option('-p, --port <port>', '端口', '3000')
|
|
603
560
|
.option('-H, --host <host>', '绑定地址', '127.0.0.1')
|
|
604
561
|
.action(async (opts) => {
|
|
605
|
-
console.log(`🚀 Starting AutoSnippet V2 API server on ${opts.host}:${opts.port}...`);
|
|
606
562
|
// 设置环境变量后启动 api-server
|
|
607
563
|
process.env.PORT = opts.port;
|
|
608
564
|
process.env.HOST = opts.host;
|
|
@@ -621,14 +577,10 @@ program
|
|
|
621
577
|
.option('-d, --dir <directory>', '指定 AutoSnippet 项目目录(默认:当前目录)')
|
|
622
578
|
.option('--api-only', '仅启动 API 服务(不启动前端)')
|
|
623
579
|
.action(async (opts) => {
|
|
624
|
-
const { spawn } = await import('child_process');
|
|
580
|
+
const { spawn } = await import('node:child_process');
|
|
625
581
|
|
|
626
582
|
// 项目根目录:-d 选项 > 环境变量 ASD_CWD > 当前目录
|
|
627
583
|
const projectRoot = opts.dir || process.env.ASD_CWD || process.cwd();
|
|
628
|
-
console.log(`📂 Project root: ${projectRoot}`);
|
|
629
|
-
|
|
630
|
-
// 1. 内联启动 API Server(不用 import api-server.js,避免其 process.exit 影响整个进程)
|
|
631
|
-
console.log(`🚀 Starting API server on port ${opts.port}...`);
|
|
632
584
|
const port = opts.port;
|
|
633
585
|
const host = '127.0.0.1';
|
|
634
586
|
process.env.PORT = port;
|
|
@@ -638,25 +590,27 @@ program
|
|
|
638
590
|
try {
|
|
639
591
|
const { default: HttpServer } = await import('../lib/http/HttpServer.js');
|
|
640
592
|
|
|
641
|
-
const {
|
|
593
|
+
const { container } = await initContainer({ projectRoot });
|
|
642
594
|
|
|
643
595
|
// 连接 EventBus → Gateway(供 SignalCollector 监听事件)
|
|
644
596
|
try {
|
|
645
597
|
const eventBus = container.get('eventBus');
|
|
646
598
|
const gateway = container.get('gateway');
|
|
647
599
|
gateway.eventBus = eventBus;
|
|
648
|
-
} catch {
|
|
600
|
+
} catch {
|
|
601
|
+
/* EventBus 不可用不阻塞启动 */
|
|
602
|
+
}
|
|
649
603
|
|
|
650
604
|
httpServer = new HttpServer({ port, host });
|
|
651
605
|
await httpServer.initialize();
|
|
652
606
|
await httpServer.start();
|
|
653
607
|
|
|
654
|
-
console.log(`✅ API server running at http://${host}:${port}`);
|
|
655
|
-
|
|
656
608
|
// 启动 SignalCollector 后台 AI 分析服务
|
|
657
609
|
try {
|
|
658
610
|
const { SignalCollector } = await import('../lib/service/skills/SignalCollector.js');
|
|
659
|
-
const { getRealtimeService } = await import(
|
|
611
|
+
const { getRealtimeService } = await import(
|
|
612
|
+
'../lib/infrastructure/realtime/RealtimeService.js'
|
|
613
|
+
);
|
|
660
614
|
const db = container.get('database');
|
|
661
615
|
const chatAgent = container.get('chatAgent');
|
|
662
616
|
|
|
@@ -670,60 +624,119 @@ program
|
|
|
670
624
|
try {
|
|
671
625
|
const realtime = getRealtimeService();
|
|
672
626
|
realtime.broadcastEvent('skill:suggestions', { suggestions });
|
|
673
|
-
} catch {
|
|
627
|
+
} catch {
|
|
628
|
+
/* realtime 未就绪 */
|
|
629
|
+
}
|
|
674
630
|
},
|
|
675
631
|
});
|
|
676
632
|
signalCollector.start();
|
|
677
633
|
global._signalCollector = signalCollector;
|
|
678
|
-
console.log(`🧠 SignalCollector started (mode=${signalCollector.getMode()}, AI-driven)`);
|
|
679
634
|
} catch (scErr) {
|
|
680
635
|
console.warn(`⚠️ SignalCollector failed to start: ${scErr.message}`);
|
|
681
|
-
if (process.env.ASD_DEBUG === '1')
|
|
636
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
637
|
+
console.error(scErr.stack);
|
|
638
|
+
}
|
|
682
639
|
}
|
|
683
640
|
|
|
684
|
-
// 3.
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
641
|
+
// 3. 启动文件监听器(仅 iOS/macOS 项目 — Xcode 工作流)
|
|
642
|
+
// VSCode 用户通过 AutoSnippet 扩展原生处理 as:s/as:c/as:a 指令
|
|
643
|
+
const isAppleProject = (() => {
|
|
644
|
+
try {
|
|
645
|
+
const entries = readdirSync(projectRoot, { withFileTypes: true });
|
|
646
|
+
|
|
647
|
+
// ── Level 1: 项目配置文件(确定性高)──
|
|
648
|
+
const hasAppleConfig = entries.some(
|
|
649
|
+
(e) =>
|
|
650
|
+
e.name === 'Package.swift' || // SPM
|
|
651
|
+
e.name === 'Podfile' || // CocoaPods
|
|
652
|
+
e.name === 'Cartfile' || // Carthage
|
|
653
|
+
e.name === 'project.yml' || // XcodeGen
|
|
654
|
+
e.name.endsWith('.xcodeproj') || // Xcode project
|
|
655
|
+
e.name.endsWith('.xcworkspace') // Xcode workspace
|
|
656
|
+
);
|
|
657
|
+
if (hasAppleConfig) return true;
|
|
658
|
+
|
|
659
|
+
// ── Level 2: 目录结构特征 ──
|
|
660
|
+
const hasAppleDir = entries.some(
|
|
661
|
+
(e) =>
|
|
662
|
+
e.isDirectory() &&
|
|
663
|
+
(e.name === 'Tuist' || // Tuist 项目
|
|
664
|
+
e.name === 'Pods' || // CocoaPods 产物
|
|
665
|
+
e.name === 'Carthage' || // Carthage 产物
|
|
666
|
+
e.name === 'DerivedData') // Xcode 构建产物
|
|
667
|
+
);
|
|
668
|
+
if (hasAppleDir) return true;
|
|
669
|
+
|
|
670
|
+
// ── Level 3: 向下扫一层(处理 monorepo 或 Sources/ 下有 .swift 的情况)──
|
|
671
|
+
const APPLE_EXTS = new Set(['.swift', '.m', '.mm', '.h']);
|
|
672
|
+
const SCAN_DIRS = ['Sources', 'Source', 'src', 'App', 'Classes', 'ios', 'iOS'];
|
|
673
|
+
for (const e of entries) {
|
|
674
|
+
// 根目录直接有 .swift/.m 文件
|
|
675
|
+
if (!e.isDirectory() && APPLE_EXTS.has(e.name.slice(e.name.lastIndexOf('.')))) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
// 常见源码目录下有 Apple 文件
|
|
679
|
+
if (e.isDirectory() && SCAN_DIRS.includes(e.name)) {
|
|
680
|
+
try {
|
|
681
|
+
const subEntries = readdirSync(join(projectRoot, e.name));
|
|
682
|
+
if (subEntries.some((f) => APPLE_EXTS.has(f.slice(f.lastIndexOf('.'))))) {
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
} catch { /* 读取失败忽略 */ }
|
|
686
|
+
}
|
|
687
|
+
}
|
|
701
688
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
console.log(`👁️ File watcher started for: ${projectRoot}`);
|
|
706
|
-
if (isDebugMode) {
|
|
707
|
-
console.log(` Spec path: ${specPath}`);
|
|
708
|
-
console.log(` Dashboard URL: ${process.env.ASD_DASHBOARD_URL}`);
|
|
689
|
+
return false;
|
|
690
|
+
} catch {
|
|
691
|
+
return false;
|
|
709
692
|
}
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
693
|
+
})();
|
|
694
|
+
|
|
695
|
+
if (isAppleProject) {
|
|
696
|
+
try {
|
|
697
|
+
const Paths = await import('../lib/infrastructure/config/Paths.js');
|
|
698
|
+
const specPath = Paths.getProjectSpecPath(projectRoot);
|
|
699
|
+
const isDebugMode = process.env.ASD_DEBUG === '1';
|
|
700
|
+
|
|
701
|
+
// 设置 Dashboard URL 供 watcher 跳转浏览器使用
|
|
702
|
+
// 生产模式用 API 同端口,开发模式用 vite dev 5173
|
|
703
|
+
const dashDirCheck = join(__dirname, '..', 'dashboard');
|
|
704
|
+
const isProductionDashboard =
|
|
705
|
+
existsSync(join(dashDirCheck, 'dist', 'index.html')) &&
|
|
706
|
+
!existsSync(join(dashDirCheck, 'src'));
|
|
707
|
+
if (!opts.apiOnly) {
|
|
708
|
+
process.env.ASD_DASHBOARD_URL = isProductionDashboard
|
|
709
|
+
? `http://127.0.0.1:${port}`
|
|
710
|
+
: `http://localhost:5173`;
|
|
711
|
+
} else {
|
|
712
|
+
process.env.ASD_DASHBOARD_URL = process.env.ASD_DASHBOARD_URL || `http://${host}:${port}`;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
|
|
716
|
+
const watcher = new FileWatcher(specPath, projectRoot, { quiet: !isDebugMode });
|
|
717
|
+
watcher.start();
|
|
718
|
+
if (isDebugMode) {
|
|
719
|
+
}
|
|
720
|
+
} catch (watchErr) {
|
|
721
|
+
console.warn(`⚠️ File watcher failed to start: ${watchErr.message}`);
|
|
722
|
+
if (process.env.ASD_DEBUG === '1') {
|
|
723
|
+
console.error(watchErr.stack);
|
|
724
|
+
}
|
|
714
725
|
}
|
|
726
|
+
} else if (process.env.ASD_DEBUG === '1') {
|
|
727
|
+
console.log('ℹ️ Non-Apple project — file watcher skipped (use VSCode extension instead)');
|
|
715
728
|
}
|
|
716
|
-
|
|
717
729
|
} catch (err) {
|
|
718
730
|
console.error(`❌ API server failed to start: ${err.message}`);
|
|
719
731
|
if (err.code === 'EADDRINUSE') {
|
|
720
|
-
console.error(
|
|
732
|
+
console.error(
|
|
733
|
+
` Port ${port} is already in use. Kill it with: lsof -ti:${port} | xargs kill -9`
|
|
734
|
+
);
|
|
721
735
|
}
|
|
722
736
|
process.exit(1);
|
|
723
737
|
}
|
|
724
738
|
|
|
725
739
|
if (opts.apiOnly) {
|
|
726
|
-
console.log(` Docs: http://127.0.0.1:${port}/api-spec`);
|
|
727
740
|
return;
|
|
728
741
|
}
|
|
729
742
|
|
|
@@ -737,24 +750,21 @@ program
|
|
|
737
750
|
// ── 生产模式:npm 安装的包,在 API 服务器上直接托管预构建产物 ──
|
|
738
751
|
// 同端口同 origin → /api 路由自然可达,无跨域问题
|
|
739
752
|
httpServer.mountDashboard(distDir);
|
|
740
|
-
console.log(`🎨 Dashboard UI ready at http://127.0.0.1:${port}/`);
|
|
741
753
|
|
|
742
754
|
if (opts.browser) {
|
|
743
755
|
const open = (await import('open')).default;
|
|
744
756
|
open(`http://127.0.0.1:${port}/`);
|
|
745
757
|
}
|
|
746
|
-
|
|
747
758
|
} else {
|
|
748
759
|
// ── 开发模式:有源码,启动 Vite Dev Server ──
|
|
749
760
|
if (!existsSync(join(dashboardDir, 'node_modules'))) {
|
|
750
|
-
console.log('📦 Installing dashboard dependencies...');
|
|
751
761
|
const install = spawn('npm', ['install'], { cwd: dashboardDir, stdio: 'inherit' });
|
|
752
762
|
await new Promise((resolve, reject) => {
|
|
753
|
-
install.on('close', code =>
|
|
763
|
+
install.on('close', (code) =>
|
|
764
|
+
code === 0 ? resolve() : reject(new Error(`npm install exited with ${code}`))
|
|
765
|
+
);
|
|
754
766
|
});
|
|
755
767
|
}
|
|
756
|
-
|
|
757
|
-
console.log('🎨 Starting Dashboard (dev mode)...');
|
|
758
768
|
const viteArgs = ['--host'];
|
|
759
769
|
if (opts.browser) {
|
|
760
770
|
viteArgs.push('--open');
|
|
@@ -770,7 +780,6 @@ program
|
|
|
770
780
|
});
|
|
771
781
|
|
|
772
782
|
process.on('SIGINT', () => {
|
|
773
|
-
console.log('\n🛑 Stopping Dashboard...');
|
|
774
783
|
vite.kill();
|
|
775
784
|
process.exit(0);
|
|
776
785
|
});
|
|
@@ -784,32 +793,19 @@ program
|
|
|
784
793
|
.command('status')
|
|
785
794
|
.description('检查环境状态')
|
|
786
795
|
.action(async () => {
|
|
787
|
-
console.log('\n📋 AutoSnippet V2 Status\n');
|
|
788
|
-
console.log(` Version: ${pkg.version}`);
|
|
789
|
-
console.log(` Node: ${process.version}`);
|
|
790
|
-
console.log(` Platform: ${process.platform} ${process.arch}`);
|
|
791
|
-
|
|
792
796
|
// AI 配置
|
|
793
797
|
const { getAiConfigInfo } = await import('../lib/external/ai/AiFactory.js');
|
|
794
|
-
const
|
|
795
|
-
console.log(`\n AI Provider: ${aiInfo.provider}`);
|
|
796
|
-
console.log(` AI Keys: ${Object.entries(aiInfo.keys).filter(([, v]) => v).map(([k]) => k).join(', ') || 'none'}`);
|
|
798
|
+
const _aiInfo = getAiConfigInfo();
|
|
797
799
|
|
|
798
800
|
// 检查数据库
|
|
799
|
-
const
|
|
800
|
-
console.log(`\n Database: ${existsSync(dbPath) ? '✅ ' + dbPath : '❌ Not found'}`);
|
|
801
|
+
const _dbPath = join(process.cwd(), '.autosnippet', 'autosnippet.db');
|
|
801
802
|
|
|
802
803
|
// 检查依赖
|
|
803
804
|
for (const dep of ['better-sqlite3', 'commander', 'express']) {
|
|
804
805
|
try {
|
|
805
806
|
await import(dep);
|
|
806
|
-
|
|
807
|
-
} catch {
|
|
808
|
-
console.log(` ${dep}: ❌ not installed`);
|
|
809
|
-
}
|
|
807
|
+
} catch {}
|
|
810
808
|
}
|
|
811
|
-
|
|
812
|
-
console.log('');
|
|
813
809
|
});
|
|
814
810
|
|
|
815
811
|
// ─────────────────────────────────────────────────────
|
|
@@ -825,10 +821,6 @@ program
|
|
|
825
821
|
const { UpgradeService } = await import('../lib/cli/UpgradeService.js');
|
|
826
822
|
const service = new UpgradeService({ projectRoot: resolve(opts.dir) });
|
|
827
823
|
|
|
828
|
-
console.log(`\n🔄 AutoSnippet V2 — 升级 IDE 集成`);
|
|
829
|
-
console.log(` 项目: ${service.projectName}`);
|
|
830
|
-
console.log(` 路径: ${service.projectRoot}\n`);
|
|
831
|
-
|
|
832
824
|
await service.run({
|
|
833
825
|
skillsOnly: opts.skillsOnly,
|
|
834
826
|
mcpOnly: opts.mcpOnly,
|
|
@@ -846,32 +838,17 @@ program
|
|
|
846
838
|
.action(async (opts) => {
|
|
847
839
|
const projectRoot = resolve(opts.dir);
|
|
848
840
|
|
|
849
|
-
console.log(`\n🚀 AutoSnippet — Cursor Delivery Pipeline`);
|
|
850
|
-
console.log(` 项目: ${basename(projectRoot)}`);
|
|
851
|
-
console.log(` 路径: ${projectRoot}\n`);
|
|
852
|
-
|
|
853
841
|
const { bootstrap, container } = await initContainer({ projectRoot });
|
|
854
842
|
try {
|
|
855
843
|
const pipeline = container.get('cursorDeliveryPipeline');
|
|
856
844
|
const result = await pipeline.deliver();
|
|
857
|
-
|
|
858
|
-
console.log(`✅ Cursor 交付物料生成完成\n`);
|
|
859
|
-
console.log(` Channel A (Always-On Rules): ${result.channelA.rulesCount} 条规则 (${result.channelA.tokensUsed} tokens)`);
|
|
860
|
-
console.log(` Channel B (Smart Rules): ${result.channelB.topicCount} 个主题, ${result.channelB.patternsCount} 个模式 (${result.channelB.totalTokens} tokens)`);
|
|
861
|
-
console.log(` Channel C (Agent Skills): ${result.channelC.synced} 个 Skills 已同步`);
|
|
862
|
-
console.log(` Channel D (Dev Documents): ${result.channelD?.documentsCount || 0} 篇文档`);
|
|
863
845
|
if (result.channelC.errors > 0) {
|
|
864
|
-
console.log(` ⚠️ ${result.channelC.errors} 个错误`);
|
|
865
846
|
}
|
|
866
|
-
console.log(`\n 总耗时: ${result.stats.duration}ms`);
|
|
867
847
|
|
|
868
848
|
if (opts.verbose && result.channelB.topics) {
|
|
869
|
-
|
|
870
|
-
for (const [topic, info] of Object.entries(result.channelB.topics)) {
|
|
871
|
-
console.log(` - ${topic}: ${info.patternsCount} patterns (${info.tokensUsed} tokens)`);
|
|
849
|
+
for (const [_topic, _info] of Object.entries(result.channelB.topics)) {
|
|
872
850
|
}
|
|
873
851
|
}
|
|
874
|
-
console.log('');
|
|
875
852
|
} finally {
|
|
876
853
|
await bootstrap.shutdown?.();
|
|
877
854
|
}
|
|
@@ -890,12 +867,8 @@ program
|
|
|
890
867
|
const projectRoot = resolve(opts.dir);
|
|
891
868
|
const { KnowledgeSyncService } = await import('../lib/cli/KnowledgeSyncService.js');
|
|
892
869
|
const syncService = new KnowledgeSyncService(projectRoot);
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
console.log(` 项目: ${basename(projectRoot)}`);
|
|
896
|
-
console.log(` 路径: ${projectRoot}`);
|
|
897
|
-
if (opts.dryRun) console.log(' 模式: dry-run(仅报告)');
|
|
898
|
-
console.log('');
|
|
870
|
+
if (opts.dryRun) {
|
|
871
|
+
}
|
|
899
872
|
|
|
900
873
|
// 通过 Bootstrap 打开目标项目的 DB
|
|
901
874
|
const dbPath = join(projectRoot, '.autosnippet', 'autosnippet.db');
|
|
@@ -917,28 +890,15 @@ program
|
|
|
917
890
|
force: opts.force,
|
|
918
891
|
});
|
|
919
892
|
|
|
920
|
-
// 输出报告
|
|
921
|
-
console.log(`✅ Knowledge 同步完成`);
|
|
922
|
-
console.log(` 扫描: ${report.synced + report.skipped} 文件`);
|
|
923
|
-
console.log(` 新增: ${report.created}`);
|
|
924
|
-
console.log(` 更新: ${report.updated}`);
|
|
925
|
-
console.log(` 跳过: ${report.skipped}`);
|
|
926
|
-
|
|
927
893
|
if (report.violations.length > 0) {
|
|
928
|
-
|
|
929
|
-
for (const v of report.violations) {
|
|
930
|
-
console.log(` - ${v}`);
|
|
894
|
+
for (const _v of report.violations) {
|
|
931
895
|
}
|
|
932
896
|
}
|
|
933
897
|
|
|
934
898
|
if (report.orphaned.length > 0) {
|
|
935
|
-
|
|
936
|
-
for (const id of report.orphaned) {
|
|
937
|
-
console.log(` - ${id}`);
|
|
899
|
+
for (const _id of report.orphaned) {
|
|
938
900
|
}
|
|
939
901
|
}
|
|
940
|
-
|
|
941
|
-
console.log('');
|
|
942
902
|
} finally {
|
|
943
903
|
await bootstrap.shutdown?.();
|
|
944
904
|
}
|