git-compass 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/README.md +114 -0
- package/dist/__tests__/commands.test.d.ts +2 -0
- package/dist/__tests__/commands.test.d.ts.map +1 -0
- package/dist/__tests__/commands.test.js +26 -0
- package/dist/__tests__/commands.test.js.map +1 -0
- package/dist/__tests__/sanity.test.d.ts +2 -0
- package/dist/__tests__/sanity.test.d.ts.map +1 -0
- package/dist/__tests__/sanity.test.js +7 -0
- package/dist/__tests__/sanity.test.js.map +1 -0
- package/dist/bin/git-compass.d.ts +3 -0
- package/dist/bin/git-compass.d.ts.map +1 -0
- package/dist/bin/git-compass.js +4 -0
- package/dist/bin/git-compass.js.map +1 -0
- package/dist/commands/analyze-all.d.ts +3 -0
- package/dist/commands/analyze-all.d.ts.map +1 -0
- package/dist/commands/analyze-all.js +136 -0
- package/dist/commands/analyze-all.js.map +1 -0
- package/dist/commands/analyze.d.ts +3 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +202 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +78 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/query.d.ts +3 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +78 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/watch.d.ts +3 -0
- package/dist/commands/watch.d.ts.map +1 -0
- package/dist/commands/watch.js +49 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config/__tests__/config.test.d.ts +2 -0
- package/dist/config/__tests__/config.test.d.ts.map +1 -0
- package/dist/config/__tests__/config.test.js +45 -0
- package/dist/config/__tests__/config.test.js.map +1 -0
- package/dist/config/index.d.ts +10 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +82 -0
- package/dist/config/index.js.map +1 -0
- package/dist/constants/index.d.ts +15 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +15 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/formatters/__tests__/console.test.d.ts +2 -0
- package/dist/formatters/__tests__/console.test.d.ts.map +1 -0
- package/dist/formatters/__tests__/console.test.js +40 -0
- package/dist/formatters/__tests__/console.test.js.map +1 -0
- package/dist/formatters/console.d.ts +3 -0
- package/dist/formatters/console.d.ts.map +1 -0
- package/dist/formatters/console.js +148 -0
- package/dist/formatters/console.js.map +1 -0
- package/dist/formatters/report-gen.d.ts +3 -0
- package/dist/formatters/report-gen.d.ts.map +1 -0
- package/dist/formatters/report-gen.js +8 -0
- package/dist/formatters/report-gen.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/__tests__/cache.test.d.ts +2 -0
- package/dist/utils/__tests__/cache.test.d.ts.map +1 -0
- package/dist/utils/__tests__/cache.test.js +38 -0
- package/dist/utils/__tests__/cache.test.js.map +1 -0
- package/dist/utils/cache.d.ts +12 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +47 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/gitignore.d.ts +5 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/gitignore.js +36 -0
- package/dist/utils/gitignore.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# git-compass
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/git-compass)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[]()
|
|
6
|
+
|
|
7
|
+
**Git Compass** is a local-first, AI-augmented analytics engine that delivers deep insights into your code's evolution directly from your terminal. No cloud required, no privacy compromises.
|
|
8
|
+
|
|
9
|
+
It helps developers and teams understand their codebases better by analyzing Git history to reveal **hotspots**, **risk areas**, **contributor trends**, and **potential knowledge silos**.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Key Features
|
|
14
|
+
|
|
15
|
+
### Deep Repository Intelligence
|
|
16
|
+
- **Hotspot Detection**: Automatically bridge the gap between "frequently changed" and "risky" by analyzing churn velocity and author diversity.
|
|
17
|
+
- **Architectural Risk Scoring**: Real-time risk assessment for every file, Helping you prioritize refactors based on where technical debt is actually growing.
|
|
18
|
+
- **Temporal Coupling**: Surface hidden dependencies between files that always change together but aren't explicitly linked in code.
|
|
19
|
+
- **Code Health Monitoring**: Track code rot (abandoned files) and average impact (blast radius).
|
|
20
|
+
|
|
21
|
+
### Team Dynamics & Health
|
|
22
|
+
- **Bus Factor Analysis**: Identify critical knowledge silos where a single person is the primary author of a component.
|
|
23
|
+
- **Burnout Mitigation**: Heatmaps of after-hours and weekend commit patterns to help teams maintain a healthy pace.
|
|
24
|
+
- **Contributor DNA**: Understand how different perspectives contribute to your codebase over time.
|
|
25
|
+
|
|
26
|
+
### AI-Powered Synthesis (Optional)
|
|
27
|
+
- **Natural Language Insights**: Generate human-readable summaries of complex Git history (powered by Anthropic Claude or OpenAI).
|
|
28
|
+
- **Interactive Queries**: Ask your repo questions: *"Who knows the most about the auth module?"* or *"Which files are most risky?"*
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Global install (recommended)
|
|
36
|
+
npm install -g git-compass
|
|
37
|
+
|
|
38
|
+
# Or run instantly via npx
|
|
39
|
+
npx git-compass analyze
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
### 1. Simple Analysis
|
|
47
|
+
Generate a comprehensive report for your current repository:
|
|
48
|
+
```bash
|
|
49
|
+
git-compass analyze
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 2. Time-Windowed Deep Dive
|
|
53
|
+
Analyze the last 90 days of history:
|
|
54
|
+
```bash
|
|
55
|
+
git-compass analyze --window 90d
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. AI Insights (Requires API Key)
|
|
59
|
+
Unlock natural language summaries:
|
|
60
|
+
```bash
|
|
61
|
+
# Set your key once
|
|
62
|
+
git-compass config set ai.openaiKey <your-openai-key>
|
|
63
|
+
# or
|
|
64
|
+
git-compass config set ai.anthropicKey <your-anthropic-key>
|
|
65
|
+
|
|
66
|
+
# Run with AI augmentation
|
|
67
|
+
git-compass analyze --ai
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Command Reference
|
|
73
|
+
|
|
74
|
+
### `analyze`
|
|
75
|
+
The primary engine for generating repository insights.
|
|
76
|
+
- `-p, --path <path>`: Path to Git repository (default: `.`)
|
|
77
|
+
- `-b, --branch <branch>`: Branch to analyze (default: `HEAD`)
|
|
78
|
+
- `-w, --window <window>`: Time window (`7d`, `30d`, `90d`, `1y`, `all`)
|
|
79
|
+
- `--max-commits <n>`: Cap the analysis scope
|
|
80
|
+
- `-o, --output <path>`: Export to JSON or HTML report
|
|
81
|
+
|
|
82
|
+
### `query`
|
|
83
|
+
Interactive natural language interface for your repository.
|
|
84
|
+
```bash
|
|
85
|
+
git-compass query "Show me the files with the highest churn in the last week"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `watch`
|
|
89
|
+
Real-time monitoring mode. Automatically re-analyzes your repo as you commit.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
Git Compass stores settings in a local config file. You can manage them via:
|
|
96
|
+
```bash
|
|
97
|
+
git-compass config list
|
|
98
|
+
git-compass config set <key> <value>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Available Keys:**
|
|
102
|
+
- `ai.provider`: LLM provider (`openai`, `anthropic`, `gemini`)
|
|
103
|
+
- `ai.openaiKey`: Your OpenAI API key.
|
|
104
|
+
- `ai.anthropicKey`: Your Anthropic API key.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Privacy & Safety
|
|
109
|
+
|
|
110
|
+
Git Compass is **local-first**. All repository parsing and traditional analytics happen entirely on your machine. If you choose to use the optional AI features, only the summarized metadata (never your full source code) is sent to the LLM provider for synthesis.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
Built with care for teams that value code health.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/commands.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { configCommand } from "../commands/config.js";
|
|
3
|
+
import { analyzeCommand } from "../commands/analyze.js";
|
|
4
|
+
import { watchCommand } from "../commands/watch.js";
|
|
5
|
+
describe("Command Registration", () => {
|
|
6
|
+
it("should have correct names and descriptions", () => {
|
|
7
|
+
expect(configCommand.name()).toBe("config");
|
|
8
|
+
expect(analyzeCommand.name()).toBe("analyze");
|
|
9
|
+
expect(watchCommand.name()).toBe("watch");
|
|
10
|
+
expect(configCommand.description()).toContain("Manage");
|
|
11
|
+
expect(analyzeCommand.description()).toContain("Analyze");
|
|
12
|
+
});
|
|
13
|
+
it("should have expected subcommands for config", () => {
|
|
14
|
+
const subcommands = configCommand.commands.map(cmd => cmd.name());
|
|
15
|
+
expect(subcommands).toContain("set");
|
|
16
|
+
expect(subcommands).toContain("get");
|
|
17
|
+
expect(subcommands).toContain("list");
|
|
18
|
+
});
|
|
19
|
+
it("should have expected options for analyze", () => {
|
|
20
|
+
const optionFlags = analyzeCommand.options.map(opt => opt.flags);
|
|
21
|
+
expect(optionFlags.some(f => f.includes("--path"))).toBe(true);
|
|
22
|
+
expect(optionFlags.some(f => f.includes("--branch"))).toBe(true);
|
|
23
|
+
expect(optionFlags.some(f => f.includes("--output"))).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=commands.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.test.js","sourceRoot":"","sources":["../../src/__tests__/commands.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAM,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9C,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAE1C,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QAClE,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACrC,MAAM,CAAC,WAAW,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sanity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanity.test.js","sourceRoot":"","sources":["../../src/__tests__/sanity.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE9C,QAAQ,CAAC,KAAK,EAAE,GAAG,EAAE;IACnB,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-compass.d.ts","sourceRoot":"","sources":["../../src/bin/git-compass.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-compass.js","sourceRoot":"","sources":["../../src/bin/git-compass.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-all.d.ts","sourceRoot":"","sources":["../../src/commands/analyze-all.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0BpC,eAAO,MAAM,iBAAiB,SAmH1B,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs/promises";
|
|
6
|
+
import { createGitParser, getCommits, analyzeHotspots, computeRiskScores, getAIProvider, generateSummary, AIProviderType } from "@git-compass/core";
|
|
7
|
+
import { config } from "../config/index.js";
|
|
8
|
+
import { CONFIG_KEYS, ENV_VARS } from "../constants/index.js";
|
|
9
|
+
import { getCachePath, loadCache, getCachedResult, updateCache, saveCache } from "../utils/cache.js";
|
|
10
|
+
import { ensureGitIgnore } from "../utils/gitignore.js";
|
|
11
|
+
export const analyzeAllCommand = new Command("analyze-all")
|
|
12
|
+
.description("Scan a directory for Git repositories and analyze them all")
|
|
13
|
+
.argument("[path]", "directory to scan", process.cwd())
|
|
14
|
+
.option("-w, --window <window>", "time window: 7d, 30d, 90d, 1y, all", "30d")
|
|
15
|
+
.option("--max-commits <n>", "max commits to analyze per repo", "100")
|
|
16
|
+
.option("-d, --detail-level <level>", "detail level: summary, normal, verbose", "normal")
|
|
17
|
+
.option("--ai", "generate AI summaries (requires API key)")
|
|
18
|
+
.action(async (scanPath, options) => {
|
|
19
|
+
const rootPath = path.resolve(scanPath || process.cwd());
|
|
20
|
+
const spinner = ora(`Scanning for Git repositories in ${rootPath}...`).start();
|
|
21
|
+
try {
|
|
22
|
+
const repos = await findGitRepos(rootPath);
|
|
23
|
+
if (repos.length === 0) {
|
|
24
|
+
spinner.fail(chalk.red("No Git repositories found."));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
spinner.succeed(chalk.green(`Found ${repos.length} repositories.`));
|
|
28
|
+
const summaries = [];
|
|
29
|
+
for (const repoPath of repos) {
|
|
30
|
+
const repoName = path.basename(repoPath);
|
|
31
|
+
const repoSpinner = ora(`Analyzing ${repoName}...`).start();
|
|
32
|
+
try {
|
|
33
|
+
const git = createGitParser(repoPath);
|
|
34
|
+
const topLevel = await git.revparse(["--show-toplevel"]);
|
|
35
|
+
const repoRoot = path.resolve(topLevel);
|
|
36
|
+
const latestCommit = await git.revparse(["HEAD"]);
|
|
37
|
+
// Ensure .git-compass is ignored
|
|
38
|
+
await ensureGitIgnore(repoRoot, [".git-compass"]);
|
|
39
|
+
const cachePath = await getCachePath(repoRoot);
|
|
40
|
+
const cache = await loadCache(cachePath);
|
|
41
|
+
const cachedResult = getCachedResult(cache, repoRoot, latestCommit);
|
|
42
|
+
let result;
|
|
43
|
+
if (cachedResult && (!options.ai || cachedResult.aiSummary)) {
|
|
44
|
+
result = cachedResult;
|
|
45
|
+
repoSpinner.text = `Loaded ${repoName} from cache.`;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const commits = await getCommits(git, {
|
|
49
|
+
maxCount: parseInt(options.maxCommits, 10),
|
|
50
|
+
since: options.window !== "all" ? options.window : undefined
|
|
51
|
+
});
|
|
52
|
+
const hotspots = analyzeHotspots(commits, options.window);
|
|
53
|
+
const riskScores = computeRiskScores(hotspots);
|
|
54
|
+
result = {
|
|
55
|
+
meta: {
|
|
56
|
+
repoPath: repoRoot,
|
|
57
|
+
branch: "HEAD",
|
|
58
|
+
window: options.window,
|
|
59
|
+
commitCount: commits.length,
|
|
60
|
+
generatedAt: new Date(),
|
|
61
|
+
},
|
|
62
|
+
hotspots,
|
|
63
|
+
riskScores,
|
|
64
|
+
churn: [],
|
|
65
|
+
contributors: [],
|
|
66
|
+
burnout: { flags: [], afterHoursCommits: 0, weekendCommits: 0, contributors: [] },
|
|
67
|
+
coupling: [],
|
|
68
|
+
knowledge: [],
|
|
69
|
+
impact: [],
|
|
70
|
+
rot: []
|
|
71
|
+
};
|
|
72
|
+
if (options.ai) {
|
|
73
|
+
const envProvider = process.env[ENV_VARS.AI_PROVIDER];
|
|
74
|
+
const configProvider = config.get(CONFIG_KEYS.AI_PROVIDER);
|
|
75
|
+
const providerType = envProvider || configProvider || AIProviderType.ANTHROPIC;
|
|
76
|
+
let apiKey;
|
|
77
|
+
if (providerType === "openai")
|
|
78
|
+
apiKey = process.env[ENV_VARS.OPENAI_API_KEY] || config.get("ai.openaiKey");
|
|
79
|
+
else if (providerType === "gemini")
|
|
80
|
+
apiKey = process.env[ENV_VARS.GEMINI_API_KEY] || config.get("ai.geminiKey");
|
|
81
|
+
else
|
|
82
|
+
apiKey = process.env[ENV_VARS.ANTHROPIC_API_KEY] || config.get("ai.anthropicKey") || config.get(CONFIG_KEYS.AI_KEY);
|
|
83
|
+
if (apiKey) {
|
|
84
|
+
try {
|
|
85
|
+
const aiProvider = getAIProvider(providerType, apiKey);
|
|
86
|
+
result.aiSummary = await generateSummary(aiProvider, result);
|
|
87
|
+
}
|
|
88
|
+
catch (e) { }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const updatedCache = updateCache(cache, repoRoot, latestCommit, result);
|
|
92
|
+
await saveCache(cachePath, updatedCache);
|
|
93
|
+
}
|
|
94
|
+
const highRiskCount = result.riskScores.filter(r => r.level === "high" || r.level === "critical").length;
|
|
95
|
+
summaries.push({
|
|
96
|
+
name: repoName,
|
|
97
|
+
commits: result.meta.commitCount,
|
|
98
|
+
hotspots: result.hotspots.length,
|
|
99
|
+
highRisk: highRiskCount
|
|
100
|
+
});
|
|
101
|
+
repoSpinner.succeed(chalk.cyan(`${repoName}: ${result.meta.commitCount} commits, ${highRiskCount} high-risk files.`));
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
repoSpinner.fail(chalk.red(`Failed to analyze ${repoName}: ${err.message}`));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
console.log("\n" + chalk.bold.underline("Organization Summary:"));
|
|
108
|
+
console.table(summaries);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
spinner.fail(chalk.red(`Scan failed: ${err.message}`));
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
async function findGitRepos(dir, depth = 0, maxDepth = 3) {
|
|
115
|
+
const repos = [];
|
|
116
|
+
try {
|
|
117
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
118
|
+
// Check if current dir is a git repo
|
|
119
|
+
if (entries.some(e => e.isDirectory() && e.name === ".git")) {
|
|
120
|
+
return [dir];
|
|
121
|
+
}
|
|
122
|
+
if (depth >= maxDepth)
|
|
123
|
+
return [];
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
126
|
+
const subRepos = await findGitRepos(path.join(dir, entry.name), depth + 1, maxDepth);
|
|
127
|
+
repos.push(...subRepos);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
// Ignore inaccessible directories
|
|
133
|
+
}
|
|
134
|
+
return repos;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=analyze-all.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-all.js","sourceRoot":"","sources":["../../src/commands/analyze-all.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA6CpC,eAAO,MAAM,cAAc,SA4JvB,CAAC"}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { createGitParser, getCommits, analyzeHotspots, computeRiskScores, analyzeChurn, analyzeContributors, analyzeBurnout, analyzeCoupling, analyzeKnowledge, analyzeImpact, analyzeRot, getAIProvider, generateSummary, AIProviderType } from "@git-compass/core";
|
|
5
|
+
import { printConsoleReport } from "../formatters/console.js";
|
|
6
|
+
import { exportJson } from "../formatters/report-gen.js";
|
|
7
|
+
import { config } from "../config/index.js";
|
|
8
|
+
import { DEFAULT_BRANCH, DEFAULT_MAX_COMMITS, DEFAULT_WINDOW, CONFIG_KEYS, ENV_VARS } from "../constants/index.js";
|
|
9
|
+
import { getCachePath, loadCache, getCachedResult, updateCache, saveCache } from "../utils/cache.js";
|
|
10
|
+
import dotenv from "dotenv";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import fs from "fs/promises";
|
|
13
|
+
import { ensureGitIgnore } from "../utils/gitignore.js";
|
|
14
|
+
dotenv.config();
|
|
15
|
+
export const analyzeCommand = new Command("analyze")
|
|
16
|
+
.description("Analyze a Git repository and surface insights")
|
|
17
|
+
.option("-p, --path <path>", "path to git repository", process.cwd())
|
|
18
|
+
.option("-b, --branch <branch>", "branch to analyze", DEFAULT_BRANCH)
|
|
19
|
+
.option("-w, --window <window>", `time window: 7d, 30d, 90d, 1y, all`, DEFAULT_WINDOW)
|
|
20
|
+
.option("--max-commits <n>", "max commits to analyze", DEFAULT_MAX_COMMITS.toString())
|
|
21
|
+
.option("-o, --output <path>", "path to save report (directory or filename)")
|
|
22
|
+
.option("-d, --detail-level <level>", "detail level: summary, normal, verbose", "normal")
|
|
23
|
+
.option("--ai", "generate AI summary (requires API key)")
|
|
24
|
+
.action(async (options) => {
|
|
25
|
+
const repoPath = path.resolve(options.path);
|
|
26
|
+
const spinner = ora("Initializing analysis...").start();
|
|
27
|
+
try {
|
|
28
|
+
const git = createGitParser(repoPath);
|
|
29
|
+
// Find repo root to place .git-compass folder
|
|
30
|
+
let repoRoot = repoPath;
|
|
31
|
+
try {
|
|
32
|
+
const topLevel = await git.revparse(["--show-toplevel"]);
|
|
33
|
+
if (topLevel)
|
|
34
|
+
repoRoot = path.resolve(topLevel);
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
// Fallback to repoPath if not a git repo or other error
|
|
38
|
+
}
|
|
39
|
+
// Ensure .git-compass is ignored
|
|
40
|
+
await ensureGitIgnore(repoRoot, [".git-compass"]);
|
|
41
|
+
spinner.text = `Fetching commits from ${options.branch}...`;
|
|
42
|
+
// Get latest commit hash for caching
|
|
43
|
+
let latestCommit = "";
|
|
44
|
+
try {
|
|
45
|
+
latestCommit = await git.revparse([options.branch || "HEAD"]);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
// Fallback if branch is invalid or other error
|
|
49
|
+
}
|
|
50
|
+
// Check cache
|
|
51
|
+
const cachePath = await getCachePath(repoRoot);
|
|
52
|
+
const cache = await loadCache(cachePath);
|
|
53
|
+
const cachedResult = latestCommit ? getCachedResult(cache, repoRoot, latestCommit) : null;
|
|
54
|
+
if (cachedResult) {
|
|
55
|
+
if (!options.ai || (options.ai && cachedResult.aiSummary)) {
|
|
56
|
+
spinner.succeed(chalk.green(`Loaded from cache for ${latestCommit.slice(0, 7)}.`));
|
|
57
|
+
if (options.output) {
|
|
58
|
+
await handleReportExport(cachedResult, repoPath, repoRoot, options, spinner);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
printConsoleReport(cachedResult, options.detailLevel, !!options.ai);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
spinner.info(chalk.blue(`Found cached analysis for ${latestCommit.slice(0, 7)}, but AI summary is missing. Generating now...`));
|
|
66
|
+
}
|
|
67
|
+
const commits = cachedResult
|
|
68
|
+
? [] // We won't re-fetch commits if we have a cached result and just need AI
|
|
69
|
+
: await getCommits(git, {
|
|
70
|
+
branch: options.branch,
|
|
71
|
+
maxCount: parseInt(options.maxCommits, 10)
|
|
72
|
+
});
|
|
73
|
+
if (!cachedResult && commits.length === 0) {
|
|
74
|
+
spinner.fail(chalk.red("No commits found in the specified window/branch."));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const result = cachedResult || {
|
|
78
|
+
meta: {
|
|
79
|
+
repoPath,
|
|
80
|
+
branch: options.branch,
|
|
81
|
+
window: options.window,
|
|
82
|
+
commitCount: commits.length,
|
|
83
|
+
generatedAt: new Date(),
|
|
84
|
+
},
|
|
85
|
+
hotspots: analyzeHotspots(commits, options.window),
|
|
86
|
+
riskScores: computeRiskScores(analyzeHotspots(commits, options.window)), // Simplified for rebuild
|
|
87
|
+
churn: analyzeChurn(commits, options.window),
|
|
88
|
+
contributors: analyzeContributors(commits),
|
|
89
|
+
burnout: analyzeBurnout(commits),
|
|
90
|
+
coupling: analyzeCoupling(commits),
|
|
91
|
+
knowledge: analyzeKnowledge(commits),
|
|
92
|
+
impact: analyzeImpact(commits),
|
|
93
|
+
rot: analyzeRot(commits)
|
|
94
|
+
};
|
|
95
|
+
// Re-calculate hotspots/risk if we don't have cached result (above logic is a bit messy, let's fix)
|
|
96
|
+
if (!cachedResult) {
|
|
97
|
+
spinner.text = `Analyzing ${commits.length} commits...`;
|
|
98
|
+
const h = analyzeHotspots(commits, options.window);
|
|
99
|
+
result.hotspots = h;
|
|
100
|
+
result.riskScores = computeRiskScores(h);
|
|
101
|
+
result.churn = analyzeChurn(commits, options.window);
|
|
102
|
+
result.contributors = analyzeContributors(commits);
|
|
103
|
+
result.burnout = analyzeBurnout(commits);
|
|
104
|
+
result.coupling = analyzeCoupling(commits);
|
|
105
|
+
result.knowledge = analyzeKnowledge(commits);
|
|
106
|
+
result.impact = analyzeImpact(commits);
|
|
107
|
+
result.rot = analyzeRot(commits);
|
|
108
|
+
}
|
|
109
|
+
if (options.ai) {
|
|
110
|
+
spinner.text = "Generating AI insights...";
|
|
111
|
+
// Resolve provider and key
|
|
112
|
+
const envProvider = process.env[ENV_VARS.AI_PROVIDER];
|
|
113
|
+
const configProvider = config.get(CONFIG_KEYS.AI_PROVIDER);
|
|
114
|
+
const providerType = envProvider || configProvider || AIProviderType.ANTHROPIC;
|
|
115
|
+
// Determine API key based on provider
|
|
116
|
+
let apiKey;
|
|
117
|
+
switch (providerType) {
|
|
118
|
+
case AIProviderType.OPENAI:
|
|
119
|
+
apiKey = process.env[ENV_VARS.OPENAI_API_KEY] || config.get("ai.openaiKey");
|
|
120
|
+
break;
|
|
121
|
+
case AIProviderType.GEMINI:
|
|
122
|
+
apiKey = process.env[ENV_VARS.GEMINI_API_KEY] || config.get("ai.geminiKey");
|
|
123
|
+
break;
|
|
124
|
+
case AIProviderType.ANTHROPIC:
|
|
125
|
+
default:
|
|
126
|
+
apiKey = process.env[ENV_VARS.ANTHROPIC_API_KEY] || config.get("ai.anthropicKey") || config.get(CONFIG_KEYS.AI_KEY);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
if (!apiKey) {
|
|
130
|
+
spinner.warn(chalk.yellow(`AI summary requested but no API key found for ${providerType}. Skipping AI layer.`));
|
|
131
|
+
spinner.info(chalk.blue(`Run 'git-compass config set ai.provider <type>' to configure your preferred provider.`));
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
try {
|
|
135
|
+
const aiProvider = getAIProvider(providerType, apiKey);
|
|
136
|
+
result.aiSummary = await generateSummary(aiProvider, result);
|
|
137
|
+
}
|
|
138
|
+
catch (aiErr) {
|
|
139
|
+
spinner.warn(chalk.yellow("AI summary failed: " + aiErr.message));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
spinner.succeed(chalk.green(`Analysis complete for ${commits.length} commits.`));
|
|
144
|
+
// Update cache
|
|
145
|
+
if (latestCommit) {
|
|
146
|
+
const updatedCache = updateCache(cache, repoRoot, latestCommit, result);
|
|
147
|
+
await saveCache(cachePath, updatedCache);
|
|
148
|
+
}
|
|
149
|
+
if (options.output) {
|
|
150
|
+
await handleReportExport(result, repoPath, repoRoot, options, spinner);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
printConsoleReport(result, options.detailLevel, !!options.ai);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
spinner.fail(chalk.red("Analysis failed: " + err.message));
|
|
158
|
+
console.error(err);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
function generateReportFilename(repoPath, branch, format) {
|
|
162
|
+
const repoName = path.basename(repoPath);
|
|
163
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
164
|
+
const cleanBranch = branch.replace(/[/\\?%*:|"<>]/g, "-");
|
|
165
|
+
return `git-compass-report-${repoName}-${cleanBranch}-${timestamp}.${format}`;
|
|
166
|
+
}
|
|
167
|
+
async function handleReportExport(result, repoPath, repoRoot, options, spinner) {
|
|
168
|
+
const gitCompassDirPath = path.join(repoRoot, ".git-compass");
|
|
169
|
+
let finalPath = path.resolve(options.output);
|
|
170
|
+
// Normalize shorthand or relative paths
|
|
171
|
+
if (options.output === "json") {
|
|
172
|
+
finalPath = gitCompassDirPath;
|
|
173
|
+
}
|
|
174
|
+
else if (!path.isAbsolute(options.output)) {
|
|
175
|
+
finalPath = path.resolve(gitCompassDirPath, options.output);
|
|
176
|
+
}
|
|
177
|
+
// Ensure .git-compass directory exists
|
|
178
|
+
await fs.mkdir(gitCompassDirPath, { recursive: true });
|
|
179
|
+
// Handle directory vs file logic
|
|
180
|
+
try {
|
|
181
|
+
if (!path.extname(finalPath)) {
|
|
182
|
+
await fs.mkdir(finalPath, { recursive: true });
|
|
183
|
+
finalPath = path.join(finalPath, generateReportFilename(repoPath, options.branch, "json"));
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
await fs.mkdir(path.dirname(finalPath), { recursive: true });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
spinner.fail(chalk.red("Failed to prepare output directory: " + err.message));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
spinner.start(`Exporting report...`);
|
|
194
|
+
try {
|
|
195
|
+
const outPath = await exportJson(result, finalPath);
|
|
196
|
+
spinner.succeed(chalk.green(`Report saved to ${outPath}`));
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
spinner.fail(chalk.red("Export failed: " + err.message));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/commands/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,eAAO,MAAM,aAAa,SAC4B,CAAC"}
|