@deimoscloud/coreai 0.1.8 → 0.1.10

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 (196) hide show
  1. package/dist/cli/index.js +5 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.js.map +1 -1
  5. package/package.json +6 -1
  6. package/.prettierrc +0 -9
  7. package/AGENT_SPEC.md +0 -347
  8. package/ARCHITECTURE.md +0 -547
  9. package/DRAFT_PRD.md +0 -1440
  10. package/IMPLEMENTATION_PLAN.md +0 -256
  11. package/PRODUCT.md +0 -473
  12. package/WORKFLOWS.md +0 -295
  13. package/commands/core/check-inbox.md +0 -34
  14. package/commands/core/delegate.md +0 -30
  15. package/commands/core/git-commit.md +0 -144
  16. package/commands/core/pr-create.md +0 -193
  17. package/commands/core/review.md +0 -56
  18. package/commands/core/sprint-status.md +0 -65
  19. package/commands/optional/docs-update.md +0 -200
  20. package/commands/optional/jira-create.md +0 -200
  21. package/commands/optional/jira-transition.md +0 -184
  22. package/commands/optional/worktree-cleanup.md +0 -167
  23. package/commands/optional/worktree-setup.md +0 -110
  24. package/eslint.config.js +0 -29
  25. package/jest.config.js +0 -22
  26. package/knowledge-library/README.md +0 -118
  27. package/knowledge-library/android-engineer/context/current.txt +0 -42
  28. package/knowledge-library/android-engineer/control/decisions.txt +0 -9
  29. package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
  30. package/knowledge-library/android-engineer/control/objectives.txt +0 -26
  31. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  32. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  33. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  34. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  35. package/knowledge-library/architecture.txt +0 -61
  36. package/knowledge-library/backend-engineer/context/current.txt +0 -42
  37. package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
  38. package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
  39. package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
  40. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  41. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  42. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  43. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  44. package/knowledge-library/context.txt +0 -52
  45. package/knowledge-library/devops-engineer/context/current.txt +0 -42
  46. package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
  47. package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
  48. package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
  49. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  50. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  51. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  52. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  53. package/knowledge-library/engineering-manager/context/current.txt +0 -40
  54. package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
  55. package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
  56. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  57. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  58. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  59. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  60. package/knowledge-library/prd.txt +0 -81
  61. package/knowledge-library/product-manager/context/current.txt +0 -42
  62. package/knowledge-library/product-manager/control/decisions.txt +0 -9
  63. package/knowledge-library/product-manager/control/dependencies.txt +0 -19
  64. package/knowledge-library/product-manager/control/objectives.txt +0 -26
  65. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  66. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  67. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  68. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  69. package/knowledge-library/qa-engineer/context/current.txt +0 -42
  70. package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
  71. package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
  72. package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
  73. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  74. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  76. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  77. package/knowledge-library/security-engineer/context/current.txt +0 -42
  78. package/knowledge-library/security-engineer/control/decisions.txt +0 -9
  79. package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
  80. package/knowledge-library/security-engineer/control/objectives.txt +0 -26
  81. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  82. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  83. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  84. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  85. package/knowledge-library/solutions-architect/context/current.txt +0 -42
  86. package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
  87. package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
  88. package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
  89. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  90. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  91. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  92. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  93. package/knowledge-library/wearos-engineer/context/current.txt +0 -42
  94. package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
  95. package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
  96. package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
  97. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  98. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  99. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  100. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  101. package/scripts/add-agent.sh +0 -323
  102. package/scripts/install.sh +0 -354
  103. package/src/adapters/factory.test.ts +0 -386
  104. package/src/adapters/factory.ts +0 -305
  105. package/src/adapters/index.ts +0 -113
  106. package/src/adapters/interfaces.ts +0 -268
  107. package/src/adapters/mcp/client.test.ts +0 -130
  108. package/src/adapters/mcp/client.ts +0 -451
  109. package/src/adapters/mcp/discovery.test.ts +0 -315
  110. package/src/adapters/mcp/discovery.ts +0 -340
  111. package/src/adapters/mcp/index.ts +0 -66
  112. package/src/adapters/mcp/mapper.test.ts +0 -218
  113. package/src/adapters/mcp/mapper.ts +0 -536
  114. package/src/adapters/mcp/registry.test.ts +0 -433
  115. package/src/adapters/mcp/registry.ts +0 -550
  116. package/src/adapters/mcp/types.ts +0 -258
  117. package/src/adapters/native/filesystem.test.ts +0 -350
  118. package/src/adapters/native/filesystem.ts +0 -393
  119. package/src/adapters/native/github.test.ts +0 -173
  120. package/src/adapters/native/github.ts +0 -627
  121. package/src/adapters/native/index.ts +0 -22
  122. package/src/adapters/native/selector.test.ts +0 -224
  123. package/src/adapters/native/selector.ts +0 -150
  124. package/src/adapters/types.ts +0 -270
  125. package/src/agents/compiler.test.ts +0 -399
  126. package/src/agents/compiler.ts +0 -422
  127. package/src/agents/index.ts +0 -37
  128. package/src/agents/loader.test.ts +0 -319
  129. package/src/agents/loader.ts +0 -143
  130. package/src/agents/resolver.test.ts +0 -282
  131. package/src/agents/resolver.ts +0 -262
  132. package/src/agents/types.ts +0 -97
  133. package/src/cache/index.ts +0 -38
  134. package/src/cache/interfaces.ts +0 -283
  135. package/src/cache/manager.test.ts +0 -266
  136. package/src/cache/manager.ts +0 -388
  137. package/src/cache/provider.test.ts +0 -485
  138. package/src/cache/provider.ts +0 -745
  139. package/src/cache/types.test.ts +0 -192
  140. package/src/cache/types.ts +0 -313
  141. package/src/cli/commands/build.test.ts +0 -248
  142. package/src/cli/commands/build.ts +0 -284
  143. package/src/cli/commands/cache.test.ts +0 -221
  144. package/src/cli/commands/cache.ts +0 -229
  145. package/src/cli/commands/index.ts +0 -63
  146. package/src/cli/commands/init.test.ts +0 -173
  147. package/src/cli/commands/init.ts +0 -296
  148. package/src/cli/commands/skills.test.ts +0 -272
  149. package/src/cli/commands/skills.ts +0 -348
  150. package/src/cli/commands/status.test.ts +0 -392
  151. package/src/cli/commands/status.ts +0 -332
  152. package/src/cli/commands/sync.test.ts +0 -213
  153. package/src/cli/commands/sync.ts +0 -251
  154. package/src/cli/commands/validate.test.ts +0 -216
  155. package/src/cli/commands/validate.ts +0 -340
  156. package/src/cli/index.test.ts +0 -190
  157. package/src/cli/index.ts +0 -493
  158. package/src/commands/context.test.ts +0 -163
  159. package/src/commands/context.ts +0 -111
  160. package/src/commands/index.ts +0 -56
  161. package/src/commands/loader.test.ts +0 -273
  162. package/src/commands/loader.ts +0 -355
  163. package/src/commands/registry.test.ts +0 -384
  164. package/src/commands/registry.ts +0 -248
  165. package/src/commands/runner.test.ts +0 -297
  166. package/src/commands/runner.ts +0 -222
  167. package/src/commands/types.ts +0 -361
  168. package/src/config/index.ts +0 -19
  169. package/src/config/loader.test.ts +0 -262
  170. package/src/config/loader.ts +0 -188
  171. package/src/config/types.ts +0 -154
  172. package/src/context/index.ts +0 -14
  173. package/src/context/loader.test.ts +0 -334
  174. package/src/context/loader.ts +0 -357
  175. package/src/index.test.ts +0 -13
  176. package/src/index.ts +0 -268
  177. package/src/knowledge-library/index.ts +0 -44
  178. package/src/knowledge-library/manager.test.ts +0 -536
  179. package/src/knowledge-library/manager.ts +0 -804
  180. package/src/knowledge-library/types.ts +0 -432
  181. package/src/skills/generator.test.ts +0 -602
  182. package/src/skills/generator.ts +0 -491
  183. package/src/skills/index.ts +0 -27
  184. package/src/skills/templates.ts +0 -520
  185. package/src/skills/types.ts +0 -251
  186. package/templates/completion-report.md +0 -72
  187. package/templates/feedback.md +0 -56
  188. package/templates/project-files/CLAUDE.md.template +0 -109
  189. package/templates/project-files/coreai.json.example +0 -47
  190. package/templates/project-files/mcp.json.template +0 -20
  191. package/templates/review-complete.md +0 -64
  192. package/templates/review-request.md +0 -67
  193. package/templates/task-assignment.md +0 -51
  194. package/tsconfig.build.json +0 -4
  195. package/tsconfig.json +0 -26
  196. package/tsup.config.ts +0 -23
@@ -1,319 +0,0 @@
1
- import { mkdtempSync, writeFileSync, rmSync } from 'fs';
2
- import { join, dirname } from 'path';
3
- import { tmpdir } from 'os';
4
- import { fileURLToPath } from 'url';
5
- import { jest } from '@jest/globals';
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = dirname(__filename);
9
- import {
10
- parseAgentYaml,
11
- validateAgentDefinition,
12
- loadAgentFromFile,
13
- loadAgentsFromDirectory,
14
- getRoleFromFilename,
15
- AgentError,
16
- } from './loader.js';
17
- import type { AgentDefinition } from './types.js';
18
-
19
- /**
20
- * Helper to check AgentError with specific code
21
- */
22
- function expectAgentError(fn: () => unknown, code: string): void {
23
- try {
24
- fn();
25
- fail('Expected AgentError to be thrown');
26
- } catch (error) {
27
- expect(error).toBeInstanceOf(AgentError);
28
- expect((error as AgentError).code).toBe(code);
29
- }
30
- }
31
-
32
- /**
33
- * Create a valid minimal agent definition
34
- */
35
- function createMinimalAgent(overrides: Partial<AgentDefinition> = {}): AgentDefinition {
36
- return {
37
- role: 'test-agent',
38
- type: 'ic-engineer',
39
- display_name: 'Test Agent',
40
- description: 'A test agent for unit tests',
41
- ...overrides,
42
- };
43
- }
44
-
45
- describe('Agent Loader', () => {
46
- let tempDir: string;
47
-
48
- beforeEach(() => {
49
- tempDir = mkdtempSync(join(tmpdir(), 'coreai-agent-test-'));
50
- });
51
-
52
- afterEach(() => {
53
- rmSync(tempDir, { recursive: true, force: true });
54
- });
55
-
56
- describe('parseAgentYaml', () => {
57
- it('should parse valid YAML', () => {
58
- const yaml = `
59
- role: backend-engineer
60
- type: ic-engineer
61
- display_name: Backend Engineer
62
- description: Backend development specialist
63
- `;
64
- const result = parseAgentYaml(yaml);
65
- expect(result).toEqual({
66
- role: 'backend-engineer',
67
- type: 'ic-engineer',
68
- display_name: 'Backend Engineer',
69
- description: 'Backend development specialist',
70
- });
71
- });
72
-
73
- it('should throw AgentError for invalid YAML', () => {
74
- const invalidYaml = `
75
- role: "test
76
- invalid: yaml
77
- `;
78
- expectAgentError(() => parseAgentYaml(invalidYaml), 'PARSE_ERROR');
79
- });
80
- });
81
-
82
- describe('validateAgentDefinition', () => {
83
- it('should validate a minimal valid agent', () => {
84
- const agent = createMinimalAgent();
85
- const result = validateAgentDefinition(agent);
86
- expect(result).toEqual(agent);
87
- });
88
-
89
- it('should validate a full agent definition', () => {
90
- const agent: AgentDefinition = {
91
- role: 'backend-engineer',
92
- type: 'ic-engineer',
93
- display_name: 'Backend Engineer',
94
- description: 'Backend development specialist',
95
- responsibilities: ['Implement APIs', 'Write tests'],
96
- expertise: {
97
- primary: ['API design', 'Database design'],
98
- tech_stack: '${config.tech_stack}',
99
- },
100
- skills: ['Code review', 'Debugging'],
101
- principles: {
102
- code_quality: ['Write clean code', 'Follow SOLID'],
103
- testing: ['Write tests first'],
104
- },
105
- behaviors: {
106
- workflow: 'ticket-implementation',
107
- quality_gates: '${config.quality_gates}',
108
- },
109
- context_sources: {
110
- shared: ['${remote.documentation}/architecture'],
111
- personal: ['KnowledgeLibrary/${agent.name}/context'],
112
- },
113
- communication: {
114
- inbox: 'KnowledgeLibrary/${agent.name}/inbox',
115
- outbox: 'KnowledgeLibrary/${agent.name}/outbox',
116
- },
117
- };
118
-
119
- const result = validateAgentDefinition(agent);
120
- expect(result).toEqual(agent);
121
- });
122
-
123
- it('should throw for missing required fields', () => {
124
- const agent = { type: 'ic-engineer' }; // missing role, display_name, description
125
- expectAgentError(() => validateAgentDefinition(agent), 'VALIDATION_ERROR');
126
- });
127
-
128
- it('should throw for invalid role format', () => {
129
- const agent = createMinimalAgent({ role: 'Invalid Role' }); // uppercase and space
130
- expectAgentError(() => validateAgentDefinition(agent), 'VALIDATION_ERROR');
131
- });
132
-
133
- it('should throw for invalid type', () => {
134
- const agent = { ...createMinimalAgent(), type: 'invalid-type' };
135
- expectAgentError(() => validateAgentDefinition(agent), 'VALIDATION_ERROR');
136
- });
137
-
138
- it('should throw for invalid workflow', () => {
139
- const agent = createMinimalAgent();
140
- agent.behaviors = { workflow: 'invalid-workflow' as never };
141
- expectAgentError(() => validateAgentDefinition(agent), 'VALIDATION_ERROR');
142
- });
143
- });
144
-
145
- describe('loadAgentFromFile', () => {
146
- it('should load and validate an agent file', () => {
147
- const agentPath = join(tempDir, 'test-agent.yaml');
148
- writeFileSync(
149
- agentPath,
150
- `
151
- role: test-agent
152
- type: ic-engineer
153
- display_name: Test Agent
154
- description: A test agent
155
- `
156
- );
157
-
158
- const result = loadAgentFromFile(agentPath);
159
- expect(result.role).toBe('test-agent');
160
- expect(result.type).toBe('ic-engineer');
161
- });
162
-
163
- it('should throw for non-existent file', () => {
164
- const agentPath = join(tempDir, 'nonexistent.yaml');
165
- expectAgentError(() => loadAgentFromFile(agentPath), 'NOT_FOUND');
166
- });
167
-
168
- it('should throw for invalid agent', () => {
169
- const agentPath = join(tempDir, 'invalid.yaml');
170
- writeFileSync(agentPath, 'invalid: content');
171
- expectAgentError(() => loadAgentFromFile(agentPath), 'VALIDATION_ERROR');
172
- });
173
- });
174
-
175
- describe('loadAgentsFromDirectory', () => {
176
- it('should load all agents from a directory', () => {
177
- // Create multiple agent files
178
- writeFileSync(
179
- join(tempDir, 'agent-one.yaml'),
180
- `
181
- role: agent-one
182
- type: ic-engineer
183
- display_name: Agent One
184
- description: First agent
185
- `
186
- );
187
- writeFileSync(
188
- join(tempDir, 'agent-two.yaml'),
189
- `
190
- role: agent-two
191
- type: manager
192
- display_name: Agent Two
193
- description: Second agent
194
- `
195
- );
196
-
197
- const agents = loadAgentsFromDirectory(tempDir, 'core');
198
-
199
- expect(agents.size).toBe(2);
200
- expect(agents.get('agent-one')?.definition.role).toBe('agent-one');
201
- expect(agents.get('agent-two')?.definition.role).toBe('agent-two');
202
- expect(agents.get('agent-one')?.source).toBe('core');
203
- });
204
-
205
- it('should return empty map for non-existent directory', () => {
206
- const agents = loadAgentsFromDirectory(join(tempDir, 'nonexistent'), 'core');
207
- expect(agents.size).toBe(0);
208
- });
209
-
210
- it('should skip invalid files and continue loading', () => {
211
- writeFileSync(
212
- join(tempDir, 'valid.yaml'),
213
- `
214
- role: valid-agent
215
- type: ic-engineer
216
- display_name: Valid Agent
217
- description: A valid agent
218
- `
219
- );
220
- writeFileSync(join(tempDir, 'invalid.yaml'), 'invalid: content');
221
-
222
- // Suppress console.warn for this test
223
- const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined);
224
-
225
- const agents = loadAgentsFromDirectory(tempDir, 'core');
226
-
227
- expect(agents.size).toBe(1);
228
- expect(agents.get('valid-agent')).toBeDefined();
229
- expect(warnSpy).toHaveBeenCalled();
230
-
231
- warnSpy.mockRestore();
232
- });
233
-
234
- it('should handle .yml extension', () => {
235
- writeFileSync(
236
- join(tempDir, 'agent.yml'),
237
- `
238
- role: yml-agent
239
- type: specialist
240
- display_name: YML Agent
241
- description: Agent with .yml extension
242
- `
243
- );
244
-
245
- const agents = loadAgentsFromDirectory(tempDir, 'custom');
246
- expect(agents.size).toBe(1);
247
- expect(agents.get('yml-agent')?.source).toBe('custom');
248
- });
249
- });
250
-
251
- describe('getRoleFromFilename', () => {
252
- it('should extract role from .yaml filename', () => {
253
- expect(getRoleFromFilename('/path/to/backend-engineer.yaml')).toBe('backend-engineer');
254
- });
255
-
256
- it('should extract role from .yml filename', () => {
257
- expect(getRoleFromFilename('/path/to/frontend-engineer.yml')).toBe('frontend-engineer');
258
- });
259
-
260
- it('should handle simple filename', () => {
261
- expect(getRoleFromFilename('devops.yaml')).toBe('devops');
262
- });
263
- });
264
-
265
- describe('Core Agent Definitions', () => {
266
- const coreAgentsDir = join(__dirname, '../../agents');
267
-
268
- it('should load all core agents from agents/ directory', () => {
269
- const agents = loadAgentsFromDirectory(coreAgentsDir, 'core');
270
-
271
- expect(agents.size).toBe(4);
272
- expect(agents.has('backend-engineer')).toBe(true);
273
- expect(agents.has('frontend-engineer')).toBe(true);
274
- expect(agents.has('devops-engineer')).toBe(true);
275
- expect(agents.has('engineering-manager')).toBe(true);
276
- });
277
-
278
- it('should validate backend-engineer agent', () => {
279
- const agent = loadAgentFromFile(join(coreAgentsDir, 'backend-engineer.yaml'));
280
-
281
- expect(agent.role).toBe('backend-engineer');
282
- expect(agent.type).toBe('ic-engineer');
283
- expect(agent.display_name).toBe('Backend Engineer');
284
- expect(agent.responsibilities).toBeDefined();
285
- expect(agent.expertise?.primary).toBeDefined();
286
- expect(agent.behaviors?.workflow).toBe('ticket-implementation');
287
- });
288
-
289
- it('should validate frontend-engineer agent', () => {
290
- const agent = loadAgentFromFile(join(coreAgentsDir, 'frontend-engineer.yaml'));
291
-
292
- expect(agent.role).toBe('frontend-engineer');
293
- expect(agent.type).toBe('ic-engineer');
294
- expect(agent.display_name).toBe('Frontend Engineer');
295
- expect(agent.responsibilities).toBeDefined();
296
- expect(agent.behaviors?.workflow).toBe('ticket-implementation');
297
- });
298
-
299
- it('should validate devops-engineer agent', () => {
300
- const agent = loadAgentFromFile(join(coreAgentsDir, 'devops-engineer.yaml'));
301
-
302
- expect(agent.role).toBe('devops-engineer');
303
- expect(agent.type).toBe('specialist');
304
- expect(agent.display_name).toBe('DevOps Engineer');
305
- expect(agent.responsibilities).toBeDefined();
306
- expect(agent.behaviors?.workflow).toBe('ticket-implementation');
307
- });
308
-
309
- it('should validate engineering-manager agent', () => {
310
- const agent = loadAgentFromFile(join(coreAgentsDir, 'engineering-manager.yaml'));
311
-
312
- expect(agent.role).toBe('engineering-manager');
313
- expect(agent.type).toBe('manager');
314
- expect(agent.display_name).toBe('Engineering Manager');
315
- expect(agent.responsibilities).toBeDefined();
316
- expect(agent.behaviors?.workflow).toBe('planning-estimation');
317
- });
318
- });
319
- });
@@ -1,143 +0,0 @@
1
- /**
2
- * Agent Loader
3
- *
4
- * Handles loading and validating agent YAML definitions.
5
- */
6
-
7
- import { existsSync, readdirSync, readFileSync } from 'fs';
8
- import { basename, extname, join } from 'path';
9
- import { parse as parseYaml } from 'yaml';
10
- import Ajv, { type ErrorObject } from 'ajv';
11
- import type { AgentDefinition, AgentMetadata, AgentSource } from './types.js';
12
-
13
- // Import schema as JSON
14
- import { createRequire } from 'module';
15
- const require = createRequire(import.meta.url);
16
- const agentSchema = require('../../schemas/agent.schema.json') as object;
17
-
18
- export class AgentError extends Error {
19
- constructor(
20
- message: string,
21
- public readonly code: AgentErrorCode,
22
- public readonly details?: unknown
23
- ) {
24
- super(message);
25
- this.name = 'AgentError';
26
- }
27
- }
28
-
29
- export type AgentErrorCode = 'NOT_FOUND' | 'PARSE_ERROR' | 'VALIDATION_ERROR' | 'READ_ERROR';
30
-
31
- /**
32
- * Parse YAML content into an agent definition
33
- */
34
- export function parseAgentYaml(content: string, filePath?: string): unknown {
35
- try {
36
- return parseYaml(content);
37
- } catch (error) {
38
- const message = error instanceof Error ? error.message : 'Unknown parse error';
39
- throw new AgentError(
40
- `Failed to parse agent YAML${filePath ? ` in ${filePath}` : ''}: ${message}`,
41
- 'PARSE_ERROR',
42
- error
43
- );
44
- }
45
- }
46
-
47
- /**
48
- * Validate parsed YAML against agent schema
49
- */
50
- export function validateAgentDefinition(agent: unknown): AgentDefinition {
51
- const ajv = new Ajv.default({ allErrors: true, strict: false });
52
- const validate = ajv.compile<AgentDefinition>(agentSchema);
53
- const valid = validate(agent);
54
-
55
- if (!valid) {
56
- const errors: ErrorObject[] = validate.errors ?? [];
57
- const errorMessages = errors.map((err: ErrorObject) => {
58
- const path = err.instancePath || 'root';
59
- return `${path}: ${err.message ?? 'unknown error'}`;
60
- });
61
-
62
- throw new AgentError(
63
- `Agent validation failed:\n - ${errorMessages.join('\n - ')}`,
64
- 'VALIDATION_ERROR',
65
- errors
66
- );
67
- }
68
-
69
- return agent as AgentDefinition;
70
- }
71
-
72
- /**
73
- * Load and validate an agent definition from a file
74
- */
75
- export function loadAgentFromFile(filePath: string): AgentDefinition {
76
- if (!existsSync(filePath)) {
77
- throw new AgentError(`Agent file not found: ${filePath}`, 'NOT_FOUND');
78
- }
79
-
80
- let content: string;
81
- try {
82
- content = readFileSync(filePath, 'utf-8');
83
- } catch (error) {
84
- const message = error instanceof Error ? error.message : 'Unknown read error';
85
- throw new AgentError(`Failed to read agent file ${filePath}: ${message}`, 'READ_ERROR', error);
86
- }
87
-
88
- const parsed = parseAgentYaml(content, filePath);
89
- return validateAgentDefinition(parsed);
90
- }
91
-
92
- /**
93
- * List all YAML files in a directory
94
- */
95
- function listYamlFiles(dir: string): string[] {
96
- if (!existsSync(dir)) {
97
- return [];
98
- }
99
-
100
- return readdirSync(dir)
101
- .filter((file) => {
102
- const ext = extname(file).toLowerCase();
103
- return ext === '.yaml' || ext === '.yml';
104
- })
105
- .map((file) => join(dir, file));
106
- }
107
-
108
- /**
109
- * Load all agents from a directory
110
- */
111
- export function loadAgentsFromDirectory(
112
- dir: string,
113
- source: AgentSource
114
- ): Map<string, AgentMetadata> {
115
- const agents = new Map<string, AgentMetadata>();
116
- const files = listYamlFiles(dir);
117
-
118
- for (const filePath of files) {
119
- try {
120
- const definition = loadAgentFromFile(filePath);
121
- agents.set(definition.role, {
122
- definition,
123
- source,
124
- filePath,
125
- });
126
- } catch (error) {
127
- // Log warning but continue loading other agents
128
- const fileName = basename(filePath);
129
- console.warn(`Warning: Failed to load agent from ${fileName}: ${(error as Error).message}`);
130
- }
131
- }
132
-
133
- return agents;
134
- }
135
-
136
- /**
137
- * Get the agent role name from a filename
138
- */
139
- export function getRoleFromFilename(filePath: string): string {
140
- const fileName = basename(filePath);
141
- const ext = extname(fileName);
142
- return fileName.slice(0, -ext.length);
143
- }