aios-core 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/.aios-core/.session/current-session.json +14 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/registry/service-registry.json +6585 -6585
  4. package/.aios-core/data/entity-registry.yaml +208 -8
  5. package/.aios-core/data/registry-update-log.jsonl +165 -0
  6. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  7. package/.aios-core/development/scripts/backup-manager.js +606 -606
  8. package/.aios-core/development/scripts/branch-manager.js +389 -389
  9. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  10. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  11. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  12. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  13. package/.aios-core/development/scripts/diff-generator.js +351 -351
  14. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  15. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  16. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  17. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  18. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  19. package/.aios-core/development/scripts/modification-validator.js +554 -554
  20. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  21. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  22. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  23. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  24. package/.aios-core/development/scripts/security-checker.js +358 -358
  25. package/.aios-core/development/scripts/template-engine.js +239 -239
  26. package/.aios-core/development/scripts/template-validator.js +278 -278
  27. package/.aios-core/development/scripts/test-generator.js +843 -843
  28. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  29. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  30. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  31. package/.aios-core/development/scripts/version-tracker.js +526 -526
  32. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  33. package/.aios-core/development/tasks/validate-next-story.md +99 -2
  34. package/.aios-core/development/templates/service-template/README.md.hbs +158 -158
  35. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
  36. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
  37. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
  38. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
  39. package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
  40. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
  41. package/.aios-core/development/templates/squad-template/LICENSE +21 -21
  42. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -0
  43. package/.aios-core/docs/component-creation-guide.md +458 -0
  44. package/.aios-core/docs/session-update-pattern.md +307 -0
  45. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -0
  46. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -0
  47. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -0
  48. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -0
  49. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -0
  50. package/.aios-core/docs/template-syntax.md +267 -0
  51. package/.aios-core/docs/troubleshooting-guide.md +625 -0
  52. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +193 -193
  53. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  54. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  55. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  56. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  57. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  58. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  59. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  60. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  61. package/.aios-core/infrastructure/tests/utilities-audit-results.json +501 -0
  62. package/.aios-core/install-manifest.yaml +101 -101
  63. package/.aios-core/local-config.yaml.template +70 -70
  64. package/.aios-core/manifests/agents.csv +29 -0
  65. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  66. package/.aios-core/manifests/tasks.csv +198 -0
  67. package/.aios-core/manifests/workers.csv +204 -0
  68. package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
  69. package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
  70. package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
  71. package/.aios-core/monitor/hooks/notification.py +29 -29
  72. package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
  73. package/.aios-core/monitor/hooks/pre_compact.py +29 -29
  74. package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
  75. package/.aios-core/monitor/hooks/stop.py +29 -29
  76. package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
  77. package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
  78. package/.aios-core/product/templates/adr.hbs +125 -125
  79. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  80. package/.aios-core/product/templates/dbdr.hbs +241 -241
  81. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  82. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  83. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  84. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  85. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  86. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  87. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  88. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  89. package/.aios-core/product/templates/epic.hbs +212 -212
  90. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  91. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  92. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  93. package/.aios-core/product/templates/pmdr.hbs +186 -186
  94. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  95. package/.aios-core/product/templates/prd.hbs +201 -201
  96. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  97. package/.aios-core/product/templates/story.hbs +263 -263
  98. package/.aios-core/product/templates/task.hbs +170 -170
  99. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  100. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  101. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  102. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  103. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  104. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  105. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  106. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  107. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  108. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  109. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  110. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  111. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  112. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  113. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  114. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  115. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  116. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  117. package/.aios-core/scripts/pm.sh +0 -0
  118. package/.claude/hooks/enforce-architecture-first.py +196 -196
  119. package/.claude/hooks/mind-clone-governance.py +192 -192
  120. package/.claude/hooks/read-protection.py +151 -151
  121. package/.claude/hooks/slug-validation.py +176 -176
  122. package/.claude/hooks/sql-governance.py +182 -182
  123. package/.claude/hooks/write-path-validation.py +194 -194
  124. package/.claude/rules/agent-authority.md +105 -0
  125. package/.claude/rules/coderabbit-integration.md +93 -0
  126. package/.claude/rules/ids-principles.md +112 -0
  127. package/.claude/rules/story-lifecycle.md +139 -0
  128. package/.claude/rules/workflow-execution.md +150 -0
  129. package/LICENSE +48 -48
  130. package/bin/aios-minimal.js +0 -0
  131. package/bin/aios.js +0 -0
  132. package/package.json +1 -1
  133. package/packages/aios-install/bin/aios-install.js +0 -0
  134. package/packages/aios-install/bin/edmcp.js +0 -0
  135. package/packages/aios-pro-cli/bin/aios-pro.js +0 -0
  136. package/packages/installer/src/wizard/pro-setup.js +433 -49
  137. package/scripts/check-markdown-links.py +352 -352
  138. package/scripts/code-intel-health-check.js +343 -0
  139. package/scripts/dashboard-parallel-dev.sh +0 -0
  140. package/scripts/dashboard-parallel-phase3.sh +0 -0
  141. package/scripts/dashboard-parallel-phase4.sh +0 -0
  142. package/scripts/glue/README.md +355 -0
  143. package/scripts/glue/compose-agent-prompt.cjs +362 -0
  144. package/scripts/install-monitor-hooks.sh +0 -0
  145. package/.aios-core/lib/build.json +0 -1
@@ -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;