@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,229 @@
1
+ /**
2
+ * Cache Commands
3
+ *
4
+ * Commands for managing the local cache.
5
+ */
6
+
7
+ import { join } from 'path';
8
+ import { FileCacheProvider, CACHE_PATHS, type CacheStats } from '../../cache/index.js';
9
+
10
+ /**
11
+ * Options for cache commands
12
+ */
13
+ export interface CacheCommandOptions {
14
+ /**
15
+ * Project root directory
16
+ */
17
+ projectRoot?: string;
18
+
19
+ /**
20
+ * Custom cache path (overrides default)
21
+ */
22
+ cachePath?: string;
23
+
24
+ /**
25
+ * Output format
26
+ */
27
+ format?: 'text' | 'json';
28
+ }
29
+
30
+ /**
31
+ * Result of cache status command
32
+ */
33
+ export interface CacheStatusResult {
34
+ initialized: boolean;
35
+ path: string;
36
+ stats: CacheStats | null;
37
+ entries: {
38
+ key: string;
39
+ source: string;
40
+ status: string;
41
+ size: number;
42
+ cachedAt: string;
43
+ expiresAt: string;
44
+ }[];
45
+ }
46
+
47
+ /**
48
+ * Result of cache clear command
49
+ */
50
+ export interface CacheClearResult {
51
+ cleared: number;
52
+ success: boolean;
53
+ error?: string;
54
+ }
55
+
56
+ /**
57
+ * Get the cache path for a project
58
+ */
59
+ function getCachePath(options: CacheCommandOptions): string {
60
+ if (options.cachePath) {
61
+ return options.cachePath;
62
+ }
63
+ const root = options.projectRoot ?? process.cwd();
64
+ return join(root, CACHE_PATHS.base);
65
+ }
66
+
67
+ /**
68
+ * Get cache status
69
+ */
70
+ export async function cacheStatus(options: CacheCommandOptions = {}): Promise<CacheStatusResult> {
71
+ const cachePath = getCachePath(options);
72
+
73
+ const provider = new FileCacheProvider({ basePath: cachePath });
74
+
75
+ try {
76
+ await provider.initialize();
77
+ } catch {
78
+ return {
79
+ initialized: false,
80
+ path: cachePath,
81
+ stats: null,
82
+ entries: [],
83
+ };
84
+ }
85
+
86
+ const stats = await provider.getStats();
87
+ const metadataList = await provider.list();
88
+
89
+ const entries = await Promise.all(
90
+ metadataList.map(async (metadata) => {
91
+ const status = await provider.getStatus(metadata.key);
92
+ return {
93
+ key: metadata.key,
94
+ source: metadata.source,
95
+ status: status ?? 'unknown',
96
+ size: metadata.size,
97
+ cachedAt: metadata.cachedAt,
98
+ expiresAt: metadata.expiresAt,
99
+ };
100
+ })
101
+ );
102
+
103
+ return {
104
+ initialized: true,
105
+ path: cachePath,
106
+ stats,
107
+ entries,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Clear all cache entries
113
+ */
114
+ export async function cacheClear(options: CacheCommandOptions = {}): Promise<CacheClearResult> {
115
+ const cachePath = getCachePath(options);
116
+
117
+ const provider = new FileCacheProvider({ basePath: cachePath });
118
+
119
+ try {
120
+ await provider.initialize();
121
+ const cleared = await provider.clear();
122
+ return { cleared, success: true };
123
+ } catch (error) {
124
+ return {
125
+ cleared: 0,
126
+ success: false,
127
+ error: error instanceof Error ? error.message : String(error),
128
+ };
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Clear expired cache entries
134
+ */
135
+ export async function cacheClearExpired(
136
+ options: CacheCommandOptions = {}
137
+ ): Promise<CacheClearResult> {
138
+ const cachePath = getCachePath(options);
139
+
140
+ const provider = new FileCacheProvider({ basePath: cachePath });
141
+
142
+ try {
143
+ await provider.initialize();
144
+ const cleared = await provider.clearExpired();
145
+ return { cleared, success: true };
146
+ } catch (error) {
147
+ return {
148
+ cleared: 0,
149
+ success: false,
150
+ error: error instanceof Error ? error.message : String(error),
151
+ };
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Format bytes to human-readable string
157
+ */
158
+ export function formatBytes(bytes: number): string {
159
+ if (bytes === 0) return '0 B';
160
+ const k = 1024;
161
+ const sizes = ['B', 'KB', 'MB', 'GB'];
162
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
163
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
164
+ }
165
+
166
+ /**
167
+ * Format cache status for terminal output
168
+ */
169
+ export function formatCacheStatus(result: CacheStatusResult): string {
170
+ const lines: string[] = [];
171
+
172
+ lines.push('Cache Status');
173
+ lines.push('============');
174
+ lines.push('');
175
+
176
+ if (!result.initialized) {
177
+ lines.push(`Path: ${result.path}`);
178
+ lines.push('Status: Not initialized (no cache found)');
179
+ return lines.join('\n');
180
+ }
181
+
182
+ lines.push(`Path: ${result.path}`);
183
+ lines.push('Status: Initialized');
184
+ lines.push('');
185
+
186
+ if (result.stats) {
187
+ lines.push('Statistics:');
188
+ lines.push(` Entries: ${result.stats.totalEntries}`);
189
+ lines.push(` Total size: ${formatBytes(result.stats.totalSize)}`);
190
+ lines.push(
191
+ ` Valid: ${result.stats.validEntries}, Stale: ${result.stats.staleEntries}, Expired: ${result.stats.expiredEntries}`
192
+ );
193
+ if (result.stats.newestEntry) {
194
+ lines.push(` Last updated: ${result.stats.newestEntry}`);
195
+ }
196
+ }
197
+
198
+ if (result.entries.length > 0) {
199
+ lines.push('');
200
+ lines.push('Entries:');
201
+ lines.push('');
202
+
203
+ // Group by source
204
+ const bySource = new Map<string, typeof result.entries>();
205
+ for (const entry of result.entries) {
206
+ const source = entry.source;
207
+ if (!bySource.has(source)) {
208
+ bySource.set(source, []);
209
+ }
210
+ const entries = bySource.get(source);
211
+ if (entries) {
212
+ entries.push(entry);
213
+ }
214
+ }
215
+
216
+ for (const [source, entries] of bySource) {
217
+ lines.push(` ${source}:`);
218
+ for (const entry of entries) {
219
+ const statusIcon = entry.status === 'valid' ? '✓' : entry.status === 'stale' ? '○' : '✗';
220
+ lines.push(` ${statusIcon} ${entry.key} (${formatBytes(entry.size)})`);
221
+ }
222
+ }
223
+ } else {
224
+ lines.push('');
225
+ lines.push('No entries in cache.');
226
+ }
227
+
228
+ return lines.join('\n');
229
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * CLI Commands
3
+ *
4
+ * Command implementations for the CoreAI CLI.
5
+ */
6
+
7
+ // Cache commands
8
+ export {
9
+ cacheStatus,
10
+ cacheClear,
11
+ cacheClearExpired,
12
+ formatCacheStatus,
13
+ formatBytes,
14
+ type CacheCommandOptions,
15
+ type CacheStatusResult,
16
+ type CacheClearResult,
17
+ } from './cache.js';
18
+
19
+ // Sync commands
20
+ export { sync, formatSyncResult, type SyncCommandOptions, type SyncCommandResult } from './sync.js';
21
+
22
+ // Init command
23
+ export { init, formatInitResult, type InitCommandOptions, type InitCommandResult } from './init.js';
24
+
25
+ // Build command
26
+ export {
27
+ build,
28
+ formatBuildResult,
29
+ type BuildCommandOptions,
30
+ type BuildCommandResult,
31
+ } from './build.js';
32
+
33
+ // Validate command
34
+ export {
35
+ validate,
36
+ formatValidateResult,
37
+ type ValidateCommandOptions,
38
+ type ValidateCommandResult,
39
+ type ValidationIssue,
40
+ } from './validate.js';
41
+
42
+ // Skills commands
43
+ export {
44
+ skillsGenerate,
45
+ formatSkillsGenerateResult,
46
+ skillsList,
47
+ formatSkillsListResult,
48
+ type SkillsGenerateOptions,
49
+ type SkillsGenerateResult,
50
+ type SkillsListOptions,
51
+ type SkillsListResult,
52
+ type SkillInfo,
53
+ } from './skills.js';
54
+
55
+ // Status command
56
+ export {
57
+ status,
58
+ formatStatusResult,
59
+ formatStatusSummary,
60
+ type StatusCommandOptions,
61
+ type StatusCommandResult,
62
+ type AgentStatus,
63
+ } from './status.js';
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Init Command Tests
3
+ */
4
+
5
+ import { promises as fs } from 'fs';
6
+ import { join } from 'path';
7
+ import { tmpdir } from 'os';
8
+ import { init, formatInitResult } from './init.js';
9
+
10
+ describe('Init Command', () => {
11
+ let testDir: string;
12
+
13
+ beforeEach(async () => {
14
+ testDir = join(tmpdir(), `init-cmd-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
15
+ await fs.mkdir(testDir, { recursive: true });
16
+ });
17
+
18
+ afterEach(async () => {
19
+ try {
20
+ await fs.rm(testDir, { recursive: true, force: true });
21
+ } catch {
22
+ // Ignore cleanup errors
23
+ }
24
+ });
25
+
26
+ describe('init', () => {
27
+ it('should create config file with defaults', async () => {
28
+ const result = init({
29
+ projectRoot: testDir,
30
+ name: 'test-project',
31
+ });
32
+
33
+ expect(result.success).toBe(true);
34
+ expect(result.configPath).toBe(join(testDir, 'coreai.config.yaml'));
35
+
36
+ // Verify file exists
37
+ const configPath = result.configPath ?? '';
38
+ const content = await fs.readFile(configPath, 'utf-8');
39
+ expect(content).toContain('name: "test-project"');
40
+ expect(content).toContain('type: software');
41
+ });
42
+
43
+ it('should create directory structure', async () => {
44
+ const result = init({
45
+ projectRoot: testDir,
46
+ name: 'test-project',
47
+ });
48
+
49
+ expect(result.success).toBe(true);
50
+ expect(result.createdDirs).toBeDefined();
51
+ expect(result.createdDirs?.length).toBeGreaterThan(0);
52
+
53
+ // Verify directories exist
54
+ const coreaiDir = join(testDir, 'coreai', 'agents');
55
+ const stat = await fs.stat(coreaiDir);
56
+ expect(stat.isDirectory()).toBe(true);
57
+ });
58
+
59
+ it('should skip directory creation when skipDirs is true', async () => {
60
+ const result = init({
61
+ projectRoot: testDir,
62
+ name: 'test-project',
63
+ skipDirs: true,
64
+ });
65
+
66
+ expect(result.success).toBe(true);
67
+ expect(result.createdDirs).toEqual([]);
68
+ });
69
+
70
+ it('should fail if config already exists without force', async () => {
71
+ // Create existing config
72
+ await fs.writeFile(join(testDir, 'coreai.config.yaml'), 'version: "1.0"');
73
+
74
+ const result = init({
75
+ projectRoot: testDir,
76
+ name: 'test-project',
77
+ });
78
+
79
+ expect(result.success).toBe(false);
80
+ expect(result.error).toContain('already exists');
81
+ });
82
+
83
+ it('should overwrite config with force option', async () => {
84
+ // Create existing config
85
+ await fs.writeFile(join(testDir, 'coreai.config.yaml'), 'version: "1.0"');
86
+
87
+ const result = init({
88
+ projectRoot: testDir,
89
+ name: 'new-project',
90
+ force: true,
91
+ });
92
+
93
+ expect(result.success).toBe(true);
94
+
95
+ const configPath2 = result.configPath ?? '';
96
+ const content = await fs.readFile(configPath2, 'utf-8');
97
+ expect(content).toContain('name: "new-project"');
98
+ });
99
+
100
+ it('should use custom project type', async () => {
101
+ const result = init({
102
+ projectRoot: testDir,
103
+ name: 'mobile-app',
104
+ type: 'mobile',
105
+ });
106
+
107
+ expect(result.success).toBe(true);
108
+
109
+ const configPath3 = result.configPath ?? '';
110
+ const content = await fs.readFile(configPath3, 'utf-8');
111
+ expect(content).toContain('type: mobile');
112
+ });
113
+
114
+ it('should detect project name from directory', async () => {
115
+ const result = init({
116
+ projectRoot: testDir,
117
+ });
118
+
119
+ expect(result.success).toBe(true);
120
+
121
+ // Should use directory name as project name
122
+ const configPath4 = result.configPath ?? '';
123
+ const content = await fs.readFile(configPath4, 'utf-8');
124
+ expect(content).toContain('name:');
125
+ });
126
+ });
127
+
128
+ describe('formatInitResult', () => {
129
+ it('should format success result', () => {
130
+ const result = {
131
+ success: true,
132
+ configPath: '/path/to/coreai.config.yaml',
133
+ createdDirs: ['/path/to/coreai/agents'],
134
+ };
135
+
136
+ const output = formatInitResult(result);
137
+
138
+ expect(output).toContain('initialized successfully');
139
+ expect(output).toContain('coreai.config.yaml');
140
+ expect(output).toContain('Next steps');
141
+ });
142
+
143
+ it('should format error result', () => {
144
+ const result = {
145
+ success: false,
146
+ error: 'Config already exists',
147
+ };
148
+
149
+ const output = formatInitResult(result);
150
+
151
+ expect(output).toContain('Error');
152
+ expect(output).toContain('Config already exists');
153
+ });
154
+
155
+ it('should show detected git info', () => {
156
+ const result = {
157
+ success: true,
158
+ configPath: '/path/to/config.yaml',
159
+ createdDirs: [],
160
+ gitInfo: {
161
+ provider: 'github' as const,
162
+ owner: 'test-owner',
163
+ repo: 'test-repo',
164
+ },
165
+ };
166
+
167
+ const output = formatInitResult(result);
168
+
169
+ expect(output).toContain('github');
170
+ expect(output).toContain('test-owner/test-repo');
171
+ });
172
+ });
173
+ });