aios-core 4.2.13 → 4.2.14
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/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/pro/README.md +0 -66
- package/pro/license/degradation.js +0 -220
- package/pro/license/errors.js +0 -450
- package/pro/license/feature-gate.js +0 -354
- package/pro/license/index.js +0 -181
- package/pro/license/license-api.js +0 -651
- package/pro/license/license-cache.js +0 -523
- package/pro/license/license-crypto.js +0 -303
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
|
@@ -1,397 +1,397 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* YAML Validator for AIOS Developer Meta-Agent
|
|
3
|
-
* Ensures YAML files maintain proper structure and syntax
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const yaml = require('js-yaml');
|
|
7
|
-
const fs = require('fs-extra');
|
|
8
|
-
|
|
9
|
-
class YAMLValidator {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.validationRules = {
|
|
12
|
-
agent: {
|
|
13
|
-
required: ['agent', 'persona', 'commands'],
|
|
14
|
-
optional: ['dependencies', 'security', 'customization'],
|
|
15
|
-
structure: {
|
|
16
|
-
agent: {
|
|
17
|
-
required: ['name', 'id', 'title', 'icon', 'whenToUse'],
|
|
18
|
-
optional: ['customization']
|
|
19
|
-
},
|
|
20
|
-
persona: {
|
|
21
|
-
required: ['role', 'style', 'identity', 'focus'],
|
|
22
|
-
optional: []
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
manifest: {
|
|
27
|
-
required: ['bundle', 'agents'],
|
|
28
|
-
optional: ['workflows'],
|
|
29
|
-
structure: {
|
|
30
|
-
bundle: {
|
|
31
|
-
required: ['name', 'icon', 'description'],
|
|
32
|
-
optional: []
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
workflow: {
|
|
37
|
-
required: ['workflow', 'stages'],
|
|
38
|
-
optional: ['transitions', 'resources', 'validation'],
|
|
39
|
-
structure: {
|
|
40
|
-
workflow: {
|
|
41
|
-
required: ['id', 'name', 'description', 'type', 'scope'],
|
|
42
|
-
optional: []
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Validate YAML content
|
|
51
|
-
*/
|
|
52
|
-
async validate(content, type = 'general') {
|
|
53
|
-
const results = {
|
|
54
|
-
valid: true,
|
|
55
|
-
errors: [],
|
|
56
|
-
warnings: [],
|
|
57
|
-
parsed: null
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
// Parse YAML
|
|
62
|
-
results.parsed = yaml.load(content, {
|
|
63
|
-
schema: yaml.SAFE_SCHEMA,
|
|
64
|
-
onWarning: (warning) => {
|
|
65
|
-
results.warnings.push({
|
|
66
|
-
type: 'yaml_warning',
|
|
67
|
-
message: warning.toString()
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Type-specific validation
|
|
73
|
-
if (type !== 'general' && this.validationRules[type]) {
|
|
74
|
-
this.validateStructure(results.parsed, type, results);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// General validations
|
|
78
|
-
this.validateGeneral(results.parsed, results);
|
|
79
|
-
|
|
80
|
-
} catch (error) {
|
|
81
|
-
results.valid = false;
|
|
82
|
-
results.errors.push({
|
|
83
|
-
type: 'parse_error',
|
|
84
|
-
message: error.message,
|
|
85
|
-
line: error.mark ? error.mark.line : null,
|
|
86
|
-
column: error.mark ? error.mark.column : null
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return results;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Validate YAML file
|
|
95
|
-
*/
|
|
96
|
-
async validateFile(filePath, type = 'general') {
|
|
97
|
-
try {
|
|
98
|
-
const content = await fs.readFile(filePath, 'utf8');
|
|
99
|
-
const results = await this.validate(content, type);
|
|
100
|
-
results.filePath = filePath;
|
|
101
|
-
return results;
|
|
102
|
-
} catch (error) {
|
|
103
|
-
return {
|
|
104
|
-
valid: false,
|
|
105
|
-
filePath,
|
|
106
|
-
errors: [{
|
|
107
|
-
type: 'file_error',
|
|
108
|
-
message: `Could not read file: ${error.message}`
|
|
109
|
-
}]
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Validate structure based on type
|
|
116
|
-
*/
|
|
117
|
-
validateStructure(data, type, results) {
|
|
118
|
-
const rules = this.validationRules[type];
|
|
119
|
-
|
|
120
|
-
// Check required top-level fields
|
|
121
|
-
for (const field of rules.required) {
|
|
122
|
-
if (!data.hasOwnProperty(field)) {
|
|
123
|
-
results.valid = false;
|
|
124
|
-
results.errors.push({
|
|
125
|
-
type: 'missing_required',
|
|
126
|
-
field,
|
|
127
|
-
message: `Missing required field: ${field}`
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Check structure of specific fields
|
|
133
|
-
if (rules.structure) {
|
|
134
|
-
for (const [field, fieldRules] of Object.entries(rules.structure)) {
|
|
135
|
-
if (data[field]) {
|
|
136
|
-
this.validateFieldStructure(
|
|
137
|
-
data[field],
|
|
138
|
-
field,
|
|
139
|
-
fieldRules,
|
|
140
|
-
results
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Warn about unknown fields
|
|
147
|
-
const allKnownFields = [
|
|
148
|
-
...(rules.required || []),
|
|
149
|
-
...(rules.optional || [])
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
for (const field of Object.keys(data)) {
|
|
153
|
-
if (!allKnownFields.includes(field)) {
|
|
154
|
-
results.warnings.push({
|
|
155
|
-
type: 'unknown_field',
|
|
156
|
-
field,
|
|
157
|
-
message: `Unknown field: ${field}`
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Validate field structure
|
|
165
|
-
*/
|
|
166
|
-
validateFieldStructure(data, fieldName, rules, results) {
|
|
167
|
-
// Check required subfields
|
|
168
|
-
for (const subfield of rules.required || []) {
|
|
169
|
-
if (!data.hasOwnProperty(subfield)) {
|
|
170
|
-
results.valid = false;
|
|
171
|
-
results.errors.push({
|
|
172
|
-
type: 'missing_required',
|
|
173
|
-
field: `${fieldName}.${subfield}`,
|
|
174
|
-
message: `Missing required field: ${fieldName}.${subfield}`
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check field types
|
|
180
|
-
this.validateFieldTypes(data, fieldName, results);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Validate field types
|
|
185
|
-
*/
|
|
186
|
-
validateFieldTypes(data, fieldName, results) {
|
|
187
|
-
for (const [key, value] of Object.entries(data)) {
|
|
188
|
-
const fullPath = `${fieldName}.${key}`;
|
|
189
|
-
|
|
190
|
-
// Check for null/undefined
|
|
191
|
-
if (value === null || value === undefined) {
|
|
192
|
-
results.warnings.push({
|
|
193
|
-
type: 'null_value',
|
|
194
|
-
field: fullPath,
|
|
195
|
-
message: `Null or undefined value at ${fullPath}`
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Type-specific checks
|
|
200
|
-
if (key === 'id' || key === 'name') {
|
|
201
|
-
if (typeof value !== 'string' || value.trim() === '') {
|
|
202
|
-
results.errors.push({
|
|
203
|
-
type: 'invalid_type',
|
|
204
|
-
field: fullPath,
|
|
205
|
-
message: `${fullPath} must be a non-empty string`
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (key === 'icon' && typeof value === 'string') {
|
|
211
|
-
// Check if it's a valid emoji or icon string
|
|
212
|
-
if (value.length === 0) {
|
|
213
|
-
results.warnings.push({
|
|
214
|
-
type: 'empty_icon',
|
|
215
|
-
field: fullPath,
|
|
216
|
-
message: 'Icon field is empty'
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/**
|
|
224
|
-
* General validations for all YAML
|
|
225
|
-
*/
|
|
226
|
-
validateGeneral(data, results) {
|
|
227
|
-
// Check for circular references
|
|
228
|
-
try {
|
|
229
|
-
JSON.stringify(data);
|
|
230
|
-
} catch (error) {
|
|
231
|
-
if (error.message.includes('circular')) {
|
|
232
|
-
results.valid = false;
|
|
233
|
-
results.errors.push({
|
|
234
|
-
type: 'circular_reference',
|
|
235
|
-
message: 'Circular reference detected in YAML structure'
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Check for excessively deep nesting
|
|
241
|
-
const maxDepth = this.getMaxDepth(data);
|
|
242
|
-
if (maxDepth > 10) {
|
|
243
|
-
results.warnings.push({
|
|
244
|
-
type: 'deep_nesting',
|
|
245
|
-
depth: maxDepth,
|
|
246
|
-
message: `Deep nesting detected (${maxDepth} levels)`
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Get maximum depth of object
|
|
253
|
-
*/
|
|
254
|
-
getMaxDepth(obj, currentDepth = 0) {
|
|
255
|
-
if (typeof obj !== 'object' || obj === null) {
|
|
256
|
-
return currentDepth;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
let maxDepth = currentDepth;
|
|
260
|
-
for (const value of Object.values(obj)) {
|
|
261
|
-
if (typeof value === 'object') {
|
|
262
|
-
const depth = this.getMaxDepth(value, currentDepth + 1);
|
|
263
|
-
maxDepth = Math.max(maxDepth, depth);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return maxDepth;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Fix common YAML issues
|
|
272
|
-
*/
|
|
273
|
-
async autoFix(content, type = 'general') {
|
|
274
|
-
let fixed = content;
|
|
275
|
-
|
|
276
|
-
// Fix common indentation issues
|
|
277
|
-
fixed = this.fixIndentation(fixed);
|
|
278
|
-
|
|
279
|
-
// Fix quote issues
|
|
280
|
-
fixed = this.fixQuotes(fixed);
|
|
281
|
-
|
|
282
|
-
// Validate the fixed content
|
|
283
|
-
const validation = await this.validate(fixed, type);
|
|
284
|
-
|
|
285
|
-
return {
|
|
286
|
-
content: fixed,
|
|
287
|
-
validation,
|
|
288
|
-
changed: content !== fixed
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Fix indentation issues
|
|
294
|
-
* @param {string} content - YAML content to fix
|
|
295
|
-
* @returns {string} Fixed YAML content
|
|
296
|
-
*/
|
|
297
|
-
fixIndentation(content) {
|
|
298
|
-
const lines = content.split('\n');
|
|
299
|
-
const fixedLines = [];
|
|
300
|
-
const indentStack = [0];
|
|
301
|
-
let currentLevel = 0;
|
|
302
|
-
|
|
303
|
-
for (let i = 0; i < lines.length; i++) {
|
|
304
|
-
const line = lines[i];
|
|
305
|
-
const trimmed = line.trim();
|
|
306
|
-
|
|
307
|
-
// Skip empty lines and comments
|
|
308
|
-
if (!trimmed || trimmed.startsWith('#')) {
|
|
309
|
-
fixedLines.push(line);
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Handle list items
|
|
314
|
-
if (trimmed.startsWith('-')) {
|
|
315
|
-
const baseIndent = indentStack[indentStack.length - 1];
|
|
316
|
-
fixedLines.push(' '.repeat(baseIndent) + trimmed);
|
|
317
|
-
|
|
318
|
-
// If list item has a key-value pair, prepare for nested content
|
|
319
|
-
if (trimmed.includes(':') && !trimmed.endsWith(':')) {
|
|
320
|
-
const afterDash = trimmed.substring(1).trim();
|
|
321
|
-
if (afterDash.includes(':')) {
|
|
322
|
-
currentLevel = baseIndent + 2;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// Handle key-value pairs
|
|
327
|
-
else if (trimmed.includes(':')) {
|
|
328
|
-
// Find appropriate indent level
|
|
329
|
-
const colonIndex = trimmed.indexOf(':');
|
|
330
|
-
const _key = trimmed.substring(0, colonIndex);
|
|
331
|
-
|
|
332
|
-
// Pop stack until we find the right level
|
|
333
|
-
while (indentStack.length > 1 &&
|
|
334
|
-
line.length - line.trimStart().length < indentStack[indentStack.length - 1]) {
|
|
335
|
-
indentStack.pop();
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
currentLevel = indentStack[indentStack.length - 1];
|
|
339
|
-
fixedLines.push(' '.repeat(currentLevel) + trimmed);
|
|
340
|
-
|
|
341
|
-
// If this opens a new block, push new indent level
|
|
342
|
-
if (trimmed.endsWith(':') || (i + 1 < lines.length && lines[i + 1].trim() &&
|
|
343
|
-
lines[i + 1].length - lines[i + 1].trimStart().length > currentLevel)) {
|
|
344
|
-
indentStack.push(currentLevel + 2);
|
|
345
|
-
}
|
|
346
|
-
} else {
|
|
347
|
-
// Regular content line
|
|
348
|
-
fixedLines.push(' '.repeat(currentLevel) + trimmed);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return fixedLines.join('\n');
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Fix quote issues
|
|
357
|
-
*/
|
|
358
|
-
fixQuotes(content) {
|
|
359
|
-
// Fix unquoted strings that need quotes
|
|
360
|
-
return content.replace(
|
|
361
|
-
/^(\s*\w+):\s*([^"'\n]*[:{}\[\]|>&*!%@`][^"'\n]*)$/gm,
|
|
362
|
-
'$1: "$2"'
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Generate validation report
|
|
368
|
-
*/
|
|
369
|
-
generateReport(validation) {
|
|
370
|
-
const report = [];
|
|
371
|
-
|
|
372
|
-
report.push(`YAML Validation Report`);
|
|
373
|
-
report.push(`=====================`);
|
|
374
|
-
report.push(`Valid: ${validation.valid ? '✅ Yes' : '❌ No'}`);
|
|
375
|
-
|
|
376
|
-
if (validation.errors.length > 0) {
|
|
377
|
-
report.push(`\nErrors (${validation.errors.length}):`);
|
|
378
|
-
for (const error of validation.errors) {
|
|
379
|
-
report.push(` - ${error.message}`);
|
|
380
|
-
if (error.line) {
|
|
381
|
-
report.push(` Line: ${error.line}, Column: ${error.column}`);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
if (validation.warnings.length > 0) {
|
|
387
|
-
report.push(`\nWarnings (${validation.warnings.length}):`);
|
|
388
|
-
for (const warning of validation.warnings) {
|
|
389
|
-
report.push(` - ${warning.message}`);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return report.join('\n');
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
1
|
+
/**
|
|
2
|
+
* YAML Validator for AIOS Developer Meta-Agent
|
|
3
|
+
* Ensures YAML files maintain proper structure and syntax
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const yaml = require('js-yaml');
|
|
7
|
+
const fs = require('fs-extra');
|
|
8
|
+
|
|
9
|
+
class YAMLValidator {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.validationRules = {
|
|
12
|
+
agent: {
|
|
13
|
+
required: ['agent', 'persona', 'commands'],
|
|
14
|
+
optional: ['dependencies', 'security', 'customization'],
|
|
15
|
+
structure: {
|
|
16
|
+
agent: {
|
|
17
|
+
required: ['name', 'id', 'title', 'icon', 'whenToUse'],
|
|
18
|
+
optional: ['customization']
|
|
19
|
+
},
|
|
20
|
+
persona: {
|
|
21
|
+
required: ['role', 'style', 'identity', 'focus'],
|
|
22
|
+
optional: []
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
manifest: {
|
|
27
|
+
required: ['bundle', 'agents'],
|
|
28
|
+
optional: ['workflows'],
|
|
29
|
+
structure: {
|
|
30
|
+
bundle: {
|
|
31
|
+
required: ['name', 'icon', 'description'],
|
|
32
|
+
optional: []
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
workflow: {
|
|
37
|
+
required: ['workflow', 'stages'],
|
|
38
|
+
optional: ['transitions', 'resources', 'validation'],
|
|
39
|
+
structure: {
|
|
40
|
+
workflow: {
|
|
41
|
+
required: ['id', 'name', 'description', 'type', 'scope'],
|
|
42
|
+
optional: []
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Validate YAML content
|
|
51
|
+
*/
|
|
52
|
+
async validate(content, type = 'general') {
|
|
53
|
+
const results = {
|
|
54
|
+
valid: true,
|
|
55
|
+
errors: [],
|
|
56
|
+
warnings: [],
|
|
57
|
+
parsed: null
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
// Parse YAML
|
|
62
|
+
results.parsed = yaml.load(content, {
|
|
63
|
+
schema: yaml.SAFE_SCHEMA,
|
|
64
|
+
onWarning: (warning) => {
|
|
65
|
+
results.warnings.push({
|
|
66
|
+
type: 'yaml_warning',
|
|
67
|
+
message: warning.toString()
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Type-specific validation
|
|
73
|
+
if (type !== 'general' && this.validationRules[type]) {
|
|
74
|
+
this.validateStructure(results.parsed, type, results);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// General validations
|
|
78
|
+
this.validateGeneral(results.parsed, results);
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
results.valid = false;
|
|
82
|
+
results.errors.push({
|
|
83
|
+
type: 'parse_error',
|
|
84
|
+
message: error.message,
|
|
85
|
+
line: error.mark ? error.mark.line : null,
|
|
86
|
+
column: error.mark ? error.mark.column : null
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return results;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Validate YAML file
|
|
95
|
+
*/
|
|
96
|
+
async validateFile(filePath, type = 'general') {
|
|
97
|
+
try {
|
|
98
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
99
|
+
const results = await this.validate(content, type);
|
|
100
|
+
results.filePath = filePath;
|
|
101
|
+
return results;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return {
|
|
104
|
+
valid: false,
|
|
105
|
+
filePath,
|
|
106
|
+
errors: [{
|
|
107
|
+
type: 'file_error',
|
|
108
|
+
message: `Could not read file: ${error.message}`
|
|
109
|
+
}]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Validate structure based on type
|
|
116
|
+
*/
|
|
117
|
+
validateStructure(data, type, results) {
|
|
118
|
+
const rules = this.validationRules[type];
|
|
119
|
+
|
|
120
|
+
// Check required top-level fields
|
|
121
|
+
for (const field of rules.required) {
|
|
122
|
+
if (!data.hasOwnProperty(field)) {
|
|
123
|
+
results.valid = false;
|
|
124
|
+
results.errors.push({
|
|
125
|
+
type: 'missing_required',
|
|
126
|
+
field,
|
|
127
|
+
message: `Missing required field: ${field}`
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Check structure of specific fields
|
|
133
|
+
if (rules.structure) {
|
|
134
|
+
for (const [field, fieldRules] of Object.entries(rules.structure)) {
|
|
135
|
+
if (data[field]) {
|
|
136
|
+
this.validateFieldStructure(
|
|
137
|
+
data[field],
|
|
138
|
+
field,
|
|
139
|
+
fieldRules,
|
|
140
|
+
results
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Warn about unknown fields
|
|
147
|
+
const allKnownFields = [
|
|
148
|
+
...(rules.required || []),
|
|
149
|
+
...(rules.optional || [])
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
for (const field of Object.keys(data)) {
|
|
153
|
+
if (!allKnownFields.includes(field)) {
|
|
154
|
+
results.warnings.push({
|
|
155
|
+
type: 'unknown_field',
|
|
156
|
+
field,
|
|
157
|
+
message: `Unknown field: ${field}`
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Validate field structure
|
|
165
|
+
*/
|
|
166
|
+
validateFieldStructure(data, fieldName, rules, results) {
|
|
167
|
+
// Check required subfields
|
|
168
|
+
for (const subfield of rules.required || []) {
|
|
169
|
+
if (!data.hasOwnProperty(subfield)) {
|
|
170
|
+
results.valid = false;
|
|
171
|
+
results.errors.push({
|
|
172
|
+
type: 'missing_required',
|
|
173
|
+
field: `${fieldName}.${subfield}`,
|
|
174
|
+
message: `Missing required field: ${fieldName}.${subfield}`
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check field types
|
|
180
|
+
this.validateFieldTypes(data, fieldName, results);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Validate field types
|
|
185
|
+
*/
|
|
186
|
+
validateFieldTypes(data, fieldName, results) {
|
|
187
|
+
for (const [key, value] of Object.entries(data)) {
|
|
188
|
+
const fullPath = `${fieldName}.${key}`;
|
|
189
|
+
|
|
190
|
+
// Check for null/undefined
|
|
191
|
+
if (value === null || value === undefined) {
|
|
192
|
+
results.warnings.push({
|
|
193
|
+
type: 'null_value',
|
|
194
|
+
field: fullPath,
|
|
195
|
+
message: `Null or undefined value at ${fullPath}`
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Type-specific checks
|
|
200
|
+
if (key === 'id' || key === 'name') {
|
|
201
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
202
|
+
results.errors.push({
|
|
203
|
+
type: 'invalid_type',
|
|
204
|
+
field: fullPath,
|
|
205
|
+
message: `${fullPath} must be a non-empty string`
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (key === 'icon' && typeof value === 'string') {
|
|
211
|
+
// Check if it's a valid emoji or icon string
|
|
212
|
+
if (value.length === 0) {
|
|
213
|
+
results.warnings.push({
|
|
214
|
+
type: 'empty_icon',
|
|
215
|
+
field: fullPath,
|
|
216
|
+
message: 'Icon field is empty'
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* General validations for all YAML
|
|
225
|
+
*/
|
|
226
|
+
validateGeneral(data, results) {
|
|
227
|
+
// Check for circular references
|
|
228
|
+
try {
|
|
229
|
+
JSON.stringify(data);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if (error.message.includes('circular')) {
|
|
232
|
+
results.valid = false;
|
|
233
|
+
results.errors.push({
|
|
234
|
+
type: 'circular_reference',
|
|
235
|
+
message: 'Circular reference detected in YAML structure'
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check for excessively deep nesting
|
|
241
|
+
const maxDepth = this.getMaxDepth(data);
|
|
242
|
+
if (maxDepth > 10) {
|
|
243
|
+
results.warnings.push({
|
|
244
|
+
type: 'deep_nesting',
|
|
245
|
+
depth: maxDepth,
|
|
246
|
+
message: `Deep nesting detected (${maxDepth} levels)`
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Get maximum depth of object
|
|
253
|
+
*/
|
|
254
|
+
getMaxDepth(obj, currentDepth = 0) {
|
|
255
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
256
|
+
return currentDepth;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let maxDepth = currentDepth;
|
|
260
|
+
for (const value of Object.values(obj)) {
|
|
261
|
+
if (typeof value === 'object') {
|
|
262
|
+
const depth = this.getMaxDepth(value, currentDepth + 1);
|
|
263
|
+
maxDepth = Math.max(maxDepth, depth);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return maxDepth;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Fix common YAML issues
|
|
272
|
+
*/
|
|
273
|
+
async autoFix(content, type = 'general') {
|
|
274
|
+
let fixed = content;
|
|
275
|
+
|
|
276
|
+
// Fix common indentation issues
|
|
277
|
+
fixed = this.fixIndentation(fixed);
|
|
278
|
+
|
|
279
|
+
// Fix quote issues
|
|
280
|
+
fixed = this.fixQuotes(fixed);
|
|
281
|
+
|
|
282
|
+
// Validate the fixed content
|
|
283
|
+
const validation = await this.validate(fixed, type);
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
content: fixed,
|
|
287
|
+
validation,
|
|
288
|
+
changed: content !== fixed
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Fix indentation issues
|
|
294
|
+
* @param {string} content - YAML content to fix
|
|
295
|
+
* @returns {string} Fixed YAML content
|
|
296
|
+
*/
|
|
297
|
+
fixIndentation(content) {
|
|
298
|
+
const lines = content.split('\n');
|
|
299
|
+
const fixedLines = [];
|
|
300
|
+
const indentStack = [0];
|
|
301
|
+
let currentLevel = 0;
|
|
302
|
+
|
|
303
|
+
for (let i = 0; i < lines.length; i++) {
|
|
304
|
+
const line = lines[i];
|
|
305
|
+
const trimmed = line.trim();
|
|
306
|
+
|
|
307
|
+
// Skip empty lines and comments
|
|
308
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
309
|
+
fixedLines.push(line);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Handle list items
|
|
314
|
+
if (trimmed.startsWith('-')) {
|
|
315
|
+
const baseIndent = indentStack[indentStack.length - 1];
|
|
316
|
+
fixedLines.push(' '.repeat(baseIndent) + trimmed);
|
|
317
|
+
|
|
318
|
+
// If list item has a key-value pair, prepare for nested content
|
|
319
|
+
if (trimmed.includes(':') && !trimmed.endsWith(':')) {
|
|
320
|
+
const afterDash = trimmed.substring(1).trim();
|
|
321
|
+
if (afterDash.includes(':')) {
|
|
322
|
+
currentLevel = baseIndent + 2;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// Handle key-value pairs
|
|
327
|
+
else if (trimmed.includes(':')) {
|
|
328
|
+
// Find appropriate indent level
|
|
329
|
+
const colonIndex = trimmed.indexOf(':');
|
|
330
|
+
const _key = trimmed.substring(0, colonIndex);
|
|
331
|
+
|
|
332
|
+
// Pop stack until we find the right level
|
|
333
|
+
while (indentStack.length > 1 &&
|
|
334
|
+
line.length - line.trimStart().length < indentStack[indentStack.length - 1]) {
|
|
335
|
+
indentStack.pop();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
currentLevel = indentStack[indentStack.length - 1];
|
|
339
|
+
fixedLines.push(' '.repeat(currentLevel) + trimmed);
|
|
340
|
+
|
|
341
|
+
// If this opens a new block, push new indent level
|
|
342
|
+
if (trimmed.endsWith(':') || (i + 1 < lines.length && lines[i + 1].trim() &&
|
|
343
|
+
lines[i + 1].length - lines[i + 1].trimStart().length > currentLevel)) {
|
|
344
|
+
indentStack.push(currentLevel + 2);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
// Regular content line
|
|
348
|
+
fixedLines.push(' '.repeat(currentLevel) + trimmed);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return fixedLines.join('\n');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Fix quote issues
|
|
357
|
+
*/
|
|
358
|
+
fixQuotes(content) {
|
|
359
|
+
// Fix unquoted strings that need quotes
|
|
360
|
+
return content.replace(
|
|
361
|
+
/^(\s*\w+):\s*([^"'\n]*[:{}\[\]|>&*!%@`][^"'\n]*)$/gm,
|
|
362
|
+
'$1: "$2"'
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Generate validation report
|
|
368
|
+
*/
|
|
369
|
+
generateReport(validation) {
|
|
370
|
+
const report = [];
|
|
371
|
+
|
|
372
|
+
report.push(`YAML Validation Report`);
|
|
373
|
+
report.push(`=====================`);
|
|
374
|
+
report.push(`Valid: ${validation.valid ? '✅ Yes' : '❌ No'}`);
|
|
375
|
+
|
|
376
|
+
if (validation.errors.length > 0) {
|
|
377
|
+
report.push(`\nErrors (${validation.errors.length}):`);
|
|
378
|
+
for (const error of validation.errors) {
|
|
379
|
+
report.push(` - ${error.message}`);
|
|
380
|
+
if (error.line) {
|
|
381
|
+
report.push(` Line: ${error.line}, Column: ${error.column}`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (validation.warnings.length > 0) {
|
|
387
|
+
report.push(`\nWarnings (${validation.warnings.length}):`);
|
|
388
|
+
for (const warning of validation.warnings) {
|
|
389
|
+
report.push(` - ${warning.message}`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return report.join('\n');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
397
|
module.exports = YAMLValidator;
|