aios-core 2.2.2 → 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.
Files changed (105) hide show
  1. package/.aios-core/.session/current-session.json +14 -14
  2. package/.aios-core/cli/commands/migrate/validate.js +1 -1
  3. package/.aios-core/core/docs/session-update-pattern.md +17 -10
  4. package/.aios-core/core/elicitation/elicitation-engine.js +11 -6
  5. package/.aios-core/core/elicitation/session-manager.js +2 -1
  6. package/.aios-core/core/registry/registry-schema.json +166 -166
  7. package/.aios-core/core/registry/service-registry.json +6585 -6585
  8. package/.aios-core/core-config.yaml +12 -1
  9. package/.aios-core/data/agent-config-requirements.yaml +5 -5
  10. package/.aios-core/development/agents/devops.md +12 -0
  11. package/.aios-core/development/scripts/squad/README.md +112 -0
  12. package/.aios-core/development/scripts/squad/index.js +41 -0
  13. package/.aios-core/development/scripts/squad/squad-loader.js +359 -0
  14. package/.aios-core/development/scripts/squad/squad-validator.js +685 -0
  15. package/.aios-core/development/tasks/add-mcp.md +11 -5
  16. package/.aios-core/development/tasks/search-mcp.md +309 -0
  17. package/.aios-core/development/tasks/setup-mcp-docker.md +11 -8
  18. package/.aios-core/development/tasks/squad-creator-validate.md +151 -0
  19. package/.aios-core/docs/standards/AGENT-PERSONALIZATION-STANDARD-V1.md +3 -3
  20. package/.aios-core/index.d.ts +7 -7
  21. package/.aios-core/index.js +1 -1
  22. package/.aios-core/infrastructure/scripts/batch-creator.js +1 -1
  23. package/.aios-core/infrastructure/scripts/component-generator.js +1 -1
  24. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  25. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  26. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  27. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  28. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  29. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  30. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  31. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  32. package/.aios-core/infrastructure/tests/utilities-audit-results.json +500 -500
  33. package/.aios-core/infrastructure/tools/README.md +1 -1
  34. package/.aios-core/install-manifest.yaml +4 -1
  35. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  36. package/.aios-core/manifests/workers.csv +203 -203
  37. package/.aios-core/package.json +102 -102
  38. package/.aios-core/product/templates/activation-instructions-template.md +7 -7
  39. package/.aios-core/product/templates/adr.hbs +125 -125
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/dbdr.hbs +241 -241
  42. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  43. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  44. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  46. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  47. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  48. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  49. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  50. package/.aios-core/product/templates/epic.hbs +212 -212
  51. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  52. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  53. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  54. package/.aios-core/product/templates/pmdr.hbs +186 -186
  55. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  56. package/.aios-core/product/templates/prd.hbs +201 -201
  57. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  58. package/.aios-core/product/templates/story.hbs +263 -263
  59. package/.aios-core/product/templates/task.hbs +170 -170
  60. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  61. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  62. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  63. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  64. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  65. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  66. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  67. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  68. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  69. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  70. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  71. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  72. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  73. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  74. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  75. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  76. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  77. package/.aios-core/schemas/squad-schema.json +185 -0
  78. package/.aios-core/scripts/README.md +90 -322
  79. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  80. package/.claude/rules/mcp-usage.md +116 -100
  81. package/LICENSE +48 -48
  82. package/README.md +3 -4
  83. package/bin/aios.js +2 -1
  84. package/package.json +1 -3
  85. package/packages/installer/package.json +39 -39
  86. package/templates/squad/LICENSE +21 -21
  87. package/templates/squad/README.md +37 -37
  88. package/templates/squad/agents/example-agent.yaml +36 -36
  89. package/templates/squad/package.json +19 -19
  90. package/templates/squad/squad.yaml +25 -25
  91. package/templates/squad/tasks/example-task.yaml +46 -46
  92. package/templates/squad/templates/example-template.md +24 -24
  93. package/templates/squad/tests/example-agent.test.js +53 -53
  94. package/templates/squad/workflows/example-workflow.yaml +54 -54
  95. package/tools/diagnose-npx-issue.ps1 +96 -96
  96. package/tools/quick-diagnose.cmd +85 -85
  97. package/tools/quick-diagnose.ps1 +117 -117
  98. package/.aios-core/core/data/agent-config-requirements.yaml +0 -368
  99. package/.aios-core/core/data/aios-kb.md +0 -924
  100. package/.aios-core/core/data/workflow-patterns.yaml +0 -267
  101. package/.aios-core/product/templates/1mcp-config.yaml +0 -225
  102. package/.aios-core/scripts/context-detector.js +0 -226
  103. package/.aios-core/scripts/elicitation-engine.js +0 -385
  104. package/.aios-core/scripts/elicitation-session-manager.js +0 -300
  105. 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: Dev Agent / DevOps Agent
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.0.0
306
- story: Story 5.11 - Docker MCP Migration
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
- updated_at: 2025-12-08
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
  ```