prjct-cli 0.10.0 → 0.10.2
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/CHANGELOG.md +31 -0
- package/core/__tests__/agentic/memory-system.test.js +263 -0
- package/core/__tests__/agentic/plan-mode.test.js +336 -0
- package/core/agentic/chain-of-thought.js +578 -0
- package/core/agentic/command-executor.js +238 -4
- package/core/agentic/context-builder.js +208 -8
- package/core/agentic/ground-truth.js +591 -0
- package/core/agentic/loop-detector.js +406 -0
- package/core/agentic/memory-system.js +850 -0
- package/core/agentic/parallel-tools.js +366 -0
- package/core/agentic/plan-mode.js +572 -0
- package/core/agentic/prompt-builder.js +76 -1
- package/core/agentic/response-templates.js +290 -0
- package/core/agentic/semantic-compression.js +517 -0
- package/core/agentic/think-blocks.js +657 -0
- package/core/agentic/tool-registry.js +32 -0
- package/core/agentic/validation-rules.js +380 -0
- package/core/command-registry.js +48 -0
- package/core/commands.js +43 -1
- package/core/context-sync.js +183 -0
- package/package.json +7 -15
- package/templates/commands/done.md +7 -0
- package/templates/commands/feature.md +8 -0
- package/templates/commands/ship.md +8 -0
- package/templates/commands/spec.md +128 -0
- package/templates/global/CLAUDE.md +17 -0
- package/core/__tests__/agentic/agent-router.test.js +0 -398
- package/core/__tests__/agentic/command-executor.test.js +0 -223
- package/core/__tests__/agentic/context-builder.test.js +0 -160
- package/core/__tests__/agentic/context-filter.test.js +0 -494
- package/core/__tests__/agentic/prompt-builder.test.js +0 -204
- package/core/__tests__/agentic/template-loader.test.js +0 -164
- package/core/__tests__/agentic/tool-registry.test.js +0 -243
- package/core/__tests__/domain/agent-generator.test.js +0 -289
- package/core/__tests__/domain/agent-loader.test.js +0 -179
- package/core/__tests__/domain/analyzer.test.js +0 -324
- package/core/__tests__/infrastructure/author-detector.test.js +0 -103
- package/core/__tests__/infrastructure/config-manager.test.js +0 -454
- package/core/__tests__/infrastructure/path-manager.test.js +0 -412
- package/core/__tests__/setup.test.js +0 -15
- package/core/__tests__/utils/date-helper.test.js +0 -169
- package/core/__tests__/utils/file-helper.test.js +0 -258
- package/core/__tests__/utils/jsonl-helper.test.js +0 -387
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
2
|
-
import AgentGenerator from '../../domain/agent-generator.js'
|
|
3
|
-
import fs from 'fs/promises'
|
|
4
|
-
import os from 'os'
|
|
5
|
-
import path from 'path'
|
|
6
|
-
|
|
7
|
-
describe('Agent Generator', () => {
|
|
8
|
-
const testProjectId = 'test-agent-gen-' + Date.now()
|
|
9
|
-
const agentsDir = path.join(os.homedir(), '.prjct-cli', 'projects', testProjectId, 'agents')
|
|
10
|
-
const generator = new AgentGenerator(testProjectId)
|
|
11
|
-
|
|
12
|
-
afterEach(async () => {
|
|
13
|
-
// Cleanup test files
|
|
14
|
-
try {
|
|
15
|
-
await fs.rm(agentsDir, { recursive: true, force: true })
|
|
16
|
-
} catch (error) {
|
|
17
|
-
// Ignore cleanup errors
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
describe('Constructor', () => {
|
|
22
|
-
it('should create generator with project ID', () => {
|
|
23
|
-
expect(generator.projectId).toBe(testProjectId)
|
|
24
|
-
expect(generator.outputDir).toContain(testProjectId)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('should use fallback directory without project ID', () => {
|
|
28
|
-
const fallbackGenerator = new AgentGenerator()
|
|
29
|
-
expect(fallbackGenerator.outputDir).toContain('.prjct-cli/agents')
|
|
30
|
-
expect(fallbackGenerator.outputDir).not.toContain('projects')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('should construct correct output path', () => {
|
|
34
|
-
expect(generator.outputDir).toBe(agentsDir)
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe('generateDynamicAgent()', () => {
|
|
39
|
-
it('should generate agent file', async () => {
|
|
40
|
-
await generator.generateDynamicAgent('test-agent', {
|
|
41
|
-
role: 'Test Agent Role',
|
|
42
|
-
expertise: 'Test Technologies',
|
|
43
|
-
responsibilities: 'Test Responsibilities',
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const agentFile = path.join(agentsDir, 'test-agent.md')
|
|
47
|
-
const exists = await fs
|
|
48
|
-
.access(agentFile)
|
|
49
|
-
.then(() => true)
|
|
50
|
-
.catch(() => false)
|
|
51
|
-
|
|
52
|
-
expect(exists).toBe(true)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('should create agent with correct content', async () => {
|
|
56
|
-
await generator.generateDynamicAgent('backend-agent', {
|
|
57
|
-
role: 'Backend Developer',
|
|
58
|
-
expertise: 'Node.js, Express, PostgreSQL',
|
|
59
|
-
responsibilities: 'API development and database management',
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
const content = await fs.readFile(path.join(agentsDir, 'backend-agent.md'), 'utf-8')
|
|
63
|
-
|
|
64
|
-
expect(content).toContain('# AGENT: BACKEND-AGENT')
|
|
65
|
-
expect(content).toContain('Role: Backend Developer')
|
|
66
|
-
expect(content).toContain('## META-INSTRUCTION')
|
|
67
|
-
// Expertise and Responsibilities are now part of the context or analysis instructions
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('should include project context in agent file', async () => {
|
|
71
|
-
await generator.generateDynamicAgent('context-agent', {
|
|
72
|
-
role: 'Agent with Context',
|
|
73
|
-
expertise: 'Testing',
|
|
74
|
-
responsibilities: 'Test things',
|
|
75
|
-
projectContext: {
|
|
76
|
-
framework: 'React',
|
|
77
|
-
version: '18.0',
|
|
78
|
-
},
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
const content = await fs.readFile(path.join(agentsDir, 'context-agent.md'), 'utf-8')
|
|
82
|
-
|
|
83
|
-
expect(content).toContain('## PROJECT CONTEXT')
|
|
84
|
-
expect(content).toContain('framework')
|
|
85
|
-
expect(content).toContain('React')
|
|
86
|
-
expect(content).toContain('version')
|
|
87
|
-
expect(content).toContain('18.0')
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('should handle missing optional fields', async () => {
|
|
91
|
-
await generator.generateDynamicAgent('minimal-agent', {
|
|
92
|
-
role: 'Minimal Role',
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
const content = await fs.readFile(path.join(agentsDir, 'minimal-agent.md'), 'utf-8')
|
|
96
|
-
|
|
97
|
-
expect(content).toContain('# AGENT: MINIMAL-AGENT')
|
|
98
|
-
expect(content).toContain('## META-INSTRUCTION')
|
|
99
|
-
expect(content).toContain('ANALYZE the provided PROJECT CONTEXT')
|
|
100
|
-
expect(content).toContain('No specific project context provided')
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('should create output directory if not exists', async () => {
|
|
104
|
-
const newProjectId = 'new-project-' + Date.now()
|
|
105
|
-
const newGenerator = new AgentGenerator(newProjectId)
|
|
106
|
-
const newAgentsDir = path.join(os.homedir(), '.prjct-cli', 'projects', newProjectId, 'agents')
|
|
107
|
-
|
|
108
|
-
await newGenerator.generateDynamicAgent('auto-create', {
|
|
109
|
-
role: 'Auto Created Agent',
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
const exists = await fs
|
|
113
|
-
.access(newAgentsDir)
|
|
114
|
-
.then(() => true)
|
|
115
|
-
.catch(() => false)
|
|
116
|
-
|
|
117
|
-
expect(exists).toBe(true)
|
|
118
|
-
|
|
119
|
-
// Cleanup
|
|
120
|
-
await fs.rm(path.join(os.homedir(), '.prjct-cli', 'projects', newProjectId), {
|
|
121
|
-
recursive: true,
|
|
122
|
-
force: true,
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
|
|
126
|
-
it('should create multiple agents', async () => {
|
|
127
|
-
await generator.generateDynamicAgent('agent-1', { role: 'Agent One' })
|
|
128
|
-
await generator.generateDynamicAgent('agent-2', { role: 'Agent Two' })
|
|
129
|
-
await generator.generateDynamicAgent('agent-3', { role: 'Agent Three' })
|
|
130
|
-
|
|
131
|
-
const agents = await generator.listAgents()
|
|
132
|
-
|
|
133
|
-
expect(agents).toHaveLength(3)
|
|
134
|
-
expect(agents).toContain('agent-1')
|
|
135
|
-
expect(agents).toContain('agent-2')
|
|
136
|
-
expect(agents).toContain('agent-3')
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('should use agent name as fallback for role', async () => {
|
|
140
|
-
await generator.generateDynamicAgent('fallback-agent', {})
|
|
141
|
-
|
|
142
|
-
const content = await fs.readFile(path.join(agentsDir, 'fallback-agent.md'), 'utf-8')
|
|
143
|
-
|
|
144
|
-
expect(content).toContain('# AGENT: FALLBACK-AGENT')
|
|
145
|
-
})
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
describe('cleanupObsoleteAgents()', () => {
|
|
149
|
-
beforeEach(async () => {
|
|
150
|
-
// Create some test agents
|
|
151
|
-
await generator.generateDynamicAgent('agent-1', { role: 'Agent 1' })
|
|
152
|
-
await generator.generateDynamicAgent('agent-2', { role: 'Agent 2' })
|
|
153
|
-
await generator.generateDynamicAgent('agent-3', { role: 'Agent 3' })
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
it('should remove obsolete agents', async () => {
|
|
157
|
-
const removed = await generator.cleanupObsoleteAgents(['agent-1', 'agent-2'])
|
|
158
|
-
|
|
159
|
-
expect(removed).toContain('agent-3')
|
|
160
|
-
expect(removed).toHaveLength(1)
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
it('should keep required agents', async () => {
|
|
164
|
-
await generator.cleanupObsoleteAgents(['agent-1', 'agent-2'])
|
|
165
|
-
|
|
166
|
-
const agents = await generator.listAgents()
|
|
167
|
-
|
|
168
|
-
expect(agents).toContain('agent-1')
|
|
169
|
-
expect(agents).toContain('agent-2')
|
|
170
|
-
expect(agents).not.toContain('agent-3')
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('should remove multiple obsolete agents', async () => {
|
|
174
|
-
await generator.generateDynamicAgent('agent-4', { role: 'Agent 4' })
|
|
175
|
-
|
|
176
|
-
const removed = await generator.cleanupObsoleteAgents(['agent-1'])
|
|
177
|
-
|
|
178
|
-
expect(removed).toHaveLength(3)
|
|
179
|
-
expect(removed).toContain('agent-2')
|
|
180
|
-
expect(removed).toContain('agent-3')
|
|
181
|
-
expect(removed).toContain('agent-4')
|
|
182
|
-
})
|
|
183
|
-
|
|
184
|
-
it('should return empty array if all agents are required', async () => {
|
|
185
|
-
const removed = await generator.cleanupObsoleteAgents(['agent-1', 'agent-2', 'agent-3'])
|
|
186
|
-
|
|
187
|
-
expect(removed).toEqual([])
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
it('should handle non-existent directory gracefully', async () => {
|
|
191
|
-
const emptyGenerator = new AgentGenerator('empty-' + Date.now())
|
|
192
|
-
|
|
193
|
-
const removed = await emptyGenerator.cleanupObsoleteAgents(['agent-1'])
|
|
194
|
-
|
|
195
|
-
expect(Array.isArray(removed)).toBe(true)
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
describe('listAgents()', () => {
|
|
200
|
-
it('should list all agents', async () => {
|
|
201
|
-
await generator.generateDynamicAgent('frontend', { role: 'Frontend' })
|
|
202
|
-
await generator.generateDynamicAgent('backend', { role: 'Backend' })
|
|
203
|
-
|
|
204
|
-
const agents = await generator.listAgents()
|
|
205
|
-
|
|
206
|
-
expect(agents).toHaveLength(2)
|
|
207
|
-
expect(agents).toContain('frontend')
|
|
208
|
-
expect(agents).toContain('backend')
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('should return empty array for no agents', async () => {
|
|
212
|
-
const agents = await generator.listAgents()
|
|
213
|
-
|
|
214
|
-
expect(agents).toEqual([])
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it('should ignore non-.md files', async () => {
|
|
218
|
-
await generator.generateDynamicAgent('valid-agent', { role: 'Valid' })
|
|
219
|
-
await fs.writeFile(path.join(agentsDir, 'not-agent.txt'), 'text file')
|
|
220
|
-
await fs.writeFile(path.join(agentsDir, 'config.json'), '{}')
|
|
221
|
-
|
|
222
|
-
const agents = await generator.listAgents()
|
|
223
|
-
|
|
224
|
-
expect(agents).toHaveLength(1)
|
|
225
|
-
expect(agents).toContain('valid-agent')
|
|
226
|
-
})
|
|
227
|
-
|
|
228
|
-
it('should ignore hidden files', async () => {
|
|
229
|
-
await generator.generateDynamicAgent('visible', { role: 'Visible' })
|
|
230
|
-
await fs.writeFile(path.join(agentsDir, '.hidden.md'), 'hidden')
|
|
231
|
-
|
|
232
|
-
const agents = await generator.listAgents()
|
|
233
|
-
|
|
234
|
-
expect(agents).toHaveLength(1)
|
|
235
|
-
expect(agents).toContain('visible')
|
|
236
|
-
})
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
describe('Integration', () => {
|
|
240
|
-
it('should create, list, and cleanup agents', async () => {
|
|
241
|
-
// Create agents
|
|
242
|
-
await generator.generateDynamicAgent('keep-me', { role: 'Keep' })
|
|
243
|
-
await generator.generateDynamicAgent('remove-me', { role: 'Remove' })
|
|
244
|
-
|
|
245
|
-
// Verify they exist
|
|
246
|
-
const initialAgents = await generator.listAgents()
|
|
247
|
-
expect(initialAgents).toHaveLength(2)
|
|
248
|
-
|
|
249
|
-
// Cleanup obsolete
|
|
250
|
-
const removed = await generator.cleanupObsoleteAgents(['keep-me'])
|
|
251
|
-
expect(removed).toContain('remove-me')
|
|
252
|
-
|
|
253
|
-
// Verify cleanup
|
|
254
|
-
const finalAgents = await generator.listAgents()
|
|
255
|
-
expect(finalAgents).toHaveLength(1)
|
|
256
|
-
expect(finalAgents).toContain('keep-me')
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
it('should handle agent file content correctly', async () => {
|
|
260
|
-
await generator.generateDynamicAgent('full-agent', {
|
|
261
|
-
role: 'Full Stack Developer',
|
|
262
|
-
expertise: 'React, Node.js, PostgreSQL, Docker',
|
|
263
|
-
responsibilities: 'Build and deploy full stack applications',
|
|
264
|
-
projectContext: {
|
|
265
|
-
stack: 'MERN',
|
|
266
|
-
deployment: 'AWS',
|
|
267
|
-
},
|
|
268
|
-
})
|
|
269
|
-
|
|
270
|
-
const content = await fs.readFile(path.join(agentsDir, 'full-agent.md'), 'utf-8')
|
|
271
|
-
|
|
272
|
-
// Should have all sections
|
|
273
|
-
expect(content).toContain('# AGENT: FULL-AGENT')
|
|
274
|
-
expect(content).toContain('Role: Full Stack Developer')
|
|
275
|
-
expect(content).toContain('## META-INSTRUCTION')
|
|
276
|
-
expect(content).toContain('## DOMAIN AUTHORITY')
|
|
277
|
-
expect(content).toContain('## DYNAMIC STANDARDS')
|
|
278
|
-
expect(content).toContain('## ORCHESTRATION PROTOCOL')
|
|
279
|
-
expect(content).toContain('## PROJECT CONTEXT')
|
|
280
|
-
|
|
281
|
-
// Should have context content
|
|
282
|
-
expect(content).toContain('MERN')
|
|
283
|
-
expect(content).toContain('AWS')
|
|
284
|
-
|
|
285
|
-
// Should NOT have hardcoded tech lists anymore
|
|
286
|
-
// expect(content).toContain('React, Node.js, PostgreSQL, Docker') // This is no longer explicitly listed in EXPERTISE section as that section is gone
|
|
287
|
-
})
|
|
288
|
-
})
|
|
289
|
-
})
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for AgentLoader
|
|
3
|
-
* Verifies that agents are loaded correctly from project files
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs').promises
|
|
7
|
-
const path = require('path')
|
|
8
|
-
const os = require('os')
|
|
9
|
-
const { describe, it, expect, beforeEach, afterEach } = require('vitest')
|
|
10
|
-
const AgentLoader = require('../../domain/agent-loader')
|
|
11
|
-
|
|
12
|
-
describe('AgentLoader', () => {
|
|
13
|
-
let testProjectId
|
|
14
|
-
let testAgentsDir
|
|
15
|
-
let loader
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
// Create unique test project ID
|
|
19
|
-
testProjectId = `test-${Date.now()}`
|
|
20
|
-
testAgentsDir = path.join(os.homedir(), '.prjct-cli', 'projects', testProjectId, 'agents')
|
|
21
|
-
await fs.mkdir(testAgentsDir, { recursive: true })
|
|
22
|
-
loader = new AgentLoader(testProjectId)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
afterEach(async () => {
|
|
26
|
-
// Cleanup: Remove test agents directory
|
|
27
|
-
try {
|
|
28
|
-
await fs.rm(testAgentsDir, { recursive: true, force: true })
|
|
29
|
-
} catch (error) {
|
|
30
|
-
// Ignore cleanup errors
|
|
31
|
-
}
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('loadAgent', () => {
|
|
35
|
-
it('should load an existing agent from file', async () => {
|
|
36
|
-
// Create test agent file
|
|
37
|
-
const agentName = 'frontend-specialist'
|
|
38
|
-
const agentContent = `# AGENT: FRONTEND-SPECIALIST
|
|
39
|
-
Role: Frontend Development Specialist
|
|
40
|
-
|
|
41
|
-
## META-INSTRUCTION
|
|
42
|
-
You are a frontend specialist.
|
|
43
|
-
|
|
44
|
-
## DOMAIN AUTHORITY
|
|
45
|
-
You are the owner of the frontend domain.
|
|
46
|
-
`
|
|
47
|
-
|
|
48
|
-
const agentPath = path.join(testAgentsDir, `${agentName}.md`)
|
|
49
|
-
await fs.writeFile(agentPath, agentContent, 'utf-8')
|
|
50
|
-
|
|
51
|
-
// Load agent
|
|
52
|
-
const agent = await loader.loadAgent(agentName)
|
|
53
|
-
|
|
54
|
-
// Verify
|
|
55
|
-
expect(agent).not.toBeNull()
|
|
56
|
-
expect(agent.name).toBe(agentName)
|
|
57
|
-
expect(agent.content).toBe(agentContent)
|
|
58
|
-
expect(agent.role).toBe('Frontend Development Specialist')
|
|
59
|
-
expect(agent.domain).toBe('frontend')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('should return null for non-existent agent', async () => {
|
|
63
|
-
const agent = await loader.loadAgent('non-existent-agent')
|
|
64
|
-
expect(agent).toBeNull()
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('should cache loaded agents', async () => {
|
|
68
|
-
// Create test agent
|
|
69
|
-
const agentName = 'backend-specialist'
|
|
70
|
-
const agentPath = path.join(testAgentsDir, `${agentName}.md`)
|
|
71
|
-
await fs.writeFile(agentPath, '# AGENT: BACKEND-SPECIALIST\nRole: Backend Specialist', 'utf-8')
|
|
72
|
-
|
|
73
|
-
// Load twice
|
|
74
|
-
const agent1 = await loader.loadAgent(agentName)
|
|
75
|
-
const agent2 = await loader.loadAgent(agentName)
|
|
76
|
-
|
|
77
|
-
// Should be same object (cached)
|
|
78
|
-
expect(agent1).toBe(agent2)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('should extract skills from agent content', async () => {
|
|
82
|
-
const agentName = 'react-specialist'
|
|
83
|
-
const agentContent = `# AGENT: REACT-SPECIALIST
|
|
84
|
-
Role: React Development Specialist
|
|
85
|
-
|
|
86
|
-
This agent specializes in React, TypeScript, and Next.js.
|
|
87
|
-
`
|
|
88
|
-
|
|
89
|
-
const agentPath = path.join(testAgentsDir, `${agentName}.md`)
|
|
90
|
-
await fs.writeFile(agentPath, agentContent, 'utf-8')
|
|
91
|
-
|
|
92
|
-
const agent = await loader.loadAgent(agentName)
|
|
93
|
-
|
|
94
|
-
expect(agent.skills).toContain('React')
|
|
95
|
-
expect(agent.skills).toContain('TypeScript')
|
|
96
|
-
expect(agent.skills).toContain('Next.js')
|
|
97
|
-
})
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
describe('loadAllAgents', () => {
|
|
101
|
-
it('should load all agents in the directory', async () => {
|
|
102
|
-
// Create multiple agent files
|
|
103
|
-
const agents = [
|
|
104
|
-
{ name: 'frontend-specialist', content: '# AGENT: FRONTEND-SPECIALIST\nRole: Frontend' },
|
|
105
|
-
{ name: 'backend-specialist', content: '# AGENT: BACKEND-SPECIALIST\nRole: Backend' },
|
|
106
|
-
{ name: 'qa-specialist', content: '# AGENT: QA-SPECIALIST\nRole: QA' }
|
|
107
|
-
]
|
|
108
|
-
|
|
109
|
-
for (const agent of agents) {
|
|
110
|
-
const agentPath = path.join(testAgentsDir, `${agent.name}.md`)
|
|
111
|
-
await fs.writeFile(agentPath, agent.content, 'utf-8')
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Load all
|
|
115
|
-
const loadedAgents = await loader.loadAllAgents()
|
|
116
|
-
|
|
117
|
-
expect(loadedAgents).toHaveLength(3)
|
|
118
|
-
expect(loadedAgents.map(a => a.name)).toContain('frontend-specialist')
|
|
119
|
-
expect(loadedAgents.map(a => a.name)).toContain('backend-specialist')
|
|
120
|
-
expect(loadedAgents.map(a => a.name)).toContain('qa-specialist')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('should return empty array if no agents exist', async () => {
|
|
124
|
-
const agents = await loader.loadAllAgents()
|
|
125
|
-
expect(agents).toEqual([])
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('should ignore non-markdown files', async () => {
|
|
129
|
-
// Create agent file and non-agent file
|
|
130
|
-
const agentPath = path.join(testAgentsDir, 'frontend-specialist.md')
|
|
131
|
-
await fs.writeFile(agentPath, '# AGENT', 'utf-8')
|
|
132
|
-
|
|
133
|
-
const otherFile = path.join(testAgentsDir, 'config.json')
|
|
134
|
-
await fs.writeFile(otherFile, '{}', 'utf-8')
|
|
135
|
-
|
|
136
|
-
const agents = await loader.loadAllAgents()
|
|
137
|
-
|
|
138
|
-
expect(agents).toHaveLength(1)
|
|
139
|
-
expect(agents[0].name).toBe('frontend-specialist')
|
|
140
|
-
})
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
describe('agentExists', () => {
|
|
144
|
-
it('should return true for existing agent', async () => {
|
|
145
|
-
const agentName = 'test-agent'
|
|
146
|
-
const agentPath = path.join(testAgentsDir, `${agentName}.md`)
|
|
147
|
-
await fs.writeFile(agentPath, '# AGENT', 'utf-8')
|
|
148
|
-
|
|
149
|
-
const exists = await loader.agentExists(agentName)
|
|
150
|
-
expect(exists).toBe(true)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('should return false for non-existent agent', async () => {
|
|
154
|
-
const exists = await loader.agentExists('non-existent')
|
|
155
|
-
expect(exists).toBe(false)
|
|
156
|
-
})
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
describe('clearCache', () => {
|
|
160
|
-
it('should clear the agent cache', async () => {
|
|
161
|
-
const agentName = 'test-agent'
|
|
162
|
-
const agentPath = path.join(testAgentsDir, `${agentName}.md`)
|
|
163
|
-
await fs.writeFile(agentPath, '# AGENT', 'utf-8')
|
|
164
|
-
|
|
165
|
-
// Load and cache
|
|
166
|
-
const agent1 = await loader.loadAgent(agentName)
|
|
167
|
-
expect(agent1).not.toBeNull()
|
|
168
|
-
|
|
169
|
-
// Clear cache
|
|
170
|
-
loader.clearCache()
|
|
171
|
-
|
|
172
|
-
// Load again - should still work but be new object
|
|
173
|
-
const agent2 = await loader.loadAgent(agentName)
|
|
174
|
-
expect(agent2).not.toBeNull()
|
|
175
|
-
// Note: In real usage, they might be same due to file system, but cache is cleared
|
|
176
|
-
})
|
|
177
|
-
})
|
|
178
|
-
})
|
|
179
|
-
|