@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,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Command
|
|
3
|
+
*
|
|
4
|
+
* Syncs shared context from remote sources.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import {
|
|
9
|
+
FileCacheProvider,
|
|
10
|
+
CACHE_PATHS,
|
|
11
|
+
type SyncResult,
|
|
12
|
+
type CacheSource,
|
|
13
|
+
} from '../../cache/index.js';
|
|
14
|
+
import { CacheManager } from '../../cache/manager.js';
|
|
15
|
+
import type { RemoteFetcher } from '../../cache/interfaces.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Options for sync command
|
|
19
|
+
*/
|
|
20
|
+
export interface SyncCommandOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Project root directory
|
|
23
|
+
*/
|
|
24
|
+
projectRoot?: string;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Custom cache path
|
|
28
|
+
*/
|
|
29
|
+
cachePath?: string;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Force refresh all entries
|
|
33
|
+
*/
|
|
34
|
+
force?: boolean;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Filter by source type
|
|
38
|
+
*/
|
|
39
|
+
source?: CacheSource;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Continue on error
|
|
43
|
+
*/
|
|
44
|
+
continueOnError?: boolean;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Maximum concurrent fetches
|
|
48
|
+
*/
|
|
49
|
+
concurrency?: number;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Progress callback
|
|
53
|
+
*/
|
|
54
|
+
onProgress?: (completed: number, total: number, message: string) => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Result of sync command
|
|
59
|
+
*/
|
|
60
|
+
export interface SyncCommandResult {
|
|
61
|
+
success: boolean;
|
|
62
|
+
result: SyncResult | null;
|
|
63
|
+
error?: string;
|
|
64
|
+
sources: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a mock fetcher for a source (placeholder until real fetchers are implemented)
|
|
69
|
+
*
|
|
70
|
+
* In a real implementation, these would be replaced with actual fetchers
|
|
71
|
+
* that use the documentation adapter interfaces.
|
|
72
|
+
*/
|
|
73
|
+
function createPlaceholderFetcher(source: string): RemoteFetcher {
|
|
74
|
+
return {
|
|
75
|
+
async fetch(url: string) {
|
|
76
|
+
// This is a placeholder - real implementation would use adapters
|
|
77
|
+
throw new Error(
|
|
78
|
+
`No fetcher implementation for source "${source}". ` +
|
|
79
|
+
`URL: ${url}. Configure a documentation provider in coreai.config.yaml.`
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
async hasChanged(_url: string, _etag?: string) {
|
|
83
|
+
// Always return true for placeholder
|
|
84
|
+
return true;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get the cache path for a project
|
|
91
|
+
*/
|
|
92
|
+
function getCachePath(options: SyncCommandOptions): string {
|
|
93
|
+
if (options.cachePath) {
|
|
94
|
+
return options.cachePath;
|
|
95
|
+
}
|
|
96
|
+
const root = options.projectRoot ?? process.cwd();
|
|
97
|
+
return join(root, CACHE_PATHS.base);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Sync cached entries from remote sources
|
|
102
|
+
*
|
|
103
|
+
* This command refreshes cached content by re-fetching from remote sources.
|
|
104
|
+
* If --force is specified, it clears stale/expired status and fetches fresh content.
|
|
105
|
+
*/
|
|
106
|
+
export async function sync(options: SyncCommandOptions = {}): Promise<SyncCommandResult> {
|
|
107
|
+
const cachePath = getCachePath(options);
|
|
108
|
+
|
|
109
|
+
const provider = new FileCacheProvider({ basePath: cachePath });
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
await provider.initialize();
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
result: null,
|
|
117
|
+
error: `Failed to initialize cache: ${error instanceof Error ? error.message : String(error)}`,
|
|
118
|
+
sources: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Get all cached entries
|
|
123
|
+
const entries = await provider.list({ source: options.source });
|
|
124
|
+
|
|
125
|
+
if (entries.length === 0) {
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
result: {
|
|
129
|
+
added: 0,
|
|
130
|
+
updated: 0,
|
|
131
|
+
removed: 0,
|
|
132
|
+
failed: 0,
|
|
133
|
+
errors: [],
|
|
134
|
+
duration: 0,
|
|
135
|
+
},
|
|
136
|
+
sources: [],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Create cache manager
|
|
141
|
+
const manager = new CacheManager({
|
|
142
|
+
cache: provider,
|
|
143
|
+
defaultTtl: 3600,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Collect unique sources and register placeholder fetchers
|
|
147
|
+
const sources = new Set<string>();
|
|
148
|
+
for (const entry of entries) {
|
|
149
|
+
sources.add(entry.source);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Register fetchers for each source
|
|
153
|
+
// In a real implementation, these would be actual fetcher implementations
|
|
154
|
+
for (const source of sources) {
|
|
155
|
+
if (!manager.hasFetcher(source)) {
|
|
156
|
+
manager.registerFetcher(source, createPlaceholderFetcher(source));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get keys to sync
|
|
161
|
+
const keys = entries.map((e) => e.key);
|
|
162
|
+
|
|
163
|
+
// Report progress
|
|
164
|
+
if (options.onProgress) {
|
|
165
|
+
options.onProgress(0, keys.length, 'Starting sync...');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Sync entries
|
|
169
|
+
try {
|
|
170
|
+
const result = await manager.syncEntries(keys, {
|
|
171
|
+
force: options.force,
|
|
172
|
+
continueOnError: options.continueOnError ?? true,
|
|
173
|
+
concurrency: options.concurrency ?? 5,
|
|
174
|
+
onProgress: (completed, total) => {
|
|
175
|
+
if (options.onProgress) {
|
|
176
|
+
options.onProgress(completed, total, `Syncing ${completed}/${total} entries...`);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
success: result.failed === 0 || options.continueOnError === true,
|
|
183
|
+
result,
|
|
184
|
+
sources: Array.from(sources),
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
result: null,
|
|
190
|
+
error: error instanceof Error ? error.message : String(error),
|
|
191
|
+
sources: Array.from(sources),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Format sync result for terminal output
|
|
198
|
+
*/
|
|
199
|
+
export function formatSyncResult(result: SyncCommandResult): string {
|
|
200
|
+
const lines: string[] = [];
|
|
201
|
+
|
|
202
|
+
lines.push('Sync Results');
|
|
203
|
+
lines.push('============');
|
|
204
|
+
lines.push('');
|
|
205
|
+
|
|
206
|
+
if (!result.success && result.error) {
|
|
207
|
+
lines.push(`Error: ${result.error}`);
|
|
208
|
+
return lines.join('\n');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (!result.result) {
|
|
212
|
+
lines.push('No sync operation performed.');
|
|
213
|
+
return lines.join('\n');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const r = result.result;
|
|
217
|
+
|
|
218
|
+
if (r.added === 0 && r.updated === 0 && r.removed === 0 && r.failed === 0) {
|
|
219
|
+
lines.push('Cache is empty. Nothing to sync.');
|
|
220
|
+
lines.push('');
|
|
221
|
+
lines.push('Tip: Add context sources to your coreai.config.yaml to fetch remote content.');
|
|
222
|
+
return lines.join('\n');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
lines.push(`Duration: ${r.duration}ms`);
|
|
226
|
+
lines.push('');
|
|
227
|
+
|
|
228
|
+
if (result.sources.length > 0) {
|
|
229
|
+
lines.push(`Sources: ${result.sources.join(', ')}`);
|
|
230
|
+
lines.push('');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
lines.push('Summary:');
|
|
234
|
+
if (r.added > 0) lines.push(` ✓ Added: ${r.added}`);
|
|
235
|
+
if (r.updated > 0) lines.push(` ✓ Updated: ${r.updated}`);
|
|
236
|
+
if (r.removed > 0) lines.push(` ○ Removed: ${r.removed}`);
|
|
237
|
+
if (r.failed > 0) lines.push(` ✗ Failed: ${r.failed}`);
|
|
238
|
+
|
|
239
|
+
if (r.errors.length > 0) {
|
|
240
|
+
lines.push('');
|
|
241
|
+
lines.push('Errors:');
|
|
242
|
+
for (const err of r.errors.slice(0, 10)) {
|
|
243
|
+
lines.push(` - ${err.key}: ${err.error}`);
|
|
244
|
+
}
|
|
245
|
+
if (r.errors.length > 10) {
|
|
246
|
+
lines.push(` ... and ${r.errors.length - 10} more errors`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return lines.join('\n');
|
|
251
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate Command Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { promises as fs } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { tmpdir } from 'os';
|
|
8
|
+
import { validate, formatValidateResult } from './validate.js';
|
|
9
|
+
|
|
10
|
+
describe('Validate Command', () => {
|
|
11
|
+
let testDir: string;
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
testDir = join(
|
|
15
|
+
tmpdir(),
|
|
16
|
+
`validate-cmd-test-${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
17
|
+
);
|
|
18
|
+
await fs.mkdir(testDir, { recursive: true });
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(async () => {
|
|
22
|
+
try {
|
|
23
|
+
await fs.rm(testDir, { recursive: true, force: true });
|
|
24
|
+
} catch {
|
|
25
|
+
// Ignore cleanup errors
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('validate', () => {
|
|
30
|
+
it('should report error if no config exists', () => {
|
|
31
|
+
const result = validate({
|
|
32
|
+
projectRoot: testDir,
|
|
33
|
+
checkAgents: false,
|
|
34
|
+
checkDirs: false,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(result.success).toBe(true);
|
|
38
|
+
expect(result.valid).toBe(false);
|
|
39
|
+
expect(result.summary.errors).toBeGreaterThan(0);
|
|
40
|
+
expect(result.issues.some((i) => i.message.includes('No CoreAI configuration'))).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should validate valid config', async () => {
|
|
44
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
45
|
+
await fs.writeFile(
|
|
46
|
+
configPath,
|
|
47
|
+
`version: "1.0"
|
|
48
|
+
project:
|
|
49
|
+
name: test-project
|
|
50
|
+
type: software
|
|
51
|
+
team:
|
|
52
|
+
agents:
|
|
53
|
+
- backend-engineer
|
|
54
|
+
`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const result = validate({
|
|
58
|
+
projectRoot: testDir,
|
|
59
|
+
checkAgents: false,
|
|
60
|
+
checkDirs: false,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
expect(result.success).toBe(true);
|
|
64
|
+
expect(result.config).toBeDefined();
|
|
65
|
+
expect(result.config?.project.name).toBe('test-project');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should report config syntax errors', async () => {
|
|
69
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
70
|
+
await fs.writeFile(configPath, 'invalid: yaml: content: [');
|
|
71
|
+
|
|
72
|
+
const result = validate({
|
|
73
|
+
projectRoot: testDir,
|
|
74
|
+
checkAgents: false,
|
|
75
|
+
checkDirs: false,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(result.valid).toBe(false);
|
|
79
|
+
expect(result.issues.some((i) => i.category === 'config' && i.level === 'error')).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should check for missing directories', async () => {
|
|
83
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
84
|
+
await fs.writeFile(
|
|
85
|
+
configPath,
|
|
86
|
+
`version: "1.0"
|
|
87
|
+
project:
|
|
88
|
+
name: test-project
|
|
89
|
+
`
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const result = validate({
|
|
93
|
+
projectRoot: testDir,
|
|
94
|
+
checkAgents: false,
|
|
95
|
+
checkDirs: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
expect(result.issues.some((i) => i.category === 'directories')).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should report info for unconfigured integrations', async () => {
|
|
102
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
103
|
+
await fs.writeFile(
|
|
104
|
+
configPath,
|
|
105
|
+
`version: "1.0"
|
|
106
|
+
project:
|
|
107
|
+
name: test-project
|
|
108
|
+
`
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const result = validate({
|
|
112
|
+
projectRoot: testDir,
|
|
113
|
+
checkAgents: false,
|
|
114
|
+
checkDirs: false,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(result.issues.some((i) => i.category === 'integrations' && i.level === 'info')).toBe(
|
|
118
|
+
true
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('should warn about incomplete git integration', async () => {
|
|
123
|
+
const configPath = join(testDir, 'coreai.config.yaml');
|
|
124
|
+
await fs.writeFile(
|
|
125
|
+
configPath,
|
|
126
|
+
`version: "1.0"
|
|
127
|
+
project:
|
|
128
|
+
name: test-project
|
|
129
|
+
integrations:
|
|
130
|
+
git:
|
|
131
|
+
provider: github
|
|
132
|
+
`
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const result = validate({
|
|
136
|
+
projectRoot: testDir,
|
|
137
|
+
checkAgents: false,
|
|
138
|
+
checkDirs: false,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(
|
|
142
|
+
result.issues.some((i) => i.category === 'integrations' && i.level === 'warning')
|
|
143
|
+
).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should calculate correct summary', async () => {
|
|
147
|
+
const result = validate({
|
|
148
|
+
projectRoot: testDir,
|
|
149
|
+
checkAgents: false,
|
|
150
|
+
checkDirs: true,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(result.summary.errors + result.summary.warnings + result.summary.info).toBe(
|
|
154
|
+
result.issues.length
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('formatValidateResult', () => {
|
|
160
|
+
it('should format valid result', () => {
|
|
161
|
+
const result = {
|
|
162
|
+
success: true,
|
|
163
|
+
valid: true,
|
|
164
|
+
issues: [],
|
|
165
|
+
summary: { errors: 0, warnings: 0, info: 0 },
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const output = formatValidateResult(result);
|
|
169
|
+
|
|
170
|
+
expect(output).toContain('✓');
|
|
171
|
+
expect(output).toContain('valid');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should format invalid result', () => {
|
|
175
|
+
const result = {
|
|
176
|
+
success: true,
|
|
177
|
+
valid: false,
|
|
178
|
+
issues: [
|
|
179
|
+
{
|
|
180
|
+
level: 'error' as const,
|
|
181
|
+
category: 'config' as const,
|
|
182
|
+
message: 'Config missing',
|
|
183
|
+
fix: 'Run coreai init',
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
summary: { errors: 1, warnings: 0, info: 0 },
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const output = formatValidateResult(result);
|
|
190
|
+
|
|
191
|
+
expect(output).toContain('✗');
|
|
192
|
+
expect(output).toContain('errors');
|
|
193
|
+
expect(output).toContain('Config missing');
|
|
194
|
+
expect(output).toContain('Run coreai init');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('should group issues by category', () => {
|
|
198
|
+
const result = {
|
|
199
|
+
success: true,
|
|
200
|
+
valid: false,
|
|
201
|
+
issues: [
|
|
202
|
+
{ level: 'error' as const, category: 'config' as const, message: 'Config error' },
|
|
203
|
+
{ level: 'warning' as const, category: 'directories' as const, message: 'Dir warning' },
|
|
204
|
+
{ level: 'info' as const, category: 'integrations' as const, message: 'Int info' },
|
|
205
|
+
],
|
|
206
|
+
summary: { errors: 1, warnings: 1, info: 1 },
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const output = formatValidateResult(result);
|
|
210
|
+
|
|
211
|
+
expect(output).toContain('Config');
|
|
212
|
+
expect(output).toContain('Directories');
|
|
213
|
+
expect(output).toContain('Integrations');
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|