Skip to content

Commit df6bdb4

Browse files
committed
feature: run hot spots analysis
1 parent fc755c4 commit df6bdb4

File tree

5 files changed

+114
-31
lines changed

5 files changed

+114
-31
lines changed

README.md

+34
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,40 @@ CLI wrapper around https://github.com/anton-107/codebase-stats-collector
44

55
## Usage
66

7+
### hot-spots
8+
9+
Identifies high-churn areas in your codebase by analyzing file modification patterns.
10+
This command helps teams discover files that require frequent changes, potentially indicating design issues,
11+
technical debt, or areas needing refactoring.
12+
It analyzes your repository's history to highlight files with the highest number of changes.
13+
These frequently modified files often represent:
14+
15+
- Areas of code that may be unstable or poorly designed
16+
- Features that experience constant requirement changes
17+
- Components that might benefit from refactoring
18+
- Potential technical debt that needs attention
19+
20+
To run:
21+
22+
```
23+
npm run cli -- hot-spots <PATH TO LOCAL GIT REPO>
24+
```
25+
26+
### knowledge-gaps
27+
28+
Identifies potential knowledge gaps in your codebase by analyzing file-level contribution patterns.
29+
This command helps teams identify files that may represent single points of failure due to limited contributor coverage.
30+
It scans your repository and generates a report of files sorted by their last modification date,
31+
highlighting those with the fewest unique contributors.
32+
33+
This helps identify:
34+
35+
- Files that may become maintenance bottlenecks
36+
- Code that could benefit from knowledge sharing
37+
- Areas where code reviews and pair programming should be prioritized
38+
39+
To run:
40+
741
```
842
npm run cli -- knowledge-gaps <PATH TO LOCAL GIT REPO> --ignoreFiles /path/to/ignored/folder
943
```

src/cli.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { cli, command } from "cleye";
22

3+
import { collectHotSpots } from "./commands/hot-spots.js";
34
import { collectKnowledgeGaps } from "./commands/knowledge-gaps.js";
45
import { log } from "./console.js";
56

@@ -8,6 +9,17 @@ export class CommandLineInterface {
89
public run() {
910
cli({
1011
commands: [
12+
command(
13+
{
14+
name: "hot-spots",
15+
parameters: ["<path to repository>"],
16+
},
17+
async (argv) => {
18+
const dir = argv._.pathToRepository;
19+
log("Collecting files with most changes", dir);
20+
await collectHotSpots(dir);
21+
},
22+
),
1123
command(
1224
{
1325
name: "knowledge-gaps",
@@ -21,7 +33,7 @@ export class CommandLineInterface {
2133
},
2234
async (argv) => {
2335
const dir = argv._.pathToRepository;
24-
log("Calculating files with least number of contributors", dir);
36+
log("Collecting files with least number of contributors", dir);
2537
let ignoreFilesPattern: string | null = null;
2638
if (argv.flags.ignoreFiles) {
2739
// eslint-disable-next-line no-console

src/commands/hot-spots.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { SummaryDashboard } from "codebase-stats-collector/dist/dashboard/summary-dashboard.js";
2+
import { GitRepository } from "codebase-stats-collector/dist/git-reader/git-repository.js";
3+
import { getNumberOfChangesPerFile } from "codebase-stats-collector/dist/stats/number-of-changes-per-file.js";
4+
5+
import { log } from "../console.js";
6+
import { setupProgressStream } from "../dashboard/progress.js";
7+
8+
export async function collectHotSpots(dir: string): Promise<void> {
9+
// initialize repo
10+
const repo = new GitRepository(dir);
11+
12+
// initialize summary dashboard:
13+
const summaryDashboard = new SummaryDashboard([]);
14+
summaryDashboard.startProgress();
15+
16+
const commitsStream = setupProgressStream(summaryDashboard);
17+
18+
// number of total commits:
19+
const commits = await repo.getListOfCommits();
20+
summaryDashboard.setCommits(commits);
21+
22+
const commitsWithChangedFiles = await repo.getListOfCommitsWithChangedFiles({
23+
stream: commitsStream,
24+
});
25+
26+
const commitsPerFile = getNumberOfChangesPerFile(commitsWithChangedFiles);
27+
const data = Object.keys(commitsPerFile).map((x) => {
28+
return [x, commitsPerFile[x]];
29+
});
30+
data.sort((a, b) => Number(b[1]) - Number(a[1]));
31+
const hotFiles = data.slice(0, 50);
32+
log("hot files (files with most changes)", hotFiles);
33+
}

src/commands/knowledge-gaps.ts

+1-30
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,9 @@
1-
import { debug } from "codebase-stats-collector/dist/console/console.js";
21
import { SummaryDashboard } from "codebase-stats-collector/dist/dashboard/summary-dashboard.js";
32
import { GitRepository } from "codebase-stats-collector/dist/git-reader/git-repository.js";
4-
import { ExpandedCommit } from "codebase-stats-collector/dist/interfaces.js";
53
import { getNumberOfContributorsPerFile } from "codebase-stats-collector/dist/stats/number-of-contributors-per-file.js";
6-
import { Readable } from "stream";
74

85
import { log } from "../console.js";
9-
10-
function setupProgressStream(summaryDashboard: SummaryDashboard): Readable {
11-
let commitsCounter = 0;
12-
13-
const commitsStream = new Readable({
14-
objectMode: true,
15-
read() {
16-
// do nothing.
17-
},
18-
});
19-
commitsStream.on("data", (commit: ExpandedCommit) => {
20-
debug("Commit", commit);
21-
22-
commitsCounter += 1;
23-
summaryDashboard.setCurrentProgress(commitsCounter, commit);
24-
});
25-
commitsStream.on("error", (err) => {
26-
debug("error reading commits", { err });
27-
});
28-
commitsStream.on("end", () => {
29-
debug("done reading commits", {});
30-
});
31-
commitsStream.on("close", () => {
32-
debug("stream closed", {});
33-
});
34-
return commitsStream;
35-
}
6+
import { setupProgressStream } from "../dashboard/progress.js";
367

378
export async function collectKnowledgeGaps(
389
dir: string,

src/dashboard/progress.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { debug } from "codebase-stats-collector/dist/console/console.js";
2+
import { SummaryDashboard } from "codebase-stats-collector/dist/dashboard/summary-dashboard.js";
3+
import { ExpandedCommit } from "codebase-stats-collector/dist/interfaces.js";
4+
import { Readable } from "stream";
5+
6+
export function setupProgressStream(
7+
summaryDashboard: SummaryDashboard,
8+
): Readable {
9+
let commitsCounter = 0;
10+
11+
const commitsStream = new Readable({
12+
objectMode: true,
13+
read() {
14+
// do nothing.
15+
},
16+
});
17+
commitsStream.on("data", (commit: ExpandedCommit) => {
18+
debug("Commit", commit);
19+
20+
commitsCounter += 1;
21+
summaryDashboard.setCurrentProgress(commitsCounter, commit);
22+
});
23+
commitsStream.on("error", (err) => {
24+
debug("error reading commits", { err });
25+
});
26+
commitsStream.on("end", () => {
27+
debug("done reading commits", {});
28+
});
29+
commitsStream.on("close", () => {
30+
debug("stream closed", {});
31+
});
32+
return commitsStream;
33+
}

0 commit comments

Comments
 (0)