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.
Files changed (29) hide show
  1. package/.aios-core/development/tasks/analyze-brownfield.md +456 -0
  2. package/.aios-core/development/tasks/setup-project-docs.md +440 -0
  3. package/.aios-core/infrastructure/scripts/documentation-integrity/brownfield-analyzer.js +501 -0
  4. package/.aios-core/infrastructure/scripts/documentation-integrity/config-generator.js +368 -0
  5. package/.aios-core/infrastructure/scripts/documentation-integrity/deployment-config-loader.js +308 -0
  6. package/.aios-core/infrastructure/scripts/documentation-integrity/doc-generator.js +331 -0
  7. package/.aios-core/infrastructure/scripts/documentation-integrity/gitignore-generator.js +312 -0
  8. package/.aios-core/infrastructure/scripts/documentation-integrity/index.js +74 -0
  9. package/.aios-core/infrastructure/scripts/documentation-integrity/mode-detector.js +389 -0
  10. package/.aios-core/infrastructure/templates/core-config/core-config-brownfield.tmpl.yaml +176 -0
  11. package/.aios-core/infrastructure/templates/core-config/core-config-greenfield.tmpl.yaml +127 -0
  12. package/.aios-core/infrastructure/templates/gitignore/gitignore-aios-base.tmpl +63 -0
  13. package/.aios-core/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +18 -0
  14. package/.aios-core/infrastructure/templates/gitignore/gitignore-node.tmpl +85 -0
  15. package/.aios-core/infrastructure/templates/gitignore/gitignore-python.tmpl +145 -0
  16. package/.aios-core/infrastructure/templates/project-docs/coding-standards-tmpl.md +346 -0
  17. package/.aios-core/infrastructure/templates/project-docs/source-tree-tmpl.md +177 -0
  18. package/.aios-core/infrastructure/templates/project-docs/tech-stack-tmpl.md +267 -0
  19. package/package.json +1 -1
  20. package/packages/installer/src/config/templates/env-template.js +2 -2
  21. package/packages/installer/src/wizard/wizard.js +185 -34
  22. package/packages/installer/tests/integration/environment-configuration.test.js +2 -1
  23. package/packages/installer/tests/unit/env-template.test.js +3 -2
  24. package/.aios-core/development/tasks/validate-structure.md +0 -243
  25. package/.aios-core/infrastructure/scripts/source-tree-guardian/index.js +0 -375
  26. package/.aios-core/infrastructure/scripts/source-tree-guardian/manifest-generator.js +0 -410
  27. package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/naming-rules.yaml +0 -285
  28. package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/placement-rules.yaml +0 -262
  29. 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
- };