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.
- package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/pro/README.md +0 -66
- package/pro/license/degradation.js +0 -220
- package/pro/license/errors.js +0 -450
- package/pro/license/feature-gate.js +0 -354
- package/pro/license/index.js +0 -181
- package/pro/license/license-api.js +0 -651
- package/pro/license/license-cache.js +0 -523
- package/pro/license/license-crypto.js +0 -303
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /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;
|