aios-core 4.2.13 → 4.2.14

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 (97) hide show
  1. package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
  2. package/.aios-core/core/registry/registry-schema.json +166 -166
  3. package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
  4. package/.aios-core/data/entity-registry.yaml +27 -0
  5. package/.aios-core/development/scripts/approval-workflow.js +642 -642
  6. package/.aios-core/development/scripts/backup-manager.js +606 -606
  7. package/.aios-core/development/scripts/branch-manager.js +389 -389
  8. package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
  9. package/.aios-core/development/scripts/commit-message-generator.js +849 -849
  10. package/.aios-core/development/scripts/conflict-resolver.js +674 -674
  11. package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
  12. package/.aios-core/development/scripts/diff-generator.js +351 -351
  13. package/.aios-core/development/scripts/elicitation-engine.js +384 -384
  14. package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
  15. package/.aios-core/development/scripts/git-wrapper.js +461 -461
  16. package/.aios-core/development/scripts/manifest-preview.js +244 -244
  17. package/.aios-core/development/scripts/metrics-tracker.js +775 -775
  18. package/.aios-core/development/scripts/modification-validator.js +554 -554
  19. package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
  20. package/.aios-core/development/scripts/performance-analyzer.js +757 -757
  21. package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
  22. package/.aios-core/development/scripts/rollback-handler.js +530 -530
  23. package/.aios-core/development/scripts/security-checker.js +358 -358
  24. package/.aios-core/development/scripts/template-engine.js +239 -239
  25. package/.aios-core/development/scripts/template-validator.js +278 -278
  26. package/.aios-core/development/scripts/test-generator.js +843 -843
  27. package/.aios-core/development/scripts/transaction-manager.js +589 -589
  28. package/.aios-core/development/scripts/usage-tracker.js +673 -673
  29. package/.aios-core/development/scripts/validate-filenames.js +226 -226
  30. package/.aios-core/development/scripts/version-tracker.js +526 -526
  31. package/.aios-core/development/scripts/yaml-validator.js +396 -396
  32. package/.aios-core/development/tasks/build-autonomous.md +10 -4
  33. package/.aios-core/development/tasks/create-service.md +23 -0
  34. package/.aios-core/development/tasks/dev-develop-story.md +12 -6
  35. package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
  36. package/.aios-core/development/tasks/publish-npm.md +3 -3
  37. package/.aios-core/hooks/unified/README.md +1 -1
  38. package/.aios-core/install-manifest.yaml +65 -61
  39. package/.aios-core/manifests/schema/manifest-schema.json +190 -190
  40. package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
  41. package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
  42. package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
  43. package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
  44. package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
  45. package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
  46. package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
  47. package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
  48. package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
  49. package/.aios-core/product/templates/eslintrc-security.json +32 -32
  50. package/.aios-core/product/templates/github-actions-cd.yml +212 -212
  51. package/.aios-core/product/templates/github-actions-ci.yml +172 -172
  52. package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
  53. package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
  54. package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
  55. package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
  56. package/README.en.md +747 -0
  57. package/README.md +4 -2
  58. package/bin/aios.js +7 -4
  59. package/package.json +1 -1
  60. package/packages/aios-pro-cli/src/recover.js +1 -1
  61. package/packages/installer/src/wizard/ide-config-generator.js +6 -6
  62. package/packages/installer/src/wizard/pro-setup.js +3 -3
  63. package/scripts/package-synapse.js +5 -5
  64. package/scripts/validate-package-completeness.js +3 -3
  65. package/.aios-core/.session/current-session.json +0 -14
  66. package/.aios-core/data/registry-update-log.jsonl +0 -191
  67. package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
  68. package/.aios-core/docs/component-creation-guide.md +0 -458
  69. package/.aios-core/docs/session-update-pattern.md +0 -307
  70. package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
  71. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
  72. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
  73. package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
  74. package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
  75. package/.aios-core/docs/template-syntax.md +0 -267
  76. package/.aios-core/docs/troubleshooting-guide.md +0 -625
  77. package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
  78. package/.aios-core/manifests/agents.csv +0 -29
  79. package/.aios-core/manifests/tasks.csv +0 -198
  80. package/.aios-core/manifests/workers.csv +0 -204
  81. package/.claude/rules/agent-authority.md +0 -105
  82. package/.claude/rules/coderabbit-integration.md +0 -93
  83. package/.claude/rules/ids-principles.md +0 -112
  84. package/.claude/rules/story-lifecycle.md +0 -139
  85. package/.claude/rules/workflow-execution.md +0 -150
  86. package/pro/README.md +0 -66
  87. package/pro/license/degradation.js +0 -220
  88. package/pro/license/errors.js +0 -450
  89. package/pro/license/feature-gate.js +0 -354
  90. package/pro/license/index.js +0 -181
  91. package/pro/license/license-api.js +0 -651
  92. package/pro/license/license-cache.js +0 -523
  93. package/pro/license/license-crypto.js +0 -303
  94. package/scripts/glue/README.md +0 -355
  95. package/scripts/glue/compose-agent-prompt.cjs +0 -362
  96. /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
  97. /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
@@ -1,850 +1,850 @@
1
- const yaml = require('js-yaml');
2
- const { _createHash } = require('crypto');
3
- const DiffGenerator = require('./diff-generator');
4
- const ModificationValidator = require('./modification-validator');
5
-
6
- /**
7
- * Generates structured commit messages following conventional commit standards
8
- * for AIOS framework modifications
9
- */
10
- class CommitMessageGenerator {
11
- constructor(options = {}) {
12
- this.diffGenerator = new DiffGenerator();
13
- this.validator = new ModificationValidator();
14
-
15
- // Conventional commit types
16
- this.commitTypes = {
17
- feat: 'A new feature',
18
- fix: 'A bug fix',
19
- docs: 'Documentation only changes',
20
- style: 'Changes that do not affect the meaning of the code',
21
- refactor: 'A code change that neither fixes a bug nor adds a feature',
22
- perf: 'A code change that improves performance',
23
- test: 'Adding missing tests or correcting existing tests',
24
- build: 'Changes that affect the build system or external dependencies',
25
- ci: 'Changes to CI configuration files and scripts',
26
- chore: 'Other changes that don\'t modify src or test files',
27
- revert: 'Reverts a previous commit'
28
- };
29
-
30
- // Component-specific actions
31
- this.componentActions = {
32
- agent: {
33
- enhance: 'Enhanced capabilities or features',
34
- fix: 'Fixed issues or bugs',
35
- update: 'Updated configuration or metadata',
36
- refactor: 'Refactored implementation',
37
- deprecate: 'Marked features as deprecated',
38
- remove: 'Removed deprecated features'
39
- },
40
- task: {
41
- improve: 'Improved task flow or logic',
42
- fix: 'Fixed task execution issues',
43
- update: 'Updated task steps or output',
44
- optimize: 'Optimized performance',
45
- clarify: 'Clarified instructions or prompts'
46
- },
47
- workflow: {
48
- restructure: 'Restructured workflow phases',
49
- add: 'Added new phases or transitions',
50
- update: 'Updated phase configuration',
51
- optimize: 'Optimized workflow execution',
52
- fix: 'Fixed workflow issues'
53
- }
54
- };
55
-
56
- // Keywords for categorizing changes
57
- this.changeKeywords = {
58
- feat: ['add', 'new', 'implement', 'introduce', 'create'],
59
- fix: ['fix', 'resolve', 'correct', 'repair', 'patch'],
60
- refactor: ['refactor', 'restructure', 'reorganize', 'improve structure'],
61
- perf: ['optimize', 'performance', 'speed up', 'efficiency'],
62
- docs: ['document', 'docs', 'readme', 'comment', 'clarify']
63
- };
64
- }
65
-
66
- /**
67
- * Generate commit message for a modification
68
- * @param {Object} modification - Modification details
69
- * @returns {Promise<Object>} Generated commit message and metadata
70
- */
71
- async generateCommitMessage(modification) {
72
- const {
73
- componentType,
74
- componentName,
75
- originalContent,
76
- modifiedContent,
77
- userIntent = '',
78
- metadata = {}
79
- } = modification;
80
-
81
- try {
82
- // Analyze the changes
83
- const analysis = await this.analyzeModification(
84
- componentType,
85
- originalContent,
86
- modifiedContent
87
- );
88
-
89
- // Determine commit type and action
90
- const commitType = this.determineCommitType(analysis, userIntent);
91
- const action = this.determineAction(componentType, analysis, userIntent);
92
-
93
- // Generate summary
94
- const summary = this.generateSummary(
95
- componentType,
96
- componentName,
97
- action,
98
- analysis,
99
- userIntent
100
- );
101
-
102
- // Generate detailed description
103
- const details = this.generateDetails(analysis, metadata);
104
-
105
- // Check for breaking changes
106
- const breakingChanges = await this.detectBreakingChanges(
107
- componentType,
108
- originalContent,
109
- modifiedContent
110
- );
111
-
112
- // Construct the full message
113
- const message = this.constructMessage({
114
- type: commitType,
115
- scope: componentType,
116
- summary,
117
- body: details,
118
- breaking: breakingChanges,
119
- metadata
120
- });
121
-
122
- return {
123
- message,
124
- type: commitType,
125
- scope: componentType,
126
- summary,
127
- analysis,
128
- breakingChanges
129
- };
130
-
131
- } catch (_error) {
132
- throw new Error(`Failed to generate commit message: ${error.message}`);
133
- }
134
- }
135
-
136
- /**
137
- * Analyze modification to understand changes
138
- * @private
139
- */
140
- async analyzeModification(componentType, originalContent, modifiedContent) {
141
- const analysis = {
142
- componentType,
143
- changeType: null,
144
- modifications: [],
145
- additions: [],
146
- deletions: [],
147
- statistics: {
148
- linesAdded: 0,
149
- linesRemoved: 0,
150
- filesChanged: 1
151
- },
152
- semanticChanges: []
153
- };
154
-
155
- // Generate diff for analysis
156
- const diff = this.diffGenerator.generateUnifiedDiff(
157
- originalContent,
158
- modifiedContent,
159
- `${componentType}.before`,
160
- `${componentType}.after`
161
- );
162
-
163
- // Parse diff to extract changes
164
- const lines = diff.split('\n');
165
- let _currentSection = null;
166
-
167
- for (const line of lines) {
168
- if (line.startsWith('+') && !line.startsWith('+++')) {
169
- analysis.statistics.linesAdded++;
170
- analysis.additions.push(line.substring(1));
171
- } else if (line.startsWith('-') && !line.startsWith('---')) {
172
- analysis.statistics.linesRemoved++;
173
- analysis.deletions.push(line.substring(1));
174
- } else if (line.startsWith('@@')) {
175
- currentSection = this.extractSectionName(line);
176
- }
177
- }
178
-
179
- // Analyze semantic changes based on component type
180
- switch (componentType) {
181
- case 'agent':
182
- analysis.semanticChanges = await this.analyzeAgentChanges(
183
- originalContent,
184
- modifiedContent
185
- );
186
- break;
187
- case 'task':
188
- analysis.semanticChanges = await this.analyzeTaskChanges(
189
- originalContent,
190
- modifiedContent
191
- );
192
- break;
193
- case 'workflow':
194
- analysis.semanticChanges = await this.analyzeWorkflowChanges(
195
- originalContent,
196
- modifiedContent
197
- );
198
- break;
199
- }
200
-
201
- // Determine overall change type
202
- if (analysis.statistics.linesRemoved === 0 && analysis.statistics.linesAdded > 0) {
203
- analysis.changeType = 'addition';
204
- } else if (analysis.statistics.linesAdded === 0 && analysis.statistics.linesRemoved > 0) {
205
- analysis.changeType = 'deletion';
206
- } else {
207
- analysis.changeType = 'modification';
208
- }
209
-
210
- return analysis;
211
- }
212
-
213
- /**
214
- * Analyze agent-specific changes
215
- * @private
216
- */
217
- async analyzeAgentChanges(originalContent, modifiedContent) {
218
- const changes = [];
219
-
220
- try {
221
- const originalParts = this.parseAgentContent(originalContent);
222
- const modifiedParts = this.parseAgentContent(modifiedContent);
223
- const originalMeta = yaml.load(originalParts.yaml);
224
- const modifiedMeta = yaml.load(modifiedParts.yaml);
225
-
226
- // Check command changes
227
- if (originalMeta.commands || modifiedMeta.commands) {
228
- const originalCmds = Object.keys(originalMeta.commands || {});
229
- const modifiedCmds = Object.keys(modifiedMeta.commands || {});
230
-
231
- const added = modifiedCmds.filter(cmd => !originalCmds.includes(cmd));
232
- const removed = originalCmds.filter(cmd => !modifiedCmds.includes(cmd));
233
- const modified = originalCmds.filter(cmd =>
234
- modifiedCmds.includes(cmd) &&
235
- originalMeta.commands[cmd] !== modifiedMeta.commands[cmd]
236
- );
237
-
238
- if (added.length > 0) {
239
- changes.push({ type: 'commands_added', items: added });
240
- }
241
- if (removed.length > 0) {
242
- changes.push({ type: 'commands_removed', items: removed });
243
- }
244
- if (modified.length > 0) {
245
- changes.push({ type: 'commands_modified', items: modified });
246
- }
247
- }
248
-
249
- // Check dependency changes
250
- if (originalMeta.dependencies || modifiedMeta.dependencies) {
251
- const depChanges = this.compareDependencies(
252
- originalMeta.dependencies || {},
253
- modifiedMeta.dependencies || {}
254
- );
255
- if (depChanges.length > 0) {
256
- changes.push(...depChanges);
257
- }
258
- }
259
-
260
- // Check metadata changes
261
- const metadataFields = ['title', 'icon', 'whenToUse', 'description'];
262
- for (const field of metadataFields) {
263
- if (originalMeta[field] !== modifiedMeta[field]) {
264
- changes.push({
265
- type: 'metadata_changed',
266
- field,
267
- from: originalMeta[field],
268
- to: modifiedMeta[field]
269
- });
270
- }
271
- }
272
-
273
- } catch (_error) {
274
- // If parsing fails, return generic change
275
- changes.push({ type: 'content_modified' });
276
- }
277
-
278
- return changes;
279
- }
280
-
281
- /**
282
- * Analyze task-specific changes
283
- * @private
284
- */
285
- async analyzeTaskChanges(originalContent, modifiedContent) {
286
- const changes = [];
287
-
288
- // Check section changes
289
- const sections = ['## Purpose', '## Task Execution', '## Output Format'];
290
- for (const section of sections) {
291
- const originalSection = this.extractSection(originalContent, section);
292
- const modifiedSection = this.extractSection(modifiedContent, section);
293
-
294
- if (originalSection !== modifiedSection) {
295
- changes.push({
296
- type: 'section_modified',
297
- section: section.replace('## ', ''),
298
- contentChanged: true
299
- });
300
- }
301
- }
302
-
303
- // Check elicitation blocks
304
- const originalElicits = (originalContent.match(/\[\[LLM:[\s\S]*?\]\]/g) || []).length;
305
- const modifiedElicits = (modifiedContent.match(/\[\[LLM:[\s\S]*?\]\]/g) || []).length;
306
-
307
- if (originalElicits !== modifiedElicits) {
308
- changes.push({
309
- type: 'elicitation_changed',
310
- from: originalElicits,
311
- to: modifiedElicits
312
- });
313
- }
314
-
315
- // Check task steps
316
- const originalSteps = (originalContent.match(/### \d+\./g) || []).length;
317
- const modifiedSteps = (modifiedContent.match(/### \d+\./g) || []).length;
318
-
319
- if (originalSteps !== modifiedSteps) {
320
- changes.push({
321
- type: 'steps_changed',
322
- from: originalSteps,
323
- to: modifiedSteps
324
- });
325
- }
326
-
327
- return changes;
328
- }
329
-
330
- /**
331
- * Analyze workflow-specific changes
332
- * @private
333
- */
334
- async analyzeWorkflowChanges(originalContent, modifiedContent) {
335
- const changes = [];
336
-
337
- try {
338
- const originalWorkflow = yaml.load(originalContent);
339
- const modifiedWorkflow = yaml.load(modifiedContent);
340
-
341
- // Check phase changes
342
- const originalPhases = Object.keys(originalWorkflow.phases || {});
343
- const modifiedPhases = Object.keys(modifiedWorkflow.phases || {});
344
-
345
- const added = modifiedPhases.filter(p => !originalPhases.includes(p));
346
- const removed = originalPhases.filter(p => !modifiedPhases.includes(p));
347
-
348
- if (added.length > 0) {
349
- changes.push({ type: 'phases_added', items: added });
350
- }
351
- if (removed.length > 0) {
352
- changes.push({ type: 'phases_removed', items: removed });
353
- }
354
-
355
- // Check phase modifications
356
- for (const phase of originalPhases) {
357
- if (modifiedPhases.includes(phase)) {
358
- const originalPhase = originalWorkflow.phases[phase];
359
- const modifiedPhase = modifiedWorkflow.phases[phase];
360
-
361
- if (JSON.stringify(originalPhase) !== JSON.stringify(modifiedPhase)) {
362
- changes.push({
363
- type: 'phase_modified',
364
- phase,
365
- details: this.comparePhases(originalPhase, modifiedPhase)
366
- });
367
- }
368
- }
369
- }
370
-
371
- } catch (_error) {
372
- changes.push({ type: 'structure_modified' });
373
- }
374
-
375
- return changes;
376
- }
377
-
378
- /**
379
- * Determine commit type based on analysis
380
- * @private
381
- */
382
- determineCommitType(analysis, userIntent) {
383
- const intent = userIntent.toLowerCase();
384
-
385
- // Check user intent first
386
- for (const [type, keywords] of Object.entries(this.changeKeywords)) {
387
- if (keywords.some(keyword => intent.includes(keyword))) {
388
- return type;
389
- }
390
- }
391
-
392
- // Analyze semantic changes
393
- const semanticTypes = analysis.semanticChanges.map(change => change.type);
394
-
395
- if (semanticTypes.some(type => type.includes('added') || type.includes('new'))) {
396
- return 'feat';
397
- }
398
-
399
- if (semanticTypes.some(type => type.includes('fixed') || type.includes('corrected'))) {
400
- return 'fix';
401
- }
402
-
403
- if (semanticTypes.some(type => type.includes('performance') || type.includes('optimized'))) {
404
- return 'perf';
405
- }
406
-
407
- if (analysis.changeType === 'modification' &&
408
- analysis.statistics.linesAdded > 0 &&
409
- analysis.statistics.linesRemoved > 0) {
410
- return 'refactor';
411
- }
412
-
413
- // Default to chore for other changes
414
- return 'chore';
415
- }
416
-
417
- /**
418
- * Determine action verb based on changes
419
- * @private
420
- */
421
- determineAction(componentType, analysis, userIntent) {
422
- const actions = this.componentActions[componentType] || {};
423
- const intent = userIntent.toLowerCase();
424
-
425
- // Check if user intent matches known actions
426
- for (const [action, description] of Object.entries(actions)) {
427
- if (intent.includes(action) || intent.includes(description.toLowerCase())) {
428
- return action;
429
- }
430
- }
431
-
432
- // Determine from semantic changes
433
- const changeTypes = analysis.semanticChanges.map(c => c.type);
434
-
435
- if (changeTypes.includes('commands_added') || changeTypes.includes('phases_added')) {
436
- return 'enhance';
437
- }
438
-
439
- if (changeTypes.includes('commands_removed') || changeTypes.includes('phases_removed')) {
440
- return 'remove';
441
- }
442
-
443
- if (changeTypes.some(t => t.includes('modified'))) {
444
- return 'update';
445
- }
446
-
447
- return 'update'; // Default action
448
- }
449
-
450
- /**
451
- * Generate commit summary
452
- * @private
453
- */
454
- generateSummary(componentType, componentName, action, analysis, userIntent) {
455
- // Use user intent if it's concise
456
- if (userIntent && userIntent.length < 50) {
457
- return userIntent.toLowerCase();
458
- }
459
-
460
- // Generate based on analysis
461
- const _changeCount = analysis.semanticChanges.length;
462
- const primaryChange = analysis.semanticChanges[0];
463
-
464
- if (primaryChange) {
465
- switch (primaryChange.type) {
466
- case 'commands_added':
467
- return `add ${primaryChange.items.join(', ')} command${primaryChange.items.length > 1 ? 's' : ''}`;
468
- case 'commands_removed':
469
- return `remove ${primaryChange.items.join(', ')} command${primaryChange.items.length > 1 ? 's' : ''}`;
470
- case 'phases_added':
471
- return `add ${primaryChange.items.join(', ')} phase${primaryChange.items.length > 1 ? 's' : ''}`;
472
- case 'phases_removed':
473
- return `remove ${primaryChange.items.join(', ')} phase${primaryChange.items.length > 1 ? 's' : ''}`;
474
- case 'metadata_changed':
475
- return `update ${primaryChange.field}`;
476
- default:
477
- return `${action} ${componentName}`;
478
- }
479
- }
480
-
481
- return `${action} ${componentName}`;
482
- }
483
-
484
- /**
485
- * Generate detailed commit body
486
- * @private
487
- */
488
- generateDetails(analysis, metadata) {
489
- const details = [];
490
-
491
- // Add statistics
492
- if (analysis.statistics.linesAdded > 0 || analysis.statistics.linesRemoved > 0) {
493
- details.push(
494
- `Changed: +${analysis.statistics.linesAdded} -${analysis.statistics.linesRemoved} lines`
495
- );
496
- }
497
-
498
- // Add semantic changes
499
- for (const change of analysis.semanticChanges) {
500
- switch (change.type) {
501
- case 'commands_added':
502
- details.push(`Added commands: ${change.items.join(', ')}`);
503
- break;
504
- case 'commands_removed':
505
- details.push(`Removed commands: ${change.items.join(', ')}`);
506
- break;
507
- case 'commands_modified':
508
- details.push(`Modified commands: ${change.items.join(', ')}`);
509
- break;
510
- case 'phases_added':
511
- details.push(`Added phases: ${change.items.join(', ')}`);
512
- break;
513
- case 'phases_removed':
514
- details.push(`Removed phases: ${change.items.join(', ')}`);
515
- break;
516
- case 'phase_modified':
517
- details.push(`Modified phase '${change.phase}': ${change.details.join(', ')}`);
518
- break;
519
- case 'metadata_changed':
520
- details.push(`Updated ${change.field}: "${change.from}" → "${change.to}"`);
521
- break;
522
- case 'section_modified':
523
- details.push(`Updated ${change.section} section`);
524
- break;
525
- case 'elicitation_changed':
526
- details.push(`Elicitation blocks: ${change.from} → ${change.to}`);
527
- break;
528
- case 'steps_changed':
529
- details.push(`Task steps: ${change.from} → ${change.to}`);
530
- break;
531
- }
532
- }
533
-
534
- // Add metadata information
535
- if (metadata.reason) {
536
- details.push(`\nReason: ${metadata.reason}`);
537
- }
538
-
539
- if (metadata.impact) {
540
- details.push(`Impact: ${metadata.impact}`);
541
- }
542
-
543
- if (metadata.relatedIssues && metadata.relatedIssues.length > 0) {
544
- details.push(`\nRelated: ${metadata.relatedIssues.join(', ')}`);
545
- }
546
-
547
- return details;
548
- }
549
-
550
- /**
551
- * Detect breaking changes
552
- * @private
553
- */
554
- async detectBreakingChanges(componentType, originalContent, modifiedContent) {
555
- const validation = await this.validator.validateModification(
556
- componentType,
557
- originalContent,
558
- modifiedContent
559
- );
560
-
561
- return validation.breakingChanges || [];
562
- }
563
-
564
- /**
565
- * Construct the full commit message
566
- * @private
567
- */
568
- constructMessage(parts) {
569
- const { type, scope, summary, body, breaking, metadata } = parts;
570
-
571
- // Header
572
- let message = `${type}(${scope}): ${summary}`;
573
-
574
- // Body
575
- if (body && body.length > 0) {
576
- message += '\n\n' + body.join('\n');
577
- }
578
-
579
- // Breaking changes
580
- if (breaking && breaking.length > 0) {
581
- message += '\n\nBREAKING CHANGE:';
582
- for (const change of breaking) {
583
- message += `\n- ${change.impact}`;
584
- if (change.items) {
585
- message += ` (${change.items.join(', ')})`;
586
- }
587
- }
588
- }
589
-
590
- // Footer
591
- const footer = [];
592
-
593
- if (metadata.approvedBy) {
594
- footer.push(`Approved-by: ${metadata.approvedBy}`);
595
- }
596
-
597
- if (metadata.reviewedBy) {
598
- footer.push(`Reviewed-by: ${metadata.reviewedBy}`);
599
- }
600
-
601
- footer.push('Generated-by: aios-developer meta-agent');
602
-
603
- if (footer.length > 0) {
604
- message += '\n\n' + footer.join('\n');
605
- }
606
-
607
- return message;
608
- }
609
-
610
- /**
611
- * Generate commit message for batch modifications
612
- * @param {Array} modifications - Array of modifications
613
- * @returns {Promise<Object>} Batch commit message
614
- */
615
- async generateBatchCommitMessage(modifications) {
616
- const summaries = [];
617
- const allBreaking = [];
618
- const stats = {
619
- agents: 0,
620
- tasks: 0,
621
- workflows: 0,
622
- total: modifications.length
623
- };
624
-
625
- // Process each modification
626
- for (const mod of modifications) {
627
- const result = await this.generateCommitMessage(mod);
628
- summaries.push(`- ${result.scope}: ${result.summary}`);
629
- allBreaking.push(...result.breakingChanges);
630
-
631
- // Count by type
632
- stats[`${mod.componentType}s`]++;
633
- }
634
-
635
- // Determine overall type
636
- const hasBreaking = allBreaking.length > 0;
637
- const type = hasBreaking ? 'feat!' : 'chore';
638
-
639
- // Construct message
640
- let message = `${type}: batch update ${stats.total} components`;
641
-
642
- message += '\n\nModifications:';
643
- message += '\n' + summaries.join('\n');
644
-
645
- message += '\n\nSummary:';
646
- if (stats.agents > 0) message += `\n- ${stats.agents} agent(s)`;
647
- if (stats.tasks > 0) message += `\n- ${stats.tasks} task(s)`;
648
- if (stats.workflows > 0) message += `\n- ${stats.workflows} workflow(s)`;
649
-
650
- if (hasBreaking) {
651
- message += '\n\nBREAKING CHANGES:';
652
- for (const breaking of allBreaking) {
653
- message += `\n- ${breaking.impact}`;
654
- }
655
- }
656
-
657
- message += '\n\nGenerated-by: aios-developer meta-agent';
658
-
659
- return {
660
- message,
661
- type,
662
- stats,
663
- breakingChanges: allBreaking
664
- };
665
- }
666
-
667
- /**
668
- * Suggest commit message improvements
669
- * @param {string} message - Original commit message
670
- * @returns {Object} Suggestions for improvement
671
- */
672
- suggestImprovements(message) {
673
- const suggestions = [];
674
- const lines = message.split('\n');
675
- const header = lines[0];
676
-
677
- // Check header format
678
- const headerMatch = header.match(/^(\w+)(\([\w-]+\))?: (.+)$/);
679
- if (!headerMatch) {
680
- suggestions.push({
681
- type: 'format',
682
- issue: 'Header doesn\'t follow conventional format',
683
- suggestion: 'Use format: type(_scope): subject'
684
- });
685
- } else {
686
- const [, type, scope, subject] = headerMatch;
687
-
688
- // Check type
689
- if (!this.commitTypes[type]) {
690
- suggestions.push({
691
- type: 'type',
692
- issue: `Unknown commit type: ${type}`,
693
- suggestion: `Use one of: ${Object.keys(this.commitTypes).join(', ')}`
694
- });
695
- }
696
-
697
- // Check subject length
698
- if (subject.length > 50) {
699
- suggestions.push({
700
- type: 'length',
701
- issue: 'Subject line too long',
702
- suggestion: 'Keep subject under 50 characters'
703
- });
704
- }
705
-
706
- // Check subject format
707
- if (subject[0] === subject[0].toUpperCase()) {
708
- suggestions.push({
709
- type: 'case',
710
- issue: 'Subject should not be capitalized',
711
- suggestion: 'Use lowercase for subject'
712
- });
713
- }
714
-
715
- if (subject.endsWith('.')) {
716
- suggestions.push({
717
- type: 'punctuation',
718
- issue: 'Subject should not end with period',
719
- suggestion: 'Remove trailing period'
720
- });
721
- }
722
- }
723
-
724
- // Check body
725
- if (lines.length > 1) {
726
- if (lines[1] !== '') {
727
- suggestions.push({
728
- type: 'spacing',
729
- issue: 'Missing blank line after header',
730
- suggestion: 'Add blank line between header and body'
731
- });
732
- }
733
-
734
- // Check line length in body
735
- for (let i = 2; i < lines.length; i++) {
736
- if (lines[i].length > 72 && !lines[i].startsWith('BREAKING')) {
737
- suggestions.push({
738
- type: 'line-length',
739
- issue: `Line ${i + 1} exceeds 72 characters`,
740
- suggestion: 'Wrap body text at 72 characters'
741
- });
742
- }
743
- }
744
- }
745
-
746
- return {
747
- valid: suggestions.length === 0,
748
- suggestions,
749
- improvedMessage: this.applyImprovements(message, suggestions)
750
- };
751
- }
752
-
753
- /**
754
- * Apply improvements to commit message
755
- * @private
756
- */
757
- applyImprovements(message, suggestions) {
758
- let improved = message;
759
-
760
- for (const suggestion of suggestions) {
761
- switch (suggestion.type) {
762
- case 'case':
763
- improved = improved.replace(/^(\w+)(\([\w-]+\))?: (.)/, (match, type, scope, firstChar) =>
764
- `${type}${scope || ''}: ${firstChar.toLowerCase()}`
765
- );
766
- break;
767
- case 'punctuation':
768
- improved = improved.replace(/^(.+)\.$/, '$1');
769
- break;
770
- case 'spacing':
771
- const lines = improved.split('\n');
772
- if (lines.length > 1 && lines[1] !== '') {
773
- lines.splice(1, 0, '');
774
- improved = lines.join('\n');
775
- }
776
- break;
777
- }
778
- }
779
-
780
- return improved;
781
- }
782
-
783
- // Utility methods
784
- parseAgentContent(content) {
785
- const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
786
- if (!match) {
787
- throw new Error('Invalid agent content format');
788
- }
789
- return {
790
- yaml: match[1],
791
- markdown: match[2]
792
- };
793
- }
794
-
795
- extractSection(content, sectionHeader) {
796
- const regex = new RegExp(`${sectionHeader}[\\s\\S]*?(?=\\n##|$)`, 'i');
797
- const match = content.match(regex);
798
- return match ? match[0] : '';
799
- }
800
-
801
- extractSectionName(diffLine) {
802
- const match = diffLine.match(/@@ .* @@ (.+)/);
803
- return match ? match[1] : 'unknown';
804
- }
805
-
806
- compareDependencies(original, modified) {
807
- const changes = [];
808
- const types = ['tasks', 'workflows', 'agents'];
809
-
810
- for (const type of types) {
811
- const originalDeps = original[type] || [];
812
- const modifiedDeps = modified[type] || [];
813
-
814
- const added = modifiedDeps.filter(d => !originalDeps.includes(d));
815
- const removed = originalDeps.filter(d => !modifiedDeps.includes(d));
816
-
817
- if (added.length > 0) {
818
- changes.push({ type: `${type}_dependencies_added`, items: added });
819
- }
820
- if (removed.length > 0) {
821
- changes.push({ type: `${type}_dependencies_removed`, items: removed });
822
- }
823
- }
824
-
825
- return changes;
826
- }
827
-
828
- comparePhases(originalPhase, modifiedPhase) {
829
- const details = [];
830
-
831
- if (originalPhase.sequence !== modifiedPhase.sequence) {
832
- details.push(`sequence ${originalPhase.sequence}→${modifiedPhase.sequence}`);
833
- }
834
-
835
- const originalAgents = originalPhase.agents || [];
836
- const modifiedAgents = modifiedPhase.agents || [];
837
-
838
- if (JSON.stringify(originalAgents) !== JSON.stringify(modifiedAgents)) {
839
- details.push('agents changed');
840
- }
841
-
842
- if (JSON.stringify(originalPhase.artifacts) !== JSON.stringify(modifiedPhase.artifacts)) {
843
- details.push('artifacts changed');
844
- }
845
-
846
- return details;
847
- }
848
- }
849
-
1
+ const yaml = require('js-yaml');
2
+ const { _createHash } = require('crypto');
3
+ const DiffGenerator = require('./diff-generator');
4
+ const ModificationValidator = require('./modification-validator');
5
+
6
+ /**
7
+ * Generates structured commit messages following conventional commit standards
8
+ * for AIOS framework modifications
9
+ */
10
+ class CommitMessageGenerator {
11
+ constructor(options = {}) {
12
+ this.diffGenerator = new DiffGenerator();
13
+ this.validator = new ModificationValidator();
14
+
15
+ // Conventional commit types
16
+ this.commitTypes = {
17
+ feat: 'A new feature',
18
+ fix: 'A bug fix',
19
+ docs: 'Documentation only changes',
20
+ style: 'Changes that do not affect the meaning of the code',
21
+ refactor: 'A code change that neither fixes a bug nor adds a feature',
22
+ perf: 'A code change that improves performance',
23
+ test: 'Adding missing tests or correcting existing tests',
24
+ build: 'Changes that affect the build system or external dependencies',
25
+ ci: 'Changes to CI configuration files and scripts',
26
+ chore: 'Other changes that don\'t modify src or test files',
27
+ revert: 'Reverts a previous commit'
28
+ };
29
+
30
+ // Component-specific actions
31
+ this.componentActions = {
32
+ agent: {
33
+ enhance: 'Enhanced capabilities or features',
34
+ fix: 'Fixed issues or bugs',
35
+ update: 'Updated configuration or metadata',
36
+ refactor: 'Refactored implementation',
37
+ deprecate: 'Marked features as deprecated',
38
+ remove: 'Removed deprecated features'
39
+ },
40
+ task: {
41
+ improve: 'Improved task flow or logic',
42
+ fix: 'Fixed task execution issues',
43
+ update: 'Updated task steps or output',
44
+ optimize: 'Optimized performance',
45
+ clarify: 'Clarified instructions or prompts'
46
+ },
47
+ workflow: {
48
+ restructure: 'Restructured workflow phases',
49
+ add: 'Added new phases or transitions',
50
+ update: 'Updated phase configuration',
51
+ optimize: 'Optimized workflow execution',
52
+ fix: 'Fixed workflow issues'
53
+ }
54
+ };
55
+
56
+ // Keywords for categorizing changes
57
+ this.changeKeywords = {
58
+ feat: ['add', 'new', 'implement', 'introduce', 'create'],
59
+ fix: ['fix', 'resolve', 'correct', 'repair', 'patch'],
60
+ refactor: ['refactor', 'restructure', 'reorganize', 'improve structure'],
61
+ perf: ['optimize', 'performance', 'speed up', 'efficiency'],
62
+ docs: ['document', 'docs', 'readme', 'comment', 'clarify']
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Generate commit message for a modification
68
+ * @param {Object} modification - Modification details
69
+ * @returns {Promise<Object>} Generated commit message and metadata
70
+ */
71
+ async generateCommitMessage(modification) {
72
+ const {
73
+ componentType,
74
+ componentName,
75
+ originalContent,
76
+ modifiedContent,
77
+ userIntent = '',
78
+ metadata = {}
79
+ } = modification;
80
+
81
+ try {
82
+ // Analyze the changes
83
+ const analysis = await this.analyzeModification(
84
+ componentType,
85
+ originalContent,
86
+ modifiedContent
87
+ );
88
+
89
+ // Determine commit type and action
90
+ const commitType = this.determineCommitType(analysis, userIntent);
91
+ const action = this.determineAction(componentType, analysis, userIntent);
92
+
93
+ // Generate summary
94
+ const summary = this.generateSummary(
95
+ componentType,
96
+ componentName,
97
+ action,
98
+ analysis,
99
+ userIntent
100
+ );
101
+
102
+ // Generate detailed description
103
+ const details = this.generateDetails(analysis, metadata);
104
+
105
+ // Check for breaking changes
106
+ const breakingChanges = await this.detectBreakingChanges(
107
+ componentType,
108
+ originalContent,
109
+ modifiedContent
110
+ );
111
+
112
+ // Construct the full message
113
+ const message = this.constructMessage({
114
+ type: commitType,
115
+ scope: componentType,
116
+ summary,
117
+ body: details,
118
+ breaking: breakingChanges,
119
+ metadata
120
+ });
121
+
122
+ return {
123
+ message,
124
+ type: commitType,
125
+ scope: componentType,
126
+ summary,
127
+ analysis,
128
+ breakingChanges
129
+ };
130
+
131
+ } catch (_error) {
132
+ throw new Error(`Failed to generate commit message: ${error.message}`);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Analyze modification to understand changes
138
+ * @private
139
+ */
140
+ async analyzeModification(componentType, originalContent, modifiedContent) {
141
+ const analysis = {
142
+ componentType,
143
+ changeType: null,
144
+ modifications: [],
145
+ additions: [],
146
+ deletions: [],
147
+ statistics: {
148
+ linesAdded: 0,
149
+ linesRemoved: 0,
150
+ filesChanged: 1
151
+ },
152
+ semanticChanges: []
153
+ };
154
+
155
+ // Generate diff for analysis
156
+ const diff = this.diffGenerator.generateUnifiedDiff(
157
+ originalContent,
158
+ modifiedContent,
159
+ `${componentType}.before`,
160
+ `${componentType}.after`
161
+ );
162
+
163
+ // Parse diff to extract changes
164
+ const lines = diff.split('\n');
165
+ let _currentSection = null;
166
+
167
+ for (const line of lines) {
168
+ if (line.startsWith('+') && !line.startsWith('+++')) {
169
+ analysis.statistics.linesAdded++;
170
+ analysis.additions.push(line.substring(1));
171
+ } else if (line.startsWith('-') && !line.startsWith('---')) {
172
+ analysis.statistics.linesRemoved++;
173
+ analysis.deletions.push(line.substring(1));
174
+ } else if (line.startsWith('@@')) {
175
+ currentSection = this.extractSectionName(line);
176
+ }
177
+ }
178
+
179
+ // Analyze semantic changes based on component type
180
+ switch (componentType) {
181
+ case 'agent':
182
+ analysis.semanticChanges = await this.analyzeAgentChanges(
183
+ originalContent,
184
+ modifiedContent
185
+ );
186
+ break;
187
+ case 'task':
188
+ analysis.semanticChanges = await this.analyzeTaskChanges(
189
+ originalContent,
190
+ modifiedContent
191
+ );
192
+ break;
193
+ case 'workflow':
194
+ analysis.semanticChanges = await this.analyzeWorkflowChanges(
195
+ originalContent,
196
+ modifiedContent
197
+ );
198
+ break;
199
+ }
200
+
201
+ // Determine overall change type
202
+ if (analysis.statistics.linesRemoved === 0 && analysis.statistics.linesAdded > 0) {
203
+ analysis.changeType = 'addition';
204
+ } else if (analysis.statistics.linesAdded === 0 && analysis.statistics.linesRemoved > 0) {
205
+ analysis.changeType = 'deletion';
206
+ } else {
207
+ analysis.changeType = 'modification';
208
+ }
209
+
210
+ return analysis;
211
+ }
212
+
213
+ /**
214
+ * Analyze agent-specific changes
215
+ * @private
216
+ */
217
+ async analyzeAgentChanges(originalContent, modifiedContent) {
218
+ const changes = [];
219
+
220
+ try {
221
+ const originalParts = this.parseAgentContent(originalContent);
222
+ const modifiedParts = this.parseAgentContent(modifiedContent);
223
+ const originalMeta = yaml.load(originalParts.yaml);
224
+ const modifiedMeta = yaml.load(modifiedParts.yaml);
225
+
226
+ // Check command changes
227
+ if (originalMeta.commands || modifiedMeta.commands) {
228
+ const originalCmds = Object.keys(originalMeta.commands || {});
229
+ const modifiedCmds = Object.keys(modifiedMeta.commands || {});
230
+
231
+ const added = modifiedCmds.filter(cmd => !originalCmds.includes(cmd));
232
+ const removed = originalCmds.filter(cmd => !modifiedCmds.includes(cmd));
233
+ const modified = originalCmds.filter(cmd =>
234
+ modifiedCmds.includes(cmd) &&
235
+ originalMeta.commands[cmd] !== modifiedMeta.commands[cmd]
236
+ );
237
+
238
+ if (added.length > 0) {
239
+ changes.push({ type: 'commands_added', items: added });
240
+ }
241
+ if (removed.length > 0) {
242
+ changes.push({ type: 'commands_removed', items: removed });
243
+ }
244
+ if (modified.length > 0) {
245
+ changes.push({ type: 'commands_modified', items: modified });
246
+ }
247
+ }
248
+
249
+ // Check dependency changes
250
+ if (originalMeta.dependencies || modifiedMeta.dependencies) {
251
+ const depChanges = this.compareDependencies(
252
+ originalMeta.dependencies || {},
253
+ modifiedMeta.dependencies || {}
254
+ );
255
+ if (depChanges.length > 0) {
256
+ changes.push(...depChanges);
257
+ }
258
+ }
259
+
260
+ // Check metadata changes
261
+ const metadataFields = ['title', 'icon', 'whenToUse', 'description'];
262
+ for (const field of metadataFields) {
263
+ if (originalMeta[field] !== modifiedMeta[field]) {
264
+ changes.push({
265
+ type: 'metadata_changed',
266
+ field,
267
+ from: originalMeta[field],
268
+ to: modifiedMeta[field]
269
+ });
270
+ }
271
+ }
272
+
273
+ } catch (_error) {
274
+ // If parsing fails, return generic change
275
+ changes.push({ type: 'content_modified' });
276
+ }
277
+
278
+ return changes;
279
+ }
280
+
281
+ /**
282
+ * Analyze task-specific changes
283
+ * @private
284
+ */
285
+ async analyzeTaskChanges(originalContent, modifiedContent) {
286
+ const changes = [];
287
+
288
+ // Check section changes
289
+ const sections = ['## Purpose', '## Task Execution', '## Output Format'];
290
+ for (const section of sections) {
291
+ const originalSection = this.extractSection(originalContent, section);
292
+ const modifiedSection = this.extractSection(modifiedContent, section);
293
+
294
+ if (originalSection !== modifiedSection) {
295
+ changes.push({
296
+ type: 'section_modified',
297
+ section: section.replace('## ', ''),
298
+ contentChanged: true
299
+ });
300
+ }
301
+ }
302
+
303
+ // Check elicitation blocks
304
+ const originalElicits = (originalContent.match(/\[\[LLM:[\s\S]*?\]\]/g) || []).length;
305
+ const modifiedElicits = (modifiedContent.match(/\[\[LLM:[\s\S]*?\]\]/g) || []).length;
306
+
307
+ if (originalElicits !== modifiedElicits) {
308
+ changes.push({
309
+ type: 'elicitation_changed',
310
+ from: originalElicits,
311
+ to: modifiedElicits
312
+ });
313
+ }
314
+
315
+ // Check task steps
316
+ const originalSteps = (originalContent.match(/### \d+\./g) || []).length;
317
+ const modifiedSteps = (modifiedContent.match(/### \d+\./g) || []).length;
318
+
319
+ if (originalSteps !== modifiedSteps) {
320
+ changes.push({
321
+ type: 'steps_changed',
322
+ from: originalSteps,
323
+ to: modifiedSteps
324
+ });
325
+ }
326
+
327
+ return changes;
328
+ }
329
+
330
+ /**
331
+ * Analyze workflow-specific changes
332
+ * @private
333
+ */
334
+ async analyzeWorkflowChanges(originalContent, modifiedContent) {
335
+ const changes = [];
336
+
337
+ try {
338
+ const originalWorkflow = yaml.load(originalContent);
339
+ const modifiedWorkflow = yaml.load(modifiedContent);
340
+
341
+ // Check phase changes
342
+ const originalPhases = Object.keys(originalWorkflow.phases || {});
343
+ const modifiedPhases = Object.keys(modifiedWorkflow.phases || {});
344
+
345
+ const added = modifiedPhases.filter(p => !originalPhases.includes(p));
346
+ const removed = originalPhases.filter(p => !modifiedPhases.includes(p));
347
+
348
+ if (added.length > 0) {
349
+ changes.push({ type: 'phases_added', items: added });
350
+ }
351
+ if (removed.length > 0) {
352
+ changes.push({ type: 'phases_removed', items: removed });
353
+ }
354
+
355
+ // Check phase modifications
356
+ for (const phase of originalPhases) {
357
+ if (modifiedPhases.includes(phase)) {
358
+ const originalPhase = originalWorkflow.phases[phase];
359
+ const modifiedPhase = modifiedWorkflow.phases[phase];
360
+
361
+ if (JSON.stringify(originalPhase) !== JSON.stringify(modifiedPhase)) {
362
+ changes.push({
363
+ type: 'phase_modified',
364
+ phase,
365
+ details: this.comparePhases(originalPhase, modifiedPhase)
366
+ });
367
+ }
368
+ }
369
+ }
370
+
371
+ } catch (_error) {
372
+ changes.push({ type: 'structure_modified' });
373
+ }
374
+
375
+ return changes;
376
+ }
377
+
378
+ /**
379
+ * Determine commit type based on analysis
380
+ * @private
381
+ */
382
+ determineCommitType(analysis, userIntent) {
383
+ const intent = userIntent.toLowerCase();
384
+
385
+ // Check user intent first
386
+ for (const [type, keywords] of Object.entries(this.changeKeywords)) {
387
+ if (keywords.some(keyword => intent.includes(keyword))) {
388
+ return type;
389
+ }
390
+ }
391
+
392
+ // Analyze semantic changes
393
+ const semanticTypes = analysis.semanticChanges.map(change => change.type);
394
+
395
+ if (semanticTypes.some(type => type.includes('added') || type.includes('new'))) {
396
+ return 'feat';
397
+ }
398
+
399
+ if (semanticTypes.some(type => type.includes('fixed') || type.includes('corrected'))) {
400
+ return 'fix';
401
+ }
402
+
403
+ if (semanticTypes.some(type => type.includes('performance') || type.includes('optimized'))) {
404
+ return 'perf';
405
+ }
406
+
407
+ if (analysis.changeType === 'modification' &&
408
+ analysis.statistics.linesAdded > 0 &&
409
+ analysis.statistics.linesRemoved > 0) {
410
+ return 'refactor';
411
+ }
412
+
413
+ // Default to chore for other changes
414
+ return 'chore';
415
+ }
416
+
417
+ /**
418
+ * Determine action verb based on changes
419
+ * @private
420
+ */
421
+ determineAction(componentType, analysis, userIntent) {
422
+ const actions = this.componentActions[componentType] || {};
423
+ const intent = userIntent.toLowerCase();
424
+
425
+ // Check if user intent matches known actions
426
+ for (const [action, description] of Object.entries(actions)) {
427
+ if (intent.includes(action) || intent.includes(description.toLowerCase())) {
428
+ return action;
429
+ }
430
+ }
431
+
432
+ // Determine from semantic changes
433
+ const changeTypes = analysis.semanticChanges.map(c => c.type);
434
+
435
+ if (changeTypes.includes('commands_added') || changeTypes.includes('phases_added')) {
436
+ return 'enhance';
437
+ }
438
+
439
+ if (changeTypes.includes('commands_removed') || changeTypes.includes('phases_removed')) {
440
+ return 'remove';
441
+ }
442
+
443
+ if (changeTypes.some(t => t.includes('modified'))) {
444
+ return 'update';
445
+ }
446
+
447
+ return 'update'; // Default action
448
+ }
449
+
450
+ /**
451
+ * Generate commit summary
452
+ * @private
453
+ */
454
+ generateSummary(componentType, componentName, action, analysis, userIntent) {
455
+ // Use user intent if it's concise
456
+ if (userIntent && userIntent.length < 50) {
457
+ return userIntent.toLowerCase();
458
+ }
459
+
460
+ // Generate based on analysis
461
+ const _changeCount = analysis.semanticChanges.length;
462
+ const primaryChange = analysis.semanticChanges[0];
463
+
464
+ if (primaryChange) {
465
+ switch (primaryChange.type) {
466
+ case 'commands_added':
467
+ return `add ${primaryChange.items.join(', ')} command${primaryChange.items.length > 1 ? 's' : ''}`;
468
+ case 'commands_removed':
469
+ return `remove ${primaryChange.items.join(', ')} command${primaryChange.items.length > 1 ? 's' : ''}`;
470
+ case 'phases_added':
471
+ return `add ${primaryChange.items.join(', ')} phase${primaryChange.items.length > 1 ? 's' : ''}`;
472
+ case 'phases_removed':
473
+ return `remove ${primaryChange.items.join(', ')} phase${primaryChange.items.length > 1 ? 's' : ''}`;
474
+ case 'metadata_changed':
475
+ return `update ${primaryChange.field}`;
476
+ default:
477
+ return `${action} ${componentName}`;
478
+ }
479
+ }
480
+
481
+ return `${action} ${componentName}`;
482
+ }
483
+
484
+ /**
485
+ * Generate detailed commit body
486
+ * @private
487
+ */
488
+ generateDetails(analysis, metadata) {
489
+ const details = [];
490
+
491
+ // Add statistics
492
+ if (analysis.statistics.linesAdded > 0 || analysis.statistics.linesRemoved > 0) {
493
+ details.push(
494
+ `Changed: +${analysis.statistics.linesAdded} -${analysis.statistics.linesRemoved} lines`
495
+ );
496
+ }
497
+
498
+ // Add semantic changes
499
+ for (const change of analysis.semanticChanges) {
500
+ switch (change.type) {
501
+ case 'commands_added':
502
+ details.push(`Added commands: ${change.items.join(', ')}`);
503
+ break;
504
+ case 'commands_removed':
505
+ details.push(`Removed commands: ${change.items.join(', ')}`);
506
+ break;
507
+ case 'commands_modified':
508
+ details.push(`Modified commands: ${change.items.join(', ')}`);
509
+ break;
510
+ case 'phases_added':
511
+ details.push(`Added phases: ${change.items.join(', ')}`);
512
+ break;
513
+ case 'phases_removed':
514
+ details.push(`Removed phases: ${change.items.join(', ')}`);
515
+ break;
516
+ case 'phase_modified':
517
+ details.push(`Modified phase '${change.phase}': ${change.details.join(', ')}`);
518
+ break;
519
+ case 'metadata_changed':
520
+ details.push(`Updated ${change.field}: "${change.from}" → "${change.to}"`);
521
+ break;
522
+ case 'section_modified':
523
+ details.push(`Updated ${change.section} section`);
524
+ break;
525
+ case 'elicitation_changed':
526
+ details.push(`Elicitation blocks: ${change.from} → ${change.to}`);
527
+ break;
528
+ case 'steps_changed':
529
+ details.push(`Task steps: ${change.from} → ${change.to}`);
530
+ break;
531
+ }
532
+ }
533
+
534
+ // Add metadata information
535
+ if (metadata.reason) {
536
+ details.push(`\nReason: ${metadata.reason}`);
537
+ }
538
+
539
+ if (metadata.impact) {
540
+ details.push(`Impact: ${metadata.impact}`);
541
+ }
542
+
543
+ if (metadata.relatedIssues && metadata.relatedIssues.length > 0) {
544
+ details.push(`\nRelated: ${metadata.relatedIssues.join(', ')}`);
545
+ }
546
+
547
+ return details;
548
+ }
549
+
550
+ /**
551
+ * Detect breaking changes
552
+ * @private
553
+ */
554
+ async detectBreakingChanges(componentType, originalContent, modifiedContent) {
555
+ const validation = await this.validator.validateModification(
556
+ componentType,
557
+ originalContent,
558
+ modifiedContent
559
+ );
560
+
561
+ return validation.breakingChanges || [];
562
+ }
563
+
564
+ /**
565
+ * Construct the full commit message
566
+ * @private
567
+ */
568
+ constructMessage(parts) {
569
+ const { type, scope, summary, body, breaking, metadata } = parts;
570
+
571
+ // Header
572
+ let message = `${type}(${scope}): ${summary}`;
573
+
574
+ // Body
575
+ if (body && body.length > 0) {
576
+ message += '\n\n' + body.join('\n');
577
+ }
578
+
579
+ // Breaking changes
580
+ if (breaking && breaking.length > 0) {
581
+ message += '\n\nBREAKING CHANGE:';
582
+ for (const change of breaking) {
583
+ message += `\n- ${change.impact}`;
584
+ if (change.items) {
585
+ message += ` (${change.items.join(', ')})`;
586
+ }
587
+ }
588
+ }
589
+
590
+ // Footer
591
+ const footer = [];
592
+
593
+ if (metadata.approvedBy) {
594
+ footer.push(`Approved-by: ${metadata.approvedBy}`);
595
+ }
596
+
597
+ if (metadata.reviewedBy) {
598
+ footer.push(`Reviewed-by: ${metadata.reviewedBy}`);
599
+ }
600
+
601
+ footer.push('Generated-by: aios-developer meta-agent');
602
+
603
+ if (footer.length > 0) {
604
+ message += '\n\n' + footer.join('\n');
605
+ }
606
+
607
+ return message;
608
+ }
609
+
610
+ /**
611
+ * Generate commit message for batch modifications
612
+ * @param {Array} modifications - Array of modifications
613
+ * @returns {Promise<Object>} Batch commit message
614
+ */
615
+ async generateBatchCommitMessage(modifications) {
616
+ const summaries = [];
617
+ const allBreaking = [];
618
+ const stats = {
619
+ agents: 0,
620
+ tasks: 0,
621
+ workflows: 0,
622
+ total: modifications.length
623
+ };
624
+
625
+ // Process each modification
626
+ for (const mod of modifications) {
627
+ const result = await this.generateCommitMessage(mod);
628
+ summaries.push(`- ${result.scope}: ${result.summary}`);
629
+ allBreaking.push(...result.breakingChanges);
630
+
631
+ // Count by type
632
+ stats[`${mod.componentType}s`]++;
633
+ }
634
+
635
+ // Determine overall type
636
+ const hasBreaking = allBreaking.length > 0;
637
+ const type = hasBreaking ? 'feat!' : 'chore';
638
+
639
+ // Construct message
640
+ let message = `${type}: batch update ${stats.total} components`;
641
+
642
+ message += '\n\nModifications:';
643
+ message += '\n' + summaries.join('\n');
644
+
645
+ message += '\n\nSummary:';
646
+ if (stats.agents > 0) message += `\n- ${stats.agents} agent(s)`;
647
+ if (stats.tasks > 0) message += `\n- ${stats.tasks} task(s)`;
648
+ if (stats.workflows > 0) message += `\n- ${stats.workflows} workflow(s)`;
649
+
650
+ if (hasBreaking) {
651
+ message += '\n\nBREAKING CHANGES:';
652
+ for (const breaking of allBreaking) {
653
+ message += `\n- ${breaking.impact}`;
654
+ }
655
+ }
656
+
657
+ message += '\n\nGenerated-by: aios-developer meta-agent';
658
+
659
+ return {
660
+ message,
661
+ type,
662
+ stats,
663
+ breakingChanges: allBreaking
664
+ };
665
+ }
666
+
667
+ /**
668
+ * Suggest commit message improvements
669
+ * @param {string} message - Original commit message
670
+ * @returns {Object} Suggestions for improvement
671
+ */
672
+ suggestImprovements(message) {
673
+ const suggestions = [];
674
+ const lines = message.split('\n');
675
+ const header = lines[0];
676
+
677
+ // Check header format
678
+ const headerMatch = header.match(/^(\w+)(\([\w-]+\))?: (.+)$/);
679
+ if (!headerMatch) {
680
+ suggestions.push({
681
+ type: 'format',
682
+ issue: 'Header doesn\'t follow conventional format',
683
+ suggestion: 'Use format: type(_scope): subject'
684
+ });
685
+ } else {
686
+ const [, type, scope, subject] = headerMatch;
687
+
688
+ // Check type
689
+ if (!this.commitTypes[type]) {
690
+ suggestions.push({
691
+ type: 'type',
692
+ issue: `Unknown commit type: ${type}`,
693
+ suggestion: `Use one of: ${Object.keys(this.commitTypes).join(', ')}`
694
+ });
695
+ }
696
+
697
+ // Check subject length
698
+ if (subject.length > 50) {
699
+ suggestions.push({
700
+ type: 'length',
701
+ issue: 'Subject line too long',
702
+ suggestion: 'Keep subject under 50 characters'
703
+ });
704
+ }
705
+
706
+ // Check subject format
707
+ if (subject[0] === subject[0].toUpperCase()) {
708
+ suggestions.push({
709
+ type: 'case',
710
+ issue: 'Subject should not be capitalized',
711
+ suggestion: 'Use lowercase for subject'
712
+ });
713
+ }
714
+
715
+ if (subject.endsWith('.')) {
716
+ suggestions.push({
717
+ type: 'punctuation',
718
+ issue: 'Subject should not end with period',
719
+ suggestion: 'Remove trailing period'
720
+ });
721
+ }
722
+ }
723
+
724
+ // Check body
725
+ if (lines.length > 1) {
726
+ if (lines[1] !== '') {
727
+ suggestions.push({
728
+ type: 'spacing',
729
+ issue: 'Missing blank line after header',
730
+ suggestion: 'Add blank line between header and body'
731
+ });
732
+ }
733
+
734
+ // Check line length in body
735
+ for (let i = 2; i < lines.length; i++) {
736
+ if (lines[i].length > 72 && !lines[i].startsWith('BREAKING')) {
737
+ suggestions.push({
738
+ type: 'line-length',
739
+ issue: `Line ${i + 1} exceeds 72 characters`,
740
+ suggestion: 'Wrap body text at 72 characters'
741
+ });
742
+ }
743
+ }
744
+ }
745
+
746
+ return {
747
+ valid: suggestions.length === 0,
748
+ suggestions,
749
+ improvedMessage: this.applyImprovements(message, suggestions)
750
+ };
751
+ }
752
+
753
+ /**
754
+ * Apply improvements to commit message
755
+ * @private
756
+ */
757
+ applyImprovements(message, suggestions) {
758
+ let improved = message;
759
+
760
+ for (const suggestion of suggestions) {
761
+ switch (suggestion.type) {
762
+ case 'case':
763
+ improved = improved.replace(/^(\w+)(\([\w-]+\))?: (.)/, (match, type, scope, firstChar) =>
764
+ `${type}${scope || ''}: ${firstChar.toLowerCase()}`
765
+ );
766
+ break;
767
+ case 'punctuation':
768
+ improved = improved.replace(/^(.+)\.$/, '$1');
769
+ break;
770
+ case 'spacing':
771
+ const lines = improved.split('\n');
772
+ if (lines.length > 1 && lines[1] !== '') {
773
+ lines.splice(1, 0, '');
774
+ improved = lines.join('\n');
775
+ }
776
+ break;
777
+ }
778
+ }
779
+
780
+ return improved;
781
+ }
782
+
783
+ // Utility methods
784
+ parseAgentContent(content) {
785
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
786
+ if (!match) {
787
+ throw new Error('Invalid agent content format');
788
+ }
789
+ return {
790
+ yaml: match[1],
791
+ markdown: match[2]
792
+ };
793
+ }
794
+
795
+ extractSection(content, sectionHeader) {
796
+ const regex = new RegExp(`${sectionHeader}[\\s\\S]*?(?=\\n##|$)`, 'i');
797
+ const match = content.match(regex);
798
+ return match ? match[0] : '';
799
+ }
800
+
801
+ extractSectionName(diffLine) {
802
+ const match = diffLine.match(/@@ .* @@ (.+)/);
803
+ return match ? match[1] : 'unknown';
804
+ }
805
+
806
+ compareDependencies(original, modified) {
807
+ const changes = [];
808
+ const types = ['tasks', 'workflows', 'agents'];
809
+
810
+ for (const type of types) {
811
+ const originalDeps = original[type] || [];
812
+ const modifiedDeps = modified[type] || [];
813
+
814
+ const added = modifiedDeps.filter(d => !originalDeps.includes(d));
815
+ const removed = originalDeps.filter(d => !modifiedDeps.includes(d));
816
+
817
+ if (added.length > 0) {
818
+ changes.push({ type: `${type}_dependencies_added`, items: added });
819
+ }
820
+ if (removed.length > 0) {
821
+ changes.push({ type: `${type}_dependencies_removed`, items: removed });
822
+ }
823
+ }
824
+
825
+ return changes;
826
+ }
827
+
828
+ comparePhases(originalPhase, modifiedPhase) {
829
+ const details = [];
830
+
831
+ if (originalPhase.sequence !== modifiedPhase.sequence) {
832
+ details.push(`sequence ${originalPhase.sequence}→${modifiedPhase.sequence}`);
833
+ }
834
+
835
+ const originalAgents = originalPhase.agents || [];
836
+ const modifiedAgents = modifiedPhase.agents || [];
837
+
838
+ if (JSON.stringify(originalAgents) !== JSON.stringify(modifiedAgents)) {
839
+ details.push('agents changed');
840
+ }
841
+
842
+ if (JSON.stringify(originalPhase.artifacts) !== JSON.stringify(modifiedPhase.artifacts)) {
843
+ details.push('artifacts changed');
844
+ }
845
+
846
+ return details;
847
+ }
848
+ }
849
+
850
850
  module.exports = CommitMessageGenerator;