@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.
- package/.prettierrc +9 -0
- package/AGENT_SPEC.md +347 -0
- package/ARCHITECTURE.md +547 -0
- package/DRAFT_PRD.md +1440 -0
- package/IMPLEMENTATION_PLAN.md +256 -0
- package/PRODUCT.md +473 -0
- package/README.md +303 -0
- package/WORKFLOWS.md +295 -0
- package/agents/_templates/ic-engineer.md +185 -0
- package/agents/_templates/reviewer.md +182 -0
- package/agents/backend-engineer.yaml +72 -0
- package/agents/devops-engineer.yaml +72 -0
- package/agents/engineering-manager.yaml +70 -0
- package/agents/examples/android-engineer.md +302 -0
- package/agents/examples/backend-engineer.md +320 -0
- package/agents/examples/devops-engineer.md +742 -0
- package/agents/examples/engineering-manager.md +469 -0
- package/agents/examples/frontend-engineer.md +58 -0
- package/agents/examples/product-manager.md +315 -0
- package/agents/examples/qa-engineer.md +371 -0
- package/agents/examples/security-engineer.md +525 -0
- package/agents/examples/solutions-architect.md +351 -0
- package/agents/examples/wearos-engineer.md +359 -0
- package/agents/frontend-engineer.yaml +72 -0
- package/commands/core/check-inbox.md +34 -0
- package/commands/core/delegate.md +30 -0
- package/commands/core/git-commit.md +144 -0
- package/commands/core/pr-create.md +193 -0
- package/commands/core/review.md +56 -0
- package/commands/core/sprint-status.md +65 -0
- package/commands/optional/docs-update.md +200 -0
- package/commands/optional/jira-create.md +200 -0
- package/commands/optional/jira-transition.md +184 -0
- package/commands/optional/worktree-cleanup.md +167 -0
- package/commands/optional/worktree-setup.md +110 -0
- package/dist/cli/index.js +4037 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +2978 -0
- package/dist/index.js +3867 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +29 -0
- package/jest.config.js +22 -0
- package/knowledge-library/README.md +118 -0
- package/knowledge-library/android-engineer/context/current.txt +42 -0
- package/knowledge-library/android-engineer/control/decisions.txt +9 -0
- package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/android-engineer/control/objectives.txt +26 -0
- 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 +61 -0
- package/knowledge-library/backend-engineer/context/current.txt +42 -0
- package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
- package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
- 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 +52 -0
- package/knowledge-library/devops-engineer/context/current.txt +42 -0
- package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
- package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
- 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 +40 -0
- package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
- package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
- 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 +81 -0
- package/knowledge-library/product-manager/context/current.txt +42 -0
- package/knowledge-library/product-manager/control/decisions.txt +9 -0
- package/knowledge-library/product-manager/control/dependencies.txt +19 -0
- package/knowledge-library/product-manager/control/objectives.txt +26 -0
- 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 +42 -0
- package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
- package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
- 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 +42 -0
- package/knowledge-library/security-engineer/control/decisions.txt +9 -0
- package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/security-engineer/control/objectives.txt +26 -0
- 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 +42 -0
- package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
- package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
- package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
- 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 +42 -0
- package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
- package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
- 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/package.json +66 -0
- package/schemas/agent.schema.json +171 -0
- package/schemas/coreai.config.schema.json +257 -0
- package/scripts/add-agent.sh +323 -0
- package/scripts/install.sh +354 -0
- package/src/adapters/factory.test.ts +386 -0
- package/src/adapters/factory.ts +305 -0
- package/src/adapters/index.ts +113 -0
- package/src/adapters/interfaces.ts +268 -0
- package/src/adapters/mcp/client.test.ts +130 -0
- package/src/adapters/mcp/client.ts +451 -0
- package/src/adapters/mcp/discovery.test.ts +315 -0
- package/src/adapters/mcp/discovery.ts +340 -0
- package/src/adapters/mcp/index.ts +66 -0
- package/src/adapters/mcp/mapper.test.ts +218 -0
- package/src/adapters/mcp/mapper.ts +536 -0
- package/src/adapters/mcp/registry.test.ts +433 -0
- package/src/adapters/mcp/registry.ts +550 -0
- package/src/adapters/mcp/types.ts +258 -0
- package/src/adapters/native/filesystem.test.ts +350 -0
- package/src/adapters/native/filesystem.ts +393 -0
- package/src/adapters/native/github.test.ts +173 -0
- package/src/adapters/native/github.ts +627 -0
- package/src/adapters/native/index.ts +22 -0
- package/src/adapters/native/selector.test.ts +224 -0
- package/src/adapters/native/selector.ts +150 -0
- package/src/adapters/types.ts +270 -0
- package/src/agents/compiler.test.ts +399 -0
- package/src/agents/compiler.ts +359 -0
- package/src/agents/index.ts +36 -0
- package/src/agents/loader.test.ts +319 -0
- package/src/agents/loader.ts +143 -0
- package/src/agents/resolver.test.ts +282 -0
- package/src/agents/resolver.ts +262 -0
- package/src/agents/types.ts +87 -0
- package/src/cache/index.ts +38 -0
- package/src/cache/interfaces.ts +283 -0
- package/src/cache/manager.test.ts +266 -0
- package/src/cache/manager.ts +388 -0
- package/src/cache/provider.test.ts +485 -0
- package/src/cache/provider.ts +745 -0
- package/src/cache/types.test.ts +192 -0
- package/src/cache/types.ts +313 -0
- package/src/cli/commands/build.test.ts +248 -0
- package/src/cli/commands/build.ts +244 -0
- package/src/cli/commands/cache.test.ts +221 -0
- package/src/cli/commands/cache.ts +229 -0
- package/src/cli/commands/index.ts +63 -0
- package/src/cli/commands/init.test.ts +173 -0
- package/src/cli/commands/init.ts +296 -0
- package/src/cli/commands/skills.test.ts +272 -0
- package/src/cli/commands/skills.ts +348 -0
- package/src/cli/commands/status.test.ts +392 -0
- package/src/cli/commands/status.ts +332 -0
- package/src/cli/commands/sync.test.ts +213 -0
- package/src/cli/commands/sync.ts +251 -0
- package/src/cli/commands/validate.test.ts +216 -0
- package/src/cli/commands/validate.ts +340 -0
- package/src/cli/index.test.ts +190 -0
- package/src/cli/index.ts +493 -0
- package/src/commands/context.test.ts +163 -0
- package/src/commands/context.ts +111 -0
- package/src/commands/index.ts +56 -0
- package/src/commands/loader.test.ts +273 -0
- package/src/commands/loader.ts +355 -0
- package/src/commands/registry.test.ts +384 -0
- package/src/commands/registry.ts +248 -0
- package/src/commands/runner.test.ts +297 -0
- package/src/commands/runner.ts +222 -0
- package/src/commands/types.ts +361 -0
- package/src/config/index.ts +19 -0
- package/src/config/loader.test.ts +262 -0
- package/src/config/loader.ts +188 -0
- package/src/config/types.ts +154 -0
- package/src/context/index.ts +14 -0
- package/src/context/loader.test.ts +334 -0
- package/src/context/loader.ts +357 -0
- package/src/index.test.ts +13 -0
- package/src/index.ts +244 -0
- package/src/knowledge-library/index.ts +44 -0
- package/src/knowledge-library/manager.test.ts +536 -0
- package/src/knowledge-library/manager.ts +804 -0
- package/src/knowledge-library/types.ts +432 -0
- package/src/skills/generator.test.ts +602 -0
- package/src/skills/generator.ts +491 -0
- package/src/skills/index.ts +27 -0
- package/src/skills/templates.ts +520 -0
- package/src/skills/types.ts +251 -0
- package/templates/completion-report.md +72 -0
- package/templates/feedback.md +56 -0
- package/templates/project-files/CLAUDE.md.template +109 -0
- package/templates/project-files/coreai.json.example +47 -0
- package/templates/project-files/mcp.json.template +20 -0
- package/templates/review-complete.md +64 -0
- package/templates/review-request.md +67 -0
- package/templates/task-assignment.md +51 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +26 -0
- package/tsup.config.ts +23 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Command Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { promises as fs } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { tmpdir } from 'os';
|
|
8
|
+
import { build, formatBuildResult } from './build.js';
|
|
9
|
+
|
|
10
|
+
describe('Build Command', () => {
|
|
11
|
+
let testDir: string;
|
|
12
|
+
let agentsDir: string;
|
|
13
|
+
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
testDir = join(tmpdir(), `build-cmd-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
16
|
+
agentsDir = join(testDir, 'coreai', 'agents');
|
|
17
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
try {
|
|
22
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
23
|
+
} catch {
|
|
24
|
+
// Ignore cleanup errors
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('build', () => {
|
|
29
|
+
it('should build without config file', async () => {
|
|
30
|
+
const result = build({
|
|
31
|
+
projectRoot: testDir,
|
|
32
|
+
coreAgentsDir: join(testDir, 'core-agents'), // Non-existent, will use custom only
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(result.success).toBe(true);
|
|
36
|
+
expect(result.warnings).toBeDefined();
|
|
37
|
+
expect(result.warnings?.some((w) => w.includes('No configuration'))).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should build with config file', async () => {
|
|
41
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
42
|
+
await fs.writeFile(
|
|
43
|
+
configPath,
|
|
44
|
+
`version: "1.0"
|
|
45
|
+
project:
|
|
46
|
+
name: test-project
|
|
47
|
+
team:
|
|
48
|
+
agents: []
|
|
49
|
+
`
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const result = build({
|
|
53
|
+
projectRoot: testDir,
|
|
54
|
+
coreAgentsDir: join(testDir, 'core-agents'),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(result.success).toBe(true);
|
|
58
|
+
expect(result.config).toBeDefined();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should report config errors', async () => {
|
|
62
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
63
|
+
await fs.writeFile(configPath, 'invalid: yaml: [');
|
|
64
|
+
|
|
65
|
+
const result = build({
|
|
66
|
+
projectRoot: testDir,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(result.success).toBe(false);
|
|
70
|
+
expect(result.error).toContain('Configuration error');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should build custom agents', async () => {
|
|
74
|
+
// Create a custom agent
|
|
75
|
+
await fs.writeFile(
|
|
76
|
+
join(agentsDir, 'test-agent.yaml'),
|
|
77
|
+
`role: test-agent
|
|
78
|
+
type: ic-engineer
|
|
79
|
+
display_name: Test Agent
|
|
80
|
+
description: A test agent
|
|
81
|
+
responsibilities:
|
|
82
|
+
- Testing
|
|
83
|
+
expertise:
|
|
84
|
+
primary:
|
|
85
|
+
- Testing
|
|
86
|
+
`
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const result = build({
|
|
90
|
+
projectRoot: testDir,
|
|
91
|
+
coreAgentsDir: join(testDir, 'non-existent'),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(result.success).toBe(true);
|
|
95
|
+
expect(result.result?.compiled.length).toBeGreaterThan(0);
|
|
96
|
+
expect(result.result?.compiled.some((a) => a.role === 'test-agent')).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should filter agents when specified', async () => {
|
|
100
|
+
// Create multiple agents
|
|
101
|
+
await fs.writeFile(
|
|
102
|
+
join(agentsDir, 'agent-a.yaml'),
|
|
103
|
+
`role: agent-a
|
|
104
|
+
type: ic-engineer
|
|
105
|
+
display_name: Agent A
|
|
106
|
+
description: Agent A
|
|
107
|
+
responsibilities: []
|
|
108
|
+
expertise:
|
|
109
|
+
primary: []
|
|
110
|
+
`
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
await fs.writeFile(
|
|
114
|
+
join(agentsDir, 'agent-b.yaml'),
|
|
115
|
+
`role: agent-b
|
|
116
|
+
type: ic-engineer
|
|
117
|
+
display_name: Agent B
|
|
118
|
+
description: Agent B
|
|
119
|
+
responsibilities: []
|
|
120
|
+
expertise:
|
|
121
|
+
primary: []
|
|
122
|
+
`
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const result = build({
|
|
126
|
+
projectRoot: testDir,
|
|
127
|
+
coreAgentsDir: join(testDir, 'non-existent'),
|
|
128
|
+
agents: ['agent-a'],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(result.success).toBe(true);
|
|
132
|
+
expect(result.result?.compiled.length).toBe(1);
|
|
133
|
+
expect(result.result?.compiled[0].role).toBe('agent-a');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should use custom output directory', async () => {
|
|
137
|
+
await fs.writeFile(
|
|
138
|
+
join(agentsDir, 'test-agent.yaml'),
|
|
139
|
+
`role: test-agent
|
|
140
|
+
type: ic-engineer
|
|
141
|
+
display_name: Test Agent
|
|
142
|
+
description: A test agent
|
|
143
|
+
responsibilities: []
|
|
144
|
+
expertise:
|
|
145
|
+
primary: []
|
|
146
|
+
`
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const outputDir = join(testDir, 'custom-output');
|
|
150
|
+
|
|
151
|
+
const result = build({
|
|
152
|
+
projectRoot: testDir,
|
|
153
|
+
coreAgentsDir: join(testDir, 'non-existent'),
|
|
154
|
+
outputDir,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(result.success).toBe(true);
|
|
158
|
+
expect(result.result?.compiled[0].outputPath).toContain('custom-output');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('formatBuildResult', () => {
|
|
163
|
+
it('should format success result', () => {
|
|
164
|
+
const result = {
|
|
165
|
+
success: true,
|
|
166
|
+
result: {
|
|
167
|
+
compiled: [
|
|
168
|
+
{ role: 'agent-a', source: 'custom' as const, outputPath: '/path/to/agent-a.md' },
|
|
169
|
+
],
|
|
170
|
+
errors: [],
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const output = formatBuildResult(result);
|
|
175
|
+
|
|
176
|
+
expect(output).toContain('Compiled');
|
|
177
|
+
expect(output).toContain('agent-a');
|
|
178
|
+
expect(output).toContain('Build complete');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should format error result', () => {
|
|
182
|
+
const result = {
|
|
183
|
+
success: false,
|
|
184
|
+
error: 'Build failed',
|
|
185
|
+
result: {
|
|
186
|
+
compiled: [],
|
|
187
|
+
errors: [{ role: 'agent-a', error: 'Invalid definition' }],
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const output = formatBuildResult(result);
|
|
192
|
+
|
|
193
|
+
expect(output).toContain('Build failed');
|
|
194
|
+
expect(output).toContain('agent-a');
|
|
195
|
+
expect(output).toContain('Invalid definition');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should show warnings', () => {
|
|
199
|
+
const result = {
|
|
200
|
+
success: true,
|
|
201
|
+
warnings: ['No config file found'],
|
|
202
|
+
result: {
|
|
203
|
+
compiled: [],
|
|
204
|
+
errors: [],
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const output = formatBuildResult(result);
|
|
209
|
+
|
|
210
|
+
expect(output).toContain('⚠');
|
|
211
|
+
expect(output).toContain('No config file found');
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should group agents by source', () => {
|
|
215
|
+
const result = {
|
|
216
|
+
success: true,
|
|
217
|
+
result: {
|
|
218
|
+
compiled: [
|
|
219
|
+
{ role: 'core-agent', source: 'core' as const, outputPath: '/core.md' },
|
|
220
|
+
{ role: 'custom-agent', source: 'custom' as const, outputPath: '/custom.md' },
|
|
221
|
+
{ role: 'override-agent', source: 'override' as const, outputPath: '/override.md' },
|
|
222
|
+
],
|
|
223
|
+
errors: [],
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const output = formatBuildResult(result);
|
|
228
|
+
|
|
229
|
+
expect(output).toContain('Core agents');
|
|
230
|
+
expect(output).toContain('Custom agents');
|
|
231
|
+
expect(output).toContain('Override agents');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('should handle no agents to compile', () => {
|
|
235
|
+
const result = {
|
|
236
|
+
success: true,
|
|
237
|
+
result: {
|
|
238
|
+
compiled: [],
|
|
239
|
+
errors: [],
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const output = formatBuildResult(result);
|
|
244
|
+
|
|
245
|
+
expect(output).toContain('No agents to compile');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build Command
|
|
3
|
+
*
|
|
4
|
+
* Compiles agent definitions to Claude-compatible markdown files.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { configExists, loadConfig, ConfigError } from '../../config/loader.js';
|
|
8
|
+
import type { ResolvedCoreAIConfig } from '../../config/types.js';
|
|
9
|
+
import { compileAgents, type CompileOptions, type CompileResult } from '../../agents/index.js';
|
|
10
|
+
import { initAgentKnowledgeLibrary } from '../../knowledge-library/index.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Options for build command
|
|
14
|
+
*/
|
|
15
|
+
export interface BuildCommandOptions {
|
|
16
|
+
/**
|
|
17
|
+
* Project root directory
|
|
18
|
+
*/
|
|
19
|
+
projectRoot?: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Path to core agents directory
|
|
23
|
+
*/
|
|
24
|
+
coreAgentsDir?: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Output directory for compiled agents
|
|
28
|
+
*/
|
|
29
|
+
outputDir?: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Watch for changes (not yet implemented)
|
|
33
|
+
*/
|
|
34
|
+
watch?: boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Only compile specific agents
|
|
38
|
+
*/
|
|
39
|
+
agents?: string[];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Skip validation before build
|
|
43
|
+
*/
|
|
44
|
+
skipValidation?: boolean;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Initialize KnowledgeLibrary directories for agents
|
|
48
|
+
*/
|
|
49
|
+
initKnowledgeLibrary?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Result of build command
|
|
54
|
+
*/
|
|
55
|
+
export interface BuildCommandResult {
|
|
56
|
+
success: boolean;
|
|
57
|
+
result?: CompileResult;
|
|
58
|
+
config?: ResolvedCoreAIConfig;
|
|
59
|
+
error?: string;
|
|
60
|
+
warnings?: string[];
|
|
61
|
+
knowledgeLibraryInitialized?: string[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build agents
|
|
66
|
+
*/
|
|
67
|
+
export function build(options: BuildCommandOptions = {}): BuildCommandResult {
|
|
68
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
69
|
+
const warnings: string[] = [];
|
|
70
|
+
|
|
71
|
+
// Load config if available
|
|
72
|
+
let config: ResolvedCoreAIConfig | undefined;
|
|
73
|
+
if (configExists(projectRoot)) {
|
|
74
|
+
try {
|
|
75
|
+
config = loadConfig(projectRoot);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof ConfigError) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: `Configuration error: ${error.message}`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
error: `Failed to load config: ${error instanceof Error ? error.message : String(error)}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
warnings.push('No configuration file found. Building with defaults.');
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Set up compile options
|
|
93
|
+
const compileOptions: CompileOptions = {
|
|
94
|
+
projectRoot,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
if (options.coreAgentsDir) {
|
|
98
|
+
compileOptions.coreAgentsDir = options.coreAgentsDir;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (options.outputDir) {
|
|
102
|
+
compileOptions.outputDir = options.outputDir;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Filter to specific agents if requested
|
|
106
|
+
if (options.agents && options.agents.length > 0) {
|
|
107
|
+
const agentsList = options.agents;
|
|
108
|
+
compileOptions.filter = (agent) => agentsList.includes(agent.role);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Compile agents
|
|
112
|
+
try {
|
|
113
|
+
const result = compileAgents(config, compileOptions);
|
|
114
|
+
|
|
115
|
+
// Check for errors
|
|
116
|
+
if (result.errors.length > 0) {
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
result,
|
|
120
|
+
config,
|
|
121
|
+
error: `Failed to compile ${result.errors.length} agent(s)`,
|
|
122
|
+
warnings,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Initialize KnowledgeLibrary for agents if requested
|
|
127
|
+
let knowledgeLibraryInitialized: string[] | undefined;
|
|
128
|
+
if (options.initKnowledgeLibrary) {
|
|
129
|
+
knowledgeLibraryInitialized = [];
|
|
130
|
+
for (const compiled of result.compiled) {
|
|
131
|
+
const initResult = initAgentKnowledgeLibrary(compiled.role, {
|
|
132
|
+
projectRoot,
|
|
133
|
+
createDefaults: true,
|
|
134
|
+
});
|
|
135
|
+
if (initResult.success && initResult.createdDirs.length > 0) {
|
|
136
|
+
knowledgeLibraryInitialized.push(compiled.role);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
success: true,
|
|
143
|
+
result,
|
|
144
|
+
config,
|
|
145
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
146
|
+
knowledgeLibraryInitialized,
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: `Build failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
152
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Format build result for display
|
|
159
|
+
*/
|
|
160
|
+
export function formatBuildResult(result: BuildCommandResult): string {
|
|
161
|
+
const lines: string[] = [];
|
|
162
|
+
|
|
163
|
+
// Warnings
|
|
164
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
165
|
+
for (const warning of result.warnings) {
|
|
166
|
+
lines.push(`⚠ ${warning}`);
|
|
167
|
+
}
|
|
168
|
+
lines.push('');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!result.success) {
|
|
172
|
+
lines.push(`Build failed: ${result.error}`);
|
|
173
|
+
|
|
174
|
+
// Show individual errors
|
|
175
|
+
if (result.result?.errors && result.result.errors.length > 0) {
|
|
176
|
+
lines.push('');
|
|
177
|
+
for (const error of result.result.errors) {
|
|
178
|
+
lines.push(` ✗ ${error.role}: ${error.error}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return lines.join('\n');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const compileResult = result.result;
|
|
186
|
+
if (!compileResult) {
|
|
187
|
+
lines.push('No agents to compile.');
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (compileResult.compiled.length === 0) {
|
|
192
|
+
lines.push('No agents to compile.');
|
|
193
|
+
return lines.join('\n');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
lines.push(`Compiled ${compileResult.compiled.length} agent(s):\n`);
|
|
197
|
+
|
|
198
|
+
// Group by source
|
|
199
|
+
const coreAgents = compileResult.compiled.filter((a) => a.source === 'core');
|
|
200
|
+
const customAgents = compileResult.compiled.filter((a) => a.source === 'custom');
|
|
201
|
+
const overrideAgents = compileResult.compiled.filter((a) => a.source === 'override');
|
|
202
|
+
|
|
203
|
+
if (coreAgents.length > 0) {
|
|
204
|
+
lines.push('Core agents:');
|
|
205
|
+
for (const agent of coreAgents) {
|
|
206
|
+
lines.push(` ✓ ${agent.role}`);
|
|
207
|
+
lines.push(` → ${agent.outputPath}`);
|
|
208
|
+
}
|
|
209
|
+
lines.push('');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (customAgents.length > 0) {
|
|
213
|
+
lines.push('Custom agents:');
|
|
214
|
+
for (const agent of customAgents) {
|
|
215
|
+
lines.push(` ✓ ${agent.role}`);
|
|
216
|
+
lines.push(` → ${agent.outputPath}`);
|
|
217
|
+
}
|
|
218
|
+
lines.push('');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (overrideAgents.length > 0) {
|
|
222
|
+
lines.push('Override agents:');
|
|
223
|
+
for (const agent of overrideAgents) {
|
|
224
|
+
lines.push(` ✓ ${agent.role} (overrides core)`);
|
|
225
|
+
lines.push(` → ${agent.outputPath}`);
|
|
226
|
+
}
|
|
227
|
+
lines.push('');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// KnowledgeLibrary initialization
|
|
231
|
+
if (result.knowledgeLibraryInitialized && result.knowledgeLibraryInitialized.length > 0) {
|
|
232
|
+
lines.push(
|
|
233
|
+
`KnowledgeLibrary initialized for ${result.knowledgeLibraryInitialized.length} agent(s):`
|
|
234
|
+
);
|
|
235
|
+
for (const agent of result.knowledgeLibraryInitialized) {
|
|
236
|
+
lines.push(` ✓ ${agent}`);
|
|
237
|
+
}
|
|
238
|
+
lines.push('');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
lines.push('Build complete!');
|
|
242
|
+
|
|
243
|
+
return lines.join('\n');
|
|
244
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Commands Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { promises as fs } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { tmpdir } from 'os';
|
|
8
|
+
import { FileCacheProvider } from '../../cache/provider.js';
|
|
9
|
+
import {
|
|
10
|
+
cacheStatus,
|
|
11
|
+
cacheClear,
|
|
12
|
+
cacheClearExpired,
|
|
13
|
+
formatCacheStatus,
|
|
14
|
+
formatBytes,
|
|
15
|
+
} from './cache.js';
|
|
16
|
+
|
|
17
|
+
describe('Cache Commands', () => {
|
|
18
|
+
let testDir: string;
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
testDir = join(tmpdir(), `cache-cmd-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
22
|
+
await fs.mkdir(testDir, { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
try {
|
|
27
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
28
|
+
} catch {
|
|
29
|
+
// Ignore cleanup errors
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('cacheStatus', () => {
|
|
34
|
+
it('should return initialized for new directory', async () => {
|
|
35
|
+
// Cache auto-initializes when called
|
|
36
|
+
const result = await cacheStatus({ cachePath: join(testDir, 'new-cache') });
|
|
37
|
+
|
|
38
|
+
expect(result.initialized).toBe(true);
|
|
39
|
+
expect(result.stats).not.toBeNull();
|
|
40
|
+
expect(result.entries).toHaveLength(0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should return status for initialized cache', async () => {
|
|
44
|
+
// Initialize cache with some entries
|
|
45
|
+
const provider = new FileCacheProvider({ basePath: testDir });
|
|
46
|
+
await provider.initialize();
|
|
47
|
+
await provider.set('test-key', 'test content', {
|
|
48
|
+
source: 'github',
|
|
49
|
+
sourceUrl: 'https://github.com/test',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const result = await cacheStatus({ cachePath: testDir });
|
|
53
|
+
|
|
54
|
+
expect(result.initialized).toBe(true);
|
|
55
|
+
expect(result.stats).not.toBeNull();
|
|
56
|
+
expect(result.stats?.totalEntries).toBe(1);
|
|
57
|
+
expect(result.entries).toHaveLength(1);
|
|
58
|
+
expect(result.entries[0].key).toBe('test-key');
|
|
59
|
+
expect(result.entries[0].source).toBe('github');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should include entry status', async () => {
|
|
63
|
+
const provider = new FileCacheProvider({ basePath: testDir });
|
|
64
|
+
await provider.initialize();
|
|
65
|
+
await provider.set(
|
|
66
|
+
'valid-entry',
|
|
67
|
+
'content',
|
|
68
|
+
{ source: 'local', sourceUrl: '' },
|
|
69
|
+
{ ttl: 3600 }
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const result = await cacheStatus({ cachePath: testDir });
|
|
73
|
+
|
|
74
|
+
expect(result.entries[0].status).toBe('valid');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('cacheClear', () => {
|
|
79
|
+
it('should clear all cache entries', async () => {
|
|
80
|
+
const provider = new FileCacheProvider({ basePath: testDir });
|
|
81
|
+
await provider.initialize();
|
|
82
|
+
await provider.set('key1', 'content1', { source: 'local', sourceUrl: '' });
|
|
83
|
+
await provider.set('key2', 'content2', { source: 'local', sourceUrl: '' });
|
|
84
|
+
|
|
85
|
+
const result = await cacheClear({ cachePath: testDir });
|
|
86
|
+
|
|
87
|
+
expect(result.success).toBe(true);
|
|
88
|
+
expect(result.cleared).toBe(2);
|
|
89
|
+
|
|
90
|
+
// Verify cache is empty
|
|
91
|
+
const status = await cacheStatus({ cachePath: testDir });
|
|
92
|
+
expect(status.entries).toHaveLength(0);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should handle empty cache', async () => {
|
|
96
|
+
const provider = new FileCacheProvider({ basePath: testDir });
|
|
97
|
+
await provider.initialize();
|
|
98
|
+
|
|
99
|
+
const result = await cacheClear({ cachePath: testDir });
|
|
100
|
+
|
|
101
|
+
expect(result.success).toBe(true);
|
|
102
|
+
expect(result.cleared).toBe(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should handle uninitialized cache directory', async () => {
|
|
106
|
+
const result = await cacheClear({ cachePath: join(testDir, 'new-cache') });
|
|
107
|
+
|
|
108
|
+
expect(result.success).toBe(true);
|
|
109
|
+
expect(result.cleared).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('cacheClearExpired', () => {
|
|
114
|
+
it('should only clear expired entries', async () => {
|
|
115
|
+
const provider = new FileCacheProvider({ basePath: testDir });
|
|
116
|
+
await provider.initialize();
|
|
117
|
+
|
|
118
|
+
// Add a valid entry
|
|
119
|
+
await provider.set('valid', 'content', { source: 'local', sourceUrl: '' }, { ttl: 3600 });
|
|
120
|
+
|
|
121
|
+
// Add an "expired" entry by manipulating the metadata
|
|
122
|
+
await provider.set('expired', 'content', { source: 'local', sourceUrl: '' }, { ttl: 1 });
|
|
123
|
+
|
|
124
|
+
// Wait for expiration
|
|
125
|
+
await new Promise((resolve) => setTimeout(resolve, 1100));
|
|
126
|
+
|
|
127
|
+
const result = await cacheClearExpired({ cachePath: testDir });
|
|
128
|
+
|
|
129
|
+
expect(result.success).toBe(true);
|
|
130
|
+
expect(result.cleared).toBe(1);
|
|
131
|
+
|
|
132
|
+
// Valid entry should still exist
|
|
133
|
+
const status = await cacheStatus({ cachePath: testDir });
|
|
134
|
+
expect(status.entries.map((e) => e.key)).toContain('valid');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('formatBytes', () => {
|
|
139
|
+
it('should format bytes correctly', () => {
|
|
140
|
+
expect(formatBytes(0)).toBe('0 B');
|
|
141
|
+
expect(formatBytes(100)).toBe('100 B');
|
|
142
|
+
expect(formatBytes(1024)).toBe('1 KB');
|
|
143
|
+
expect(formatBytes(1536)).toBe('1.5 KB');
|
|
144
|
+
expect(formatBytes(1048576)).toBe('1 MB');
|
|
145
|
+
expect(formatBytes(1073741824)).toBe('1 GB');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('formatCacheStatus', () => {
|
|
150
|
+
it('should format uninitialized cache', () => {
|
|
151
|
+
const result = formatCacheStatus({
|
|
152
|
+
initialized: false,
|
|
153
|
+
path: '/test/cache',
|
|
154
|
+
stats: null,
|
|
155
|
+
entries: [],
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(result).toContain('Not initialized');
|
|
159
|
+
expect(result).toContain('/test/cache');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should format initialized cache with entries', () => {
|
|
163
|
+
const result = formatCacheStatus({
|
|
164
|
+
initialized: true,
|
|
165
|
+
path: '/test/cache',
|
|
166
|
+
stats: {
|
|
167
|
+
totalEntries: 2,
|
|
168
|
+
totalSize: 1024,
|
|
169
|
+
validEntries: 1,
|
|
170
|
+
staleEntries: 1,
|
|
171
|
+
expiredEntries: 0,
|
|
172
|
+
bySource: { github: 2, confluence: 0, notion: 0, local: 0, custom: 0 },
|
|
173
|
+
newestEntry: '2024-01-01T00:00:00Z',
|
|
174
|
+
},
|
|
175
|
+
entries: [
|
|
176
|
+
{
|
|
177
|
+
key: 'entry1',
|
|
178
|
+
source: 'github',
|
|
179
|
+
status: 'valid',
|
|
180
|
+
size: 512,
|
|
181
|
+
cachedAt: '2024-01-01T00:00:00Z',
|
|
182
|
+
expiresAt: '2024-01-02T00:00:00Z',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
key: 'entry2',
|
|
186
|
+
source: 'github',
|
|
187
|
+
status: 'stale',
|
|
188
|
+
size: 512,
|
|
189
|
+
cachedAt: '2024-01-01T00:00:00Z',
|
|
190
|
+
expiresAt: '2024-01-02T00:00:00Z',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
expect(result).toContain('Initialized');
|
|
196
|
+
expect(result).toContain('Entries: 2');
|
|
197
|
+
expect(result).toContain('1 KB');
|
|
198
|
+
expect(result).toContain('github');
|
|
199
|
+
expect(result).toContain('entry1');
|
|
200
|
+
expect(result).toContain('entry2');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should format empty cache', () => {
|
|
204
|
+
const result = formatCacheStatus({
|
|
205
|
+
initialized: true,
|
|
206
|
+
path: '/test/cache',
|
|
207
|
+
stats: {
|
|
208
|
+
totalEntries: 0,
|
|
209
|
+
totalSize: 0,
|
|
210
|
+
validEntries: 0,
|
|
211
|
+
staleEntries: 0,
|
|
212
|
+
expiredEntries: 0,
|
|
213
|
+
bySource: { github: 0, confluence: 0, notion: 0, local: 0, custom: 0 },
|
|
214
|
+
},
|
|
215
|
+
entries: [],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(result).toContain('No entries in cache');
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|