@aiready/cli 0.9.39 ā 0.9.41
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/.aiready/aiready-report-20260227-133806.json +7805 -0
- package/.aiready/aiready-report-20260227-133938.json +7951 -0
- package/.github/FUNDING.yml +2 -2
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-test.log +5 -5
- package/CONTRIBUTING.md +11 -2
- package/dist/chunk-HLBKROD3.mjs +237 -0
- package/dist/cli.js +719 -179
- package/dist/cli.mjs +711 -180
- package/dist/index.js +12 -3
- package/dist/index.mjs +1 -1
- package/package.json +12 -12
- package/src/__tests__/cli.test.ts +1 -1
- package/src/cli.ts +118 -32
- package/src/commands/agent-grounding.ts +22 -7
- package/src/commands/ai-signal-clarity.ts +13 -8
- package/src/commands/consistency.ts +69 -29
- package/src/commands/context.ts +108 -38
- package/src/commands/deps-health.ts +15 -6
- package/src/commands/doc-drift.ts +10 -4
- package/src/commands/index.ts +6 -2
- package/src/commands/patterns.ts +67 -26
- package/src/commands/scan.ts +430 -119
- package/src/commands/testability.ts +22 -9
- package/src/commands/visualize.ts +102 -45
- package/src/index.ts +34 -10
- package/src/utils/helpers.ts +57 -32
package/src/commands/context.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { resolve as resolvePath } from 'path';
|
|
7
|
-
import {
|
|
8
|
-
loadMergedConfig,
|
|
9
|
-
handleJSONOutput,
|
|
10
|
-
handleCLIError,
|
|
11
|
-
getElapsedTime,
|
|
7
|
+
import {
|
|
8
|
+
loadMergedConfig,
|
|
9
|
+
handleJSONOutput,
|
|
10
|
+
handleCLIError,
|
|
11
|
+
getElapsedTime,
|
|
12
12
|
resolveOutputPath,
|
|
13
13
|
formatToolScore,
|
|
14
14
|
} from '@aiready/core';
|
|
@@ -25,7 +25,10 @@ interface ContextOptions {
|
|
|
25
25
|
score?: boolean;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export async function contextAction(
|
|
28
|
+
export async function contextAction(
|
|
29
|
+
directory: string,
|
|
30
|
+
options: ContextOptions
|
|
31
|
+
) {
|
|
29
32
|
console.log(chalk.blue('š§ Analyzing context costs...\n'));
|
|
30
33
|
|
|
31
34
|
const startTime = Date.now();
|
|
@@ -45,9 +48,11 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
45
48
|
};
|
|
46
49
|
|
|
47
50
|
// Load and merge config with CLI options
|
|
48
|
-
|
|
51
|
+
const baseOptions = await loadMergedConfig(resolvedDir, defaults, {
|
|
49
52
|
maxDepth: options.maxDepth ? parseInt(options.maxDepth) : undefined,
|
|
50
|
-
maxContextBudget: options.maxContext
|
|
53
|
+
maxContextBudget: options.maxContext
|
|
54
|
+
? parseInt(options.maxContext)
|
|
55
|
+
: undefined,
|
|
51
56
|
include: options.include?.split(','),
|
|
52
57
|
exclude: options.exclude?.split(','),
|
|
53
58
|
});
|
|
@@ -55,32 +60,41 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
55
60
|
// Apply smart defaults for context analysis (always for individual context command)
|
|
56
61
|
let finalOptions: any = { ...baseOptions };
|
|
57
62
|
const { getSmartDefaults } = await import('@aiready/context-analyzer');
|
|
58
|
-
const contextSmartDefaults = await getSmartDefaults(
|
|
63
|
+
const contextSmartDefaults = await getSmartDefaults(
|
|
64
|
+
resolvedDir,
|
|
65
|
+
baseOptions
|
|
66
|
+
);
|
|
59
67
|
finalOptions = { ...contextSmartDefaults, ...finalOptions };
|
|
60
|
-
|
|
68
|
+
|
|
61
69
|
// Display configuration
|
|
62
70
|
console.log('š Configuration:');
|
|
63
71
|
console.log(` Max depth: ${finalOptions.maxDepth}`);
|
|
64
72
|
console.log(` Max context budget: ${finalOptions.maxContextBudget}`);
|
|
65
|
-
console.log(
|
|
66
|
-
|
|
73
|
+
console.log(
|
|
74
|
+
` Min cohesion: ${(finalOptions.minCohesion * 100).toFixed(1)}%`
|
|
75
|
+
);
|
|
76
|
+
console.log(
|
|
77
|
+
` Max fragmentation: ${(finalOptions.maxFragmentation * 100).toFixed(1)}%`
|
|
78
|
+
);
|
|
67
79
|
console.log(` Analysis focus: ${finalOptions.focus}`);
|
|
68
80
|
console.log('');
|
|
69
81
|
|
|
70
|
-
const { analyzeContext, generateSummary, calculateContextScore } =
|
|
82
|
+
const { analyzeContext, generateSummary, calculateContextScore } =
|
|
83
|
+
await import('@aiready/context-analyzer');
|
|
71
84
|
|
|
72
85
|
const results = await analyzeContext(finalOptions);
|
|
73
86
|
|
|
74
87
|
const elapsedTime = getElapsedTime(startTime);
|
|
75
88
|
const summary = generateSummary(results);
|
|
76
|
-
|
|
89
|
+
|
|
77
90
|
// Calculate score if requested
|
|
78
91
|
let contextScore: ToolScoringOutput | undefined;
|
|
79
92
|
if (options.score) {
|
|
80
93
|
contextScore = calculateContextScore(summary as any);
|
|
81
94
|
}
|
|
82
95
|
|
|
83
|
-
const outputFormat =
|
|
96
|
+
const outputFormat =
|
|
97
|
+
options.output || finalOptions.output?.format || 'console';
|
|
84
98
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
85
99
|
|
|
86
100
|
if (outputFormat === 'json') {
|
|
@@ -95,8 +109,12 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
95
109
|
`aiready-report-${getReportTimestamp()}.json`,
|
|
96
110
|
resolvedDir
|
|
97
111
|
);
|
|
98
|
-
|
|
99
|
-
handleJSONOutput(
|
|
112
|
+
|
|
113
|
+
handleJSONOutput(
|
|
114
|
+
outputData,
|
|
115
|
+
outputPath,
|
|
116
|
+
`ā
Results saved to ${outputPath}`
|
|
117
|
+
);
|
|
100
118
|
} else {
|
|
101
119
|
// Console output - format the results nicely
|
|
102
120
|
const terminalWidth = process.stdout.columns || 80;
|
|
@@ -107,25 +125,48 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
107
125
|
console.log(chalk.bold.white(' CONTEXT ANALYSIS SUMMARY'));
|
|
108
126
|
console.log(chalk.cyan(divider) + '\n');
|
|
109
127
|
|
|
110
|
-
console.log(
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log(
|
|
128
|
+
console.log(
|
|
129
|
+
chalk.white(`š Files analyzed: ${chalk.bold(summary.totalFiles)}`)
|
|
130
|
+
);
|
|
131
|
+
console.log(
|
|
132
|
+
chalk.white(
|
|
133
|
+
`š Total tokens: ${chalk.bold(summary.totalTokens.toLocaleString())}`
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
console.log(
|
|
137
|
+
chalk.yellow(
|
|
138
|
+
`š° Avg context budget: ${chalk.bold(summary.avgContextBudget.toFixed(0))} tokens/file`
|
|
139
|
+
)
|
|
140
|
+
);
|
|
141
|
+
console.log(
|
|
142
|
+
chalk.white(`ā± Analysis time: ${chalk.bold(elapsedTime + 's')}\n`)
|
|
143
|
+
);
|
|
114
144
|
|
|
115
145
|
// Issues summary
|
|
116
|
-
const totalIssues =
|
|
146
|
+
const totalIssues =
|
|
147
|
+
summary.criticalIssues + summary.majorIssues + summary.minorIssues;
|
|
117
148
|
if (totalIssues > 0) {
|
|
118
149
|
console.log(chalk.bold('ā ļø Issues Found:\n'));
|
|
119
150
|
if (summary.criticalIssues > 0) {
|
|
120
|
-
console.log(
|
|
151
|
+
console.log(
|
|
152
|
+
chalk.red(` š“ Critical: ${chalk.bold(summary.criticalIssues)}`)
|
|
153
|
+
);
|
|
121
154
|
}
|
|
122
155
|
if (summary.majorIssues > 0) {
|
|
123
|
-
console.log(
|
|
156
|
+
console.log(
|
|
157
|
+
chalk.yellow(` š” Major: ${chalk.bold(summary.majorIssues)}`)
|
|
158
|
+
);
|
|
124
159
|
}
|
|
125
160
|
if (summary.minorIssues > 0) {
|
|
126
|
-
console.log(
|
|
161
|
+
console.log(
|
|
162
|
+
chalk.blue(` šµ Minor: ${chalk.bold(summary.minorIssues)}`)
|
|
163
|
+
);
|
|
127
164
|
}
|
|
128
|
-
console.log(
|
|
165
|
+
console.log(
|
|
166
|
+
chalk.green(
|
|
167
|
+
`\n š” Potential savings: ${chalk.bold(summary.totalPotentialSavings.toLocaleString())} tokens\n`
|
|
168
|
+
)
|
|
169
|
+
);
|
|
129
170
|
} else {
|
|
130
171
|
console.log(chalk.green('ā
No significant issues found!\n'));
|
|
131
172
|
}
|
|
@@ -133,11 +174,17 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
133
174
|
// Deep import chains
|
|
134
175
|
if (summary.deepFiles.length > 0) {
|
|
135
176
|
console.log(chalk.bold('š Deep Import Chains:\n'));
|
|
136
|
-
console.log(
|
|
137
|
-
|
|
177
|
+
console.log(
|
|
178
|
+
chalk.gray(` Average depth: ${summary.avgImportDepth.toFixed(1)}`)
|
|
179
|
+
);
|
|
180
|
+
console.log(
|
|
181
|
+
chalk.gray(` Maximum depth: ${summary.maxImportDepth}\n`)
|
|
182
|
+
);
|
|
138
183
|
summary.deepFiles.slice(0, 10).forEach((item) => {
|
|
139
184
|
const fileName = item.file.split('/').slice(-2).join('/');
|
|
140
|
-
console.log(
|
|
185
|
+
console.log(
|
|
186
|
+
` ${chalk.cyan('ā')} ${chalk.white(fileName)} ${chalk.dim(`(depth: ${item.depth})`)}`
|
|
187
|
+
);
|
|
141
188
|
});
|
|
142
189
|
console.log();
|
|
143
190
|
}
|
|
@@ -145,10 +192,20 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
145
192
|
// Fragmented modules
|
|
146
193
|
if (summary.fragmentedModules.length > 0) {
|
|
147
194
|
console.log(chalk.bold('š§© Fragmented Modules:\n'));
|
|
148
|
-
console.log(
|
|
195
|
+
console.log(
|
|
196
|
+
chalk.gray(
|
|
197
|
+
` Average fragmentation: ${(summary.avgFragmentation * 100).toFixed(0)}%\n`
|
|
198
|
+
)
|
|
199
|
+
);
|
|
149
200
|
summary.fragmentedModules.slice(0, 10).forEach((module) => {
|
|
150
|
-
console.log(
|
|
151
|
-
|
|
201
|
+
console.log(
|
|
202
|
+
` ${chalk.yellow('ā')} ${chalk.white(module.domain)} - ${chalk.dim(`${module.files.length} files, ${(module.fragmentationScore * 100).toFixed(0)}% scattered`)}`
|
|
203
|
+
);
|
|
204
|
+
console.log(
|
|
205
|
+
chalk.dim(
|
|
206
|
+
` Token cost: ${module.totalTokens.toLocaleString()}, Cohesion: ${(module.avgCohesion * 100).toFixed(0)}%`
|
|
207
|
+
)
|
|
208
|
+
);
|
|
152
209
|
});
|
|
153
210
|
console.log();
|
|
154
211
|
}
|
|
@@ -156,12 +213,18 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
156
213
|
// Low cohesion files
|
|
157
214
|
if (summary.lowCohesionFiles.length > 0) {
|
|
158
215
|
console.log(chalk.bold('š Low Cohesion Files:\n'));
|
|
159
|
-
console.log(
|
|
216
|
+
console.log(
|
|
217
|
+
chalk.gray(
|
|
218
|
+
` Average cohesion: ${(summary.avgCohesion * 100).toFixed(0)}%\n`
|
|
219
|
+
)
|
|
220
|
+
);
|
|
160
221
|
summary.lowCohesionFiles.slice(0, 10).forEach((item) => {
|
|
161
222
|
const fileName = item.file.split('/').slice(-2).join('/');
|
|
162
223
|
const scorePercent = (item.score * 100).toFixed(0);
|
|
163
224
|
const color = item.score < 0.4 ? chalk.red : chalk.yellow;
|
|
164
|
-
console.log(
|
|
225
|
+
console.log(
|
|
226
|
+
` ${color('ā')} ${chalk.white(fileName)} ${chalk.dim(`(${scorePercent}% cohesion)`)}`
|
|
227
|
+
);
|
|
165
228
|
});
|
|
166
229
|
console.log();
|
|
167
230
|
}
|
|
@@ -171,12 +234,19 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
171
234
|
console.log(chalk.bold('šø Most Expensive Files (Context Budget):\n'));
|
|
172
235
|
summary.topExpensiveFiles.slice(0, 10).forEach((item) => {
|
|
173
236
|
const fileName = item.file.split('/').slice(-2).join('/');
|
|
174
|
-
const severityColor =
|
|
175
|
-
|
|
237
|
+
const severityColor =
|
|
238
|
+
item.severity === 'critical'
|
|
239
|
+
? chalk.red
|
|
240
|
+
: item.severity === 'major'
|
|
241
|
+
? chalk.yellow
|
|
242
|
+
: chalk.blue;
|
|
243
|
+
console.log(
|
|
244
|
+
` ${severityColor('ā')} ${chalk.white(fileName)} ${chalk.dim(`(${item.contextBudget.toLocaleString()} tokens)`)}`
|
|
245
|
+
);
|
|
176
246
|
});
|
|
177
247
|
console.log();
|
|
178
248
|
}
|
|
179
|
-
|
|
249
|
+
|
|
180
250
|
// Display score if calculated
|
|
181
251
|
if (contextScore) {
|
|
182
252
|
console.log(chalk.cyan(divider));
|
|
@@ -189,4 +259,4 @@ export async function contextAction(directory: string, options: ContextOptions)
|
|
|
189
259
|
} catch (error) {
|
|
190
260
|
handleCLIError(error, 'Context analysis');
|
|
191
261
|
}
|
|
192
|
-
}
|
|
262
|
+
}
|
|
@@ -8,7 +8,7 @@ import type { ToolScoringOutput } from '@aiready/core';
|
|
|
8
8
|
|
|
9
9
|
export async function depsHealthAction(
|
|
10
10
|
directory: string,
|
|
11
|
-
options: any
|
|
11
|
+
options: any
|
|
12
12
|
): Promise<ToolScoringOutput | undefined> {
|
|
13
13
|
const { analyzeDeps } = await import('@aiready/deps');
|
|
14
14
|
|
|
@@ -21,7 +21,8 @@ export async function depsHealthAction(
|
|
|
21
21
|
rootDir: directory,
|
|
22
22
|
include: options.include,
|
|
23
23
|
exclude: options.exclude,
|
|
24
|
-
trainingCutoffYear:
|
|
24
|
+
trainingCutoffYear:
|
|
25
|
+
options.trainingCutoffYear ?? merged.trainingCutoffYear ?? 2023,
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
const scoring: ToolScoringOutput = {
|
|
@@ -29,7 +30,11 @@ export async function depsHealthAction(
|
|
|
29
30
|
score: report.summary.score,
|
|
30
31
|
rawMetrics: report.rawData,
|
|
31
32
|
factors: [],
|
|
32
|
-
recommendations: report.recommendations.map((action: string) => ({
|
|
33
|
+
recommendations: report.recommendations.map((action: string) => ({
|
|
34
|
+
action,
|
|
35
|
+
estimatedImpact: 5,
|
|
36
|
+
priority: 'medium',
|
|
37
|
+
})),
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
if (options.output === 'json') {
|
|
@@ -37,7 +42,7 @@ export async function depsHealthAction(
|
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
const { summary } = report;
|
|
40
|
-
const ratingColors: Record<string,
|
|
45
|
+
const ratingColors: Record<string, (s: string) => string> = {
|
|
41
46
|
excellent: chalk.green,
|
|
42
47
|
good: chalk.blueBright,
|
|
43
48
|
moderate: chalk.yellow,
|
|
@@ -45,9 +50,13 @@ export async function depsHealthAction(
|
|
|
45
50
|
hazardous: chalk.bgRed.white,
|
|
46
51
|
};
|
|
47
52
|
const color = ratingColors[summary.rating] ?? chalk.white;
|
|
48
|
-
console.log(
|
|
53
|
+
console.log(
|
|
54
|
+
` š¦ Dependency Health: ${chalk.bold(scoring.score + '/100 health')} (${color(summary.rating)})`
|
|
55
|
+
);
|
|
49
56
|
if (report.issues.length > 0) {
|
|
50
|
-
console.log(
|
|
57
|
+
console.log(
|
|
58
|
+
chalk.dim(` Found ${report.issues.length} dependency issues.`)
|
|
59
|
+
);
|
|
51
60
|
} else {
|
|
52
61
|
console.log(chalk.dim(` Dependencies look healthy for AI assistance.`));
|
|
53
62
|
}
|
|
@@ -8,7 +8,7 @@ import type { ToolScoringOutput } from '@aiready/core';
|
|
|
8
8
|
|
|
9
9
|
export async function docDriftAction(
|
|
10
10
|
directory: string,
|
|
11
|
-
options: any
|
|
11
|
+
options: any
|
|
12
12
|
): Promise<ToolScoringOutput | undefined> {
|
|
13
13
|
const { analyzeDocDrift } = await import('@aiready/doc-drift');
|
|
14
14
|
|
|
@@ -29,7 +29,11 @@ export async function docDriftAction(
|
|
|
29
29
|
score: report.summary.score,
|
|
30
30
|
rawMetrics: report.rawData,
|
|
31
31
|
factors: [],
|
|
32
|
-
recommendations: report.recommendations.map((action: string) => ({
|
|
32
|
+
recommendations: report.recommendations.map((action: string) => ({
|
|
33
|
+
action,
|
|
34
|
+
estimatedImpact: 5,
|
|
35
|
+
priority: 'medium',
|
|
36
|
+
})),
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
if (options.output === 'json') {
|
|
@@ -37,7 +41,7 @@ export async function docDriftAction(
|
|
|
37
41
|
}
|
|
38
42
|
|
|
39
43
|
const { summary } = report;
|
|
40
|
-
const ratingColors: Record<string,
|
|
44
|
+
const ratingColors: Record<string, (s: string) => string> = {
|
|
41
45
|
minimal: chalk.green,
|
|
42
46
|
low: chalk.cyan,
|
|
43
47
|
moderate: chalk.yellow,
|
|
@@ -45,7 +49,9 @@ export async function docDriftAction(
|
|
|
45
49
|
severe: chalk.bgRed.white,
|
|
46
50
|
};
|
|
47
51
|
const color = ratingColors[summary.rating] ?? chalk.white;
|
|
48
|
-
console.log(
|
|
52
|
+
console.log(
|
|
53
|
+
` š Documentation Drift: ${chalk.bold(100 - scoring.score + '/100 health')} (${color(summary.rating)} risk)`
|
|
54
|
+
);
|
|
49
55
|
if (report.issues.length > 0) {
|
|
50
56
|
console.log(chalk.dim(` Found ${report.issues.length} drift issues.`));
|
|
51
57
|
} else {
|
package/src/commands/index.ts
CHANGED
|
@@ -6,8 +6,12 @@ export { scanAction, scanHelpText } from './scan';
|
|
|
6
6
|
export { patternsAction, patternsHelpText } from './patterns';
|
|
7
7
|
export { contextAction } from './context';
|
|
8
8
|
export { consistencyAction } from './consistency';
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
visualizeAction,
|
|
11
|
+
visualizeHelpText,
|
|
12
|
+
visualiseHelpText,
|
|
13
|
+
} from './visualize';
|
|
10
14
|
export { aiSignalClarityAction } from './ai-signal-clarity';
|
|
11
15
|
export { agentGroundingAction } from './agent-grounding';
|
|
12
16
|
export { testabilityAction } from './testability';
|
|
13
|
-
export { changeAmplificationAction } from './change-amplification';
|
|
17
|
+
export { changeAmplificationAction } from './change-amplification';
|
package/src/commands/patterns.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { resolve as resolvePath } from 'path';
|
|
7
|
-
import {
|
|
8
|
-
loadMergedConfig,
|
|
9
|
-
handleJSONOutput,
|
|
10
|
-
handleCLIError,
|
|
11
|
-
getElapsedTime,
|
|
7
|
+
import {
|
|
8
|
+
loadMergedConfig,
|
|
9
|
+
handleJSONOutput,
|
|
10
|
+
handleCLIError,
|
|
11
|
+
getElapsedTime,
|
|
12
12
|
resolveOutputPath,
|
|
13
13
|
formatToolScore,
|
|
14
14
|
} from '@aiready/core';
|
|
@@ -28,7 +28,10 @@ interface PatternsOptions {
|
|
|
28
28
|
score?: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export async function patternsAction(
|
|
31
|
+
export async function patternsAction(
|
|
32
|
+
directory: string,
|
|
33
|
+
options: PatternsOptions
|
|
34
|
+
) {
|
|
32
35
|
console.log(chalk.blue('š Analyzing patterns...\n'));
|
|
33
36
|
|
|
34
37
|
const startTime = Date.now();
|
|
@@ -57,7 +60,9 @@ export async function patternsAction(directory: string, options: PatternsOptions
|
|
|
57
60
|
|
|
58
61
|
// Load and merge config with CLI options
|
|
59
62
|
const cliOptions: any = {
|
|
60
|
-
minSimilarity: options.similarity
|
|
63
|
+
minSimilarity: options.similarity
|
|
64
|
+
? parseFloat(options.similarity)
|
|
65
|
+
: undefined,
|
|
61
66
|
minLines: options.minLines ? parseInt(options.minLines) : undefined,
|
|
62
67
|
useSmartDefaults,
|
|
63
68
|
include: options.include?.split(','),
|
|
@@ -72,22 +77,28 @@ export async function patternsAction(directory: string, options: PatternsOptions
|
|
|
72
77
|
cliOptions.minSharedTokens = parseInt(options.minSharedTokens);
|
|
73
78
|
}
|
|
74
79
|
|
|
75
|
-
const finalOptions = await loadMergedConfig(
|
|
80
|
+
const finalOptions = await loadMergedConfig(
|
|
81
|
+
resolvedDir,
|
|
82
|
+
defaults,
|
|
83
|
+
cliOptions
|
|
84
|
+
);
|
|
76
85
|
|
|
77
|
-
const { analyzePatterns, generateSummary, calculatePatternScore } =
|
|
86
|
+
const { analyzePatterns, generateSummary, calculatePatternScore } =
|
|
87
|
+
await import('@aiready/pattern-detect');
|
|
78
88
|
|
|
79
89
|
const { results, duplicates } = await analyzePatterns(finalOptions);
|
|
80
90
|
|
|
81
91
|
const elapsedTime = getElapsedTime(startTime);
|
|
82
92
|
const summary = generateSummary(results);
|
|
83
|
-
|
|
93
|
+
|
|
84
94
|
// Calculate score if requested
|
|
85
95
|
let patternScore: ToolScoringOutput | undefined;
|
|
86
96
|
if (options.score) {
|
|
87
97
|
patternScore = calculatePatternScore(duplicates, results.length);
|
|
88
98
|
}
|
|
89
99
|
|
|
90
|
-
const outputFormat =
|
|
100
|
+
const outputFormat =
|
|
101
|
+
options.output || finalOptions.output?.format || 'console';
|
|
91
102
|
const userOutputFile = options.outputFile || finalOptions.output?.file;
|
|
92
103
|
|
|
93
104
|
if (outputFormat === 'json') {
|
|
@@ -102,22 +113,38 @@ export async function patternsAction(directory: string, options: PatternsOptions
|
|
|
102
113
|
`aiready-report-${getReportTimestamp()}.json`,
|
|
103
114
|
resolvedDir
|
|
104
115
|
);
|
|
105
|
-
|
|
106
|
-
handleJSONOutput(
|
|
116
|
+
|
|
117
|
+
handleJSONOutput(
|
|
118
|
+
outputData,
|
|
119
|
+
outputPath,
|
|
120
|
+
`ā
Results saved to ${outputPath}`
|
|
121
|
+
);
|
|
107
122
|
} else {
|
|
108
123
|
// Console output - format to match standalone CLI
|
|
109
124
|
const terminalWidth = process.stdout.columns || 80;
|
|
110
125
|
const dividerWidth = Math.min(60, terminalWidth - 2);
|
|
111
126
|
const divider = 'ā'.repeat(dividerWidth);
|
|
112
|
-
|
|
127
|
+
|
|
113
128
|
console.log(chalk.cyan(divider));
|
|
114
129
|
console.log(chalk.bold.white(' PATTERN ANALYSIS SUMMARY'));
|
|
115
130
|
console.log(chalk.cyan(divider) + '\n');
|
|
116
131
|
|
|
117
|
-
console.log(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
console.log(
|
|
132
|
+
console.log(
|
|
133
|
+
chalk.white(`š Files analyzed: ${chalk.bold(results.length)}`)
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
chalk.yellow(
|
|
137
|
+
`ā Duplicate patterns found: ${chalk.bold(summary.totalPatterns)}`
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
console.log(
|
|
141
|
+
chalk.red(
|
|
142
|
+
`š° Token cost (wasted): ${chalk.bold(summary.totalTokenCost.toLocaleString())}`
|
|
143
|
+
)
|
|
144
|
+
);
|
|
145
|
+
console.log(
|
|
146
|
+
chalk.gray(`ā± Analysis time: ${chalk.bold(elapsedTime + 's')}`)
|
|
147
|
+
);
|
|
121
148
|
|
|
122
149
|
// Show breakdown by pattern type
|
|
123
150
|
const sortedTypes = Object.entries(summary.patternsByType || {})
|
|
@@ -145,18 +172,32 @@ export async function patternsAction(directory: string, options: PatternsOptions
|
|
|
145
172
|
.slice(0, 10);
|
|
146
173
|
|
|
147
174
|
topDuplicates.forEach((dup) => {
|
|
148
|
-
const severity =
|
|
149
|
-
|
|
175
|
+
const severity =
|
|
176
|
+
dup.similarity > 0.95
|
|
177
|
+
? 'CRITICAL'
|
|
178
|
+
: dup.similarity > 0.9
|
|
179
|
+
? 'HIGH'
|
|
180
|
+
: 'MEDIUM';
|
|
181
|
+
const severityIcon =
|
|
182
|
+
dup.similarity > 0.95 ? 'š“' : dup.similarity > 0.9 ? 'š”' : 'šµ';
|
|
150
183
|
const file1Name = dup.file1.split('/').pop() || dup.file1;
|
|
151
184
|
const file2Name = dup.file2.split('/').pop() || dup.file2;
|
|
152
|
-
console.log(
|
|
153
|
-
|
|
154
|
-
|
|
185
|
+
console.log(
|
|
186
|
+
`${severityIcon} ${severity}: ${chalk.bold(file1Name)} ā ${chalk.bold(file2Name)}`
|
|
187
|
+
);
|
|
188
|
+
console.log(
|
|
189
|
+
` Similarity: ${chalk.bold(Math.round(dup.similarity * 100) + '%')} | Wasted: ${chalk.bold(dup.tokenCost.toLocaleString())} tokens each`
|
|
190
|
+
);
|
|
191
|
+
console.log(
|
|
192
|
+
` Lines: ${chalk.cyan(dup.line1 + '-' + dup.endLine1)} ā ${chalk.cyan(dup.line2 + '-' + dup.endLine2)}\n`
|
|
193
|
+
);
|
|
155
194
|
});
|
|
156
195
|
} else {
|
|
157
|
-
console.log(
|
|
196
|
+
console.log(
|
|
197
|
+
chalk.green('\n⨠Great! No duplicate patterns detected.\n')
|
|
198
|
+
);
|
|
158
199
|
}
|
|
159
|
-
|
|
200
|
+
|
|
160
201
|
// Display score if calculated
|
|
161
202
|
if (patternScore) {
|
|
162
203
|
console.log(chalk.cyan(divider));
|
|
@@ -176,4 +217,4 @@ EXAMPLES:
|
|
|
176
217
|
$ aiready patterns # Default analysis
|
|
177
218
|
$ aiready patterns --similarity 0.6 # Stricter matching
|
|
178
219
|
$ aiready patterns --min-lines 10 # Larger patterns only
|
|
179
|
-
`;
|
|
220
|
+
`;
|