@deimoscloud/coreai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/.prettierrc +9 -0
  2. package/AGENT_SPEC.md +347 -0
  3. package/ARCHITECTURE.md +547 -0
  4. package/DRAFT_PRD.md +1440 -0
  5. package/IMPLEMENTATION_PLAN.md +256 -0
  6. package/PRODUCT.md +473 -0
  7. package/README.md +303 -0
  8. package/WORKFLOWS.md +295 -0
  9. package/agents/_templates/ic-engineer.md +185 -0
  10. package/agents/_templates/reviewer.md +182 -0
  11. package/agents/backend-engineer.yaml +72 -0
  12. package/agents/devops-engineer.yaml +72 -0
  13. package/agents/engineering-manager.yaml +70 -0
  14. package/agents/examples/android-engineer.md +302 -0
  15. package/agents/examples/backend-engineer.md +320 -0
  16. package/agents/examples/devops-engineer.md +742 -0
  17. package/agents/examples/engineering-manager.md +469 -0
  18. package/agents/examples/frontend-engineer.md +58 -0
  19. package/agents/examples/product-manager.md +315 -0
  20. package/agents/examples/qa-engineer.md +371 -0
  21. package/agents/examples/security-engineer.md +525 -0
  22. package/agents/examples/solutions-architect.md +351 -0
  23. package/agents/examples/wearos-engineer.md +359 -0
  24. package/agents/frontend-engineer.yaml +72 -0
  25. package/commands/core/check-inbox.md +34 -0
  26. package/commands/core/delegate.md +30 -0
  27. package/commands/core/git-commit.md +144 -0
  28. package/commands/core/pr-create.md +193 -0
  29. package/commands/core/review.md +56 -0
  30. package/commands/core/sprint-status.md +65 -0
  31. package/commands/optional/docs-update.md +200 -0
  32. package/commands/optional/jira-create.md +200 -0
  33. package/commands/optional/jira-transition.md +184 -0
  34. package/commands/optional/worktree-cleanup.md +167 -0
  35. package/commands/optional/worktree-setup.md +110 -0
  36. package/dist/cli/index.js +4037 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/index.d.ts +2978 -0
  39. package/dist/index.js +3867 -0
  40. package/dist/index.js.map +1 -0
  41. package/eslint.config.js +29 -0
  42. package/jest.config.js +22 -0
  43. package/knowledge-library/README.md +118 -0
  44. package/knowledge-library/android-engineer/context/current.txt +42 -0
  45. package/knowledge-library/android-engineer/control/decisions.txt +9 -0
  46. package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
  47. package/knowledge-library/android-engineer/control/objectives.txt +26 -0
  48. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  49. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  50. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  51. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  52. package/knowledge-library/architecture.txt +61 -0
  53. package/knowledge-library/backend-engineer/context/current.txt +42 -0
  54. package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
  55. package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
  56. package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
  57. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  58. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  59. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  60. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  61. package/knowledge-library/context.txt +52 -0
  62. package/knowledge-library/devops-engineer/context/current.txt +42 -0
  63. package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
  64. package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
  65. package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
  66. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  67. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  68. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  69. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  70. package/knowledge-library/engineering-manager/context/current.txt +40 -0
  71. package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
  72. package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
  73. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  74. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  76. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  77. package/knowledge-library/prd.txt +81 -0
  78. package/knowledge-library/product-manager/context/current.txt +42 -0
  79. package/knowledge-library/product-manager/control/decisions.txt +9 -0
  80. package/knowledge-library/product-manager/control/dependencies.txt +19 -0
  81. package/knowledge-library/product-manager/control/objectives.txt +26 -0
  82. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  83. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  84. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  85. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  86. package/knowledge-library/qa-engineer/context/current.txt +42 -0
  87. package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
  88. package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
  89. package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
  90. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  91. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  92. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  93. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  94. package/knowledge-library/security-engineer/context/current.txt +42 -0
  95. package/knowledge-library/security-engineer/control/decisions.txt +9 -0
  96. package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
  97. package/knowledge-library/security-engineer/control/objectives.txt +26 -0
  98. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  99. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  100. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  101. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  102. package/knowledge-library/solutions-architect/context/current.txt +42 -0
  103. package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
  104. package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
  105. package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
  106. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  107. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  108. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  109. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  110. package/knowledge-library/wearos-engineer/context/current.txt +42 -0
  111. package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
  112. package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
  113. package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
  114. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  115. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  116. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  117. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  118. package/package.json +66 -0
  119. package/schemas/agent.schema.json +171 -0
  120. package/schemas/coreai.config.schema.json +257 -0
  121. package/scripts/add-agent.sh +323 -0
  122. package/scripts/install.sh +354 -0
  123. package/src/adapters/factory.test.ts +386 -0
  124. package/src/adapters/factory.ts +305 -0
  125. package/src/adapters/index.ts +113 -0
  126. package/src/adapters/interfaces.ts +268 -0
  127. package/src/adapters/mcp/client.test.ts +130 -0
  128. package/src/adapters/mcp/client.ts +451 -0
  129. package/src/adapters/mcp/discovery.test.ts +315 -0
  130. package/src/adapters/mcp/discovery.ts +340 -0
  131. package/src/adapters/mcp/index.ts +66 -0
  132. package/src/adapters/mcp/mapper.test.ts +218 -0
  133. package/src/adapters/mcp/mapper.ts +536 -0
  134. package/src/adapters/mcp/registry.test.ts +433 -0
  135. package/src/adapters/mcp/registry.ts +550 -0
  136. package/src/adapters/mcp/types.ts +258 -0
  137. package/src/adapters/native/filesystem.test.ts +350 -0
  138. package/src/adapters/native/filesystem.ts +393 -0
  139. package/src/adapters/native/github.test.ts +173 -0
  140. package/src/adapters/native/github.ts +627 -0
  141. package/src/adapters/native/index.ts +22 -0
  142. package/src/adapters/native/selector.test.ts +224 -0
  143. package/src/adapters/native/selector.ts +150 -0
  144. package/src/adapters/types.ts +270 -0
  145. package/src/agents/compiler.test.ts +399 -0
  146. package/src/agents/compiler.ts +359 -0
  147. package/src/agents/index.ts +36 -0
  148. package/src/agents/loader.test.ts +319 -0
  149. package/src/agents/loader.ts +143 -0
  150. package/src/agents/resolver.test.ts +282 -0
  151. package/src/agents/resolver.ts +262 -0
  152. package/src/agents/types.ts +87 -0
  153. package/src/cache/index.ts +38 -0
  154. package/src/cache/interfaces.ts +283 -0
  155. package/src/cache/manager.test.ts +266 -0
  156. package/src/cache/manager.ts +388 -0
  157. package/src/cache/provider.test.ts +485 -0
  158. package/src/cache/provider.ts +745 -0
  159. package/src/cache/types.test.ts +192 -0
  160. package/src/cache/types.ts +313 -0
  161. package/src/cli/commands/build.test.ts +248 -0
  162. package/src/cli/commands/build.ts +244 -0
  163. package/src/cli/commands/cache.test.ts +221 -0
  164. package/src/cli/commands/cache.ts +229 -0
  165. package/src/cli/commands/index.ts +63 -0
  166. package/src/cli/commands/init.test.ts +173 -0
  167. package/src/cli/commands/init.ts +296 -0
  168. package/src/cli/commands/skills.test.ts +272 -0
  169. package/src/cli/commands/skills.ts +348 -0
  170. package/src/cli/commands/status.test.ts +392 -0
  171. package/src/cli/commands/status.ts +332 -0
  172. package/src/cli/commands/sync.test.ts +213 -0
  173. package/src/cli/commands/sync.ts +251 -0
  174. package/src/cli/commands/validate.test.ts +216 -0
  175. package/src/cli/commands/validate.ts +340 -0
  176. package/src/cli/index.test.ts +190 -0
  177. package/src/cli/index.ts +493 -0
  178. package/src/commands/context.test.ts +163 -0
  179. package/src/commands/context.ts +111 -0
  180. package/src/commands/index.ts +56 -0
  181. package/src/commands/loader.test.ts +273 -0
  182. package/src/commands/loader.ts +355 -0
  183. package/src/commands/registry.test.ts +384 -0
  184. package/src/commands/registry.ts +248 -0
  185. package/src/commands/runner.test.ts +297 -0
  186. package/src/commands/runner.ts +222 -0
  187. package/src/commands/types.ts +361 -0
  188. package/src/config/index.ts +19 -0
  189. package/src/config/loader.test.ts +262 -0
  190. package/src/config/loader.ts +188 -0
  191. package/src/config/types.ts +154 -0
  192. package/src/context/index.ts +14 -0
  193. package/src/context/loader.test.ts +334 -0
  194. package/src/context/loader.ts +357 -0
  195. package/src/index.test.ts +13 -0
  196. package/src/index.ts +244 -0
  197. package/src/knowledge-library/index.ts +44 -0
  198. package/src/knowledge-library/manager.test.ts +536 -0
  199. package/src/knowledge-library/manager.ts +804 -0
  200. package/src/knowledge-library/types.ts +432 -0
  201. package/src/skills/generator.test.ts +602 -0
  202. package/src/skills/generator.ts +491 -0
  203. package/src/skills/index.ts +27 -0
  204. package/src/skills/templates.ts +520 -0
  205. package/src/skills/types.ts +251 -0
  206. package/templates/completion-report.md +72 -0
  207. package/templates/feedback.md +56 -0
  208. package/templates/project-files/CLAUDE.md.template +109 -0
  209. package/templates/project-files/coreai.json.example +47 -0
  210. package/templates/project-files/mcp.json.template +20 -0
  211. package/templates/review-complete.md +64 -0
  212. package/templates/review-request.md +67 -0
  213. package/templates/task-assignment.md +51 -0
  214. package/tsconfig.build.json +4 -0
  215. package/tsconfig.json +26 -0
  216. package/tsup.config.ts +23 -0
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Validate Command
3
+ *
4
+ * Validates CoreAI configuration and project setup.
5
+ */
6
+
7
+ import { existsSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { configExists, loadConfig, ConfigError } from '../../config/loader.js';
10
+ import type { ResolvedCoreAIConfig } from '../../config/types.js';
11
+ import { loadAllAgents } from '../../agents/index.js';
12
+
13
+ /**
14
+ * Options for validate command
15
+ */
16
+ export interface ValidateCommandOptions {
17
+ /**
18
+ * Project root directory
19
+ */
20
+ projectRoot?: string;
21
+
22
+ /**
23
+ * Path to core agents directory
24
+ */
25
+ coreAgentsDir?: string;
26
+
27
+ /**
28
+ * Check if configured agents exist
29
+ */
30
+ checkAgents?: boolean;
31
+
32
+ /**
33
+ * Check directory structure
34
+ */
35
+ checkDirs?: boolean;
36
+
37
+ /**
38
+ * Check quality gates commands exist
39
+ */
40
+ checkQualityGates?: boolean;
41
+ }
42
+
43
+ /**
44
+ * Single validation issue
45
+ */
46
+ export interface ValidationIssue {
47
+ level: 'error' | 'warning' | 'info';
48
+ category: 'config' | 'agents' | 'directories' | 'quality_gates' | 'integrations';
49
+ message: string;
50
+ fix?: string;
51
+ }
52
+
53
+ /**
54
+ * Result of validate command
55
+ */
56
+ export interface ValidateCommandResult {
57
+ success: boolean;
58
+ valid: boolean;
59
+ config?: ResolvedCoreAIConfig;
60
+ issues: ValidationIssue[];
61
+ summary: {
62
+ errors: number;
63
+ warnings: number;
64
+ info: number;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Validate configuration file
70
+ */
71
+ function validateConfig(projectRoot: string): {
72
+ config?: ResolvedCoreAIConfig;
73
+ issues: ValidationIssue[];
74
+ } {
75
+ const issues: ValidationIssue[] = [];
76
+
77
+ if (!configExists(projectRoot)) {
78
+ issues.push({
79
+ level: 'error',
80
+ category: 'config',
81
+ message: 'No CoreAI configuration file found',
82
+ fix: 'Run `coreai init` to create a configuration file',
83
+ });
84
+ return { issues };
85
+ }
86
+
87
+ try {
88
+ const config = loadConfig(projectRoot);
89
+ return { config, issues };
90
+ } catch (error) {
91
+ if (error instanceof ConfigError) {
92
+ issues.push({
93
+ level: 'error',
94
+ category: 'config',
95
+ message: `Configuration error: ${error.message}`,
96
+ fix: 'Check coreai.config.yaml for syntax errors',
97
+ });
98
+ } else {
99
+ issues.push({
100
+ level: 'error',
101
+ category: 'config',
102
+ message: `Failed to load config: ${error instanceof Error ? error.message : String(error)}`,
103
+ });
104
+ }
105
+ return { issues };
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Validate configured agents exist
111
+ */
112
+ function validateAgents(
113
+ config: ResolvedCoreAIConfig,
114
+ coreAgentsDir: string,
115
+ projectRoot: string
116
+ ): ValidationIssue[] {
117
+ const issues: ValidationIssue[] = [];
118
+ const customAgentsDir = join(projectRoot, 'coreai', 'agents');
119
+
120
+ // Load available agents
121
+ let availableAgents: Map<string, unknown>;
122
+ try {
123
+ availableAgents = loadAllAgents({
124
+ coreAgentsDir,
125
+ customAgentsDir,
126
+ });
127
+ } catch (error) {
128
+ issues.push({
129
+ level: 'warning',
130
+ category: 'agents',
131
+ message: `Could not load agents: ${error instanceof Error ? error.message : String(error)}`,
132
+ });
133
+ return issues;
134
+ }
135
+
136
+ // Check each configured agent
137
+ const configuredAgents = config.team.agents;
138
+ for (const agentRole of configuredAgents) {
139
+ if (!availableAgents.has(agentRole)) {
140
+ issues.push({
141
+ level: 'error',
142
+ category: 'agents',
143
+ message: `Configured agent '${agentRole}' not found`,
144
+ fix: `Add agent definition to coreai/agents/${agentRole}.yaml or remove from config`,
145
+ });
146
+ }
147
+ }
148
+
149
+ // Info about available but not configured agents
150
+ const unconfigured = Array.from(availableAgents.keys()).filter(
151
+ (role) => !configuredAgents.includes(role)
152
+ );
153
+ if (unconfigured.length > 0) {
154
+ issues.push({
155
+ level: 'info',
156
+ category: 'agents',
157
+ message: `${unconfigured.length} available agent(s) not configured: ${unconfigured.join(', ')}`,
158
+ });
159
+ }
160
+
161
+ return issues;
162
+ }
163
+
164
+ /**
165
+ * Validate directory structure
166
+ */
167
+ function validateDirectories(projectRoot: string): ValidationIssue[] {
168
+ const issues: ValidationIssue[] = [];
169
+
170
+ const requiredDirs = [{ path: 'coreai', description: 'CoreAI configuration directory' }];
171
+
172
+ const optionalDirs = [
173
+ { path: 'coreai/agents', description: 'Custom agent definitions' },
174
+ { path: 'coreai/commands', description: 'Custom command definitions' },
175
+ { path: '.coreai', description: 'CoreAI runtime directory' },
176
+ { path: '.coreai/cache', description: 'Cache directory' },
177
+ { path: '.claude/agents', description: 'Compiled agents output' },
178
+ ];
179
+
180
+ // Check required directories
181
+ for (const dir of requiredDirs) {
182
+ const fullPath = join(projectRoot, dir.path);
183
+ if (!existsSync(fullPath)) {
184
+ issues.push({
185
+ level: 'warning',
186
+ category: 'directories',
187
+ message: `Missing directory: ${dir.path} (${dir.description})`,
188
+ fix: `Run mkdir -p ${dir.path}`,
189
+ });
190
+ }
191
+ }
192
+
193
+ // Check optional directories (info only)
194
+ for (const dir of optionalDirs) {
195
+ const fullPath = join(projectRoot, dir.path);
196
+ if (!existsSync(fullPath)) {
197
+ issues.push({
198
+ level: 'info',
199
+ category: 'directories',
200
+ message: `Optional directory not found: ${dir.path} (${dir.description})`,
201
+ });
202
+ }
203
+ }
204
+
205
+ return issues;
206
+ }
207
+
208
+ /**
209
+ * Validate integrations configuration
210
+ */
211
+ function validateIntegrations(config: ResolvedCoreAIConfig): ValidationIssue[] {
212
+ const issues: ValidationIssue[] = [];
213
+
214
+ if (!config.integrations) {
215
+ issues.push({
216
+ level: 'info',
217
+ category: 'integrations',
218
+ message: 'No integrations configured',
219
+ });
220
+ return issues;
221
+ }
222
+
223
+ // Check git integration
224
+ if (config.integrations.git) {
225
+ const git = config.integrations.git;
226
+ if (!git.config?.owner || !git.config?.repo) {
227
+ issues.push({
228
+ level: 'warning',
229
+ category: 'integrations',
230
+ message: 'Git integration configured but owner/repo not specified',
231
+ fix: 'Add integrations.git.config.owner and integrations.git.config.repo to config',
232
+ });
233
+ }
234
+ }
235
+
236
+ // Check issue tracker
237
+ if (config.integrations.issue_tracker) {
238
+ const tracker = config.integrations.issue_tracker;
239
+ if (tracker.provider === 'jira' && !tracker.config?.base_url) {
240
+ issues.push({
241
+ level: 'warning',
242
+ category: 'integrations',
243
+ message: 'Jira integration configured but base_url not specified',
244
+ fix: 'Add integrations.issue_tracker.config.base_url to config',
245
+ });
246
+ }
247
+ }
248
+
249
+ return issues;
250
+ }
251
+
252
+ /**
253
+ * Validate the CoreAI project
254
+ */
255
+ export function validate(options: ValidateCommandOptions = {}): ValidateCommandResult {
256
+ const projectRoot = options.projectRoot ?? process.cwd();
257
+ const coreAgentsDir =
258
+ options.coreAgentsDir ?? join(projectRoot, 'node_modules', '@coreai', 'cli', 'agents');
259
+ const checkAgents = options.checkAgents ?? true;
260
+ const checkDirs = options.checkDirs ?? true;
261
+
262
+ const allIssues: ValidationIssue[] = [];
263
+
264
+ // Validate config
265
+ const { config, issues: configIssues } = validateConfig(projectRoot);
266
+ allIssues.push(...configIssues);
267
+
268
+ // If config loaded successfully, validate other aspects
269
+ if (config) {
270
+ // Validate agents
271
+ if (checkAgents) {
272
+ allIssues.push(...validateAgents(config, coreAgentsDir, projectRoot));
273
+ }
274
+
275
+ // Validate integrations
276
+ allIssues.push(...validateIntegrations(config));
277
+ }
278
+
279
+ // Validate directories (independent of config)
280
+ if (checkDirs) {
281
+ allIssues.push(...validateDirectories(projectRoot));
282
+ }
283
+
284
+ // Calculate summary
285
+ const summary = {
286
+ errors: allIssues.filter((i) => i.level === 'error').length,
287
+ warnings: allIssues.filter((i) => i.level === 'warning').length,
288
+ info: allIssues.filter((i) => i.level === 'info').length,
289
+ };
290
+
291
+ return {
292
+ success: true,
293
+ valid: summary.errors === 0,
294
+ config,
295
+ issues: allIssues,
296
+ summary,
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Format validate result for display
302
+ */
303
+ export function formatValidateResult(result: ValidateCommandResult): string {
304
+ const lines: string[] = [];
305
+
306
+ // Header
307
+ if (result.valid) {
308
+ lines.push('✓ Configuration is valid\n');
309
+ } else {
310
+ lines.push('✗ Configuration has errors\n');
311
+ }
312
+
313
+ // Group issues by category
314
+ const categories = ['config', 'agents', 'integrations', 'directories', 'quality_gates'] as const;
315
+
316
+ for (const category of categories) {
317
+ const categoryIssues = result.issues.filter((i) => i.category === category);
318
+ if (categoryIssues.length === 0) continue;
319
+
320
+ const categoryLabel = category.charAt(0).toUpperCase() + category.slice(1).replace('_', ' ');
321
+ lines.push(`${categoryLabel}:`);
322
+
323
+ for (const issue of categoryIssues) {
324
+ const prefix = issue.level === 'error' ? ' ✗' : issue.level === 'warning' ? ' ⚠' : ' ℹ';
325
+ lines.push(`${prefix} ${issue.message}`);
326
+ if (issue.fix) {
327
+ lines.push(` → ${issue.fix}`);
328
+ }
329
+ }
330
+ lines.push('');
331
+ }
332
+
333
+ // Summary
334
+ lines.push('Summary:');
335
+ lines.push(` Errors: ${result.summary.errors}`);
336
+ lines.push(` Warnings: ${result.summary.warnings}`);
337
+ lines.push(` Info: ${result.summary.info}`);
338
+
339
+ return lines.join('\n');
340
+ }
@@ -0,0 +1,190 @@
1
+ import { execSync } from 'child_process';
2
+ import { join } from 'path';
3
+ import { mkdtempSync, rmSync, existsSync, readFileSync } from 'fs';
4
+ import { tmpdir } from 'os';
5
+
6
+ const CLI_PATH = join(process.cwd(), 'dist', 'cli', 'index.js');
7
+
8
+ function runCli(args: string, cwd?: string): string {
9
+ return execSync(`node ${CLI_PATH} ${args}`, {
10
+ encoding: 'utf-8',
11
+ cwd: cwd ?? process.cwd(),
12
+ });
13
+ }
14
+
15
+ function runCliWithError(args: string, cwd?: string): { stdout: string; stderr: string } {
16
+ try {
17
+ const stdout = execSync(`node ${CLI_PATH} ${args}`, {
18
+ encoding: 'utf-8',
19
+ cwd: cwd ?? process.cwd(),
20
+ stdio: ['pipe', 'pipe', 'pipe'],
21
+ });
22
+ return { stdout, stderr: '' };
23
+ } catch (error) {
24
+ const e = error as { stdout?: string; stderr?: string };
25
+ return { stdout: e.stdout ?? '', stderr: e.stderr ?? '' };
26
+ }
27
+ }
28
+
29
+ describe('CoreAI CLI', () => {
30
+ describe('--version', () => {
31
+ it('should output the version number', () => {
32
+ const output = runCli('--version');
33
+ expect(output.trim()).toBe('0.1.0');
34
+ });
35
+ });
36
+
37
+ describe('--help', () => {
38
+ it('should display help information', () => {
39
+ const output = runCli('--help');
40
+ expect(output).toContain('coreai');
41
+ expect(output).toContain('A configurable, team-ready AI agent orchestration platform');
42
+ });
43
+
44
+ it('should list all commands', () => {
45
+ const output = runCli('--help');
46
+ expect(output).toContain('init');
47
+ expect(output).toContain('build');
48
+ expect(output).toContain('sync');
49
+ expect(output).toContain('validate');
50
+ expect(output).toContain('agents');
51
+ expect(output).toContain('cache');
52
+ expect(output).toContain('status');
53
+ });
54
+ });
55
+
56
+ describe('init command', () => {
57
+ it('should have --force option', () => {
58
+ const output = runCli('init --help');
59
+ expect(output).toContain('--force');
60
+ expect(output).toContain('overwrite existing configuration');
61
+ });
62
+ });
63
+
64
+ describe('build command', () => {
65
+ let tempDir: string;
66
+
67
+ beforeEach(() => {
68
+ tempDir = mkdtempSync(join(tmpdir(), 'coreai-cli-build-test-'));
69
+ });
70
+
71
+ afterEach(() => {
72
+ rmSync(tempDir, { recursive: true, force: true });
73
+ });
74
+
75
+ it('should have --watch option', () => {
76
+ const output = runCli('build --help');
77
+ expect(output).toContain('--watch');
78
+ });
79
+
80
+ it('should have --output option', () => {
81
+ const output = runCli('build --help');
82
+ expect(output).toContain('--output');
83
+ });
84
+
85
+ it('should compile agents to output directory', () => {
86
+ const outputDir = join(tempDir, 'output');
87
+ const output = runCli(`build --output ${outputDir}`, tempDir);
88
+
89
+ expect(output).toContain('Building agents');
90
+ expect(output).toContain('Compiled');
91
+ expect(output).toContain('backend-engineer');
92
+ expect(output).toContain('Build complete');
93
+
94
+ // Verify files were created
95
+ expect(existsSync(join(outputDir, 'backend-engineer.md'))).toBe(true);
96
+ expect(existsSync(join(outputDir, 'frontend-engineer.md'))).toBe(true);
97
+ expect(existsSync(join(outputDir, 'devops-engineer.md'))).toBe(true);
98
+ expect(existsSync(join(outputDir, 'engineering-manager.md'))).toBe(true);
99
+ });
100
+
101
+ it('should generate valid markdown', () => {
102
+ const outputDir = join(tempDir, 'output');
103
+ runCli(`build --output ${outputDir}`, tempDir);
104
+
105
+ const content = readFileSync(join(outputDir, 'backend-engineer.md'), 'utf-8');
106
+ expect(content).toContain('# Backend Engineer');
107
+ expect(content).toContain('## Description');
108
+ expect(content).toContain('*Generated by CoreAI*');
109
+ });
110
+ });
111
+
112
+ describe('agents subcommand', () => {
113
+ it('should list subcommands', () => {
114
+ const output = runCli('agents --help');
115
+ expect(output).toContain('list');
116
+ expect(output).toContain('show');
117
+ expect(output).toContain('<name>');
118
+ });
119
+
120
+ describe('agents list', () => {
121
+ it('should list core agents', () => {
122
+ const output = runCli('agents list');
123
+ expect(output).toContain('backend-engineer');
124
+ expect(output).toContain('frontend-engineer');
125
+ expect(output).toContain('devops-engineer');
126
+ expect(output).toContain('engineering-manager');
127
+ });
128
+
129
+ it('should show agent types', () => {
130
+ const output = runCli('agents list');
131
+ expect(output).toContain('ic-engineer');
132
+ expect(output).toContain('specialist');
133
+ expect(output).toContain('manager');
134
+ });
135
+
136
+ it('should have --core option', () => {
137
+ const output = runCli('agents list --help');
138
+ expect(output).toContain('--core');
139
+ });
140
+
141
+ it('should have --custom option', () => {
142
+ const output = runCli('agents list --help');
143
+ expect(output).toContain('--custom');
144
+ });
145
+ });
146
+
147
+ describe('agents show', () => {
148
+ it('should show agent details', () => {
149
+ const output = runCli('agents show backend-engineer');
150
+ expect(output).toContain('Backend Engineer');
151
+ expect(output).toContain('backend-engineer');
152
+ expect(output).toContain('ic-engineer');
153
+ expect(output).toContain('Description:');
154
+ });
155
+
156
+ it('should show agent responsibilities', () => {
157
+ const output = runCli('agents show backend-engineer');
158
+ expect(output).toContain('Responsibilities:');
159
+ });
160
+
161
+ it('should support --json output', () => {
162
+ const output = runCli('agents show backend-engineer --json');
163
+ const agent = JSON.parse(output);
164
+ expect(agent.role).toBe('backend-engineer');
165
+ expect(agent.type).toBe('ic-engineer');
166
+ });
167
+
168
+ it('should support --markdown output', () => {
169
+ const output = runCli('agents show backend-engineer --markdown');
170
+ expect(output).toContain('# Backend Engineer');
171
+ expect(output).toContain('*Generated by CoreAI*');
172
+ });
173
+
174
+ it('should error for unknown agent', () => {
175
+ const result = runCliWithError('agents show unknown-agent');
176
+ // Error goes to stderr, available agents list goes to stdout
177
+ expect(result.stderr).toContain('Agent not found');
178
+ expect(result.stdout).toContain('Available agents');
179
+ });
180
+ });
181
+ });
182
+
183
+ describe('cache subcommand', () => {
184
+ it('should list subcommands', () => {
185
+ const output = runCli('cache --help');
186
+ expect(output).toContain('clear');
187
+ expect(output).toContain('status');
188
+ });
189
+ });
190
+ });