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,643 +1,643 @@
1
- const fs = require('fs').promises;
2
- const path = require('path');
3
- const chalk = require('chalk');
4
- const inquirer = require('inquirer');
5
-
6
- /**
7
- * Approval workflow for AIOS-FULLSTACK framework
8
- * Manages approval process for high-impact modifications
9
- */
10
- class ApprovalWorkflow {
11
- constructor(options = {}) {
12
- this.rootPath = options.rootPath || process.cwd();
13
- this.approvalThresholds = {
14
- low: { auto_approve: true, requires_review: false },
15
- medium: { auto_approve: false, requires_review: true },
16
- high: { auto_approve: false, requires_review: true, requires_approval: true },
17
- critical: { auto_approve: false, requires_review: true, requires_approval: true, requires_multiple_approvers: true }
18
- };
19
- this.approvalHistory = [];
20
- this.pendingApprovals = new Map();
21
- this.approvalRules = new Map();
22
- this.initializeApprovalRules();
23
- }
24
-
25
- /**
26
- * Initialize default approval rules
27
- */
28
- initializeApprovalRules() {
29
- // Component type rules
30
- this.approvalRules.set('agent_modification', {
31
- risk_threshold: 'medium',
32
- required_approvers: 1,
33
- timeout_hours: 24,
34
- auto_approve_conditions: ['low_risk', 'has_tests', 'non_breaking']
35
- });
36
-
37
- this.approvalRules.set('workflow_modification', {
38
- risk_threshold: 'medium',
39
- required_approvers: 1,
40
- timeout_hours: 48,
41
- auto_approve_conditions: ['low_risk', 'has_tests']
42
- });
43
-
44
- this.approvalRules.set('core_util_modification', {
45
- risk_threshold: 'low',
46
- required_approvers: 2,
47
- timeout_hours: 72,
48
- auto_approve_conditions: ['minimal_risk', 'comprehensive_tests']
49
- });
50
-
51
- // Modification type rules
52
- this.approvalRules.set('component_removal', {
53
- risk_threshold: 'low',
54
- required_approvers: 2,
55
- timeout_hours: 168, // 1 week
56
- auto_approve_conditions: [] // Never auto-approve removals
57
- });
58
-
59
- this.approvalRules.set('breaking_change', {
60
- risk_threshold: 'low',
61
- required_approvers: 2,
62
- timeout_hours: 96,
63
- auto_approve_conditions: []
64
- });
65
- }
66
-
67
- /**
68
- * Process approval request for impact report
69
- */
70
- async processApprovalRequest(impactReport, options = {}) {
71
- const requestId = `approval-${Date.now()}`;
72
-
73
- try {
74
- console.log(chalk.blue(`🔍 Processing approval request for: ${impactReport.targetComponent.path}`));
75
-
76
- const _config = {
77
- skip_approval: options.skip_approval || false,
78
- auto_approve_low_risk: options.auto_approve_low_risk !== false,
79
- timeout_hours: options.timeout_hours || 24,
80
- required_approvers: options.required_approvers,
81
- ...options
82
- };
83
-
84
- // Determine approval requirements
85
- const approvalRequirements = await this.determineApprovalRequirements(impactReport, config);
86
-
87
- // Check if modification can be auto-approved
88
- const autoApprovalResult = await this.checkAutoApproval(impactReport, approvalRequirements);
89
-
90
- if (autoApprovalResult.can_auto_approve) {
91
- const approvalResult = await this.executeAutoApproval(impactReport, autoApprovalResult, requestId);
92
- return approvalResult;
93
- }
94
-
95
- // Manual approval required
96
- const approvalResult = await this.executeManualApproval(
97
- impactReport,
98
- approvalRequirements,
99
- config,
100
- requestId
101
- );
102
-
103
- return approvalResult;
104
-
105
- } catch (_error) {
106
- console.error(chalk.red(`Approval process failed: ${error.message}`));
107
- throw error;
108
- }
109
- }
110
-
111
- /**
112
- * Determine approval requirements based on impact analysis
113
- */
114
- async determineApprovalRequirements(impactReport, config) {
115
- const requirements = {
116
- approval_needed: false,
117
- risk_level: impactReport.riskAssessment.overallRisk,
118
- risk_score: impactReport.riskAssessment.riskScore,
119
- required_approvers: 1,
120
- timeout_hours: 24,
121
- review_criteria: [],
122
- blocking_issues: []
123
- };
124
-
125
- const riskLevel = impactReport.riskAssessment.overallRisk;
126
- const thresholds = this.approvalThresholds[riskLevel];
127
-
128
- // Basic approval requirements from risk level
129
- if (thresholds) {
130
- requirements.approval_needed = !thresholds.auto_approve;
131
- requirements.requires_review = thresholds.requires_review;
132
- requirements.requires_approval = thresholds.requires_approval;
133
- requirements.requires_multiple_approvers = thresholds.requires_multiple_approvers;
134
- }
135
-
136
- // Component-specific rules
137
- const componentRule = this.getComponentApprovalRule(impactReport.targetComponent);
138
- if (componentRule) {
139
- requirements.required_approvers = Math.max(requirements.required_approvers, componentRule.required_approvers);
140
- requirements.timeout_hours = Math.max(requirements.timeout_hours, componentRule.timeout_hours);
141
- }
142
-
143
- // Modification-specific rules
144
- const modificationRule = this.getModificationApprovalRule(impactReport.modificationType);
145
- if (modificationRule) {
146
- requirements.required_approvers = Math.max(requirements.required_approvers, modificationRule.required_approvers);
147
- requirements.timeout_hours = Math.max(requirements.timeout_hours, modificationRule.timeout_hours);
148
- }
149
-
150
- // Critical issues that block auto-approval
151
- if (impactReport.riskAssessment.criticalIssues.length > 0) {
152
- requirements.approval_needed = true;
153
- requirements.blocking_issues = impactReport.riskAssessment.criticalIssues.map(issue => issue.description);
154
- }
155
-
156
- // High-impact propagation
157
- if (impactReport.propagationAnalysis.criticalPaths?.length > 2) {
158
- requirements.approval_needed = true;
159
- requirements.review_criteria.push('Multiple critical propagation paths require review');
160
- }
161
-
162
- // Many affected components
163
- if (impactReport.summary.affectedComponents > 20) {
164
- requirements.approval_needed = true;
165
- requirements.review_criteria.push('Large number of affected components requires careful review');
166
- }
167
-
168
- // Breaking changes
169
- const hasBreakingChanges = impactReport.propagationAnalysis.directEffects?.some(
170
- effect => effect.changeType?.severity === 'breaking'
171
- ) || false;
172
-
173
- if (hasBreakingChanges) {
174
- requirements.approval_needed = true;
175
- requirements.required_approvers = Math.max(requirements.required_approvers, 2);
176
- requirements.review_criteria.push('Breaking changes require multiple approvers');
177
- }
178
-
179
- // Security-sensitive modifications
180
- if (impactReport.riskAssessment.riskDimensions?.security_risk?.score >= 6) {
181
- requirements.approval_needed = true;
182
- requirements.review_criteria.push('Security-sensitive modification requires approval');
183
- }
184
-
185
- return requirements;
186
- }
187
-
188
- /**
189
- * Check if modification can be auto-approved
190
- */
191
- async checkAutoApproval(impactReport, requirements) {
192
- const result = {
193
- can_auto_approve: false,
194
- reasons: [],
195
- conditions_met: [],
196
- conditions_failed: []
197
- };
198
-
199
- // Never auto-approve if manual approval is explicitly needed
200
- if (requirements.approval_needed) {
201
- result.reasons.push('Manual approval explicitly required due to risk level or critical issues');
202
- return result;
203
- }
204
-
205
- // Never auto-approve critical risk modifications
206
- if (impactReport.riskAssessment.overallRisk === 'critical') {
207
- result.reasons.push('Critical risk level requires manual approval');
208
- return result;
209
- }
210
-
211
- // Never auto-approve component removals
212
- if (impactReport.modificationType === 'remove') {
213
- result.reasons.push('Component removal always requires manual approval');
214
- return result;
215
- }
216
-
217
- // Check auto-approval conditions
218
- const autoApprovalConditions = await this.evaluateAutoApprovalConditions(impactReport);
219
-
220
- if (autoApprovalConditions.all_conditions_met) {
221
- result.can_auto_approve = true;
222
- result.conditions_met = autoApprovalConditions.met_conditions;
223
- result.reasons.push('All auto-approval conditions satisfied');
224
- } else {
225
- result.conditions_failed = autoApprovalConditions.failed_conditions;
226
- result.reasons.push('Auto-approval conditions not met');
227
- }
228
-
229
- return result;
230
- }
231
-
232
- /**
233
- * Evaluate auto-approval conditions
234
- */
235
- async evaluateAutoApprovalConditions(impactReport) {
236
- const conditions = {
237
- low_risk: impactReport.riskAssessment.overallRisk === 'low',
238
- minimal_impact: impactReport.summary.affectedComponents <= 5,
239
- no_critical_issues: impactReport.riskAssessment.criticalIssues.length === 0,
240
- no_breaking_changes: !this.hasBreakingChanges(impactReport),
241
- has_tests: await this.componentHasTests(impactReport.targetComponent),
242
- small_change: this.isSmallChange(impactReport),
243
- no_security_risk: impactReport.riskAssessment.riskDimensions?.security_risk?.score < 5
244
- };
245
-
246
- const metConditions = Object.entries(conditions)
247
- .filter(([condition, met]) => met)
248
- .map(([condition]) => condition);
249
-
250
- const failedConditions = Object.entries(conditions)
251
- .filter(([condition, met]) => !met)
252
- .map(([condition]) => condition);
253
-
254
- // Require at least 5 out of 7 conditions for auto-approval
255
- const requiredConditions = 5;
256
- const allConditionsMet = metConditions.length >= requiredConditions;
257
-
258
- return {
259
- all_conditions_met: allConditionsMet,
260
- met_conditions: metConditions,
261
- failed_conditions: failedConditions,
262
- condition_score: `${metConditions.length}/${Object.keys(conditions).length}`
263
- };
264
- }
265
-
266
- /**
267
- * Execute auto-approval
268
- */
269
- async executeAutoApproval(impactReport, autoApprovalResult, requestId) {
270
- const approval = {
271
- request_id: requestId,
272
- target_component: impactReport.targetComponent.path,
273
- modification_type: impactReport.modificationType,
274
- approval_status: 'auto_approved',
275
- approval_type: 'automatic',
276
- risk_level: impactReport.riskAssessment.overallRisk,
277
- auto_approval_reasons: autoApprovalResult.reasons,
278
- conditions_met: autoApprovalResult.conditions_met,
279
- approved_by: 'system_auto_approval',
280
- approved_at: new Date().toISOString(),
281
- valid_until: this.calculateExpirationTime(24), // Auto-approvals valid for 24 hours
282
- metadata: {
283
- impact_summary: impactReport.summary,
284
- approval_confidence: this.calculateApprovalConfidence(autoApprovalResult)
285
- }
286
- };
287
-
288
- // Log approval
289
- await this.logApproval(approval);
290
-
291
- console.log(chalk.green(`✅ Auto-approved: ${impactReport.targetComponent.path}`));
292
- console.log(chalk.gray(` Risk level: ${impactReport.riskAssessment.overallRisk}`));
293
- console.log(chalk.gray(` Conditions met: ${autoApprovalResult.conditions_met.length}`));
294
-
295
- return approval;
296
- }
297
-
298
- /**
299
- * Execute manual approval process
300
- */
301
- async executeManualApproval(impactReport, requirements, config, requestId) {
302
- console.log(chalk.yellow(`\n⚠️ MANUAL APPROVAL REQUIRED`));
303
- console.log(chalk.gray(`Component: ${impactReport.targetComponent.path}`));
304
- console.log(chalk.gray(`Risk Level: ${impactReport.riskAssessment.overallRisk.toUpperCase()}`));
305
- console.log(chalk.gray(`Affected Components: ${impactReport.summary.affectedComponents}`));
306
-
307
- if (requirements.blocking_issues.length > 0) {
308
- console.log(chalk.red(`\nBlocking Issues:`));
309
- requirements.blocking_issues.forEach((issue, index) => {
310
- console.log(chalk.red(` ${index + 1}. ${issue}`));
311
- });
312
- }
313
-
314
- if (requirements.review_criteria.length > 0) {
315
- console.log(chalk.yellow(`\nReview Criteria:`));
316
- requirements.review_criteria.forEach((criteria, index) => {
317
- console.log(chalk.yellow(` ${index + 1}. ${criteria}`));
318
- });
319
- }
320
-
321
- // Display key recommendations
322
- if (impactReport.riskAssessment.recommendations.length > 0) {
323
- console.log(chalk.blue(`\nKey Recommendations:`));
324
- impactReport.riskAssessment.recommendations.slice(0, 3).forEach((rec, index) => {
325
- console.log(chalk.blue(` ${index + 1}. ${rec.title}`));
326
- console.log(chalk.gray(` ${rec.description}`));
327
- });
328
- }
329
-
330
- // Approval prompt
331
- const approvalQuestions = await this.buildApprovalQuestions(impactReport, requirements);
332
- const approvalAnswers = await inquirer.prompt(approvalQuestions);
333
-
334
- const approval = {
335
- request_id: requestId,
336
- target_component: impactReport.targetComponent.path,
337
- modification_type: impactReport.modificationType,
338
- approval_status: approvalAnswers.approved ? 'approved' : 'rejected',
339
- approval_type: 'manual',
340
- risk_level: impactReport.riskAssessment.overallRisk,
341
- approved_by: approvalAnswers.approver_name || 'user',
342
- approved_at: new Date().toISOString(),
343
- approval_reason: approvalAnswers.approval_reason,
344
- conditions_acknowledged: approvalAnswers.conditions_acknowledged || false,
345
- valid_until: this.calculateExpirationTime(requirements.timeout_hours),
346
- requirements_met: requirements,
347
- metadata: {
348
- impact_summary: impactReport.summary,
349
- approval_answers: approvalAnswers
350
- }
351
- };
352
-
353
- // Add approval conditions if approved
354
- if (approvalAnswers.approved) {
355
- approval.approval_conditions = approvalAnswers.approval_conditions;
356
- approval.monitoring_required = approvalAnswers.monitoring_required || false;
357
- approval.rollback_plan_required = approvalAnswers.rollback_plan || false;
358
- } else {
359
- approval.rejection_reason = approvalAnswers.rejection_reason;
360
- approval.recommended_actions = approvalAnswers.recommended_actions;
361
- }
362
-
363
- // Log approval decision
364
- await this.logApproval(approval);
365
-
366
- if (approvalAnswers.approved) {
367
- console.log(chalk.green(`\n✅ Manual approval granted`));
368
- console.log(chalk.gray(` Approved by: ${approval.approved_by}`));
369
- console.log(chalk.gray(` Valid until: ${new Date(approval.valid_until).toLocaleString()}`));
370
-
371
- if (approval.approval_conditions) {
372
- console.log(chalk.blue(` Conditions: ${approval.approval_conditions}`));
373
- }
374
- } else {
375
- console.log(chalk.red(`\n❌ Approval rejected`));
376
- console.log(chalk.gray(` Reason: ${approval.rejection_reason}`));
377
- }
378
-
379
- return approval;
380
- }
381
-
382
- /**
383
- * Build approval question flow
384
- */
385
- async buildApprovalQuestions(impactReport, requirements) {
386
- const questions = [];
387
-
388
- // Main approval question
389
- questions.push({
390
- type: 'confirm',
391
- name: 'approved',
392
- message: `Approve ${impactReport.modificationType} of ${impactReport.targetComponent.path}?`,
393
- default: false
394
- });
395
-
396
- // Conditional questions based on approval
397
- questions.push({
398
- type: 'input',
399
- name: 'approver_name',
400
- message: 'Enter your name/identifier:',
401
- when: (answers) => answers.approved,
402
- validate: (input) => input.length > 0 || 'Name is required'
403
- });
404
-
405
- questions.push({
406
- type: 'input',
407
- name: 'approval_reason',
408
- message: 'Reason for approval:',
409
- when: (answers) => answers.approved,
410
- default: 'Impact analysis reviewed and acceptable'
411
- });
412
-
413
- // High-risk additional questions
414
- if (requirements.risk_level === 'high' || requirements.risk_level === 'critical') {
415
- questions.push({
416
- type: 'confirm',
417
- name: 'conditions_acknowledged',
418
- message: 'Do you acknowledge all risk factors and recommendations?',
419
- when: (answers) => answers.approved,
420
- default: false
421
- });
422
-
423
- questions.push({
424
- type: 'input',
425
- name: 'approval_conditions',
426
- message: 'Enter any approval conditions or requirements:',
427
- when: (answers) => answers.approved && answers.conditions_acknowledged,
428
- default: 'Standard monitoring and rollback procedures apply'
429
- });
430
-
431
- questions.push({
432
- type: 'confirm',
433
- name: 'monitoring_required',
434
- message: 'Require enhanced monitoring after deployment?',
435
- when: (answers) => answers.approved,
436
- default: true
437
- });
438
-
439
- questions.push({
440
- type: 'confirm',
441
- name: 'rollback_plan',
442
- message: 'Require documented rollback plan?',
443
- when: (answers) => answers.approved,
444
- default: true
445
- });
446
- }
447
-
448
- // Rejection questions
449
- questions.push({
450
- type: 'input',
451
- name: 'rejection_reason',
452
- message: 'Reason for rejection:',
453
- when: (answers) => !answers.approved,
454
- validate: (input) => input.length > 0 || 'Rejection reason is required'
455
- });
456
-
457
- questions.push({
458
- type: 'input',
459
- name: 'recommended_actions',
460
- message: 'Recommended actions before resubmission:',
461
- when: (answers) => !answers.approved,
462
- default: 'Address critical issues and reduce risk factors'
463
- });
464
-
465
- return questions;
466
- }
467
-
468
- /**
469
- * Log approval decision for audit trail
470
- */
471
- async logApproval(approval) {
472
- // Add to approval history
473
- this.approvalHistory.push({
474
- request_id: approval.request_id,
475
- component: approval.target_component,
476
- status: approval.approval_status,
477
- risk_level: approval.risk_level,
478
- approved_by: approval.approved_by,
479
- timestamp: approval.approved_at
480
- });
481
-
482
- // Write to audit log file
483
- try {
484
- const logDir = path.join(this.rootPath, '.aios', 'audit');
485
- await fs.mkdir(logDir, { recursive: true });
486
-
487
- const logFile = path.join(logDir, 'approval_log.jsonl');
488
- const logEntry = JSON.stringify(approval) + '\n';
489
-
490
- await fs.appendFile(logFile, logEntry);
491
-
492
- console.log(chalk.gray(` Approval logged to audit trail`));
493
-
494
- } catch (_error) {
495
- console.warn(chalk.yellow(`Failed to write approval log: ${error.message}`));
496
- }
497
- }
498
-
499
- // Helper methods
500
-
501
- getComponentApprovalRule(component) {
502
- if (component.type === 'agent') {
503
- return this.approvalRules.get('agent_modification');
504
- } else if (component.type === 'workflow') {
505
- return this.approvalRules.get('workflow_modification');
506
- } else if (component.type === 'util' && component.path.includes('core')) {
507
- return this.approvalRules.get('core_util_modification');
508
- }
509
- return null;
510
- }
511
-
512
- getModificationApprovalRule(modificationType) {
513
- if (modificationType === 'remove') {
514
- return this.approvalRules.get('component_removal');
515
- }
516
- return null;
517
- }
518
-
519
- hasBreakingChanges(impactReport) {
520
- return impactReport.propagationAnalysis.directEffects?.some(
521
- effect => effect.changeType?.severity === 'breaking'
522
- ) || false;
523
- }
524
-
525
- async componentHasTests(component) {
526
- const testPaths = [
527
- path.join(this.rootPath, 'tests', 'unit', component.type, `${component.name}.test.js`),
528
- path.join(this.rootPath, 'tests', 'integration', component.type, `${component.name}.integration.test.js`),
529
- path.join(this.rootPath, 'test', `${component.name}.test.js`)
530
- ];
531
-
532
- for (const testPath of testPaths) {
533
- try {
534
- await fs.access(testPath);
535
- return true;
536
- } catch (_error) {
537
- // File doesn't exist, continue
538
- }
539
- }
540
-
541
- return false;
542
- }
543
-
544
- isSmallChange(impactReport) {
545
- return impactReport.summary.affectedComponents <= 3 &&
546
- impactReport.summary.propagationDepth <= 2;
547
- }
548
-
549
- calculateExpirationTime(hours) {
550
- const now = new Date();
551
- now.setHours(now.getHours() + hours);
552
- return now.toISOString();
553
- }
554
-
555
- calculateApprovalConfidence(autoApprovalResult) {
556
- const conditionsMet = autoApprovalResult.conditions_met.length;
557
- const totalConditions = 7; // Based on evaluateAutoApprovalConditions
558
- return Math.round((conditionsMet / totalConditions) * 100);
559
- }
560
-
561
- /**
562
- * Check if approval is still valid
563
- */
564
- isApprovalValid(approval) {
565
- const now = new Date();
566
- const validUntil = new Date(approval.valid_until);
567
- return now < validUntil;
568
- }
569
-
570
- /**
571
- * Get approval history
572
- */
573
- getApprovalHistory(options = {}) {
574
- const history = {
575
- total_approvals: this.approvalHistory.length,
576
- approval_stats: this.calculateApprovalStats(),
577
- recent_approvals: this.approvalHistory.slice(-10)
578
- };
579
-
580
- if (options.component) {
581
- history.component_approvals = this.approvalHistory.filter(
582
- approval => approval.component === options.component
583
- );
584
- }
585
-
586
- if (options.risk_level) {
587
- history.risk_level_approvals = this.approvalHistory.filter(
588
- approval => approval.risk_level === options.risk_level
589
- );
590
- }
591
-
592
- return history;
593
- }
594
-
595
- calculateApprovalStats() {
596
- const stats = {
597
- approved: 0,
598
- rejected: 0,
599
- auto_approved: 0,
600
- by_risk_level: { low: 0, medium: 0, high: 0, critical: 0 }
601
- };
602
-
603
- this.approvalHistory.forEach(approval => {
604
- if (approval.status === 'approved') stats.approved++;
605
- else if (approval.status === 'rejected') stats.rejected++;
606
- else if (approval.status === 'auto_approved') stats.auto_approved++;
607
-
608
- stats.by_risk_level[approval.risk_level]++;
609
- });
610
-
611
- return stats;
612
- }
613
-
614
- /**
615
- * Get pending approvals
616
- */
617
- getPendingApprovals() {
618
- return Array.from(this.pendingApprovals.values());
619
- }
620
-
621
- /**
622
- * Clear expired approvals
623
- */
624
- clearExpiredApprovals() {
625
- const now = new Date();
626
- let clearedCount = 0;
627
-
628
- for (const [requestId, approval] of this.pendingApprovals) {
629
- if (new Date(approval.valid_until) < now) {
630
- this.pendingApprovals.delete(requestId);
631
- clearedCount++;
632
- }
633
- }
634
-
635
- if (clearedCount > 0) {
636
- console.log(chalk.gray(`Cleared ${clearedCount} expired approvals`));
637
- }
638
-
639
- return clearedCount;
640
- }
641
- }
642
-
1
+ const fs = require('fs').promises;
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const inquirer = require('inquirer');
5
+
6
+ /**
7
+ * Approval workflow for AIOS-FULLSTACK framework
8
+ * Manages approval process for high-impact modifications
9
+ */
10
+ class ApprovalWorkflow {
11
+ constructor(options = {}) {
12
+ this.rootPath = options.rootPath || process.cwd();
13
+ this.approvalThresholds = {
14
+ low: { auto_approve: true, requires_review: false },
15
+ medium: { auto_approve: false, requires_review: true },
16
+ high: { auto_approve: false, requires_review: true, requires_approval: true },
17
+ critical: { auto_approve: false, requires_review: true, requires_approval: true, requires_multiple_approvers: true }
18
+ };
19
+ this.approvalHistory = [];
20
+ this.pendingApprovals = new Map();
21
+ this.approvalRules = new Map();
22
+ this.initializeApprovalRules();
23
+ }
24
+
25
+ /**
26
+ * Initialize default approval rules
27
+ */
28
+ initializeApprovalRules() {
29
+ // Component type rules
30
+ this.approvalRules.set('agent_modification', {
31
+ risk_threshold: 'medium',
32
+ required_approvers: 1,
33
+ timeout_hours: 24,
34
+ auto_approve_conditions: ['low_risk', 'has_tests', 'non_breaking']
35
+ });
36
+
37
+ this.approvalRules.set('workflow_modification', {
38
+ risk_threshold: 'medium',
39
+ required_approvers: 1,
40
+ timeout_hours: 48,
41
+ auto_approve_conditions: ['low_risk', 'has_tests']
42
+ });
43
+
44
+ this.approvalRules.set('core_util_modification', {
45
+ risk_threshold: 'low',
46
+ required_approvers: 2,
47
+ timeout_hours: 72,
48
+ auto_approve_conditions: ['minimal_risk', 'comprehensive_tests']
49
+ });
50
+
51
+ // Modification type rules
52
+ this.approvalRules.set('component_removal', {
53
+ risk_threshold: 'low',
54
+ required_approvers: 2,
55
+ timeout_hours: 168, // 1 week
56
+ auto_approve_conditions: [] // Never auto-approve removals
57
+ });
58
+
59
+ this.approvalRules.set('breaking_change', {
60
+ risk_threshold: 'low',
61
+ required_approvers: 2,
62
+ timeout_hours: 96,
63
+ auto_approve_conditions: []
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Process approval request for impact report
69
+ */
70
+ async processApprovalRequest(impactReport, options = {}) {
71
+ const requestId = `approval-${Date.now()}`;
72
+
73
+ try {
74
+ console.log(chalk.blue(`🔍 Processing approval request for: ${impactReport.targetComponent.path}`));
75
+
76
+ const _config = {
77
+ skip_approval: options.skip_approval || false,
78
+ auto_approve_low_risk: options.auto_approve_low_risk !== false,
79
+ timeout_hours: options.timeout_hours || 24,
80
+ required_approvers: options.required_approvers,
81
+ ...options
82
+ };
83
+
84
+ // Determine approval requirements
85
+ const approvalRequirements = await this.determineApprovalRequirements(impactReport, config);
86
+
87
+ // Check if modification can be auto-approved
88
+ const autoApprovalResult = await this.checkAutoApproval(impactReport, approvalRequirements);
89
+
90
+ if (autoApprovalResult.can_auto_approve) {
91
+ const approvalResult = await this.executeAutoApproval(impactReport, autoApprovalResult, requestId);
92
+ return approvalResult;
93
+ }
94
+
95
+ // Manual approval required
96
+ const approvalResult = await this.executeManualApproval(
97
+ impactReport,
98
+ approvalRequirements,
99
+ config,
100
+ requestId
101
+ );
102
+
103
+ return approvalResult;
104
+
105
+ } catch (_error) {
106
+ console.error(chalk.red(`Approval process failed: ${error.message}`));
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Determine approval requirements based on impact analysis
113
+ */
114
+ async determineApprovalRequirements(impactReport, config) {
115
+ const requirements = {
116
+ approval_needed: false,
117
+ risk_level: impactReport.riskAssessment.overallRisk,
118
+ risk_score: impactReport.riskAssessment.riskScore,
119
+ required_approvers: 1,
120
+ timeout_hours: 24,
121
+ review_criteria: [],
122
+ blocking_issues: []
123
+ };
124
+
125
+ const riskLevel = impactReport.riskAssessment.overallRisk;
126
+ const thresholds = this.approvalThresholds[riskLevel];
127
+
128
+ // Basic approval requirements from risk level
129
+ if (thresholds) {
130
+ requirements.approval_needed = !thresholds.auto_approve;
131
+ requirements.requires_review = thresholds.requires_review;
132
+ requirements.requires_approval = thresholds.requires_approval;
133
+ requirements.requires_multiple_approvers = thresholds.requires_multiple_approvers;
134
+ }
135
+
136
+ // Component-specific rules
137
+ const componentRule = this.getComponentApprovalRule(impactReport.targetComponent);
138
+ if (componentRule) {
139
+ requirements.required_approvers = Math.max(requirements.required_approvers, componentRule.required_approvers);
140
+ requirements.timeout_hours = Math.max(requirements.timeout_hours, componentRule.timeout_hours);
141
+ }
142
+
143
+ // Modification-specific rules
144
+ const modificationRule = this.getModificationApprovalRule(impactReport.modificationType);
145
+ if (modificationRule) {
146
+ requirements.required_approvers = Math.max(requirements.required_approvers, modificationRule.required_approvers);
147
+ requirements.timeout_hours = Math.max(requirements.timeout_hours, modificationRule.timeout_hours);
148
+ }
149
+
150
+ // Critical issues that block auto-approval
151
+ if (impactReport.riskAssessment.criticalIssues.length > 0) {
152
+ requirements.approval_needed = true;
153
+ requirements.blocking_issues = impactReport.riskAssessment.criticalIssues.map(issue => issue.description);
154
+ }
155
+
156
+ // High-impact propagation
157
+ if (impactReport.propagationAnalysis.criticalPaths?.length > 2) {
158
+ requirements.approval_needed = true;
159
+ requirements.review_criteria.push('Multiple critical propagation paths require review');
160
+ }
161
+
162
+ // Many affected components
163
+ if (impactReport.summary.affectedComponents > 20) {
164
+ requirements.approval_needed = true;
165
+ requirements.review_criteria.push('Large number of affected components requires careful review');
166
+ }
167
+
168
+ // Breaking changes
169
+ const hasBreakingChanges = impactReport.propagationAnalysis.directEffects?.some(
170
+ effect => effect.changeType?.severity === 'breaking'
171
+ ) || false;
172
+
173
+ if (hasBreakingChanges) {
174
+ requirements.approval_needed = true;
175
+ requirements.required_approvers = Math.max(requirements.required_approvers, 2);
176
+ requirements.review_criteria.push('Breaking changes require multiple approvers');
177
+ }
178
+
179
+ // Security-sensitive modifications
180
+ if (impactReport.riskAssessment.riskDimensions?.security_risk?.score >= 6) {
181
+ requirements.approval_needed = true;
182
+ requirements.review_criteria.push('Security-sensitive modification requires approval');
183
+ }
184
+
185
+ return requirements;
186
+ }
187
+
188
+ /**
189
+ * Check if modification can be auto-approved
190
+ */
191
+ async checkAutoApproval(impactReport, requirements) {
192
+ const result = {
193
+ can_auto_approve: false,
194
+ reasons: [],
195
+ conditions_met: [],
196
+ conditions_failed: []
197
+ };
198
+
199
+ // Never auto-approve if manual approval is explicitly needed
200
+ if (requirements.approval_needed) {
201
+ result.reasons.push('Manual approval explicitly required due to risk level or critical issues');
202
+ return result;
203
+ }
204
+
205
+ // Never auto-approve critical risk modifications
206
+ if (impactReport.riskAssessment.overallRisk === 'critical') {
207
+ result.reasons.push('Critical risk level requires manual approval');
208
+ return result;
209
+ }
210
+
211
+ // Never auto-approve component removals
212
+ if (impactReport.modificationType === 'remove') {
213
+ result.reasons.push('Component removal always requires manual approval');
214
+ return result;
215
+ }
216
+
217
+ // Check auto-approval conditions
218
+ const autoApprovalConditions = await this.evaluateAutoApprovalConditions(impactReport);
219
+
220
+ if (autoApprovalConditions.all_conditions_met) {
221
+ result.can_auto_approve = true;
222
+ result.conditions_met = autoApprovalConditions.met_conditions;
223
+ result.reasons.push('All auto-approval conditions satisfied');
224
+ } else {
225
+ result.conditions_failed = autoApprovalConditions.failed_conditions;
226
+ result.reasons.push('Auto-approval conditions not met');
227
+ }
228
+
229
+ return result;
230
+ }
231
+
232
+ /**
233
+ * Evaluate auto-approval conditions
234
+ */
235
+ async evaluateAutoApprovalConditions(impactReport) {
236
+ const conditions = {
237
+ low_risk: impactReport.riskAssessment.overallRisk === 'low',
238
+ minimal_impact: impactReport.summary.affectedComponents <= 5,
239
+ no_critical_issues: impactReport.riskAssessment.criticalIssues.length === 0,
240
+ no_breaking_changes: !this.hasBreakingChanges(impactReport),
241
+ has_tests: await this.componentHasTests(impactReport.targetComponent),
242
+ small_change: this.isSmallChange(impactReport),
243
+ no_security_risk: impactReport.riskAssessment.riskDimensions?.security_risk?.score < 5
244
+ };
245
+
246
+ const metConditions = Object.entries(conditions)
247
+ .filter(([condition, met]) => met)
248
+ .map(([condition]) => condition);
249
+
250
+ const failedConditions = Object.entries(conditions)
251
+ .filter(([condition, met]) => !met)
252
+ .map(([condition]) => condition);
253
+
254
+ // Require at least 5 out of 7 conditions for auto-approval
255
+ const requiredConditions = 5;
256
+ const allConditionsMet = metConditions.length >= requiredConditions;
257
+
258
+ return {
259
+ all_conditions_met: allConditionsMet,
260
+ met_conditions: metConditions,
261
+ failed_conditions: failedConditions,
262
+ condition_score: `${metConditions.length}/${Object.keys(conditions).length}`
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Execute auto-approval
268
+ */
269
+ async executeAutoApproval(impactReport, autoApprovalResult, requestId) {
270
+ const approval = {
271
+ request_id: requestId,
272
+ target_component: impactReport.targetComponent.path,
273
+ modification_type: impactReport.modificationType,
274
+ approval_status: 'auto_approved',
275
+ approval_type: 'automatic',
276
+ risk_level: impactReport.riskAssessment.overallRisk,
277
+ auto_approval_reasons: autoApprovalResult.reasons,
278
+ conditions_met: autoApprovalResult.conditions_met,
279
+ approved_by: 'system_auto_approval',
280
+ approved_at: new Date().toISOString(),
281
+ valid_until: this.calculateExpirationTime(24), // Auto-approvals valid for 24 hours
282
+ metadata: {
283
+ impact_summary: impactReport.summary,
284
+ approval_confidence: this.calculateApprovalConfidence(autoApprovalResult)
285
+ }
286
+ };
287
+
288
+ // Log approval
289
+ await this.logApproval(approval);
290
+
291
+ console.log(chalk.green(`✅ Auto-approved: ${impactReport.targetComponent.path}`));
292
+ console.log(chalk.gray(` Risk level: ${impactReport.riskAssessment.overallRisk}`));
293
+ console.log(chalk.gray(` Conditions met: ${autoApprovalResult.conditions_met.length}`));
294
+
295
+ return approval;
296
+ }
297
+
298
+ /**
299
+ * Execute manual approval process
300
+ */
301
+ async executeManualApproval(impactReport, requirements, config, requestId) {
302
+ console.log(chalk.yellow(`\n⚠️ MANUAL APPROVAL REQUIRED`));
303
+ console.log(chalk.gray(`Component: ${impactReport.targetComponent.path}`));
304
+ console.log(chalk.gray(`Risk Level: ${impactReport.riskAssessment.overallRisk.toUpperCase()}`));
305
+ console.log(chalk.gray(`Affected Components: ${impactReport.summary.affectedComponents}`));
306
+
307
+ if (requirements.blocking_issues.length > 0) {
308
+ console.log(chalk.red(`\nBlocking Issues:`));
309
+ requirements.blocking_issues.forEach((issue, index) => {
310
+ console.log(chalk.red(` ${index + 1}. ${issue}`));
311
+ });
312
+ }
313
+
314
+ if (requirements.review_criteria.length > 0) {
315
+ console.log(chalk.yellow(`\nReview Criteria:`));
316
+ requirements.review_criteria.forEach((criteria, index) => {
317
+ console.log(chalk.yellow(` ${index + 1}. ${criteria}`));
318
+ });
319
+ }
320
+
321
+ // Display key recommendations
322
+ if (impactReport.riskAssessment.recommendations.length > 0) {
323
+ console.log(chalk.blue(`\nKey Recommendations:`));
324
+ impactReport.riskAssessment.recommendations.slice(0, 3).forEach((rec, index) => {
325
+ console.log(chalk.blue(` ${index + 1}. ${rec.title}`));
326
+ console.log(chalk.gray(` ${rec.description}`));
327
+ });
328
+ }
329
+
330
+ // Approval prompt
331
+ const approvalQuestions = await this.buildApprovalQuestions(impactReport, requirements);
332
+ const approvalAnswers = await inquirer.prompt(approvalQuestions);
333
+
334
+ const approval = {
335
+ request_id: requestId,
336
+ target_component: impactReport.targetComponent.path,
337
+ modification_type: impactReport.modificationType,
338
+ approval_status: approvalAnswers.approved ? 'approved' : 'rejected',
339
+ approval_type: 'manual',
340
+ risk_level: impactReport.riskAssessment.overallRisk,
341
+ approved_by: approvalAnswers.approver_name || 'user',
342
+ approved_at: new Date().toISOString(),
343
+ approval_reason: approvalAnswers.approval_reason,
344
+ conditions_acknowledged: approvalAnswers.conditions_acknowledged || false,
345
+ valid_until: this.calculateExpirationTime(requirements.timeout_hours),
346
+ requirements_met: requirements,
347
+ metadata: {
348
+ impact_summary: impactReport.summary,
349
+ approval_answers: approvalAnswers
350
+ }
351
+ };
352
+
353
+ // Add approval conditions if approved
354
+ if (approvalAnswers.approved) {
355
+ approval.approval_conditions = approvalAnswers.approval_conditions;
356
+ approval.monitoring_required = approvalAnswers.monitoring_required || false;
357
+ approval.rollback_plan_required = approvalAnswers.rollback_plan || false;
358
+ } else {
359
+ approval.rejection_reason = approvalAnswers.rejection_reason;
360
+ approval.recommended_actions = approvalAnswers.recommended_actions;
361
+ }
362
+
363
+ // Log approval decision
364
+ await this.logApproval(approval);
365
+
366
+ if (approvalAnswers.approved) {
367
+ console.log(chalk.green(`\n✅ Manual approval granted`));
368
+ console.log(chalk.gray(` Approved by: ${approval.approved_by}`));
369
+ console.log(chalk.gray(` Valid until: ${new Date(approval.valid_until).toLocaleString()}`));
370
+
371
+ if (approval.approval_conditions) {
372
+ console.log(chalk.blue(` Conditions: ${approval.approval_conditions}`));
373
+ }
374
+ } else {
375
+ console.log(chalk.red(`\n❌ Approval rejected`));
376
+ console.log(chalk.gray(` Reason: ${approval.rejection_reason}`));
377
+ }
378
+
379
+ return approval;
380
+ }
381
+
382
+ /**
383
+ * Build approval question flow
384
+ */
385
+ async buildApprovalQuestions(impactReport, requirements) {
386
+ const questions = [];
387
+
388
+ // Main approval question
389
+ questions.push({
390
+ type: 'confirm',
391
+ name: 'approved',
392
+ message: `Approve ${impactReport.modificationType} of ${impactReport.targetComponent.path}?`,
393
+ default: false
394
+ });
395
+
396
+ // Conditional questions based on approval
397
+ questions.push({
398
+ type: 'input',
399
+ name: 'approver_name',
400
+ message: 'Enter your name/identifier:',
401
+ when: (answers) => answers.approved,
402
+ validate: (input) => input.length > 0 || 'Name is required'
403
+ });
404
+
405
+ questions.push({
406
+ type: 'input',
407
+ name: 'approval_reason',
408
+ message: 'Reason for approval:',
409
+ when: (answers) => answers.approved,
410
+ default: 'Impact analysis reviewed and acceptable'
411
+ });
412
+
413
+ // High-risk additional questions
414
+ if (requirements.risk_level === 'high' || requirements.risk_level === 'critical') {
415
+ questions.push({
416
+ type: 'confirm',
417
+ name: 'conditions_acknowledged',
418
+ message: 'Do you acknowledge all risk factors and recommendations?',
419
+ when: (answers) => answers.approved,
420
+ default: false
421
+ });
422
+
423
+ questions.push({
424
+ type: 'input',
425
+ name: 'approval_conditions',
426
+ message: 'Enter any approval conditions or requirements:',
427
+ when: (answers) => answers.approved && answers.conditions_acknowledged,
428
+ default: 'Standard monitoring and rollback procedures apply'
429
+ });
430
+
431
+ questions.push({
432
+ type: 'confirm',
433
+ name: 'monitoring_required',
434
+ message: 'Require enhanced monitoring after deployment?',
435
+ when: (answers) => answers.approved,
436
+ default: true
437
+ });
438
+
439
+ questions.push({
440
+ type: 'confirm',
441
+ name: 'rollback_plan',
442
+ message: 'Require documented rollback plan?',
443
+ when: (answers) => answers.approved,
444
+ default: true
445
+ });
446
+ }
447
+
448
+ // Rejection questions
449
+ questions.push({
450
+ type: 'input',
451
+ name: 'rejection_reason',
452
+ message: 'Reason for rejection:',
453
+ when: (answers) => !answers.approved,
454
+ validate: (input) => input.length > 0 || 'Rejection reason is required'
455
+ });
456
+
457
+ questions.push({
458
+ type: 'input',
459
+ name: 'recommended_actions',
460
+ message: 'Recommended actions before resubmission:',
461
+ when: (answers) => !answers.approved,
462
+ default: 'Address critical issues and reduce risk factors'
463
+ });
464
+
465
+ return questions;
466
+ }
467
+
468
+ /**
469
+ * Log approval decision for audit trail
470
+ */
471
+ async logApproval(approval) {
472
+ // Add to approval history
473
+ this.approvalHistory.push({
474
+ request_id: approval.request_id,
475
+ component: approval.target_component,
476
+ status: approval.approval_status,
477
+ risk_level: approval.risk_level,
478
+ approved_by: approval.approved_by,
479
+ timestamp: approval.approved_at
480
+ });
481
+
482
+ // Write to audit log file
483
+ try {
484
+ const logDir = path.join(this.rootPath, '.aios', 'audit');
485
+ await fs.mkdir(logDir, { recursive: true });
486
+
487
+ const logFile = path.join(logDir, 'approval_log.jsonl');
488
+ const logEntry = JSON.stringify(approval) + '\n';
489
+
490
+ await fs.appendFile(logFile, logEntry);
491
+
492
+ console.log(chalk.gray(` Approval logged to audit trail`));
493
+
494
+ } catch (_error) {
495
+ console.warn(chalk.yellow(`Failed to write approval log: ${error.message}`));
496
+ }
497
+ }
498
+
499
+ // Helper methods
500
+
501
+ getComponentApprovalRule(component) {
502
+ if (component.type === 'agent') {
503
+ return this.approvalRules.get('agent_modification');
504
+ } else if (component.type === 'workflow') {
505
+ return this.approvalRules.get('workflow_modification');
506
+ } else if (component.type === 'util' && component.path.includes('core')) {
507
+ return this.approvalRules.get('core_util_modification');
508
+ }
509
+ return null;
510
+ }
511
+
512
+ getModificationApprovalRule(modificationType) {
513
+ if (modificationType === 'remove') {
514
+ return this.approvalRules.get('component_removal');
515
+ }
516
+ return null;
517
+ }
518
+
519
+ hasBreakingChanges(impactReport) {
520
+ return impactReport.propagationAnalysis.directEffects?.some(
521
+ effect => effect.changeType?.severity === 'breaking'
522
+ ) || false;
523
+ }
524
+
525
+ async componentHasTests(component) {
526
+ const testPaths = [
527
+ path.join(this.rootPath, 'tests', 'unit', component.type, `${component.name}.test.js`),
528
+ path.join(this.rootPath, 'tests', 'integration', component.type, `${component.name}.integration.test.js`),
529
+ path.join(this.rootPath, 'test', `${component.name}.test.js`)
530
+ ];
531
+
532
+ for (const testPath of testPaths) {
533
+ try {
534
+ await fs.access(testPath);
535
+ return true;
536
+ } catch (_error) {
537
+ // File doesn't exist, continue
538
+ }
539
+ }
540
+
541
+ return false;
542
+ }
543
+
544
+ isSmallChange(impactReport) {
545
+ return impactReport.summary.affectedComponents <= 3 &&
546
+ impactReport.summary.propagationDepth <= 2;
547
+ }
548
+
549
+ calculateExpirationTime(hours) {
550
+ const now = new Date();
551
+ now.setHours(now.getHours() + hours);
552
+ return now.toISOString();
553
+ }
554
+
555
+ calculateApprovalConfidence(autoApprovalResult) {
556
+ const conditionsMet = autoApprovalResult.conditions_met.length;
557
+ const totalConditions = 7; // Based on evaluateAutoApprovalConditions
558
+ return Math.round((conditionsMet / totalConditions) * 100);
559
+ }
560
+
561
+ /**
562
+ * Check if approval is still valid
563
+ */
564
+ isApprovalValid(approval) {
565
+ const now = new Date();
566
+ const validUntil = new Date(approval.valid_until);
567
+ return now < validUntil;
568
+ }
569
+
570
+ /**
571
+ * Get approval history
572
+ */
573
+ getApprovalHistory(options = {}) {
574
+ const history = {
575
+ total_approvals: this.approvalHistory.length,
576
+ approval_stats: this.calculateApprovalStats(),
577
+ recent_approvals: this.approvalHistory.slice(-10)
578
+ };
579
+
580
+ if (options.component) {
581
+ history.component_approvals = this.approvalHistory.filter(
582
+ approval => approval.component === options.component
583
+ );
584
+ }
585
+
586
+ if (options.risk_level) {
587
+ history.risk_level_approvals = this.approvalHistory.filter(
588
+ approval => approval.risk_level === options.risk_level
589
+ );
590
+ }
591
+
592
+ return history;
593
+ }
594
+
595
+ calculateApprovalStats() {
596
+ const stats = {
597
+ approved: 0,
598
+ rejected: 0,
599
+ auto_approved: 0,
600
+ by_risk_level: { low: 0, medium: 0, high: 0, critical: 0 }
601
+ };
602
+
603
+ this.approvalHistory.forEach(approval => {
604
+ if (approval.status === 'approved') stats.approved++;
605
+ else if (approval.status === 'rejected') stats.rejected++;
606
+ else if (approval.status === 'auto_approved') stats.auto_approved++;
607
+
608
+ stats.by_risk_level[approval.risk_level]++;
609
+ });
610
+
611
+ return stats;
612
+ }
613
+
614
+ /**
615
+ * Get pending approvals
616
+ */
617
+ getPendingApprovals() {
618
+ return Array.from(this.pendingApprovals.values());
619
+ }
620
+
621
+ /**
622
+ * Clear expired approvals
623
+ */
624
+ clearExpiredApprovals() {
625
+ const now = new Date();
626
+ let clearedCount = 0;
627
+
628
+ for (const [requestId, approval] of this.pendingApprovals) {
629
+ if (new Date(approval.valid_until) < now) {
630
+ this.pendingApprovals.delete(requestId);
631
+ clearedCount++;
632
+ }
633
+ }
634
+
635
+ if (clearedCount > 0) {
636
+ console.log(chalk.gray(`Cleared ${clearedCount} expired approvals`));
637
+ }
638
+
639
+ return clearedCount;
640
+ }
641
+ }
642
+
643
643
  module.exports = ApprovalWorkflow;