@girardelli/architect 2.2.0 → 5.0.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/README.md +105 -116
- package/architect-run.sh +431 -0
- package/assets/banner-v3.html +561 -0
- package/dist/agent-generator/context-enricher.d.ts +58 -0
- package/dist/agent-generator/context-enricher.d.ts.map +1 -0
- package/dist/agent-generator/context-enricher.js +613 -0
- package/dist/agent-generator/context-enricher.js.map +1 -0
- package/dist/agent-generator/domain-inferrer.d.ts +52 -0
- package/dist/agent-generator/domain-inferrer.d.ts.map +1 -0
- package/dist/agent-generator/domain-inferrer.js +585 -0
- package/dist/agent-generator/domain-inferrer.js.map +1 -0
- package/dist/agent-generator/framework-detector.d.ts +40 -0
- package/dist/agent-generator/framework-detector.d.ts.map +1 -0
- package/dist/agent-generator/framework-detector.js +611 -0
- package/dist/agent-generator/framework-detector.js.map +1 -0
- package/dist/agent-generator/index.d.ts +47 -0
- package/dist/agent-generator/index.d.ts.map +1 -0
- package/dist/agent-generator/index.js +545 -0
- package/dist/agent-generator/index.js.map +1 -0
- package/dist/agent-generator/stack-detector.d.ts +14 -0
- package/dist/agent-generator/stack-detector.d.ts.map +1 -0
- package/dist/agent-generator/stack-detector.js +124 -0
- package/dist/agent-generator/stack-detector.js.map +1 -0
- package/dist/agent-generator/templates/core/agents.d.ts +17 -0
- package/dist/agent-generator/templates/core/agents.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/agents.js +1256 -0
- package/dist/agent-generator/templates/core/agents.js.map +1 -0
- package/dist/agent-generator/templates/core/architecture-rules.d.ts +7 -0
- package/dist/agent-generator/templates/core/architecture-rules.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/architecture-rules.js +274 -0
- package/dist/agent-generator/templates/core/architecture-rules.js.map +1 -0
- package/dist/agent-generator/templates/core/general-rules.d.ts +8 -0
- package/dist/agent-generator/templates/core/general-rules.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/general-rules.js +301 -0
- package/dist/agent-generator/templates/core/general-rules.js.map +1 -0
- package/dist/agent-generator/templates/core/hooks-generator.d.ts +21 -0
- package/dist/agent-generator/templates/core/hooks-generator.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/hooks-generator.js +233 -0
- package/dist/agent-generator/templates/core/hooks-generator.js.map +1 -0
- package/dist/agent-generator/templates/core/index-md.d.ts +7 -0
- package/dist/agent-generator/templates/core/index-md.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/index-md.js +246 -0
- package/dist/agent-generator/templates/core/index-md.js.map +1 -0
- package/dist/agent-generator/templates/core/orchestrator.d.ts +8 -0
- package/dist/agent-generator/templates/core/orchestrator.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/orchestrator.js +422 -0
- package/dist/agent-generator/templates/core/orchestrator.js.map +1 -0
- package/dist/agent-generator/templates/core/preflight.d.ts +8 -0
- package/dist/agent-generator/templates/core/preflight.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/preflight.js +213 -0
- package/dist/agent-generator/templates/core/preflight.js.map +1 -0
- package/dist/agent-generator/templates/core/quality-gates.d.ts +11 -0
- package/dist/agent-generator/templates/core/quality-gates.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/quality-gates.js +254 -0
- package/dist/agent-generator/templates/core/quality-gates.js.map +1 -0
- package/dist/agent-generator/templates/core/security-rules.d.ts +7 -0
- package/dist/agent-generator/templates/core/security-rules.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/security-rules.js +528 -0
- package/dist/agent-generator/templates/core/security-rules.js.map +1 -0
- package/dist/agent-generator/templates/core/skills-generator.d.ts +19 -0
- package/dist/agent-generator/templates/core/skills-generator.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/skills-generator.js +546 -0
- package/dist/agent-generator/templates/core/skills-generator.js.map +1 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts +7 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.js +237 -0
- package/dist/agent-generator/templates/core/workflow-fix-bug.js.map +1 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.d.ts +8 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.js +321 -0
- package/dist/agent-generator/templates/core/workflow-new-feature.js.map +1 -0
- package/dist/agent-generator/templates/core/workflow-review.d.ts +7 -0
- package/dist/agent-generator/templates/core/workflow-review.d.ts.map +1 -0
- package/dist/agent-generator/templates/core/workflow-review.js +104 -0
- package/dist/agent-generator/templates/core/workflow-review.js.map +1 -0
- package/dist/agent-generator/templates/domain/index.d.ts +22 -0
- package/dist/agent-generator/templates/domain/index.d.ts.map +1 -0
- package/dist/agent-generator/templates/domain/index.js +1176 -0
- package/dist/agent-generator/templates/domain/index.js.map +1 -0
- package/dist/agent-generator/templates/stack/index.d.ts +8 -0
- package/dist/agent-generator/templates/stack/index.d.ts.map +1 -0
- package/dist/agent-generator/templates/stack/index.js +695 -0
- package/dist/agent-generator/templates/stack/index.js.map +1 -0
- package/dist/agent-generator/templates/template-helpers.d.ts +75 -0
- package/dist/agent-generator/templates/template-helpers.d.ts.map +1 -0
- package/dist/agent-generator/templates/template-helpers.js +726 -0
- package/dist/agent-generator/templates/template-helpers.js.map +1 -0
- package/dist/agent-generator/types.d.ts +196 -0
- package/dist/agent-generator/types.d.ts.map +1 -0
- package/dist/agent-generator/types.js +27 -0
- package/dist/agent-generator/types.js.map +1 -0
- package/dist/analyzer.d.ts +5 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +46 -5
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzers/forecast.d.ts +85 -0
- package/dist/analyzers/forecast.d.ts.map +1 -0
- package/dist/analyzers/forecast.js +337 -0
- package/dist/analyzers/forecast.js.map +1 -0
- package/dist/analyzers/git-cache.d.ts +7 -0
- package/dist/analyzers/git-cache.d.ts.map +1 -0
- package/dist/analyzers/git-cache.js +41 -0
- package/dist/analyzers/git-cache.js.map +1 -0
- package/dist/analyzers/git-history.d.ts +113 -0
- package/dist/analyzers/git-history.d.ts.map +1 -0
- package/dist/analyzers/git-history.js +333 -0
- package/dist/analyzers/git-history.js.map +1 -0
- package/dist/analyzers/index.d.ts +10 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +7 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/temporal-scorer.d.ts +72 -0
- package/dist/analyzers/temporal-scorer.d.ts.map +1 -0
- package/dist/analyzers/temporal-scorer.js +140 -0
- package/dist/analyzers/temporal-scorer.js.map +1 -0
- package/dist/anti-patterns.d.ts +7 -0
- package/dist/anti-patterns.d.ts.map +1 -1
- package/dist/anti-patterns.js +25 -6
- package/dist/anti-patterns.js.map +1 -1
- package/dist/cli.d.ts +2 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +275 -113
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +48 -11
- package/dist/config.js.map +1 -1
- package/dist/html-reporter.d.ts +3 -1
- package/dist/html-reporter.d.ts.map +1 -1
- package/dist/html-reporter.js +248 -12
- package/dist/html-reporter.js.map +1 -1
- package/dist/index.d.ts +16 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -4
- package/dist/index.js.map +1 -1
- package/dist/project-summarizer.d.ts +38 -0
- package/dist/project-summarizer.d.ts.map +1 -0
- package/dist/project-summarizer.js +463 -0
- package/dist/project-summarizer.js.map +1 -0
- package/dist/refactor-reporter.js +1 -1
- package/dist/scanner.d.ts +8 -2
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +153 -113
- package/dist/scanner.js.map +1 -1
- package/dist/scorer.d.ts.map +1 -1
- package/dist/scorer.js +24 -11
- package/dist/scorer.js.map +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -3
- package/src/agent-generator/context-enricher.ts +672 -0
- package/src/agent-generator/domain-inferrer.ts +635 -0
- package/src/agent-generator/framework-detector.ts +669 -0
- package/src/agent-generator/index.ts +634 -0
- package/src/agent-generator/stack-detector.ts +115 -0
- package/src/agent-generator/templates/core/agents.ts +1296 -0
- package/src/agent-generator/templates/core/architecture-rules.ts +287 -0
- package/src/agent-generator/templates/core/general-rules.ts +306 -0
- package/src/agent-generator/templates/core/hooks-generator.ts +242 -0
- package/src/agent-generator/templates/core/index-md.ts +260 -0
- package/src/agent-generator/templates/core/orchestrator.ts +459 -0
- package/src/agent-generator/templates/core/preflight.ts +215 -0
- package/src/agent-generator/templates/core/quality-gates.ts +256 -0
- package/src/agent-generator/templates/core/security-rules.ts +543 -0
- package/src/agent-generator/templates/core/skills-generator.ts +585 -0
- package/src/agent-generator/templates/core/workflow-fix-bug.ts +239 -0
- package/src/agent-generator/templates/core/workflow-new-feature.ts +323 -0
- package/src/agent-generator/templates/core/workflow-review.ts +106 -0
- package/src/agent-generator/templates/domain/index.ts +1201 -0
- package/src/agent-generator/templates/stack/index.ts +705 -0
- package/src/agent-generator/templates/template-helpers.ts +776 -0
- package/src/agent-generator/types.ts +232 -0
- package/src/analyzer.ts +51 -5
- package/src/analyzers/forecast.ts +496 -0
- package/src/analyzers/git-cache.ts +52 -0
- package/src/analyzers/git-history.ts +488 -0
- package/src/analyzers/index.ts +33 -0
- package/src/analyzers/temporal-scorer.ts +227 -0
- package/src/anti-patterns.ts +29 -6
- package/src/cli.ts +316 -117
- package/src/config.ts +52 -11
- package/src/html-reporter.ts +263 -13
- package/src/index.ts +93 -10
- package/src/project-summarizer.ts +521 -0
- package/src/refactor-reporter.ts +1 -1
- package/src/scanner.ts +136 -90
- package/src/scorer.ts +26 -11
- package/src/types.ts +27 -0
- package/tests/agent-generator.test.ts +427 -0
- package/tests/analyzers-integration.test.ts +174 -0
- package/tests/architect-adapter-enrichment.test.ts +9 -0
- package/tests/context-enricher.test.ts +971 -0
- package/tests/fixtures/monorepo/package.json +6 -0
- package/tests/fixtures/monorepo/packages/app/package.json +12 -0
- package/tests/fixtures/monorepo/packages/app/src/index.ts +6 -0
- package/tests/fixtures/monorepo/packages/core/package.json +7 -0
- package/tests/fixtures/monorepo/packages/core/src/index.ts +7 -0
- package/tests/forecast.test.ts +509 -0
- package/tests/framework-detector.test.ts +1172 -0
- package/tests/git-history.test.ts +254 -0
- package/tests/monorepo-scan.test.ts +170 -0
- package/tests/scanner.test.ts +7 -8
- package/tests/scorer.test.ts +594 -0
- package/tests/stack-detector.test.ts +241 -0
- package/tests/template-generation.test.ts +706 -0
- package/tests/template-helpers.test.ts +1152 -0
- package/tests/temporal-scorer.test.ts +307 -0
- package/dist/agent-generator.d.ts +0 -106
- package/dist/agent-generator.d.ts.map +0 -1
- package/dist/agent-generator.js +0 -1398
- package/dist/agent-generator.js.map +0 -1
- package/src/agent-generator.ts +0 -1526
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporal Score Dimension — Adds time-series awareness to architecture scoring
|
|
3
|
+
*
|
|
4
|
+
* Combines current static score with historical velocity to produce:
|
|
5
|
+
* - Trend per module (improving / stable / degrading)
|
|
6
|
+
* - Temporal risk score (static score penalized by negative velocity)
|
|
7
|
+
* - Projected score in N weeks
|
|
8
|
+
*
|
|
9
|
+
* @author Camilo Girardelli — Girardelli Tecnologia
|
|
10
|
+
* @license MIT
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
ModuleHistory,
|
|
15
|
+
VelocityVector,
|
|
16
|
+
GitHistoryReport,
|
|
17
|
+
} from './git-history.js';
|
|
18
|
+
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════
|
|
20
|
+
// TYPES
|
|
21
|
+
// ═══════════════════════════════════════════════════════════════
|
|
22
|
+
|
|
23
|
+
export type Trend = 'improving' | 'stable' | 'degrading';
|
|
24
|
+
|
|
25
|
+
export interface TemporalScore {
|
|
26
|
+
/** Module or file path */
|
|
27
|
+
module: string;
|
|
28
|
+
/** Current static score (from ArchitectureScorer) */
|
|
29
|
+
staticScore: number;
|
|
30
|
+
/** Temporal-adjusted score (penalizes degrading trends) */
|
|
31
|
+
temporalScore: number;
|
|
32
|
+
/** Direction of change */
|
|
33
|
+
trend: Trend;
|
|
34
|
+
/** Projected score in projectionWeeks */
|
|
35
|
+
projectedScore: number;
|
|
36
|
+
/** Confidence in the projection (0-1) */
|
|
37
|
+
projectionConfidence: number;
|
|
38
|
+
/** Weeks used for projection */
|
|
39
|
+
projectionWeeks: number;
|
|
40
|
+
/** Risk level derived from temporal analysis */
|
|
41
|
+
riskLevel: 'low' | 'medium' | 'high' | 'critical';
|
|
42
|
+
/** Velocity data */
|
|
43
|
+
velocity: VelocityVector;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TemporalReport {
|
|
47
|
+
projectPath: string;
|
|
48
|
+
analyzedAt: string;
|
|
49
|
+
overallTrend: Trend;
|
|
50
|
+
overallTemporalScore: number;
|
|
51
|
+
modules: TemporalScore[];
|
|
52
|
+
degradingModules: TemporalScore[];
|
|
53
|
+
improvingModules: TemporalScore[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface TemporalScorerConfig {
|
|
57
|
+
/** Weeks ahead to project (default: 12) */
|
|
58
|
+
projectionWeeks?: number;
|
|
59
|
+
/** Weight of churn trend in temporal penalty (0-1, default: 0.6) */
|
|
60
|
+
churnWeight?: number;
|
|
61
|
+
/** Weight of commit acceleration in temporal penalty (0-1, default: 0.4) */
|
|
62
|
+
commitWeight?: number;
|
|
63
|
+
/** Threshold for trend classification: accelerating if > threshold % */
|
|
64
|
+
acceleratingThreshold?: number;
|
|
65
|
+
/** Threshold for trend classification: decelerating if < -threshold % */
|
|
66
|
+
deceleratingThreshold?: number;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const DEFAULT_CONFIG: Required<TemporalScorerConfig> = {
|
|
70
|
+
projectionWeeks: 12,
|
|
71
|
+
churnWeight: 0.6,
|
|
72
|
+
commitWeight: 0.4,
|
|
73
|
+
acceleratingThreshold: 15,
|
|
74
|
+
deceleratingThreshold: -15,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// ═══════════════════════════════════════════════════════════════
|
|
78
|
+
// TEMPORAL SCORER
|
|
79
|
+
// ═══════════════════════════════════════════════════════════════
|
|
80
|
+
|
|
81
|
+
export class TemporalScorer {
|
|
82
|
+
private config: Required<TemporalScorerConfig>;
|
|
83
|
+
|
|
84
|
+
constructor(config?: TemporalScorerConfig) {
|
|
85
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Score modules temporally using git history + static scores.
|
|
90
|
+
*
|
|
91
|
+
* @param gitReport - Output from GitHistoryAnalyzer
|
|
92
|
+
* @param staticScores - Map of modulePath → static score (0-100)
|
|
93
|
+
*/
|
|
94
|
+
score(
|
|
95
|
+
gitReport: GitHistoryReport,
|
|
96
|
+
staticScores: Map<string, number>,
|
|
97
|
+
): TemporalReport {
|
|
98
|
+
const modules: TemporalScore[] = [];
|
|
99
|
+
|
|
100
|
+
for (const moduleHistory of gitReport.modules) {
|
|
101
|
+
const staticScore = staticScores.get(moduleHistory.modulePath)
|
|
102
|
+
?? this.inferStaticScore(moduleHistory);
|
|
103
|
+
|
|
104
|
+
const ts = this.scoreModule(moduleHistory, staticScore);
|
|
105
|
+
modules.push(ts);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Sort by risk (worst first)
|
|
109
|
+
modules.sort((a, b) => a.temporalScore - b.temporalScore);
|
|
110
|
+
|
|
111
|
+
const degrading = modules.filter(m => m.trend === 'degrading');
|
|
112
|
+
const improving = modules.filter(m => m.trend === 'improving');
|
|
113
|
+
|
|
114
|
+
const overallTrend = this.classifyOverallTrend(modules);
|
|
115
|
+
const overallScore = modules.length > 0
|
|
116
|
+
? Math.round(modules.reduce((s, m) => s + m.temporalScore, 0) / modules.length)
|
|
117
|
+
: 0;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
projectPath: gitReport.projectPath,
|
|
121
|
+
analyzedAt: new Date().toISOString(),
|
|
122
|
+
overallTrend: overallTrend,
|
|
123
|
+
overallTemporalScore: overallScore,
|
|
124
|
+
modules,
|
|
125
|
+
degradingModules: degrading,
|
|
126
|
+
improvingModules: improving,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private scoreModule(module: ModuleHistory, staticScore: number): TemporalScore {
|
|
131
|
+
const velocity = module.velocityVector;
|
|
132
|
+
|
|
133
|
+
// Calculate temporal penalty based on velocity
|
|
134
|
+
const churnPenalty = velocity.churnTrend > 0
|
|
135
|
+
? velocity.churnTrend * this.config.churnWeight * 0.3 // 30% impact per 100% churn increase
|
|
136
|
+
: velocity.churnTrend * this.config.churnWeight * 0.1; // 10% bonus per 100% churn decrease
|
|
137
|
+
|
|
138
|
+
const commitPenalty = velocity.commitAcceleration > 20
|
|
139
|
+
? (velocity.commitAcceleration - 20) * this.config.commitWeight * 0.2 // penalty for excessive churn
|
|
140
|
+
: 0;
|
|
141
|
+
|
|
142
|
+
const totalPenalty = Math.max(-20, Math.min(30, churnPenalty + commitPenalty));
|
|
143
|
+
const temporalScore = Math.max(0, Math.min(100, Math.round(staticScore - totalPenalty)));
|
|
144
|
+
|
|
145
|
+
// Trend classification
|
|
146
|
+
const trend = this.classifyTrend(velocity);
|
|
147
|
+
|
|
148
|
+
// Linear projection
|
|
149
|
+
const weeklyDelta = totalPenalty / Math.max(this.config.projectionWeeks, 1);
|
|
150
|
+
const projectedScore = Math.max(0, Math.min(100,
|
|
151
|
+
Math.round(temporalScore - (weeklyDelta * this.config.projectionWeeks))
|
|
152
|
+
));
|
|
153
|
+
|
|
154
|
+
// Confidence decreases with projection distance and instability
|
|
155
|
+
const instability = Math.abs(velocity.churnTrend) + Math.abs(velocity.commitAcceleration);
|
|
156
|
+
const projectionConfidence = Math.max(0.1, Math.min(1,
|
|
157
|
+
1 - (instability / 200) - (this.config.projectionWeeks / 52)
|
|
158
|
+
));
|
|
159
|
+
|
|
160
|
+
const riskLevel = this.classifyRisk(temporalScore, trend, module.busFactor);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
module: module.modulePath,
|
|
164
|
+
staticScore,
|
|
165
|
+
temporalScore,
|
|
166
|
+
trend,
|
|
167
|
+
projectedScore,
|
|
168
|
+
projectionConfidence: Math.round(projectionConfidence * 100) / 100,
|
|
169
|
+
projectionWeeks: this.config.projectionWeeks,
|
|
170
|
+
riskLevel,
|
|
171
|
+
velocity,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private classifyTrend(velocity: VelocityVector): Trend {
|
|
176
|
+
// Degrading: churn increasing significantly or commit acceleration very high
|
|
177
|
+
if (velocity.churnTrend > 30 || velocity.commitAcceleration > 50) {
|
|
178
|
+
return 'degrading';
|
|
179
|
+
}
|
|
180
|
+
// Improving: churn decreasing and stable or decelerating
|
|
181
|
+
if (velocity.churnTrend < -10 && velocity.direction !== 'accelerating') {
|
|
182
|
+
return 'improving';
|
|
183
|
+
}
|
|
184
|
+
return 'stable';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private classifyRisk(
|
|
188
|
+
temporalScore: number,
|
|
189
|
+
trend: Trend,
|
|
190
|
+
busFactor: number,
|
|
191
|
+
): TemporalScore['riskLevel'] {
|
|
192
|
+
if (temporalScore < 30 || (temporalScore < 50 && trend === 'degrading')) {
|
|
193
|
+
return 'critical';
|
|
194
|
+
}
|
|
195
|
+
if (temporalScore < 50 || (trend === 'degrading' && busFactor <= 1)) {
|
|
196
|
+
return 'high';
|
|
197
|
+
}
|
|
198
|
+
if (temporalScore < 70 || trend === 'degrading') {
|
|
199
|
+
return 'medium';
|
|
200
|
+
}
|
|
201
|
+
return 'low';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private classifyOverallTrend(modules: TemporalScore[]): Trend {
|
|
205
|
+
if (modules.length === 0) return 'stable';
|
|
206
|
+
|
|
207
|
+
const degrading = modules.filter(m => m.trend === 'degrading').length;
|
|
208
|
+
const improving = modules.filter(m => m.trend === 'improving').length;
|
|
209
|
+
|
|
210
|
+
const degradingRatio = degrading / modules.length;
|
|
211
|
+
const improvingRatio = improving / modules.length;
|
|
212
|
+
|
|
213
|
+
if (degradingRatio > 0.3) return 'degrading';
|
|
214
|
+
if (improvingRatio > 0.3) return 'improving';
|
|
215
|
+
return 'stable';
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Infer a static score when none is provided (based on churn metrics) */
|
|
219
|
+
private inferStaticScore(module: ModuleHistory): number {
|
|
220
|
+
const avgChurn = module.aggregateChurn / Math.max(module.aggregateCommits, 1);
|
|
221
|
+
if (avgChurn < 20) return 85;
|
|
222
|
+
if (avgChurn < 50) return 75;
|
|
223
|
+
if (avgChurn < 100) return 65;
|
|
224
|
+
if (avgChurn < 200) return 50;
|
|
225
|
+
return 35;
|
|
226
|
+
}
|
|
227
|
+
}
|
package/src/anti-patterns.ts
CHANGED
|
@@ -5,11 +5,28 @@ export class AntiPatternDetector {
|
|
|
5
5
|
private config: ArchitectConfig;
|
|
6
6
|
private dependencyGraph: Map<string, Set<string>>;
|
|
7
7
|
|
|
8
|
+
/** Paths that indicate third-party or build artifacts — never report anti-patterns here */
|
|
9
|
+
private static readonly EXCLUDED_PATH_SEGMENTS = [
|
|
10
|
+
'node_modules', '/dist/', '/build/', '/coverage/',
|
|
11
|
+
'/.next/', '/venv/', '/__pycache__/', '/target/',
|
|
12
|
+
];
|
|
13
|
+
|
|
8
14
|
constructor(config: ArchitectConfig) {
|
|
9
15
|
this.config = config;
|
|
10
16
|
this.dependencyGraph = new Map();
|
|
11
17
|
}
|
|
12
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Check if a file path belongs to the project's own source code.
|
|
21
|
+
* Returns false for node_modules, dist, build artifacts, etc.
|
|
22
|
+
*/
|
|
23
|
+
private isProjectFile(filePath: string): boolean {
|
|
24
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
25
|
+
return !AntiPatternDetector.EXCLUDED_PATH_SEGMENTS.some(seg =>
|
|
26
|
+
normalized.includes(seg)
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
13
30
|
detect(
|
|
14
31
|
fileTree: FileNode,
|
|
15
32
|
dependencies: Map<string, Set<string>>
|
|
@@ -42,7 +59,7 @@ export class AntiPatternDetector {
|
|
|
42
59
|
this.config.antiPatterns?.godClass?.methodsThreshold || 10;
|
|
43
60
|
|
|
44
61
|
this.walkFileTree(node, (file) => {
|
|
45
|
-
if (file.type === 'file' && (file.lines || 0) > threshold) {
|
|
62
|
+
if (file.type === 'file' && (file.lines || 0) > threshold && this.isProjectFile(file.path)) {
|
|
46
63
|
const methods = this.countMethods(file.path);
|
|
47
64
|
if (methods > methodThreshold) {
|
|
48
65
|
patterns.push({
|
|
@@ -70,9 +87,12 @@ export class AntiPatternDetector {
|
|
|
70
87
|
const recursionStack = new Set<string>();
|
|
71
88
|
|
|
72
89
|
for (const file of this.dependencyGraph.keys()) {
|
|
90
|
+
// Only check cycles starting from project files
|
|
91
|
+
if (!this.isProjectFile(file)) continue;
|
|
92
|
+
|
|
73
93
|
if (!visited.has(file)) {
|
|
74
94
|
const cycle = this.findCycle(file, visited, recursionStack);
|
|
75
|
-
if (cycle) {
|
|
95
|
+
if (cycle && cycle.every(f => this.isProjectFile(f))) {
|
|
76
96
|
patterns.push({
|
|
77
97
|
name: 'Circular Dependency',
|
|
78
98
|
severity: 'HIGH',
|
|
@@ -118,7 +138,7 @@ export class AntiPatternDetector {
|
|
|
118
138
|
const patterns: AntiPattern[] = [];
|
|
119
139
|
|
|
120
140
|
this.walkFileTree(node, (file) => {
|
|
121
|
-
if (file.type === 'file') {
|
|
141
|
+
if (file.type === 'file' && this.isProjectFile(file.path)) {
|
|
122
142
|
const internalExports = this.countInternalExports(file.path);
|
|
123
143
|
if (internalExports > 5) {
|
|
124
144
|
patterns.push({
|
|
@@ -146,13 +166,13 @@ export class AntiPatternDetector {
|
|
|
146
166
|
const patterns: AntiPattern[] = [];
|
|
147
167
|
|
|
148
168
|
this.walkFileTree(node, (file) => {
|
|
149
|
-
if (file.type === 'file') {
|
|
169
|
+
if (file.type === 'file' && this.isProjectFile(file.path)) {
|
|
150
170
|
const externalMethodCalls = (dependencies.get(file.path) || new Set())
|
|
151
171
|
.size;
|
|
152
172
|
const internalMethods = this.countMethods(file.path);
|
|
153
173
|
const name = file.name.toLowerCase();
|
|
154
174
|
|
|
155
|
-
// Skip
|
|
175
|
+
// Skip infrastructure files where external deps are by design
|
|
156
176
|
const isInfraFile =
|
|
157
177
|
name.endsWith('.module.ts') ||
|
|
158
178
|
name.endsWith('.dto.ts') ||
|
|
@@ -194,6 +214,9 @@ export class AntiPatternDetector {
|
|
|
194
214
|
?.changePropagationThreshold || 8;
|
|
195
215
|
|
|
196
216
|
for (const [file, dependents] of dependencies) {
|
|
217
|
+
// Only report for project files
|
|
218
|
+
if (!this.isProjectFile(file)) continue;
|
|
219
|
+
|
|
197
220
|
if (dependents.size >= threshold) {
|
|
198
221
|
patterns.push({
|
|
199
222
|
name: 'Shotgun Surgery',
|
|
@@ -202,7 +225,7 @@ export class AntiPatternDetector {
|
|
|
202
225
|
description: `Changes to this file likely require modifications in ${dependents.size} other files`,
|
|
203
226
|
suggestion:
|
|
204
227
|
'Refactor to reduce coupling and consolidate related functionality into modules',
|
|
205
|
-
affectedFiles: Array.from(dependents),
|
|
228
|
+
affectedFiles: Array.from(dependents).filter(f => this.isProjectFile(f)),
|
|
206
229
|
metrics: {
|
|
207
230
|
dependentFileCount: dependents.size,
|
|
208
231
|
},
|