@m2015agg/git-skill 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/commands/approve.d.ts +2 -0
- package/dist/commands/approve.js +56 -0
- package/dist/commands/approve.js.map +1 -0
- package/dist/commands/blame.d.ts +2 -0
- package/dist/commands/blame.js +139 -0
- package/dist/commands/blame.js.map +1 -0
- package/dist/commands/capture.d.ts +2 -0
- package/dist/commands/capture.js +68 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/coupling.d.ts +2 -0
- package/dist/commands/coupling.js +48 -0
- package/dist/commands/coupling.js.map +1 -0
- package/dist/commands/cron.d.ts +2 -0
- package/dist/commands/cron.js +70 -0
- package/dist/commands/cron.js.map +1 -0
- package/dist/commands/decisions.d.ts +2 -0
- package/dist/commands/decisions.js +62 -0
- package/dist/commands/decisions.js.map +1 -0
- package/dist/commands/diff-summary.d.ts +2 -0
- package/dist/commands/diff-summary.js +151 -0
- package/dist/commands/diff-summary.js.map +1 -0
- package/dist/commands/docs.d.ts +3 -0
- package/dist/commands/docs.js +38 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +60 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/embed.d.ts +2 -0
- package/dist/commands/embed.js +81 -0
- package/dist/commands/embed.js.map +1 -0
- package/dist/commands/enrich.d.ts +2 -0
- package/dist/commands/enrich.js +148 -0
- package/dist/commands/enrich.js.map +1 -0
- package/dist/commands/experts.d.ts +2 -0
- package/dist/commands/experts.js +55 -0
- package/dist/commands/experts.js.map +1 -0
- package/dist/commands/hotspots.d.ts +2 -0
- package/dist/commands/hotspots.js +46 -0
- package/dist/commands/hotspots.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +91 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +59 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/metric.d.ts +2 -0
- package/dist/commands/metric.js +26 -0
- package/dist/commands/metric.js.map +1 -0
- package/dist/commands/regression.d.ts +2 -0
- package/dist/commands/regression.js +166 -0
- package/dist/commands/regression.js.map +1 -0
- package/dist/commands/release-notes.d.ts +2 -0
- package/dist/commands/release-notes.js +244 -0
- package/dist/commands/release-notes.js.map +1 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +93 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/snapshot.d.ts +6 -0
- package/dist/commands/snapshot.js +154 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/timeline.d.ts +2 -0
- package/dist/commands/timeline.js +78 -0
- package/dist/commands/timeline.js.map +1 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +92 -0
- package/dist/commands/trends.js.map +1 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +79 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +59 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/why.d.ts +2 -0
- package/dist/commands/why.js +111 -0
- package/dist/commands/why.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +65 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/walkthrough.d.ts +1 -0
- package/dist/templates/walkthrough.js +13 -0
- package/dist/templates/walkthrough.js.map +1 -0
- package/dist/util/analytics.d.ts +32 -0
- package/dist/util/analytics.js +308 -0
- package/dist/util/analytics.js.map +1 -0
- package/dist/util/claude-md.d.ts +2 -0
- package/dist/util/claude-md.js +41 -0
- package/dist/util/claude-md.js.map +1 -0
- package/dist/util/config.d.ts +21 -0
- package/dist/util/config.js +26 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/db.d.ts +3 -0
- package/dist/util/db.js +183 -0
- package/dist/util/db.js.map +1 -0
- package/dist/util/detect.d.ts +2 -0
- package/dist/util/detect.js +14 -0
- package/dist/util/detect.js.map +1 -0
- package/dist/util/embedding.d.ts +6 -0
- package/dist/util/embedding.js +48 -0
- package/dist/util/embedding.js.map +1 -0
- package/dist/util/git.d.ts +45 -0
- package/dist/util/git.js +191 -0
- package/dist/util/git.js.map +1 -0
- package/dist/util/hooks.d.ts +3 -0
- package/dist/util/hooks.js +43 -0
- package/dist/util/hooks.js.map +1 -0
- package/dist/util/metrics.d.ts +2 -0
- package/dist/util/metrics.js +42 -0
- package/dist/util/metrics.js.map +1 -0
- package/dist/util/search-hybrid.d.ts +10 -0
- package/dist/util/search-hybrid.js +33 -0
- package/dist/util/search-hybrid.js.map +1 -0
- package/package.json +35 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
function write(msg) { process.stdout.write(msg); }
|
|
6
|
+
const READ_COMMANDS = [
|
|
7
|
+
"Bash(git-skill search:*)",
|
|
8
|
+
"Bash(git-skill timeline:*)",
|
|
9
|
+
"Bash(git-skill blame:*)",
|
|
10
|
+
"Bash(git-skill trends:*)",
|
|
11
|
+
"Bash(git-skill hotspots:*)",
|
|
12
|
+
"Bash(git-skill coupling:*)",
|
|
13
|
+
"Bash(git-skill decisions:*)",
|
|
14
|
+
"Bash(git-skill experts:*)",
|
|
15
|
+
"Bash(git-skill diff-summary:*)",
|
|
16
|
+
"Bash(git-skill why:*)",
|
|
17
|
+
"Bash(git-skill regression:*)",
|
|
18
|
+
"Bash(git-skill doctor:*)",
|
|
19
|
+
"Bash(git-skill metric record:*)",
|
|
20
|
+
];
|
|
21
|
+
export function approveCommand() {
|
|
22
|
+
return new Command("approve")
|
|
23
|
+
.description("Pre-approve read-only commands in Claude Code")
|
|
24
|
+
.option("--global", "Apply to global ~/.claude/settings.json")
|
|
25
|
+
.option("--remove", "Remove pre-approved permissions")
|
|
26
|
+
.action((opts) => {
|
|
27
|
+
const settingsDir = opts.global ? join(homedir(), ".claude") : join(process.cwd(), ".claude");
|
|
28
|
+
const settingsPath = join(settingsDir, "settings.json");
|
|
29
|
+
mkdirSync(settingsDir, { recursive: true });
|
|
30
|
+
let settings = {};
|
|
31
|
+
if (existsSync(settingsPath)) {
|
|
32
|
+
try {
|
|
33
|
+
settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
settings = {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!settings.permissions)
|
|
40
|
+
settings.permissions = {};
|
|
41
|
+
if (!settings.permissions.allow)
|
|
42
|
+
settings.permissions.allow = [];
|
|
43
|
+
if (opts.remove) {
|
|
44
|
+
settings.permissions.allow = settings.permissions.allow.filter((p) => !READ_COMMANDS.includes(p));
|
|
45
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
46
|
+
write(`Removed ${READ_COMMANDS.length} git-skill permissions.\n`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const existing = new Set(settings.permissions.allow);
|
|
50
|
+
const added = READ_COMMANDS.filter(c => !existing.has(c));
|
|
51
|
+
settings.permissions.allow = [...settings.permissions.allow, ...added];
|
|
52
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
53
|
+
write(`Pre-approved ${added.length} commands (${READ_COMMANDS.length} total).\n`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=approve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approve.js","sourceRoot":"","sources":["../../src/commands/approve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { openDb } from "../util/db.js";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
function parsePorcelain(output) {
|
|
6
|
+
const lines = output.split("\n");
|
|
7
|
+
const result = new Map();
|
|
8
|
+
let currentHash = "";
|
|
9
|
+
let currentAuthor = "";
|
|
10
|
+
let currentTimestamp = "";
|
|
11
|
+
let currentLine = 0;
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
// Header line: <hash> <orig-line> <final-line> [<num-lines>]
|
|
14
|
+
const headerMatch = line.match(/^([0-9a-f]{40}) \d+ (\d+)/);
|
|
15
|
+
if (headerMatch) {
|
|
16
|
+
currentHash = headerMatch[1];
|
|
17
|
+
currentLine = parseInt(headerMatch[2], 10);
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (line.startsWith("author ") && !line.startsWith("author-")) {
|
|
21
|
+
currentAuthor = line.slice(7);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (line.startsWith("author-time ")) {
|
|
25
|
+
const epochSecs = parseInt(line.slice(12), 10);
|
|
26
|
+
currentTimestamp = new Date(epochSecs * 1000).toISOString();
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
// Content line (starts with tab) signals end of this entry
|
|
30
|
+
if (line.startsWith("\t")) {
|
|
31
|
+
result.set(currentLine, {
|
|
32
|
+
hash: currentHash,
|
|
33
|
+
author: currentAuthor,
|
|
34
|
+
timestamp: currentTimestamp,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
export function blameCommand() {
|
|
41
|
+
return new Command("blame")
|
|
42
|
+
.description("Enhanced blame combining git blame with enrichment data")
|
|
43
|
+
.argument("<path>", "File path to blame")
|
|
44
|
+
.option("--json", "Output as JSON")
|
|
45
|
+
.action((filePath, opts) => {
|
|
46
|
+
const historyDir = join(process.cwd(), ".git-history");
|
|
47
|
+
const db = openDb(historyDir);
|
|
48
|
+
try {
|
|
49
|
+
// Run git blame --porcelain
|
|
50
|
+
let porcelainOutput;
|
|
51
|
+
try {
|
|
52
|
+
porcelainOutput = execSync(`git blame --porcelain ${filePath}`, {
|
|
53
|
+
cwd: process.cwd(),
|
|
54
|
+
encoding: "utf-8",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
process.stderr.write(`Could not run git blame on: ${filePath}\n`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const lineMap = parsePorcelain(porcelainOutput);
|
|
62
|
+
// Get unique hashes
|
|
63
|
+
const hashes = [...new Set([...lineMap.values()].map((v) => v.hash))];
|
|
64
|
+
// Look up commits for messages
|
|
65
|
+
const commitMessages = new Map();
|
|
66
|
+
const enrichmentData = new Map();
|
|
67
|
+
if (hashes.length > 0) {
|
|
68
|
+
const placeholders = hashes.map(() => "?").join(",");
|
|
69
|
+
const commitRows = db
|
|
70
|
+
.prepare(`SELECT hash, message FROM commits WHERE hash IN (${placeholders})`)
|
|
71
|
+
.all(...hashes);
|
|
72
|
+
for (const row of commitRows) {
|
|
73
|
+
commitMessages.set(row.hash, row.message);
|
|
74
|
+
}
|
|
75
|
+
const enrichRows = db
|
|
76
|
+
.prepare(`SELECT commit_hash, intent, category FROM enrichments WHERE commit_hash IN (${placeholders})`)
|
|
77
|
+
.all(...hashes);
|
|
78
|
+
for (const row of enrichRows) {
|
|
79
|
+
enrichmentData.set(row.commit_hash, { intent: row.intent, category: row.category });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Group consecutive lines by hash into ranges
|
|
83
|
+
const entries = [];
|
|
84
|
+
let currentEntry = null;
|
|
85
|
+
const sortedLines = [...lineMap.entries()].sort((a, b) => a[0] - b[0]);
|
|
86
|
+
for (const [lineNum, info] of sortedLines) {
|
|
87
|
+
if (currentEntry && currentEntry.hash === info.hash && currentEntry.lineEnd === lineNum - 1) {
|
|
88
|
+
currentEntry.lineEnd = lineNum;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
if (currentEntry)
|
|
92
|
+
entries.push(currentEntry);
|
|
93
|
+
currentEntry = {
|
|
94
|
+
hash: info.hash,
|
|
95
|
+
author: info.author,
|
|
96
|
+
timestamp: info.timestamp,
|
|
97
|
+
lineStart: lineNum,
|
|
98
|
+
lineEnd: lineNum,
|
|
99
|
+
message: commitMessages.get(info.hash),
|
|
100
|
+
intent: enrichmentData.get(info.hash)?.intent,
|
|
101
|
+
category: enrichmentData.get(info.hash)?.category,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (currentEntry)
|
|
106
|
+
entries.push(currentEntry);
|
|
107
|
+
if (opts.json) {
|
|
108
|
+
process.stdout.write(JSON.stringify(entries, null, 2) + "\n");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (entries.length === 0) {
|
|
112
|
+
process.stdout.write(`No blame data found for: ${filePath}\n`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
process.stdout.write(`Blame: ${filePath}\n`);
|
|
116
|
+
process.stdout.write(`${"─".repeat(80)}\n`);
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
const hash = entry.hash.slice(0, 7);
|
|
119
|
+
const date = entry.timestamp.slice(0, 10);
|
|
120
|
+
const lines = entry.lineStart === entry.lineEnd
|
|
121
|
+
? `L${entry.lineStart}`
|
|
122
|
+
: `L${entry.lineStart}-${entry.lineEnd}`;
|
|
123
|
+
const message = entry.message ?? "(no message)";
|
|
124
|
+
process.stdout.write(`${lines.padEnd(12)} ${hash} ${date} ${entry.author}\n`);
|
|
125
|
+
process.stdout.write(`${"".padEnd(12)} ${message}\n`);
|
|
126
|
+
if (entry.intent) {
|
|
127
|
+
process.stdout.write(`${"".padEnd(12)} intent: ${entry.intent}\n`);
|
|
128
|
+
}
|
|
129
|
+
if (entry.category) {
|
|
130
|
+
process.stdout.write(`${"".padEnd(12)} category: ${entry.category}\n`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
db.close();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=blame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blame.js","sourceRoot":"","sources":["../../src/commands/blame.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { openDb } from "../util/db.js";
|
|
3
|
+
import { getLog, getDiffTree, getLastCommitHash } from "../util/git.js";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { existsSync } from "fs";
|
|
6
|
+
function write(msg) {
|
|
7
|
+
process.stdout.write(msg);
|
|
8
|
+
}
|
|
9
|
+
export function captureCommand() {
|
|
10
|
+
return new Command("capture")
|
|
11
|
+
.description("Capture the latest commit (called by post-commit hook)")
|
|
12
|
+
.option("--hook", "Called from post-commit hook (suppress output)")
|
|
13
|
+
.option("--hash <hash>", "Specific commit hash to capture")
|
|
14
|
+
.action((opts) => {
|
|
15
|
+
const cwd = process.cwd();
|
|
16
|
+
const historyDir = join(cwd, ".git-history");
|
|
17
|
+
if (!existsSync(historyDir)) {
|
|
18
|
+
if (!opts.hook)
|
|
19
|
+
write("No .git-history/ found. Run `git-skill init` first.\n");
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const db = openDb(historyDir);
|
|
23
|
+
try {
|
|
24
|
+
const hash = opts.hash || getLastCommitHash(cwd);
|
|
25
|
+
if (!hash) {
|
|
26
|
+
if (!opts.hook)
|
|
27
|
+
write("Could not determine last commit hash.\n");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Check if already captured (idempotency)
|
|
31
|
+
const existing = db.prepare("SELECT hash FROM commits WHERE hash = ?").get(hash);
|
|
32
|
+
if (existing) {
|
|
33
|
+
if (!opts.hook)
|
|
34
|
+
write(`Commit ${hash.slice(0, 7)} already captured.\n`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// Get commit data (just the one commit)
|
|
38
|
+
const commits = getLog(cwd, { limit: 1 });
|
|
39
|
+
if (commits.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
const commit = commits[0];
|
|
42
|
+
// Insert commit
|
|
43
|
+
db.prepare(`
|
|
44
|
+
INSERT OR IGNORE INTO commits (hash, message, author, email, timestamp, branch, parent_hash, merge_commit, insertions, deletions, files_changed)
|
|
45
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
46
|
+
`).run(commit.hash, commit.message, commit.author, commit.email, commit.timestamp, commit.branch, commit.parentHash, commit.mergeCommit ? 1 : 0, commit.insertions, commit.deletions, commit.filesChanged);
|
|
47
|
+
// Insert commit files
|
|
48
|
+
const files = getDiffTree(cwd, commit.hash);
|
|
49
|
+
const insertFile = db.prepare(`
|
|
50
|
+
INSERT INTO commit_files (commit_hash, file_path, status, insertions, deletions, old_path)
|
|
51
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
52
|
+
`);
|
|
53
|
+
const insertMany = db.transaction((fileList) => {
|
|
54
|
+
for (const f of fileList) {
|
|
55
|
+
insertFile.run(commit.hash, f.path, f.status, f.insertions, f.deletions, f.oldPath);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
insertMany(files);
|
|
59
|
+
if (!opts.hook) {
|
|
60
|
+
write(`Captured ${commit.hash.slice(0, 7)}: ${commit.message} (${files.length} files)\n`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
db.close();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/commands/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { openDb } from "../util/db.js";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function couplingCommand() {
|
|
5
|
+
return new Command("coupling")
|
|
6
|
+
.description("Show files that co-change with a given path")
|
|
7
|
+
.argument("<path>", "File path to check coupling for")
|
|
8
|
+
.option("--limit <n>", "Max results", "20")
|
|
9
|
+
.option("--json", "Output as JSON")
|
|
10
|
+
.action((filePath, opts) => {
|
|
11
|
+
const historyDir = join(process.cwd(), ".git-history");
|
|
12
|
+
const db = openDb(historyDir);
|
|
13
|
+
try {
|
|
14
|
+
const limit = parseInt(opts.limit, 10);
|
|
15
|
+
const sql = `
|
|
16
|
+
SELECT
|
|
17
|
+
CASE WHEN file_a = ? THEN file_b ELSE file_a END AS paired_file,
|
|
18
|
+
co_commit_count,
|
|
19
|
+
coupling_score
|
|
20
|
+
FROM coupling
|
|
21
|
+
WHERE file_a = ? OR file_b = ?
|
|
22
|
+
ORDER BY coupling_score DESC
|
|
23
|
+
LIMIT ?
|
|
24
|
+
`;
|
|
25
|
+
const rows = db.prepare(sql).all(filePath, filePath, filePath, limit);
|
|
26
|
+
if (opts.json) {
|
|
27
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + "\n");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (rows.length === 0) {
|
|
31
|
+
process.stdout.write(`No coupling data found for: ${filePath}\n`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
process.stdout.write(`Files that co-change with: ${filePath}\n`);
|
|
35
|
+
process.stdout.write(`${"─".repeat(80)}\n`);
|
|
36
|
+
const colWidth = Math.max(...rows.map((r) => r.paired_file.length), 10);
|
|
37
|
+
process.stdout.write(`${"Paired File".padEnd(colWidth)} ${"Co-Commits".padStart(10)} ${"Score".padStart(8)}\n`);
|
|
38
|
+
process.stdout.write(`${"─".repeat(colWidth + 22)}\n`);
|
|
39
|
+
for (const row of rows) {
|
|
40
|
+
process.stdout.write(`${row.paired_file.padEnd(colWidth)} ${String(row.co_commit_count).padStart(10)} ${row.coupling_score.toFixed(3).padStart(8)}\n`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
db.close();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=coupling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coupling.js","sourceRoot":"","sources":["../../src/commands/coupling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,eAAe;IAC7B,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;SAC3B,WAAW,CAAC,6CAA6C,CAAC;SAC1D,QAAQ,CAAC,QAAQ,EAAE,iCAAiC,CAAC;SACrD,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC;SAC1C,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,CAAC,QAAgB,EAAE,IAAuC,EAAE,EAAE;QACpE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,CAAC;QACvD,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAEvC,MAAM,GAAG,GAAG;;;;;;;;;SASX,CAAC;YAEF,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAIlE,CAAC;YAEH,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,QAAQ,IAAI,CAAC,CAAC;gBAClE,OAAO;YACT,CAAC;YAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,QAAQ,IAAI,CAAC,CAAC;YACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACxE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAC5F,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACvD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CACnI,CAAC;YACJ,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { execSync, spawnSync } from "child_process";
|
|
3
|
+
function write(msg) { process.stdout.write(msg); }
|
|
4
|
+
const CRON_MARKER = "# git-skill";
|
|
5
|
+
function getCrontab() {
|
|
6
|
+
try {
|
|
7
|
+
return execSync("crontab -l", { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] });
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return "";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function setCrontab(content) {
|
|
14
|
+
const result = spawnSync("crontab", ["-"], {
|
|
15
|
+
input: content,
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
18
|
+
});
|
|
19
|
+
if (result.status !== 0) {
|
|
20
|
+
throw new Error(`crontab write failed: ${result.stderr}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function buildCronLine(cwd) {
|
|
24
|
+
return `0 3 * * * cd ${cwd} && git-skill snapshot ${CRON_MARKER}`;
|
|
25
|
+
}
|
|
26
|
+
export function cronCommand() {
|
|
27
|
+
return new Command("cron")
|
|
28
|
+
.description("Set up nightly snapshot via crontab")
|
|
29
|
+
.option("--status", "Show current cron entry")
|
|
30
|
+
.option("--remove", "Remove cron entry")
|
|
31
|
+
.action((opts) => {
|
|
32
|
+
const crontab = getCrontab();
|
|
33
|
+
if (opts.status) {
|
|
34
|
+
const lines = crontab.split("\n").filter(l => l.includes(CRON_MARKER));
|
|
35
|
+
if (lines.length === 0) {
|
|
36
|
+
write("No git-skill cron entry found.\n");
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
write("Current git-skill cron entries:\n");
|
|
40
|
+
lines.forEach(l => write(` ${l}\n`));
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (opts.remove) {
|
|
45
|
+
const filtered = crontab.split("\n").filter(l => !l.includes(CRON_MARKER)).join("\n");
|
|
46
|
+
setCrontab(filtered.trim() ? filtered : "");
|
|
47
|
+
write("Removed git-skill cron entry.\n");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Add cron entry
|
|
51
|
+
const cwd = process.cwd();
|
|
52
|
+
const cronLine = buildCronLine(cwd);
|
|
53
|
+
if (crontab.includes(CRON_MARKER)) {
|
|
54
|
+
// Replace existing entry
|
|
55
|
+
const updated = crontab
|
|
56
|
+
.split("\n")
|
|
57
|
+
.map(l => l.includes(CRON_MARKER) ? cronLine : l)
|
|
58
|
+
.join("\n");
|
|
59
|
+
setCrontab(updated);
|
|
60
|
+
write(`Updated cron entry for ${cwd}.\n`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const separator = crontab.trim() ? "\n" : "";
|
|
64
|
+
setCrontab(crontab + separator + cronLine + "\n");
|
|
65
|
+
write(`Added nightly snapshot cron job (3 AM) for ${cwd}.\n`);
|
|
66
|
+
}
|
|
67
|
+
write(`Entry: ${cronLine}\n`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=cron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.js","sourceRoot":"","sources":["../../src/commands/cron.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { openDb } from "../util/db.js";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function decisionsCommand() {
|
|
5
|
+
return new Command("decisions")
|
|
6
|
+
.description("Show decision points (reverts, refactors, architecture changes)")
|
|
7
|
+
.option("--type <type>", "Filter by decision type (e.g. revert, big_refactor, architecture_change)")
|
|
8
|
+
.option("--limit <n>", "Max results", "20")
|
|
9
|
+
.option("--json", "Output as JSON")
|
|
10
|
+
.action((opts) => {
|
|
11
|
+
const historyDir = join(process.cwd(), ".git-history");
|
|
12
|
+
const db = openDb(historyDir);
|
|
13
|
+
try {
|
|
14
|
+
const limit = parseInt(opts.limit, 10);
|
|
15
|
+
const params = [];
|
|
16
|
+
let typeClause = "";
|
|
17
|
+
if (opts.type) {
|
|
18
|
+
typeClause = "WHERE dp.type = ?";
|
|
19
|
+
params.push(opts.type);
|
|
20
|
+
}
|
|
21
|
+
params.push(limit);
|
|
22
|
+
const sql = `
|
|
23
|
+
SELECT
|
|
24
|
+
dp.id,
|
|
25
|
+
dp.commit_hash,
|
|
26
|
+
dp.type,
|
|
27
|
+
dp.impact_score,
|
|
28
|
+
dp.files_affected,
|
|
29
|
+
c.message,
|
|
30
|
+
c.author,
|
|
31
|
+
c.timestamp
|
|
32
|
+
FROM decision_points dp
|
|
33
|
+
JOIN commits c ON dp.commit_hash = c.hash
|
|
34
|
+
${typeClause}
|
|
35
|
+
ORDER BY dp.impact_score DESC
|
|
36
|
+
LIMIT ?
|
|
37
|
+
`;
|
|
38
|
+
const rows = db.prepare(sql).all(...params);
|
|
39
|
+
if (opts.json) {
|
|
40
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + "\n");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (rows.length === 0) {
|
|
44
|
+
process.stdout.write("No decision points found.\n");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
process.stdout.write(`Decision Points\n`);
|
|
48
|
+
process.stdout.write(`${"─".repeat(80)}\n`);
|
|
49
|
+
for (const row of rows) {
|
|
50
|
+
const hash = row.commit_hash.slice(0, 7);
|
|
51
|
+
const date = row.timestamp.slice(0, 10);
|
|
52
|
+
process.stdout.write(`${hash} [${row.type}] impact: ${row.impact_score.toFixed(2)} files: ${row.files_affected} ${date}\n`);
|
|
53
|
+
process.stdout.write(` ${row.message}\n`);
|
|
54
|
+
process.stdout.write(` by ${row.author}\n\n`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
db.close();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../src/commands/decisions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,UAAU,gBAAgB;IAC9B,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC;SAC5B,WAAW,CAAC,iEAAiE,CAAC;SAC9E,MAAM,CAAC,eAAe,EAAE,0EAA0E,CAAC;SACnG,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC;SAC1C,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,CAAC,IAAsD,EAAE,EAAE;QACjE,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,CAAC;QACvD,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACvC,MAAM,MAAM,GAAwB,EAAE,CAAC;YACvC,IAAI,UAAU,GAAG,EAAE,CAAC;YAEpB,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,UAAU,GAAG,mBAAmB,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzB,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAEnB,MAAM,GAAG,GAAG;;;;;;;;;;;;YAYR,UAAU;;;SAGb,CAAC;YAEF,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CASxC,CAAC;YAEH,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC3D,OAAO;YACT,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YAC5C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,MAAM,IAAI,GAAG,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACxC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,MAAM,GAAG,CAAC,IAAI,cAAc,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,cAAc,KAAK,IAAI,IAAI,CAC1G,CAAC;gBACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;gBAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|