aios-core 2.1.5 → 2.1.6
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/development/tasks/analyze-brownfield.md +456 -0
- package/.aios-core/development/tasks/setup-project-docs.md +440 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/brownfield-analyzer.js +501 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/config-generator.js +368 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/deployment-config-loader.js +308 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/doc-generator.js +331 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/gitignore-generator.js +312 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/index.js +74 -0
- package/.aios-core/infrastructure/scripts/documentation-integrity/mode-detector.js +389 -0
- package/.aios-core/infrastructure/templates/core-config/core-config-brownfield.tmpl.yaml +176 -0
- package/.aios-core/infrastructure/templates/core-config/core-config-greenfield.tmpl.yaml +127 -0
- package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -0
- package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -0
- package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -0
- package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -0
- package/.aios-core/infrastructure/templates/project-docs/coding-standards-tmpl.md +346 -0
- package/.aios-core/infrastructure/templates/project-docs/source-tree-tmpl.md +177 -0
- package/.aios-core/infrastructure/templates/project-docs/tech-stack-tmpl.md +267 -0
- package/package.json +1 -1
- package/packages/installer/src/config/templates/env-template.js +2 -2
- package/packages/installer/src/wizard/wizard.js +185 -34
- package/packages/installer/tests/integration/environment-configuration.test.js +2 -1
- package/packages/installer/tests/unit/env-template.test.js +3 -2
- package/.aios-core/development/tasks/validate-structure.md +0 -243
- package/.aios-core/infrastructure/scripts/source-tree-guardian/index.js +0 -375
- package/.aios-core/infrastructure/scripts/source-tree-guardian/manifest-generator.js +0 -410
- package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/naming-rules.yaml +0 -285
- package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/placement-rules.yaml +0 -262
- package/.aios-core/infrastructure/scripts/source-tree-guardian/validator.js +0 -468
|
@@ -1,468 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Source-Tree Guardian - Validator Engine
|
|
3
|
-
*
|
|
4
|
-
* Validates file placements against AIOS source-tree standards.
|
|
5
|
-
*
|
|
6
|
-
* @module source-tree-guardian/validator
|
|
7
|
-
* @version 1.0.0
|
|
8
|
-
* @story 6.8
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs').promises;
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const yaml = require('yaml');
|
|
14
|
-
const { glob } = require('glob');
|
|
15
|
-
|
|
16
|
-
// Constants
|
|
17
|
-
const DEFAULT_RULES_PATH = path.join(__dirname, 'rules', 'placement-rules.yaml');
|
|
18
|
-
const DEFAULT_NAMING_RULES_PATH = path.join(__dirname, 'rules', 'naming-rules.yaml');
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Severity levels for violations
|
|
22
|
-
* @enum {string}
|
|
23
|
-
*/
|
|
24
|
-
const Severity = {
|
|
25
|
-
ERROR: 'error',
|
|
26
|
-
WARNING: 'warning',
|
|
27
|
-
INFO: 'info',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Normalizes a file path for cross-platform compatibility
|
|
32
|
-
* @param {string} filePath - The path to normalize
|
|
33
|
-
* @returns {string} Normalized path with forward slashes
|
|
34
|
-
*/
|
|
35
|
-
function normalizePath(filePath) {
|
|
36
|
-
if (!filePath) {
|
|
37
|
-
return '';
|
|
38
|
-
}
|
|
39
|
-
// Convert backslashes to forward slashes
|
|
40
|
-
let normalized = filePath.replace(/\\/g, '/');
|
|
41
|
-
// Remove drive letter for Windows paths (e.g., C:/)
|
|
42
|
-
normalized = normalized.replace(/^[a-zA-Z]:/, '');
|
|
43
|
-
// Remove leading slash if present
|
|
44
|
-
normalized = normalized.replace(/^\/+/, '');
|
|
45
|
-
return normalized;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Checks if a path matches a glob pattern
|
|
50
|
-
* @param {string} filePath - The file path to check
|
|
51
|
-
* @param {string} pattern - The glob pattern
|
|
52
|
-
* @returns {boolean} True if the path matches the pattern
|
|
53
|
-
*/
|
|
54
|
-
function matchesPattern(filePath, pattern) {
|
|
55
|
-
const normalized = normalizePath(filePath);
|
|
56
|
-
const normalizedPattern = normalizePath(pattern);
|
|
57
|
-
|
|
58
|
-
// Simple glob matching
|
|
59
|
-
const regexPattern = normalizedPattern
|
|
60
|
-
.replace(/\*\*/g, '.*')
|
|
61
|
-
.replace(/\*/g, '[^/]*')
|
|
62
|
-
.replace(/\?/g, '.');
|
|
63
|
-
|
|
64
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
65
|
-
return regex.test(normalized);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Validates a filename against naming conventions
|
|
70
|
-
* @param {string} filename - The filename to validate
|
|
71
|
-
* @param {string} convention - The naming convention to check
|
|
72
|
-
* @returns {Object} Validation result with isValid and message
|
|
73
|
-
*/
|
|
74
|
-
function validateNamingConvention(filename, convention) {
|
|
75
|
-
const patterns = {
|
|
76
|
-
'kebab-case': /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,
|
|
77
|
-
'camelCase': /^[a-z][a-zA-Z0-9]*$/,
|
|
78
|
-
'PascalCase': /^[A-Z][a-zA-Z0-9]*$/,
|
|
79
|
-
'SCREAMING_SNAKE_CASE': /^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$/,
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// Remove extension for validation
|
|
83
|
-
const nameWithoutExt = path.basename(filename, path.extname(filename));
|
|
84
|
-
|
|
85
|
-
// Check for special allowed names
|
|
86
|
-
const allowedNames = ['index', 'README', 'CHANGELOG', 'CLAUDE', 'LICENSE', 'CONTRIBUTING'];
|
|
87
|
-
if (allowedNames.includes(nameWithoutExt)) {
|
|
88
|
-
return { isValid: true, message: 'Allowed special filename' };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const pattern = patterns[convention];
|
|
92
|
-
if (!pattern) {
|
|
93
|
-
return { isValid: true, message: `Unknown convention: ${convention}` };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const isValid = pattern.test(nameWithoutExt);
|
|
97
|
-
return {
|
|
98
|
-
isValid,
|
|
99
|
-
message: isValid
|
|
100
|
-
? `Valid ${convention}`
|
|
101
|
-
: `Expected ${convention}, got: ${nameWithoutExt}`,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Loads rules from a YAML file
|
|
107
|
-
* @param {string} rulesPath - Path to the rules file
|
|
108
|
-
* @returns {Promise<Object>} Parsed rules object
|
|
109
|
-
*/
|
|
110
|
-
async function loadRules(rulesPath) {
|
|
111
|
-
try {
|
|
112
|
-
const content = await fs.readFile(rulesPath, 'utf8');
|
|
113
|
-
return yaml.parse(content);
|
|
114
|
-
} catch (error) {
|
|
115
|
-
if (error.code === 'ENOENT') {
|
|
116
|
-
console.warn(`Rules file not found: ${rulesPath}`);
|
|
117
|
-
return { rules: [], violations: [], exclusions: { directories: [], files: [], patterns: [] } };
|
|
118
|
-
}
|
|
119
|
-
throw new Error(`Failed to load rules from ${rulesPath}: ${error.message}`);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Checks if a file should be excluded from validation
|
|
125
|
-
* @param {string} filePath - The file path to check
|
|
126
|
-
* @param {Object} exclusions - Exclusion rules
|
|
127
|
-
* @returns {boolean} True if the file should be excluded
|
|
128
|
-
*/
|
|
129
|
-
function shouldExclude(filePath, exclusions) {
|
|
130
|
-
const normalized = normalizePath(filePath);
|
|
131
|
-
const parts = normalized.split('/');
|
|
132
|
-
|
|
133
|
-
// Check directory exclusions
|
|
134
|
-
if (exclusions.directories) {
|
|
135
|
-
for (const dir of exclusions.directories) {
|
|
136
|
-
if (parts.includes(dir)) {
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Check file exclusions
|
|
143
|
-
if (exclusions.files) {
|
|
144
|
-
const filename = path.basename(filePath);
|
|
145
|
-
for (const pattern of exclusions.files) {
|
|
146
|
-
if (matchesPattern(filename, pattern)) {
|
|
147
|
-
return true;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Check pattern exclusions
|
|
153
|
-
if (exclusions.patterns) {
|
|
154
|
-
for (const pattern of exclusions.patterns) {
|
|
155
|
-
if (matchesPattern(normalized, pattern)) {
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Validates a single file against placement rules
|
|
166
|
-
* @param {string} filePath - The file path to validate
|
|
167
|
-
* @param {Object} rules - Placement rules
|
|
168
|
-
* @param {string} projectRoot - Project root directory
|
|
169
|
-
* @returns {Object} Validation result with violations
|
|
170
|
-
*/
|
|
171
|
-
function validateFilePlacement(filePath, rules, projectRoot) {
|
|
172
|
-
const relativePath = normalizePath(path.relative(projectRoot, filePath));
|
|
173
|
-
const filename = path.basename(filePath);
|
|
174
|
-
const violations = [];
|
|
175
|
-
|
|
176
|
-
// Check against violation rules (anti-patterns)
|
|
177
|
-
if (rules.violations) {
|
|
178
|
-
for (const violation of rules.violations) {
|
|
179
|
-
// Check if file matches violation pattern
|
|
180
|
-
if (violation.at_root && matchesPattern(relativePath, violation.pattern)) {
|
|
181
|
-
// Check exclusions for this violation
|
|
182
|
-
if (violation.exclude_patterns) {
|
|
183
|
-
const excluded = violation.exclude_patterns.some((p) =>
|
|
184
|
-
matchesPattern(relativePath, p),
|
|
185
|
-
);
|
|
186
|
-
if (excluded) {
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
violations.push({
|
|
192
|
-
type: 'placement',
|
|
193
|
-
severity: violation.severity || Severity.ERROR,
|
|
194
|
-
file: relativePath,
|
|
195
|
-
message: violation.message,
|
|
196
|
-
suggestedLocation: violation.suggested_location,
|
|
197
|
-
ruleId: violation.id,
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
file: relativePath,
|
|
205
|
-
violations,
|
|
206
|
-
isValid: violations.length === 0,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Gets staged files from git
|
|
212
|
-
* @param {string} projectRoot - Project root directory
|
|
213
|
-
* @returns {Promise<string[]>} List of staged file paths
|
|
214
|
-
*/
|
|
215
|
-
async function getStagedFiles(projectRoot) {
|
|
216
|
-
const { execSync } = require('child_process');
|
|
217
|
-
try {
|
|
218
|
-
const output = execSync('git diff --cached --name-only --diff-filter=ACM', {
|
|
219
|
-
cwd: projectRoot,
|
|
220
|
-
encoding: 'utf8',
|
|
221
|
-
});
|
|
222
|
-
return output.trim().split('\n').filter(Boolean);
|
|
223
|
-
} catch (error) {
|
|
224
|
-
console.warn('Failed to get staged files:', error.message);
|
|
225
|
-
return [];
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Main validator class
|
|
231
|
-
*/
|
|
232
|
-
class SourceTreeValidator {
|
|
233
|
-
/**
|
|
234
|
-
* Creates a new SourceTreeValidator instance
|
|
235
|
-
* @param {Object} options - Validator options
|
|
236
|
-
* @param {string} options.projectRoot - Project root directory
|
|
237
|
-
* @param {string} options.rulesPath - Path to placement rules
|
|
238
|
-
* @param {string} options.namingRulesPath - Path to naming rules
|
|
239
|
-
* @param {boolean} options.verbose - Enable verbose output
|
|
240
|
-
*/
|
|
241
|
-
constructor(options = {}) {
|
|
242
|
-
this.projectRoot = options.projectRoot || process.cwd();
|
|
243
|
-
this.rulesPath = options.rulesPath || DEFAULT_RULES_PATH;
|
|
244
|
-
this.namingRulesPath = options.namingRulesPath || DEFAULT_NAMING_RULES_PATH;
|
|
245
|
-
this.verbose = options.verbose || false;
|
|
246
|
-
this.rules = null;
|
|
247
|
-
this.namingRules = null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Initializes the validator by loading rules
|
|
252
|
-
* @returns {Promise<void>}
|
|
253
|
-
*/
|
|
254
|
-
async initialize() {
|
|
255
|
-
this.rules = await loadRules(this.rulesPath);
|
|
256
|
-
this.namingRules = await loadRules(this.namingRulesPath);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Validates all files in the project
|
|
261
|
-
* @param {Object} options - Validation options
|
|
262
|
-
* @param {string} options.files - Glob pattern for files to validate
|
|
263
|
-
* @param {boolean} options.stagedOnly - Only validate staged files
|
|
264
|
-
* @returns {Promise<Object>} Validation report
|
|
265
|
-
*/
|
|
266
|
-
async validate(options = {}) {
|
|
267
|
-
if (!this.rules) {
|
|
268
|
-
await this.initialize();
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const startTime = Date.now();
|
|
272
|
-
let filesToValidate = [];
|
|
273
|
-
|
|
274
|
-
if (options.stagedOnly) {
|
|
275
|
-
filesToValidate = await getStagedFiles(this.projectRoot);
|
|
276
|
-
filesToValidate = filesToValidate.map((f) => path.join(this.projectRoot, f));
|
|
277
|
-
} else if (options.files) {
|
|
278
|
-
filesToValidate = await glob(options.files, {
|
|
279
|
-
cwd: this.projectRoot,
|
|
280
|
-
absolute: true,
|
|
281
|
-
nodir: true,
|
|
282
|
-
});
|
|
283
|
-
} else {
|
|
284
|
-
// Validate all files
|
|
285
|
-
filesToValidate = await glob('**/*', {
|
|
286
|
-
cwd: this.projectRoot,
|
|
287
|
-
absolute: true,
|
|
288
|
-
nodir: true,
|
|
289
|
-
ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/coverage/**'],
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
const results = {
|
|
294
|
-
filesScanned: 0,
|
|
295
|
-
filesExcluded: 0,
|
|
296
|
-
violations: [],
|
|
297
|
-
warnings: [],
|
|
298
|
-
info: [],
|
|
299
|
-
summary: {
|
|
300
|
-
errors: 0,
|
|
301
|
-
warnings: 0,
|
|
302
|
-
info: 0,
|
|
303
|
-
},
|
|
304
|
-
duration: 0,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
for (const filePath of filesToValidate) {
|
|
308
|
-
// Check exclusions
|
|
309
|
-
if (shouldExclude(filePath, this.rules.exclusions || {})) {
|
|
310
|
-
results.filesExcluded++;
|
|
311
|
-
continue;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
results.filesScanned++;
|
|
315
|
-
const validation = validateFilePlacement(filePath, this.rules, this.projectRoot);
|
|
316
|
-
|
|
317
|
-
for (const violation of validation.violations) {
|
|
318
|
-
switch (violation.severity) {
|
|
319
|
-
case Severity.ERROR:
|
|
320
|
-
results.violations.push(violation);
|
|
321
|
-
results.summary.errors++;
|
|
322
|
-
break;
|
|
323
|
-
case Severity.WARNING:
|
|
324
|
-
results.warnings.push(violation);
|
|
325
|
-
results.summary.warnings++;
|
|
326
|
-
break;
|
|
327
|
-
case Severity.INFO:
|
|
328
|
-
results.info.push(violation);
|
|
329
|
-
results.summary.info++;
|
|
330
|
-
break;
|
|
331
|
-
default:
|
|
332
|
-
results.violations.push(violation);
|
|
333
|
-
results.summary.errors++;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
results.duration = Date.now() - startTime;
|
|
339
|
-
return results;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
* Validates a single file
|
|
344
|
-
* @param {string} filePath - Path to the file to validate
|
|
345
|
-
* @returns {Promise<Object>} Validation result
|
|
346
|
-
*/
|
|
347
|
-
async validateFile(filePath) {
|
|
348
|
-
if (!this.rules) {
|
|
349
|
-
await this.initialize();
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
const absolutePath = path.isAbsolute(filePath)
|
|
353
|
-
? filePath
|
|
354
|
-
: path.join(this.projectRoot, filePath);
|
|
355
|
-
|
|
356
|
-
return validateFilePlacement(absolutePath, this.rules, this.projectRoot);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Generates fix suggestions for violations
|
|
361
|
-
* @param {Array} violations - List of violations
|
|
362
|
-
* @returns {Array} List of fix suggestions
|
|
363
|
-
*/
|
|
364
|
-
generateFixSuggestions(violations) {
|
|
365
|
-
return violations.map((violation) => {
|
|
366
|
-
const suggestion = {
|
|
367
|
-
file: violation.file,
|
|
368
|
-
currentLocation: path.dirname(violation.file),
|
|
369
|
-
suggestedLocation: violation.suggestedLocation || 'No suggestion available',
|
|
370
|
-
message: violation.message,
|
|
371
|
-
command: null,
|
|
372
|
-
};
|
|
373
|
-
|
|
374
|
-
if (violation.suggestedLocation) {
|
|
375
|
-
const filename = path.basename(violation.file);
|
|
376
|
-
const newPath = path.join(violation.suggestedLocation, filename);
|
|
377
|
-
suggestion.command = `mv "${violation.file}" "${newPath}"`;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
return suggestion;
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Formats the validation report for console output
|
|
386
|
-
* @param {Object} report - Validation report
|
|
387
|
-
* @param {Object} options - Formatting options
|
|
388
|
-
* @returns {string} Formatted report
|
|
389
|
-
*/
|
|
390
|
-
formatReport(report, options = {}) {
|
|
391
|
-
const lines = [];
|
|
392
|
-
const width = 75;
|
|
393
|
-
const border = '═'.repeat(width);
|
|
394
|
-
|
|
395
|
-
lines.push(`╔${border}╗`);
|
|
396
|
-
lines.push(`║${'SOURCE-TREE VALIDATION REPORT'.padStart((width + 29) / 2).padEnd(width)}║`);
|
|
397
|
-
lines.push(`╠${border}╣`);
|
|
398
|
-
lines.push(`║${''.padEnd(width)}║`);
|
|
399
|
-
lines.push(`║ Files Scanned: ${String(report.filesScanned).padEnd(width - 19)}║`);
|
|
400
|
-
lines.push(`║ Violations: ${String(report.summary.errors).padEnd(width - 16)}║`);
|
|
401
|
-
lines.push(`║ Warnings: ${String(report.summary.warnings).padEnd(width - 14)}║`);
|
|
402
|
-
lines.push(`║ Duration: ${String(report.duration + 'ms').padEnd(width - 14)}║`);
|
|
403
|
-
lines.push(`║${''.padEnd(width)}║`);
|
|
404
|
-
|
|
405
|
-
if (report.violations.length > 0) {
|
|
406
|
-
lines.push(`╠${border}╣`);
|
|
407
|
-
lines.push(`║ VIOLATIONS${' '.repeat(width - 13)}║`);
|
|
408
|
-
lines.push(`╠${border}╣`);
|
|
409
|
-
lines.push(`║${''.padEnd(width)}║`);
|
|
410
|
-
|
|
411
|
-
for (const violation of report.violations) {
|
|
412
|
-
lines.push(`║ ❌ ERROR: ${violation.file.substring(0, width - 14).padEnd(width - 13)}║`);
|
|
413
|
-
lines.push(`║ Problem: ${violation.message.substring(0, width - 16).padEnd(width - 15)}║`);
|
|
414
|
-
if (violation.suggestedLocation) {
|
|
415
|
-
lines.push(`║ Suggested: ${violation.suggestedLocation.substring(0, width - 18).padEnd(width - 17)}║`);
|
|
416
|
-
}
|
|
417
|
-
lines.push(`║${''.padEnd(width)}║`);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (report.warnings.length > 0) {
|
|
422
|
-
lines.push(`╠${border}╣`);
|
|
423
|
-
lines.push(`║ WARNINGS${' '.repeat(width - 11)}║`);
|
|
424
|
-
lines.push(`╠${border}╣`);
|
|
425
|
-
lines.push(`║${''.padEnd(width)}║`);
|
|
426
|
-
|
|
427
|
-
for (const warning of report.warnings) {
|
|
428
|
-
lines.push(`║ ⚠️ WARNING: ${warning.file.substring(0, width - 16).padEnd(width - 15)}║`);
|
|
429
|
-
lines.push(`║ Problem: ${warning.message.substring(0, width - 16).padEnd(width - 15)}║`);
|
|
430
|
-
if (warning.suggestedLocation) {
|
|
431
|
-
lines.push(`║ Suggested: ${warning.suggestedLocation.substring(0, width - 18).padEnd(width - 17)}║`);
|
|
432
|
-
}
|
|
433
|
-
lines.push(`║${''.padEnd(width)}║`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
lines.push(`╚${border}╝`);
|
|
438
|
-
|
|
439
|
-
if (options.showFixHint && report.summary.errors > 0) {
|
|
440
|
-
lines.push('');
|
|
441
|
-
lines.push('Run with --fix to see auto-fix suggestions');
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
return lines.join('\n');
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/**
|
|
448
|
-
* Formats the report as JSON
|
|
449
|
-
* @param {Object} report - Validation report
|
|
450
|
-
* @returns {string} JSON formatted report
|
|
451
|
-
*/
|
|
452
|
-
formatJsonReport(report) {
|
|
453
|
-
return JSON.stringify(report, null, 2);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Export for use as module
|
|
458
|
-
module.exports = {
|
|
459
|
-
SourceTreeValidator,
|
|
460
|
-
normalizePath,
|
|
461
|
-
validateNamingConvention,
|
|
462
|
-
matchesPattern,
|
|
463
|
-
loadRules,
|
|
464
|
-
shouldExclude,
|
|
465
|
-
validateFilePlacement,
|
|
466
|
-
getStagedFiles,
|
|
467
|
-
Severity,
|
|
468
|
-
};
|