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
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { config } from "../config/index.js";
|
|
4
|
+
import { PROJECT_NAME } from "../constants/index.js";
|
|
5
|
+
export const configCommand = new Command("config")
|
|
6
|
+
.description(`Manage ${PROJECT_NAME} configuration`);
|
|
7
|
+
configCommand
|
|
8
|
+
.command("set <key> <value>")
|
|
9
|
+
.description("Set a configuration value (e.g., ai.key)")
|
|
10
|
+
.action((key, value) => {
|
|
11
|
+
config.set(key, value);
|
|
12
|
+
console.log(chalk.green(`${key} set successfully.`));
|
|
13
|
+
});
|
|
14
|
+
configCommand
|
|
15
|
+
.command("set-ai")
|
|
16
|
+
.description("Interactively configure AI provider and API key")
|
|
17
|
+
.action(async () => {
|
|
18
|
+
const { select, password } = await import("@inquirer/prompts");
|
|
19
|
+
const provider = await select({
|
|
20
|
+
message: "Select an AI provider:",
|
|
21
|
+
choices: [
|
|
22
|
+
{ name: "Anthropic (Claude)", value: "anthropic" },
|
|
23
|
+
{ name: "OpenAI (GPT-4o)", value: "openai" },
|
|
24
|
+
{ name: "Google Gemini (1.5 Pro)", value: "gemini" }
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
const apiKey = await password({
|
|
28
|
+
message: `Enter your ${provider} API Key:`,
|
|
29
|
+
mask: "*"
|
|
30
|
+
});
|
|
31
|
+
if (apiKey) {
|
|
32
|
+
config.set("ai.provider", provider);
|
|
33
|
+
config.set(`ai.${provider}Key`, apiKey);
|
|
34
|
+
// Also set the main ai.key for backward compatibility/simplicity
|
|
35
|
+
config.set("ai.key", apiKey);
|
|
36
|
+
console.log(chalk.green(`\nAI configured successfully with ${provider}!`));
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
configCommand
|
|
40
|
+
.command("get <key>")
|
|
41
|
+
.description("Get a configuration value")
|
|
42
|
+
.action((key) => {
|
|
43
|
+
const value = config.get(key);
|
|
44
|
+
if (key.toLowerCase().includes("key") && value) {
|
|
45
|
+
console.log(`${key}: ${maskKey(value)}`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.log(`${key}: ${value ?? "not set"}`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
configCommand
|
|
52
|
+
.command("list")
|
|
53
|
+
.description("List all configuration")
|
|
54
|
+
.action(() => {
|
|
55
|
+
const all = config.store;
|
|
56
|
+
console.log(chalk.blue.bold(`\n--- ${PROJECT_NAME} Configuration ---`));
|
|
57
|
+
if (all.ai) {
|
|
58
|
+
console.log(`${chalk.yellow("AI Provider:")} ${all.ai.provider || "not set"}`);
|
|
59
|
+
if (all.ai.anthropicKey)
|
|
60
|
+
console.log(`${chalk.yellow("Anthropic Key:")} ${maskKey(all.ai.anthropicKey)}`);
|
|
61
|
+
if (all.ai.openaiKey)
|
|
62
|
+
console.log(`${chalk.yellow("OpenAI Key:")} ${maskKey(all.ai.openaiKey)}`);
|
|
63
|
+
if (all.ai.geminiKey)
|
|
64
|
+
console.log(`${chalk.yellow("Gemini Key:")} ${maskKey(all.ai.geminiKey)}`);
|
|
65
|
+
}
|
|
66
|
+
// List other top-level keys if any
|
|
67
|
+
for (const [key, val] of Object.entries(all)) {
|
|
68
|
+
if (key !== "ai") {
|
|
69
|
+
console.log(`${key}: ${JSON.stringify(val)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
function maskKey(key) {
|
|
74
|
+
if (key.length <= 8)
|
|
75
|
+
return "****";
|
|
76
|
+
return `${key.slice(0, 7)}...${key.slice(-4)}`;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiCpC,eAAO,MAAM,YAAY,SA4ErB,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
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, queryAnalysis, getAIProvider, AIProviderType } from "@git-compass/core";
|
|
5
|
+
import { config } from "../config/index.js";
|
|
6
|
+
import { DEFAULT_BRANCH, DEFAULT_MAX_COMMITS, DEFAULT_WINDOW, CONFIG_KEYS, ENV_VARS } from "../constants/index.js";
|
|
7
|
+
import dotenv from "dotenv";
|
|
8
|
+
import path from "path";
|
|
9
|
+
dotenv.config();
|
|
10
|
+
export const queryCommand = new Command("query")
|
|
11
|
+
.description("Ask a natural language question about the repository")
|
|
12
|
+
.argument("<question>", "the question to ask")
|
|
13
|
+
.option("-p, --path <path>", "path to git repository", process.cwd())
|
|
14
|
+
.option("-b, --branch <branch>", "branch to analyze", DEFAULT_BRANCH)
|
|
15
|
+
.option("-w, --window <window>", "time window: 7d, 30d, 90d, 1y, all", DEFAULT_WINDOW)
|
|
16
|
+
.option("--max-commits <n>", "max commits to analyze", DEFAULT_MAX_COMMITS.toString())
|
|
17
|
+
.option("--ai", "generate AI summary")
|
|
18
|
+
.action(async (question, options) => {
|
|
19
|
+
const repoPath = path.resolve(options.path);
|
|
20
|
+
const spinner = ora("Setting up context...").start();
|
|
21
|
+
try {
|
|
22
|
+
const git = createGitParser(repoPath);
|
|
23
|
+
const commits = await getCommits(git, {
|
|
24
|
+
branch: options.branch,
|
|
25
|
+
maxCount: parseInt(options.maxCommits, 10),
|
|
26
|
+
since: options.window !== "all" ? options.window : undefined
|
|
27
|
+
});
|
|
28
|
+
if (commits.length === 0) {
|
|
29
|
+
spinner.fail(chalk.red("No commits found to build context."));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
spinner.text = "Analyzing repository state...";
|
|
33
|
+
const result = {
|
|
34
|
+
meta: { repoPath, branch: options.branch, commitCount: commits.length },
|
|
35
|
+
hotspots: analyzeHotspots(commits),
|
|
36
|
+
riskScores: computeRiskScores(analyzeHotspots(commits)),
|
|
37
|
+
churn: analyzeChurn(commits),
|
|
38
|
+
contributors: analyzeContributors(commits),
|
|
39
|
+
burnout: analyzeBurnout(commits),
|
|
40
|
+
coupling: analyzeCoupling(commits),
|
|
41
|
+
knowledge: analyzeKnowledge(commits),
|
|
42
|
+
impact: analyzeImpact(commits),
|
|
43
|
+
rot: analyzeRot(commits)
|
|
44
|
+
};
|
|
45
|
+
spinner.text = "Consulting AI...";
|
|
46
|
+
// Determine provider
|
|
47
|
+
const envProvider = process.env[ENV_VARS.AI_PROVIDER];
|
|
48
|
+
const configProvider = config.get(CONFIG_KEYS.AI_PROVIDER);
|
|
49
|
+
const providerType = envProvider || configProvider || AIProviderType.ANTHROPIC;
|
|
50
|
+
// Determine API key based on provider
|
|
51
|
+
let apiKey;
|
|
52
|
+
switch (providerType) {
|
|
53
|
+
case AIProviderType.OPENAI:
|
|
54
|
+
apiKey = process.env[ENV_VARS.OPENAI_API_KEY] || config.get("ai.openaiKey");
|
|
55
|
+
break;
|
|
56
|
+
case AIProviderType.GEMINI:
|
|
57
|
+
apiKey = process.env[ENV_VARS.GEMINI_API_KEY] || config.get("ai.geminiKey");
|
|
58
|
+
break;
|
|
59
|
+
case AIProviderType.ANTHROPIC:
|
|
60
|
+
default:
|
|
61
|
+
apiKey = process.env[ENV_VARS.ANTHROPIC_API_KEY] || config.get("ai.anthropicKey") || config.get(CONFIG_KEYS.AI_KEY);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
if (!apiKey) {
|
|
65
|
+
spinner.fail(chalk.red(`No API key found for ${providerType}. Use 'Git Compass config set' to configure.`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const aiClient = getAIProvider(providerType, apiKey);
|
|
69
|
+
const answer = await queryAnalysis(aiClient, question, result);
|
|
70
|
+
spinner.succeed(chalk.green("AI Query Complete."));
|
|
71
|
+
console.log(`\n${chalk.magenta.bold("Git Compass AI:")} ${answer}\n`);
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
spinner.fail(chalk.red("Query failed: " + err.message));
|
|
75
|
+
console.error(err);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EACL,eAAe,EACf,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,UAAU,EAEV,aAAa,EACb,aAAa,EACb,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,cAAc,EACd,WAAW,EACX,QAAQ,EACT,MAAM,uBAAuB,CAAC;AAC/B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,sDAAsD,CAAC;KACnE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC;KAC7C,MAAM,CAAC,mBAAmB,EAAE,wBAAwB,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;KACpE,MAAM,CAAC,uBAAuB,EAAE,mBAAmB,EAAE,cAAc,CAAC;KACpE,MAAM,CAAC,uBAAuB,EAAE,oCAAoC,EAAE,cAAc,CAAC;KACrF,MAAM,CAAC,mBAAmB,EAAE,wBAAwB,EAAE,mBAAmB,CAAC,QAAQ,EAAE,CAAC;KACrF,MAAM,CAAC,MAAM,EAAE,qBAAqB,CAAC;KACrC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,GAAG,CAAC,uBAAuB,CAAC,CAAC,KAAK,EAAE,CAAC;IAErD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE;YACpC,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;YAC1C,KAAK,EAAE,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;SAC7D,CAAC,CAAC;QAEH,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC,CAAC;YAC9D,OAAO;QACT,CAAC;QAED,OAAO,CAAC,IAAI,GAAG,+BAA+B,CAAC;QAC/C,MAAM,MAAM,GAAQ;YAClB,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,MAAM,EAAE;YACvE,QAAQ,EAAE,eAAe,CAAC,OAAO,CAAC;YAClC,UAAU,EAAE,iBAAiB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YACvD,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC;YAC5B,YAAY,EAAE,mBAAmB,CAAC,OAAO,CAAC;YAC1C,OAAO,EAAE,cAAc,CAAC,OAAO,CAAC;YAChC,QAAQ,EAAE,eAAe,CAAC,OAAO,CAAC;YAClC,SAAS,EAAE,gBAAgB,CAAC,OAAO,CAAC;YACpC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC;YAC9B,GAAG,EAAE,UAAU,CAAC,OAAO,CAAC;SACzB,CAAC;QAEF,OAAO,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAElC,qBAAqB;QACrB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAmB,CAAC;QACxE,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,WAAW,CAAmB,CAAC;QAC7E,MAAM,YAAY,GAAG,WAAW,IAAI,cAAc,IAAI,cAAc,CAAC,SAAS,CAAC;QAE/E,sCAAsC;QACtC,IAAI,MAA0B,CAAC;QAC/B,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,cAAc,CAAC,MAAM;gBACxB,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBAC5E,MAAM;YACR,KAAK,cAAc,CAAC,MAAM;gBACxB,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;gBAC5E,MAAM;YACR,KAAK,cAAc,CAAC,SAAS,CAAC;YAC9B;gBACE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;gBACpH,MAAM;QACV,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,wBAAwB,YAAY,8CAA8C,CAAC,CAAC,CAAC;YAC5G,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAG,aAAa,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAE/D,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC;IAExE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,GAAI,GAAa,CAAC,OAAO,CAAC,CAAC,CAAC;QACnE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,YAAY,SAkCrB,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chokidar from "chokidar";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { DEFAULT_BRANCH, PROJECT_NAME } from "../constants/index.js";
|
|
7
|
+
export const watchCommand = new Command("watch")
|
|
8
|
+
.description(`Watch for repository changes and re-run analysis`)
|
|
9
|
+
.option("-p, --path <path>", "path to git repository", process.cwd())
|
|
10
|
+
.option("-b, --branch <branch>", "branch to analyze", DEFAULT_BRANCH)
|
|
11
|
+
.option("-w, --window <window>", "time window: 7d, 30d, 90d, 1y, all", "30d")
|
|
12
|
+
.option("--max-commits <n>", "max commits to analyze", "500")
|
|
13
|
+
.option("--ai", "generate AI summary")
|
|
14
|
+
.action((options) => {
|
|
15
|
+
const repoPath = path.resolve(options.path);
|
|
16
|
+
const gitDir = path.join(repoPath, ".git");
|
|
17
|
+
console.log(chalk.cyan.bold(`\n${PROJECT_NAME} is watching ${repoPath}...`));
|
|
18
|
+
console.log(chalk.gray("Analysis will re-run on every commit.\n"));
|
|
19
|
+
// Initial run
|
|
20
|
+
runAnalysis(options);
|
|
21
|
+
const watcher = chokidar.watch([
|
|
22
|
+
path.join(gitDir, "refs", "heads"),
|
|
23
|
+
path.join(gitDir, "index")
|
|
24
|
+
], {
|
|
25
|
+
persistent: true,
|
|
26
|
+
ignoreInitial: true
|
|
27
|
+
});
|
|
28
|
+
watcher.on("change", (filePath) => {
|
|
29
|
+
console.log(chalk.yellow(`\nChange detected in ${path.basename(filePath)}. Re-analyzing...`));
|
|
30
|
+
runAnalysis(options);
|
|
31
|
+
});
|
|
32
|
+
process.on("SIGINT", () => {
|
|
33
|
+
watcher.close();
|
|
34
|
+
process.exit(0);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
function runAnalysis(options) {
|
|
38
|
+
try {
|
|
39
|
+
const binPath = path.join(process.cwd(), "dist/bin/git-compass.js");
|
|
40
|
+
let cmd = `node ${binPath} analyze -p "${options.path}" -b "${options.branch}" -w "${options.window}" --max-commits ${options.maxCommits}`;
|
|
41
|
+
if (options.ai)
|
|
42
|
+
cmd += " --ai";
|
|
43
|
+
execSync(cmd, { stdio: "inherit" });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error(chalk.red("Watch analysis failed. check your git state."));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../../src/config/__tests__/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { config } from "../index.js";
|
|
5
|
+
// Mock fs to avoid writing to actual .env
|
|
6
|
+
vi.mock("fs");
|
|
7
|
+
describe("EnvConfig", () => {
|
|
8
|
+
const mockEnvPath = path.resolve(process.cwd(), ".env");
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
vi.resetAllMocks();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
it("should return undefined if key does not exist and .env is missing", () => {
|
|
16
|
+
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
17
|
+
expect(config.get("non-existent")).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
it("should get value from process.env if .env is missing", () => {
|
|
20
|
+
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
21
|
+
process.env.TEST_KEY = "test-value";
|
|
22
|
+
expect(config.get("TEST_KEY")).toBe("test-value");
|
|
23
|
+
delete process.env.TEST_KEY;
|
|
24
|
+
});
|
|
25
|
+
it("should set value in .env file", () => {
|
|
26
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
27
|
+
vi.mocked(fs.readFileSync).mockReturnValue("");
|
|
28
|
+
config.set("ai.provider", "openai");
|
|
29
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining(".env"), expect.stringContaining("GIT_COMPASS_AI_PROVIDER=openai"), "utf-8");
|
|
30
|
+
});
|
|
31
|
+
it("should update existing value in .env file", () => {
|
|
32
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
33
|
+
vi.mocked(fs.readFileSync).mockReturnValue("GIT_COMPASS_AI_PROVIDER=anthropic\nOTHER_KEY=val");
|
|
34
|
+
config.set("ai.provider", "gemini");
|
|
35
|
+
expect(fs.writeFileSync).toHaveBeenCalledWith(expect.stringContaining(".env"), expect.stringContaining("GIT_COMPASS_AI_PROVIDER=gemini\nOTHER_KEY=val"), "utf-8");
|
|
36
|
+
});
|
|
37
|
+
it("should retrieve masked store accurately", () => {
|
|
38
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
39
|
+
vi.mocked(fs.readFileSync).mockReturnValue("GIT_COMPASS_AI_PROVIDER=openai\nOPENAI_API_KEY=sk-12345");
|
|
40
|
+
const store = config.store;
|
|
41
|
+
expect(store.ai.provider).toBe("openai");
|
|
42
|
+
expect(store.ai.openaiKey).toBe("sk-12345");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../../src/config/__tests__/config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
declare class EnvConfig {
|
|
2
|
+
private envPath;
|
|
3
|
+
constructor();
|
|
4
|
+
get(key: string): string | undefined;
|
|
5
|
+
set(key: string, value: string): void;
|
|
6
|
+
get store(): Record<string, any>;
|
|
7
|
+
}
|
|
8
|
+
export declare const config: EnvConfig;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAyBA,cAAM,SAAS;IACb,OAAO,CAAC,OAAO,CAAS;;IAOxB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAWpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IA6BrC,IAAI,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAmB/B;CACF;AAED,eAAO,MAAM,MAAM,WAAkB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
const CONFIG_MAP = {
|
|
5
|
+
"ai.provider": "GIT_COMPASS_AI_PROVIDER",
|
|
6
|
+
"ai.key": "GIT_COMPASS_AI_KEY",
|
|
7
|
+
"ai.anthropicKey": "ANTHROPIC_API_KEY",
|
|
8
|
+
"ai.openaiKey": "OPENAI_API_KEY",
|
|
9
|
+
"ai.geminiKey": "GEMINI_API_KEY"
|
|
10
|
+
};
|
|
11
|
+
function findGitRoot(startDir) {
|
|
12
|
+
let current = path.resolve(startDir);
|
|
13
|
+
const root = path.parse(current).root;
|
|
14
|
+
while (current !== root) {
|
|
15
|
+
if (fs.existsSync(path.join(current, ".git"))) {
|
|
16
|
+
return current;
|
|
17
|
+
}
|
|
18
|
+
current = path.dirname(current);
|
|
19
|
+
}
|
|
20
|
+
return startDir;
|
|
21
|
+
}
|
|
22
|
+
class EnvConfig {
|
|
23
|
+
envPath;
|
|
24
|
+
constructor() {
|
|
25
|
+
const root = findGitRoot(process.cwd());
|
|
26
|
+
this.envPath = path.resolve(root, ".env");
|
|
27
|
+
}
|
|
28
|
+
get(key) {
|
|
29
|
+
// Reload env to get latest values
|
|
30
|
+
if (fs.existsSync(this.envPath)) {
|
|
31
|
+
const envContent = fs.readFileSync(this.envPath);
|
|
32
|
+
const envConfig = dotenv.parse(envContent);
|
|
33
|
+
const envKey = CONFIG_MAP[key] || key;
|
|
34
|
+
return envConfig[envKey];
|
|
35
|
+
}
|
|
36
|
+
return process.env[CONFIG_MAP[key] || key];
|
|
37
|
+
}
|
|
38
|
+
set(key, value) {
|
|
39
|
+
const envKey = CONFIG_MAP[key] || key;
|
|
40
|
+
let content = "";
|
|
41
|
+
if (fs.existsSync(this.envPath)) {
|
|
42
|
+
content = fs.readFileSync(this.envPath, "utf-8");
|
|
43
|
+
}
|
|
44
|
+
const lines = content.split("\n");
|
|
45
|
+
let found = false;
|
|
46
|
+
const newLines = lines.map(line => {
|
|
47
|
+
if (line.trim().startsWith(`${envKey}=`)) {
|
|
48
|
+
found = true;
|
|
49
|
+
return `${envKey}=${value}`;
|
|
50
|
+
}
|
|
51
|
+
return line;
|
|
52
|
+
});
|
|
53
|
+
if (!found) {
|
|
54
|
+
newLines.push(`${envKey}=${value}`);
|
|
55
|
+
}
|
|
56
|
+
fs.writeFileSync(this.envPath, newLines.join("\n").trim() + "\n", "utf-8");
|
|
57
|
+
// Update process.env for current session
|
|
58
|
+
process.env[envKey] = value;
|
|
59
|
+
}
|
|
60
|
+
get store() {
|
|
61
|
+
if (fs.existsSync(this.envPath)) {
|
|
62
|
+
const data = dotenv.parse(fs.readFileSync(this.envPath, "utf-8"));
|
|
63
|
+
const result = { ai: {} };
|
|
64
|
+
// Map back to schema for compatibility
|
|
65
|
+
for (const [configKey, envKey] of Object.entries(CONFIG_MAP)) {
|
|
66
|
+
if (data[envKey]) {
|
|
67
|
+
const parts = configKey.split(".");
|
|
68
|
+
if (parts.length === 2 && parts[0] === "ai") {
|
|
69
|
+
result.ai[parts[1]] = data[envKey];
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
result[configKey] = data[envKey];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
return { ai: {} };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export const config = new EnvConfig();
|
|
82
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const PROJECT_NAME = "Git Compass";
|
|
2
|
+
export declare const DEFAULT_BRANCH = "HEAD";
|
|
3
|
+
export declare const DEFAULT_WINDOW = "30d";
|
|
4
|
+
export declare const DEFAULT_MAX_COMMITS = 500;
|
|
5
|
+
export declare const CONFIG_KEYS: {
|
|
6
|
+
readonly AI_KEY: "ai.key";
|
|
7
|
+
readonly AI_PROVIDER: "ai.provider";
|
|
8
|
+
};
|
|
9
|
+
export declare const ENV_VARS: {
|
|
10
|
+
readonly ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY";
|
|
11
|
+
readonly OPENAI_API_KEY: "OPENAI_API_KEY";
|
|
12
|
+
readonly GEMINI_API_KEY: "GEMINI_API_KEY";
|
|
13
|
+
readonly AI_PROVIDER: "GIT_COMPASS_AI_PROVIDER";
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,eAAO,MAAM,cAAc,QAAQ,CAAC;AACpC,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,eAAO,MAAM,WAAW;;;CAGd,CAAC;AAEX,eAAO,MAAM,QAAQ;;;;;CAKX,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const PROJECT_NAME = "Git Compass";
|
|
2
|
+
export const DEFAULT_BRANCH = "HEAD";
|
|
3
|
+
export const DEFAULT_WINDOW = "30d";
|
|
4
|
+
export const DEFAULT_MAX_COMMITS = 500;
|
|
5
|
+
export const CONFIG_KEYS = {
|
|
6
|
+
AI_KEY: "ai.key",
|
|
7
|
+
AI_PROVIDER: "ai.provider"
|
|
8
|
+
};
|
|
9
|
+
export const ENV_VARS = {
|
|
10
|
+
ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY",
|
|
11
|
+
OPENAI_API_KEY: "OPENAI_API_KEY",
|
|
12
|
+
GEMINI_API_KEY: "GEMINI_API_KEY",
|
|
13
|
+
AI_PROVIDER: "GIT_COMPASS_AI_PROVIDER"
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC;AACpC,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,aAAa;CAClB,CAAC;AAEX,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,iBAAiB,EAAE,mBAAmB;IACtC,cAAc,EAAE,gBAAgB;IAChC,cAAc,EAAE,gBAAgB;IAChC,WAAW,EAAE,yBAAyB;CAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.test.d.ts","sourceRoot":"","sources":["../../../src/formatters/__tests__/console.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { printConsoleReport } from "../console.js";
|
|
3
|
+
describe("Console Formatter", () => {
|
|
4
|
+
const mockResult = {
|
|
5
|
+
meta: {
|
|
6
|
+
repoPath: "/test/repo",
|
|
7
|
+
branch: "master",
|
|
8
|
+
window: "30d",
|
|
9
|
+
commitCount: 10,
|
|
10
|
+
generatedAt: new Date()
|
|
11
|
+
},
|
|
12
|
+
hotspots: [],
|
|
13
|
+
riskScores: [],
|
|
14
|
+
churn: [],
|
|
15
|
+
contributors: [],
|
|
16
|
+
burnout: [],
|
|
17
|
+
coupling: [],
|
|
18
|
+
knowledge: [],
|
|
19
|
+
impact: [],
|
|
20
|
+
rot: []
|
|
21
|
+
};
|
|
22
|
+
it("should output basic report structure", () => {
|
|
23
|
+
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
24
|
+
printConsoleReport(mockResult, "summary", false);
|
|
25
|
+
expect(logSpy).toHaveBeenCalled();
|
|
26
|
+
const calls = logSpy.mock.calls.map(call => call[0]);
|
|
27
|
+
// console.log("DEBUG CALLS:", JSON.stringify(calls)); // Temporarily uncomment if needed
|
|
28
|
+
expect(calls.some(c => c && c.toLowerCase().includes("analysis"))).toBe(true);
|
|
29
|
+
logSpy.mockRestore();
|
|
30
|
+
});
|
|
31
|
+
it("should indicate AI summary presence", () => {
|
|
32
|
+
const logSpy = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
33
|
+
const aiResult = { ...mockResult, aiSummary: { digest: "AI summary here", provider: "test", model: "test" } };
|
|
34
|
+
printConsoleReport(aiResult, "normal", true);
|
|
35
|
+
const calls = logSpy.mock.calls.map(call => call[0]);
|
|
36
|
+
expect(calls.some(c => c && c.toLowerCase().includes("ai"))).toBe(true);
|
|
37
|
+
logSpy.mockRestore();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=console.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.test.js","sourceRoot":"","sources":["../../../src/formatters/__tests__/console.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,MAAM,UAAU,GAAG;QACjB,IAAI,EAAE;YACJ,QAAQ,EAAE,YAAY;YACtB,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,KAAK;YACb,WAAW,EAAE,EAAE;YACf,WAAW,EAAE,IAAI,IAAI,EAAE;SACxB;QACD,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;QACd,KAAK,EAAE,EAAE;QACT,YAAY,EAAE,EAAE;QAChB,OAAO,EAAE,EAAE;QACX,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,MAAM,EAAE,EAAE;QACV,GAAG,EAAE,EAAE;KACR,CAAC;IAEF,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAErE,kBAAkB,CAAC,UAAiB,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QAExD,MAAM,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,yFAAyF;QAEzF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE9E,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACrE,MAAM,QAAQ,GAAG,EAAE,GAAG,UAAU,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;QAE9G,kBAAkB,CAAC,QAAe,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAEpD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAExE,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"console.d.ts","sourceRoot":"","sources":["../../src/formatters/console.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,GAAE,MAAiB,EAAE,MAAM,GAAE,OAAe,QA4IjH"}
|