@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,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Framework Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the CoreAI command registration and execution system.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ResolvedCoreAIConfig } from '../config/types.js';
|
|
8
|
+
import type { AdapterFactory, AdapterType } from '../adapters/index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Command category for organization
|
|
12
|
+
*/
|
|
13
|
+
export type CommandCategory = 'core' | 'optional' | 'custom';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Integration dependency for a command
|
|
17
|
+
*/
|
|
18
|
+
export interface IntegrationDependency {
|
|
19
|
+
/**
|
|
20
|
+
* Adapter type required
|
|
21
|
+
*/
|
|
22
|
+
type: AdapterType;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Whether this integration is required or optional
|
|
26
|
+
* If required and missing, command won't be registered
|
|
27
|
+
* If optional and missing, command degrades gracefully
|
|
28
|
+
*/
|
|
29
|
+
required: boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Description of what this integration is used for
|
|
33
|
+
*/
|
|
34
|
+
description?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Command metadata parsed from markdown frontmatter
|
|
39
|
+
*/
|
|
40
|
+
export interface CommandMetadata {
|
|
41
|
+
/**
|
|
42
|
+
* Command name (derived from filename)
|
|
43
|
+
*/
|
|
44
|
+
name: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Human-readable description
|
|
48
|
+
*/
|
|
49
|
+
description: string;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Hint for command arguments
|
|
53
|
+
*/
|
|
54
|
+
argumentHint?: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Command category
|
|
58
|
+
*/
|
|
59
|
+
category: CommandCategory;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Integration dependencies
|
|
63
|
+
*/
|
|
64
|
+
dependencies: IntegrationDependency[];
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Path to the source markdown file
|
|
68
|
+
*/
|
|
69
|
+
sourcePath: string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether command is available (all required deps satisfied)
|
|
73
|
+
*/
|
|
74
|
+
available: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Reason command is unavailable (if not available)
|
|
78
|
+
*/
|
|
79
|
+
unavailableReason?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Runtime context available during command execution
|
|
84
|
+
*/
|
|
85
|
+
export interface CommandContext {
|
|
86
|
+
/**
|
|
87
|
+
* Loaded configuration (may be null if no config file)
|
|
88
|
+
*/
|
|
89
|
+
config: ResolvedCoreAIConfig | null;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Adapter factory for accessing integrations
|
|
93
|
+
*/
|
|
94
|
+
adapterFactory: AdapterFactory | null;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Project root directory
|
|
98
|
+
*/
|
|
99
|
+
projectRoot: string;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if an integration is available
|
|
103
|
+
*/
|
|
104
|
+
hasIntegration(type: AdapterType): boolean;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get adapter with graceful handling
|
|
108
|
+
* Returns null if not available instead of throwing
|
|
109
|
+
*/
|
|
110
|
+
getAdapterSafe<T>(type: AdapterType): Promise<T | null>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Base options available to all commands
|
|
115
|
+
*/
|
|
116
|
+
export interface BaseCommandOptions {
|
|
117
|
+
/**
|
|
118
|
+
* Output format
|
|
119
|
+
*/
|
|
120
|
+
format?: 'text' | 'json';
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Verbose output
|
|
124
|
+
*/
|
|
125
|
+
verbose?: boolean;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Project root directory override
|
|
129
|
+
*/
|
|
130
|
+
projectRoot?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Result of command execution
|
|
135
|
+
*/
|
|
136
|
+
export interface CommandResult<T = unknown> {
|
|
137
|
+
/**
|
|
138
|
+
* Whether command completed successfully
|
|
139
|
+
*/
|
|
140
|
+
success: boolean;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Result data (command-specific)
|
|
144
|
+
*/
|
|
145
|
+
data?: T;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Error message if failed
|
|
149
|
+
*/
|
|
150
|
+
error?: string;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Warnings that occurred during execution
|
|
154
|
+
*/
|
|
155
|
+
warnings?: string[];
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Steps that were skipped due to missing integrations
|
|
159
|
+
*/
|
|
160
|
+
skippedSteps?: {
|
|
161
|
+
step: string;
|
|
162
|
+
reason: string;
|
|
163
|
+
}[];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Command handler function signature
|
|
168
|
+
*/
|
|
169
|
+
export type CommandHandler<TOptions = BaseCommandOptions, TResult = unknown> = (
|
|
170
|
+
args: string[],
|
|
171
|
+
options: TOptions,
|
|
172
|
+
context: CommandContext
|
|
173
|
+
) => Promise<CommandResult<TResult>>;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Command definition for programmatic commands
|
|
177
|
+
*/
|
|
178
|
+
export interface CommandDefinition<TOptions = BaseCommandOptions, TResult = unknown> {
|
|
179
|
+
/**
|
|
180
|
+
* Command name
|
|
181
|
+
*/
|
|
182
|
+
name: string;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Human-readable description
|
|
186
|
+
*/
|
|
187
|
+
description: string;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Command category
|
|
191
|
+
*/
|
|
192
|
+
category: CommandCategory;
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Integration dependencies
|
|
196
|
+
*/
|
|
197
|
+
dependencies?: IntegrationDependency[];
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Option definitions for Commander.js
|
|
201
|
+
*/
|
|
202
|
+
options?: CommandOptionDefinition[];
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Positional argument definitions
|
|
206
|
+
*/
|
|
207
|
+
arguments?: CommandArgumentDefinition[];
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Command handler
|
|
211
|
+
*/
|
|
212
|
+
handler: CommandHandler<TOptions, TResult>;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Subcommands (for command groups like 'agents list', 'agents show')
|
|
216
|
+
*/
|
|
217
|
+
subcommands?: CommandDefinition[];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Command option definition
|
|
222
|
+
*/
|
|
223
|
+
export interface CommandOptionDefinition {
|
|
224
|
+
/**
|
|
225
|
+
* Option flags (e.g., '-f, --force')
|
|
226
|
+
*/
|
|
227
|
+
flags: string;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Description
|
|
231
|
+
*/
|
|
232
|
+
description: string;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Default value
|
|
236
|
+
*/
|
|
237
|
+
defaultValue?: unknown;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Whether option is required
|
|
241
|
+
*/
|
|
242
|
+
required?: boolean;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Command argument definition
|
|
247
|
+
*/
|
|
248
|
+
export interface CommandArgumentDefinition {
|
|
249
|
+
/**
|
|
250
|
+
* Argument name
|
|
251
|
+
*/
|
|
252
|
+
name: string;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Description
|
|
256
|
+
*/
|
|
257
|
+
description: string;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Whether argument is required
|
|
261
|
+
*/
|
|
262
|
+
required?: boolean;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Default value
|
|
266
|
+
*/
|
|
267
|
+
defaultValue?: unknown;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Markdown command definition (parsed from file)
|
|
272
|
+
*/
|
|
273
|
+
export interface MarkdownCommand {
|
|
274
|
+
/**
|
|
275
|
+
* Command metadata
|
|
276
|
+
*/
|
|
277
|
+
metadata: CommandMetadata;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Raw markdown content (instructions for agents)
|
|
281
|
+
*/
|
|
282
|
+
content: string;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Parsed sections from markdown
|
|
286
|
+
*/
|
|
287
|
+
sections: {
|
|
288
|
+
title?: string;
|
|
289
|
+
instructions?: string;
|
|
290
|
+
fallbacks?: string;
|
|
291
|
+
outputFormat?: string;
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Command registry entry
|
|
297
|
+
*/
|
|
298
|
+
export interface RegistryEntry {
|
|
299
|
+
/**
|
|
300
|
+
* Command metadata
|
|
301
|
+
*/
|
|
302
|
+
metadata: CommandMetadata;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Command definition (for programmatic commands)
|
|
306
|
+
*/
|
|
307
|
+
definition?: CommandDefinition;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Markdown command (for agent commands)
|
|
311
|
+
*/
|
|
312
|
+
markdownCommand?: MarkdownCommand;
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Source type
|
|
316
|
+
*/
|
|
317
|
+
source: 'programmatic' | 'markdown';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Options for loading commands from directory
|
|
322
|
+
*/
|
|
323
|
+
export interface CommandLoaderOptions {
|
|
324
|
+
/**
|
|
325
|
+
* Directory to load commands from
|
|
326
|
+
*/
|
|
327
|
+
directory: string;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Category to assign to loaded commands
|
|
331
|
+
*/
|
|
332
|
+
category: CommandCategory;
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Recursively search subdirectories
|
|
336
|
+
*/
|
|
337
|
+
recursive?: boolean;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Filter function to include/exclude commands
|
|
341
|
+
*/
|
|
342
|
+
filter?: (metadata: CommandMetadata) => boolean;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Result of loading commands
|
|
347
|
+
*/
|
|
348
|
+
export interface CommandLoadResult {
|
|
349
|
+
/**
|
|
350
|
+
* Successfully loaded commands
|
|
351
|
+
*/
|
|
352
|
+
loaded: MarkdownCommand[];
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Failed to load
|
|
356
|
+
*/
|
|
357
|
+
errors: {
|
|
358
|
+
path: string;
|
|
359
|
+
error: string;
|
|
360
|
+
}[];
|
|
361
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Module
|
|
3
|
+
*
|
|
4
|
+
* Provides configuration loading, validation, and management for CoreAI.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from './types.js';
|
|
8
|
+
export {
|
|
9
|
+
loadConfig,
|
|
10
|
+
loadConfigFromFile,
|
|
11
|
+
findConfigFile,
|
|
12
|
+
parseConfig,
|
|
13
|
+
validateConfig,
|
|
14
|
+
applyDefaults,
|
|
15
|
+
configExists,
|
|
16
|
+
getConfigPath,
|
|
17
|
+
ConfigError,
|
|
18
|
+
type ConfigErrorCode,
|
|
19
|
+
} from './loader.js';
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { tmpdir } from 'os';
|
|
4
|
+
import {
|
|
5
|
+
findConfigFile,
|
|
6
|
+
parseConfig,
|
|
7
|
+
validateConfig,
|
|
8
|
+
applyDefaults,
|
|
9
|
+
loadConfigFromFile,
|
|
10
|
+
configExists,
|
|
11
|
+
getConfigPath,
|
|
12
|
+
ConfigError,
|
|
13
|
+
} from './loader.js';
|
|
14
|
+
import type { CoreAIConfig } from './types.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Helper to check ConfigError with specific code
|
|
18
|
+
*/
|
|
19
|
+
function expectConfigError(fn: () => unknown, code: string): void {
|
|
20
|
+
try {
|
|
21
|
+
fn();
|
|
22
|
+
fail('Expected ConfigError to be thrown');
|
|
23
|
+
} catch (error) {
|
|
24
|
+
expect(error).toBeInstanceOf(ConfigError);
|
|
25
|
+
expect((error as ConfigError).code).toBe(code);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('Config Loader', () => {
|
|
30
|
+
let tempDir: string;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
tempDir = mkdtempSync(join(tmpdir(), 'coreai-test-'));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('findConfigFile', () => {
|
|
41
|
+
it('should find config file in current directory', () => {
|
|
42
|
+
const configPath = join(tempDir, 'coreai.config.yaml');
|
|
43
|
+
writeFileSync(configPath, 'version: "1.0"\nproject:\n name: test');
|
|
44
|
+
|
|
45
|
+
const result = findConfigFile(tempDir);
|
|
46
|
+
expect(result).toBe(configPath);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should find config file with .yml extension', () => {
|
|
50
|
+
const configPath = join(tempDir, 'coreai.config.yml');
|
|
51
|
+
writeFileSync(configPath, 'version: "1.0"\nproject:\n name: test');
|
|
52
|
+
|
|
53
|
+
const result = findConfigFile(tempDir);
|
|
54
|
+
expect(result).toBe(configPath);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should find config file in parent directory', () => {
|
|
58
|
+
const subDir = join(tempDir, 'subdir');
|
|
59
|
+
mkdirSync(subDir);
|
|
60
|
+
const configPath = join(tempDir, 'coreai.config.yaml');
|
|
61
|
+
writeFileSync(configPath, 'version: "1.0"\nproject:\n name: test');
|
|
62
|
+
|
|
63
|
+
const result = findConfigFile(subDir);
|
|
64
|
+
expect(result).toBe(configPath);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should return null when no config file exists in temp dir', () => {
|
|
68
|
+
// Create an isolated deep directory structure to avoid finding project root config
|
|
69
|
+
const deepDir = join(tempDir, 'a', 'b', 'c', 'd', 'e');
|
|
70
|
+
mkdirSync(deepDir, { recursive: true });
|
|
71
|
+
|
|
72
|
+
// findConfigFile walks up, so we test from within tempDir where we control the env
|
|
73
|
+
const result = findConfigFile(deepDir);
|
|
74
|
+
// It might find the project root config, so we just check it doesn't find one in tempDir
|
|
75
|
+
if (result !== null) {
|
|
76
|
+
expect(result.startsWith(tempDir)).toBe(false);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('parseConfig', () => {
|
|
82
|
+
it('should parse valid YAML', () => {
|
|
83
|
+
const yaml = `
|
|
84
|
+
version: "1.0"
|
|
85
|
+
project:
|
|
86
|
+
name: "Test Project"
|
|
87
|
+
type: software
|
|
88
|
+
`;
|
|
89
|
+
const result = parseConfig(yaml);
|
|
90
|
+
expect(result).toEqual({
|
|
91
|
+
version: '1.0',
|
|
92
|
+
project: {
|
|
93
|
+
name: 'Test Project',
|
|
94
|
+
type: 'software',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should throw ConfigError for invalid YAML', () => {
|
|
100
|
+
const invalidYaml = `
|
|
101
|
+
version: "1.0"
|
|
102
|
+
project:
|
|
103
|
+
name: "Test
|
|
104
|
+
invalid: yaml
|
|
105
|
+
`;
|
|
106
|
+
expectConfigError(() => parseConfig(invalidYaml), 'PARSE_ERROR');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('validateConfig', () => {
|
|
111
|
+
it('should validate a minimal valid config', () => {
|
|
112
|
+
const config = {
|
|
113
|
+
version: '1.0',
|
|
114
|
+
project: { name: 'Test' },
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const result = validateConfig(config);
|
|
118
|
+
expect(result).toEqual(config);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should validate a full config', () => {
|
|
122
|
+
const config: CoreAIConfig = {
|
|
123
|
+
version: '1.0',
|
|
124
|
+
project: { name: 'Test', type: 'software' },
|
|
125
|
+
team: { agents: ['backend-engineer'] },
|
|
126
|
+
integrations: {
|
|
127
|
+
git: { provider: 'github', config: { repo: 'org/repo' } },
|
|
128
|
+
issue_tracker: { provider: 'jira', config: { project_key: 'TEST' } },
|
|
129
|
+
},
|
|
130
|
+
quality_gates: {
|
|
131
|
+
lint: { command: 'npm run lint', required: true },
|
|
132
|
+
},
|
|
133
|
+
tech_stack: { primary_language: 'typescript' },
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const result = validateConfig(config);
|
|
137
|
+
expect(result).toEqual(config);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should throw for missing required fields', () => {
|
|
141
|
+
const config = { version: '1.0' }; // missing project
|
|
142
|
+
expectConfigError(() => validateConfig(config), 'VALIDATION_ERROR');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('should throw for invalid version', () => {
|
|
146
|
+
const config = {
|
|
147
|
+
version: '2.0', // invalid version
|
|
148
|
+
project: { name: 'Test' },
|
|
149
|
+
};
|
|
150
|
+
expectConfigError(() => validateConfig(config), 'VALIDATION_ERROR');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should throw for invalid provider', () => {
|
|
154
|
+
const config = {
|
|
155
|
+
version: '1.0',
|
|
156
|
+
project: { name: 'Test' },
|
|
157
|
+
integrations: {
|
|
158
|
+
git: { provider: 'invalid-provider' },
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
expectConfigError(() => validateConfig(config), 'VALIDATION_ERROR');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('applyDefaults', () => {
|
|
166
|
+
it('should apply default project type', () => {
|
|
167
|
+
const config: CoreAIConfig = {
|
|
168
|
+
version: '1.0',
|
|
169
|
+
project: { name: 'Test' },
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const result = applyDefaults(config);
|
|
173
|
+
expect(result.project.type).toBe('software');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should apply default agents', () => {
|
|
177
|
+
const config: CoreAIConfig = {
|
|
178
|
+
version: '1.0',
|
|
179
|
+
project: { name: 'Test' },
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = applyDefaults(config);
|
|
183
|
+
expect(result.team.agents).toEqual([
|
|
184
|
+
'backend-engineer',
|
|
185
|
+
'frontend-engineer',
|
|
186
|
+
'devops-engineer',
|
|
187
|
+
'engineering-manager',
|
|
188
|
+
]);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should preserve explicit values', () => {
|
|
192
|
+
const config: CoreAIConfig = {
|
|
193
|
+
version: '1.0',
|
|
194
|
+
project: { name: 'Test', type: 'infrastructure' },
|
|
195
|
+
team: { agents: ['devops-engineer'] },
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const result = applyDefaults(config);
|
|
199
|
+
expect(result.project.type).toBe('infrastructure');
|
|
200
|
+
expect(result.team.agents).toEqual(['devops-engineer']);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('loadConfigFromFile', () => {
|
|
205
|
+
it('should load and validate a config file', () => {
|
|
206
|
+
const configPath = join(tempDir, 'coreai.config.yaml');
|
|
207
|
+
writeFileSync(
|
|
208
|
+
configPath,
|
|
209
|
+
`
|
|
210
|
+
version: "1.0"
|
|
211
|
+
project:
|
|
212
|
+
name: "Test Project"
|
|
213
|
+
`
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const result = loadConfigFromFile(configPath);
|
|
217
|
+
expect(result.project.name).toBe('Test Project');
|
|
218
|
+
expect(result.project.type).toBe('software'); // default applied
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should throw for non-existent file', () => {
|
|
222
|
+
const configPath = join(tempDir, 'nonexistent.yaml');
|
|
223
|
+
expectConfigError(() => loadConfigFromFile(configPath), 'READ_ERROR');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should throw for invalid config', () => {
|
|
227
|
+
const configPath = join(tempDir, 'coreai.config.yaml');
|
|
228
|
+
writeFileSync(configPath, 'invalid: config');
|
|
229
|
+
expectConfigError(() => loadConfigFromFile(configPath), 'VALIDATION_ERROR');
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('configExists', () => {
|
|
234
|
+
it('should return true when config exists', () => {
|
|
235
|
+
const configPath = join(tempDir, 'coreai.config.yaml');
|
|
236
|
+
writeFileSync(configPath, 'version: "1.0"\nproject:\n name: test');
|
|
237
|
+
|
|
238
|
+
expect(configExists(tempDir)).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should return false when no config in temp directory tree', () => {
|
|
242
|
+
// We can't truly test "no config exists" because findConfigFile walks up
|
|
243
|
+
// to the filesystem root. Instead, test that configExists returns a boolean.
|
|
244
|
+
const result = configExists(tempDir);
|
|
245
|
+
expect(typeof result).toBe('boolean');
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('getConfigPath', () => {
|
|
250
|
+
it('should return path when config exists', () => {
|
|
251
|
+
const configPath = join(tempDir, 'coreai.config.yaml');
|
|
252
|
+
writeFileSync(configPath, 'version: "1.0"\nproject:\n name: test');
|
|
253
|
+
|
|
254
|
+
expect(getConfigPath(tempDir)).toBe(configPath);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should return string or null', () => {
|
|
258
|
+
const result = getConfigPath(tempDir);
|
|
259
|
+
expect(result === null || typeof result === 'string').toBe(true);
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|