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,352 +1,352 @@
|
|
|
1
|
-
const diffLib = require('diff');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
const yaml = require('js-yaml');
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Generates visual diffs for component modifications
|
|
7
|
-
*/
|
|
8
|
-
class DiffGenerator {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.colors = {
|
|
11
|
-
added: chalk.green,
|
|
12
|
-
removed: chalk.red,
|
|
13
|
-
unchanged: chalk.gray,
|
|
14
|
-
header: chalk.cyan,
|
|
15
|
-
lineNumber: chalk.yellow
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Generate a unified diff between two text contents
|
|
21
|
-
* @param {string} originalContent - Original file content
|
|
22
|
-
* @param {string} modifiedContent - Modified file content
|
|
23
|
-
* @param {string} fileName - Name of the file being diffed
|
|
24
|
-
* @param {Object} options - Diff options
|
|
25
|
-
* @returns {string} Formatted diff output
|
|
26
|
-
*/
|
|
27
|
-
generateUnifiedDiff(originalContent, modifiedContent, fileName, options = {}) {
|
|
28
|
-
const {
|
|
29
|
-
contextLines = 3,
|
|
30
|
-
showLineNumbers = true,
|
|
31
|
-
colorize = true
|
|
32
|
-
} = options;
|
|
33
|
-
|
|
34
|
-
const patch = diffLib.createPatch(
|
|
35
|
-
fileName,
|
|
36
|
-
originalContent,
|
|
37
|
-
modifiedContent,
|
|
38
|
-
'Current Version',
|
|
39
|
-
'Modified Version',
|
|
40
|
-
{ context: contextLines }
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
if (!colorize) {
|
|
44
|
-
return patch;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return this.colorizeDiff(patch, showLineNumbers);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Generate a diff specifically for YAML content
|
|
52
|
-
* @param {string} originalYaml - Original YAML content
|
|
53
|
-
* @param {string} modifiedYaml - Modified YAML content
|
|
54
|
-
* @param {string} componentName - Name of the component
|
|
55
|
-
* @returns {Object} Structured diff with sections
|
|
56
|
-
*/
|
|
57
|
-
generateYamlDiff(originalYaml, modifiedYaml, componentName) {
|
|
58
|
-
const original = yaml.load(originalYaml);
|
|
59
|
-
const modified = yaml.load(modifiedYaml);
|
|
60
|
-
|
|
61
|
-
const diff = {
|
|
62
|
-
component: componentName,
|
|
63
|
-
sections: {},
|
|
64
|
-
summary: {
|
|
65
|
-
added: [],
|
|
66
|
-
removed: [],
|
|
67
|
-
modified: []
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Compare top-level keys
|
|
72
|
-
const allKeys = new Set([...Object.keys(original), ...Object.keys(modified)]);
|
|
73
|
-
|
|
74
|
-
for (const key of allKeys) {
|
|
75
|
-
if (!original.hasOwnProperty(key)) {
|
|
76
|
-
diff.sections[key] = { status: 'added', value: modified[key] };
|
|
77
|
-
diff.summary.added.push(key);
|
|
78
|
-
} else if (!modified.hasOwnProperty(key)) {
|
|
79
|
-
diff.sections[key] = { status: 'removed', value: original[key] };
|
|
80
|
-
diff.summary.removed.push(key);
|
|
81
|
-
} else if (JSON.stringify(original[key]) !== JSON.stringify(modified[key])) {
|
|
82
|
-
diff.sections[key] = {
|
|
83
|
-
status: 'modified',
|
|
84
|
-
original: original[key],
|
|
85
|
-
modified: modified[key],
|
|
86
|
-
changes: this.compareValues(original[key], modified[key])
|
|
87
|
-
};
|
|
88
|
-
diff.summary.modified.push(key);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return diff;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Generate a structured diff for agents
|
|
97
|
-
* @param {string} originalContent - Original agent content
|
|
98
|
-
* @param {string} modifiedContent - Modified agent content
|
|
99
|
-
* @param {string} agentName - Name of the agent
|
|
100
|
-
* @returns {Object} Structured agent diff
|
|
101
|
-
*/
|
|
102
|
-
generateAgentDiff(originalContent, modifiedContent, agentName) {
|
|
103
|
-
// Split content into YAML and markdown sections
|
|
104
|
-
const originalParts = this.splitAgentContent(originalContent);
|
|
105
|
-
const modifiedParts = this.splitAgentContent(modifiedContent);
|
|
106
|
-
|
|
107
|
-
const yamlDiff = this.generateYamlDiff(
|
|
108
|
-
originalParts.yaml,
|
|
109
|
-
modifiedParts.yaml,
|
|
110
|
-
agentName
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
const markdownDiff = this.generateUnifiedDiff(
|
|
114
|
-
originalParts.markdown,
|
|
115
|
-
modifiedParts.markdown,
|
|
116
|
-
`${agentName}.md`,
|
|
117
|
-
{ contextLines: 5 }
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
return {
|
|
121
|
-
agent: agentName,
|
|
122
|
-
yamlChanges: yamlDiff,
|
|
123
|
-
markdownChanges: markdownDiff,
|
|
124
|
-
impactSummary: this.generateImpactSummary(yamlDiff)
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Generate a visual diff summary
|
|
130
|
-
* @param {Object} diff - Structured diff object
|
|
131
|
-
* @returns {string} Formatted summary
|
|
132
|
-
*/
|
|
133
|
-
generateDiffSummary(diff) {
|
|
134
|
-
const lines = [];
|
|
135
|
-
|
|
136
|
-
lines.push(this.colors.header('=== Modification Summary ==='));
|
|
137
|
-
lines.push('');
|
|
138
|
-
|
|
139
|
-
if (diff.summary) {
|
|
140
|
-
if (diff.summary.added.length > 0) {
|
|
141
|
-
lines.push(this.colors.added(`+ Added (${diff.summary.added.length}):`));
|
|
142
|
-
diff.summary.added.forEach(item => {
|
|
143
|
-
lines.push(this.colors.added(` + ${item}`));
|
|
144
|
-
});
|
|
145
|
-
lines.push('');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (diff.summary.modified.length > 0) {
|
|
149
|
-
lines.push(this.colors.header(`~ Modified (${diff.summary.modified.length}):`));
|
|
150
|
-
diff.summary.modified.forEach(item => {
|
|
151
|
-
lines.push(this.colors.header(` ~ ${item}`));
|
|
152
|
-
});
|
|
153
|
-
lines.push('');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (diff.summary.removed.length > 0) {
|
|
157
|
-
lines.push(this.colors.removed(`- Removed (${diff.summary.removed.length}):`));
|
|
158
|
-
diff.summary.removed.forEach(item => {
|
|
159
|
-
lines.push(this.colors.removed(` - ${item}`));
|
|
160
|
-
});
|
|
161
|
-
lines.push('');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return lines.join('\n');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Split agent content into YAML and markdown sections
|
|
170
|
-
* @private
|
|
171
|
-
*/
|
|
172
|
-
splitAgentContent(content) {
|
|
173
|
-
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
174
|
-
if (!match) {
|
|
175
|
-
throw new Error('Invalid agent content format');
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
yaml: match[1],
|
|
180
|
-
markdown: match[2]
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Compare two values and generate change details
|
|
186
|
-
* @private
|
|
187
|
-
*/
|
|
188
|
-
compareValues(original, modified) {
|
|
189
|
-
const changes = [];
|
|
190
|
-
|
|
191
|
-
if (Array.isArray(original) && Array.isArray(modified)) {
|
|
192
|
-
const added = modified.filter(item => !original.includes(item));
|
|
193
|
-
const removed = original.filter(item => !modified.includes(item));
|
|
194
|
-
|
|
195
|
-
if (added.length > 0) {
|
|
196
|
-
changes.push({ type: 'added', items: added });
|
|
197
|
-
}
|
|
198
|
-
if (removed.length > 0) {
|
|
199
|
-
changes.push({ type: 'removed', items: removed });
|
|
200
|
-
}
|
|
201
|
-
} else if (typeof original === 'object' && typeof modified === 'object') {
|
|
202
|
-
const allKeys = new Set([...Object.keys(original), ...Object.keys(modified)]);
|
|
203
|
-
|
|
204
|
-
for (const key of allKeys) {
|
|
205
|
-
if (!original.hasOwnProperty(key)) {
|
|
206
|
-
changes.push({ type: 'added', key, value: modified[key] });
|
|
207
|
-
} else if (!modified.hasOwnProperty(key)) {
|
|
208
|
-
changes.push({ type: 'removed', key, value: original[key] });
|
|
209
|
-
} else if (JSON.stringify(original[key]) !== JSON.stringify(modified[key])) {
|
|
210
|
-
changes.push({
|
|
211
|
-
type: 'modified',
|
|
212
|
-
key,
|
|
213
|
-
original: original[key],
|
|
214
|
-
modified: modified[key]
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
} else {
|
|
219
|
-
changes.push({
|
|
220
|
-
type: 'value_changed',
|
|
221
|
-
original,
|
|
222
|
-
modified
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return changes;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Colorize a diff patch
|
|
231
|
-
* @private
|
|
232
|
-
*/
|
|
233
|
-
colorizeDiff(patch, showLineNumbers) {
|
|
234
|
-
const lines = patch.split('\n');
|
|
235
|
-
const colorized = [];
|
|
236
|
-
let lineNumOriginal = 0;
|
|
237
|
-
let lineNumModified = 0;
|
|
238
|
-
|
|
239
|
-
for (const line of lines) {
|
|
240
|
-
if (line.startsWith('@@')) {
|
|
241
|
-
// Parse line numbers from hunk header
|
|
242
|
-
const match = line.match(/@@ -(\d+),\d+ \+(\d+),\d+ @@/);
|
|
243
|
-
if (match) {
|
|
244
|
-
lineNumOriginal = parseInt(match[1]);
|
|
245
|
-
lineNumModified = parseInt(match[2]);
|
|
246
|
-
}
|
|
247
|
-
colorized.push(this.colors.header(line));
|
|
248
|
-
} else if (line.startsWith('+')) {
|
|
249
|
-
const lineNum = showLineNumbers ? `${lineNumModified.toString().padStart(4)}: ` : '';
|
|
250
|
-
colorized.push(this.colors.added(`+${lineNum}${line.substring(1)}`));
|
|
251
|
-
lineNumModified++;
|
|
252
|
-
} else if (line.startsWith('-')) {
|
|
253
|
-
const lineNum = showLineNumbers ? `${lineNumOriginal.toString().padStart(4)}: ` : '';
|
|
254
|
-
colorized.push(this.colors.removed(`-${lineNum}${line.substring(1)}`));
|
|
255
|
-
lineNumOriginal++;
|
|
256
|
-
} else if (line.startsWith(' ')) {
|
|
257
|
-
const lineNum = showLineNumbers ?
|
|
258
|
-
`${lineNumOriginal.toString().padStart(4)}: ` : '';
|
|
259
|
-
colorized.push(this.colors.unchanged(` ${lineNum}${line.substring(1)}`));
|
|
260
|
-
lineNumOriginal++;
|
|
261
|
-
lineNumModified++;
|
|
262
|
-
} else {
|
|
263
|
-
colorized.push(this.colors.header(line));
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return colorized.join('\n');
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Generate impact summary from YAML diff
|
|
272
|
-
* @private
|
|
273
|
-
*/
|
|
274
|
-
generateImpactSummary(yamlDiff) {
|
|
275
|
-
const impacts = [];
|
|
276
|
-
|
|
277
|
-
// Check for dependency changes
|
|
278
|
-
if (yamlDiff.sections.dependencies) {
|
|
279
|
-
const changes = yamlDiff.sections.dependencies.changes || [];
|
|
280
|
-
for (const change of changes) {
|
|
281
|
-
if (change.type === 'added') {
|
|
282
|
-
impacts.push(`New dependency added: ${change.items.join(', ')}`);
|
|
283
|
-
} else if (change.type === 'removed') {
|
|
284
|
-
impacts.push(`Dependency removed: ${change.items.join(', ')}`);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Check for command changes
|
|
290
|
-
if (yamlDiff.sections.commands) {
|
|
291
|
-
impacts.push('Commands modified - users may need to update their workflows');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Check for persona changes
|
|
295
|
-
if (yamlDiff.sections.persona) {
|
|
296
|
-
impacts.push('Agent persona modified - behavior may change');
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return impacts;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Generate a side-by-side diff view
|
|
304
|
-
* @param {string} original - Original content
|
|
305
|
-
* @param {string} modified - Modified content
|
|
306
|
-
* @param {Object} options - Display options
|
|
307
|
-
* @returns {string} Side-by-side diff
|
|
308
|
-
*/
|
|
309
|
-
generateSideBySideDiff(original, modified, options = {}) {
|
|
310
|
-
const { width = 80, gutter = 3 } = options;
|
|
311
|
-
const columnWidth = Math.floor((width - gutter) / 2);
|
|
312
|
-
|
|
313
|
-
const originalLines = original.split('\n');
|
|
314
|
-
const modifiedLines = modified.split('\n');
|
|
315
|
-
const maxLines = Math.max(originalLines.length, modifiedLines.length);
|
|
316
|
-
|
|
317
|
-
const output = [];
|
|
318
|
-
output.push(this.colors.header('─'.repeat(width)));
|
|
319
|
-
output.push(
|
|
320
|
-
this.colors.header('Original'.padEnd(columnWidth)) +
|
|
321
|
-
' '.repeat(gutter) +
|
|
322
|
-
this.colors.header('Modified'.padEnd(columnWidth))
|
|
323
|
-
);
|
|
324
|
-
output.push(this.colors.header('─'.repeat(width)));
|
|
325
|
-
|
|
326
|
-
for (let i = 0; i < maxLines; i++) {
|
|
327
|
-
const origLine = (originalLines[i] || '').substring(0, columnWidth);
|
|
328
|
-
const modLine = (modifiedLines[i] || '').substring(0, columnWidth);
|
|
329
|
-
|
|
330
|
-
let coloredOrig = origLine.padEnd(columnWidth);
|
|
331
|
-
let coloredMod = modLine.padEnd(columnWidth);
|
|
332
|
-
|
|
333
|
-
if (origLine !== modLine) {
|
|
334
|
-
if (!originalLines[i]) {
|
|
335
|
-
coloredMod = this.colors.added(coloredMod);
|
|
336
|
-
} else if (!modifiedLines[i]) {
|
|
337
|
-
coloredOrig = this.colors.removed(coloredOrig);
|
|
338
|
-
} else {
|
|
339
|
-
coloredOrig = this.colors.removed(coloredOrig);
|
|
340
|
-
coloredMod = this.colors.added(coloredMod);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
output.push(coloredOrig + ' '.repeat(gutter) + coloredMod);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
output.push(this.colors.header('─'.repeat(width)));
|
|
348
|
-
return output.join('\n');
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
1
|
+
const diffLib = require('diff');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates visual diffs for component modifications
|
|
7
|
+
*/
|
|
8
|
+
class DiffGenerator {
|
|
9
|
+
constructor() {
|
|
10
|
+
this.colors = {
|
|
11
|
+
added: chalk.green,
|
|
12
|
+
removed: chalk.red,
|
|
13
|
+
unchanged: chalk.gray,
|
|
14
|
+
header: chalk.cyan,
|
|
15
|
+
lineNumber: chalk.yellow
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a unified diff between two text contents
|
|
21
|
+
* @param {string} originalContent - Original file content
|
|
22
|
+
* @param {string} modifiedContent - Modified file content
|
|
23
|
+
* @param {string} fileName - Name of the file being diffed
|
|
24
|
+
* @param {Object} options - Diff options
|
|
25
|
+
* @returns {string} Formatted diff output
|
|
26
|
+
*/
|
|
27
|
+
generateUnifiedDiff(originalContent, modifiedContent, fileName, options = {}) {
|
|
28
|
+
const {
|
|
29
|
+
contextLines = 3,
|
|
30
|
+
showLineNumbers = true,
|
|
31
|
+
colorize = true
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
const patch = diffLib.createPatch(
|
|
35
|
+
fileName,
|
|
36
|
+
originalContent,
|
|
37
|
+
modifiedContent,
|
|
38
|
+
'Current Version',
|
|
39
|
+
'Modified Version',
|
|
40
|
+
{ context: contextLines }
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (!colorize) {
|
|
44
|
+
return patch;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return this.colorizeDiff(patch, showLineNumbers);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Generate a diff specifically for YAML content
|
|
52
|
+
* @param {string} originalYaml - Original YAML content
|
|
53
|
+
* @param {string} modifiedYaml - Modified YAML content
|
|
54
|
+
* @param {string} componentName - Name of the component
|
|
55
|
+
* @returns {Object} Structured diff with sections
|
|
56
|
+
*/
|
|
57
|
+
generateYamlDiff(originalYaml, modifiedYaml, componentName) {
|
|
58
|
+
const original = yaml.load(originalYaml);
|
|
59
|
+
const modified = yaml.load(modifiedYaml);
|
|
60
|
+
|
|
61
|
+
const diff = {
|
|
62
|
+
component: componentName,
|
|
63
|
+
sections: {},
|
|
64
|
+
summary: {
|
|
65
|
+
added: [],
|
|
66
|
+
removed: [],
|
|
67
|
+
modified: []
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Compare top-level keys
|
|
72
|
+
const allKeys = new Set([...Object.keys(original), ...Object.keys(modified)]);
|
|
73
|
+
|
|
74
|
+
for (const key of allKeys) {
|
|
75
|
+
if (!original.hasOwnProperty(key)) {
|
|
76
|
+
diff.sections[key] = { status: 'added', value: modified[key] };
|
|
77
|
+
diff.summary.added.push(key);
|
|
78
|
+
} else if (!modified.hasOwnProperty(key)) {
|
|
79
|
+
diff.sections[key] = { status: 'removed', value: original[key] };
|
|
80
|
+
diff.summary.removed.push(key);
|
|
81
|
+
} else if (JSON.stringify(original[key]) !== JSON.stringify(modified[key])) {
|
|
82
|
+
diff.sections[key] = {
|
|
83
|
+
status: 'modified',
|
|
84
|
+
original: original[key],
|
|
85
|
+
modified: modified[key],
|
|
86
|
+
changes: this.compareValues(original[key], modified[key])
|
|
87
|
+
};
|
|
88
|
+
diff.summary.modified.push(key);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return diff;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Generate a structured diff for agents
|
|
97
|
+
* @param {string} originalContent - Original agent content
|
|
98
|
+
* @param {string} modifiedContent - Modified agent content
|
|
99
|
+
* @param {string} agentName - Name of the agent
|
|
100
|
+
* @returns {Object} Structured agent diff
|
|
101
|
+
*/
|
|
102
|
+
generateAgentDiff(originalContent, modifiedContent, agentName) {
|
|
103
|
+
// Split content into YAML and markdown sections
|
|
104
|
+
const originalParts = this.splitAgentContent(originalContent);
|
|
105
|
+
const modifiedParts = this.splitAgentContent(modifiedContent);
|
|
106
|
+
|
|
107
|
+
const yamlDiff = this.generateYamlDiff(
|
|
108
|
+
originalParts.yaml,
|
|
109
|
+
modifiedParts.yaml,
|
|
110
|
+
agentName
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const markdownDiff = this.generateUnifiedDiff(
|
|
114
|
+
originalParts.markdown,
|
|
115
|
+
modifiedParts.markdown,
|
|
116
|
+
`${agentName}.md`,
|
|
117
|
+
{ contextLines: 5 }
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
agent: agentName,
|
|
122
|
+
yamlChanges: yamlDiff,
|
|
123
|
+
markdownChanges: markdownDiff,
|
|
124
|
+
impactSummary: this.generateImpactSummary(yamlDiff)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Generate a visual diff summary
|
|
130
|
+
* @param {Object} diff - Structured diff object
|
|
131
|
+
* @returns {string} Formatted summary
|
|
132
|
+
*/
|
|
133
|
+
generateDiffSummary(diff) {
|
|
134
|
+
const lines = [];
|
|
135
|
+
|
|
136
|
+
lines.push(this.colors.header('=== Modification Summary ==='));
|
|
137
|
+
lines.push('');
|
|
138
|
+
|
|
139
|
+
if (diff.summary) {
|
|
140
|
+
if (diff.summary.added.length > 0) {
|
|
141
|
+
lines.push(this.colors.added(`+ Added (${diff.summary.added.length}):`));
|
|
142
|
+
diff.summary.added.forEach(item => {
|
|
143
|
+
lines.push(this.colors.added(` + ${item}`));
|
|
144
|
+
});
|
|
145
|
+
lines.push('');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (diff.summary.modified.length > 0) {
|
|
149
|
+
lines.push(this.colors.header(`~ Modified (${diff.summary.modified.length}):`));
|
|
150
|
+
diff.summary.modified.forEach(item => {
|
|
151
|
+
lines.push(this.colors.header(` ~ ${item}`));
|
|
152
|
+
});
|
|
153
|
+
lines.push('');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (diff.summary.removed.length > 0) {
|
|
157
|
+
lines.push(this.colors.removed(`- Removed (${diff.summary.removed.length}):`));
|
|
158
|
+
diff.summary.removed.forEach(item => {
|
|
159
|
+
lines.push(this.colors.removed(` - ${item}`));
|
|
160
|
+
});
|
|
161
|
+
lines.push('');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return lines.join('\n');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Split agent content into YAML and markdown sections
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
splitAgentContent(content) {
|
|
173
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
174
|
+
if (!match) {
|
|
175
|
+
throw new Error('Invalid agent content format');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
yaml: match[1],
|
|
180
|
+
markdown: match[2]
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Compare two values and generate change details
|
|
186
|
+
* @private
|
|
187
|
+
*/
|
|
188
|
+
compareValues(original, modified) {
|
|
189
|
+
const changes = [];
|
|
190
|
+
|
|
191
|
+
if (Array.isArray(original) && Array.isArray(modified)) {
|
|
192
|
+
const added = modified.filter(item => !original.includes(item));
|
|
193
|
+
const removed = original.filter(item => !modified.includes(item));
|
|
194
|
+
|
|
195
|
+
if (added.length > 0) {
|
|
196
|
+
changes.push({ type: 'added', items: added });
|
|
197
|
+
}
|
|
198
|
+
if (removed.length > 0) {
|
|
199
|
+
changes.push({ type: 'removed', items: removed });
|
|
200
|
+
}
|
|
201
|
+
} else if (typeof original === 'object' && typeof modified === 'object') {
|
|
202
|
+
const allKeys = new Set([...Object.keys(original), ...Object.keys(modified)]);
|
|
203
|
+
|
|
204
|
+
for (const key of allKeys) {
|
|
205
|
+
if (!original.hasOwnProperty(key)) {
|
|
206
|
+
changes.push({ type: 'added', key, value: modified[key] });
|
|
207
|
+
} else if (!modified.hasOwnProperty(key)) {
|
|
208
|
+
changes.push({ type: 'removed', key, value: original[key] });
|
|
209
|
+
} else if (JSON.stringify(original[key]) !== JSON.stringify(modified[key])) {
|
|
210
|
+
changes.push({
|
|
211
|
+
type: 'modified',
|
|
212
|
+
key,
|
|
213
|
+
original: original[key],
|
|
214
|
+
modified: modified[key]
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
changes.push({
|
|
220
|
+
type: 'value_changed',
|
|
221
|
+
original,
|
|
222
|
+
modified
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return changes;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Colorize a diff patch
|
|
231
|
+
* @private
|
|
232
|
+
*/
|
|
233
|
+
colorizeDiff(patch, showLineNumbers) {
|
|
234
|
+
const lines = patch.split('\n');
|
|
235
|
+
const colorized = [];
|
|
236
|
+
let lineNumOriginal = 0;
|
|
237
|
+
let lineNumModified = 0;
|
|
238
|
+
|
|
239
|
+
for (const line of lines) {
|
|
240
|
+
if (line.startsWith('@@')) {
|
|
241
|
+
// Parse line numbers from hunk header
|
|
242
|
+
const match = line.match(/@@ -(\d+),\d+ \+(\d+),\d+ @@/);
|
|
243
|
+
if (match) {
|
|
244
|
+
lineNumOriginal = parseInt(match[1]);
|
|
245
|
+
lineNumModified = parseInt(match[2]);
|
|
246
|
+
}
|
|
247
|
+
colorized.push(this.colors.header(line));
|
|
248
|
+
} else if (line.startsWith('+')) {
|
|
249
|
+
const lineNum = showLineNumbers ? `${lineNumModified.toString().padStart(4)}: ` : '';
|
|
250
|
+
colorized.push(this.colors.added(`+${lineNum}${line.substring(1)}`));
|
|
251
|
+
lineNumModified++;
|
|
252
|
+
} else if (line.startsWith('-')) {
|
|
253
|
+
const lineNum = showLineNumbers ? `${lineNumOriginal.toString().padStart(4)}: ` : '';
|
|
254
|
+
colorized.push(this.colors.removed(`-${lineNum}${line.substring(1)}`));
|
|
255
|
+
lineNumOriginal++;
|
|
256
|
+
} else if (line.startsWith(' ')) {
|
|
257
|
+
const lineNum = showLineNumbers ?
|
|
258
|
+
`${lineNumOriginal.toString().padStart(4)}: ` : '';
|
|
259
|
+
colorized.push(this.colors.unchanged(` ${lineNum}${line.substring(1)}`));
|
|
260
|
+
lineNumOriginal++;
|
|
261
|
+
lineNumModified++;
|
|
262
|
+
} else {
|
|
263
|
+
colorized.push(this.colors.header(line));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return colorized.join('\n');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Generate impact summary from YAML diff
|
|
272
|
+
* @private
|
|
273
|
+
*/
|
|
274
|
+
generateImpactSummary(yamlDiff) {
|
|
275
|
+
const impacts = [];
|
|
276
|
+
|
|
277
|
+
// Check for dependency changes
|
|
278
|
+
if (yamlDiff.sections.dependencies) {
|
|
279
|
+
const changes = yamlDiff.sections.dependencies.changes || [];
|
|
280
|
+
for (const change of changes) {
|
|
281
|
+
if (change.type === 'added') {
|
|
282
|
+
impacts.push(`New dependency added: ${change.items.join(', ')}`);
|
|
283
|
+
} else if (change.type === 'removed') {
|
|
284
|
+
impacts.push(`Dependency removed: ${change.items.join(', ')}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Check for command changes
|
|
290
|
+
if (yamlDiff.sections.commands) {
|
|
291
|
+
impacts.push('Commands modified - users may need to update their workflows');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Check for persona changes
|
|
295
|
+
if (yamlDiff.sections.persona) {
|
|
296
|
+
impacts.push('Agent persona modified - behavior may change');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return impacts;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Generate a side-by-side diff view
|
|
304
|
+
* @param {string} original - Original content
|
|
305
|
+
* @param {string} modified - Modified content
|
|
306
|
+
* @param {Object} options - Display options
|
|
307
|
+
* @returns {string} Side-by-side diff
|
|
308
|
+
*/
|
|
309
|
+
generateSideBySideDiff(original, modified, options = {}) {
|
|
310
|
+
const { width = 80, gutter = 3 } = options;
|
|
311
|
+
const columnWidth = Math.floor((width - gutter) / 2);
|
|
312
|
+
|
|
313
|
+
const originalLines = original.split('\n');
|
|
314
|
+
const modifiedLines = modified.split('\n');
|
|
315
|
+
const maxLines = Math.max(originalLines.length, modifiedLines.length);
|
|
316
|
+
|
|
317
|
+
const output = [];
|
|
318
|
+
output.push(this.colors.header('─'.repeat(width)));
|
|
319
|
+
output.push(
|
|
320
|
+
this.colors.header('Original'.padEnd(columnWidth)) +
|
|
321
|
+
' '.repeat(gutter) +
|
|
322
|
+
this.colors.header('Modified'.padEnd(columnWidth))
|
|
323
|
+
);
|
|
324
|
+
output.push(this.colors.header('─'.repeat(width)));
|
|
325
|
+
|
|
326
|
+
for (let i = 0; i < maxLines; i++) {
|
|
327
|
+
const origLine = (originalLines[i] || '').substring(0, columnWidth);
|
|
328
|
+
const modLine = (modifiedLines[i] || '').substring(0, columnWidth);
|
|
329
|
+
|
|
330
|
+
let coloredOrig = origLine.padEnd(columnWidth);
|
|
331
|
+
let coloredMod = modLine.padEnd(columnWidth);
|
|
332
|
+
|
|
333
|
+
if (origLine !== modLine) {
|
|
334
|
+
if (!originalLines[i]) {
|
|
335
|
+
coloredMod = this.colors.added(coloredMod);
|
|
336
|
+
} else if (!modifiedLines[i]) {
|
|
337
|
+
coloredOrig = this.colors.removed(coloredOrig);
|
|
338
|
+
} else {
|
|
339
|
+
coloredOrig = this.colors.removed(coloredOrig);
|
|
340
|
+
coloredMod = this.colors.added(coloredMod);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
output.push(coloredOrig + ' '.repeat(gutter) + coloredMod);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
output.push(this.colors.header('─'.repeat(width)));
|
|
348
|
+
return output.join('\n');
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
352
|
module.exports = DiffGenerator;
|