swift-patterns-mcp 1.0.17 → 1.0.18

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 (208) hide show
  1. package/README.md +48 -2
  2. package/build/__tests__/fixtures/patterns.d.ts +72 -0
  3. package/build/__tests__/fixtures/patterns.d.ts.map +1 -0
  4. package/build/__tests__/fixtures/patterns.js +105 -0
  5. package/build/__tests__/fixtures/patterns.js.map +1 -0
  6. package/build/__tests__/fixtures/tool-context.d.ts +53 -0
  7. package/build/__tests__/fixtures/tool-context.d.ts.map +1 -0
  8. package/build/__tests__/fixtures/tool-context.js +41 -0
  9. package/build/__tests__/fixtures/tool-context.js.map +1 -0
  10. package/build/__tests__/server.test.d.ts +2 -0
  11. package/build/__tests__/server.test.d.ts.map +1 -0
  12. package/build/__tests__/server.test.js +167 -0
  13. package/build/__tests__/server.test.js.map +1 -0
  14. package/build/cli/__tests__/setup.test.d.ts +2 -0
  15. package/build/cli/__tests__/setup.test.d.ts.map +1 -0
  16. package/build/cli/__tests__/setup.test.js +85 -0
  17. package/build/cli/__tests__/setup.test.js.map +1 -0
  18. package/build/cli/patreon.js +3 -11
  19. package/build/cli/patreon.js.map +1 -1
  20. package/build/cli/router.d.ts +6 -0
  21. package/build/cli/router.d.ts.map +1 -0
  22. package/build/cli/router.js +42 -0
  23. package/build/cli/router.js.map +1 -0
  24. package/build/cli/setup-utils.d.ts +17 -0
  25. package/build/cli/setup-utils.d.ts.map +1 -0
  26. package/build/cli/setup-utils.js +86 -0
  27. package/build/cli/setup-utils.js.map +1 -0
  28. package/build/cli/setup.d.ts +3 -0
  29. package/build/cli/setup.d.ts.map +1 -0
  30. package/build/cli/setup.js +141 -0
  31. package/build/cli/setup.js.map +1 -0
  32. package/build/config/__tests__/sources.test.js +0 -16
  33. package/build/config/__tests__/sources.test.js.map +1 -1
  34. package/build/config/creators.d.ts +1 -2
  35. package/build/config/creators.d.ts.map +1 -1
  36. package/build/config/sources.d.ts +0 -16
  37. package/build/config/sources.d.ts.map +1 -1
  38. package/build/config/sources.js +1 -26
  39. package/build/config/sources.js.map +1 -1
  40. package/build/index.js +9 -206
  41. package/build/index.js.map +1 -1
  42. package/build/integration/__tests__/mcp-client.test.js +21 -7
  43. package/build/integration/__tests__/mcp-client.test.js.map +1 -1
  44. package/build/{sources/premium/__tests__ → integration/__tests__/slow}/patreon-integration.test.d.ts +1 -1
  45. package/build/integration/__tests__/slow/patreon-integration.test.d.ts.map +1 -0
  46. package/build/{sources/premium/__tests__ → integration/__tests__/slow}/patreon-integration.test.js +9 -19
  47. package/build/integration/__tests__/slow/patreon-integration.test.js.map +1 -0
  48. package/build/integration/__tests__/slow/response-quality.test.d.ts.map +1 -0
  49. package/build/integration/__tests__/{response-quality.test.js → slow/response-quality.test.js} +105 -19
  50. package/build/integration/__tests__/slow/response-quality.test.js.map +1 -0
  51. package/build/integration/test-client.d.ts +2 -0
  52. package/build/integration/test-client.d.ts.map +1 -1
  53. package/build/integration/test-client.js +48 -8
  54. package/build/integration/test-client.js.map +1 -1
  55. package/build/server.d.ts +5 -0
  56. package/build/server.d.ts.map +1 -0
  57. package/build/server.js +93 -0
  58. package/build/server.js.map +1 -0
  59. package/build/sources/free/__tests__/nilcoalescing.test.js +49 -20
  60. package/build/sources/free/__tests__/nilcoalescing.test.js.map +1 -1
  61. package/build/sources/free/__tests__/pointfree.test.js +73 -0
  62. package/build/sources/free/__tests__/pointfree.test.js.map +1 -1
  63. package/build/sources/free/__tests__/rssPatternSource.test.js +87 -20
  64. package/build/sources/free/__tests__/rssPatternSource.test.js.map +1 -1
  65. package/build/sources/free/__tests__/sundell.test.js +49 -20
  66. package/build/sources/free/__tests__/sundell.test.js.map +1 -1
  67. package/build/sources/free/__tests__/vanderlee.test.js +60 -20
  68. package/build/sources/free/__tests__/vanderlee.test.js.map +1 -1
  69. package/build/sources/free/nilcoalescing.d.ts +1 -3
  70. package/build/sources/free/nilcoalescing.d.ts.map +1 -1
  71. package/build/sources/free/nilcoalescing.js.map +1 -1
  72. package/build/sources/free/sundell.d.ts +1 -3
  73. package/build/sources/free/sundell.d.ts.map +1 -1
  74. package/build/sources/free/sundell.js.map +1 -1
  75. package/build/sources/free/vanderlee.d.ts +1 -3
  76. package/build/sources/free/vanderlee.d.ts.map +1 -1
  77. package/build/sources/free/vanderlee.js.map +1 -1
  78. package/build/sources/premium/__tests__/patreon-dedup.test.d.ts +2 -0
  79. package/build/sources/premium/__tests__/patreon-dedup.test.d.ts.map +1 -0
  80. package/build/sources/premium/__tests__/patreon-dedup.test.js +78 -0
  81. package/build/sources/premium/__tests__/patreon-dedup.test.js.map +1 -0
  82. package/build/sources/premium/__tests__/patreon-dl.test.d.ts +2 -0
  83. package/build/sources/premium/__tests__/patreon-dl.test.d.ts.map +1 -0
  84. package/build/sources/premium/__tests__/patreon-dl.test.js +170 -0
  85. package/build/sources/premium/__tests__/patreon-dl.test.js.map +1 -0
  86. package/build/sources/premium/__tests__/patreon-oauth.test.d.ts +2 -0
  87. package/build/sources/premium/__tests__/patreon-oauth.test.d.ts.map +1 -0
  88. package/build/sources/premium/__tests__/patreon-oauth.test.js +220 -0
  89. package/build/sources/premium/__tests__/patreon-oauth.test.js.map +1 -0
  90. package/build/sources/premium/__tests__/patreon-scoring.test.d.ts +2 -0
  91. package/build/sources/premium/__tests__/patreon-scoring.test.d.ts.map +1 -0
  92. package/build/sources/premium/__tests__/patreon-scoring.test.js +103 -0
  93. package/build/sources/premium/__tests__/patreon-scoring.test.js.map +1 -0
  94. package/build/sources/premium/__tests__/youtube.test.d.ts +2 -0
  95. package/build/sources/premium/__tests__/youtube.test.d.ts.map +1 -0
  96. package/build/sources/premium/__tests__/youtube.test.js +107 -0
  97. package/build/sources/premium/__tests__/youtube.test.js.map +1 -0
  98. package/build/sources/premium/patreon-dedup.d.ts +8 -0
  99. package/build/sources/premium/patreon-dedup.d.ts.map +1 -0
  100. package/build/sources/premium/patreon-dedup.js +61 -0
  101. package/build/sources/premium/patreon-dedup.js.map +1 -0
  102. package/build/sources/premium/patreon-enrichment.d.ts +30 -0
  103. package/build/sources/premium/patreon-enrichment.d.ts.map +1 -0
  104. package/build/sources/premium/patreon-enrichment.js +154 -0
  105. package/build/sources/premium/patreon-enrichment.js.map +1 -0
  106. package/build/sources/premium/patreon-oauth.d.ts.map +1 -1
  107. package/build/sources/premium/patreon-oauth.js +62 -21
  108. package/build/sources/premium/patreon-oauth.js.map +1 -1
  109. package/build/sources/premium/patreon-scoring.d.ts +9 -0
  110. package/build/sources/premium/patreon-scoring.d.ts.map +1 -0
  111. package/build/sources/premium/patreon-scoring.js +43 -0
  112. package/build/sources/premium/patreon-scoring.js.map +1 -0
  113. package/build/sources/premium/patreon.d.ts +9 -40
  114. package/build/sources/premium/patreon.d.ts.map +1 -1
  115. package/build/sources/premium/patreon.js +168 -139
  116. package/build/sources/premium/patreon.js.map +1 -1
  117. package/build/sources/premium/youtube.d.ts +3 -6
  118. package/build/sources/premium/youtube.d.ts.map +1 -1
  119. package/build/sources/premium/youtube.js +31 -53
  120. package/build/sources/premium/youtube.js.map +1 -1
  121. package/build/tools/handlers/__tests__/getPatreonPatterns.test.js +29 -96
  122. package/build/tools/handlers/__tests__/getPatreonPatterns.test.js.map +1 -1
  123. package/build/tools/handlers/__tests__/handlers.test.js +26 -125
  124. package/build/tools/handlers/__tests__/handlers.test.js.map +1 -1
  125. package/build/tools/handlers/__tests__/harness.d.ts +8 -0
  126. package/build/tools/handlers/__tests__/harness.d.ts.map +1 -0
  127. package/build/tools/handlers/__tests__/harness.js +36 -0
  128. package/build/tools/handlers/__tests__/harness.js.map +1 -0
  129. package/build/tools/handlers/cached-search.d.ts +22 -0
  130. package/build/tools/handlers/cached-search.d.ts.map +1 -0
  131. package/build/tools/handlers/cached-search.js +59 -0
  132. package/build/tools/handlers/cached-search.js.map +1 -0
  133. package/build/tools/handlers/enableSource.d.ts.map +1 -1
  134. package/build/tools/handlers/enableSource.js +5 -2
  135. package/build/tools/handlers/enableSource.js.map +1 -1
  136. package/build/tools/handlers/getPatreonPatterns.d.ts.map +1 -1
  137. package/build/tools/handlers/getPatreonPatterns.js +40 -31
  138. package/build/tools/handlers/getPatreonPatterns.js.map +1 -1
  139. package/build/tools/handlers/getSwiftPattern.d.ts.map +1 -1
  140. package/build/tools/handlers/getSwiftPattern.js +111 -68
  141. package/build/tools/handlers/getSwiftPattern.js.map +1 -1
  142. package/build/tools/handlers/listContentSources.js +1 -1
  143. package/build/tools/handlers/searchSwiftContent.d.ts.map +1 -1
  144. package/build/tools/handlers/searchSwiftContent.js +93 -95
  145. package/build/tools/handlers/searchSwiftContent.js.map +1 -1
  146. package/build/tools/handlers/setupPatreon.d.ts.map +1 -1
  147. package/build/tools/handlers/setupPatreon.js +44 -27
  148. package/build/tools/handlers/setupPatreon.js.map +1 -1
  149. package/build/tools/registration.d.ts +10 -0
  150. package/build/tools/registration.d.ts.map +1 -0
  151. package/build/tools/registration.js +117 -0
  152. package/build/tools/registration.js.map +1 -0
  153. package/build/tools/types.d.ts +3 -1
  154. package/build/tools/types.d.ts.map +1 -1
  155. package/build/tools/validation.d.ts +26 -0
  156. package/build/tools/validation.d.ts.map +1 -0
  157. package/build/tools/validation.js +60 -0
  158. package/build/tools/validation.js.map +1 -0
  159. package/build/utils/__tests__/cache.test.js +96 -1
  160. package/build/utils/__tests__/cache.test.js.map +1 -1
  161. package/build/utils/__tests__/http.test.d.ts +2 -0
  162. package/build/utils/__tests__/http.test.d.ts.map +1 -0
  163. package/build/utils/__tests__/http.test.js +108 -0
  164. package/build/utils/__tests__/http.test.js.map +1 -0
  165. package/build/utils/__tests__/inflight-dedup.test.d.ts +2 -0
  166. package/build/utils/__tests__/inflight-dedup.test.d.ts.map +1 -0
  167. package/build/utils/__tests__/inflight-dedup.test.js +124 -0
  168. package/build/utils/__tests__/inflight-dedup.test.js.map +1 -0
  169. package/build/utils/__tests__/intent-cache.test.js +81 -4
  170. package/build/utils/__tests__/intent-cache.test.js.map +1 -1
  171. package/build/utils/__tests__/query-analysis.test.d.ts +2 -0
  172. package/build/utils/__tests__/query-analysis.test.d.ts.map +1 -0
  173. package/build/utils/__tests__/query-analysis.test.js +52 -0
  174. package/build/utils/__tests__/query-analysis.test.js.map +1 -0
  175. package/build/utils/__tests__/response-helpers.test.js +11 -1
  176. package/build/utils/__tests__/response-helpers.test.js.map +1 -1
  177. package/build/utils/__tests__/search.test.js +1 -60
  178. package/build/utils/__tests__/search.test.js.map +1 -1
  179. package/build/utils/cache.d.ts +11 -0
  180. package/build/utils/cache.d.ts.map +1 -1
  181. package/build/utils/cache.js +23 -0
  182. package/build/utils/cache.js.map +1 -1
  183. package/build/utils/fetch.d.ts +2 -3
  184. package/build/utils/fetch.d.ts.map +1 -1
  185. package/build/utils/fetch.js +2 -3
  186. package/build/utils/fetch.js.map +1 -1
  187. package/build/utils/patreon-env.d.ts +5 -0
  188. package/build/utils/patreon-env.d.ts.map +1 -0
  189. package/build/utils/patreon-env.js +17 -0
  190. package/build/utils/patreon-env.js.map +1 -0
  191. package/build/utils/query-analysis.d.ts +25 -0
  192. package/build/utils/query-analysis.d.ts.map +1 -0
  193. package/build/utils/query-analysis.js +110 -0
  194. package/build/utils/query-analysis.js.map +1 -0
  195. package/build/utils/response-helpers.d.ts +16 -0
  196. package/build/utils/response-helpers.d.ts.map +1 -1
  197. package/build/utils/response-helpers.js +43 -0
  198. package/build/utils/response-helpers.js.map +1 -1
  199. package/build/utils/search.d.ts +0 -3
  200. package/build/utils/search.d.ts.map +1 -1
  201. package/build/utils/search.js +2 -41
  202. package/build/utils/search.js.map +1 -1
  203. package/package.json +19 -12
  204. package/build/integration/__tests__/response-quality.test.d.ts.map +0 -1
  205. package/build/integration/__tests__/response-quality.test.js.map +0 -1
  206. package/build/sources/premium/__tests__/patreon-integration.test.d.ts.map +0 -1
  207. package/build/sources/premium/__tests__/patreon-integration.test.js.map +0 -1
  208. /package/build/integration/__tests__/{response-quality.test.d.ts → slow/response-quality.test.d.ts} +0 -0
package/README.md CHANGED
@@ -72,12 +72,55 @@ Access exclusive content from top iOS educators: **Kavsoft**, **SwiftUI Codes**,
72
72
 
73
73
  ## 🚀 Quick Start
74
74
 
75
- ### Install
75
+ ### Run Setup
76
76
 
77
77
  ```bash
78
78
  npx -y swift-patterns-mcp@latest
79
79
  ```
80
80
 
81
+ In an interactive terminal, this opens the setup wizard.
82
+ When launched by an MCP client (non-interactive stdio), it runs as the MCP server automatically.
83
+
84
+ ### Interactive Setup Wizard
85
+
86
+ ```bash
87
+ npx -y swift-patterns-mcp@latest setup
88
+ ```
89
+
90
+ If installed globally, you can also run:
91
+
92
+ ```bash
93
+ swift-patterns-mcp setup
94
+ ```
95
+
96
+ The wizard helps you choose:
97
+ - Config scope (local project vs global)
98
+ - MCP client (Cursor, Claude Code, Windsurf, VS Code)
99
+ - Optional Patreon setup prompt
100
+
101
+ ### Non-interactive Setup (CI/Scripts)
102
+
103
+ ```bash
104
+ # Cursor
105
+ npx -y swift-patterns-mcp@latest setup --cursor --global
106
+ npx -y swift-patterns-mcp@latest setup --cursor --local
107
+
108
+ # Claude Code
109
+ npx -y swift-patterns-mcp@latest setup --claude --global
110
+
111
+ # Windsurf
112
+ npx -y swift-patterns-mcp@latest setup --windsurf --global
113
+
114
+ # VS Code
115
+ npx -y swift-patterns-mcp@latest setup --vscode --local
116
+
117
+ # All clients
118
+ npx -y swift-patterns-mcp@latest setup --all --global
119
+ ```
120
+
121
+ Use `--global` (`-g`) or `--local` (`-l`) to skip the location prompt.
122
+ Use `--cursor`, `--claude`, `--windsurf`, `--vscode`, or `--all` to skip the client prompt.
123
+
81
124
  ### Configure Your AI Assistant
82
125
 
83
126
  #### Cursor
@@ -359,6 +402,9 @@ Patreon requires OAuth apps to be registered by creators. You don't need to laun
359
402
  # List all content sources and status
360
403
  swift-patterns-mcp sources
361
404
 
405
+ # Interactive onboarding/configuration wizard
406
+ swift-patterns-mcp setup
407
+
362
408
  # Patreon integration
363
409
  swift-patterns-mcp patreon setup # Connect your Patreon account
364
410
  swift-patterns-mcp patreon status # Check connection status
@@ -450,4 +496,4 @@ MIT License - Copyright (c) 2026 Lasha Efremidze
450
496
 
451
497
  **Made with ❤️ for the Swift community**
452
498
 
453
- [⭐ Star this repo](https://github.com/efremidze/swift-patterns-mcp) • [🐛 Report Bug](./issues) • [✨ Request Feature](./issues)
499
+ [⭐ Star this repo](https://github.com/efremidze/swift-patterns-mcp) • [🐛 Report Bug](https://github.com/efremidze/swift-patterns-mcp/issues) • [✨ Request Feature](https://github.com/efremidze/swift-patterns-mcp/issues)
@@ -0,0 +1,72 @@
1
+ import type { PatreonPattern } from '../../tools/types.js';
2
+ export declare const FREE_SOURCE_PATTERNS: {
3
+ readonly sundell: readonly [{
4
+ readonly id: "sundell-1";
5
+ readonly title: "Advanced SwiftUI Patterns";
6
+ readonly url: "https://swiftbysundell.com/swiftui";
7
+ readonly excerpt: "Learn advanced SwiftUI patterns for production apps";
8
+ readonly content: "Full content about SwiftUI state management and views";
9
+ readonly topics: readonly ["swiftui", "architecture"];
10
+ readonly relevanceScore: 85;
11
+ readonly hasCode: true;
12
+ readonly publishDate: "2024-01-15T00:00:00Z";
13
+ }, {
14
+ readonly id: "sundell-2";
15
+ readonly title: "Basic Swift Tips";
16
+ readonly url: "https://swiftbysundell.com/tips";
17
+ readonly excerpt: "Simple tips for Swift developers";
18
+ readonly content: "Basic content without code examples";
19
+ readonly topics: readonly ["swift"];
20
+ readonly relevanceScore: 55;
21
+ readonly hasCode: false;
22
+ readonly publishDate: "2024-01-10T00:00:00Z";
23
+ }];
24
+ readonly vanderlee: readonly [{
25
+ readonly id: "vanderlee-1";
26
+ readonly title: "iOS Performance Optimization";
27
+ readonly url: "https://avanderlee.com/performance";
28
+ readonly excerpt: "Optimize your iOS app performance";
29
+ readonly content: "Detailed performance optimization techniques";
30
+ readonly topics: readonly ["performance", "optimization"];
31
+ readonly relevanceScore: 78;
32
+ readonly hasCode: true;
33
+ readonly publishDate: "2024-01-12T00:00:00Z";
34
+ }, {
35
+ readonly id: "vanderlee-2";
36
+ readonly title: "Debugging Tips";
37
+ readonly url: "https://avanderlee.com/debugging";
38
+ readonly excerpt: "Debug your iOS apps effectively";
39
+ readonly content: "Debugging techniques without code";
40
+ readonly topics: readonly ["debugging"];
41
+ readonly relevanceScore: 65;
42
+ readonly hasCode: false;
43
+ readonly publishDate: "2024-01-08T00:00:00Z";
44
+ }];
45
+ readonly nilcoalescing: readonly [{
46
+ readonly id: "nilcoalescing-1";
47
+ readonly title: "SwiftUI Navigation Deep Dive";
48
+ readonly url: "https://nilcoalescing.com/navigation";
49
+ readonly excerpt: "Master SwiftUI navigation patterns";
50
+ readonly content: "Navigation code examples and patterns";
51
+ readonly topics: readonly ["swiftui", "navigation"];
52
+ readonly relevanceScore: 72;
53
+ readonly hasCode: true;
54
+ readonly publishDate: "2024-01-14T00:00:00Z";
55
+ }];
56
+ readonly pointfree: readonly [{
57
+ readonly id: "pointfree-1";
58
+ readonly title: "Composable Architecture Case Study";
59
+ readonly url: "https://github.com/pointfreeco/pointfreeco/blob/main/Sources/Models/Episodes/0001-functions.md";
60
+ readonly excerpt: "Build apps with TCA";
61
+ readonly content: "TCA reducer and store patterns";
62
+ readonly topics: readonly ["architecture", "tca"];
63
+ readonly relevanceScore: 90;
64
+ readonly hasCode: true;
65
+ readonly publishDate: "2024-01-16T00:00:00Z";
66
+ }];
67
+ };
68
+ export declare function createPatreonPattern(overrides?: Partial<PatreonPattern>): PatreonPattern;
69
+ export declare const PATREON_PATTERNS_WITH_CODE: PatreonPattern[];
70
+ export declare const PATREON_PATTERNS_MIXED: PatreonPattern[];
71
+ export declare const PATREON_PATTERNS_TWELVE: PatreonPattern[];
72
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/patterns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EvB,CAAC;AAEX,wBAAgB,oBAAoB,CAAC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GAAG,cAAc,CAc5F;AAED,eAAO,MAAM,0BAA0B,kBAGtC,CAAC;AAEF,eAAO,MAAM,sBAAsB,kBAGlC,CAAC;AAEF,eAAO,MAAM,uBAAuB,kBAMnC,CAAC"}
@@ -0,0 +1,105 @@
1
+ export const FREE_SOURCE_PATTERNS = {
2
+ sundell: [
3
+ {
4
+ id: 'sundell-1',
5
+ title: 'Advanced SwiftUI Patterns',
6
+ url: 'https://swiftbysundell.com/swiftui',
7
+ excerpt: 'Learn advanced SwiftUI patterns for production apps',
8
+ content: 'Full content about SwiftUI state management and views',
9
+ topics: ['swiftui', 'architecture'],
10
+ relevanceScore: 85,
11
+ hasCode: true,
12
+ publishDate: '2024-01-15T00:00:00Z',
13
+ },
14
+ {
15
+ id: 'sundell-2',
16
+ title: 'Basic Swift Tips',
17
+ url: 'https://swiftbysundell.com/tips',
18
+ excerpt: 'Simple tips for Swift developers',
19
+ content: 'Basic content without code examples',
20
+ topics: ['swift'],
21
+ relevanceScore: 55,
22
+ hasCode: false,
23
+ publishDate: '2024-01-10T00:00:00Z',
24
+ },
25
+ ],
26
+ vanderlee: [
27
+ {
28
+ id: 'vanderlee-1',
29
+ title: 'iOS Performance Optimization',
30
+ url: 'https://avanderlee.com/performance',
31
+ excerpt: 'Optimize your iOS app performance',
32
+ content: 'Detailed performance optimization techniques',
33
+ topics: ['performance', 'optimization'],
34
+ relevanceScore: 78,
35
+ hasCode: true,
36
+ publishDate: '2024-01-12T00:00:00Z',
37
+ },
38
+ {
39
+ id: 'vanderlee-2',
40
+ title: 'Debugging Tips',
41
+ url: 'https://avanderlee.com/debugging',
42
+ excerpt: 'Debug your iOS apps effectively',
43
+ content: 'Debugging techniques without code',
44
+ topics: ['debugging'],
45
+ relevanceScore: 65,
46
+ hasCode: false,
47
+ publishDate: '2024-01-08T00:00:00Z',
48
+ },
49
+ ],
50
+ nilcoalescing: [
51
+ {
52
+ id: 'nilcoalescing-1',
53
+ title: 'SwiftUI Navigation Deep Dive',
54
+ url: 'https://nilcoalescing.com/navigation',
55
+ excerpt: 'Master SwiftUI navigation patterns',
56
+ content: 'Navigation code examples and patterns',
57
+ topics: ['swiftui', 'navigation'],
58
+ relevanceScore: 72,
59
+ hasCode: true,
60
+ publishDate: '2024-01-14T00:00:00Z',
61
+ },
62
+ ],
63
+ pointfree: [
64
+ {
65
+ id: 'pointfree-1',
66
+ title: 'Composable Architecture Case Study',
67
+ url: 'https://github.com/pointfreeco/pointfreeco/blob/main/Sources/Models/Episodes/0001-functions.md',
68
+ excerpt: 'Build apps with TCA',
69
+ content: 'TCA reducer and store patterns',
70
+ topics: ['architecture', 'tca'],
71
+ relevanceScore: 90,
72
+ hasCode: true,
73
+ publishDate: '2024-01-16T00:00:00Z',
74
+ },
75
+ ],
76
+ };
77
+ export function createPatreonPattern(overrides = {}) {
78
+ return {
79
+ id: 'pat-001',
80
+ title: 'Test Pattern',
81
+ url: 'https://patreon.com/posts/test-123',
82
+ publishDate: '2024-06-15T00:00:00Z',
83
+ excerpt: 'A test excerpt about SwiftUI',
84
+ content: 'Full test content',
85
+ creator: 'TestCreator',
86
+ topics: ['swiftui'],
87
+ relevanceScore: 80,
88
+ hasCode: true,
89
+ ...overrides,
90
+ };
91
+ }
92
+ export const PATREON_PATTERNS_WITH_CODE = [
93
+ createPatreonPattern({ id: 'pat-101', title: 'Pattern A', hasCode: true }),
94
+ createPatreonPattern({ id: 'pat-102', title: 'Pattern B', hasCode: true }),
95
+ ];
96
+ export const PATREON_PATTERNS_MIXED = [
97
+ createPatreonPattern({ id: 'pat-201', title: 'With Code', hasCode: true }),
98
+ createPatreonPattern({ id: 'pat-202', title: 'No Code', hasCode: false }),
99
+ ];
100
+ export const PATREON_PATTERNS_TWELVE = Array.from({ length: 12 }, (_, index) => createPatreonPattern({
101
+ id: `pat-${String(index + 1).padStart(3, '0')}`,
102
+ title: `Pattern ${index + 1}`,
103
+ creator: `Creator ${index + 1}`,
104
+ }));
105
+ //# sourceMappingURL=patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/__tests__/fixtures/patterns.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { ToolContext } from '../../tools/types.js';
2
+ export declare function createMockSourceManager(): {
3
+ getAllSources: import("vitest").Mock<(...args: any[]) => any>;
4
+ getSource: import("vitest").Mock<(id: string) => {
5
+ id: "sundell";
6
+ name: "Swift by Sundell";
7
+ type: "free";
8
+ requiresAuth: false;
9
+ isEnabled: true;
10
+ isConfigured: true;
11
+ description: "Swift articles";
12
+ } | {
13
+ id: "vanderlee";
14
+ name: "Antoine van der Lee";
15
+ type: "free";
16
+ requiresAuth: false;
17
+ isEnabled: true;
18
+ isConfigured: true;
19
+ description: "iOS tips";
20
+ } | {
21
+ id: "nilcoalescing";
22
+ name: "Nil Coalescing";
23
+ type: "free";
24
+ requiresAuth: false;
25
+ isEnabled: true;
26
+ isConfigured: true;
27
+ description: "SwiftUI tips";
28
+ } | {
29
+ id: "pointfree";
30
+ name: "Point-Free";
31
+ type: "free";
32
+ requiresAuth: false;
33
+ isEnabled: true;
34
+ isConfigured: true;
35
+ description: "Open source patterns";
36
+ } | {
37
+ id: "patreon";
38
+ name: "Patreon";
39
+ type: "premium";
40
+ requiresAuth: true;
41
+ isEnabled: false;
42
+ isConfigured: false;
43
+ description: "Premium content";
44
+ } | undefined>;
45
+ isSourceConfigured: import("vitest").Mock<(id: string) => boolean>;
46
+ getSemanticRecallConfig: import("vitest").Mock<(...args: any[]) => any>;
47
+ getMemvidConfig: import("vitest").Mock<(...args: any[]) => any>;
48
+ enableSource: import("vitest").Mock<(...args: any[]) => any>;
49
+ disableSource: import("vitest").Mock<(...args: any[]) => any>;
50
+ getEnabledSources: import("vitest").Mock<(...args: any[]) => any>;
51
+ };
52
+ export declare function createToolContext(overrides?: Partial<ToolContext>): ToolContext;
53
+ //# sourceMappingURL=tool-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-context.d.ts","sourceRoot":"","sources":["../../../src/__tests__/fixtures/tool-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAUxD,wBAAgB,uBAAuB;;0CAKb,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDACG,MAAM;;;;;;EAmBxC;AAED,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,OAAO,CAAC,WAAW,CAAM,GAAG,WAAW,CAMnF"}
@@ -0,0 +1,41 @@
1
+ import { vi } from 'vitest';
2
+ const DEFAULT_SOURCES = [
3
+ { id: 'sundell', name: 'Swift by Sundell', type: 'free', requiresAuth: false, isEnabled: true, isConfigured: true, description: 'Swift articles' },
4
+ { id: 'vanderlee', name: 'Antoine van der Lee', type: 'free', requiresAuth: false, isEnabled: true, isConfigured: true, description: 'iOS tips' },
5
+ { id: 'nilcoalescing', name: 'Nil Coalescing', type: 'free', requiresAuth: false, isEnabled: true, isConfigured: true, description: 'SwiftUI tips' },
6
+ { id: 'pointfree', name: 'Point-Free', type: 'free', requiresAuth: false, isEnabled: true, isConfigured: true, description: 'Open source patterns' },
7
+ { id: 'patreon', name: 'Patreon', type: 'premium', requiresAuth: true, isEnabled: false, isConfigured: false, description: 'Premium content' },
8
+ ];
9
+ export function createMockSourceManager() {
10
+ const sources = DEFAULT_SOURCES.map((source) => ({ ...source }));
11
+ return {
12
+ getAllSources: vi.fn().mockReturnValue(sources),
13
+ getSource: vi.fn((id) => sources.find((source) => source.id === id)),
14
+ isSourceConfigured: vi.fn((id) => {
15
+ const source = sources.find((item) => item.id === id);
16
+ return source?.isConfigured ?? false;
17
+ }),
18
+ getSemanticRecallConfig: vi.fn().mockReturnValue({
19
+ enabled: false,
20
+ minLexicalScore: 0.35,
21
+ minRelevanceScore: 70,
22
+ }),
23
+ getMemvidConfig: vi.fn().mockReturnValue({
24
+ enabled: false,
25
+ autoStore: false,
26
+ useEmbeddings: false,
27
+ embeddingModel: 'bge-small',
28
+ }),
29
+ enableSource: vi.fn(),
30
+ disableSource: vi.fn(),
31
+ getEnabledSources: vi.fn().mockReturnValue(sources.filter((source) => source.isEnabled)),
32
+ };
33
+ }
34
+ export function createToolContext(overrides = {}) {
35
+ return {
36
+ sourceManager: createMockSourceManager(),
37
+ patreonSource: null,
38
+ ...overrides,
39
+ };
40
+ }
41
+ //# sourceMappingURL=tool-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-context.js","sourceRoot":"","sources":["../../../src/__tests__/fixtures/tool-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/server.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,167 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
+ const mockGetToolList = vi.hoisted(() => vi.fn());
3
+ const mockGetHandler = vi.hoisted(() => vi.fn());
4
+ const mockCreateErrorResponseFromError = vi.hoisted(() => vi.fn());
5
+ const mockPrefetchAllSources = vi.hoisted(() => vi.fn());
6
+ const mockPrefetchEmbeddingModel = vi.hoisted(() => vi.fn());
7
+ const mockLoggerInfo = vi.hoisted(() => vi.fn());
8
+ const mockLoggerWarn = vi.hoisted(() => vi.fn());
9
+ const mockLoggerError = vi.hoisted(() => vi.fn());
10
+ const mockConnect = vi.hoisted(() => vi.fn());
11
+ const handlerMap = vi.hoisted(() => new Map());
12
+ const sourceManagerState = vi.hoisted(() => ({
13
+ enabledSources: [{ id: 'sundell' }],
14
+ patreonConfigured: false,
15
+ prefetchEnabled: false,
16
+ semanticEnabled: false,
17
+ }));
18
+ const sourceManagerInstances = vi.hoisted(() => []);
19
+ const listToolsSchema = vi.hoisted(() => Symbol('ListToolsRequestSchema'));
20
+ const callToolSchema = vi.hoisted(() => Symbol('CallToolRequestSchema'));
21
+ const MockServer = vi.hoisted(() => {
22
+ return class {
23
+ setRequestHandler = vi.fn((schema, handler) => {
24
+ handlerMap.set(schema, handler);
25
+ });
26
+ connect = mockConnect;
27
+ };
28
+ });
29
+ const MockSourceManager = vi.hoisted(() => {
30
+ return class {
31
+ getEnabledSources = vi.fn(() => sourceManagerState.enabledSources);
32
+ isSourceConfigured = vi.fn((id) => (id === 'patreon' ? sourceManagerState.patreonConfigured : true));
33
+ markSourceConfigured = vi.fn();
34
+ isPrefetchEnabled = vi.fn(() => sourceManagerState.prefetchEnabled);
35
+ getSemanticRecallConfig = vi.fn(() => ({ enabled: sourceManagerState.semanticEnabled }));
36
+ constructor() {
37
+ sourceManagerInstances.push(this);
38
+ }
39
+ };
40
+ });
41
+ vi.mock('@modelcontextprotocol/sdk/server/index.js', () => ({
42
+ Server: MockServer,
43
+ }));
44
+ vi.mock('@modelcontextprotocol/sdk/server/stdio.js', () => ({
45
+ StdioServerTransport: class MockStdioServerTransport {
46
+ },
47
+ }));
48
+ vi.mock('@modelcontextprotocol/sdk/types.js', () => ({
49
+ ListToolsRequestSchema: listToolsSchema,
50
+ CallToolRequestSchema: callToolSchema,
51
+ }));
52
+ vi.mock('../config/sources.js', () => ({
53
+ default: MockSourceManager,
54
+ }));
55
+ vi.mock('../tools/index.js', () => ({
56
+ getHandler: mockGetHandler,
57
+ }));
58
+ vi.mock('../tools/registration.js', () => ({
59
+ getToolList: mockGetToolList,
60
+ }));
61
+ vi.mock('../utils/response-helpers.js', () => ({
62
+ createErrorResponseFromError: mockCreateErrorResponseFromError,
63
+ }));
64
+ vi.mock('../utils/source-registry.js', () => ({
65
+ prefetchAllSources: mockPrefetchAllSources,
66
+ }));
67
+ vi.mock('../utils/semantic-recall.js', () => ({
68
+ prefetchEmbeddingModel: mockPrefetchEmbeddingModel,
69
+ }));
70
+ vi.mock('../sources/premium/patreon.js', () => ({
71
+ PatreonSource: class PatreonSource {
72
+ },
73
+ }));
74
+ vi.mock('../utils/logger.js', () => ({
75
+ default: {
76
+ info: mockLoggerInfo,
77
+ warn: mockLoggerWarn,
78
+ error: mockLoggerError,
79
+ },
80
+ }));
81
+ import { startServer } from '../server.js';
82
+ function getLatestSourceManager() {
83
+ const instance = sourceManagerInstances[sourceManagerInstances.length - 1];
84
+ if (!instance)
85
+ throw new Error('Expected SourceManager instance');
86
+ return instance;
87
+ }
88
+ describe('startServer', () => {
89
+ beforeEach(() => {
90
+ vi.clearAllMocks();
91
+ handlerMap.clear();
92
+ sourceManagerInstances.length = 0;
93
+ sourceManagerState.enabledSources = [{ id: 'sundell' }];
94
+ sourceManagerState.patreonConfigured = false;
95
+ sourceManagerState.prefetchEnabled = false;
96
+ sourceManagerState.semanticEnabled = false;
97
+ mockConnect.mockResolvedValue(undefined);
98
+ mockGetToolList.mockReturnValue([{ name: 'get_swift_pattern' }]);
99
+ mockGetHandler.mockReturnValue(undefined);
100
+ mockCreateErrorResponseFromError.mockReturnValue({
101
+ content: [{ type: 'text', text: 'Error: mocked' }],
102
+ isError: true,
103
+ });
104
+ mockPrefetchAllSources.mockResolvedValue([{ status: 'fulfilled', value: [] }]);
105
+ mockPrefetchEmbeddingModel.mockResolvedValue(undefined);
106
+ });
107
+ afterEach(() => {
108
+ vi.restoreAllMocks();
109
+ });
110
+ it('registers ListTools handler and returns tools from getToolList', async () => {
111
+ const tools = [{ name: 'tool-one' }, { name: 'tool-two' }];
112
+ mockGetToolList.mockReturnValue(tools);
113
+ await startServer();
114
+ const sourceManager = getLatestSourceManager();
115
+ const listToolsHandler = handlerMap.get(listToolsSchema);
116
+ expect(listToolsHandler).toBeDefined();
117
+ const response = await listToolsHandler?.();
118
+ expect(response).toEqual({ tools });
119
+ expect(mockGetToolList).toHaveBeenCalledWith(sourceManager, expect.any(Function));
120
+ });
121
+ it('routes CallTool requests to a registered handler', async () => {
122
+ const handlerResponse = { content: [{ type: 'text', text: 'ok' }] };
123
+ const handler = vi.fn().mockResolvedValue(handlerResponse);
124
+ mockGetHandler.mockReturnValue(handler);
125
+ await startServer();
126
+ const callToolHandler = handlerMap.get(callToolSchema);
127
+ const request = { params: { name: 'get_swift_pattern', arguments: { topic: 'swiftui' } } };
128
+ const response = await callToolHandler?.(request);
129
+ expect(handler).toHaveBeenCalledWith({ topic: 'swiftui' }, expect.objectContaining({ sourceManager: expect.any(Object), patreonSource: expect.any(Function) }));
130
+ expect(response).toEqual(handlerResponse);
131
+ });
132
+ it('returns standardized error response for unknown tools', async () => {
133
+ const errorResponse = { content: [{ type: 'text', text: 'Error: Unknown tool' }], isError: true };
134
+ mockCreateErrorResponseFromError.mockReturnValue(errorResponse);
135
+ await startServer();
136
+ const callToolHandler = handlerMap.get(callToolSchema);
137
+ const response = await callToolHandler?.({ params: { name: 'missing_tool', arguments: {} } });
138
+ expect(mockCreateErrorResponseFromError).toHaveBeenCalledWith(expect.any(Error));
139
+ expect(response).toEqual(errorResponse);
140
+ });
141
+ it('auto-enables Patreon when credentials are detected', async () => {
142
+ sourceManagerState.patreonConfigured = true;
143
+ sourceManagerState.enabledSources = [{ id: 'sundell' }];
144
+ await startServer();
145
+ const sourceManager = getLatestSourceManager();
146
+ expect(sourceManager.markSourceConfigured).toHaveBeenCalledWith('patreon');
147
+ expect(mockLoggerInfo).toHaveBeenCalledWith('Patreon auto-enabled (credentials detected)');
148
+ });
149
+ it('runs source and embedding prefetch hooks when enabled', async () => {
150
+ sourceManagerState.prefetchEnabled = true;
151
+ sourceManagerState.semanticEnabled = true;
152
+ await startServer();
153
+ await Promise.resolve();
154
+ expect(mockPrefetchAllSources).toHaveBeenCalledTimes(1);
155
+ expect(mockPrefetchEmbeddingModel).toHaveBeenCalledTimes(1);
156
+ });
157
+ it('logs fatal error and exits when connect fails', async () => {
158
+ mockConnect.mockRejectedValueOnce(new Error('connect failed'));
159
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code) => {
160
+ throw new Error(`process.exit called with ${code}`);
161
+ }));
162
+ await expect(startServer()).rejects.toThrow('process.exit called with 1');
163
+ expect(mockLoggerError).toHaveBeenCalled();
164
+ expect(exitSpy).toHaveBeenCalledWith(1);
165
+ });
166
+ });
167
+ //# sourceMappingURL=server.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.test.js","sourceRoot":"","sources":["../../src/__tests__/server.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAEzE,MAAM,eAAe,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClD,MAAM,cAAc,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACjD,MAAM,gCAAgC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACnE,MAAM,sBAAsB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACzD,MAAM,0BAA0B,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7D,MAAM,cAAc,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACjD,MAAM,cAAc,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACjD,MAAM,eAAe,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClD,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAE9C,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,EAA4C,CAAC,CAAC;AAEzF,MAAM,kBAAkB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC3C,cAAc,EAAE,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;IACnC,iBAAiB,EAAE,KAAK;IACxB,eAAe,EAAE,KAAK;IACtB,eAAe,EAAE,KAAK;CACvB,CAAC,CAAC,CAAC;AAEJ,MAAM,sBAAsB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAM9C,CAAC,CAAC;AAEJ,MAAM,eAAe,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC;AAC3E,MAAM,cAAc,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAEzE,MAAM,UAAU,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;IACjC,OAAO;QACL,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,MAAe,EAAE,OAAwC,EAAE,EAAE;YACtF,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,WAAW,CAAC;KACvB,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;IACxC,OAAO;QACL,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;QACnE,kBAAkB,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC7G,oBAAoB,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,iBAAiB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;QACpE,uBAAuB,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,kBAAkB,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;QAEzF;YACE,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1D,MAAM,EAAE,UAAU;CACnB,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE,CAAC,CAAC;IAC1D,oBAAoB,EAAE,MAAM,wBAAwB;KAAG;CACxD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE,CAAC,CAAC;IACnD,sBAAsB,EAAE,eAAe;IACvC,qBAAqB,EAAE,cAAc;CACtC,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,sBAAsB,EAAE,GAAG,EAAE,CAAC,CAAC;IACrC,OAAO,EAAE,iBAAiB;CAC3B,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,UAAU,EAAE,cAAc;CAC3B,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,WAAW,EAAE,eAAe;CAC7B,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7C,4BAA4B,EAAE,gCAAgC;CAC/D,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,kBAAkB,EAAE,sBAAsB;CAC3C,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,sBAAsB,EAAE,0BAA0B;CACnD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9C,aAAa,EAAE,MAAM,aAAa;KAAG;CACtC,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,OAAO,EAAE;QACP,IAAI,EAAE,cAAc;QACpB,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,eAAe;KACvB;CACF,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,SAAS,sBAAsB;IAC7B,MAAM,QAAQ,GAAG,sBAAsB,CAAC,sBAAsB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC3E,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IAClE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,sBAAsB,CAAC,MAAM,GAAG,CAAC,CAAC;QAElC,kBAAkB,CAAC,cAAc,GAAG,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QACxD,kBAAkB,CAAC,iBAAiB,GAAG,KAAK,CAAC;QAC7C,kBAAkB,CAAC,eAAe,GAAG,KAAK,CAAC;QAC3C,kBAAkB,CAAC,eAAe,GAAG,KAAK,CAAC;QAE3C,WAAW,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;QACzC,eAAe,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC;QACjE,cAAc,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAC1C,gCAAgC,CAAC,eAAe,CAAC;YAC/C,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;YAClD,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,sBAAsB,CAAC,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC/E,0BAA0B,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,eAAe,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,KAAK,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3D,eAAe,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAEvC,MAAM,WAAW,EAAE,CAAC;QAEpB,MAAM,aAAa,GAAG,sBAAsB,EAAE,CAAC;QAC/C,MAAM,gBAAgB,GAAG,UAAU,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QACzD,MAAM,CAAC,gBAAgB,CAAC,CAAC,WAAW,EAAE,CAAC;QAEvC,MAAM,QAAQ,GAAG,MAAM,gBAAgB,EAAE,EAAE,CAAC;QAE5C,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACpC,MAAM,CAAC,eAAe,CAAC,CAAC,oBAAoB,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,eAAe,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,eAAe,CAAC,CAAC;QAC3D,cAAc,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAExC,MAAM,WAAW,EAAE,CAAC;QAEpB,MAAM,eAAe,GAAG,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACvD,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;QAC3F,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC,OAAO,CAAC,CAAC;QAElD,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAClC,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,MAAM,CAAC,gBAAgB,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CACpG,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,MAAM,aAAa,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAClG,gCAAgC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;QAEhE,MAAM,WAAW,EAAE,CAAC;QAEpB,MAAM,eAAe,GAAG,UAAU,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,MAAM,eAAe,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAE9F,MAAM,CAAC,gCAAgC,CAAC,CAAC,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,kBAAkB,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC5C,kBAAkB,CAAC,cAAc,GAAG,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAExD,MAAM,WAAW,EAAE,CAAC;QAEpB,MAAM,aAAa,GAAG,sBAAsB,EAAE,CAAC;QAC/C,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC3E,MAAM,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,6CAA6C,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;QACrE,kBAAkB,CAAC,eAAe,GAAG,IAAI,CAAC;QAC1C,kBAAkB,CAAC,eAAe,GAAG,IAAI,CAAC;QAE1C,MAAM,WAAW,EAAE,CAAC;QACpB,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QAExB,MAAM,CAAC,sBAAsB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,0BAA0B,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,WAAW,CAAC,qBAAqB,CAAC,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAa,EAAE,EAAE;YAC9E,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC,CAAU,CAAC,CAAC;QAEb,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,4BAA4B,CAAC,CAAC;QAE1E,MAAM,CAAC,eAAe,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAC3C,MAAM,CAAC,OAAO,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.test.d.ts","sourceRoot":"","sources":["../../../src/cli/__tests__/setup.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { buildSnippet, getConfigPath, getServerCommand, parseOptions, shouldRunNonInteractive, } from '../setup-utils.js';
3
+ describe('setup-utils', () => {
4
+ it('returns npx latest command for on-demand install mode', () => {
5
+ expect(getServerCommand('npx')).toEqual({
6
+ command: 'npx',
7
+ args: ['-y', 'swift-patterns-mcp@latest'],
8
+ });
9
+ });
10
+ it('returns local install command and hint', () => {
11
+ expect(getServerCommand('local')).toEqual({
12
+ command: 'npx',
13
+ args: ['swift-patterns-mcp'],
14
+ installHint: 'npm install -D swift-patterns-mcp',
15
+ });
16
+ });
17
+ it('returns global install command and hint', () => {
18
+ expect(getServerCommand('global')).toEqual({
19
+ command: 'swift-patterns-mcp',
20
+ args: [],
21
+ installHint: 'npm install -g swift-patterns-mcp',
22
+ });
23
+ });
24
+ it('resolves client config paths for local and global scopes', () => {
25
+ expect(getConfigPath('cursor', 'local')).toBe('.cursor/mcp.json');
26
+ expect(getConfigPath('claude', 'local')).toBe('.mcp.json');
27
+ expect(getConfigPath('windsurf', 'local')).toBe('.windsurf/mcp.json');
28
+ expect(getConfigPath('vscode', 'local')).toBe('.vscode/mcp.json');
29
+ expect(getConfigPath('cursor', 'global')).toBe('~/.cursor/mcp.json');
30
+ expect(getConfigPath('claude', 'global')).toContain('claude mcp add');
31
+ expect(getConfigPath('windsurf', 'global')).toContain('Windsurf');
32
+ expect(getConfigPath('vscode', 'global')).toContain('VS Code');
33
+ });
34
+ it('builds VS Code snippet with mcp.servers shape', () => {
35
+ const snippet = JSON.parse(buildSnippet('vscode', 'npx', ['-y', 'swift-patterns-mcp@latest']));
36
+ expect(snippet).toEqual({
37
+ mcp: {
38
+ servers: {
39
+ 'swift-patterns': {
40
+ command: 'npx',
41
+ args: ['-y', 'swift-patterns-mcp@latest'],
42
+ },
43
+ },
44
+ },
45
+ });
46
+ });
47
+ it('builds non-VS Code snippet with mcpServers shape', () => {
48
+ const snippet = JSON.parse(buildSnippet('cursor', 'npx', ['-y', 'swift-patterns-mcp@latest']));
49
+ expect(snippet).toEqual({
50
+ mcpServers: {
51
+ 'swift-patterns': {
52
+ command: 'npx',
53
+ args: ['-y', 'swift-patterns-mcp@latest'],
54
+ },
55
+ },
56
+ });
57
+ });
58
+ it('parses --all and scope flags correctly', () => {
59
+ expect(parseOptions(['--all', '--global'])).toEqual({
60
+ clients: ['cursor', 'claude', 'windsurf', 'vscode'],
61
+ scope: 'global',
62
+ });
63
+ expect(parseOptions(['--all', '-l'])).toEqual({
64
+ clients: ['cursor', 'claude', 'windsurf', 'vscode'],
65
+ scope: 'local',
66
+ });
67
+ });
68
+ it('parses selected client flags without --all', () => {
69
+ expect(parseOptions(['--cursor', '--vscode'])).toEqual({
70
+ clients: ['cursor', 'vscode'],
71
+ scope: undefined,
72
+ });
73
+ expect(parseOptions(['--claude', '--windsurf', '-g'])).toEqual({
74
+ clients: ['claude', 'windsurf'],
75
+ scope: 'global',
76
+ });
77
+ });
78
+ it('detects non-interactive mode when clients or scope are provided', () => {
79
+ expect(shouldRunNonInteractive({ clients: ['cursor'] })).toBe(true);
80
+ expect(shouldRunNonInteractive({ scope: 'global' })).toBe(true);
81
+ expect(shouldRunNonInteractive({ clients: ['cursor'], scope: 'local' })).toBe(true);
82
+ expect(shouldRunNonInteractive({})).toBe(false);
83
+ });
84
+ });
85
+ //# sourceMappingURL=setup.test.js.map