aios-core 2.2.1 → 2.3.1
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/.session/current-session.json +14 -14
- package/.aios-core/cli/commands/migrate/validate.js +1 -1
- package/.aios-core/core/docs/session-update-pattern.md +17 -10
- package/.aios-core/core/elicitation/elicitation-engine.js +11 -6
- package/.aios-core/core/elicitation/session-manager.js +2 -1
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/registry/service-registry.json +6585 -6585
- package/.aios-core/core-config.yaml +12 -1
- package/.aios-core/data/agent-config-requirements.yaml +5 -5
- package/.aios-core/development/agents/devops.md +12 -0
- package/.aios-core/development/scripts/squad/README.md +112 -0
- package/.aios-core/development/scripts/squad/index.js +41 -0
- package/.aios-core/development/scripts/squad/squad-loader.js +359 -0
- package/.aios-core/development/scripts/squad/squad-validator.js +685 -0
- package/.aios-core/development/tasks/add-mcp.md +11 -5
- package/.aios-core/development/tasks/search-mcp.md +309 -0
- package/.aios-core/development/tasks/setup-mcp-docker.md +11 -8
- package/.aios-core/development/tasks/squad-creator-validate.md +151 -0
- package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +3 -3
- package/.aios-core/index.d.ts +7 -7
- package/.aios-core/index.js +1 -1
- package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
- package/.aios-core/infrastructure/scripts/component-generator.js +1 -1
- package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
- package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
- package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
- package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
- package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
- package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
- package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
- package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +500 -500
- package/.aios-core/infrastructure/tools/README.md +1 -1
- package/.aios-core/install-manifest.yaml +4 -1
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/manifests/workers.csv +203 -203
- package/.aios-core/package.json +102 -102
- package/.aios-core/product/templates/activation-instructions-template.md +7 -7
- package/.aios-core/product/templates/adr.hbs +125 -125
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/dbdr.hbs +241 -241
- 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/epic.hbs +212 -212
- 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/pmdr.hbs +186 -186
- package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
- package/.aios-core/product/templates/prd.hbs +201 -201
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/story.hbs +263 -263
- package/.aios-core/product/templates/task.hbs +170 -170
- package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
- package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
- package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
- package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
- package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
- package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
- package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
- package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
- package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
- package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
- package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
- package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
- package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
- package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
- package/.aios-core/product/templates/tmpl-view.sql +177 -177
- 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/schemas/squad-schema.json +185 -0
- package/.aios-core/scripts/README.md +90 -322
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/.claude/rules/mcp-usage.md +116 -100
- package/LICENSE +48 -48
- package/README.md +3 -4
- package/bin/aios-init.js +11 -6
- package/bin/aios.js +2 -1
- package/package.json +2 -3
- package/packages/installer/package.json +39 -39
- package/packages/installer/tests/integration/environment-configuration.test.js +2 -2
- package/packages/installer/tests/unit/env-template.test.js +4 -3
- package/templates/squad/LICENSE +21 -21
- package/templates/squad/README.md +37 -37
- package/templates/squad/agents/example-agent.yaml +36 -36
- package/templates/squad/package.json +19 -19
- package/templates/squad/squad.yaml +25 -25
- package/templates/squad/tasks/example-task.yaml +46 -46
- package/templates/squad/templates/example-template.md +24 -24
- package/templates/squad/tests/example-agent.test.js +53 -53
- package/templates/squad/workflows/example-workflow.yaml +54 -54
- package/tools/diagnose-npx-issue.ps1 +96 -96
- package/tools/quick-diagnose.cmd +85 -85
- package/tools/quick-diagnose.ps1 +117 -117
- package/.aios-core/core/data/agent-config-requirements.yaml +0 -368
- package/.aios-core/core/data/aios-kb.md +0 -924
- package/.aios-core/core/data/workflow-patterns.yaml +0 -267
- package/.aios-core/product/templates/1mcp-config.yaml +0 -225
- package/.aios-core/scripts/context-detector.js +0 -226
- package/.aios-core/scripts/elicitation-engine.js +0 -385
- package/.aios-core/scripts/elicitation-session-manager.js +0 -300
- package/.claude/CLAUDE.md +0 -221
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Squad Validator Utility
|
|
3
|
+
*
|
|
4
|
+
* Validates squads against:
|
|
5
|
+
* 1. JSON Schema (squad.yaml/config.yaml)
|
|
6
|
+
* 2. Directory structure (task-first architecture)
|
|
7
|
+
* 3. Task format (TASK-FORMAT-SPECIFICATION-V1)
|
|
8
|
+
* 4. Agent definitions
|
|
9
|
+
*
|
|
10
|
+
* Used by: squad-creator agent (*validate-squad task)
|
|
11
|
+
*
|
|
12
|
+
* @module squad-validator
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
* @see Story SQS-3: Squad Validator + JSON Schema
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const Ajv = require('ajv');
|
|
18
|
+
const fs = require('fs').promises;
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const yaml = require('js-yaml');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Load schema - handle both require and dynamic loading
|
|
24
|
+
* @returns {Object} JSON Schema
|
|
25
|
+
*/
|
|
26
|
+
function loadSchema() {
|
|
27
|
+
try {
|
|
28
|
+
return require('../../../schemas/squad-schema.json');
|
|
29
|
+
} catch {
|
|
30
|
+
// Fallback for test environments
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Supported manifest file names in order of preference
|
|
37
|
+
* @constant {string[]}
|
|
38
|
+
*/
|
|
39
|
+
const MANIFEST_FILES = ['squad.yaml', 'config.yaml'];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Required fields in tasks (TASK-FORMAT-SPECIFICATION-V1)
|
|
43
|
+
* @constant {string[]}
|
|
44
|
+
*/
|
|
45
|
+
const TASK_REQUIRED_FIELDS = [
|
|
46
|
+
'task',
|
|
47
|
+
'responsavel',
|
|
48
|
+
'responsavel_type',
|
|
49
|
+
'atomic_layer',
|
|
50
|
+
'Entrada',
|
|
51
|
+
'Saida',
|
|
52
|
+
'Checklist',
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Error codes for SquadValidatorError
|
|
57
|
+
* @enum {string}
|
|
58
|
+
*/
|
|
59
|
+
const ValidationErrorCodes = {
|
|
60
|
+
MANIFEST_NOT_FOUND: 'MANIFEST_NOT_FOUND',
|
|
61
|
+
YAML_PARSE_ERROR: 'YAML_PARSE_ERROR',
|
|
62
|
+
SCHEMA_ERROR: 'SCHEMA_ERROR',
|
|
63
|
+
DEPRECATED_MANIFEST: 'DEPRECATED_MANIFEST',
|
|
64
|
+
MISSING_DIRECTORY: 'MISSING_DIRECTORY',
|
|
65
|
+
NO_TASKS: 'NO_TASKS',
|
|
66
|
+
TASK_MISSING_FIELD: 'TASK_MISSING_FIELD',
|
|
67
|
+
TASK_READ_ERROR: 'TASK_READ_ERROR',
|
|
68
|
+
AGENT_INVALID_FORMAT: 'AGENT_INVALID_FORMAT',
|
|
69
|
+
FILE_NOT_FOUND: 'FILE_NOT_FOUND',
|
|
70
|
+
INVALID_NAMING: 'INVALID_NAMING',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Validation result structure
|
|
75
|
+
* @typedef {Object} ValidationResult
|
|
76
|
+
* @property {boolean} valid - Whether validation passed (no errors)
|
|
77
|
+
* @property {Array<ValidationError>} errors - Critical errors that fail validation
|
|
78
|
+
* @property {Array<ValidationWarning>} warnings - Non-critical issues
|
|
79
|
+
* @property {Array<string>} suggestions - Helpful suggestions
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Validation error structure
|
|
84
|
+
* @typedef {Object} ValidationError
|
|
85
|
+
* @property {string} code - Error code from ValidationErrorCodes
|
|
86
|
+
* @property {string} message - Human-readable error message
|
|
87
|
+
* @property {string} [suggestion] - Suggested fix
|
|
88
|
+
* @property {string} [path] - JSON path where error occurred
|
|
89
|
+
* @property {string} [file] - File where error occurred
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Squad Validator class for validating squad structure and content
|
|
94
|
+
*/
|
|
95
|
+
class SquadValidator {
|
|
96
|
+
/**
|
|
97
|
+
* Create a SquadValidator instance
|
|
98
|
+
* @param {Object} [options={}] - Configuration options
|
|
99
|
+
* @param {boolean} [options.verbose=false] - Enable verbose logging
|
|
100
|
+
* @param {boolean} [options.strict=false] - Treat warnings as errors
|
|
101
|
+
* @param {Object} [options.schema] - Custom schema (for testing)
|
|
102
|
+
*/
|
|
103
|
+
constructor(options = {}) {
|
|
104
|
+
this.verbose = options.verbose || false;
|
|
105
|
+
this.strict = options.strict || false;
|
|
106
|
+
|
|
107
|
+
// Initialize AJV with schema
|
|
108
|
+
this.ajv = new Ajv({ allErrors: true, verbose: true });
|
|
109
|
+
const schema = options.schema || loadSchema();
|
|
110
|
+
if (schema) {
|
|
111
|
+
this.validateSchema = this.ajv.compile(schema);
|
|
112
|
+
} else {
|
|
113
|
+
this.validateSchema = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Log message if verbose mode is enabled
|
|
119
|
+
* @private
|
|
120
|
+
* @param {string} message - Message to log
|
|
121
|
+
*/
|
|
122
|
+
_log(message) {
|
|
123
|
+
if (this.verbose) {
|
|
124
|
+
console.log(`[SquadValidator] ${message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Validate entire squad
|
|
130
|
+
*
|
|
131
|
+
* Runs all validation checks:
|
|
132
|
+
* 1. Manifest validation (schema)
|
|
133
|
+
* 2. Directory structure
|
|
134
|
+
* 3. Task format
|
|
135
|
+
* 4. Agent definitions
|
|
136
|
+
*
|
|
137
|
+
* @param {string} squadPath - Path to squad directory
|
|
138
|
+
* @returns {Promise<ValidationResult>} Validation result
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* const validator = new SquadValidator();
|
|
142
|
+
* const result = await validator.validate('./squads/my-squad');
|
|
143
|
+
* if (result.valid) {
|
|
144
|
+
* console.log('Squad is valid!');
|
|
145
|
+
* } else {
|
|
146
|
+
* console.log('Errors:', result.errors);
|
|
147
|
+
* }
|
|
148
|
+
*/
|
|
149
|
+
async validate(squadPath) {
|
|
150
|
+
this._log(`Validating squad: ${squadPath}`);
|
|
151
|
+
|
|
152
|
+
const result = {
|
|
153
|
+
valid: true,
|
|
154
|
+
errors: [],
|
|
155
|
+
warnings: [],
|
|
156
|
+
suggestions: [],
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
// 1. Validate manifest
|
|
160
|
+
const manifestResult = await this.validateManifest(squadPath);
|
|
161
|
+
this._mergeResults(result, manifestResult);
|
|
162
|
+
|
|
163
|
+
// 2. Validate directory structure
|
|
164
|
+
const structureResult = await this.validateStructure(squadPath);
|
|
165
|
+
this._mergeResults(result, structureResult);
|
|
166
|
+
|
|
167
|
+
// 3. Validate tasks (task-first!)
|
|
168
|
+
const tasksResult = await this.validateTasks(squadPath);
|
|
169
|
+
this._mergeResults(result, tasksResult);
|
|
170
|
+
|
|
171
|
+
// 4. Validate agents
|
|
172
|
+
const agentsResult = await this.validateAgents(squadPath);
|
|
173
|
+
this._mergeResults(result, agentsResult);
|
|
174
|
+
|
|
175
|
+
// In strict mode, warnings become errors
|
|
176
|
+
if (this.strict && result.warnings.length > 0) {
|
|
177
|
+
result.errors.push(...result.warnings);
|
|
178
|
+
result.warnings = [];
|
|
179
|
+
result.valid = false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this._log(`Validation complete: ${result.valid ? 'VALID' : 'INVALID'}`);
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Validate manifest against JSON Schema
|
|
188
|
+
*
|
|
189
|
+
* @param {string} squadPath - Path to squad directory
|
|
190
|
+
* @returns {Promise<ValidationResult>} Validation result for manifest
|
|
191
|
+
*/
|
|
192
|
+
async validateManifest(squadPath) {
|
|
193
|
+
this._log(`Validating manifest in: ${squadPath}`);
|
|
194
|
+
|
|
195
|
+
const result = {
|
|
196
|
+
valid: true,
|
|
197
|
+
errors: [],
|
|
198
|
+
warnings: [],
|
|
199
|
+
suggestions: [],
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Find manifest file
|
|
203
|
+
const manifestPath = await this._findManifest(squadPath);
|
|
204
|
+
if (!manifestPath) {
|
|
205
|
+
result.valid = false;
|
|
206
|
+
result.errors.push({
|
|
207
|
+
code: ValidationErrorCodes.MANIFEST_NOT_FOUND,
|
|
208
|
+
message: `No manifest found in ${squadPath}/ (expected squad.yaml or config.yaml)`,
|
|
209
|
+
suggestion: 'Create squad.yaml with required fields: name, version',
|
|
210
|
+
});
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Deprecation warning for config.yaml
|
|
215
|
+
const manifestFilename = path.basename(manifestPath);
|
|
216
|
+
if (manifestFilename === 'config.yaml') {
|
|
217
|
+
result.warnings.push({
|
|
218
|
+
code: ValidationErrorCodes.DEPRECATED_MANIFEST,
|
|
219
|
+
message: 'config.yaml is deprecated, rename to squad.yaml',
|
|
220
|
+
suggestion: 'mv config.yaml squad.yaml',
|
|
221
|
+
file: manifestFilename,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Parse YAML
|
|
226
|
+
let manifest;
|
|
227
|
+
try {
|
|
228
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
229
|
+
manifest = yaml.load(content);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
result.valid = false;
|
|
232
|
+
result.errors.push({
|
|
233
|
+
code: ValidationErrorCodes.YAML_PARSE_ERROR,
|
|
234
|
+
message: `Failed to parse manifest: ${error.message}`,
|
|
235
|
+
suggestion: 'Check YAML syntax - use a YAML linter',
|
|
236
|
+
file: manifestFilename,
|
|
237
|
+
});
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Validate against schema
|
|
242
|
+
if (this.validateSchema && manifest) {
|
|
243
|
+
const schemaValid = this.validateSchema(manifest);
|
|
244
|
+
if (!schemaValid) {
|
|
245
|
+
result.valid = false;
|
|
246
|
+
for (const err of this.validateSchema.errors) {
|
|
247
|
+
result.errors.push({
|
|
248
|
+
code: ValidationErrorCodes.SCHEMA_ERROR,
|
|
249
|
+
path: err.instancePath || '/',
|
|
250
|
+
message: err.message,
|
|
251
|
+
suggestion: this._getSchemaSuggestion(err),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this._log(`Manifest validation: ${result.valid ? 'PASS' : 'FAIL'}`);
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Validate directory structure
|
|
263
|
+
*
|
|
264
|
+
* Checks for expected directories in task-first architecture.
|
|
265
|
+
*
|
|
266
|
+
* @param {string} squadPath - Path to squad directory
|
|
267
|
+
* @returns {Promise<ValidationResult>} Validation result for structure
|
|
268
|
+
*/
|
|
269
|
+
async validateStructure(squadPath) {
|
|
270
|
+
this._log(`Validating structure of: ${squadPath}`);
|
|
271
|
+
|
|
272
|
+
const result = {
|
|
273
|
+
valid: true,
|
|
274
|
+
errors: [],
|
|
275
|
+
warnings: [],
|
|
276
|
+
suggestions: [],
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Expected directories (task-first: tasks and agents are primary)
|
|
280
|
+
const expectedDirs = ['tasks', 'agents'];
|
|
281
|
+
const optionalDirs = [
|
|
282
|
+
'workflows',
|
|
283
|
+
'checklists',
|
|
284
|
+
'templates',
|
|
285
|
+
'tools',
|
|
286
|
+
'scripts',
|
|
287
|
+
'data',
|
|
288
|
+
'config',
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
// Check expected directories (warn if missing)
|
|
292
|
+
for (const dir of expectedDirs) {
|
|
293
|
+
const dirPath = path.join(squadPath, dir);
|
|
294
|
+
if (!(await this._pathExists(dirPath))) {
|
|
295
|
+
result.warnings.push({
|
|
296
|
+
code: ValidationErrorCodes.MISSING_DIRECTORY,
|
|
297
|
+
message: `Expected directory not found: ${dir}/`,
|
|
298
|
+
suggestion: `mkdir ${dir} (task-first architecture recommends tasks/ and agents/)`,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Validate files referenced in manifest exist
|
|
304
|
+
const manifestPath = await this._findManifest(squadPath);
|
|
305
|
+
if (manifestPath) {
|
|
306
|
+
try {
|
|
307
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
308
|
+
const manifest = yaml.load(content);
|
|
309
|
+
|
|
310
|
+
if (manifest && manifest.components) {
|
|
311
|
+
await this._validateReferencedFiles(
|
|
312
|
+
squadPath,
|
|
313
|
+
manifest.components,
|
|
314
|
+
result,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
// Already handled in manifest validation
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
this._log(`Structure validation: ${result.errors.length} errors, ${result.warnings.length} warnings`);
|
|
323
|
+
return result;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Validate task files against TASK-FORMAT-SPECIFICATION-V1
|
|
328
|
+
*
|
|
329
|
+
* @param {string} squadPath - Path to squad directory
|
|
330
|
+
* @returns {Promise<ValidationResult>} Validation result for tasks
|
|
331
|
+
*/
|
|
332
|
+
async validateTasks(squadPath) {
|
|
333
|
+
this._log(`Validating tasks in: ${squadPath}`);
|
|
334
|
+
|
|
335
|
+
const result = {
|
|
336
|
+
valid: true,
|
|
337
|
+
errors: [],
|
|
338
|
+
warnings: [],
|
|
339
|
+
suggestions: [],
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const tasksDir = path.join(squadPath, 'tasks');
|
|
343
|
+
|
|
344
|
+
// Check if tasks directory exists
|
|
345
|
+
if (!(await this._pathExists(tasksDir))) {
|
|
346
|
+
return result; // Already warned in structure validation
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Get task files
|
|
350
|
+
let files;
|
|
351
|
+
try {
|
|
352
|
+
files = await fs.readdir(tasksDir);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
result.errors.push({
|
|
355
|
+
code: ValidationErrorCodes.TASK_READ_ERROR,
|
|
356
|
+
message: `Failed to read tasks directory: ${error.message}`,
|
|
357
|
+
});
|
|
358
|
+
result.valid = false;
|
|
359
|
+
return result;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const taskFiles = files.filter((f) => f.endsWith('.md'));
|
|
363
|
+
|
|
364
|
+
if (taskFiles.length === 0) {
|
|
365
|
+
result.warnings.push({
|
|
366
|
+
code: ValidationErrorCodes.NO_TASKS,
|
|
367
|
+
message: 'No task files found in tasks/',
|
|
368
|
+
suggestion: 'Task-first architecture: Create at least one task file',
|
|
369
|
+
});
|
|
370
|
+
return result;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Validate each task file
|
|
374
|
+
for (const taskFile of taskFiles) {
|
|
375
|
+
const taskPath = path.join(tasksDir, taskFile);
|
|
376
|
+
const taskResult = await this._validateTaskFile(taskPath);
|
|
377
|
+
this._mergeResults(result, taskResult);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
this._log(`Task validation: ${taskFiles.length} files checked`);
|
|
381
|
+
return result;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Validate agent definitions
|
|
386
|
+
*
|
|
387
|
+
* @param {string} squadPath - Path to squad directory
|
|
388
|
+
* @returns {Promise<ValidationResult>} Validation result for agents
|
|
389
|
+
*/
|
|
390
|
+
async validateAgents(squadPath) {
|
|
391
|
+
this._log(`Validating agents in: ${squadPath}`);
|
|
392
|
+
|
|
393
|
+
const result = {
|
|
394
|
+
valid: true,
|
|
395
|
+
errors: [],
|
|
396
|
+
warnings: [],
|
|
397
|
+
suggestions: [],
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const agentsDir = path.join(squadPath, 'agents');
|
|
401
|
+
|
|
402
|
+
// Check if agents directory exists
|
|
403
|
+
if (!(await this._pathExists(agentsDir))) {
|
|
404
|
+
return result; // Already warned in structure validation
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Get agent files
|
|
408
|
+
let files;
|
|
409
|
+
try {
|
|
410
|
+
files = await fs.readdir(agentsDir);
|
|
411
|
+
} catch (error) {
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const agentFiles = files.filter((f) => f.endsWith('.md'));
|
|
416
|
+
|
|
417
|
+
// Validate each agent file
|
|
418
|
+
for (const agentFile of agentFiles) {
|
|
419
|
+
const agentPath = path.join(agentsDir, agentFile);
|
|
420
|
+
try {
|
|
421
|
+
const content = await fs.readFile(agentPath, 'utf-8');
|
|
422
|
+
|
|
423
|
+
// Check for basic agent structure (YAML frontmatter or markdown structure)
|
|
424
|
+
const hasYamlFrontmatter = content.includes('agent:');
|
|
425
|
+
const hasMarkdownHeading = content.match(/^#\s+.+/m);
|
|
426
|
+
|
|
427
|
+
if (!hasYamlFrontmatter && !hasMarkdownHeading) {
|
|
428
|
+
result.warnings.push({
|
|
429
|
+
code: ValidationErrorCodes.AGENT_INVALID_FORMAT,
|
|
430
|
+
file: agentFile,
|
|
431
|
+
message: 'Agent file may not follow AIOS agent definition format',
|
|
432
|
+
suggestion:
|
|
433
|
+
'Use agent: YAML frontmatter or markdown heading structure',
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Check naming convention (kebab-case)
|
|
438
|
+
if (!this._isKebabCase(path.basename(agentFile, '.md'))) {
|
|
439
|
+
result.warnings.push({
|
|
440
|
+
code: ValidationErrorCodes.INVALID_NAMING,
|
|
441
|
+
file: agentFile,
|
|
442
|
+
message: 'Agent filename should be kebab-case',
|
|
443
|
+
suggestion: 'Rename to use lowercase letters and hyphens only',
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
} catch (error) {
|
|
447
|
+
result.errors.push({
|
|
448
|
+
code: ValidationErrorCodes.TASK_READ_ERROR,
|
|
449
|
+
file: agentFile,
|
|
450
|
+
message: `Failed to read agent file: ${error.message}`,
|
|
451
|
+
});
|
|
452
|
+
result.valid = false;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
this._log(`Agent validation: ${agentFiles.length} files checked`);
|
|
457
|
+
return result;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Format validation result for display
|
|
462
|
+
*
|
|
463
|
+
* @param {ValidationResult} result - Validation result
|
|
464
|
+
* @param {string} squadPath - Path to squad
|
|
465
|
+
* @returns {string} Formatted output
|
|
466
|
+
*/
|
|
467
|
+
formatResult(result, squadPath) {
|
|
468
|
+
const lines = [];
|
|
469
|
+
|
|
470
|
+
lines.push(`Validating squad: ${squadPath}/`);
|
|
471
|
+
lines.push('');
|
|
472
|
+
|
|
473
|
+
// Errors
|
|
474
|
+
if (result.errors.length > 0) {
|
|
475
|
+
lines.push(`Errors: ${result.errors.length}`);
|
|
476
|
+
for (const err of result.errors) {
|
|
477
|
+
const filePart = err.file ? ` (${err.file})` : '';
|
|
478
|
+
const pathPart = err.path ? ` at ${err.path}` : '';
|
|
479
|
+
lines.push(` - [${err.code}]${pathPart}${filePart}: ${err.message}`);
|
|
480
|
+
if (err.suggestion) {
|
|
481
|
+
lines.push(` Suggestion: ${err.suggestion}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
lines.push('');
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Warnings
|
|
488
|
+
if (result.warnings.length > 0) {
|
|
489
|
+
lines.push(`Warnings: ${result.warnings.length}`);
|
|
490
|
+
for (const warn of result.warnings) {
|
|
491
|
+
const filePart = warn.file ? ` (${warn.file})` : '';
|
|
492
|
+
lines.push(` - [${warn.code}]${filePart}: ${warn.message}`);
|
|
493
|
+
if (warn.suggestion) {
|
|
494
|
+
lines.push(` Suggestion: ${warn.suggestion}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
lines.push('');
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Result
|
|
501
|
+
if (result.valid) {
|
|
502
|
+
if (result.warnings.length > 0) {
|
|
503
|
+
lines.push('Result: VALID (with warnings)');
|
|
504
|
+
} else {
|
|
505
|
+
lines.push('Result: VALID');
|
|
506
|
+
}
|
|
507
|
+
} else {
|
|
508
|
+
lines.push('Result: INVALID');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return lines.join('\n');
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// ============ Private Helper Methods ============
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Find manifest file in squad directory
|
|
518
|
+
* @private
|
|
519
|
+
*/
|
|
520
|
+
async _findManifest(squadPath) {
|
|
521
|
+
for (const filename of MANIFEST_FILES) {
|
|
522
|
+
const manifestPath = path.join(squadPath, filename);
|
|
523
|
+
if (await this._pathExists(manifestPath)) {
|
|
524
|
+
return manifestPath;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Check if path exists
|
|
532
|
+
* @private
|
|
533
|
+
*/
|
|
534
|
+
async _pathExists(filePath) {
|
|
535
|
+
try {
|
|
536
|
+
await fs.access(filePath);
|
|
537
|
+
return true;
|
|
538
|
+
} catch {
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Validate a single task file
|
|
545
|
+
* @private
|
|
546
|
+
*/
|
|
547
|
+
async _validateTaskFile(taskPath) {
|
|
548
|
+
const result = {
|
|
549
|
+
valid: true,
|
|
550
|
+
errors: [],
|
|
551
|
+
warnings: [],
|
|
552
|
+
suggestions: [],
|
|
553
|
+
};
|
|
554
|
+
const filename = path.basename(taskPath);
|
|
555
|
+
|
|
556
|
+
try {
|
|
557
|
+
const content = await fs.readFile(taskPath, 'utf-8');
|
|
558
|
+
|
|
559
|
+
// Check for required fields (case-insensitive, handle accents)
|
|
560
|
+
for (const field of TASK_REQUIRED_FIELDS) {
|
|
561
|
+
// Create patterns that handle Portuguese accents
|
|
562
|
+
const patterns = [
|
|
563
|
+
new RegExp(`^[#*-]*\\s*${field}\\s*:`, 'im'),
|
|
564
|
+
new RegExp(
|
|
565
|
+
`^[#*-]*\\s*${field.replace(/a/g, '[aá]').replace(/i/g, '[ií]')}\\s*:`,
|
|
566
|
+
'im',
|
|
567
|
+
),
|
|
568
|
+
// Also check for markdown headers with the field
|
|
569
|
+
new RegExp(`^#+\\s*${field}`, 'im'),
|
|
570
|
+
];
|
|
571
|
+
|
|
572
|
+
const found = patterns.some((p) => p.test(content));
|
|
573
|
+
if (!found) {
|
|
574
|
+
result.warnings.push({
|
|
575
|
+
code: ValidationErrorCodes.TASK_MISSING_FIELD,
|
|
576
|
+
file: filename,
|
|
577
|
+
message: `Task missing recommended field: ${field}`,
|
|
578
|
+
suggestion: `Add "${field}:" to ${filename} (TASK-FORMAT-SPECIFICATION-V1)`,
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Check naming convention
|
|
584
|
+
if (!this._isKebabCase(path.basename(filename, '.md'))) {
|
|
585
|
+
result.warnings.push({
|
|
586
|
+
code: ValidationErrorCodes.INVALID_NAMING,
|
|
587
|
+
file: filename,
|
|
588
|
+
message: 'Task filename should be kebab-case',
|
|
589
|
+
suggestion: 'Rename to use lowercase letters and hyphens only',
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
} catch (error) {
|
|
593
|
+
result.errors.push({
|
|
594
|
+
code: ValidationErrorCodes.TASK_READ_ERROR,
|
|
595
|
+
file: filename,
|
|
596
|
+
message: `Failed to read task: ${error.message}`,
|
|
597
|
+
});
|
|
598
|
+
result.valid = false;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return result;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Validate files referenced in manifest components
|
|
606
|
+
* @private
|
|
607
|
+
*/
|
|
608
|
+
async _validateReferencedFiles(squadPath, components, result) {
|
|
609
|
+
const componentDirs = {
|
|
610
|
+
tasks: 'tasks',
|
|
611
|
+
agents: 'agents',
|
|
612
|
+
workflows: 'workflows',
|
|
613
|
+
checklists: 'checklists',
|
|
614
|
+
templates: 'templates',
|
|
615
|
+
tools: 'tools',
|
|
616
|
+
scripts: 'scripts',
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
for (const [component, dir] of Object.entries(componentDirs)) {
|
|
620
|
+
if (components[component] && Array.isArray(components[component])) {
|
|
621
|
+
for (const file of components[component]) {
|
|
622
|
+
const filePath = path.join(squadPath, dir, file);
|
|
623
|
+
if (!(await this._pathExists(filePath))) {
|
|
624
|
+
result.errors.push({
|
|
625
|
+
code: ValidationErrorCodes.FILE_NOT_FOUND,
|
|
626
|
+
message: `Referenced file not found: ${dir}/${file}`,
|
|
627
|
+
suggestion: `Create ${filePath} or remove from components.${component}`,
|
|
628
|
+
});
|
|
629
|
+
result.valid = false;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Check if string is kebab-case
|
|
638
|
+
* @private
|
|
639
|
+
*/
|
|
640
|
+
_isKebabCase(str) {
|
|
641
|
+
return /^[a-z0-9]+(-[a-z0-9]+)*$/.test(str);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Get suggestion for schema error
|
|
646
|
+
* @private
|
|
647
|
+
*/
|
|
648
|
+
_getSchemaSuggestion(error) {
|
|
649
|
+
const suggestions = {
|
|
650
|
+
'must match pattern':
|
|
651
|
+
'Use correct format (kebab-case for names, semver for versions)',
|
|
652
|
+
'must be string': 'Wrap value in quotes',
|
|
653
|
+
'must be array': 'Use YAML array syntax: [item1, item2] or - item',
|
|
654
|
+
'must have required property': 'Add the missing required property',
|
|
655
|
+
'must be equal to one of the allowed values': 'Use one of the allowed values',
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
for (const [key, suggestion] of Object.entries(suggestions)) {
|
|
659
|
+
if (error.message && error.message.includes(key)) {
|
|
660
|
+
return suggestion;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return 'Check squad.yaml syntax against the schema';
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Merge validation results
|
|
668
|
+
* @private
|
|
669
|
+
*/
|
|
670
|
+
_mergeResults(target, source) {
|
|
671
|
+
target.errors.push(...(source.errors || []));
|
|
672
|
+
target.warnings.push(...(source.warnings || []));
|
|
673
|
+
target.suggestions.push(...(source.suggestions || []));
|
|
674
|
+
if (source.errors && source.errors.length > 0) {
|
|
675
|
+
target.valid = false;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
module.exports = {
|
|
681
|
+
SquadValidator,
|
|
682
|
+
ValidationErrorCodes,
|
|
683
|
+
TASK_REQUIRED_FIELDS,
|
|
684
|
+
MANIFEST_FILES,
|
|
685
|
+
};
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
```yaml
|
|
10
10
|
task: addMcp()
|
|
11
|
-
responsavel:
|
|
11
|
+
responsavel: DevOps Agent
|
|
12
12
|
responsavel_type: Agente
|
|
13
13
|
atomic_layer: Infrastructure
|
|
14
14
|
elicit: true
|
|
@@ -302,8 +302,8 @@ Next steps:
|
|
|
302
302
|
|
|
303
303
|
```yaml
|
|
304
304
|
task: add-mcp
|
|
305
|
-
version: 1.
|
|
306
|
-
story: Story
|
|
305
|
+
version: 1.1.0
|
|
306
|
+
story: Story 6.14 - MCP Governance Consolidation
|
|
307
307
|
dependencies:
|
|
308
308
|
- Docker MCP Toolkit
|
|
309
309
|
- docker mcp gateway running
|
|
@@ -312,8 +312,14 @@ tags:
|
|
|
312
312
|
- mcp
|
|
313
313
|
- docker
|
|
314
314
|
- dynamic
|
|
315
|
-
|
|
315
|
+
created_at: 2025-12-08
|
|
316
|
+
updated_at: 2025-12-17
|
|
316
317
|
agents:
|
|
317
|
-
- dev
|
|
318
318
|
- devops
|
|
319
|
+
changelog:
|
|
320
|
+
1.1.0:
|
|
321
|
+
- Changed: DevOps Agent now exclusive responsible (Story 6.14)
|
|
322
|
+
- Removed: Dev Agent from agents list
|
|
323
|
+
1.0.0:
|
|
324
|
+
- Initial version (Story 5.11)
|
|
319
325
|
```
|