Skip to content

Commit ea3b274

Browse files
committed
Merge branch 'staging' of https://github.com/HUMBLEF0OL/grotto
2 parents 3e333cb + 7280b85 commit ea3b274

13 files changed

Lines changed: 378 additions & 91 deletions

File tree

.changeset/petite-tires-relax.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@git-compass/core": minor
3+
"git-compass": minor
4+
"@git-compass/web": minor
5+
---
6+
7+
Metric fix for web

packages/cli/src/formatters/console.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,25 @@ export function printConsoleReport(
168168
if (contributors.length > 0) {
169169
console.log(chalk.green.bold("\nTop Contributors"));
170170
const contribData = [
171-
[chalk.bold("Author"), chalk.bold("Commits"), chalk.bold("Activity"), chalk.bold("Status")],
171+
[
172+
chalk.bold("Author"),
173+
chalk.bold("Commits"),
174+
chalk.bold("Impact"),
175+
chalk.bold("Stability"),
176+
chalk.bold("Status")
177+
],
172178
];
173179

174180
contributors.slice(0, limit).forEach((c) => {
175181
const burnoutInfo = burnout.contributors.find((b) => b.email === c.email);
176-
const isBurnedOut =
177-
burnoutInfo && (burnoutInfo.riskLevel === 'high');
182+
const isBurnedOut = burnoutInfo && burnoutInfo.riskLevel === "high";
178183

179184
contribData.push([
180185
chalk.white(c.name),
181186
chalk.green(c.commitCount.toString()),
182-
chalk.cyan(`${c.activeDays} days`),
183-
isBurnedOut ? chalk.bgRed.white.bold(" BURNOUT ") : chalk.bgGreen.black.bold(" STABLE "),
187+
chalk.yellow(`+${c.insertions || 0}`),
188+
chalk.magenta(`${c.stability || 0}%`),
189+
isBurnedOut ? chalk.bgRed.white.bold(" BURNOUT ") : chalk.bgGreen.black.bold(" ACTIVE "),
184190
]);
185191
});
186192
console.log(table(contribData, { border: getBorderCharacters("ramac") }));

packages/core/inspect-diff.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { createGitParser } from './src/parser/git-parser.js';
2+
3+
async function inspect() {
4+
const git = createGitParser(process.cwd());
5+
const log = await git.log(['-n', '1', '--stat']);
6+
const commit = log.all[0];
7+
console.log('Commit hash:', commit.hash);
8+
console.log('Diff info:', JSON.stringify(commit.diff, null, 2));
9+
}
10+
11+
inspect().catch(console.error);

packages/core/src/analyzers/contributors.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export function analyzeContributors(commits: GitCommit[]): ContributorReport {
1414
email: string;
1515
commitCount: number;
1616
files: Set<string>;
17+
insertions: number;
18+
deletions: number;
19+
totalFilesAcrossCommits: number;
1720
first: string;
1821
last: string;
1922
days: Set<string>;
@@ -26,12 +29,19 @@ export function analyzeContributors(commits: GitCommit[]): ContributorReport {
2629
email: commit.author.email,
2730
commitCount: 0,
2831
files: new Set<string>(),
32+
insertions: 0,
33+
deletions: 0,
34+
totalFilesAcrossCommits: 0,
2935
first: commit.date,
3036
last: commit.date,
3137
days: new Set<string>(),
3238
};
3339

3440
existing.commitCount++;
41+
existing.insertions += commit.insertions || 0;
42+
existing.deletions += commit.deletions || 0;
43+
existing.totalFilesAcrossCommits += commit.files.length;
44+
3545
if (new Date(commit.date) < new Date(existing.first)) existing.first = commit.date;
3646
if (new Date(commit.date) > new Date(existing.last)) existing.last = commit.date;
3747

@@ -41,15 +51,24 @@ export function analyzeContributors(commits: GitCommit[]): ContributorReport {
4151
contributorMap.set(commit.author.email, existing);
4252
}
4353

44-
const contributors: ContributorDetail[] = Array.from(contributorMap.values()).map((data) => ({
45-
name: data.name,
46-
email: data.email,
47-
commitCount: data.commitCount,
48-
filesChanged: data.files.size,
49-
firstCommit: data.first,
50-
lastCommit: data.last,
51-
activeDays: data.days.size,
52-
}));
54+
const contributors: ContributorDetail[] = Array.from(contributorMap.values()).map((data) => {
55+
// Stability per author: based on commit atomicity
56+
const avgFilesPerCommit = data.commitCount > 0 ? (data.totalFilesAcrossCommits / data.commitCount) : 0;
57+
const stability = Math.max(20, Math.min(100, Math.round(100 - (avgFilesPerCommit * 5))));
58+
59+
return {
60+
name: data.name,
61+
email: data.email,
62+
commitCount: data.commitCount,
63+
filesChanged: data.files.size,
64+
firstCommit: data.first,
65+
lastCommit: data.last,
66+
activeDays: data.days.size,
67+
insertions: data.insertions,
68+
deletions: data.deletions,
69+
stability
70+
};
71+
});
5372

5473
return {
5574
contributors,

packages/core/src/parser/git-parser.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,17 @@ export async function getCommitsSince(
105105
args.push(since);
106106
args.push("HEAD");
107107
} else {
108-
const days = parseInt(since, 10);
109-
if (isPureNumber && !isNaN(days)) {
108+
const daysStr = since.toLowerCase();
109+
if (daysStr.endsWith("d")) {
110+
const days = daysStr.slice(0, -1);
110111
args.push(`--since=${days} days ago`);
111112
} else {
112-
args.push(`--since=${since}`);
113+
const days = parseInt(since, 10);
114+
if (isPureNumber && !isNaN(days)) {
115+
args.push(`--since=${days} days ago`);
116+
} else {
117+
args.push(`--since=${since}`);
118+
}
113119
}
114120
}
115121
}
@@ -120,17 +126,37 @@ export async function getCommitsSince(
120126

121127
const logResult = await git.log(args);
122128

129+
// Fetch parents separately to avoid breaking simple-git's diff parser
130+
const parentArgs = args.filter(arg => !arg.startsWith('--stat'));
131+
const parentsRaw = await git.raw(['log', ...parentArgs, '--format=%H %P']);
132+
const parentMap = new Map<string, string[]>();
133+
134+
parentsRaw.split('\n').forEach(line => {
135+
const parts = line.trim().split(' ');
136+
const hash = parts[0];
137+
if (hash) {
138+
parentMap.set(hash, parts.slice(1));
139+
}
140+
});
141+
123142
return logResult.all.map((commit: any) => {
143+
const hash = commit.hash;
144+
const parents = parentMap.get(hash) || [];
145+
// Combine subject and body for full message analysis
146+
const fullMessage = commit.body ? `${commit.message}\n\n${commit.body}` : commit.message;
147+
124148
return {
125-
hash: commit.hash,
126-
parents: commit.parents || [],
149+
hash,
150+
parents,
127151
author: {
128152
name: commit.author_name || 'Unknown',
129153
email: commit.author_email || 'unknown@example.com'
130154
},
131155
date: commit.date,
132-
message: commit.message,
133-
files: commit.diff ? commit.diff.files.map((f: any) => f.file).filter(Boolean) : []
156+
message: fullMessage,
157+
files: commit.diff ? commit.diff.files.map((f: any) => f.file).filter(Boolean) : [],
158+
insertions: commit.diff?.insertions || 0,
159+
deletions: commit.diff?.deletions || 0
134160
} as GitCommit;
135161
});
136162
}

packages/core/src/types/extended.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ export interface ContributorDetail {
123123
readonly firstCommit: ISODateString;
124124
readonly lastCommit: ISODateString;
125125
readonly activeDays: number;
126+
readonly insertions: number;
127+
readonly deletions: number;
128+
readonly stability: number; // 0–100 score
126129
}
127130

128131
export interface ContributorReport {

packages/core/src/types/signal.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,6 @@ export interface GitCommit {
112112
readonly date: string;
113113
readonly parents: string[];
114114
readonly files: string[];
115+
readonly insertions: number;
116+
readonly deletions: number;
115117
}

packages/core/verify-data.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createGitParser, getCommitsSince, analyzeContributors } from './src/index.js';
2+
3+
async function test() {
4+
const git = createGitParser(process.cwd() + '/../..');
5+
const commits = await getCommitsSince(git, '30d', { branch: 'dev' });
6+
console.log(`Found ${commits.length} commits`);
7+
8+
if (commits.length > 0) {
9+
console.log('Sample commit insertions:', commits[0].insertions);
10+
const authors = analyzeContributors(commits);
11+
console.log('Top author data:', JSON.stringify(authors.contributors[0], null, 2));
12+
}
13+
}
14+
15+
test().catch(console.error);

packages/web/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

packages/web/public/app.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -971,17 +971,23 @@ document.addEventListener('DOMContentLoaded', () => {
971971
const onboardingTableBody = document.querySelector('#onboarding-table tbody');
972972
if (onboardingTableBody && p1.contributors && p2.onboarding) {
973973
onboardingTableBody.innerHTML = '';
974-
p1.contributors.contributors.slice(0, 8).forEach(c => {
974+
p1.contributors.contributors
975+
.sort((a, b) => b.commitCount - a.commitCount)
976+
.slice(0, 8)
977+
.forEach(c => {
975978
const tr = document.createElement('tr');
976-
const onboardingScore = c.email === 'github-actions' ? 97 : (Math.floor(Math.random() * (95 - 75 + 1)) + 75);
977-
const impact = c.commitCount * 12;
978-
const stability = 100 - Math.round((data.summary.churnRate || 0.1) * 100);
979+
// Use per-contributor stability from core
980+
const stability = c.stability || 85;
981+
// Impact is now real insertion count
982+
const impact = c.insertions || 0;
983+
// Onboarding score: crude estimate based on stability and commit volume
984+
const onboardingScore = Math.min(98, Math.round((stability * 0.6) + (Math.min(c.commitCount, 50) * 0.8)));
979985

980986
tr.innerHTML = `
981987
<td>
982988
<div class="author-cell">
983989
<div class="author-avatar-mini"></div>
984-
<span>${c.name.split(' ')[0].toUpperCase()}</span>
990+
<span>${(c.name || 'Unknown').split(' ')[0].toUpperCase()}</span>
985991
</div>
986992
</td>
987993
<td>${c.commitCount}</td>

0 commit comments

Comments
 (0)