aios-core 4.2.13 → 4.2.15

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 (95) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/pro/license/degradation.js +220 -220
  64. package/pro/license/errors.js +450 -450
  65. package/pro/license/feature-gate.js +354 -354
  66. package/pro/license/index.js +181 -181
  67. package/pro/license/license-cache.js +523 -523
  68. package/pro/license/license-crypto.js +303 -303
  69. package/scripts/package-synapse.js +5 -5
  70. package/scripts/validate-package-completeness.js +3 -3
  71. package/.aios-core/.session/current-session.json +0 -14
  72. package/.aios-core/data/registry-update-log.jsonl +0 -191
  73. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  74. package/.aios-core/docs/component-creation-guide.md +0 -458
  75. package/.aios-core/docs/session-update-pattern.md +0 -307
  76. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  77. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  78. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  79. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  80. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  81. package/.aios-core/docs/template-syntax.md +0 -267
  82. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  83. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  84. package/.aios-core/manifests/agents.csv +0 -29
  85. package/.aios-core/manifests/tasks.csv +0 -198
  86. package/.aios-core/manifests/workers.csv +0 -204
  87. package/.claude/rules/agent-authority.md +0 -105
  88. package/.claude/rules/coderabbit-integration.md +0 -93
  89. package/.claude/rules/ids-principles.md +0 -112
  90. package/.claude/rules/story-lifecycle.md +0 -139
  91. package/.claude/rules/workflow-execution.md +0 -150
  92. package/scripts/glue/README.md +0 -355
  93. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  94. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  95. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,555 +1,555 @@
1
- const fs = require('fs').promises;
2
- const path = require('path');
3
- const yaml = require('js-yaml');
4
- const { validateYAML } = require('./yaml-validator');
5
- const DependencyAnalyzer = require('./dependency-analyzer');
6
- const SecurityChecker = require('./security-checker');
7
-
8
- /**
9
- * Validates component modifications before applying them
10
- */
11
- class ModificationValidator {
12
- constructor() {
13
- this.dependencyAnalyzer = new DependencyAnalyzer();
14
- this.securityChecker = new SecurityChecker();
15
- this.validationRules = {
16
- agent: this.validateAgentModification.bind(this),
17
- task: this.validateTaskModification.bind(this),
18
- workflow: this.validateWorkflowModification.bind(this),
19
- template: this.validateTemplateModification.bind(this)
20
- };
21
- }
22
-
23
- /**
24
- * Validate a component modification
25
- * @param {string} componentType - Type of component (agent, task, workflow, etc.)
26
- * @param {string} originalContent - Original component content
27
- * @param {string} modifiedContent - Modified component content
28
- * @param {Object} options - Validation options
29
- * @returns {Object} Validation result with errors and warnings
30
- */
31
- async validateModification(_componentType, originalContent, modifiedContent, options = {}) {
32
- const result = {
33
- valid: true,
34
- errors: [],
35
- warnings: [],
36
- suggestions: [],
37
- breakingChanges: []
38
- };
39
-
40
- // Basic validation
41
- if (!originalContent || !modifiedContent) {
42
- result.valid = false;
43
- result.errors.push('Original or modified content is empty');
44
- return result;
45
- }
46
-
47
- // Run type-specific validation
48
- if (this.validationRules[componentType]) {
49
- const typeResult = await this.validationRules[componentType](
50
- originalContent,
51
- modifiedContent,
52
- options
53
- );
54
- this.mergeResults(result, typeResult);
55
- } else {
56
- result.warnings.push(`No specific validation rules for component type: ${componentType}`);
57
- }
58
-
59
- // Run security validation
60
- const securityResult = await this.validateSecurity(modifiedContent, componentType);
61
- this.mergeResults(result, securityResult);
62
-
63
- // Check for breaking changes
64
- const breakingChanges = await this.detectBreakingChanges(
65
- _componentType,
66
- originalContent,
67
- modifiedContent
68
- );
69
- result.breakingChanges = breakingChanges;
70
-
71
- result.valid = result.errors.length === 0;
72
- return result;
73
- }
74
-
75
- /**
76
- * Validate agent modifications
77
- * @private
78
- */
79
- async validateAgentModification(originalContent, modifiedContent, options) {
80
- const result = {
81
- valid: true,
82
- errors: [],
83
- warnings: [],
84
- suggestions: []
85
- };
86
-
87
- try {
88
- // Parse agent content
89
- const originalParts = this.parseAgentContent(originalContent);
90
- const modifiedParts = this.parseAgentContent(modifiedContent);
91
-
92
- // Validate YAML structure
93
- const yamlValidation = validateYAML(modifiedParts.yaml);
94
- if (!yamlValidation.valid) {
95
- result.valid = false;
96
- result.errors.push(`YAML validation failed: ${yamlValidation.error}`);
97
- return result;
98
- }
99
-
100
- const originalMeta = yaml.load(originalParts.yaml);
101
- const modifiedMeta = yaml.load(modifiedParts.yaml);
102
-
103
- // Check required fields
104
- const requiredFields = ['name', 'id', 'title', 'icon', 'whenToUse'];
105
- for (const field of requiredFields) {
106
- if (!modifiedMeta[field]) {
107
- result.errors.push(`Required field missing: ${field}`);
108
- }
109
- }
110
-
111
- // Validate dependencies
112
- if (modifiedMeta.dependencies) {
113
- const depValidation = await this.validateDependencies(modifiedMeta.dependencies);
114
- this.mergeResults(result, depValidation);
115
- }
116
-
117
- // Check command structure
118
- if (modifiedMeta.commands) {
119
- for (const [cmd, desc] of Object.entries(modifiedMeta.commands)) {
120
- if (!desc || typeof desc !== 'string') {
121
- result.errors.push(`Invalid command description for: ${cmd}`);
122
- }
123
- }
124
- }
125
-
126
- // Check for removed commands (breaking change)
127
- if (originalMeta.commands && modifiedMeta.commands) {
128
- const removedCommands = Object.keys(originalMeta.commands)
129
- .filter(cmd => !modifiedMeta.commands[cmd]);
130
-
131
- if (removedCommands.length > 0) {
132
- result.warnings.push(`Commands removed: ${removedCommands.join(', ')}`);
133
- }
134
- }
135
-
136
- // Validate markdown content
137
- const markdownValidation = this.validateMarkdown(modifiedParts.markdown);
138
- this.mergeResults(result, markdownValidation);
139
-
140
- } catch (_error) {
141
- result.valid = false;
142
- result.errors.push(`Failed to parse agent content: ${error.message}`);
143
- }
144
-
145
- return result;
146
- }
147
-
148
- /**
149
- * Validate task modifications
150
- * @private
151
- */
152
- async validateTaskModification(originalContent, modifiedContent, options) {
153
- const result = {
154
- valid: true,
155
- errors: [],
156
- warnings: [],
157
- suggestions: []
158
- };
159
-
160
- // Check for required sections
161
- const requiredSections = ['## Purpose', '## Task Execution'];
162
- for (const section of requiredSections) {
163
- if (!modifiedContent.includes(section)) {
164
- result.errors.push(`Required section missing: ${section}`);
165
- }
166
- }
167
-
168
- // Validate elicitation blocks
169
- const elicitationBlocks = modifiedContent.match(/\[\[LLM:([\s\S]*?)\]\]/g) || [];
170
- for (const block of elicitationBlocks) {
171
- if (!block.includes(']]')) {
172
- result.errors.push('Unclosed elicitation block found');
173
- }
174
- }
175
-
176
- // Check for task flow consistency
177
- const taskSteps = modifiedContent.match(/###\s+\d+\.\s+/g) || [];
178
- const expectedSteps = taskSteps.length;
179
- for (let i = 1; i <= expectedSteps; i++) {
180
- if (!modifiedContent.includes(`### ${i}.`)) {
181
- result.warnings.push(`Task step ${i} appears to be missing or misnumbered`);
182
- }
183
- }
184
-
185
- // Validate output format if specified
186
- const outputMatch = modifiedContent.match(/## Output Format[\s\S]*?```([\s\S]*?)```/);
187
- if (outputMatch) {
188
- const outputFormat = outputMatch[1].trim();
189
- if (outputFormat.startsWith('json')) {
190
- try {
191
- // Extract JSON and validate
192
- const jsonContent = outputFormat.replace(/^json\s*/, '');
193
- JSON.parse(jsonContent);
194
- } catch (_error) {
195
- result.warnings.push('Output format contains invalid JSON example');
196
- }
197
- }
198
- }
199
-
200
- return result;
201
- }
202
-
203
- /**
204
- * Validate workflow modifications
205
- * @private
206
- */
207
- async validateWorkflowModification(originalContent, modifiedContent, options) {
208
- const result = {
209
- valid: true,
210
- errors: [],
211
- warnings: [],
212
- suggestions: []
213
- };
214
-
215
- try {
216
- const _originalWorkflow = yaml.load(originalContent);
217
- const modifiedWorkflow = yaml.load(modifiedContent);
218
-
219
- // Validate YAML structure
220
- const yamlValidation = validateYAML(modifiedContent);
221
- if (!yamlValidation.valid) {
222
- result.valid = false;
223
- result.errors.push(`YAML validation failed: ${yamlValidation.error}`);
224
- return result;
225
- }
226
-
227
- // Check required fields
228
- if (!modifiedWorkflow.name) {
229
- result.errors.push('Workflow name is required');
230
- }
231
-
232
- if (!modifiedWorkflow.phases || Object.keys(modifiedWorkflow.phases).length === 0) {
233
- result.errors.push('Workflow must have at least one phase');
234
- }
235
-
236
- // Validate phase structure
237
- const phaseSequences = [];
238
- for (const [phaseName, phase] of Object.entries(modifiedWorkflow.phases || {})) {
239
- if (!phase.sequence) {
240
- result.errors.push(`Phase '${phaseName}' missing sequence number`);
241
- } else {
242
- phaseSequences.push(phase.sequence);
243
- }
244
-
245
- if (!phase.agents || phase.agents.length === 0) {
246
- result.errors.push(`Phase '${phaseName}' must have at least one agent`);
247
- }
248
-
249
- // Validate agent references
250
- for (const agent of phase.agents || []) {
251
- const agentExists = await this.checkAgentExists(agent);
252
- if (!agentExists) {
253
- result.warnings.push(`Agent '${agent}' referenced in phase '${phaseName}' not found`);
254
- }
255
- }
256
- }
257
-
258
- // Check for sequence gaps
259
- phaseSequences.sort((a, b) => a - b);
260
- for (let i = 1; i < phaseSequences.length; i++) {
261
- if (phaseSequences[i] - phaseSequences[i-1] > 2) {
262
- result.warnings.push('Large gap in phase sequences detected');
263
- }
264
- }
265
-
266
- // Validate entry/exit criteria references
267
- for (const [phaseName, phase] of Object.entries(modifiedWorkflow.phases || {})) {
268
- if (phase.entry_criteria) {
269
- for (const criteria of phase.entry_criteria) {
270
- // Check if criteria references valid artifacts or phases
271
- const referencesValid = this.validateCriteriaReferences(
272
- criteria,
273
- modifiedWorkflow
274
- );
275
- if (!referencesValid) {
276
- result.warnings.push(
277
- `Entry criteria '${criteria}' in phase '${phaseName}' may reference non-existent artifact`
278
- );
279
- }
280
- }
281
- }
282
- }
283
-
284
- } catch (_error) {
285
- result.valid = false;
286
- result.errors.push(`Failed to parse workflow: ${error.message}`);
287
- }
288
-
289
- return result;
290
- }
291
-
292
- /**
293
- * Validate template modifications
294
- * @private
295
- */
296
- async validateTemplateModification(originalContent, modifiedContent, options) {
297
- const result = {
298
- valid: true,
299
- errors: [],
300
- warnings: [],
301
- suggestions: []
302
- };
303
-
304
- // Check for placeholder consistency
305
- const placeholders = modifiedContent.match(/\{\{[^}]+\}\}/g) || [];
306
- const uniquePlaceholders = [...new Set(placeholders)];
307
-
308
- // Warn about unreplaced placeholders
309
- if (uniquePlaceholders.length > 0) {
310
- result.suggestions.push(
311
- `Template contains ${uniquePlaceholders.length} placeholders: ${uniquePlaceholders.join(', ')}`
312
- );
313
- }
314
-
315
- // Validate LLM instruction blocks
316
- const llmBlocks = modifiedContent.match(/\[\[LLM:([\s\S]*?)\]\]/g) || [];
317
- for (const block of llmBlocks) {
318
- if (block.length > 1000) {
319
- result.warnings.push('LLM instruction block exceeds recommended length (1000 chars)');
320
- }
321
- }
322
-
323
- return result;
324
- }
325
-
326
- /**
327
- * Validate dependencies exist
328
- * @private
329
- */
330
- async validateDependencies(dependencies) {
331
- const result = {
332
- valid: true,
333
- errors: [],
334
- warnings: []
335
- };
336
-
337
- const baseDir = path.join(process.cwd(), 'aios-core');
338
-
339
- for (const [type, files] of Object.entries(dependencies)) {
340
- if (!Array.isArray(files)) continue;
341
-
342
- const typeDir = path.join(baseDir, type);
343
-
344
- for (const file of files) {
345
- const filePath = path.join(typeDir, file);
346
- try {
347
- await fs.access(filePath);
348
- } catch (_error) {
349
- result.warnings.push(`Dependency not found: ${type}/${file}`);
350
- }
351
- }
352
- }
353
-
354
- return result;
355
- }
356
-
357
- /**
358
- * Validate markdown content
359
- * @private
360
- */
361
- validateMarkdown(content) {
362
- const result = {
363
- valid: true,
364
- errors: [],
365
- warnings: []
366
- };
367
-
368
- // Check for broken internal links
369
- const internalLinks = content.match(/\[([^\]]+)\]\(#[^)]+\)/g) || [];
370
- for (const link of internalLinks) {
371
- const anchor = link.match(/#([^)]+)/)[1];
372
- const headingRegex = new RegExp(`^#+.*${anchor}`, 'mi');
373
- if (!headingRegex.test(content)) {
374
- result.warnings.push(`Broken internal link: ${link}`);
375
- }
376
- }
377
-
378
- // Check for code block closure
379
- const codeBlocks = content.split('```');
380
- if (codeBlocks.length % 2 === 0) {
381
- result.errors.push('Unclosed code block detected');
382
- }
383
-
384
- return result;
385
- }
386
-
387
- /**
388
- * Validate security concerns
389
- * @private
390
- */
391
- async validateSecurity(content, componentType) {
392
- const result = {
393
- valid: true,
394
- errors: [],
395
- warnings: []
396
- };
397
-
398
- const securityIssues = await this.securityChecker.checkContent(content);
399
-
400
- if (securityIssues.length > 0) {
401
- for (const issue of securityIssues) {
402
- if (issue.severity === 'high') {
403
- result.errors.push(`Security issue: ${issue.message}`);
404
- } else {
405
- result.warnings.push(`Security concern: ${issue.message}`);
406
- }
407
- }
408
- }
409
-
410
- return result;
411
- }
412
-
413
- /**
414
- * Detect breaking changes
415
- * @private
416
- */
417
- async detectBreakingChanges(_componentType, originalContent, modifiedContent) {
418
- const breakingChanges = [];
419
-
420
- switch (_componentType) {
421
- case 'agent':
422
- // Check for removed commands
423
- try {
424
- const originalParts = this.parseAgentContent(originalContent);
425
- const modifiedParts = this.parseAgentContent(modifiedContent);
426
- const originalMeta = yaml.load(originalParts.yaml);
427
- const modifiedMeta = yaml.load(modifiedParts.yaml);
428
-
429
- if (originalMeta.commands && modifiedMeta.commands) {
430
- const removedCommands = Object.keys(originalMeta.commands)
431
- .filter(cmd => !modifiedMeta.commands[cmd]);
432
-
433
- if (removedCommands.length > 0) {
434
- breakingChanges.push({
435
- type: 'removed_commands',
436
- items: removedCommands,
437
- impact: 'Users relying on these commands will need to update their workflows'
438
- });
439
- }
440
- }
441
- } catch (_error) {
442
- // Ignore parsing errors here
443
- }
444
- break;
445
-
446
- case 'task':
447
- // Check for changed output format
448
- const originalOutput = originalContent.match(/## Output Format[\s\S]*?```[\s\S]*?```/);
449
- const modifiedOutput = modifiedContent.match(/## Output Format[\s\S]*?```[\s\S]*?```/);
450
-
451
- if (originalOutput && modifiedOutput && originalOutput[0] !== modifiedOutput[0]) {
452
- breakingChanges.push({
453
- type: 'output_format_changed',
454
- impact: 'Components consuming this task output may need updates'
455
- });
456
- }
457
- break;
458
-
459
- case 'workflow':
460
- // Check for removed phases
461
- try {
462
- const _originalWorkflow = yaml.load(originalContent);
463
- const modifiedWorkflow = yaml.load(modifiedContent);
464
-
465
- const originalPhases = Object.keys(originalWorkflow.phases || {});
466
- const modifiedPhases = Object.keys(modifiedWorkflow.phases || {});
467
-
468
- const removedPhases = originalPhases.filter(p => !modifiedPhases.includes(p));
469
-
470
- if (removedPhases.length > 0) {
471
- breakingChanges.push({
472
- type: 'removed_phases',
473
- items: removedPhases,
474
- impact: 'Projects using this workflow may fail at removed phases'
475
- });
476
- }
477
- } catch (_error) {
478
- // Ignore parsing errors here
479
- }
480
- break;
481
- }
482
-
483
- return breakingChanges;
484
- }
485
-
486
- /**
487
- * Parse agent content into YAML and markdown sections
488
- * @private
489
- */
490
- parseAgentContent(content) {
491
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
492
- if (!match) {
493
- throw new Error('Invalid agent content format');
494
- }
495
-
496
- return {
497
- yaml: match[1],
498
- markdown: match[2]
499
- };
500
- }
501
-
502
- /**
503
- * Check if an agent exists
504
- * @private
505
- */
506
- async checkAgentExists(agentName) {
507
- const agentPath = path.join(process.cwd(), 'aios-core', 'agents', `${agentName}.md`);
508
- try {
509
- await fs.access(agentPath);
510
- return true;
511
- } catch {
512
- return false;
513
- }
514
- }
515
-
516
- /**
517
- * Validate criteria references
518
- * @private
519
- */
520
- validateCriteriaReferences(criteria, workflow) {
521
- // Simple check - could be enhanced
522
- const artifacts = new Set();
523
-
524
- for (const phase of Object.values(workflow.phases || {})) {
525
- if (phase.artifacts) {
526
- phase.artifacts.forEach(a => artifacts.add(a));
527
- }
528
- }
529
-
530
- // Check if criteria mentions any known artifact
531
- for (const artifact of artifacts) {
532
- if (criteria.toLowerCase().includes(artifact.toLowerCase())) {
533
- return true;
534
- }
535
- }
536
-
537
- return false;
538
- }
539
-
540
- /**
541
- * Merge validation results
542
- * @private
543
- */
544
- mergeResults(target, source) {
545
- target.errors.push(...(source.errors || []));
546
- target.warnings.push(...(source.warnings || []));
547
- target.suggestions.push(...(source.suggestions || []));
548
-
549
- if (source.valid === false) {
550
- target.valid = false;
551
- }
552
- }
553
- }
554
-
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const yaml = require('js-yaml');
4
+ const { validateYAML } = require('./yaml-validator');
5
+ const DependencyAnalyzer = require('./dependency-analyzer');
6
+ const SecurityChecker = require('./security-checker');
7
+
8
+ /**
9
+ * Validates component modifications before applying them
10
+ */
11
+ class ModificationValidator {
12
+ constructor() {
13
+ this.dependencyAnalyzer = new DependencyAnalyzer();
14
+ this.securityChecker = new SecurityChecker();
15
+ this.validationRules = {
16
+ agent: this.validateAgentModification.bind(this),
17
+ task: this.validateTaskModification.bind(this),
18
+ workflow: this.validateWorkflowModification.bind(this),
19
+ template: this.validateTemplateModification.bind(this)
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Validate a component modification
25
+ * @param {string} componentType - Type of component (agent, task, workflow, etc.)
26
+ * @param {string} originalContent - Original component content
27
+ * @param {string} modifiedContent - Modified component content
28
+ * @param {Object} options - Validation options
29
+ * @returns {Object} Validation result with errors and warnings
30
+ */
31
+ async validateModification(_componentType, originalContent, modifiedContent, options = {}) {
32
+ const result = {
33
+ valid: true,
34
+ errors: [],
35
+ warnings: [],
36
+ suggestions: [],
37
+ breakingChanges: []
38
+ };
39
+
40
+ // Basic validation
41
+ if (!originalContent || !modifiedContent) {
42
+ result.valid = false;
43
+ result.errors.push('Original or modified content is empty');
44
+ return result;
45
+ }
46
+
47
+ // Run type-specific validation
48
+ if (this.validationRules[componentType]) {
49
+ const typeResult = await this.validationRules[componentType](
50
+ originalContent,
51
+ modifiedContent,
52
+ options
53
+ );
54
+ this.mergeResults(result, typeResult);
55
+ } else {
56
+ result.warnings.push(`No specific validation rules for component type: ${componentType}`);
57
+ }
58
+
59
+ // Run security validation
60
+ const securityResult = await this.validateSecurity(modifiedContent, componentType);
61
+ this.mergeResults(result, securityResult);
62
+
63
+ // Check for breaking changes
64
+ const breakingChanges = await this.detectBreakingChanges(
65
+ _componentType,
66
+ originalContent,
67
+ modifiedContent
68
+ );
69
+ result.breakingChanges = breakingChanges;
70
+
71
+ result.valid = result.errors.length === 0;
72
+ return result;
73
+ }
74
+
75
+ /**
76
+ * Validate agent modifications
77
+ * @private
78
+ */
79
+ async validateAgentModification(originalContent, modifiedContent, options) {
80
+ const result = {
81
+ valid: true,
82
+ errors: [],
83
+ warnings: [],
84
+ suggestions: []
85
+ };
86
+
87
+ try {
88
+ // Parse agent content
89
+ const originalParts = this.parseAgentContent(originalContent);
90
+ const modifiedParts = this.parseAgentContent(modifiedContent);
91
+
92
+ // Validate YAML structure
93
+ const yamlValidation = validateYAML(modifiedParts.yaml);
94
+ if (!yamlValidation.valid) {
95
+ result.valid = false;
96
+ result.errors.push(`YAML validation failed: ${yamlValidation.error}`);
97
+ return result;
98
+ }
99
+
100
+ const originalMeta = yaml.load(originalParts.yaml);
101
+ const modifiedMeta = yaml.load(modifiedParts.yaml);
102
+
103
+ // Check required fields
104
+ const requiredFields = ['name', 'id', 'title', 'icon', 'whenToUse'];
105
+ for (const field of requiredFields) {
106
+ if (!modifiedMeta[field]) {
107
+ result.errors.push(`Required field missing: ${field}`);
108
+ }
109
+ }
110
+
111
+ // Validate dependencies
112
+ if (modifiedMeta.dependencies) {
113
+ const depValidation = await this.validateDependencies(modifiedMeta.dependencies);
114
+ this.mergeResults(result, depValidation);
115
+ }
116
+
117
+ // Check command structure
118
+ if (modifiedMeta.commands) {
119
+ for (const [cmd, desc] of Object.entries(modifiedMeta.commands)) {
120
+ if (!desc || typeof desc !== 'string') {
121
+ result.errors.push(`Invalid command description for: ${cmd}`);
122
+ }
123
+ }
124
+ }
125
+
126
+ // Check for removed commands (breaking change)
127
+ if (originalMeta.commands && modifiedMeta.commands) {
128
+ const removedCommands = Object.keys(originalMeta.commands)
129
+ .filter(cmd => !modifiedMeta.commands[cmd]);
130
+
131
+ if (removedCommands.length > 0) {
132
+ result.warnings.push(`Commands removed: ${removedCommands.join(', ')}`);
133
+ }
134
+ }
135
+
136
+ // Validate markdown content
137
+ const markdownValidation = this.validateMarkdown(modifiedParts.markdown);
138
+ this.mergeResults(result, markdownValidation);
139
+
140
+ } catch (_error) {
141
+ result.valid = false;
142
+ result.errors.push(`Failed to parse agent content: ${error.message}`);
143
+ }
144
+
145
+ return result;
146
+ }
147
+
148
+ /**
149
+ * Validate task modifications
150
+ * @private
151
+ */
152
+ async validateTaskModification(originalContent, modifiedContent, options) {
153
+ const result = {
154
+ valid: true,
155
+ errors: [],
156
+ warnings: [],
157
+ suggestions: []
158
+ };
159
+
160
+ // Check for required sections
161
+ const requiredSections = ['## Purpose', '## Task Execution'];
162
+ for (const section of requiredSections) {
163
+ if (!modifiedContent.includes(section)) {
164
+ result.errors.push(`Required section missing: ${section}`);
165
+ }
166
+ }
167
+
168
+ // Validate elicitation blocks
169
+ const elicitationBlocks = modifiedContent.match(/\[\[LLM:([\s\S]*?)\]\]/g) || [];
170
+ for (const block of elicitationBlocks) {
171
+ if (!block.includes(']]')) {
172
+ result.errors.push('Unclosed elicitation block found');
173
+ }
174
+ }
175
+
176
+ // Check for task flow consistency
177
+ const taskSteps = modifiedContent.match(/###\s+\d+\.\s+/g) || [];
178
+ const expectedSteps = taskSteps.length;
179
+ for (let i = 1; i <= expectedSteps; i++) {
180
+ if (!modifiedContent.includes(`### ${i}.`)) {
181
+ result.warnings.push(`Task step ${i} appears to be missing or misnumbered`);
182
+ }
183
+ }
184
+
185
+ // Validate output format if specified
186
+ const outputMatch = modifiedContent.match(/## Output Format[\s\S]*?```([\s\S]*?)```/);
187
+ if (outputMatch) {
188
+ const outputFormat = outputMatch[1].trim();
189
+ if (outputFormat.startsWith('json')) {
190
+ try {
191
+ // Extract JSON and validate
192
+ const jsonContent = outputFormat.replace(/^json\s*/, '');
193
+ JSON.parse(jsonContent);
194
+ } catch (_error) {
195
+ result.warnings.push('Output format contains invalid JSON example');
196
+ }
197
+ }
198
+ }
199
+
200
+ return result;
201
+ }
202
+
203
+ /**
204
+ * Validate workflow modifications
205
+ * @private
206
+ */
207
+ async validateWorkflowModification(originalContent, modifiedContent, options) {
208
+ const result = {
209
+ valid: true,
210
+ errors: [],
211
+ warnings: [],
212
+ suggestions: []
213
+ };
214
+
215
+ try {
216
+ const _originalWorkflow = yaml.load(originalContent);
217
+ const modifiedWorkflow = yaml.load(modifiedContent);
218
+
219
+ // Validate YAML structure
220
+ const yamlValidation = validateYAML(modifiedContent);
221
+ if (!yamlValidation.valid) {
222
+ result.valid = false;
223
+ result.errors.push(`YAML validation failed: ${yamlValidation.error}`);
224
+ return result;
225
+ }
226
+
227
+ // Check required fields
228
+ if (!modifiedWorkflow.name) {
229
+ result.errors.push('Workflow name is required');
230
+ }
231
+
232
+ if (!modifiedWorkflow.phases || Object.keys(modifiedWorkflow.phases).length === 0) {
233
+ result.errors.push('Workflow must have at least one phase');
234
+ }
235
+
236
+ // Validate phase structure
237
+ const phaseSequences = [];
238
+ for (const [phaseName, phase] of Object.entries(modifiedWorkflow.phases || {})) {
239
+ if (!phase.sequence) {
240
+ result.errors.push(`Phase '${phaseName}' missing sequence number`);
241
+ } else {
242
+ phaseSequences.push(phase.sequence);
243
+ }
244
+
245
+ if (!phase.agents || phase.agents.length === 0) {
246
+ result.errors.push(`Phase '${phaseName}' must have at least one agent`);
247
+ }
248
+
249
+ // Validate agent references
250
+ for (const agent of phase.agents || []) {
251
+ const agentExists = await this.checkAgentExists(agent);
252
+ if (!agentExists) {
253
+ result.warnings.push(`Agent '${agent}' referenced in phase '${phaseName}' not found`);
254
+ }
255
+ }
256
+ }
257
+
258
+ // Check for sequence gaps
259
+ phaseSequences.sort((a, b) => a - b);
260
+ for (let i = 1; i < phaseSequences.length; i++) {
261
+ if (phaseSequences[i] - phaseSequences[i-1] > 2) {
262
+ result.warnings.push('Large gap in phase sequences detected');
263
+ }
264
+ }
265
+
266
+ // Validate entry/exit criteria references
267
+ for (const [phaseName, phase] of Object.entries(modifiedWorkflow.phases || {})) {
268
+ if (phase.entry_criteria) {
269
+ for (const criteria of phase.entry_criteria) {
270
+ // Check if criteria references valid artifacts or phases
271
+ const referencesValid = this.validateCriteriaReferences(
272
+ criteria,
273
+ modifiedWorkflow
274
+ );
275
+ if (!referencesValid) {
276
+ result.warnings.push(
277
+ `Entry criteria '${criteria}' in phase '${phaseName}' may reference non-existent artifact`
278
+ );
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ } catch (_error) {
285
+ result.valid = false;
286
+ result.errors.push(`Failed to parse workflow: ${error.message}`);
287
+ }
288
+
289
+ return result;
290
+ }
291
+
292
+ /**
293
+ * Validate template modifications
294
+ * @private
295
+ */
296
+ async validateTemplateModification(originalContent, modifiedContent, options) {
297
+ const result = {
298
+ valid: true,
299
+ errors: [],
300
+ warnings: [],
301
+ suggestions: []
302
+ };
303
+
304
+ // Check for placeholder consistency
305
+ const placeholders = modifiedContent.match(/\{\{[^}]+\}\}/g) || [];
306
+ const uniquePlaceholders = [...new Set(placeholders)];
307
+
308
+ // Warn about unreplaced placeholders
309
+ if (uniquePlaceholders.length > 0) {
310
+ result.suggestions.push(
311
+ `Template contains ${uniquePlaceholders.length} placeholders: ${uniquePlaceholders.join(', ')}`
312
+ );
313
+ }
314
+
315
+ // Validate LLM instruction blocks
316
+ const llmBlocks = modifiedContent.match(/\[\[LLM:([\s\S]*?)\]\]/g) || [];
317
+ for (const block of llmBlocks) {
318
+ if (block.length > 1000) {
319
+ result.warnings.push('LLM instruction block exceeds recommended length (1000 chars)');
320
+ }
321
+ }
322
+
323
+ return result;
324
+ }
325
+
326
+ /**
327
+ * Validate dependencies exist
328
+ * @private
329
+ */
330
+ async validateDependencies(dependencies) {
331
+ const result = {
332
+ valid: true,
333
+ errors: [],
334
+ warnings: []
335
+ };
336
+
337
+ const baseDir = path.join(process.cwd(), 'aios-core');
338
+
339
+ for (const [type, files] of Object.entries(dependencies)) {
340
+ if (!Array.isArray(files)) continue;
341
+
342
+ const typeDir = path.join(baseDir, type);
343
+
344
+ for (const file of files) {
345
+ const filePath = path.join(typeDir, file);
346
+ try {
347
+ await fs.access(filePath);
348
+ } catch (_error) {
349
+ result.warnings.push(`Dependency not found: ${type}/${file}`);
350
+ }
351
+ }
352
+ }
353
+
354
+ return result;
355
+ }
356
+
357
+ /**
358
+ * Validate markdown content
359
+ * @private
360
+ */
361
+ validateMarkdown(content) {
362
+ const result = {
363
+ valid: true,
364
+ errors: [],
365
+ warnings: []
366
+ };
367
+
368
+ // Check for broken internal links
369
+ const internalLinks = content.match(/\[([^\]]+)\]\(#[^)]+\)/g) || [];
370
+ for (const link of internalLinks) {
371
+ const anchor = link.match(/#([^)]+)/)[1];
372
+ const headingRegex = new RegExp(`^#+.*${anchor}`, 'mi');
373
+ if (!headingRegex.test(content)) {
374
+ result.warnings.push(`Broken internal link: ${link}`);
375
+ }
376
+ }
377
+
378
+ // Check for code block closure
379
+ const codeBlocks = content.split('```');
380
+ if (codeBlocks.length % 2 === 0) {
381
+ result.errors.push('Unclosed code block detected');
382
+ }
383
+
384
+ return result;
385
+ }
386
+
387
+ /**
388
+ * Validate security concerns
389
+ * @private
390
+ */
391
+ async validateSecurity(content, componentType) {
392
+ const result = {
393
+ valid: true,
394
+ errors: [],
395
+ warnings: []
396
+ };
397
+
398
+ const securityIssues = await this.securityChecker.checkContent(content);
399
+
400
+ if (securityIssues.length > 0) {
401
+ for (const issue of securityIssues) {
402
+ if (issue.severity === 'high') {
403
+ result.errors.push(`Security issue: ${issue.message}`);
404
+ } else {
405
+ result.warnings.push(`Security concern: ${issue.message}`);
406
+ }
407
+ }
408
+ }
409
+
410
+ return result;
411
+ }
412
+
413
+ /**
414
+ * Detect breaking changes
415
+ * @private
416
+ */
417
+ async detectBreakingChanges(_componentType, originalContent, modifiedContent) {
418
+ const breakingChanges = [];
419
+
420
+ switch (_componentType) {
421
+ case 'agent':
422
+ // Check for removed commands
423
+ try {
424
+ const originalParts = this.parseAgentContent(originalContent);
425
+ const modifiedParts = this.parseAgentContent(modifiedContent);
426
+ const originalMeta = yaml.load(originalParts.yaml);
427
+ const modifiedMeta = yaml.load(modifiedParts.yaml);
428
+
429
+ if (originalMeta.commands && modifiedMeta.commands) {
430
+ const removedCommands = Object.keys(originalMeta.commands)
431
+ .filter(cmd => !modifiedMeta.commands[cmd]);
432
+
433
+ if (removedCommands.length > 0) {
434
+ breakingChanges.push({
435
+ type: 'removed_commands',
436
+ items: removedCommands,
437
+ impact: 'Users relying on these commands will need to update their workflows'
438
+ });
439
+ }
440
+ }
441
+ } catch (_error) {
442
+ // Ignore parsing errors here
443
+ }
444
+ break;
445
+
446
+ case 'task':
447
+ // Check for changed output format
448
+ const originalOutput = originalContent.match(/## Output Format[\s\S]*?```[\s\S]*?```/);
449
+ const modifiedOutput = modifiedContent.match(/## Output Format[\s\S]*?```[\s\S]*?```/);
450
+
451
+ if (originalOutput && modifiedOutput && originalOutput[0] !== modifiedOutput[0]) {
452
+ breakingChanges.push({
453
+ type: 'output_format_changed',
454
+ impact: 'Components consuming this task output may need updates'
455
+ });
456
+ }
457
+ break;
458
+
459
+ case 'workflow':
460
+ // Check for removed phases
461
+ try {
462
+ const _originalWorkflow = yaml.load(originalContent);
463
+ const modifiedWorkflow = yaml.load(modifiedContent);
464
+
465
+ const originalPhases = Object.keys(originalWorkflow.phases || {});
466
+ const modifiedPhases = Object.keys(modifiedWorkflow.phases || {});
467
+
468
+ const removedPhases = originalPhases.filter(p => !modifiedPhases.includes(p));
469
+
470
+ if (removedPhases.length > 0) {
471
+ breakingChanges.push({
472
+ type: 'removed_phases',
473
+ items: removedPhases,
474
+ impact: 'Projects using this workflow may fail at removed phases'
475
+ });
476
+ }
477
+ } catch (_error) {
478
+ // Ignore parsing errors here
479
+ }
480
+ break;
481
+ }
482
+
483
+ return breakingChanges;
484
+ }
485
+
486
+ /**
487
+ * Parse agent content into YAML and markdown sections
488
+ * @private
489
+ */
490
+ parseAgentContent(content) {
491
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
492
+ if (!match) {
493
+ throw new Error('Invalid agent content format');
494
+ }
495
+
496
+ return {
497
+ yaml: match[1],
498
+ markdown: match[2]
499
+ };
500
+ }
501
+
502
+ /**
503
+ * Check if an agent exists
504
+ * @private
505
+ */
506
+ async checkAgentExists(agentName) {
507
+ const agentPath = path.join(process.cwd(), 'aios-core', 'agents', `${agentName}.md`);
508
+ try {
509
+ await fs.access(agentPath);
510
+ return true;
511
+ } catch {
512
+ return false;
513
+ }
514
+ }
515
+
516
+ /**
517
+ * Validate criteria references
518
+ * @private
519
+ */
520
+ validateCriteriaReferences(criteria, workflow) {
521
+ // Simple check - could be enhanced
522
+ const artifacts = new Set();
523
+
524
+ for (const phase of Object.values(workflow.phases || {})) {
525
+ if (phase.artifacts) {
526
+ phase.artifacts.forEach(a => artifacts.add(a));
527
+ }
528
+ }
529
+
530
+ // Check if criteria mentions any known artifact
531
+ for (const artifact of artifacts) {
532
+ if (criteria.toLowerCase().includes(artifact.toLowerCase())) {
533
+ return true;
534
+ }
535
+ }
536
+
537
+ return false;
538
+ }
539
+
540
+ /**
541
+ * Merge validation results
542
+ * @private
543
+ */
544
+ mergeResults(target, source) {
545
+ target.errors.push(...(source.errors || []));
546
+ target.warnings.push(...(source.warnings || []));
547
+ target.suggestions.push(...(source.suggestions || []));
548
+
549
+ if (source.valid === false) {
550
+ target.valid = false;
551
+ }
552
+ }
553
+ }
554
+
555
555
  module.exports = ModificationValidator;