aios-core 4.0.2 → 4.0.4

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/registry-update-log.jsonl +113 -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/templates/service-template/README.md.hbs +158 -158
  33. package/.aios-core/development/templates/service-template/__tests__/index.test.ts.hbs +237 -237
  34. package/.aios-core/development/templates/service-template/client.ts.hbs +403 -403
  35. package/.aios-core/development/templates/service-template/errors.ts.hbs +182 -182
  36. package/.aios-core/development/templates/service-template/index.ts.hbs +120 -120
  37. package/.aios-core/development/templates/service-template/package.json.hbs +87 -87
  38. package/.aios-core/development/templates/service-template/types.ts.hbs +145 -145
  39. package/.aios-core/development/templates/squad-template/LICENSE +21 -21
  40. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +335 -0
  41. package/.aios-core/docs/component-creation-guide.md +458 -0
  42. package/.aios-core/docs/session-update-pattern.md +307 -0
  43. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +1963 -0
  44. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +1190 -0
  45. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +439 -0
  46. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +5398 -0
  47. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +523 -0
  48. package/.aios-core/docs/template-syntax.md +267 -0
  49. package/.aios-core/docs/troubleshooting-guide.md +625 -0
  50. package/.aios-core/infrastructure/templates/aios-sync.yaml.template +193 -193
  51. package/.aios-core/infrastructure/templates/coderabbit.yaml.template +279 -279
  52. package/.aios-core/infrastructure/templates/github-workflows/ci.yml.template +169 -169
  53. package/.aios-core/infrastructure/templates/github-workflows/pr-automation.yml.template +330 -330
  54. package/.aios-core/infrastructure/templates/github-workflows/release.yml.template +196 -196
  55. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -63
  56. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -18
  57. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -85
  58. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -145
  59. package/.aios-core/infrastructure/tests/utilities-audit-results.json +501 -0
  60. package/.aios-core/install-manifest.yaml +97 -97
  61. package/.aios-core/local-config.yaml.template +68 -68
  62. package/.aios-core/manifests/agents.csv +1 -0
  63. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  64. package/.aios-core/manifests/tasks.csv +121 -0
  65. package/.aios-core/manifests/workers.csv +204 -0
  66. package/.aios-core/monitor/hooks/lib/__init__.py +1 -1
  67. package/.aios-core/monitor/hooks/lib/enrich.py +58 -58
  68. package/.aios-core/monitor/hooks/lib/send_event.py +47 -47
  69. package/.aios-core/monitor/hooks/notification.py +29 -29
  70. package/.aios-core/monitor/hooks/post_tool_use.py +45 -45
  71. package/.aios-core/monitor/hooks/pre_compact.py +29 -29
  72. package/.aios-core/monitor/hooks/pre_tool_use.py +40 -40
  73. package/.aios-core/monitor/hooks/stop.py +29 -29
  74. package/.aios-core/monitor/hooks/subagent_stop.py +29 -29
  75. package/.aios-core/monitor/hooks/user_prompt_submit.py +38 -38
  76. package/.aios-core/product/templates/adr.hbs +125 -125
  77. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  78. package/.aios-core/product/templates/dbdr.hbs +241 -241
  79. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  80. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  81. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  82. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  83. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  84. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  85. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  86. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  87. package/.aios-core/product/templates/epic.hbs +212 -212
  88. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  89. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  90. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  91. package/.aios-core/product/templates/pmdr.hbs +186 -186
  92. package/.aios-core/product/templates/prd-v2.0.hbs +216 -216
  93. package/.aios-core/product/templates/prd.hbs +201 -201
  94. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  95. package/.aios-core/product/templates/story.hbs +263 -263
  96. package/.aios-core/product/templates/task.hbs +170 -170
  97. package/.aios-core/product/templates/tmpl-comment-on-examples.sql +158 -158
  98. package/.aios-core/product/templates/tmpl-migration-script.sql +91 -91
  99. package/.aios-core/product/templates/tmpl-rls-granular-policies.sql +104 -104
  100. package/.aios-core/product/templates/tmpl-rls-kiss-policy.sql +10 -10
  101. package/.aios-core/product/templates/tmpl-rls-roles.sql +135 -135
  102. package/.aios-core/product/templates/tmpl-rls-simple.sql +77 -77
  103. package/.aios-core/product/templates/tmpl-rls-tenant.sql +152 -152
  104. package/.aios-core/product/templates/tmpl-rollback-script.sql +77 -77
  105. package/.aios-core/product/templates/tmpl-seed-data.sql +140 -140
  106. package/.aios-core/product/templates/tmpl-smoke-test.sql +16 -16
  107. package/.aios-core/product/templates/tmpl-staging-copy-merge.sql +139 -139
  108. package/.aios-core/product/templates/tmpl-stored-proc.sql +140 -140
  109. package/.aios-core/product/templates/tmpl-trigger.sql +152 -152
  110. package/.aios-core/product/templates/tmpl-view-materialized.sql +133 -133
  111. package/.aios-core/product/templates/tmpl-view.sql +177 -177
  112. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  113. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  114. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  115. package/.aios-core/scripts/pm.sh +0 -0
  116. package/.claude/hooks/enforce-architecture-first.py +196 -0
  117. package/.claude/hooks/install-hooks.sh +41 -0
  118. package/.claude/hooks/mind-clone-governance.py +192 -0
  119. package/.claude/hooks/pre-commit-mmos-guard.sh +99 -0
  120. package/.claude/hooks/pre-commit-version-check.sh +156 -0
  121. package/.claude/hooks/read-protection.py +151 -0
  122. package/.claude/hooks/slug-validation.py +176 -0
  123. package/.claude/hooks/sql-governance.py +182 -0
  124. package/.claude/hooks/write-path-validation.py +194 -0
  125. package/.claude/rules/agent-authority.md +105 -0
  126. package/.claude/rules/coderabbit-integration.md +93 -0
  127. package/.claude/rules/ids-principles.md +112 -0
  128. package/.claude/rules/story-lifecycle.md +139 -0
  129. package/.claude/rules/workflow-execution.md +150 -0
  130. package/LICENSE +48 -48
  131. package/README.md +30 -7
  132. package/bin/aios-minimal.js +0 -0
  133. package/bin/aios.js +15 -15
  134. package/package.json +2 -4
  135. package/packages/aios-install/bin/aios-install.js +0 -0
  136. package/packages/aios-install/bin/edmcp.js +0 -0
  137. package/packages/aios-pro-cli/bin/aios-pro.js +0 -0
  138. package/scripts/check-markdown-links.py +352 -352
  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;