autosnippet 3.2.21 → 3.3.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.
Files changed (107) hide show
  1. package/dashboard/dist/assets/{icons-C1dUryS-.js → icons-BofcEZ3f.js} +1 -1
  2. package/dashboard/dist/assets/index-SiN1GChm.js +128 -0
  3. package/dashboard/dist/index.html +2 -2
  4. package/dist/bin/cli.d.ts +0 -1
  5. package/dist/bin/cli.js +0 -133
  6. package/dist/lib/cli/SetupService.d.ts +46 -2
  7. package/dist/lib/cli/SetupService.js +2 -27
  8. package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
  9. package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
  10. package/dist/lib/core/discovery/index.d.ts +1 -1
  11. package/dist/lib/core/discovery/index.js +2 -2
  12. package/dist/lib/external/mcp/handlers/guard.js +6 -3
  13. package/dist/lib/http/HttpServer.js +0 -6
  14. package/dist/lib/http/routes/commands.d.ts +1 -1
  15. package/dist/lib/http/routes/commands.js +1 -66
  16. package/dist/lib/http/routes/remote.js +0 -5
  17. package/dist/lib/injection/ServiceMap.d.ts +0 -9
  18. package/dist/lib/injection/modules/AppModule.d.ts +2 -3
  19. package/dist/lib/injection/modules/AppModule.js +3 -30
  20. package/dist/lib/injection/modules/GuardModule.js +33 -1
  21. package/dist/lib/service/guard/GuardCheckEngine.d.ts +13 -1
  22. package/dist/lib/service/guard/GuardCheckEngine.js +44 -2
  23. package/dist/lib/service/module/ModuleService.js +3 -13
  24. package/dist/lib/service/search/SearchEngine.js +1 -1
  25. package/dist/lib/shared/constants.d.ts +0 -15
  26. package/dist/lib/shared/constants.js +0 -10
  27. package/dist/lib/shared/schemas/config.d.ts +4 -1
  28. package/dist/lib/shared/schemas/config.js +8 -1
  29. package/dist/scripts/release.js +2 -10
  30. package/package.json +4 -19
  31. package/dashboard/dist/assets/index-DdvZE4Yd.js +0 -128
  32. package/dist/lib/http/routes/snippets.d.ts +0 -6
  33. package/dist/lib/http/routes/snippets.js +0 -49
  34. package/dist/lib/platform/ClipboardManager.d.ts +0 -24
  35. package/dist/lib/platform/ClipboardManager.js +0 -142
  36. package/dist/lib/platform/NativeUi.d.ts +0 -53
  37. package/dist/lib/platform/NativeUi.js +0 -284
  38. package/dist/lib/platform/ios/index.d.ts +0 -38
  39. package/dist/lib/platform/ios/index.js +0 -42
  40. package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
  41. package/dist/lib/platform/ios/routes/spm.js +0 -371
  42. package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
  43. package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
  44. package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
  45. package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
  46. package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
  47. package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
  48. package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
  49. package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
  50. package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
  51. package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
  52. package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
  53. package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
  54. package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
  55. package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
  56. package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
  57. package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
  58. package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
  59. package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
  60. package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
  61. package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
  62. package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
  63. package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
  64. package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
  65. package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
  66. package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
  67. package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
  68. package/dist/lib/service/automation/ActionPipeline.d.ts +0 -34
  69. package/dist/lib/service/automation/ActionPipeline.js +0 -53
  70. package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
  71. package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
  72. package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
  73. package/dist/lib/service/automation/ContextCollector.js +0 -35
  74. package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
  75. package/dist/lib/service/automation/DirectiveDetector.js +0 -112
  76. package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
  77. package/dist/lib/service/automation/FileWatcher.js +0 -366
  78. package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
  79. package/dist/lib/service/automation/TriggerResolver.js +0 -62
  80. package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
  81. package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
  82. package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
  83. package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
  84. package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
  85. package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
  86. package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
  87. package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
  88. package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
  89. package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
  90. package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
  91. package/dist/lib/service/snippet/SnippetFactory.js +0 -145
  92. package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
  93. package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
  94. package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
  95. package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
  96. package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
  97. package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
  98. package/dist/scripts/build-native-ui.d.ts +0 -3
  99. package/dist/scripts/build-native-ui.js +0 -62
  100. package/dist/scripts/init-snippets.d.ts +0 -30
  101. package/dist/scripts/init-snippets.js +0 -298
  102. package/dist/scripts/install-full.d.ts +0 -7
  103. package/dist/scripts/install-full.js +0 -38
  104. package/resources/native-ui/README.md +0 -29
  105. package/resources/native-ui/combined-window.swift +0 -494
  106. package/resources/native-ui/main.swift +0 -598
  107. package/scripts/postinstall-safe.mjs +0 -89
@@ -5,10 +5,10 @@
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-DdvZE4Yd.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-SiN1GChm.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vendor-BZEJEVBn.js">
11
- <link rel="modulepreload" crossorigin href="/assets/icons-C1dUryS-.js">
11
+ <link rel="modulepreload" crossorigin href="/assets/icons-BofcEZ3f.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/axios-42ANG6Sg.js">
13
13
  <link rel="modulepreload" crossorigin href="/assets/framer-motion-CZfOSYpP.js">
14
14
  <link rel="stylesheet" crossorigin href="/assets/index-D0whuycy.css">
package/dist/bin/cli.d.ts CHANGED
@@ -10,7 +10,6 @@
10
10
  * asd search <query> - 搜索知识库
11
11
  * asd guard <file> - Guard 检查
12
12
  * asd guard:ci [path] - CI/CD Guard 合规检查
13
- * asd watch - 文件监控
14
13
  * asd server - 启动 API 服务
15
14
  * asd ui - 启动 Dashboard UI
16
15
  * asd upgrade - 升级 IDE 集成
package/dist/bin/cli.js CHANGED
@@ -10,7 +10,6 @@
10
10
  * asd search <query> - 搜索知识库
11
11
  * asd guard <file> - Guard 检查
12
12
  * asd guard:ci [path] - CI/CD Guard 合规检查
13
- * asd watch - 文件监控
14
13
  * asd server - 启动 API 服务
15
14
  * asd ui - 启动 Dashboard UI
16
15
  * asd upgrade - 升级 IDE 集成
@@ -605,53 +604,6 @@ program
605
604
  }
606
605
  });
607
606
  // ─────────────────────────────────────────────────────
608
- // watch 命令
609
- // ─────────────────────────────────────────────────────
610
- program
611
- .command('watch')
612
- .option('-d, --dir <path>', '监控目录', '.')
613
- .option('-e, --ext <exts>', '文件扩展名(逗号分隔,留空则自动检测)')
614
- .option('--guard', '自动运行 Guard 检查', true)
615
- .action(async (opts) => {
616
- try {
617
- const dir = resolve(opts.dir);
618
- let bootstrap;
619
- try {
620
- const result = await initContainer({ projectRoot: dir });
621
- bootstrap = result.bootstrap;
622
- }
623
- catch {
624
- // ServiceContainer 初始化失败不阻塞 watch(HTTP fallback 仍可用)
625
- bootstrap = await initBootstrap();
626
- }
627
- const Paths = await import('../lib/infrastructure/config/Paths.js');
628
- const specPath = Paths.getProjectSpecPath(dir);
629
- // IDE + 扩展名自动检测
630
- let exts = null;
631
- if (opts.ext) {
632
- exts = opts.ext.split(',').map((e) => e.trim());
633
- }
634
- // 不指定 --ext 时,FileWatcher 内部根据 IDE 检测结果使用默认模式
635
- const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
636
- const watcher = new FileWatcher(specPath, dir, {
637
- quiet: false,
638
- exts,
639
- });
640
- watcher.start();
641
- // 优雅退出
642
- process.on('SIGINT', async () => {
643
- await watcher.stop();
644
- await bootstrap.shutdown();
645
- process.exit(0);
646
- });
647
- }
648
- catch (err) {
649
- cli.error(`Error: ${err.message}`);
650
- cli.debug(err.stack);
651
- process.exit(1);
652
- }
653
- });
654
- // ─────────────────────────────────────────────────────
655
607
  // server 命令
656
608
  // ─────────────────────────────────────────────────────
657
609
  program
@@ -766,91 +718,6 @@ program
766
718
  cli.warn(`⚠️ SignalCollector failed to start: ${scErr.message}`);
767
719
  cli.debug(scErr.stack);
768
720
  }
769
- // 3. 启动文件监听器(仅 iOS/macOS 项目 — Xcode 工作流)
770
- // VSCode 用户通过 AutoSnippet 扩展原生处理 as:s/as:c/as:a 指令
771
- const isAppleProject = (() => {
772
- try {
773
- const entries = readdirSync(projectRoot, { withFileTypes: true });
774
- // ── Level 1: 项目配置文件(确定性高)──
775
- const hasAppleConfig = entries.some((e) => e.name === 'Package.swift' || // SPM
776
- e.name === 'Podfile' || // CocoaPods
777
- e.name === 'Cartfile' || // Carthage
778
- e.name === 'project.yml' || // XcodeGen
779
- e.name.endsWith('.xcodeproj') || // Xcode project
780
- e.name.endsWith('.xcworkspace') // Xcode workspace
781
- );
782
- if (hasAppleConfig) {
783
- return true;
784
- }
785
- // ── Level 2: 目录结构特征 ──
786
- const hasAppleDir = entries.some((e) => e.isDirectory() &&
787
- (e.name === 'Tuist' || // Tuist 项目
788
- e.name === 'Pods' || // CocoaPods 产物
789
- e.name === 'Carthage' || // Carthage 产物
790
- e.name === 'DerivedData') // Xcode 构建产物
791
- );
792
- if (hasAppleDir) {
793
- return true;
794
- }
795
- // ── Level 3: 向下扫一层(处理 monorepo 或 Sources/ 下有 .swift 的情况)──
796
- const APPLE_EXTS = new Set(['.swift', '.m', '.mm', '.h']);
797
- const SCAN_DIRS = ['Sources', 'Source', 'src', 'App', 'Classes', 'ios', 'iOS'];
798
- for (const e of entries) {
799
- // 根目录直接有 .swift/.m 文件
800
- if (!e.isDirectory() && APPLE_EXTS.has(e.name.slice(e.name.lastIndexOf('.')))) {
801
- return true;
802
- }
803
- // 常见源码目录下有 Apple 文件
804
- if (e.isDirectory() && SCAN_DIRS.includes(e.name)) {
805
- try {
806
- const subEntries = readdirSync(join(projectRoot, e.name));
807
- if (subEntries.some((f) => APPLE_EXTS.has(f.slice(f.lastIndexOf('.'))))) {
808
- return true;
809
- }
810
- }
811
- catch {
812
- /* 读取失败忽略 */
813
- }
814
- }
815
- }
816
- return false;
817
- }
818
- catch {
819
- return false;
820
- }
821
- })();
822
- if (isAppleProject) {
823
- try {
824
- const Paths = await import('../lib/infrastructure/config/Paths.js');
825
- const specPath = Paths.getProjectSpecPath(projectRoot);
826
- const isDebugMode = process.env.ASD_DEBUG === '1';
827
- // 设置 Dashboard URL 供 watcher 跳转浏览器使用
828
- // 生产模式用 API 同端口,开发模式用 vite dev 5173
829
- const dashDirCheck = DASHBOARD_DIR;
830
- const isProductionDashboard = existsSync(join(dashDirCheck, 'dist', 'index.html')) &&
831
- !existsSync(join(dashDirCheck, 'src'));
832
- if (!opts.apiOnly) {
833
- process.env.ASD_DASHBOARD_URL = isProductionDashboard
834
- ? `http://127.0.0.1:${port}`
835
- : `http://localhost:5173`;
836
- }
837
- else {
838
- process.env.ASD_DASHBOARD_URL =
839
- process.env.ASD_DASHBOARD_URL || `http://${host}:${port}`;
840
- }
841
- const { FileWatcher } = await import('../lib/service/automation/FileWatcher.js');
842
- const watcher = new FileWatcher(specPath, projectRoot, { quiet: !isDebugMode });
843
- watcher.start();
844
- if (isDebugMode) {
845
- }
846
- }
847
- catch (watchErr) {
848
- cli.warn(`⚠️ File watcher failed to start: ${watchErr.message}`);
849
- cli.debug(watchErr.stack);
850
- }
851
- }
852
- else if (process.env.ASD_DEBUG === '1') {
853
- }
854
721
  if (opts.apiOnly) {
855
722
  return;
856
723
  }
@@ -98,7 +98,49 @@ export declare class SetupService {
98
98
  };
99
99
  } | {
100
100
  label: string;
101
- fn: () => Promise<any>;
101
+ fn: () => Promise<{
102
+ dbPath: string;
103
+ }>;
104
+ } | {
105
+ label: string;
106
+ fn: () => Promise<{
107
+ skipped: boolean;
108
+ }>;
109
+ } | {
110
+ label: string;
111
+ fn: () => Promise<{
112
+ status: string;
113
+ reason: string;
114
+ hint: string;
115
+ indexed?: undefined;
116
+ skipped?: undefined;
117
+ errors?: undefined;
118
+ error?: undefined;
119
+ } | {
120
+ status: string;
121
+ reason: string;
122
+ hint?: undefined;
123
+ indexed?: undefined;
124
+ skipped?: undefined;
125
+ errors?: undefined;
126
+ error?: undefined;
127
+ } | {
128
+ status: string;
129
+ indexed: number;
130
+ skipped: number;
131
+ errors: number;
132
+ reason?: undefined;
133
+ hint?: undefined;
134
+ error?: undefined;
135
+ } | {
136
+ status: string;
137
+ error: string;
138
+ hint: string;
139
+ reason?: undefined;
140
+ indexed?: undefined;
141
+ skipped?: undefined;
142
+ errors?: undefined;
143
+ }>;
102
144
  })[];
103
145
  run(): Promise<{
104
146
  step: number;
@@ -139,7 +181,9 @@ export declare class SetupService {
139
181
  * 委托 KnowledgeSyncService 执行全字段同步(setup 场景跳过违规记录)
140
182
  */
141
183
  private _syncRecipesToDB;
142
- stepPlatform(): Promise<any>;
184
+ stepPlatform(): Promise<{
185
+ skipped: boolean;
186
+ }>;
143
187
  /**
144
188
  * 在项目根目录创建 .env 文件(从 .env.example 复制)
145
189
  * 如果 .env 已存在则跳过并提示用户手动配置。
@@ -514,34 +514,9 @@ export class SetupService {
514
514
  if (report.orphaned.length > 0) {
515
515
  }
516
516
  }
517
- /* ═══ Step 5: Snippet 初始化 (Xcode + VSCode) ═══════ */
517
+ /* ═══ Step 5: Snippet 初始化 (已移除 AI-first 迁移) ═════ */
518
518
  async stepPlatform() {
519
- const initScript = join(REPO_ROOT, 'scripts', 'init-snippets.js');
520
- if (!existsSync(initScript)) {
521
- return { skipped: true };
522
- }
523
- try {
524
- const mod = await import(initScript);
525
- // 优先使用模块级导出的 initialize 函数(无 this 依赖)
526
- // 若 mod.default 是对象则在其上调用 initialize 保留 this
527
- if (typeof mod.initialize === 'function') {
528
- const result = await mod.initialize(this.projectRoot, 'all');
529
- return result;
530
- }
531
- if (mod.default && typeof mod.default.initialize === 'function') {
532
- const result = await mod.default.initialize(this.projectRoot, 'all');
533
- return result;
534
- }
535
- if (typeof mod.default === 'function') {
536
- const result = await mod.default(this.projectRoot, 'all');
537
- return result;
538
- }
539
- return { skipped: true };
540
- }
541
- catch (e) {
542
- console.warn(` ⚠️ Snippet 初始化失败:${e.message}`);
543
- return { error: e.message };
544
- }
519
+ return { skipped: true };
545
520
  }
546
521
  /* ═══ Helpers ════════════════════════════════════════ */
547
522
  /**
@@ -2,12 +2,11 @@
2
2
  * @module SpmDiscoverer
3
3
  * @description SPM 项目发现器,适配 ProjectDiscoverer 接口
4
4
  *
5
- * 直接使用 PackageSwiftParser 解析 Package.swift,提供模块列表和文件遍历。
6
- * SpmHelper 仅用于 Xcode 工作流的依赖检查/修复,不在此链路加载。
5
+ * 内置 Package.swift 正则解析,提供模块列表和文件遍历。
7
6
  *
8
7
  * 检测: 项目根或子目录存在 Package.swift
9
8
  */
10
- import { ProjectDiscoverer } from '#core/discovery/ProjectDiscoverer.js';
9
+ import { ProjectDiscoverer } from './ProjectDiscoverer.js';
11
10
  export declare class SpmDiscoverer extends ProjectDiscoverer {
12
11
  #private;
13
12
  get id(): string;
@@ -50,6 +49,4 @@ export declare class SpmDiscoverer extends ProjectDiscoverer {
50
49
  type: string;
51
50
  }[];
52
51
  }>;
53
- /** @deprecated SpmHelper 不再由 SpmDiscoverer 持有,仅 XcodeIntegration 通过 container 使用 */
54
- getSpmService(): null;
55
52
  }
@@ -2,20 +2,27 @@
2
2
  * @module SpmDiscoverer
3
3
  * @description SPM 项目发现器,适配 ProjectDiscoverer 接口
4
4
  *
5
- * 直接使用 PackageSwiftParser 解析 Package.swift,提供模块列表和文件遍历。
6
- * SpmHelper 仅用于 Xcode 工作流的依赖检查/修复,不在此链路加载。
5
+ * 内置 Package.swift 正则解析,提供模块列表和文件遍历。
7
6
  *
8
7
  * 检测: 项目根或子目录存在 Package.swift
9
8
  */
10
- import { existsSync, readdirSync, statSync } from 'node:fs';
9
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
11
10
  import { basename, dirname, extname, join } from 'node:path';
12
- import { ProjectDiscoverer } from '#core/discovery/ProjectDiscoverer.js';
13
11
  import { LanguageService } from '#shared/LanguageService.js';
14
- import { PackageSwiftParser } from './PackageSwiftParser.js';
12
+ import { ProjectDiscoverer } from './ProjectDiscoverer.js';
13
+ const SKIP_DIRS = new Set([
14
+ 'node_modules',
15
+ '.git',
16
+ 'Build',
17
+ '.build',
18
+ '.swiftpm',
19
+ 'Pods',
20
+ 'DerivedData',
21
+ 'Carthage',
22
+ '.cursor',
23
+ ]);
15
24
  export class SpmDiscoverer extends ProjectDiscoverer {
16
- #parser = null;
17
25
  #projectRoot = null;
18
- /** >} */
19
26
  #parsedPackages = [];
20
27
  get id() {
21
28
  return 'spm';
@@ -24,12 +31,10 @@ export class SpmDiscoverer extends ProjectDiscoverer {
24
31
  return 'Swift Package Manager (SPM)';
25
32
  }
26
33
  async detect(projectRoot) {
27
- // 检查项目根是否有 Package.swift
28
34
  const hasRoot = existsSync(join(projectRoot, 'Package.swift'));
29
35
  if (hasRoot) {
30
36
  return { match: true, confidence: 0.95, reason: 'Package.swift found at project root' };
31
37
  }
32
- // 检查子目录是否有 Package.swift(多包项目)
33
38
  try {
34
39
  const entries = readdirSync(projectRoot, { withFileTypes: true });
35
40
  for (const entry of entries) {
@@ -51,12 +56,11 @@ export class SpmDiscoverer extends ProjectDiscoverer {
51
56
  }
52
57
  async load(projectRoot) {
53
58
  this.#projectRoot = projectRoot;
54
- this.#parser = new PackageSwiftParser(projectRoot);
55
59
  this.#parsedPackages = [];
56
- const allPaths = this.#parser.findAllPackageSwifts(projectRoot);
60
+ const allPaths = this.#findAllPackageSwifts(projectRoot);
57
61
  for (const pkgPath of allPaths) {
58
62
  try {
59
- const parsed = this.#parser.parse(pkgPath);
63
+ const parsed = this.#parsePackageSwift(pkgPath);
60
64
  if (parsed) {
61
65
  this.#parsedPackages.push({ pkgPath, parsed });
62
66
  }
@@ -67,9 +71,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
67
71
  }
68
72
  }
69
73
  async listTargets() {
70
- if (!this.#parser) {
71
- return [];
72
- }
73
74
  const targets = [];
74
75
  for (const { pkgPath, parsed } of this.#parsedPackages) {
75
76
  const pkgDir = dirname(pkgPath);
@@ -91,17 +92,12 @@ export class SpmDiscoverer extends ProjectDiscoverer {
91
92
  return targets;
92
93
  }
93
94
  async getTargetFiles(target) {
94
- if (!this.#parser) {
95
- return [];
96
- }
97
95
  const targetName = typeof target === 'string' ? target : target.name;
98
- // 找到 target 所在的包目录和自定义 path
99
96
  let sourcesDir = null;
100
97
  for (const { pkgPath, parsed } of this.#parsedPackages) {
101
98
  const matchTarget = parsed.targets?.find((t) => t.name === targetName);
102
99
  if (matchTarget) {
103
100
  const pkgDir = dirname(pkgPath);
104
- // 优先使用 target 声明的自定义 path
105
101
  const candidates = [];
106
102
  if (matchTarget.path) {
107
103
  candidates.push(join(pkgDir, matchTarget.path));
@@ -120,7 +116,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
120
116
  }
121
117
  }
122
118
  if (!sourcesDir) {
123
- // Fallback: projectRoot/Sources/targetName
124
119
  const fallback = join(this.#projectRoot, 'Sources', targetName);
125
120
  if (existsSync(fallback)) {
126
121
  sourcesDir = fallback;
@@ -137,7 +132,7 @@ export class SpmDiscoverer extends ProjectDiscoverer {
137
132
  }));
138
133
  }
139
134
  async getDependencyGraph() {
140
- if (!this.#projectRoot || !this.#parser) {
135
+ if (!this.#projectRoot) {
141
136
  return { nodes: [], edges: [] };
142
137
  }
143
138
  if (this.#parsedPackages.length === 0) {
@@ -147,7 +142,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
147
142
  const edges = [];
148
143
  const pkgNameSet = new Set();
149
144
  const targetToPkg = new Map();
150
- // ── 第一遍:收集所有 package + target 节点 ──
151
145
  const allParsed = [];
152
146
  const umbrellaNames = new Set();
153
147
  for (const { pkgPath, parsed } of this.#parsedPackages) {
@@ -156,14 +150,12 @@ export class SpmDiscoverer extends ProjectDiscoverer {
156
150
  }
157
151
  pkgNameSet.add(parsed.name);
158
152
  allParsed.push({ ...parsed, _dir: dirname(pkgPath) });
159
- // 跳过 umbrella 包(无 targets + 无 products)——它只是组织子包的入口
160
153
  const hasTargets = parsed.targets && parsed.targets.length > 0;
161
154
  const hasProducts = parsed.products && parsed.products.length > 0;
162
155
  if (!hasTargets && !hasProducts) {
163
156
  umbrellaNames.add(parsed.name);
164
157
  continue;
165
158
  }
166
- // package 节点
167
159
  nodes.push({
168
160
  id: parsed.name,
169
161
  label: parsed.name,
@@ -171,7 +163,6 @@ export class SpmDiscoverer extends ProjectDiscoverer {
171
163
  fullPath: dirname(pkgPath),
172
164
  targetCount: parsed.targets.length,
173
165
  });
174
- // target 节点
175
166
  for (const t of parsed.targets) {
176
167
  nodes.push({
177
168
  id: t.name,
@@ -182,27 +173,22 @@ export class SpmDiscoverer extends ProjectDiscoverer {
182
173
  });
183
174
  targetToPkg.set(t.name, parsed.name);
184
175
  }
185
- // product name → package(product 名可能和 target 名不同)
186
176
  for (const prod of parsed.products || []) {
187
177
  if (!targetToPkg.has(prod.name)) {
188
178
  targetToPkg.set(prod.name, parsed.name);
189
179
  }
190
180
  }
191
181
  }
192
- // ── 第二遍:构建 edges ──
193
182
  for (const parsed of allParsed) {
194
- // 跳过 umbrella 包的边
195
183
  if (umbrellaNames.has(parsed.name)) {
196
184
  continue;
197
185
  }
198
- // 包级 local path 依赖
199
186
  for (const dep of parsed.dependencies || []) {
200
187
  if (dep.type === 'local' && 'path' in dep && dep.path) {
201
188
  const depPkgSwift = join(parsed._dir, dep.path, 'Package.swift');
202
189
  if (existsSync(depPkgSwift)) {
203
190
  try {
204
- const depParsed = this.#parser.parse(depPkgSwift);
205
- // 跳过指向 umbrella 包的边
191
+ const depParsed = this.#parsePackageSwift(depPkgSwift);
206
192
  if (!umbrellaNames.has(depParsed.name)) {
207
193
  edges.push({ from: parsed.name, to: depParsed.name, type: 'depends_on' });
208
194
  }
@@ -224,12 +210,9 @@ export class SpmDiscoverer extends ProjectDiscoverer {
224
210
  edges.push({ from: parsed.name, to: remoteName, type: 'depends_on' });
225
211
  }
226
212
  }
227
- // target 级依赖
228
213
  for (const t of parsed.targets || []) {
229
- // target → parent package (contains)
230
214
  edges.push({ from: parsed.name, to: t.name, type: 'contains' });
231
215
  for (const depName of t.dependencies || []) {
232
- // target → target 依赖(跳过指向 umbrella 包的)
233
216
  if (!umbrellaNames.has(depName)) {
234
217
  edges.push({ from: t.name, to: depName, type: 'depends_on' });
235
218
  }
@@ -238,16 +221,148 @@ export class SpmDiscoverer extends ProjectDiscoverer {
238
221
  }
239
222
  return { nodes, edges };
240
223
  }
241
- /** @deprecated SpmHelper 不再由 SpmDiscoverer 持有,仅 XcodeIntegration 通过 container 使用 */
242
- getSpmService() {
243
- return null;
244
- }
245
224
  // ─────────────── Private Helpers ───────────────
246
- /**
247
- * 遍历源码目录,返回源文件列表
248
- * @param dir 源码根目录
249
- * @returns []}
250
- */
225
+ /** 向下递归扫描所有 Package.swift(支持多 Package 项目) */
226
+ #findAllPackageSwifts(rootDir) {
227
+ const results = [];
228
+ const scan = (dir, depth = 0) => {
229
+ if (depth > 5) {
230
+ return;
231
+ }
232
+ try {
233
+ const entries = readdirSync(dir, { withFileTypes: true });
234
+ for (const entry of entries) {
235
+ if (entry.isDirectory()) {
236
+ if (SKIP_DIRS.has(entry.name)) {
237
+ continue;
238
+ }
239
+ scan(join(dir, entry.name), depth + 1);
240
+ }
241
+ else if (entry.name === 'Package.swift') {
242
+ results.push(join(dir, entry.name));
243
+ }
244
+ }
245
+ }
246
+ catch {
247
+ // 权限错误等,跳过
248
+ }
249
+ };
250
+ scan(rootDir);
251
+ return results;
252
+ }
253
+ /** 简易解析 Package.swift(无 Swift 编译器,使用正则) */
254
+ #parsePackageSwift(packagePath) {
255
+ if (!packagePath || !existsSync(packagePath)) {
256
+ throw new Error(`Package.swift not found: ${packagePath}`);
257
+ }
258
+ const content = readFileSync(packagePath, 'utf-8');
259
+ return {
260
+ path: packagePath,
261
+ name: this.#extractName(content),
262
+ version: this.#extractVersion(content),
263
+ targets: this.#extractTargets(content),
264
+ dependencies: this.#extractDependencies(content),
265
+ products: this.#extractProducts(content),
266
+ platforms: this.#extractPlatforms(content),
267
+ };
268
+ }
269
+ #extractName(content) {
270
+ const m = content.match(/name\s*:\s*"([^"]+)"/);
271
+ return m ? m[1] : 'unknown';
272
+ }
273
+ #extractVersion(content) {
274
+ const m = content.match(/version\s*:\s*"([^"]+)"/);
275
+ return m ? m[1] : '0.0.0';
276
+ }
277
+ #extractTargets(content) {
278
+ const targets = [];
279
+ const re = /\.(?:target|testTarget|executableTarget)\s*\(/g;
280
+ let match;
281
+ while ((match = re.exec(content)) !== null) {
282
+ const type = match[0].includes('testTarget')
283
+ ? 'testTarget'
284
+ : match[0].includes('executableTarget')
285
+ ? 'executableTarget'
286
+ : 'target';
287
+ const startPos = match.index + match[0].length;
288
+ let depth = 1;
289
+ let endPos = startPos;
290
+ while (depth > 0 && endPos < content.length) {
291
+ if (content[endPos] === '(') {
292
+ depth++;
293
+ }
294
+ else if (content[endPos] === ')') {
295
+ depth--;
296
+ }
297
+ endPos++;
298
+ }
299
+ if (depth === 0) {
300
+ const block = content.substring(startPos, endPos - 1);
301
+ const nameMatch = block.match(/name\s*:\s*"([^"]+)"/);
302
+ if (!nameMatch) {
303
+ continue;
304
+ }
305
+ const pathMatch = block.match(/path\s*:\s*"([^"]+)"/);
306
+ const depsMatch = block.match(/dependencies\s*:\s*\[([^\]]*)\]/s);
307
+ const deps = [];
308
+ if (depsMatch) {
309
+ const depRe = /\.(?:product|target)\s*\(\s*name\s*:\s*"([^"]+)"/g;
310
+ let dm;
311
+ while ((dm = depRe.exec(depsMatch[1])) !== null) {
312
+ deps.push(dm[1]);
313
+ }
314
+ }
315
+ targets.push({
316
+ name: nameMatch[1],
317
+ type,
318
+ path: pathMatch ? pathMatch[1] : null,
319
+ dependencies: deps,
320
+ });
321
+ }
322
+ }
323
+ return targets;
324
+ }
325
+ #extractDependencies(content) {
326
+ const deps = [];
327
+ const urlRe = /\.package\s*\(\s*url\s*:\s*"([^"]+)"[^)]*\)/g;
328
+ let m;
329
+ while ((m = urlRe.exec(content)) !== null) {
330
+ const block = m[0];
331
+ const fromMatch = block.match(/from\s*:\s*"([^"]+)"/);
332
+ const exactMatch = block.match(/exact\s*:\s*"([^"]+)"/);
333
+ deps.push({
334
+ url: m[1],
335
+ version: fromMatch ? fromMatch[1] : exactMatch ? exactMatch[1] : null,
336
+ type: 'package',
337
+ });
338
+ }
339
+ const pathRe = /\.package\s*\(\s*path\s*:\s*"([^"]+)"\s*\)/g;
340
+ while ((m = pathRe.exec(content)) !== null) {
341
+ deps.push({
342
+ path: m[1],
343
+ type: 'local',
344
+ });
345
+ }
346
+ return deps;
347
+ }
348
+ #extractProducts(content) {
349
+ const products = [];
350
+ const re = /\.(library|executable)\s*\(\s*name\s*:\s*"([^"]+)"/g;
351
+ let m;
352
+ while ((m = re.exec(content)) !== null) {
353
+ products.push({ name: m[2], type: m[1] });
354
+ }
355
+ return products;
356
+ }
357
+ #extractPlatforms(content) {
358
+ const platforms = [];
359
+ const re = /\.(iOS|macOS|tvOS|watchOS|visionOS)\s*\(\s*\.v(\d+(?:_\d+)?)\s*\)/g;
360
+ let m;
361
+ while ((m = re.exec(content)) !== null) {
362
+ platforms.push({ name: m[1], version: m[2].replace(/_/g, '.') });
363
+ }
364
+ return platforms;
365
+ }
251
366
  #walkSourceFiles(dir) {
252
367
  const CODE_EXTS = new Set(['.swift', '.m', '.h', '.c', '.cpp', '.mm']);
253
368
  const SKIP_DIRS = new Set([
@@ -7,7 +7,6 @@ import { DiscovererRegistry } from './DiscovererRegistry.js';
7
7
  export declare function getDiscovererRegistry(): DiscovererRegistry;
8
8
  /** 重置 Registry(仅用于测试) */
9
9
  export declare function resetDiscovererRegistry(): void;
10
- export { SpmDiscoverer } from '../../platform/ios/spm/SpmDiscoverer.js';
11
10
  export { DartDiscoverer } from './DartDiscoverer.js';
12
11
  export { DiscovererRegistry } from './DiscovererRegistry.js';
13
12
  export { GenericDiscoverer } from './GenericDiscoverer.js';
@@ -17,3 +16,4 @@ export { NodeDiscoverer } from './NodeDiscoverer.js';
17
16
  export { ProjectDiscoverer } from './ProjectDiscoverer.js';
18
17
  export { PythonDiscoverer } from './PythonDiscoverer.js';
19
18
  export { RustDiscoverer } from './RustDiscoverer.js';
19
+ export { SpmDiscoverer } from './SpmDiscoverer.js';
@@ -2,7 +2,6 @@
2
2
  * @module discovery/index
3
3
  * @description ProjectDiscoverer 系统入口 - 初始化 Registry 并注册所有 Discoverer
4
4
  */
5
- import { SpmDiscoverer } from '../../platform/ios/spm/SpmDiscoverer.js';
6
5
  import { DartDiscoverer } from './DartDiscoverer.js';
7
6
  import { DiscovererRegistry } from './DiscovererRegistry.js';
8
7
  import { GenericDiscoverer } from './GenericDiscoverer.js';
@@ -11,6 +10,7 @@ import { JvmDiscoverer } from './JvmDiscoverer.js';
11
10
  import { NodeDiscoverer } from './NodeDiscoverer.js';
12
11
  import { PythonDiscoverer } from './PythonDiscoverer.js';
13
12
  import { RustDiscoverer } from './RustDiscoverer.js';
13
+ import { SpmDiscoverer } from './SpmDiscoverer.js';
14
14
  let _registry = null;
15
15
  /** 获取全局 DiscovererRegistry 单例 */
16
16
  export function getDiscovererRegistry() {
@@ -32,7 +32,6 @@ export function getDiscovererRegistry() {
32
32
  export function resetDiscovererRegistry() {
33
33
  _registry = null;
34
34
  }
35
- export { SpmDiscoverer } from '../../platform/ios/spm/SpmDiscoverer.js';
36
35
  export { DartDiscoverer } from './DartDiscoverer.js';
37
36
  export { DiscovererRegistry } from './DiscovererRegistry.js';
38
37
  export { GenericDiscoverer } from './GenericDiscoverer.js';
@@ -43,3 +42,4 @@ export { NodeDiscoverer } from './NodeDiscoverer.js';
43
42
  export { ProjectDiscoverer } from './ProjectDiscoverer.js';
44
43
  export { PythonDiscoverer } from './PythonDiscoverer.js';
45
44
  export { RustDiscoverer } from './RustDiscoverer.js';
45
+ export { SpmDiscoverer } from './SpmDiscoverer.js';