@deimoscloud/coreai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/.prettierrc +9 -0
  2. package/AGENT_SPEC.md +347 -0
  3. package/ARCHITECTURE.md +547 -0
  4. package/DRAFT_PRD.md +1440 -0
  5. package/IMPLEMENTATION_PLAN.md +256 -0
  6. package/PRODUCT.md +473 -0
  7. package/README.md +303 -0
  8. package/WORKFLOWS.md +295 -0
  9. package/agents/_templates/ic-engineer.md +185 -0
  10. package/agents/_templates/reviewer.md +182 -0
  11. package/agents/backend-engineer.yaml +72 -0
  12. package/agents/devops-engineer.yaml +72 -0
  13. package/agents/engineering-manager.yaml +70 -0
  14. package/agents/examples/android-engineer.md +302 -0
  15. package/agents/examples/backend-engineer.md +320 -0
  16. package/agents/examples/devops-engineer.md +742 -0
  17. package/agents/examples/engineering-manager.md +469 -0
  18. package/agents/examples/frontend-engineer.md +58 -0
  19. package/agents/examples/product-manager.md +315 -0
  20. package/agents/examples/qa-engineer.md +371 -0
  21. package/agents/examples/security-engineer.md +525 -0
  22. package/agents/examples/solutions-architect.md +351 -0
  23. package/agents/examples/wearos-engineer.md +359 -0
  24. package/agents/frontend-engineer.yaml +72 -0
  25. package/commands/core/check-inbox.md +34 -0
  26. package/commands/core/delegate.md +30 -0
  27. package/commands/core/git-commit.md +144 -0
  28. package/commands/core/pr-create.md +193 -0
  29. package/commands/core/review.md +56 -0
  30. package/commands/core/sprint-status.md +65 -0
  31. package/commands/optional/docs-update.md +200 -0
  32. package/commands/optional/jira-create.md +200 -0
  33. package/commands/optional/jira-transition.md +184 -0
  34. package/commands/optional/worktree-cleanup.md +167 -0
  35. package/commands/optional/worktree-setup.md +110 -0
  36. package/dist/cli/index.js +4037 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/index.d.ts +2978 -0
  39. package/dist/index.js +3867 -0
  40. package/dist/index.js.map +1 -0
  41. package/eslint.config.js +29 -0
  42. package/jest.config.js +22 -0
  43. package/knowledge-library/README.md +118 -0
  44. package/knowledge-library/android-engineer/context/current.txt +42 -0
  45. package/knowledge-library/android-engineer/control/decisions.txt +9 -0
  46. package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
  47. package/knowledge-library/android-engineer/control/objectives.txt +26 -0
  48. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  49. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  50. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  51. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  52. package/knowledge-library/architecture.txt +61 -0
  53. package/knowledge-library/backend-engineer/context/current.txt +42 -0
  54. package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
  55. package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
  56. package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
  57. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  58. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  59. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  60. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  61. package/knowledge-library/context.txt +52 -0
  62. package/knowledge-library/devops-engineer/context/current.txt +42 -0
  63. package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
  64. package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
  65. package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
  66. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  67. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  68. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  69. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  70. package/knowledge-library/engineering-manager/context/current.txt +40 -0
  71. package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
  72. package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
  73. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  74. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  76. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  77. package/knowledge-library/prd.txt +81 -0
  78. package/knowledge-library/product-manager/context/current.txt +42 -0
  79. package/knowledge-library/product-manager/control/decisions.txt +9 -0
  80. package/knowledge-library/product-manager/control/dependencies.txt +19 -0
  81. package/knowledge-library/product-manager/control/objectives.txt +26 -0
  82. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  83. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  84. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  85. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  86. package/knowledge-library/qa-engineer/context/current.txt +42 -0
  87. package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
  88. package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
  89. package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
  90. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  91. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  92. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  93. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  94. package/knowledge-library/security-engineer/context/current.txt +42 -0
  95. package/knowledge-library/security-engineer/control/decisions.txt +9 -0
  96. package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
  97. package/knowledge-library/security-engineer/control/objectives.txt +26 -0
  98. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  99. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  100. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  101. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  102. package/knowledge-library/solutions-architect/context/current.txt +42 -0
  103. package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
  104. package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
  105. package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
  106. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  107. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  108. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  109. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  110. package/knowledge-library/wearos-engineer/context/current.txt +42 -0
  111. package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
  112. package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
  113. package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
  114. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  115. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  116. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  117. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  118. package/package.json +66 -0
  119. package/schemas/agent.schema.json +171 -0
  120. package/schemas/coreai.config.schema.json +257 -0
  121. package/scripts/add-agent.sh +323 -0
  122. package/scripts/install.sh +354 -0
  123. package/src/adapters/factory.test.ts +386 -0
  124. package/src/adapters/factory.ts +305 -0
  125. package/src/adapters/index.ts +113 -0
  126. package/src/adapters/interfaces.ts +268 -0
  127. package/src/adapters/mcp/client.test.ts +130 -0
  128. package/src/adapters/mcp/client.ts +451 -0
  129. package/src/adapters/mcp/discovery.test.ts +315 -0
  130. package/src/adapters/mcp/discovery.ts +340 -0
  131. package/src/adapters/mcp/index.ts +66 -0
  132. package/src/adapters/mcp/mapper.test.ts +218 -0
  133. package/src/adapters/mcp/mapper.ts +536 -0
  134. package/src/adapters/mcp/registry.test.ts +433 -0
  135. package/src/adapters/mcp/registry.ts +550 -0
  136. package/src/adapters/mcp/types.ts +258 -0
  137. package/src/adapters/native/filesystem.test.ts +350 -0
  138. package/src/adapters/native/filesystem.ts +393 -0
  139. package/src/adapters/native/github.test.ts +173 -0
  140. package/src/adapters/native/github.ts +627 -0
  141. package/src/adapters/native/index.ts +22 -0
  142. package/src/adapters/native/selector.test.ts +224 -0
  143. package/src/adapters/native/selector.ts +150 -0
  144. package/src/adapters/types.ts +270 -0
  145. package/src/agents/compiler.test.ts +399 -0
  146. package/src/agents/compiler.ts +359 -0
  147. package/src/agents/index.ts +36 -0
  148. package/src/agents/loader.test.ts +319 -0
  149. package/src/agents/loader.ts +143 -0
  150. package/src/agents/resolver.test.ts +282 -0
  151. package/src/agents/resolver.ts +262 -0
  152. package/src/agents/types.ts +87 -0
  153. package/src/cache/index.ts +38 -0
  154. package/src/cache/interfaces.ts +283 -0
  155. package/src/cache/manager.test.ts +266 -0
  156. package/src/cache/manager.ts +388 -0
  157. package/src/cache/provider.test.ts +485 -0
  158. package/src/cache/provider.ts +745 -0
  159. package/src/cache/types.test.ts +192 -0
  160. package/src/cache/types.ts +313 -0
  161. package/src/cli/commands/build.test.ts +248 -0
  162. package/src/cli/commands/build.ts +244 -0
  163. package/src/cli/commands/cache.test.ts +221 -0
  164. package/src/cli/commands/cache.ts +229 -0
  165. package/src/cli/commands/index.ts +63 -0
  166. package/src/cli/commands/init.test.ts +173 -0
  167. package/src/cli/commands/init.ts +296 -0
  168. package/src/cli/commands/skills.test.ts +272 -0
  169. package/src/cli/commands/skills.ts +348 -0
  170. package/src/cli/commands/status.test.ts +392 -0
  171. package/src/cli/commands/status.ts +332 -0
  172. package/src/cli/commands/sync.test.ts +213 -0
  173. package/src/cli/commands/sync.ts +251 -0
  174. package/src/cli/commands/validate.test.ts +216 -0
  175. package/src/cli/commands/validate.ts +340 -0
  176. package/src/cli/index.test.ts +190 -0
  177. package/src/cli/index.ts +493 -0
  178. package/src/commands/context.test.ts +163 -0
  179. package/src/commands/context.ts +111 -0
  180. package/src/commands/index.ts +56 -0
  181. package/src/commands/loader.test.ts +273 -0
  182. package/src/commands/loader.ts +355 -0
  183. package/src/commands/registry.test.ts +384 -0
  184. package/src/commands/registry.ts +248 -0
  185. package/src/commands/runner.test.ts +297 -0
  186. package/src/commands/runner.ts +222 -0
  187. package/src/commands/types.ts +361 -0
  188. package/src/config/index.ts +19 -0
  189. package/src/config/loader.test.ts +262 -0
  190. package/src/config/loader.ts +188 -0
  191. package/src/config/types.ts +154 -0
  192. package/src/context/index.ts +14 -0
  193. package/src/context/loader.test.ts +334 -0
  194. package/src/context/loader.ts +357 -0
  195. package/src/index.test.ts +13 -0
  196. package/src/index.ts +244 -0
  197. package/src/knowledge-library/index.ts +44 -0
  198. package/src/knowledge-library/manager.test.ts +536 -0
  199. package/src/knowledge-library/manager.ts +804 -0
  200. package/src/knowledge-library/types.ts +432 -0
  201. package/src/skills/generator.test.ts +602 -0
  202. package/src/skills/generator.ts +491 -0
  203. package/src/skills/index.ts +27 -0
  204. package/src/skills/templates.ts +520 -0
  205. package/src/skills/types.ts +251 -0
  206. package/templates/completion-report.md +72 -0
  207. package/templates/feedback.md +56 -0
  208. package/templates/project-files/CLAUDE.md.template +109 -0
  209. package/templates/project-files/coreai.json.example +47 -0
  210. package/templates/project-files/mcp.json.template +20 -0
  211. package/templates/review-complete.md +64 -0
  212. package/templates/review-request.md +67 -0
  213. package/templates/task-assignment.md +51 -0
  214. package/tsconfig.build.json +4 -0
  215. package/tsconfig.json +26 -0
  216. package/tsup.config.ts +23 -0
@@ -0,0 +1,433 @@
1
+ import { McpRegistry, createRegistry } from './registry.js';
2
+ import type { McpServerDefinition } from './types.js';
3
+ import { McpError } from './types.js';
4
+
5
+ describe('McpRegistry', () => {
6
+ let registry: McpRegistry;
7
+
8
+ beforeEach(() => {
9
+ registry = createRegistry({
10
+ maxRetries: 1,
11
+ retryDelay: 10,
12
+ });
13
+ });
14
+
15
+ afterEach(async () => {
16
+ await registry.disconnectAll();
17
+ });
18
+
19
+ describe('constructor', () => {
20
+ it('should create a registry with default options', () => {
21
+ const reg = new McpRegistry();
22
+ expect(reg).toBeInstanceOf(McpRegistry);
23
+ expect(reg.getServerNames()).toEqual([]);
24
+ });
25
+
26
+ it('should accept custom options', () => {
27
+ const reg = new McpRegistry({
28
+ autoConnect: true,
29
+ maxRetries: 5,
30
+ retryDelay: 2000,
31
+ });
32
+ expect(reg).toBeInstanceOf(McpRegistry);
33
+ });
34
+ });
35
+
36
+ describe('registerServer', () => {
37
+ it('should register a server definition', () => {
38
+ const definition: McpServerDefinition = {
39
+ name: 'test-server',
40
+ config: { transport: 'stdio', command: 'test' },
41
+ enabled: true,
42
+ };
43
+
44
+ registry.registerServer(definition);
45
+
46
+ expect(registry.getServerNames()).toContain('test-server');
47
+ const entry = registry.getServer('test-server');
48
+ expect(entry).toBeDefined();
49
+ expect(entry?.status).toBe('disconnected');
50
+ });
51
+
52
+ it('should set disabled status if enabled is false', () => {
53
+ const definition: McpServerDefinition = {
54
+ name: 'disabled-server',
55
+ config: { transport: 'stdio', command: 'test' },
56
+ enabled: false,
57
+ };
58
+
59
+ registry.registerServer(definition);
60
+
61
+ const entry = registry.getServer('disabled-server');
62
+ expect(entry?.status).toBe('disabled');
63
+ });
64
+
65
+ it('should throw if server already registered', () => {
66
+ const definition: McpServerDefinition = {
67
+ name: 'test-server',
68
+ config: { transport: 'stdio', command: 'test' },
69
+ };
70
+
71
+ registry.registerServer(definition);
72
+
73
+ expect(() => registry.registerServer(definition)).toThrow(McpError);
74
+ });
75
+
76
+ it('should emit serversChanged event', () => {
77
+ let emittedServers: string[] = [];
78
+ registry.on('serversChanged', (servers) => {
79
+ emittedServers = servers;
80
+ });
81
+
82
+ registry.registerServer({
83
+ name: 'test-server',
84
+ config: { transport: 'stdio', command: 'test' },
85
+ });
86
+
87
+ expect(emittedServers).toEqual(['test-server']);
88
+ });
89
+ });
90
+
91
+ describe('unregisterServer', () => {
92
+ it('should remove a registered server', async () => {
93
+ registry.registerServer({
94
+ name: 'test-server',
95
+ config: { transport: 'stdio', command: 'test' },
96
+ });
97
+
98
+ await registry.unregisterServer('test-server');
99
+
100
+ expect(registry.getServerNames()).not.toContain('test-server');
101
+ });
102
+
103
+ it('should do nothing if server not found', async () => {
104
+ await expect(registry.unregisterServer('nonexistent')).resolves.toBeUndefined();
105
+ });
106
+
107
+ it('should emit serversChanged event', async () => {
108
+ registry.registerServer({
109
+ name: 'test-server',
110
+ config: { transport: 'stdio', command: 'test' },
111
+ });
112
+
113
+ let eventEmitted = false;
114
+ registry.on('serversChanged', () => {
115
+ eventEmitted = true;
116
+ });
117
+
118
+ await registry.unregisterServer('test-server');
119
+
120
+ expect(eventEmitted).toBe(true);
121
+ });
122
+ });
123
+
124
+ describe('connect error handling', () => {
125
+ it('should throw if server not registered', async () => {
126
+ await expect(registry.connect('nonexistent')).rejects.toThrow(McpError);
127
+ });
128
+
129
+ it('should throw if server is disabled', async () => {
130
+ registry.registerServer({
131
+ name: 'disabled-server',
132
+ config: { transport: 'stdio', command: 'test' },
133
+ enabled: false,
134
+ });
135
+
136
+ await expect(registry.connect('disabled-server')).rejects.toThrow(McpError);
137
+ });
138
+ });
139
+
140
+ describe('getClient', () => {
141
+ it('should return null for disconnected server', () => {
142
+ registry.registerServer({
143
+ name: 'test-server',
144
+ config: { transport: 'stdio', command: 'test' },
145
+ });
146
+
147
+ const client = registry.getClient('test-server');
148
+ expect(client).toBeNull();
149
+ });
150
+
151
+ it('should return null for unknown server', () => {
152
+ const client = registry.getClient('nonexistent');
153
+ expect(client).toBeNull();
154
+ });
155
+ });
156
+
157
+ describe('getConnectedServers', () => {
158
+ it('should return empty array when no servers connected', () => {
159
+ registry.registerServer({
160
+ name: 'disconnected',
161
+ config: { transport: 'stdio', command: 'test' },
162
+ });
163
+
164
+ const connected = registry.getConnectedServers();
165
+ expect(connected).toEqual([]);
166
+ });
167
+ });
168
+
169
+ describe('getAllTools', () => {
170
+ it('should return empty map when no servers connected', () => {
171
+ registry.registerServer({
172
+ name: 'server-1',
173
+ config: { transport: 'stdio', command: 'test' },
174
+ });
175
+
176
+ const allTools = registry.getAllTools();
177
+ expect(allTools.size).toBe(0);
178
+ });
179
+ });
180
+
181
+ describe('findServersForAdapter', () => {
182
+ it('should find servers that provide a specific adapter type', () => {
183
+ registry.registerServer({
184
+ name: 'github-server',
185
+ config: { transport: 'stdio', command: 'github' },
186
+ });
187
+
188
+ // Manually set up the entry as if it were connected
189
+ const entry = registry.getServer('github-server');
190
+ if (entry) {
191
+ entry.status = 'connected';
192
+ entry.mappings = [
193
+ { toolName: 'create_pr', adapterType: 'git', method: 'createPullRequest' },
194
+ ];
195
+ }
196
+
197
+ const servers = registry.findServersForAdapter('git');
198
+ expect(servers).toContain('github-server');
199
+ });
200
+
201
+ it('should return empty array if no servers match', () => {
202
+ const servers = registry.findServersForAdapter('documentation');
203
+ expect(servers).toEqual([]);
204
+ });
205
+ });
206
+
207
+ describe('getStatus', () => {
208
+ it('should return registry status summary', () => {
209
+ registry.registerServer({
210
+ name: 'test-server',
211
+ config: { transport: 'stdio', command: 'test' },
212
+ });
213
+
214
+ const status = registry.getStatus();
215
+
216
+ expect(status.totalServers).toBe(1);
217
+ expect(status.connectedServers).toBe(0);
218
+ expect(status.servers['test-server']).toBeDefined();
219
+ expect(status.servers['test-server'].status).toBe('disconnected');
220
+ });
221
+
222
+ it('should include lastError when present', () => {
223
+ registry.registerServer({
224
+ name: 'error-server',
225
+ config: { transport: 'stdio', command: 'test' },
226
+ });
227
+
228
+ // Manually set an error
229
+ const entry = registry.getServer('error-server');
230
+ if (entry) {
231
+ entry.status = 'error';
232
+ entry.lastError = new Error('Connection failed');
233
+ }
234
+
235
+ const status = registry.getStatus();
236
+ expect(status.servers['error-server'].lastError).toBe('Connection failed');
237
+ });
238
+
239
+ it('should not include lastError when not present', () => {
240
+ registry.registerServer({
241
+ name: 'test-server',
242
+ config: { transport: 'stdio', command: 'test' },
243
+ });
244
+
245
+ const status = registry.getStatus();
246
+ expect(status.servers['test-server'].lastError).toBeUndefined();
247
+ });
248
+ });
249
+
250
+ describe('callTool error handling', () => {
251
+ it('should throw if server not registered', async () => {
252
+ await expect(registry.callTool('nonexistent', 'tool')).rejects.toThrow(McpError);
253
+ });
254
+
255
+ it('should throw if server not connected', async () => {
256
+ registry.registerServer({
257
+ name: 'test-server',
258
+ config: { transport: 'stdio', command: 'test' },
259
+ });
260
+
261
+ await expect(registry.callTool('test-server', 'tool')).rejects.toThrow(McpError);
262
+ });
263
+ });
264
+
265
+ describe('getMappings', () => {
266
+ it('should return empty array for disconnected server', () => {
267
+ registry.registerServer({
268
+ name: 'test-server',
269
+ config: { transport: 'stdio', command: 'test' },
270
+ });
271
+
272
+ const mappings = registry.getMappings('test-server');
273
+ expect(mappings).toEqual([]);
274
+ });
275
+
276
+ it('should return empty array for unknown server', () => {
277
+ const mappings = registry.getMappings('nonexistent');
278
+ expect(mappings).toEqual([]);
279
+ });
280
+ });
281
+
282
+ describe('getTools', () => {
283
+ it('should return empty array for disconnected server', () => {
284
+ registry.registerServer({
285
+ name: 'test-server',
286
+ config: { transport: 'stdio', command: 'test' },
287
+ });
288
+
289
+ const tools = registry.getTools('test-server');
290
+ expect(tools).toEqual([]);
291
+ });
292
+
293
+ it('should return empty array for unknown server', () => {
294
+ const tools = registry.getTools('nonexistent');
295
+ expect(tools).toEqual([]);
296
+ });
297
+ });
298
+ });
299
+
300
+ describe('createRegistry', () => {
301
+ it('should create a registry instance', () => {
302
+ const registry = createRegistry();
303
+ expect(registry).toBeInstanceOf(McpRegistry);
304
+ });
305
+
306
+ it('should accept options', () => {
307
+ const registry = createRegistry({ maxRetries: 5 });
308
+ expect(registry).toBeInstanceOf(McpRegistry);
309
+ });
310
+ });
311
+
312
+ describe('McpRegistry with simulated connections', () => {
313
+ let registry: McpRegistry;
314
+
315
+ beforeEach(() => {
316
+ registry = createRegistry();
317
+ });
318
+
319
+ afterEach(async () => {
320
+ await registry.disconnectAll();
321
+ });
322
+
323
+ it('should track multiple servers', () => {
324
+ registry.registerServer({
325
+ name: 'server-1',
326
+ config: { transport: 'stdio', command: 'test1' },
327
+ });
328
+ registry.registerServer({
329
+ name: 'server-2',
330
+ config: { transport: 'stdio', command: 'test2' },
331
+ });
332
+ registry.registerServer({
333
+ name: 'server-3',
334
+ config: { transport: 'stdio', command: 'test3' },
335
+ enabled: false,
336
+ });
337
+
338
+ expect(registry.getServerNames()).toHaveLength(3);
339
+
340
+ const allServers = registry.getAllServers();
341
+ expect(allServers.size).toBe(3);
342
+ expect(allServers.get('server-3')?.status).toBe('disabled');
343
+ });
344
+
345
+ it('should emit events on server registration', () => {
346
+ let eventCount = 0;
347
+ registry.on('serversChanged', () => {
348
+ eventCount++;
349
+ });
350
+
351
+ registry.registerServer({
352
+ name: 'server-1',
353
+ config: { transport: 'stdio', command: 'test1' },
354
+ });
355
+
356
+ registry.registerServer({
357
+ name: 'server-2',
358
+ config: { transport: 'stdio', command: 'test2' },
359
+ });
360
+
361
+ expect(eventCount).toBe(2);
362
+ });
363
+
364
+ it('should return mappings for manually configured server', () => {
365
+ registry.registerServer({
366
+ name: 'jira-server',
367
+ config: { transport: 'stdio', command: 'jira' },
368
+ });
369
+
370
+ const entry = registry.getServer('jira-server');
371
+ if (entry) {
372
+ entry.status = 'connected';
373
+ entry.mappings = [
374
+ { toolName: 'get_issue', adapterType: 'issue_tracker', method: 'getIssue' },
375
+ { toolName: 'create_issue', adapterType: 'issue_tracker', method: 'createIssue' },
376
+ ];
377
+ }
378
+
379
+ const mappings = registry.getMappings('jira-server');
380
+ expect(mappings).toHaveLength(2);
381
+ expect(mappings[0].adapterType).toBe('issue_tracker');
382
+ });
383
+
384
+ it('should group mappings by adapter type across servers', () => {
385
+ registry.registerServer({
386
+ name: 'github-server',
387
+ config: { transport: 'stdio', command: 'github' },
388
+ });
389
+ registry.registerServer({
390
+ name: 'jira-server',
391
+ config: { transport: 'stdio', command: 'jira' },
392
+ });
393
+
394
+ const githubEntry = registry.getServer('github-server');
395
+ if (githubEntry) {
396
+ githubEntry.status = 'connected';
397
+ githubEntry.mappings = [
398
+ { toolName: 'create_pr', adapterType: 'git', method: 'createPullRequest' },
399
+ { toolName: 'get_issue', adapterType: 'issue_tracker', method: 'getIssue' },
400
+ ];
401
+ }
402
+
403
+ const jiraEntry = registry.getServer('jira-server');
404
+ if (jiraEntry) {
405
+ jiraEntry.status = 'connected';
406
+ jiraEntry.mappings = [
407
+ { toolName: 'jira_get_issue', adapterType: 'issue_tracker', method: 'getIssue' },
408
+ ];
409
+ }
410
+
411
+ const grouped = registry.getAllMappingsByAdapter();
412
+
413
+ expect(grouped.has('git')).toBe(true);
414
+ expect(grouped.has('issue_tracker')).toBe(true);
415
+
416
+ const gitMappings = grouped.get('git');
417
+ expect(gitMappings?.has('github-server')).toBe(true);
418
+
419
+ const issueTrackerMappings = grouped.get('issue_tracker');
420
+ expect(issueTrackerMappings?.has('github-server')).toBe(true);
421
+ expect(issueTrackerMappings?.has('jira-server')).toBe(true);
422
+ });
423
+
424
+ it('should handle getAllMappingsByAdapter with no connected servers', () => {
425
+ registry.registerServer({
426
+ name: 'server-1',
427
+ config: { transport: 'stdio', command: 'test1' },
428
+ });
429
+
430
+ const grouped = registry.getAllMappingsByAdapter();
431
+ expect(grouped.size).toBe(0);
432
+ });
433
+ });