@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.
- package/dist/cli/index.js +5 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/.prettierrc +0 -9
- package/AGENT_SPEC.md +0 -347
- package/ARCHITECTURE.md +0 -547
- package/DRAFT_PRD.md +0 -1440
- package/IMPLEMENTATION_PLAN.md +0 -256
- package/PRODUCT.md +0 -473
- package/WORKFLOWS.md +0 -295
- package/commands/core/check-inbox.md +0 -34
- package/commands/core/delegate.md +0 -30
- package/commands/core/git-commit.md +0 -144
- package/commands/core/pr-create.md +0 -193
- package/commands/core/review.md +0 -56
- package/commands/core/sprint-status.md +0 -65
- package/commands/optional/docs-update.md +0 -200
- package/commands/optional/jira-create.md +0 -200
- package/commands/optional/jira-transition.md +0 -184
- package/commands/optional/worktree-cleanup.md +0 -167
- package/commands/optional/worktree-setup.md +0 -110
- package/eslint.config.js +0 -29
- package/jest.config.js +0 -22
- package/knowledge-library/README.md +0 -118
- package/knowledge-library/android-engineer/context/current.txt +0 -42
- package/knowledge-library/android-engineer/control/decisions.txt +0 -9
- package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/android-engineer/control/objectives.txt +0 -26
- package/knowledge-library/android-engineer/history/.gitkeep +0 -0
- package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/architecture.txt +0 -61
- package/knowledge-library/backend-engineer/context/current.txt +0 -42
- package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
- package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
- package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/context.txt +0 -52
- package/knowledge-library/devops-engineer/context/current.txt +0 -42
- package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
- package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
- package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/context/current.txt +0 -40
- package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
- package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
- package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
- package/knowledge-library/prd.txt +0 -81
- package/knowledge-library/product-manager/context/current.txt +0 -42
- package/knowledge-library/product-manager/control/decisions.txt +0 -9
- package/knowledge-library/product-manager/control/dependencies.txt +0 -19
- package/knowledge-library/product-manager/control/objectives.txt +0 -26
- package/knowledge-library/product-manager/history/.gitkeep +0 -0
- package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/product-manager/tech/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/context/current.txt +0 -42
- package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
- package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
- package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/security-engineer/context/current.txt +0 -42
- package/knowledge-library/security-engineer/control/decisions.txt +0 -9
- package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/security-engineer/control/objectives.txt +0 -26
- package/knowledge-library/security-engineer/history/.gitkeep +0 -0
- package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/context/current.txt +0 -42
- package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
- package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
- package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
- package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/context/current.txt +0 -42
- package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
- package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
- package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
- package/scripts/add-agent.sh +0 -323
- package/scripts/install.sh +0 -354
- package/src/adapters/factory.test.ts +0 -386
- package/src/adapters/factory.ts +0 -305
- package/src/adapters/index.ts +0 -113
- package/src/adapters/interfaces.ts +0 -268
- package/src/adapters/mcp/client.test.ts +0 -130
- package/src/adapters/mcp/client.ts +0 -451
- package/src/adapters/mcp/discovery.test.ts +0 -315
- package/src/adapters/mcp/discovery.ts +0 -340
- package/src/adapters/mcp/index.ts +0 -66
- package/src/adapters/mcp/mapper.test.ts +0 -218
- package/src/adapters/mcp/mapper.ts +0 -536
- package/src/adapters/mcp/registry.test.ts +0 -433
- package/src/adapters/mcp/registry.ts +0 -550
- package/src/adapters/mcp/types.ts +0 -258
- package/src/adapters/native/filesystem.test.ts +0 -350
- package/src/adapters/native/filesystem.ts +0 -393
- package/src/adapters/native/github.test.ts +0 -173
- package/src/adapters/native/github.ts +0 -627
- package/src/adapters/native/index.ts +0 -22
- package/src/adapters/native/selector.test.ts +0 -224
- package/src/adapters/native/selector.ts +0 -150
- package/src/adapters/types.ts +0 -270
- package/src/agents/compiler.test.ts +0 -399
- package/src/agents/compiler.ts +0 -422
- package/src/agents/index.ts +0 -37
- package/src/agents/loader.test.ts +0 -319
- package/src/agents/loader.ts +0 -143
- package/src/agents/resolver.test.ts +0 -282
- package/src/agents/resolver.ts +0 -262
- package/src/agents/types.ts +0 -97
- package/src/cache/index.ts +0 -38
- package/src/cache/interfaces.ts +0 -283
- package/src/cache/manager.test.ts +0 -266
- package/src/cache/manager.ts +0 -388
- package/src/cache/provider.test.ts +0 -485
- package/src/cache/provider.ts +0 -745
- package/src/cache/types.test.ts +0 -192
- package/src/cache/types.ts +0 -313
- package/src/cli/commands/build.test.ts +0 -248
- package/src/cli/commands/build.ts +0 -284
- package/src/cli/commands/cache.test.ts +0 -221
- package/src/cli/commands/cache.ts +0 -229
- package/src/cli/commands/index.ts +0 -63
- package/src/cli/commands/init.test.ts +0 -173
- package/src/cli/commands/init.ts +0 -296
- package/src/cli/commands/skills.test.ts +0 -272
- package/src/cli/commands/skills.ts +0 -348
- package/src/cli/commands/status.test.ts +0 -392
- package/src/cli/commands/status.ts +0 -332
- package/src/cli/commands/sync.test.ts +0 -213
- package/src/cli/commands/sync.ts +0 -251
- package/src/cli/commands/validate.test.ts +0 -216
- package/src/cli/commands/validate.ts +0 -340
- package/src/cli/index.test.ts +0 -190
- package/src/cli/index.ts +0 -493
- package/src/commands/context.test.ts +0 -163
- package/src/commands/context.ts +0 -111
- package/src/commands/index.ts +0 -56
- package/src/commands/loader.test.ts +0 -273
- package/src/commands/loader.ts +0 -355
- package/src/commands/registry.test.ts +0 -384
- package/src/commands/registry.ts +0 -248
- package/src/commands/runner.test.ts +0 -297
- package/src/commands/runner.ts +0 -222
- package/src/commands/types.ts +0 -361
- package/src/config/index.ts +0 -19
- package/src/config/loader.test.ts +0 -262
- package/src/config/loader.ts +0 -188
- package/src/config/types.ts +0 -154
- package/src/context/index.ts +0 -14
- package/src/context/loader.test.ts +0 -334
- package/src/context/loader.ts +0 -357
- package/src/index.test.ts +0 -13
- package/src/index.ts +0 -268
- package/src/knowledge-library/index.ts +0 -44
- package/src/knowledge-library/manager.test.ts +0 -536
- package/src/knowledge-library/manager.ts +0 -804
- package/src/knowledge-library/types.ts +0 -432
- package/src/skills/generator.test.ts +0 -602
- package/src/skills/generator.ts +0 -491
- package/src/skills/index.ts +0 -27
- package/src/skills/templates.ts +0 -520
- package/src/skills/types.ts +0 -251
- package/templates/completion-report.md +0 -72
- package/templates/feedback.md +0 -56
- package/templates/project-files/CLAUDE.md.template +0 -109
- package/templates/project-files/coreai.json.example +0 -47
- package/templates/project-files/mcp.json.template +0 -20
- package/templates/review-complete.md +0 -64
- package/templates/review-request.md +0 -67
- package/templates/task-assignment.md +0 -51
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -26
- 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
|
-
});
|
package/src/agents/loader.ts
DELETED
|
@@ -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
|
-
}
|