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,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;