@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,355 @@
1
+ /**
2
+ * Command Loader
3
+ *
4
+ * Loads commands from markdown files in the coreai/commands/ directory.
5
+ */
6
+
7
+ import { promises as fs } from 'fs';
8
+ import { join, basename, dirname, relative } from 'path';
9
+ import type {
10
+ CommandMetadata,
11
+ MarkdownCommand,
12
+ CommandCategory,
13
+ IntegrationDependency,
14
+ CommandLoaderOptions,
15
+ CommandLoadResult,
16
+ } from './types.js';
17
+ import type { AdapterType } from '../adapters/types.js';
18
+
19
+ /**
20
+ * Frontmatter data from markdown file
21
+ */
22
+ interface CommandFrontmatter {
23
+ description?: string;
24
+ 'argument-hint'?: string;
25
+ requires?: string[];
26
+ optional?: string[];
27
+ }
28
+
29
+ /**
30
+ * Parse YAML-like frontmatter from markdown content
31
+ */
32
+ function parseFrontmatter(content: string): { frontmatter: CommandFrontmatter; body: string } {
33
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
34
+
35
+ if (!match) {
36
+ return { frontmatter: {}, body: content };
37
+ }
38
+
39
+ const frontmatterText = match[1] ?? '';
40
+ const body = match[2] ?? '';
41
+
42
+ // Simple YAML-like parsing (key: value)
43
+ const frontmatter: CommandFrontmatter = {};
44
+ const lines = frontmatterText.split(/\r?\n/);
45
+
46
+ for (const line of lines) {
47
+ const colonIndex = line.indexOf(':');
48
+ if (colonIndex === -1) continue;
49
+
50
+ const key = line.slice(0, colonIndex).trim();
51
+ let value = line.slice(colonIndex + 1).trim();
52
+
53
+ // Handle arrays (simple format: [item1, item2])
54
+ if (value.startsWith('[') && value.endsWith(']')) {
55
+ const arrayContent = value.slice(1, -1);
56
+ const items = arrayContent.split(',').map((s) => s.trim().replace(/['"]/g, ''));
57
+ (frontmatter as Record<string, unknown>)[key] = items;
58
+ } else {
59
+ // Remove quotes if present
60
+ if (
61
+ (value.startsWith('"') && value.endsWith('"')) ||
62
+ (value.startsWith("'") && value.endsWith("'"))
63
+ ) {
64
+ value = value.slice(1, -1);
65
+ }
66
+ (frontmatter as Record<string, unknown>)[key] = value;
67
+ }
68
+ }
69
+
70
+ return { frontmatter, body };
71
+ }
72
+
73
+ /**
74
+ * Parse markdown sections
75
+ */
76
+ function parseSections(body: string): MarkdownCommand['sections'] {
77
+ const sections: MarkdownCommand['sections'] = {};
78
+
79
+ // Extract title (first h1)
80
+ const titleMatch = body.match(/^#\s+(.+)$/m);
81
+ if (titleMatch?.[1]) {
82
+ sections.title = titleMatch[1].trim();
83
+ }
84
+
85
+ // Extract instructions section
86
+ const instructionsMatch = body.match(/##\s*Instructions?\s*\n([\s\S]*?)(?=\n##|$)/i);
87
+ if (instructionsMatch?.[1]) {
88
+ sections.instructions = instructionsMatch[1].trim();
89
+ }
90
+
91
+ // Extract fallbacks section
92
+ const fallbacksMatch = body.match(/##\s*Fallbacks?\s*\n([\s\S]*?)(?=\n##|$)/i);
93
+ if (fallbacksMatch?.[1]) {
94
+ sections.fallbacks = fallbacksMatch[1].trim();
95
+ }
96
+
97
+ // Extract output format section
98
+ const outputMatch = body.match(/##\s*Output\s*(Format)?\s*\n([\s\S]*?)(?=\n##|$)/i);
99
+ if (outputMatch?.[2]) {
100
+ sections.outputFormat = outputMatch[2].trim();
101
+ }
102
+
103
+ return sections;
104
+ }
105
+
106
+ /**
107
+ * Map integration name to adapter type
108
+ */
109
+ function mapIntegrationToAdapterType(integration: string): AdapterType | null {
110
+ const mapping: Record<string, AdapterType> = {
111
+ // Issue tracker mappings
112
+ jira: 'issue_tracker',
113
+ issue_tracker: 'issue_tracker',
114
+ issues: 'issue_tracker',
115
+ linear: 'issue_tracker',
116
+ github_issues: 'issue_tracker',
117
+
118
+ // Git provider mappings
119
+ git: 'git',
120
+ github: 'git',
121
+ gitlab: 'git',
122
+ bitbucket: 'git',
123
+
124
+ // Documentation mappings
125
+ docs: 'documentation',
126
+ documentation: 'documentation',
127
+ confluence: 'documentation',
128
+ notion: 'documentation',
129
+ wiki: 'documentation',
130
+
131
+ // State mappings
132
+ state: 'state',
133
+ filesystem: 'state',
134
+ };
135
+
136
+ return mapping[integration.toLowerCase()] ?? null;
137
+ }
138
+
139
+ /**
140
+ * Parse integration dependencies from frontmatter
141
+ */
142
+ function parseDependencies(frontmatter: CommandFrontmatter): IntegrationDependency[] {
143
+ const dependencies: IntegrationDependency[] = [];
144
+
145
+ // Required dependencies
146
+ if (frontmatter.requires && Array.isArray(frontmatter.requires)) {
147
+ for (const req of frontmatter.requires) {
148
+ const type = mapIntegrationToAdapterType(req);
149
+ if (type) {
150
+ dependencies.push({
151
+ type,
152
+ required: true,
153
+ description: `Requires ${req} integration`,
154
+ });
155
+ }
156
+ }
157
+ }
158
+
159
+ // Optional dependencies
160
+ if (frontmatter.optional && Array.isArray(frontmatter.optional)) {
161
+ for (const opt of frontmatter.optional) {
162
+ const type = mapIntegrationToAdapterType(opt);
163
+ if (type) {
164
+ dependencies.push({
165
+ type,
166
+ required: false,
167
+ description: `Optionally uses ${opt} integration`,
168
+ });
169
+ }
170
+ }
171
+ }
172
+
173
+ return dependencies;
174
+ }
175
+
176
+ /**
177
+ * Derive command name from file path
178
+ */
179
+ function deriveCommandName(filePath: string, baseDir: string): string {
180
+ const relativePath = relative(baseDir, filePath);
181
+ const name = basename(filePath, '.md');
182
+
183
+ // Include parent directory for nested commands (e.g., core/check-inbox -> check-inbox)
184
+ // But not for simple structure
185
+ const dir = dirname(relativePath);
186
+ if (dir === '.' || dir === 'core' || dir === 'optional') {
187
+ return name;
188
+ }
189
+
190
+ // For deeper nesting, include the subdirectory
191
+ return name;
192
+ }
193
+
194
+ /**
195
+ * Derive category from file path
196
+ */
197
+ function deriveCategoryFromPath(filePath: string, baseDir: string): CommandCategory {
198
+ const relativePath = relative(baseDir, filePath);
199
+ const parts = relativePath.split('/');
200
+
201
+ if (parts.includes('optional')) {
202
+ return 'optional';
203
+ }
204
+ if (parts.includes('core')) {
205
+ return 'core';
206
+ }
207
+
208
+ // Default to custom for project-specific commands
209
+ return 'custom';
210
+ }
211
+
212
+ /**
213
+ * Load a single command from a markdown file
214
+ */
215
+ export async function loadCommandFromFile(
216
+ filePath: string,
217
+ baseDir: string,
218
+ categoryOverride?: CommandCategory
219
+ ): Promise<MarkdownCommand> {
220
+ const content = await fs.readFile(filePath, 'utf-8');
221
+ const { frontmatter, body } = parseFrontmatter(content);
222
+ const sections = parseSections(body);
223
+
224
+ const name = deriveCommandName(filePath, baseDir);
225
+ const category = categoryOverride ?? deriveCategoryFromPath(filePath, baseDir);
226
+ const dependencies = parseDependencies(frontmatter);
227
+
228
+ const metadata: CommandMetadata = {
229
+ name,
230
+ description: frontmatter.description ?? sections.title ?? name,
231
+ category,
232
+ dependencies,
233
+ sourcePath: filePath,
234
+ available: true, // Will be updated by registry
235
+ };
236
+ if (frontmatter['argument-hint']) {
237
+ metadata.argumentHint = frontmatter['argument-hint'];
238
+ }
239
+
240
+ return {
241
+ metadata,
242
+ content: body,
243
+ sections,
244
+ };
245
+ }
246
+
247
+ /**
248
+ * Find all markdown files in a directory
249
+ */
250
+ async function findMarkdownFiles(dir: string, recursive: boolean): Promise<string[]> {
251
+ const files: string[] = [];
252
+
253
+ try {
254
+ const entries = await fs.readdir(dir, { withFileTypes: true });
255
+
256
+ for (const entry of entries) {
257
+ const fullPath = join(dir, entry.name);
258
+
259
+ if (entry.isDirectory() && recursive) {
260
+ const subFiles = await findMarkdownFiles(fullPath, recursive);
261
+ files.push(...subFiles);
262
+ } else if (entry.isFile() && entry.name.endsWith('.md')) {
263
+ files.push(fullPath);
264
+ }
265
+ }
266
+ } catch (error) {
267
+ // Directory doesn't exist or can't be read
268
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
269
+ throw error;
270
+ }
271
+ }
272
+
273
+ return files;
274
+ }
275
+
276
+ /**
277
+ * Load commands from a directory
278
+ */
279
+ export async function loadCommandsFromDirectory(
280
+ options: CommandLoaderOptions
281
+ ): Promise<CommandLoadResult> {
282
+ const result: CommandLoadResult = {
283
+ loaded: [],
284
+ errors: [],
285
+ };
286
+
287
+ const files = await findMarkdownFiles(options.directory, options.recursive ?? true);
288
+
289
+ for (const filePath of files) {
290
+ try {
291
+ const command = await loadCommandFromFile(filePath, options.directory, options.category);
292
+
293
+ // Apply filter if provided
294
+ if (options.filter && !options.filter(command.metadata)) {
295
+ continue;
296
+ }
297
+
298
+ result.loaded.push(command);
299
+ } catch (error) {
300
+ result.errors.push({
301
+ path: filePath,
302
+ error: error instanceof Error ? error.message : String(error),
303
+ });
304
+ }
305
+ }
306
+
307
+ return result;
308
+ }
309
+
310
+ /**
311
+ * Load commands from the default CoreAI commands directory
312
+ */
313
+ export async function loadCoreAICommands(projectRoot: string): Promise<CommandLoadResult> {
314
+ const commandsDir = join(projectRoot, 'coreai', 'commands');
315
+ return loadCommandsFromDirectory({
316
+ directory: commandsDir,
317
+ category: 'core', // Will be overridden by path derivation
318
+ recursive: true,
319
+ });
320
+ }
321
+
322
+ /**
323
+ * Load commands from both core and custom directories
324
+ */
325
+ export async function loadAllCommands(
326
+ coreCommandsDir: string,
327
+ customCommandsDir?: string
328
+ ): Promise<CommandLoadResult> {
329
+ const result: CommandLoadResult = {
330
+ loaded: [],
331
+ errors: [],
332
+ };
333
+
334
+ // Load core commands
335
+ const coreResult = await loadCommandsFromDirectory({
336
+ directory: coreCommandsDir,
337
+ category: 'core',
338
+ recursive: true,
339
+ });
340
+ result.loaded.push(...coreResult.loaded);
341
+ result.errors.push(...coreResult.errors);
342
+
343
+ // Load custom commands if directory specified
344
+ if (customCommandsDir) {
345
+ const customResult = await loadCommandsFromDirectory({
346
+ directory: customCommandsDir,
347
+ category: 'custom',
348
+ recursive: true,
349
+ });
350
+ result.loaded.push(...customResult.loaded);
351
+ result.errors.push(...customResult.errors);
352
+ }
353
+
354
+ return result;
355
+ }