@aiready/consistency 0.2.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/src/cli.ts ADDED
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { analyzeConsistency } from './analyzer';
5
+ import type { ConsistencyOptions } from './types';
6
+ import chalk from 'chalk';
7
+ import { writeFileSync } from 'fs';
8
+ import { join } from 'path';
9
+ import { loadConfig, mergeConfigWithDefaults } from '@aiready/core';
10
+
11
+ const program = new Command();
12
+
13
+ program
14
+ .name('aiready-consistency')
15
+ .description('Detect consistency issues in naming, patterns, and architecture')
16
+ .version('0.1.0')
17
+ .addHelpText('after', `
18
+ CONFIGURATION:
19
+ Supports config files: aiready.json, aiready.config.json, .aiready.json, .aireadyrc.json
20
+ CLI options override config file settings
21
+
22
+ ANALYSIS CATEGORIES:
23
+ --naming Check naming conventions and quality (default: enabled)
24
+ --patterns Check code pattern consistency (default: enabled)
25
+ --architecture Check architectural consistency (coming soon)
26
+
27
+ EXAMPLES:
28
+ aiready-consistency . # Full analysis
29
+ aiready-consistency . --no-naming # Skip naming checks
30
+ aiready-consistency . --min-severity major # Only show major+ issues
31
+ aiready-consistency . --output json > report.json # JSON export
32
+ `)
33
+ .argument('<directory>', 'Directory to analyze')
34
+ .option('--naming', 'Check naming conventions and quality (default: true)')
35
+ .option('--no-naming', 'Skip naming analysis')
36
+ .option('--patterns', 'Check code pattern consistency (default: true)')
37
+ .option('--no-patterns', 'Skip pattern analysis')
38
+ .option('--architecture', 'Check architectural consistency (not yet implemented)')
39
+ .option('--min-severity <level>', 'Minimum severity: info|minor|major|critical. Default: info')
40
+ .option('--include <patterns>', 'File patterns to include (comma-separated)')
41
+ .option('--exclude <patterns>', 'File patterns to exclude (comma-separated)')
42
+ .option('-o, --output <format>', 'Output format: console|json|markdown', 'console')
43
+ .option('--output-file <path>', 'Output file path (for json/markdown)')
44
+ .action(async (directory, options) => {
45
+ console.log(chalk.blue('🔍 Analyzing consistency...\n'));
46
+
47
+ const startTime = Date.now();
48
+
49
+ // Load config file if it exists
50
+ const config = loadConfig(directory);
51
+
52
+ // Define defaults
53
+ const defaults = {
54
+ checkNaming: true,
55
+ checkPatterns: true,
56
+ checkArchitecture: false,
57
+ minSeverity: 'info' as const,
58
+ include: undefined,
59
+ exclude: undefined,
60
+ };
61
+
62
+ // Merge config with defaults
63
+ const mergedConfig = mergeConfigWithDefaults(config, defaults);
64
+
65
+ // Override with CLI options (CLI takes precedence)
66
+ const finalOptions: ConsistencyOptions = {
67
+ rootDir: directory,
68
+ checkNaming: options.naming !== false && mergedConfig.checkNaming,
69
+ checkPatterns: options.patterns !== false && mergedConfig.checkPatterns,
70
+ checkArchitecture: options.architecture || mergedConfig.checkArchitecture,
71
+ minSeverity: (options.minSeverity as any) || mergedConfig.minSeverity,
72
+ include: options.include?.split(',') || mergedConfig.include,
73
+ exclude: options.exclude?.split(',') || mergedConfig.exclude,
74
+ };
75
+
76
+ const report = await analyzeConsistency(finalOptions);
77
+
78
+ const elapsedTime = ((Date.now() - startTime) / 1000).toFixed(2);
79
+
80
+ // Output based on format
81
+ if (options.output === 'json') {
82
+ const output = JSON.stringify(report, null, 2);
83
+ if (options.outputFile) {
84
+ writeFileSync(options.outputFile, output);
85
+ console.log(chalk.green(`✓ Report saved to ${options.outputFile}`));
86
+ } else {
87
+ console.log(output);
88
+ }
89
+ } else if (options.output === 'markdown') {
90
+ const markdown = generateMarkdownReport(report, elapsedTime);
91
+ if (options.outputFile) {
92
+ writeFileSync(options.outputFile, markdown);
93
+ console.log(chalk.green(`✓ Report saved to ${options.outputFile}`));
94
+ } else {
95
+ console.log(markdown);
96
+ }
97
+ } else {
98
+ // Console output
99
+ displayConsoleReport(report, elapsedTime);
100
+ }
101
+ });
102
+
103
+ program.parse();
104
+
105
+ function displayConsoleReport(report: any, elapsedTime: string): void {
106
+ const { summary, results, recommendations } = report;
107
+
108
+ console.log(chalk.bold('\n📊 Summary\n'));
109
+ console.log(`Files Analyzed: ${chalk.cyan(summary.filesAnalyzed)}`);
110
+ console.log(`Total Issues: ${chalk.yellow(summary.totalIssues)}`);
111
+ console.log(` Naming: ${chalk.yellow(summary.namingIssues)}`);
112
+ console.log(` Patterns: ${chalk.yellow(summary.patternIssues)}`);
113
+ console.log(` Architecture: ${chalk.yellow(summary.architectureIssues)}`);
114
+ console.log(`Analysis Time: ${chalk.gray(elapsedTime + 's')}\n`);
115
+
116
+ if (summary.totalIssues === 0) {
117
+ console.log(chalk.green('✨ No consistency issues found! Your codebase is well-maintained.\n'));
118
+ return;
119
+ }
120
+
121
+ // Group issues by category
122
+ const namingResults = results.filter((r: any) =>
123
+ r.issues.some((i: any) => i.category === 'naming')
124
+ );
125
+ const patternResults = results.filter((r: any) =>
126
+ r.issues.some((i: any) => i.category === 'patterns')
127
+ );
128
+
129
+ if (namingResults.length > 0) {
130
+ console.log(chalk.bold('🏷️ Naming Issues\n'));
131
+ displayCategoryIssues(namingResults, 5);
132
+ }
133
+
134
+ if (patternResults.length > 0) {
135
+ console.log(chalk.bold('\n🔄 Pattern Issues\n'));
136
+ displayCategoryIssues(patternResults, 5);
137
+ }
138
+
139
+ console.log(chalk.bold('\n💡 Recommendations\n'));
140
+ recommendations.forEach((rec: string, i: number) => {
141
+ console.log(`${i + 1}. ${rec}`);
142
+ });
143
+
144
+ console.log();
145
+ }
146
+
147
+ function displayCategoryIssues(results: any[], maxToShow: number): void {
148
+ let shown = 0;
149
+ for (const result of results) {
150
+ if (shown >= maxToShow) break;
151
+
152
+ for (const issue of result.issues) {
153
+ if (shown >= maxToShow) break;
154
+
155
+ const severityColor =
156
+ issue.severity === 'critical'
157
+ ? chalk.red
158
+ : issue.severity === 'major'
159
+ ? chalk.yellow
160
+ : issue.severity === 'minor'
161
+ ? chalk.blue
162
+ : chalk.gray;
163
+
164
+ console.log(
165
+ `${severityColor(issue.severity.toUpperCase())} ${chalk.dim(
166
+ `${issue.location.file}:${issue.location.line}`
167
+ )}`
168
+ );
169
+ console.log(` ${issue.message}`);
170
+ if (issue.suggestion) {
171
+ console.log(` ${chalk.dim('→')} ${chalk.italic(issue.suggestion)}`);
172
+ }
173
+ console.log();
174
+ shown++;
175
+ }
176
+ }
177
+
178
+ const remaining = results.reduce((sum, r) => sum + r.issues.length, 0) - shown;
179
+ if (remaining > 0) {
180
+ console.log(chalk.dim(` ... and ${remaining} more issues\n`));
181
+ }
182
+ }
183
+
184
+ function generateMarkdownReport(report: any, elapsedTime: string): string {
185
+ const { summary, results, recommendations } = report;
186
+
187
+ let markdown = `# Consistency Analysis Report\n\n`;
188
+ markdown += `**Generated:** ${new Date().toISOString()}\n`;
189
+ markdown += `**Analysis Time:** ${elapsedTime}s\n\n`;
190
+
191
+ markdown += `## Summary\n\n`;
192
+ markdown += `- **Files Analyzed:** ${summary.filesAnalyzed}\n`;
193
+ markdown += `- **Total Issues:** ${summary.totalIssues}\n`;
194
+ markdown += ` - Naming: ${summary.namingIssues}\n`;
195
+ markdown += ` - Patterns: ${summary.patternIssues}\n`;
196
+ markdown += ` - Architecture: ${summary.architectureIssues}\n\n`;
197
+
198
+ if (summary.totalIssues === 0) {
199
+ markdown += `✨ No consistency issues found!\n`;
200
+ return markdown;
201
+ }
202
+
203
+ markdown += `## Issues by Category\n\n`;
204
+
205
+ // Naming issues
206
+ const namingResults = results.filter((r: any) =>
207
+ r.issues.some((i: any) => i.category === 'naming')
208
+ );
209
+ if (namingResults.length > 0) {
210
+ markdown += `### 🏷️ Naming Issues\n\n`;
211
+ for (const result of namingResults) {
212
+ for (const issue of result.issues) {
213
+ if (issue.category !== 'naming') continue;
214
+ markdown += `- **${issue.severity.toUpperCase()}** \`${issue.location.file}:${issue.location.line}\`\n`;
215
+ markdown += ` - ${issue.message}\n`;
216
+ if (issue.suggestion) {
217
+ markdown += ` - 💡 ${issue.suggestion}\n`;
218
+ }
219
+ }
220
+ }
221
+ markdown += `\n`;
222
+ }
223
+
224
+ // Pattern issues
225
+ const patternResults = results.filter((r: any) =>
226
+ r.issues.some((i: any) => i.category === 'patterns')
227
+ );
228
+ if (patternResults.length > 0) {
229
+ markdown += `### 🔄 Pattern Issues\n\n`;
230
+ for (const result of patternResults) {
231
+ for (const issue of result.issues) {
232
+ if (issue.category !== 'patterns') continue;
233
+ markdown += `- **${issue.severity.toUpperCase()}** ${issue.message}\n`;
234
+ if (issue.examples && issue.examples.length > 0) {
235
+ markdown += ` - Examples:\n`;
236
+ issue.examples.forEach((ex: string) => {
237
+ markdown += ` - ${ex}\n`;
238
+ });
239
+ }
240
+ if (issue.suggestion) {
241
+ markdown += ` - 💡 ${issue.suggestion}\n`;
242
+ }
243
+ }
244
+ }
245
+ markdown += `\n`;
246
+ }
247
+
248
+ markdown += `## Recommendations\n\n`;
249
+ recommendations.forEach((rec: string, i: number) => {
250
+ markdown += `${i + 1}. ${rec}\n`;
251
+ });
252
+
253
+ return markdown;
254
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export { analyzeConsistency } from './analyzer';
2
+ export { analyzeNaming, detectNamingConventions } from './analyzers/naming';
3
+ export { analyzePatterns } from './analyzers/patterns';
4
+ export type {
5
+ ConsistencyOptions,
6
+ ConsistencyReport,
7
+ ConsistencyIssue,
8
+ NamingIssue,
9
+ PatternIssue,
10
+ ArchitectureIssue,
11
+ } from './types';
package/src/types.ts ADDED
@@ -0,0 +1,62 @@
1
+ import type { ScanOptions, AnalysisResult, Issue } from '@aiready/core';
2
+
3
+ export interface ConsistencyOptions extends ScanOptions {
4
+ /** Check naming conventions and quality */
5
+ checkNaming?: boolean;
6
+ /** Check code pattern consistency */
7
+ checkPatterns?: boolean;
8
+ /** Check architectural consistency */
9
+ checkArchitecture?: boolean;
10
+ /** Minimum severity to report */
11
+ minSeverity?: 'info' | 'minor' | 'major' | 'critical';
12
+ }
13
+
14
+ export interface ConsistencyIssue extends Issue {
15
+ type:
16
+ | 'naming-inconsistency'
17
+ | 'naming-quality'
18
+ | 'pattern-inconsistency'
19
+ | 'architecture-inconsistency';
20
+ category: 'naming' | 'patterns' | 'architecture';
21
+ /** Examples of the inconsistency found */
22
+ examples?: string[];
23
+ /** Suggested fix or convention to follow */
24
+ suggestion?: string;
25
+ }
26
+
27
+ export interface NamingIssue {
28
+ file: string;
29
+ line: number;
30
+ column?: number;
31
+ type: 'poor-naming' | 'convention-mix' | 'abbreviation' | 'unclear';
32
+ identifier: string;
33
+ suggestion?: string;
34
+ severity: 'critical' | 'major' | 'minor' | 'info';
35
+ }
36
+
37
+ export interface PatternIssue {
38
+ files: string[];
39
+ type: 'error-handling' | 'async-style' | 'import-style' | 'api-design';
40
+ description: string;
41
+ examples: string[];
42
+ severity: 'critical' | 'major' | 'minor' | 'info';
43
+ }
44
+
45
+ export interface ArchitectureIssue {
46
+ type: 'file-organization' | 'module-structure' | 'export-style';
47
+ description: string;
48
+ affectedPaths: string[];
49
+ severity: 'critical' | 'major' | 'minor' | 'info';
50
+ }
51
+
52
+ export interface ConsistencyReport {
53
+ summary: {
54
+ totalIssues: number;
55
+ namingIssues: number;
56
+ patternIssues: number;
57
+ architectureIssues: number;
58
+ filesAnalyzed: number;
59
+ };
60
+ results: AnalysisResult[];
61
+ recommendations: string[];
62
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../core/tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": "./src"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }