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,531 +1,531 @@
1
- /**
2
- * Rollback Handler for AIOS-FULLSTACK
3
- * Handles undo operations for component transactions
4
- * @module rollback-handler
5
- */
6
-
7
- const TransactionManager = require('./transaction-manager');
8
- const chalk = require('chalk');
9
- const inquirer = require('inquirer');
10
- const path = require('path');
11
- const fs = require('fs').promises;
12
- const ModificationValidator = require('./modification-validator');
13
-
14
- class RollbackHandler {
15
- constructor(options = {}) {
16
- this.rootPath = options.rootPath || process.cwd();
17
- this.transactionManager = new TransactionManager({ rootPath: this.rootPath });
18
- this.modificationValidator = new ModificationValidator();
19
- this.backupPath = path.join(this.rootPath, 'aios-core', '.backups');
20
- }
21
-
22
- /**
23
- * Execute undo-last command
24
- * @param {Object} options - Rollback options
25
- * @returns {Promise<Object>} Rollback result
26
- */
27
- async undoLast(options = {}) {
28
- try {
29
- let transaction;
30
-
31
- if (options.transactionId) {
32
- // Load specific transaction
33
- transaction = await this.transactionManager.loadTransaction(options.transactionId);
34
- if (!transaction) {
35
- throw new Error(`Transaction not found: ${options.transactionId}`);
36
- }
37
- } else {
38
- // Get last transaction
39
- transaction = await this.transactionManager.getLastTransaction();
40
- if (!transaction) {
41
- console.log(chalk.yellow('No transactions found to rollback'));
42
- return { success: false, error: 'No transactions found' };
43
- }
44
- }
45
-
46
- // Display transaction details
47
- console.log(chalk.blue('\n📋 Transaction Details:'));
48
- console.log(chalk.gray(`ID: ${transaction.id}`));
49
- console.log(chalk.gray(`Type: ${transaction.type}`));
50
- console.log(chalk.gray(`Date: ${new Date(transaction.startTime).toLocaleString()}`));
51
- console.log(chalk.gray(`Status: ${transaction.status}`));
52
- console.log(chalk.gray(`Operations: ${transaction.operations.length}`));
53
-
54
- // Show operations
55
- console.log(chalk.blue('\n📝 Operations to rollback:'));
56
- for (const op of transaction.operations) {
57
- const icon = this.getOperationIcon(op.type);
58
- console.log(chalk.gray(` ${icon} ${op.type}: ${path.basename(op.path)}`));
59
- }
60
-
61
- // Confirm rollback
62
- if (!options.force) {
63
- const { confirm } = await inquirer.prompt([{
64
- type: 'confirm',
65
- name: 'confirm',
66
- message: 'Do you want to rollback this transaction?',
67
- default: true
68
- }]);
69
-
70
- if (!confirm) {
71
- console.log(chalk.yellow('Rollback cancelled'));
72
- return { success: false, error: 'User cancelled' };
73
- }
74
- }
75
-
76
- // Execute rollback
77
- console.log(chalk.blue('\n⚙️ Executing rollback...'));
78
-
79
- const rollbackResult = await this.transactionManager.rollbackTransaction(
80
- transaction.id,
81
- {
82
- continueOnError: options.continueOnError !== false
83
- }
84
- );
85
-
86
- // Display results
87
- this.displayRollbackResults(rollbackResult);
88
-
89
- return {
90
- success: rollbackResult.failed.length === 0,
91
- result: rollbackResult
92
- };
93
-
94
- } catch (error) {
95
- console.error(chalk.red(`\n❌ Rollback failed: ${error.message}`));
96
- return {
97
- success: false,
98
- error: error.message
99
- };
100
- }
101
- }
102
-
103
- /**
104
- * List recent transactions
105
- * @param {number} limit - Number of transactions to show
106
- * @returns {Promise<void>}
107
- */
108
- async listTransactions(limit = 10) {
109
- try {
110
- const transactions = await this.transactionManager.listTransactions(limit);
111
-
112
- if (transactions.length === 0) {
113
- console.log(chalk.yellow('No transactions found'));
114
- return;
115
- }
116
-
117
- console.log(chalk.blue('\n📋 Recent Transactions:'));
118
- console.log(chalk.gray('─'.repeat(80)));
119
-
120
- for (const txn of transactions) {
121
- const date = new Date(txn.startTime).toLocaleString();
122
- const duration = txn.endTime
123
- ? `${new Date(txn.endTime) - new Date(txn.startTime)}ms`
124
- : 'active';
125
-
126
- console.log(chalk.white(`\nID: ${txn.id}`));
127
- console.log(chalk.gray(`Type: ${txn.type}`));
128
- console.log(chalk.gray(`Description: ${txn.description}`));
129
- console.log(chalk.gray(`User: ${txn.user}`));
130
- console.log(chalk.gray(`Date: ${date}`));
131
- console.log(chalk.gray(`Status: ${this.getStatusColor(txn.status)}`));
132
- console.log(chalk.gray(`Operations: ${txn.operations}`));
133
- console.log(chalk.gray(`Duration: ${duration}`));
134
- console.log(chalk.gray('─'.repeat(80)));
135
- }
136
-
137
- } catch (error) {
138
- console.error(chalk.red(`Failed to list transactions: ${error.message}`));
139
- }
140
- }
141
-
142
- /**
143
- * Execute selective rollback
144
- * @param {string} transactionId - Transaction ID
145
- * @param {Array<string>} operationIds - Specific operations to rollback
146
- * @returns {Promise<Object>} Rollback result
147
- */
148
- async selectiveRollback(transactionId, operationIds) {
149
- try {
150
- const transaction = await this.transactionManager.loadTransaction(transactionId);
151
- if (!transaction) {
152
- throw new Error(`Transaction not found: ${transactionId}`);
153
- }
154
-
155
- // Filter operations
156
- const selectedOps = transaction.operations.filter(op =>
157
- operationIds.includes(op.id)
158
- );
159
-
160
- if (selectedOps.length === 0) {
161
- throw new Error('No matching operations found');
162
- }
163
-
164
- console.log(chalk.blue(`\n📝 Selective rollback: ${selectedOps.length} operations`));
165
-
166
- // Create a new transaction for selective rollback
167
- const rollbackTxnId = await this.transactionManager.beginTransaction({
168
- type: 'selective_rollback',
169
- description: `Selective rollback of ${transactionId}`,
170
- metadata: {
171
- originalTransaction: transactionId,
172
- selectedOperations: operationIds
173
- }
174
- });
175
-
176
- const results = {
177
- successful: [],
178
- failed: [],
179
- warnings: []
180
- };
181
-
182
- // Rollback selected operations
183
- for (const op of selectedOps) {
184
- try {
185
- await this.transactionManager.rollbackOperation(op, results);
186
- } catch (error) {
187
- results.failed.push({
188
- operation: op.id,
189
- error: error.message
190
- });
191
- }
192
- }
193
-
194
- // Commit rollback transaction
195
- await this.transactionManager.commitTransaction(rollbackTxnId);
196
-
197
- this.displayRollbackResults(results);
198
-
199
- return {
200
- success: results.failed.length === 0,
201
- result: results
202
- };
203
-
204
- } catch (error) {
205
- console.error(chalk.red(`Selective rollback failed: ${error.message}`));
206
- return {
207
- success: false,
208
- error: error.message
209
- };
210
- }
211
- }
212
-
213
- /**
214
- * Clean up old transactions
215
- * @returns {Promise<number>} Number cleaned
216
- */
217
- async cleanup() {
218
- try {
219
- console.log(chalk.blue('🧹 Cleaning up old transactions...'));
220
- const cleaned = await this.transactionManager.cleanupOldTransactions();
221
- console.log(chalk.green(`✅ Cleaned up ${cleaned} old transactions`));
222
- return cleaned;
223
- } catch (error) {
224
- console.error(chalk.red(`Cleanup failed: ${error.message}`));
225
- return 0;
226
- }
227
- }
228
-
229
- /**
230
- * Create backup before modification
231
- * @param {string} componentType - Type of component
232
- * @param {string} componentName - Name of component
233
- * @param {string} content - Content to backup
234
- * @returns {Promise<string>} Backup file path
235
- */
236
- async createBackup(componentType, componentName, content) {
237
- try {
238
- // Ensure backup directory exists
239
- await fs.mkdir(this.backupPath, { recursive: true });
240
-
241
- // Create subdirectory for component type
242
- const typeBackupPath = path.join(this.backupPath, componentType);
243
- await fs.mkdir(typeBackupPath, { recursive: true });
244
-
245
- // Generate backup filename with timestamp
246
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
247
- const backupFilename = `${componentName}.${timestamp}.backup`;
248
- const backupFilePath = path.join(typeBackupPath, backupFilename);
249
-
250
- // Write backup
251
- await fs.writeFile(backupFilePath, content, 'utf8');
252
-
253
- // Record backup in transaction
254
- if (this.currentTransactionId) {
255
- await this.transactionManager.recordOperation({
256
- type: 'backup_created',
257
- path: backupFilePath,
258
- componentType,
259
- componentName,
260
- timestamp
261
- });
262
- }
263
-
264
- return backupFilePath;
265
- } catch (error) {
266
- throw new Error(`Failed to create backup: ${error.message}`);
267
- }
268
- }
269
-
270
- /**
271
- * Restore from backup
272
- * @param {string} backupPath - Path to backup file
273
- * @param {string} targetPath - Path to restore to
274
- * @returns {Promise<boolean>} Success status
275
- */
276
- async restoreFromBackup(backupPath, targetPath) {
277
- try {
278
- // Verify backup exists
279
- await fs.access(backupPath);
280
-
281
- // Read backup content
282
- const content = await fs.readFile(backupPath, 'utf8');
283
-
284
- // Restore to target
285
- await fs.writeFile(targetPath, content, 'utf8');
286
-
287
- console.log(chalk.green(`✅ Restored from backup: ${path.basename(backupPath)}`));
288
- return true;
289
- } catch (error) {
290
- console.error(chalk.red(`Failed to restore from backup: ${error.message}`));
291
- return false;
292
- }
293
- }
294
-
295
- /**
296
- * Validate modification before applying
297
- * @param {string} componentType - Type of component
298
- * @param {string} originalContent - Original content
299
- * @param {string} modifiedContent - Modified content
300
- * @returns {Promise<Object>} Validation result
301
- */
302
- async validateModification(componentType, originalContent, modifiedContent) {
303
- const validation = await this.modificationValidator.validateModification(
304
- componentType,
305
- originalContent,
306
- modifiedContent
307
- );
308
-
309
- if (!validation.valid) {
310
- console.log(chalk.red('\n❌ Modification validation failed:'));
311
- validation.errors.forEach(error => {
312
- console.log(chalk.red(` • ${error}`));
313
- });
314
- }
315
-
316
- if (validation.warnings.length > 0) {
317
- console.log(chalk.yellow('\n⚠️ Warnings:'));
318
- validation.warnings.forEach(warning => {
319
- console.log(chalk.yellow(` • ${warning}`));
320
- });
321
- }
322
-
323
- if (validation.breakingChanges.length > 0) {
324
- console.log(chalk.red('\n🚨 Breaking Changes Detected:'));
325
- validation.breakingChanges.forEach(change => {
326
- console.log(chalk.red(` • ${change.type}: ${change.impact}`));
327
- if (change.items) {
328
- console.log(chalk.red(` Items: ${change.items.join(', ')}`));
329
- }
330
- });
331
- }
332
-
333
- return validation;
334
- }
335
-
336
- /**
337
- * Rollback modification
338
- * @param {Object} modificationData - Modification details
339
- * @returns {Promise<Object>} Rollback result
340
- */
341
- async rollbackModification(modificationData) {
342
- const { componentType, componentName, backupPath, targetPath } = modificationData;
343
-
344
- try {
345
- console.log(chalk.blue(`\n⏪ Rolling back ${componentType}: ${componentName}`));
346
-
347
- // Restore from backup
348
- const restored = await this.restoreFromBackup(backupPath, targetPath);
349
-
350
- if (restored) {
351
- // Record rollback
352
- await this.transactionManager.recordOperation({
353
- type: 'modification_rollback',
354
- componentType,
355
- componentName,
356
- backupPath,
357
- targetPath,
358
- timestamp: new Date().toISOString()
359
- });
360
-
361
- return {
362
- success: true,
363
- message: `Successfully rolled back ${componentType}: ${componentName}`
364
- };
365
- } else {
366
- throw new Error('Restoration failed');
367
- }
368
- } catch (error) {
369
- return {
370
- success: false,
371
- error: error.message
372
- };
373
- }
374
- }
375
-
376
- /**
377
- * List available backups for a component
378
- * @param {string} componentType - Type of component
379
- * @param {string} componentName - Name of component
380
- * @returns {Promise<Array>} List of backups
381
- */
382
- async listBackups(componentType, componentName) {
383
- try {
384
- const typeBackupPath = path.join(this.backupPath, componentType);
385
- const files = await fs.readdir(typeBackupPath);
386
-
387
- const backups = files
388
- .filter(file => file.startsWith(`${componentName}.`) && file.endsWith('.backup'))
389
- .map(file => {
390
- const match = file.match(/\.(\d{4}-\d{2}-\d{2}T[\d-]+Z)\.backup$/);
391
- const timestamp = match ? match[1].replace(/-/g, ':') : 'unknown';
392
-
393
- return {
394
- filename: file,
395
- path: path.join(typeBackupPath, file),
396
- timestamp: new Date(timestamp),
397
- componentName,
398
- componentType
399
- };
400
- })
401
- .sort((a, b) => b.timestamp - a.timestamp);
402
-
403
- return backups;
404
- } catch (error) {
405
- if (error.code === 'ENOENT') {
406
- return [];
407
- }
408
- throw error;
409
- }
410
- }
411
-
412
- /**
413
- * Clean up old backups
414
- * @param {number} daysToKeep - Number of days to keep backups
415
- * @returns {Promise<number>} Number of backups deleted
416
- */
417
- async cleanupBackups(daysToKeep = 30) {
418
- try {
419
- const cutoffDate = new Date();
420
- cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
421
-
422
- let deletedCount = 0;
423
-
424
- // Iterate through component types
425
- const componentTypes = await fs.readdir(this.backupPath);
426
-
427
- for (const componentType of componentTypes) {
428
- const typePath = path.join(this.backupPath, componentType);
429
- const stat = await fs.stat(typePath);
430
-
431
- if (!stat.isDirectory()) continue;
432
-
433
- const files = await fs.readdir(typePath);
434
-
435
- for (const file of files) {
436
- if (!file.endsWith('.backup')) continue;
437
-
438
- const filePath = path.join(typePath, file);
439
- const fileStat = await fs.stat(filePath);
440
-
441
- if (fileStat.mtime < cutoffDate) {
442
- await fs.unlink(filePath);
443
- deletedCount++;
444
- }
445
- }
446
- }
447
-
448
- return deletedCount;
449
- } catch (error) {
450
- console.error(chalk.red(`Backup cleanup failed: ${error.message}`));
451
- return 0;
452
- }
453
- }
454
-
455
- /**
456
- * Get operation icon
457
- * @private
458
- */
459
- getOperationIcon(type) {
460
- const icons = {
461
- create: '➕',
462
- update: '✏️',
463
- delete: '🗑️',
464
- manifest_update: '📋',
465
- metadata_update: '📊',
466
- component_created: '📦',
467
- backup_created: '💾',
468
- modification_rollback: '⏪'
469
- };
470
-
471
- return icons[type] || '•';
472
- }
473
-
474
- /**
475
- * Get status color
476
- * @private
477
- */
478
- getStatusColor(status) {
479
- switch (status) {
480
- case 'active':
481
- return chalk.yellow(status);
482
- case 'committed':
483
- return chalk.green(status);
484
- case 'rolled_back':
485
- return chalk.blue(status);
486
- case 'failed':
487
- return chalk.red(status);
488
- default:
489
- return status;
490
- }
491
- }
492
-
493
- /**
494
- * Display rollback results
495
- * @private
496
- */
497
- displayRollbackResults(results) {
498
- console.log(chalk.blue('\n📊 Rollback Results:'));
499
-
500
- if (results.successful.length > 0) {
501
- console.log(chalk.green(`\n✅ Successful (${results.successful.length}):`));
502
- results.successful.forEach(item => {
503
- console.log(chalk.green(` ✓ ${item.action}: ${path.basename(item.path)}`));
504
- });
505
- }
506
-
507
- if (results.warnings.length > 0) {
508
- console.log(chalk.yellow(`\n⚠️ Warnings (${results.warnings.length}):`));
509
- results.warnings.forEach(item => {
510
- console.log(chalk.yellow(` ⚠ ${item.warning}: ${path.basename(item.path || 'N/A')}`));
511
- });
512
- }
513
-
514
- if (results.failed.length > 0) {
515
- console.log(chalk.red(`\n❌ Failed (${results.failed.length}):`));
516
- results.failed.forEach(item => {
517
- console.log(chalk.red(` ✗ ${item.operation}: ${item.error}`));
518
- });
519
- }
520
-
521
- // Summary
522
- const total = results.successful.length + results.failed.length;
523
- const successRate = total > 0 ? (results.successful.length / total * 100).toFixed(0) : 0;
524
-
525
- console.log(chalk.blue('\n📈 Summary:'));
526
- console.log(chalk.gray(` Total operations: ${total}`));
527
- console.log(chalk.gray(` Success rate: ${successRate}%`));
528
- }
529
- }
530
-
1
+ /**
2
+ * Rollback Handler for AIOS-FULLSTACK
3
+ * Handles undo operations for component transactions
4
+ * @module rollback-handler
5
+ */
6
+
7
+ const TransactionManager = require('./transaction-manager');
8
+ const chalk = require('chalk');
9
+ const inquirer = require('inquirer');
10
+ const path = require('path');
11
+ const fs = require('fs').promises;
12
+ const ModificationValidator = require('./modification-validator');
13
+
14
+ class RollbackHandler {
15
+ constructor(options = {}) {
16
+ this.rootPath = options.rootPath || process.cwd();
17
+ this.transactionManager = new TransactionManager({ rootPath: this.rootPath });
18
+ this.modificationValidator = new ModificationValidator();
19
+ this.backupPath = path.join(this.rootPath, 'aios-core', '.backups');
20
+ }
21
+
22
+ /**
23
+ * Execute undo-last command
24
+ * @param {Object} options - Rollback options
25
+ * @returns {Promise<Object>} Rollback result
26
+ */
27
+ async undoLast(options = {}) {
28
+ try {
29
+ let transaction;
30
+
31
+ if (options.transactionId) {
32
+ // Load specific transaction
33
+ transaction = await this.transactionManager.loadTransaction(options.transactionId);
34
+ if (!transaction) {
35
+ throw new Error(`Transaction not found: ${options.transactionId}`);
36
+ }
37
+ } else {
38
+ // Get last transaction
39
+ transaction = await this.transactionManager.getLastTransaction();
40
+ if (!transaction) {
41
+ console.log(chalk.yellow('No transactions found to rollback'));
42
+ return { success: false, error: 'No transactions found' };
43
+ }
44
+ }
45
+
46
+ // Display transaction details
47
+ console.log(chalk.blue('\n📋 Transaction Details:'));
48
+ console.log(chalk.gray(`ID: ${transaction.id}`));
49
+ console.log(chalk.gray(`Type: ${transaction.type}`));
50
+ console.log(chalk.gray(`Date: ${new Date(transaction.startTime).toLocaleString()}`));
51
+ console.log(chalk.gray(`Status: ${transaction.status}`));
52
+ console.log(chalk.gray(`Operations: ${transaction.operations.length}`));
53
+
54
+ // Show operations
55
+ console.log(chalk.blue('\n📝 Operations to rollback:'));
56
+ for (const op of transaction.operations) {
57
+ const icon = this.getOperationIcon(op.type);
58
+ console.log(chalk.gray(` ${icon} ${op.type}: ${path.basename(op.path)}`));
59
+ }
60
+
61
+ // Confirm rollback
62
+ if (!options.force) {
63
+ const { confirm } = await inquirer.prompt([{
64
+ type: 'confirm',
65
+ name: 'confirm',
66
+ message: 'Do you want to rollback this transaction?',
67
+ default: true
68
+ }]);
69
+
70
+ if (!confirm) {
71
+ console.log(chalk.yellow('Rollback cancelled'));
72
+ return { success: false, error: 'User cancelled' };
73
+ }
74
+ }
75
+
76
+ // Execute rollback
77
+ console.log(chalk.blue('\n⚙️ Executing rollback...'));
78
+
79
+ const rollbackResult = await this.transactionManager.rollbackTransaction(
80
+ transaction.id,
81
+ {
82
+ continueOnError: options.continueOnError !== false
83
+ }
84
+ );
85
+
86
+ // Display results
87
+ this.displayRollbackResults(rollbackResult);
88
+
89
+ return {
90
+ success: rollbackResult.failed.length === 0,
91
+ result: rollbackResult
92
+ };
93
+
94
+ } catch (error) {
95
+ console.error(chalk.red(`\n❌ Rollback failed: ${error.message}`));
96
+ return {
97
+ success: false,
98
+ error: error.message
99
+ };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * List recent transactions
105
+ * @param {number} limit - Number of transactions to show
106
+ * @returns {Promise<void>}
107
+ */
108
+ async listTransactions(limit = 10) {
109
+ try {
110
+ const transactions = await this.transactionManager.listTransactions(limit);
111
+
112
+ if (transactions.length === 0) {
113
+ console.log(chalk.yellow('No transactions found'));
114
+ return;
115
+ }
116
+
117
+ console.log(chalk.blue('\n📋 Recent Transactions:'));
118
+ console.log(chalk.gray('─'.repeat(80)));
119
+
120
+ for (const txn of transactions) {
121
+ const date = new Date(txn.startTime).toLocaleString();
122
+ const duration = txn.endTime
123
+ ? `${new Date(txn.endTime) - new Date(txn.startTime)}ms`
124
+ : 'active';
125
+
126
+ console.log(chalk.white(`\nID: ${txn.id}`));
127
+ console.log(chalk.gray(`Type: ${txn.type}`));
128
+ console.log(chalk.gray(`Description: ${txn.description}`));
129
+ console.log(chalk.gray(`User: ${txn.user}`));
130
+ console.log(chalk.gray(`Date: ${date}`));
131
+ console.log(chalk.gray(`Status: ${this.getStatusColor(txn.status)}`));
132
+ console.log(chalk.gray(`Operations: ${txn.operations}`));
133
+ console.log(chalk.gray(`Duration: ${duration}`));
134
+ console.log(chalk.gray('─'.repeat(80)));
135
+ }
136
+
137
+ } catch (error) {
138
+ console.error(chalk.red(`Failed to list transactions: ${error.message}`));
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Execute selective rollback
144
+ * @param {string} transactionId - Transaction ID
145
+ * @param {Array<string>} operationIds - Specific operations to rollback
146
+ * @returns {Promise<Object>} Rollback result
147
+ */
148
+ async selectiveRollback(transactionId, operationIds) {
149
+ try {
150
+ const transaction = await this.transactionManager.loadTransaction(transactionId);
151
+ if (!transaction) {
152
+ throw new Error(`Transaction not found: ${transactionId}`);
153
+ }
154
+
155
+ // Filter operations
156
+ const selectedOps = transaction.operations.filter(op =>
157
+ operationIds.includes(op.id)
158
+ );
159
+
160
+ if (selectedOps.length === 0) {
161
+ throw new Error('No matching operations found');
162
+ }
163
+
164
+ console.log(chalk.blue(`\n📝 Selective rollback: ${selectedOps.length} operations`));
165
+
166
+ // Create a new transaction for selective rollback
167
+ const rollbackTxnId = await this.transactionManager.beginTransaction({
168
+ type: 'selective_rollback',
169
+ description: `Selective rollback of ${transactionId}`,
170
+ metadata: {
171
+ originalTransaction: transactionId,
172
+ selectedOperations: operationIds
173
+ }
174
+ });
175
+
176
+ const results = {
177
+ successful: [],
178
+ failed: [],
179
+ warnings: []
180
+ };
181
+
182
+ // Rollback selected operations
183
+ for (const op of selectedOps) {
184
+ try {
185
+ await this.transactionManager.rollbackOperation(op, results);
186
+ } catch (error) {
187
+ results.failed.push({
188
+ operation: op.id,
189
+ error: error.message
190
+ });
191
+ }
192
+ }
193
+
194
+ // Commit rollback transaction
195
+ await this.transactionManager.commitTransaction(rollbackTxnId);
196
+
197
+ this.displayRollbackResults(results);
198
+
199
+ return {
200
+ success: results.failed.length === 0,
201
+ result: results
202
+ };
203
+
204
+ } catch (error) {
205
+ console.error(chalk.red(`Selective rollback failed: ${error.message}`));
206
+ return {
207
+ success: false,
208
+ error: error.message
209
+ };
210
+ }
211
+ }
212
+
213
+ /**
214
+ * Clean up old transactions
215
+ * @returns {Promise<number>} Number cleaned
216
+ */
217
+ async cleanup() {
218
+ try {
219
+ console.log(chalk.blue('🧹 Cleaning up old transactions...'));
220
+ const cleaned = await this.transactionManager.cleanupOldTransactions();
221
+ console.log(chalk.green(`✅ Cleaned up ${cleaned} old transactions`));
222
+ return cleaned;
223
+ } catch (error) {
224
+ console.error(chalk.red(`Cleanup failed: ${error.message}`));
225
+ return 0;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Create backup before modification
231
+ * @param {string} componentType - Type of component
232
+ * @param {string} componentName - Name of component
233
+ * @param {string} content - Content to backup
234
+ * @returns {Promise<string>} Backup file path
235
+ */
236
+ async createBackup(componentType, componentName, content) {
237
+ try {
238
+ // Ensure backup directory exists
239
+ await fs.mkdir(this.backupPath, { recursive: true });
240
+
241
+ // Create subdirectory for component type
242
+ const typeBackupPath = path.join(this.backupPath, componentType);
243
+ await fs.mkdir(typeBackupPath, { recursive: true });
244
+
245
+ // Generate backup filename with timestamp
246
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
247
+ const backupFilename = `${componentName}.${timestamp}.backup`;
248
+ const backupFilePath = path.join(typeBackupPath, backupFilename);
249
+
250
+ // Write backup
251
+ await fs.writeFile(backupFilePath, content, 'utf8');
252
+
253
+ // Record backup in transaction
254
+ if (this.currentTransactionId) {
255
+ await this.transactionManager.recordOperation({
256
+ type: 'backup_created',
257
+ path: backupFilePath,
258
+ componentType,
259
+ componentName,
260
+ timestamp
261
+ });
262
+ }
263
+
264
+ return backupFilePath;
265
+ } catch (error) {
266
+ throw new Error(`Failed to create backup: ${error.message}`);
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Restore from backup
272
+ * @param {string} backupPath - Path to backup file
273
+ * @param {string} targetPath - Path to restore to
274
+ * @returns {Promise<boolean>} Success status
275
+ */
276
+ async restoreFromBackup(backupPath, targetPath) {
277
+ try {
278
+ // Verify backup exists
279
+ await fs.access(backupPath);
280
+
281
+ // Read backup content
282
+ const content = await fs.readFile(backupPath, 'utf8');
283
+
284
+ // Restore to target
285
+ await fs.writeFile(targetPath, content, 'utf8');
286
+
287
+ console.log(chalk.green(`✅ Restored from backup: ${path.basename(backupPath)}`));
288
+ return true;
289
+ } catch (error) {
290
+ console.error(chalk.red(`Failed to restore from backup: ${error.message}`));
291
+ return false;
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Validate modification before applying
297
+ * @param {string} componentType - Type of component
298
+ * @param {string} originalContent - Original content
299
+ * @param {string} modifiedContent - Modified content
300
+ * @returns {Promise<Object>} Validation result
301
+ */
302
+ async validateModification(componentType, originalContent, modifiedContent) {
303
+ const validation = await this.modificationValidator.validateModification(
304
+ componentType,
305
+ originalContent,
306
+ modifiedContent
307
+ );
308
+
309
+ if (!validation.valid) {
310
+ console.log(chalk.red('\n❌ Modification validation failed:'));
311
+ validation.errors.forEach(error => {
312
+ console.log(chalk.red(` • ${error}`));
313
+ });
314
+ }
315
+
316
+ if (validation.warnings.length > 0) {
317
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
318
+ validation.warnings.forEach(warning => {
319
+ console.log(chalk.yellow(` • ${warning}`));
320
+ });
321
+ }
322
+
323
+ if (validation.breakingChanges.length > 0) {
324
+ console.log(chalk.red('\n🚨 Breaking Changes Detected:'));
325
+ validation.breakingChanges.forEach(change => {
326
+ console.log(chalk.red(` • ${change.type}: ${change.impact}`));
327
+ if (change.items) {
328
+ console.log(chalk.red(` Items: ${change.items.join(', ')}`));
329
+ }
330
+ });
331
+ }
332
+
333
+ return validation;
334
+ }
335
+
336
+ /**
337
+ * Rollback modification
338
+ * @param {Object} modificationData - Modification details
339
+ * @returns {Promise<Object>} Rollback result
340
+ */
341
+ async rollbackModification(modificationData) {
342
+ const { componentType, componentName, backupPath, targetPath } = modificationData;
343
+
344
+ try {
345
+ console.log(chalk.blue(`\n⏪ Rolling back ${componentType}: ${componentName}`));
346
+
347
+ // Restore from backup
348
+ const restored = await this.restoreFromBackup(backupPath, targetPath);
349
+
350
+ if (restored) {
351
+ // Record rollback
352
+ await this.transactionManager.recordOperation({
353
+ type: 'modification_rollback',
354
+ componentType,
355
+ componentName,
356
+ backupPath,
357
+ targetPath,
358
+ timestamp: new Date().toISOString()
359
+ });
360
+
361
+ return {
362
+ success: true,
363
+ message: `Successfully rolled back ${componentType}: ${componentName}`
364
+ };
365
+ } else {
366
+ throw new Error('Restoration failed');
367
+ }
368
+ } catch (error) {
369
+ return {
370
+ success: false,
371
+ error: error.message
372
+ };
373
+ }
374
+ }
375
+
376
+ /**
377
+ * List available backups for a component
378
+ * @param {string} componentType - Type of component
379
+ * @param {string} componentName - Name of component
380
+ * @returns {Promise<Array>} List of backups
381
+ */
382
+ async listBackups(componentType, componentName) {
383
+ try {
384
+ const typeBackupPath = path.join(this.backupPath, componentType);
385
+ const files = await fs.readdir(typeBackupPath);
386
+
387
+ const backups = files
388
+ .filter(file => file.startsWith(`${componentName}.`) && file.endsWith('.backup'))
389
+ .map(file => {
390
+ const match = file.match(/\.(\d{4}-\d{2}-\d{2}T[\d-]+Z)\.backup$/);
391
+ const timestamp = match ? match[1].replace(/-/g, ':') : 'unknown';
392
+
393
+ return {
394
+ filename: file,
395
+ path: path.join(typeBackupPath, file),
396
+ timestamp: new Date(timestamp),
397
+ componentName,
398
+ componentType
399
+ };
400
+ })
401
+ .sort((a, b) => b.timestamp - a.timestamp);
402
+
403
+ return backups;
404
+ } catch (error) {
405
+ if (error.code === 'ENOENT') {
406
+ return [];
407
+ }
408
+ throw error;
409
+ }
410
+ }
411
+
412
+ /**
413
+ * Clean up old backups
414
+ * @param {number} daysToKeep - Number of days to keep backups
415
+ * @returns {Promise<number>} Number of backups deleted
416
+ */
417
+ async cleanupBackups(daysToKeep = 30) {
418
+ try {
419
+ const cutoffDate = new Date();
420
+ cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
421
+
422
+ let deletedCount = 0;
423
+
424
+ // Iterate through component types
425
+ const componentTypes = await fs.readdir(this.backupPath);
426
+
427
+ for (const componentType of componentTypes) {
428
+ const typePath = path.join(this.backupPath, componentType);
429
+ const stat = await fs.stat(typePath);
430
+
431
+ if (!stat.isDirectory()) continue;
432
+
433
+ const files = await fs.readdir(typePath);
434
+
435
+ for (const file of files) {
436
+ if (!file.endsWith('.backup')) continue;
437
+
438
+ const filePath = path.join(typePath, file);
439
+ const fileStat = await fs.stat(filePath);
440
+
441
+ if (fileStat.mtime < cutoffDate) {
442
+ await fs.unlink(filePath);
443
+ deletedCount++;
444
+ }
445
+ }
446
+ }
447
+
448
+ return deletedCount;
449
+ } catch (error) {
450
+ console.error(chalk.red(`Backup cleanup failed: ${error.message}`));
451
+ return 0;
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Get operation icon
457
+ * @private
458
+ */
459
+ getOperationIcon(type) {
460
+ const icons = {
461
+ create: '➕',
462
+ update: '✏️',
463
+ delete: '🗑️',
464
+ manifest_update: '📋',
465
+ metadata_update: '📊',
466
+ component_created: '📦',
467
+ backup_created: '💾',
468
+ modification_rollback: '⏪'
469
+ };
470
+
471
+ return icons[type] || '•';
472
+ }
473
+
474
+ /**
475
+ * Get status color
476
+ * @private
477
+ */
478
+ getStatusColor(status) {
479
+ switch (status) {
480
+ case 'active':
481
+ return chalk.yellow(status);
482
+ case 'committed':
483
+ return chalk.green(status);
484
+ case 'rolled_back':
485
+ return chalk.blue(status);
486
+ case 'failed':
487
+ return chalk.red(status);
488
+ default:
489
+ return status;
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Display rollback results
495
+ * @private
496
+ */
497
+ displayRollbackResults(results) {
498
+ console.log(chalk.blue('\n📊 Rollback Results:'));
499
+
500
+ if (results.successful.length > 0) {
501
+ console.log(chalk.green(`\n✅ Successful (${results.successful.length}):`));
502
+ results.successful.forEach(item => {
503
+ console.log(chalk.green(` ✓ ${item.action}: ${path.basename(item.path)}`));
504
+ });
505
+ }
506
+
507
+ if (results.warnings.length > 0) {
508
+ console.log(chalk.yellow(`\n⚠️ Warnings (${results.warnings.length}):`));
509
+ results.warnings.forEach(item => {
510
+ console.log(chalk.yellow(` ⚠ ${item.warning}: ${path.basename(item.path || 'N/A')}`));
511
+ });
512
+ }
513
+
514
+ if (results.failed.length > 0) {
515
+ console.log(chalk.red(`\n❌ Failed (${results.failed.length}):`));
516
+ results.failed.forEach(item => {
517
+ console.log(chalk.red(` ✗ ${item.operation}: ${item.error}`));
518
+ });
519
+ }
520
+
521
+ // Summary
522
+ const total = results.successful.length + results.failed.length;
523
+ const successRate = total > 0 ? (results.successful.length / total * 100).toFixed(0) : 0;
524
+
525
+ console.log(chalk.blue('\n📈 Summary:'));
526
+ console.log(chalk.gray(` Total operations: ${total}`));
527
+ console.log(chalk.gray(` Success rate: ${successRate}%`));
528
+ }
529
+ }
530
+
531
531
  module.exports = RollbackHandler;