@mytechtoday/augment-extensions 1.2.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/visual-design/CHANGELOG.md +132 -0
- package/augment-extensions/visual-design/README.md +255 -0
- package/augment-extensions/visual-design/__tests__/README.md +119 -0
- package/augment-extensions/visual-design/__tests__/style-selector.test.ts +172 -0
- package/augment-extensions/visual-design/__tests__/vendor-styles.test.ts +214 -0
- package/augment-extensions/visual-design/domains/other/ai-prompt-helper.ts +157 -0
- package/augment-extensions/visual-design/domains/other/dotnet-application.ts +156 -0
- package/augment-extensions/visual-design/domains/other/linux-platform.ts +156 -0
- package/augment-extensions/visual-design/domains/other/mobile-application.ts +157 -0
- package/augment-extensions/visual-design/domains/other/motion-picture.ts +156 -0
- package/augment-extensions/visual-design/domains/other/os-application.ts +156 -0
- package/augment-extensions/visual-design/domains/other/print-campaigns.ts +158 -0
- package/augment-extensions/visual-design/domains/other/web-app.ts +157 -0
- package/augment-extensions/visual-design/domains/other/website.ts +161 -0
- package/augment-extensions/visual-design/domains/other/windows-platform.ts +156 -0
- package/augment-extensions/visual-design/domains/web-page-styles/amazon-cloudscape.ts +506 -0
- package/augment-extensions/visual-design/domains/web-page-styles/google-modern.ts +615 -0
- package/augment-extensions/visual-design/domains/web-page-styles/microsoft-fluent.ts +531 -0
- package/augment-extensions/visual-design/examples/README.md +97 -0
- package/augment-extensions/visual-design/examples/ai-prompt-generation.md +233 -0
- package/augment-extensions/visual-design/examples/basic-usage.md +216 -0
- package/augment-extensions/visual-design/examples/domain-workflows.md +257 -0
- package/augment-extensions/visual-design/examples/vendor-comparison.md +247 -0
- package/augment-extensions/visual-design/module.json +78 -0
- package/augment-extensions/visual-design/style-selector.ts +177 -0
- package/augment-extensions/visual-design/types.ts +302 -0
- package/augment-extensions/visual-design/visual-design-core.ts +469 -0
- package/augment-extensions/workflows/adr-support/README.md +227 -0
- package/augment-extensions/workflows/adr-support/__tests__/adr-validator.test.ts +203 -0
- package/augment-extensions/workflows/adr-support/adr-validator.ts +162 -0
- package/augment-extensions/workflows/adr-support/examples/complete-lifecycle-example.md +449 -0
- package/augment-extensions/workflows/adr-support/examples/integration-example.md +580 -0
- package/augment-extensions/workflows/adr-support/examples/superseding-example.md +436 -0
- package/augment-extensions/workflows/adr-support/module.json +112 -0
- package/augment-extensions/workflows/adr-support/rules/adr-creation.md +372 -0
- package/augment-extensions/workflows/adr-support/rules/beads-integration.md +443 -0
- package/augment-extensions/workflows/adr-support/rules/conflict-detection.md +486 -0
- package/augment-extensions/workflows/adr-support/rules/decision-detection.md +362 -0
- package/augment-extensions/workflows/adr-support/rules/lifecycle-management.md +427 -0
- package/augment-extensions/workflows/adr-support/rules/openspec-integration.md +465 -0
- package/augment-extensions/workflows/adr-support/rules/template-selection.md +405 -0
- package/augment-extensions/workflows/adr-support/rules/validation-rules.md +543 -0
- package/augment-extensions/workflows/adr-support/schemas/adr-config.json +191 -0
- package/augment-extensions/workflows/adr-support/schemas/adr-metadata.json +172 -0
- package/augment-extensions/workflows/adr-support/templates/business-case.md +235 -0
- package/augment-extensions/workflows/adr-support/templates/madr-elaborate.md +197 -0
- package/augment-extensions/workflows/adr-support/templates/madr-simple.md +68 -0
- package/augment-extensions/workflows/adr-support/templates/nygard.md +84 -0
- package/cli/dist/utils/__tests__/adr-validator.example.d.ts +6 -0
- package/cli/dist/utils/__tests__/adr-validator.example.d.ts.map +1 -0
- package/cli/dist/utils/__tests__/adr-validator.example.js +148 -0
- package/cli/dist/utils/__tests__/adr-validator.example.js.map +1 -0
- package/cli/dist/utils/adr-validator.d.ts +65 -0
- package/cli/dist/utils/adr-validator.d.ts.map +1 -0
- package/cli/dist/utils/adr-validator.js +203 -0
- package/cli/dist/utils/adr-validator.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for ADR Validation Module
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from '@jest/globals';
|
|
6
|
+
import {
|
|
7
|
+
ADR,
|
|
8
|
+
isValidISO8601,
|
|
9
|
+
validateMetadata,
|
|
10
|
+
validateOptionalFields,
|
|
11
|
+
validateADRReferences
|
|
12
|
+
} from '../adr-validator';
|
|
13
|
+
|
|
14
|
+
describe('ADR Validator', () => {
|
|
15
|
+
describe('isValidISO8601', () => {
|
|
16
|
+
it('should accept valid ISO 8601 dates', () => {
|
|
17
|
+
expect(isValidISO8601('2026-02-06')).toBe(true);
|
|
18
|
+
expect(isValidISO8601('2024-01-01')).toBe(true);
|
|
19
|
+
expect(isValidISO8601('2024-12-31')).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should reject invalid dates', () => {
|
|
23
|
+
expect(isValidISO8601('2024-02-30')).toBe(false); // Invalid day
|
|
24
|
+
expect(isValidISO8601('2024-13-01')).toBe(false); // Invalid month
|
|
25
|
+
expect(isValidISO8601('2024-00-01')).toBe(false); // Invalid month
|
|
26
|
+
expect(isValidISO8601('2024-01-32')).toBe(false); // Invalid day
|
|
27
|
+
expect(isValidISO8601('24-01-01')).toBe(false); // Wrong format
|
|
28
|
+
expect(isValidISO8601('2024/01/01')).toBe(false); // Wrong separator
|
|
29
|
+
expect(isValidISO8601('2024-1-1')).toBe(false); // Missing leading zeros
|
|
30
|
+
expect(isValidISO8601('not-a-date')).toBe(false);
|
|
31
|
+
expect(isValidISO8601('')).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('validateMetadata', () => {
|
|
36
|
+
it('should accept valid ADR metadata', () => {
|
|
37
|
+
const validADR: ADR = {
|
|
38
|
+
id: 'adr-0001',
|
|
39
|
+
title: 'Use PostgreSQL for Primary Database',
|
|
40
|
+
status: 'approved',
|
|
41
|
+
date: '2026-02-06',
|
|
42
|
+
deciders: ['Tech Lead', 'Database Architect']
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const result = validateMetadata(validADR);
|
|
46
|
+
expect(result.valid).toBe(true);
|
|
47
|
+
expect(result.errors).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should reject missing required fields', () => {
|
|
51
|
+
const invalidADR = {
|
|
52
|
+
id: 'adr-0001',
|
|
53
|
+
title: 'Test',
|
|
54
|
+
status: 'approved'
|
|
55
|
+
} as ADR;
|
|
56
|
+
|
|
57
|
+
const result = validateMetadata(invalidADR);
|
|
58
|
+
expect(result.valid).toBe(false);
|
|
59
|
+
expect(result.errors).toContain('Missing required field: date');
|
|
60
|
+
expect(result.errors).toContain('At least one decider must be specified');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should reject invalid ID format', () => {
|
|
64
|
+
const invalidADR: ADR = {
|
|
65
|
+
id: 'adr-1', // Should be adr-0001
|
|
66
|
+
title: 'Valid Title Here',
|
|
67
|
+
status: 'approved',
|
|
68
|
+
date: '2026-02-06',
|
|
69
|
+
deciders: ['Tech Lead']
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = validateMetadata(invalidADR);
|
|
73
|
+
expect(result.valid).toBe(false);
|
|
74
|
+
expect(result.errors).toContain('Invalid ID format. Must be adr-NNNN (e.g., adr-0001)');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should reject invalid title length', () => {
|
|
78
|
+
const shortTitle: ADR = {
|
|
79
|
+
id: 'adr-0001',
|
|
80
|
+
title: 'Short', // Less than 10 characters
|
|
81
|
+
status: 'approved',
|
|
82
|
+
date: '2026-02-06',
|
|
83
|
+
deciders: ['Tech Lead']
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const result = validateMetadata(shortTitle);
|
|
87
|
+
expect(result.valid).toBe(false);
|
|
88
|
+
expect(result.errors).toContain('Title must be 10-100 characters');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should reject invalid status', () => {
|
|
92
|
+
const invalidStatus: ADR = {
|
|
93
|
+
id: 'adr-0001',
|
|
94
|
+
title: 'Valid Title Here',
|
|
95
|
+
status: 'invalid-status',
|
|
96
|
+
date: '2026-02-06',
|
|
97
|
+
deciders: ['Tech Lead']
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const result = validateMetadata(invalidStatus);
|
|
101
|
+
expect(result.valid).toBe(false);
|
|
102
|
+
expect(result.errors?.some(e => e.includes('Invalid status'))).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should reject invalid date format', () => {
|
|
106
|
+
const invalidDate: ADR = {
|
|
107
|
+
id: 'adr-0001',
|
|
108
|
+
title: 'Valid Title Here',
|
|
109
|
+
status: 'approved',
|
|
110
|
+
date: '2024-02-30', // Invalid date
|
|
111
|
+
deciders: ['Tech Lead']
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const result = validateMetadata(invalidDate);
|
|
115
|
+
expect(result.valid).toBe(false);
|
|
116
|
+
expect(result.errors).toContain('Invalid date format. Must be ISO 8601 (YYYY-MM-DD)');
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('validateOptionalFields', () => {
|
|
121
|
+
it('should accept valid optional fields', () => {
|
|
122
|
+
const adr: ADR = {
|
|
123
|
+
id: 'adr-0001',
|
|
124
|
+
title: 'Valid Title Here',
|
|
125
|
+
status: 'approved',
|
|
126
|
+
date: '2026-02-06',
|
|
127
|
+
deciders: ['Tech Lead'],
|
|
128
|
+
tags: ['database', 'postgresql'],
|
|
129
|
+
supersedes: ['adr-0002'],
|
|
130
|
+
superseded_by: 'adr-0003'
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const result = validateOptionalFields(adr);
|
|
134
|
+
expect(result.valid).toBe(true);
|
|
135
|
+
expect(result.warnings).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should warn about invalid supersedes format', () => {
|
|
139
|
+
const adr: ADR = {
|
|
140
|
+
id: 'adr-0001',
|
|
141
|
+
title: 'Valid Title Here',
|
|
142
|
+
status: 'approved',
|
|
143
|
+
date: '2026-02-06',
|
|
144
|
+
deciders: ['Tech Lead'],
|
|
145
|
+
supersedes: ['adr-1', 'invalid-id'] // Invalid formats
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const result = validateOptionalFields(adr);
|
|
149
|
+
expect(result.valid).toBe(false);
|
|
150
|
+
expect(result.warnings?.some(w => w.includes('Invalid ADR ID in supersedes'))).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe('validateADRReferences', () => {
|
|
155
|
+
const existingADRs: ADR[] = [
|
|
156
|
+
{
|
|
157
|
+
id: 'adr-0001',
|
|
158
|
+
title: 'First Decision',
|
|
159
|
+
status: 'approved',
|
|
160
|
+
date: '2026-01-01',
|
|
161
|
+
deciders: ['Tech Lead']
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: 'adr-0002',
|
|
165
|
+
title: 'Second Decision',
|
|
166
|
+
status: 'approved',
|
|
167
|
+
date: '2026-01-02',
|
|
168
|
+
deciders: ['Tech Lead']
|
|
169
|
+
}
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
it('should warn when related_decisions references non-existent ADR', () => {
|
|
173
|
+
const adr: ADR = {
|
|
174
|
+
id: 'adr-0003',
|
|
175
|
+
title: 'Third Decision',
|
|
176
|
+
status: 'approved',
|
|
177
|
+
date: '2026-01-03',
|
|
178
|
+
deciders: ['Tech Lead'],
|
|
179
|
+
related_decisions: ['adr-0001', 'adr-9999'] // adr-9999 doesn't exist
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const result = validateADRReferences(adr, existingADRs);
|
|
183
|
+
expect(result.valid).toBe(false);
|
|
184
|
+
expect(result.warnings).toContain('Related ADR does not exist: adr-9999');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should pass when all references exist', () => {
|
|
188
|
+
const adr: ADR = {
|
|
189
|
+
id: 'adr-0003',
|
|
190
|
+
title: 'Third Decision',
|
|
191
|
+
status: 'approved',
|
|
192
|
+
date: '2026-01-03',
|
|
193
|
+
deciders: ['Tech Lead'],
|
|
194
|
+
related_decisions: ['adr-0001', 'adr-0002']
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const result = validateADRReferences(adr, existingADRs);
|
|
198
|
+
expect(result.valid).toBe(true);
|
|
199
|
+
expect(result.warnings).toEqual([]);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR Validation Module
|
|
3
|
+
* Implements validation rules from validation-rules.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ADR {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
status: string;
|
|
10
|
+
date: string;
|
|
11
|
+
deciders: string[];
|
|
12
|
+
tags?: string[];
|
|
13
|
+
supersedes?: string[];
|
|
14
|
+
superseded_by?: string;
|
|
15
|
+
related_decisions?: string[];
|
|
16
|
+
related_specs?: string[];
|
|
17
|
+
related_tasks?: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ValidationResult {
|
|
21
|
+
valid: boolean;
|
|
22
|
+
errors?: string[];
|
|
23
|
+
warnings?: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const VALID_STATUSES = ['draft', 'proposed', 'approved', 'implemented', 'maintained', 'superseded', 'sunset'];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate ISO 8601 date format (YYYY-MM-DD)
|
|
30
|
+
*/
|
|
31
|
+
export function isValidISO8601(date: string): boolean {
|
|
32
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [year, month, day] = date.split('-').map(Number);
|
|
37
|
+
|
|
38
|
+
// Check basic ranges
|
|
39
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Create date in UTC to avoid timezone issues
|
|
44
|
+
const d = new Date(Date.UTC(year, month - 1, day));
|
|
45
|
+
|
|
46
|
+
if (isNaN(d.getTime())) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Verify the date components match (handles invalid dates like 2024-02-30)
|
|
51
|
+
return d.getUTCFullYear() === year &&
|
|
52
|
+
d.getUTCMonth() === month - 1 &&
|
|
53
|
+
d.getUTCDate() === day;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Validate required metadata fields
|
|
58
|
+
*/
|
|
59
|
+
export function validateMetadata(adr: ADR): ValidationResult {
|
|
60
|
+
const errors: string[] = [];
|
|
61
|
+
|
|
62
|
+
// ID validation
|
|
63
|
+
if (!adr.id) {
|
|
64
|
+
errors.push("Missing required field: id");
|
|
65
|
+
} else if (!/^adr-\d{4}$/.test(adr.id)) {
|
|
66
|
+
errors.push("Invalid ID format. Must be adr-NNNN (e.g., adr-0001)");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Title validation
|
|
70
|
+
if (!adr.title) {
|
|
71
|
+
errors.push("Missing required field: title");
|
|
72
|
+
} else if (adr.title.length < 10 || adr.title.length > 100) {
|
|
73
|
+
errors.push("Title must be 10-100 characters");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Status validation
|
|
77
|
+
if (!adr.status) {
|
|
78
|
+
errors.push("Missing required field: status");
|
|
79
|
+
} else if (!VALID_STATUSES.includes(adr.status)) {
|
|
80
|
+
errors.push(`Invalid status. Must be one of: ${VALID_STATUSES.join(', ')}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Date validation
|
|
84
|
+
if (!adr.date) {
|
|
85
|
+
errors.push("Missing required field: date");
|
|
86
|
+
} else if (!isValidISO8601(adr.date)) {
|
|
87
|
+
errors.push("Invalid date format. Must be ISO 8601 (YYYY-MM-DD)");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Deciders validation
|
|
91
|
+
if (!adr.deciders || adr.deciders.length === 0) {
|
|
92
|
+
errors.push("At least one decider must be specified");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { valid: errors.length === 0, errors };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Validate optional fields
|
|
100
|
+
*/
|
|
101
|
+
export function validateOptionalFields(adr: ADR): ValidationResult {
|
|
102
|
+
const warnings: string[] = [];
|
|
103
|
+
|
|
104
|
+
// Tags validation
|
|
105
|
+
if (adr.tags && !Array.isArray(adr.tags)) {
|
|
106
|
+
warnings.push("Tags must be an array");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Supersedes validation
|
|
110
|
+
if (adr.supersedes) {
|
|
111
|
+
if (!Array.isArray(adr.supersedes)) {
|
|
112
|
+
warnings.push("Supersedes must be an array");
|
|
113
|
+
} else {
|
|
114
|
+
adr.supersedes.forEach(id => {
|
|
115
|
+
if (!/^adr-\d{4}$/.test(id)) {
|
|
116
|
+
warnings.push(`Invalid ADR ID in supersedes: ${id}`);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Superseded_by validation
|
|
123
|
+
if (adr.superseded_by && !/^adr-\d{4}$/.test(adr.superseded_by)) {
|
|
124
|
+
warnings.push(`Invalid ADR ID in superseded_by: ${adr.superseded_by}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return { valid: warnings.length === 0, warnings };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Validate ADR references (related_decisions, supersedes, etc.)
|
|
132
|
+
*/
|
|
133
|
+
export function validateADRReferences(adr: ADR, allADRs: ADR[]): ValidationResult {
|
|
134
|
+
const warnings: string[] = [];
|
|
135
|
+
const adrIds = new Set(allADRs.map(a => a.id));
|
|
136
|
+
|
|
137
|
+
// Validate supersedes references
|
|
138
|
+
if (adr.supersedes) {
|
|
139
|
+
adr.supersedes.forEach(id => {
|
|
140
|
+
if (!adrIds.has(id)) {
|
|
141
|
+
warnings.push(`Referenced ADR does not exist: ${id}`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Validate superseded_by reference
|
|
147
|
+
if (adr.superseded_by && !adrIds.has(adr.superseded_by)) {
|
|
148
|
+
warnings.push(`Referenced ADR does not exist: ${adr.superseded_by}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Validate related_decisions
|
|
152
|
+
if (adr.related_decisions) {
|
|
153
|
+
adr.related_decisions.forEach(id => {
|
|
154
|
+
if (!adrIds.has(id)) {
|
|
155
|
+
warnings.push(`Related ADR does not exist: ${id}`);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { valid: warnings.length === 0, warnings };
|
|
161
|
+
}
|
|
162
|
+
|