gitbun 0.1.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/dist/bin/smartcommit.js +16 -0
- package/dist/src/analyzer/scopeDetector.js +17 -0
- package/dist/src/analyzer/summarizer.js +9 -0
- package/dist/src/analyzer/typeClassifier.js +22 -0
- package/dist/src/config/loadConfig.js +9 -0
- package/dist/src/generator/commitGenerator.js +34 -0
- package/dist/src/git/checkRepo.js +17 -0
- package/dist/src/git/commit.js +16 -0
- package/dist/src/git/getDiffStats.js +19 -0
- package/dist/src/git/getStagedFiles.js +22 -0
- package/dist/src/index.js +86 -0
- package/dist/src/llm/checkOllama.js +16 -0
- package/dist/src/llm/ollamaEnhancer.js +65 -0
- package/dist/src/ui/interactive.js +33 -0
- package/package.json +55 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const index_1 = require("../src/index");
|
|
6
|
+
commander_1.program
|
|
7
|
+
.name("smartcommit")
|
|
8
|
+
.description("AI-powered commit assistant")
|
|
9
|
+
.version("0.1.0")
|
|
10
|
+
.option("--ai", "Enhance commit message using AI")
|
|
11
|
+
.option("--auto", "Auto accept commit without confirmation")
|
|
12
|
+
.option("--model <name>", "Specify Ollama model");
|
|
13
|
+
commander_1.program.action(async (options) => {
|
|
14
|
+
await (0, index_1.run)(options);
|
|
15
|
+
});
|
|
16
|
+
commander_1.program.parse();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectScope = detectScope;
|
|
4
|
+
function detectScope(filePaths) {
|
|
5
|
+
const scopes = {};
|
|
6
|
+
for (const path of filePaths) {
|
|
7
|
+
const parts = path.split("/");
|
|
8
|
+
const srcIndex = parts.indexOf("src");
|
|
9
|
+
if (srcIndex !== -1 && parts[srcIndex + 1]) {
|
|
10
|
+
const scope = parts[srcIndex + 1];
|
|
11
|
+
scopes[scope] = (scopes[scope] || 0) + 1;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (Object.keys(scopes).length === 0)
|
|
15
|
+
return "core";
|
|
16
|
+
return Object.entries(scopes).sort((a, b) => b[1] - a[1])[0][0];
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSummary = generateSummary;
|
|
4
|
+
function generateSummary(files) {
|
|
5
|
+
const summaries = files.map(file => {
|
|
6
|
+
return `${file.path} (+${file.additions} - ${file.deletions})`;
|
|
7
|
+
});
|
|
8
|
+
return summaries.join("\n");
|
|
9
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifyCommitType = classifyCommitType;
|
|
4
|
+
function classifyCommitType(files) {
|
|
5
|
+
let totalAdd = 0;
|
|
6
|
+
let totalDel = 0;
|
|
7
|
+
for (const file of files) {
|
|
8
|
+
totalAdd += file.additions;
|
|
9
|
+
totalDel += file.deletions;
|
|
10
|
+
if (file.path.includes("test"))
|
|
11
|
+
return "test";
|
|
12
|
+
if (file.path.includes(".md"))
|
|
13
|
+
return "docs";
|
|
14
|
+
if (file.path.includes("lock"))
|
|
15
|
+
return "chore";
|
|
16
|
+
}
|
|
17
|
+
if (totalAdd > totalDel * 2)
|
|
18
|
+
return "feat";
|
|
19
|
+
if (totalDel > totalAdd * 2)
|
|
20
|
+
return "refactor";
|
|
21
|
+
return "fix";
|
|
22
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
const cosmiconfig_1 = require("cosmiconfig");
|
|
5
|
+
async function loadConfig() {
|
|
6
|
+
const explorer = (0, cosmiconfig_1.cosmiconfig)("smartcommit");
|
|
7
|
+
const result = await explorer.search();
|
|
8
|
+
return result?.config || {};
|
|
9
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateCommitMessage = generateCommitMessage;
|
|
4
|
+
function generateCommitMessage(type, scope, files) {
|
|
5
|
+
const fileNames = files.map(file => {
|
|
6
|
+
const parts = file.path.split("/");
|
|
7
|
+
return parts[parts.length - 1].replace(/\.[^/.]+$/, "");
|
|
8
|
+
});
|
|
9
|
+
const uniqueNames = Array.from(new Set(fileNames));
|
|
10
|
+
let description = "";
|
|
11
|
+
if (uniqueNames.length === 1) {
|
|
12
|
+
description = `update ${uniqueNames[0]} module`;
|
|
13
|
+
}
|
|
14
|
+
else if (uniqueNames.length <= 3) {
|
|
15
|
+
description = `update ${uniqueNames.join(" and ")} modules`;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
description = `update multiple ${scope} files`;
|
|
19
|
+
}
|
|
20
|
+
const message = `${type}(${scope}): ${description}`;
|
|
21
|
+
return enforceRules(message);
|
|
22
|
+
}
|
|
23
|
+
function enforceRules(message) {
|
|
24
|
+
const parts = message.split(": ");
|
|
25
|
+
if (parts.length === 2) {
|
|
26
|
+
parts[1] = parts[1].charAt(0).toLowerCase() + parts[1].slice(1);
|
|
27
|
+
}
|
|
28
|
+
let formatted = parts.join(": ");
|
|
29
|
+
formatted = formatted.replace(/\.$/, "");
|
|
30
|
+
if (formatted.length > 72) {
|
|
31
|
+
formatted = formatted.slice(0, 69) + "...";
|
|
32
|
+
}
|
|
33
|
+
return formatted;
|
|
34
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isGitRepo = isGitRepo;
|
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
|
+
async function isGitRepo() {
|
|
9
|
+
const git = (0, simple_git_1.default)();
|
|
10
|
+
try {
|
|
11
|
+
return await git.checkIsRepo();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.commit = commit;
|
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
|
+
async function commit(message) {
|
|
9
|
+
const git = (0, simple_git_1.default)();
|
|
10
|
+
const output = await git.raw([
|
|
11
|
+
"commit",
|
|
12
|
+
"-m",
|
|
13
|
+
message
|
|
14
|
+
]);
|
|
15
|
+
return output;
|
|
16
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getDiffStats = getDiffStats;
|
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
|
+
async function getDiffStats(filePath) {
|
|
9
|
+
const git = (0, simple_git_1.default)();
|
|
10
|
+
const diff = await git.diff(["--cached", "--numstat", filePath]);
|
|
11
|
+
if (!diff.trim()) {
|
|
12
|
+
return { additions: 0, deletions: 0 };
|
|
13
|
+
}
|
|
14
|
+
const parts = diff.trim().split("\t");
|
|
15
|
+
return {
|
|
16
|
+
additions: Number(parts[0]) || 0,
|
|
17
|
+
deletions: Number(parts[1]) || 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getStagedFiles = getStagedFiles;
|
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
|
+
async function getStagedFiles() {
|
|
9
|
+
const git = (0, simple_git_1.default)();
|
|
10
|
+
const status = await git.status();
|
|
11
|
+
return status.staged.map(file => ({
|
|
12
|
+
path: file,
|
|
13
|
+
status: getStatusType(status, file)
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
function getStatusType(status, file) {
|
|
17
|
+
if (status.created.includes(file))
|
|
18
|
+
return "A";
|
|
19
|
+
if (status.deleted.includes(file))
|
|
20
|
+
return "D";
|
|
21
|
+
return "M";
|
|
22
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.run = run;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const checkRepo_1 = require("./git/checkRepo");
|
|
10
|
+
const getStagedFiles_1 = require("./git/getStagedFiles");
|
|
11
|
+
const getDiffStats_1 = require("./git/getDiffStats");
|
|
12
|
+
const scopeDetector_1 = require("./analyzer/scopeDetector");
|
|
13
|
+
const typeClassifier_1 = require("./analyzer/typeClassifier");
|
|
14
|
+
const summarizer_1 = require("./analyzer/summarizer");
|
|
15
|
+
const commitGenerator_1 = require("./generator/commitGenerator");
|
|
16
|
+
const interactive_1 = require("./ui/interactive");
|
|
17
|
+
const commit_1 = require("./git/commit");
|
|
18
|
+
const ollamaEnhancer_1 = require("./llm/ollamaEnhancer");
|
|
19
|
+
const loadConfig_1 = require("./config/loadConfig");
|
|
20
|
+
const checkOllama_1 = require("./llm/checkOllama");
|
|
21
|
+
async function run(options) {
|
|
22
|
+
// Ensure we are inside a Git repo
|
|
23
|
+
const repo = await (0, checkRepo_1.isGitRepo)();
|
|
24
|
+
if (!repo) {
|
|
25
|
+
console.log(chalk_1.default.red("Not inside a Git repository."));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const stagedFiles = await (0, getStagedFiles_1.getStagedFiles)();
|
|
29
|
+
if (stagedFiles.length === 0) {
|
|
30
|
+
console.log(chalk_1.default.yellow("No staged changes found."));
|
|
31
|
+
console.log("Stage changes using: git add <file>");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
// Enrich file stats
|
|
35
|
+
const enrichedFiles = [];
|
|
36
|
+
for (const file of stagedFiles) {
|
|
37
|
+
const stats = await (0, getDiffStats_1.getDiffStats)(file.path);
|
|
38
|
+
enrichedFiles.push({
|
|
39
|
+
path: file.path,
|
|
40
|
+
additions: stats.additions,
|
|
41
|
+
deletions: stats.deletions
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const scope = (0, scopeDetector_1.detectScope)(enrichedFiles.map(f => f.path));
|
|
45
|
+
const type = (0, typeClassifier_1.classifyCommitType)(enrichedFiles);
|
|
46
|
+
const summary = (0, summarizer_1.generateSummary)(enrichedFiles);
|
|
47
|
+
let commitMessage = (0, commitGenerator_1.generateCommitMessage)(type, scope, enrichedFiles);
|
|
48
|
+
// Load config
|
|
49
|
+
const config = await (0, loadConfig_1.loadConfig)();
|
|
50
|
+
const model = options.model ||
|
|
51
|
+
config.model ||
|
|
52
|
+
"deepseek-coder:6.7b";
|
|
53
|
+
// AI enhancement (optional)
|
|
54
|
+
if (options.ai) {
|
|
55
|
+
const running = await (0, checkOllama_1.isOllamaRunning)();
|
|
56
|
+
if (!running) {
|
|
57
|
+
console.log(chalk_1.default.yellow("Ollama is not running. Using rule-based commit."));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const spinner = (0, ora_1.default)("Enhancing commit with AI...").start();
|
|
61
|
+
try {
|
|
62
|
+
commitMessage = await (0, ollamaEnhancer_1.enhanceCommit)(commitMessage, summary, model);
|
|
63
|
+
spinner.succeed();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
spinner.fail("AI enhancement failed");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Confirmation flow
|
|
71
|
+
let finalMessage;
|
|
72
|
+
if (options.auto) {
|
|
73
|
+
finalMessage = commitMessage;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const result = await (0, interactive_1.confirmCommit)(commitMessage);
|
|
77
|
+
if (!result) {
|
|
78
|
+
console.log("Commit cancelled.");
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
finalMessage = result;
|
|
82
|
+
}
|
|
83
|
+
// Perform commit (git-native output)
|
|
84
|
+
const output = await (0, commit_1.commit)(finalMessage);
|
|
85
|
+
console.log("\n" + output);
|
|
86
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isOllamaRunning = isOllamaRunning;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
async function isOllamaRunning() {
|
|
9
|
+
try {
|
|
10
|
+
const res = await (0, node_fetch_1.default)("http://localhost:11434");
|
|
11
|
+
return res.ok;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.enhanceCommit = enhanceCommit;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
async function enhanceCommit(originalMessage, summary, model) {
|
|
9
|
+
const prompt = `
|
|
10
|
+
You are a senior software engineer.
|
|
11
|
+
|
|
12
|
+
Rewrite ONLY the description part of this Conventional Commit.
|
|
13
|
+
Do NOT add explanations.
|
|
14
|
+
Do NOT add prefixes like "Improved:".
|
|
15
|
+
Return exactly one single-line commit message.
|
|
16
|
+
Keep format: <type>(<scope>): <description>
|
|
17
|
+
Max 72 characters.
|
|
18
|
+
Description must start lowercase.
|
|
19
|
+
|
|
20
|
+
Original commit:
|
|
21
|
+
${originalMessage}
|
|
22
|
+
|
|
23
|
+
Changes:
|
|
24
|
+
${summary}
|
|
25
|
+
`;
|
|
26
|
+
try {
|
|
27
|
+
const response = await (0, node_fetch_1.default)("http://localhost:11434/api/generate", {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
model: model, // ✅ now dynamic
|
|
32
|
+
prompt,
|
|
33
|
+
stream: false
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
const data = (await response.json());
|
|
37
|
+
// Handle model-not-found or API errors
|
|
38
|
+
if (data.error) {
|
|
39
|
+
console.log(`Ollama error: ${data.error}`);
|
|
40
|
+
return originalMessage;
|
|
41
|
+
}
|
|
42
|
+
if (!data.response) {
|
|
43
|
+
console.log("AI returned unexpected format:", data);
|
|
44
|
+
return originalMessage;
|
|
45
|
+
}
|
|
46
|
+
let result = data.response.trim();
|
|
47
|
+
// Remove common LLM prefixes
|
|
48
|
+
result = result.replace(/^Improved:\s*/i, "");
|
|
49
|
+
result = result.replace(/^Here.*:\s*/i, "");
|
|
50
|
+
// Convert past tense to imperative
|
|
51
|
+
result = result.replace(/\bupdated\b/i, "update");
|
|
52
|
+
result = result.replace(/\badded\b/i, "add");
|
|
53
|
+
result = result.replace(/\bfixed\b/i, "fix");
|
|
54
|
+
result = result.replace(/\bremoved\b/i, "remove");
|
|
55
|
+
// Take only first line
|
|
56
|
+
result = result.split("\n")[0];
|
|
57
|
+
// Remove trailing period
|
|
58
|
+
result = result.replace(/\.$/, "");
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.log("AI Enhancement Failed:", error);
|
|
63
|
+
return originalMessage;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.confirmCommit = confirmCommit;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
async function confirmCommit(message) {
|
|
9
|
+
console.log("\n" + message + "\n");
|
|
10
|
+
const { action } = await inquirer_1.default.prompt([
|
|
11
|
+
{
|
|
12
|
+
type: "input",
|
|
13
|
+
name: "action",
|
|
14
|
+
message: "Accept commit? (Y/n/e)",
|
|
15
|
+
default: "Y"
|
|
16
|
+
}
|
|
17
|
+
]);
|
|
18
|
+
const value = action.toLowerCase();
|
|
19
|
+
if (value === "y" || value === "")
|
|
20
|
+
return message;
|
|
21
|
+
if (value === "e") {
|
|
22
|
+
const { edited } = await inquirer_1.default.prompt([
|
|
23
|
+
{
|
|
24
|
+
type: "input",
|
|
25
|
+
name: "edited",
|
|
26
|
+
message: "Edit commit:",
|
|
27
|
+
default: message
|
|
28
|
+
}
|
|
29
|
+
]);
|
|
30
|
+
return edited;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gitbun",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A comfy git commit assistant with optional AI enhancement",
|
|
5
|
+
"main": "dist/src/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"gitbun": "dist/bin/smartcommit.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "ts-node bin/smartcommit.ts",
|
|
18
|
+
"start": "node dist/bin/smartcommit.js"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"git",
|
|
22
|
+
"commit",
|
|
23
|
+
"cli",
|
|
24
|
+
"developer-tools",
|
|
25
|
+
"conventional-commits"
|
|
26
|
+
],
|
|
27
|
+
"author": "Nirvik Goswami",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"chalk": "^5.6.2",
|
|
34
|
+
"commander": "^14.0.3",
|
|
35
|
+
"cosmiconfig": "^9.0.0",
|
|
36
|
+
"inquirer": "^13.2.4",
|
|
37
|
+
"node-fetch": "^3.3.2",
|
|
38
|
+
"ora": "^9.3.0",
|
|
39
|
+
"simple-git": "^3.31.1"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^25.2.3",
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "https://github.com/nirvik34/gitbun.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/nirvik34/gitbun/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/nirvik34/gitbun#readme"
|
|
54
|
+
|
|
55
|
+
}
|