@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,151 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { openDb } from "../util/db.js";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
export function diffSummaryCommand() {
|
|
6
|
+
return new Command("diff-summary")
|
|
7
|
+
.description("LLM-friendly summary of a commit range")
|
|
8
|
+
.argument("<range>", "Commit range (e.g. v1.0..v1.1 or HEAD~10..HEAD)")
|
|
9
|
+
.option("--json", "Output as JSON")
|
|
10
|
+
.action((range, opts) => {
|
|
11
|
+
const historyDir = join(process.cwd(), ".git-history");
|
|
12
|
+
const db = openDb(historyDir);
|
|
13
|
+
try {
|
|
14
|
+
// Get hashes in range
|
|
15
|
+
let revListOutput;
|
|
16
|
+
try {
|
|
17
|
+
revListOutput = execSync(`git rev-list ${range}`, {
|
|
18
|
+
cwd: process.cwd(),
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
process.stderr.write(`Could not resolve range: ${range}\n`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const hashes = revListOutput
|
|
27
|
+
.split("\n")
|
|
28
|
+
.map((h) => h.trim())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
if (hashes.length === 0) {
|
|
31
|
+
const empty = {
|
|
32
|
+
range,
|
|
33
|
+
commits: [],
|
|
34
|
+
authors: [],
|
|
35
|
+
added_files: [],
|
|
36
|
+
modified_files: [],
|
|
37
|
+
deleted_files: [],
|
|
38
|
+
total_insertions: 0,
|
|
39
|
+
total_deletions: 0,
|
|
40
|
+
decision_points: 0,
|
|
41
|
+
};
|
|
42
|
+
if (opts.json) {
|
|
43
|
+
process.stdout.write(JSON.stringify(empty, null, 2) + "\n");
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
process.stdout.write(`No commits found in range: ${range}\n`);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const placeholders = hashes.map(() => "?").join(",");
|
|
51
|
+
// Query commits
|
|
52
|
+
const commitRows = db
|
|
53
|
+
.prepare(`SELECT hash, author, timestamp, message, insertions, deletions
|
|
54
|
+
FROM commits
|
|
55
|
+
WHERE hash IN (${placeholders})
|
|
56
|
+
ORDER BY timestamp ASC`)
|
|
57
|
+
.all(...hashes);
|
|
58
|
+
// Query files
|
|
59
|
+
const fileRows = db
|
|
60
|
+
.prepare(`SELECT file_path, status, insertions, deletions
|
|
61
|
+
FROM commit_files
|
|
62
|
+
WHERE commit_hash IN (${placeholders})`)
|
|
63
|
+
.all(...hashes);
|
|
64
|
+
// Decision points in range
|
|
65
|
+
const dpRow = db
|
|
66
|
+
.prepare(`SELECT COUNT(*) AS cnt FROM decision_points WHERE commit_hash IN (${placeholders})`)
|
|
67
|
+
.get(...hashes);
|
|
68
|
+
// Compute stats
|
|
69
|
+
const authorSet = new Set();
|
|
70
|
+
let totalInsertions = 0;
|
|
71
|
+
let totalDeletions = 0;
|
|
72
|
+
for (const c of commitRows) {
|
|
73
|
+
authorSet.add(c.author);
|
|
74
|
+
totalInsertions += c.insertions ?? 0;
|
|
75
|
+
totalDeletions += c.deletions ?? 0;
|
|
76
|
+
}
|
|
77
|
+
const addedFiles = [];
|
|
78
|
+
const modifiedFiles = [];
|
|
79
|
+
const deletedFiles = [];
|
|
80
|
+
const seenFiles = new Set();
|
|
81
|
+
for (const f of fileRows) {
|
|
82
|
+
if (seenFiles.has(f.file_path))
|
|
83
|
+
continue;
|
|
84
|
+
seenFiles.add(f.file_path);
|
|
85
|
+
if (f.status === "A")
|
|
86
|
+
addedFiles.push(f.file_path);
|
|
87
|
+
else if (f.status === "D")
|
|
88
|
+
deletedFiles.push(f.file_path);
|
|
89
|
+
else
|
|
90
|
+
modifiedFiles.push(f.file_path);
|
|
91
|
+
}
|
|
92
|
+
const result = {
|
|
93
|
+
range,
|
|
94
|
+
commits: commitRows.map((c) => ({
|
|
95
|
+
hash: c.hash,
|
|
96
|
+
author: c.author,
|
|
97
|
+
timestamp: c.timestamp,
|
|
98
|
+
message: c.message,
|
|
99
|
+
})),
|
|
100
|
+
authors: [...authorSet],
|
|
101
|
+
added_files: addedFiles,
|
|
102
|
+
modified_files: modifiedFiles,
|
|
103
|
+
deleted_files: deletedFiles,
|
|
104
|
+
total_insertions: totalInsertions,
|
|
105
|
+
total_deletions: totalDeletions,
|
|
106
|
+
decision_points: dpRow?.cnt ?? 0,
|
|
107
|
+
};
|
|
108
|
+
if (opts.json) {
|
|
109
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Text output
|
|
113
|
+
process.stdout.write(`Diff Summary: ${range}\n`);
|
|
114
|
+
process.stdout.write(`${"─".repeat(80)}\n`);
|
|
115
|
+
process.stdout.write(`Commits: ${commitRows.length}\n`);
|
|
116
|
+
process.stdout.write(`Authors: ${[...authorSet].join(", ")}\n`);
|
|
117
|
+
process.stdout.write(`Insertions: +${totalInsertions}\n`);
|
|
118
|
+
process.stdout.write(`Deletions: -${totalDeletions}\n`);
|
|
119
|
+
process.stdout.write(`Decision pts: ${dpRow?.cnt ?? 0}\n`);
|
|
120
|
+
process.stdout.write(`\n`);
|
|
121
|
+
if (addedFiles.length > 0) {
|
|
122
|
+
process.stdout.write(`Added files (${addedFiles.length}):\n`);
|
|
123
|
+
for (const f of addedFiles)
|
|
124
|
+
process.stdout.write(` + ${f}\n`);
|
|
125
|
+
process.stdout.write(`\n`);
|
|
126
|
+
}
|
|
127
|
+
if (deletedFiles.length > 0) {
|
|
128
|
+
process.stdout.write(`Deleted files (${deletedFiles.length}):\n`);
|
|
129
|
+
for (const f of deletedFiles)
|
|
130
|
+
process.stdout.write(` - ${f}\n`);
|
|
131
|
+
process.stdout.write(`\n`);
|
|
132
|
+
}
|
|
133
|
+
if (modifiedFiles.length > 0) {
|
|
134
|
+
process.stdout.write(`Modified files (${modifiedFiles.length}):\n`);
|
|
135
|
+
for (const f of modifiedFiles)
|
|
136
|
+
process.stdout.write(` ~ ${f}\n`);
|
|
137
|
+
process.stdout.write(`\n`);
|
|
138
|
+
}
|
|
139
|
+
process.stdout.write(`Commits:\n`);
|
|
140
|
+
for (const c of commitRows) {
|
|
141
|
+
const date = c.timestamp.slice(0, 10);
|
|
142
|
+
const hash = c.hash.slice(0, 7);
|
|
143
|
+
process.stdout.write(` ${date} ${hash} ${c.message}\n`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
db.close();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=diff-summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-summary.js","sourceRoot":"","sources":["../../src/commands/diff-summary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
function write(msg) { process.stdout.write(msg); }
|
|
3
|
+
export function getSkillDoc() {
|
|
4
|
+
return `## git-skill (Git History Intelligence)
|
|
5
|
+
|
|
6
|
+
### Quick Reference
|
|
7
|
+
| Command | Use For |
|
|
8
|
+
|---------|---------|
|
|
9
|
+
| \`git-skill search <query>\` | Search commit history |
|
|
10
|
+
| \`git-skill timeline <path>\` | File/directory evolution |
|
|
11
|
+
| \`git-skill blame <path>\` | Enhanced blame with enrichments |
|
|
12
|
+
| \`git-skill trends\` | Metric trends dashboard |
|
|
13
|
+
| \`git-skill hotspots\` | Files with most churn |
|
|
14
|
+
| \`git-skill coupling <path>\` | Co-changed file analysis |
|
|
15
|
+
| \`git-skill decisions\` | Major decision points |
|
|
16
|
+
| \`git-skill experts <path>\` | Who has most context |
|
|
17
|
+
| \`git-skill diff-summary <range>\` | Range summary |
|
|
18
|
+
| \`git-skill why <hash>\` | Commit intent/reasoning |
|
|
19
|
+
| \`git-skill regression\` | Change-point detection |
|
|
20
|
+
| \`git-skill doctor\` | Health check |
|
|
21
|
+
|
|
22
|
+
### Write Commands (require confirmation)
|
|
23
|
+
| \`git-skill snapshot\` | Full re-index |
|
|
24
|
+
| \`git-skill enrich [range]\` | Backfill LLM enrichments |
|
|
25
|
+
| \`git-skill release-notes <range>\` | Generate release notes |
|
|
26
|
+
| \`git-skill embed\` | Generate embeddings |
|
|
27
|
+
|
|
28
|
+
### Global Flags
|
|
29
|
+
- \`--json\` — structured output
|
|
30
|
+
- \`--limit N\` — cap results
|
|
31
|
+
- \`--since <date>\` / \`--until <date>\` — time filter`;
|
|
32
|
+
}
|
|
33
|
+
export function docsCommand() {
|
|
34
|
+
return new Command("docs")
|
|
35
|
+
.description("Output CLAUDE.md instruction snippet")
|
|
36
|
+
.action(() => { write(getSkillDoc() + "\n"); });
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/commands/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,SAAS,KAAK,CAAC,GAAW,IAAU,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,UAAU,WAAW;IACzB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;wDA2B+C,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;SACvB,WAAW,CAAC,sCAAsC,CAAC;SACnD,MAAM,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { existsSync, readFileSync } from "fs";
|
|
4
|
+
import { isGitRepo } from "../util/git.js";
|
|
5
|
+
import { hasHook } from "../util/hooks.js";
|
|
6
|
+
import { hasDb, openDb } from "../util/db.js";
|
|
7
|
+
function write(msg) { process.stdout.write(msg); }
|
|
8
|
+
export function doctorCommand() {
|
|
9
|
+
return new Command("doctor")
|
|
10
|
+
.description("Health check — verify git-skill setup")
|
|
11
|
+
.option("--json", "Output as JSON")
|
|
12
|
+
.action((opts) => {
|
|
13
|
+
const cwd = process.cwd();
|
|
14
|
+
const checks = [];
|
|
15
|
+
// 1. Git repo
|
|
16
|
+
const isRepo = isGitRepo(cwd);
|
|
17
|
+
checks.push({ name: "git repo", status: isRepo ? "pass" : "fail", message: isRepo ? "Git repository detected" : "Not a git repository" });
|
|
18
|
+
// 2. Post-commit hook
|
|
19
|
+
const gitDir = join(cwd, ".git");
|
|
20
|
+
const hookOk = isRepo && hasHook(gitDir);
|
|
21
|
+
checks.push({ name: "post-commit hook", status: hookOk ? "pass" : "warn", message: hookOk ? "Hook installed" : "Hook not installed" });
|
|
22
|
+
// 3. Database
|
|
23
|
+
const historyDir = join(cwd, ".git-history");
|
|
24
|
+
const dbOk = hasDb(historyDir);
|
|
25
|
+
checks.push({ name: "history.db", status: dbOk ? "pass" : "fail", message: dbOk ? "Database exists" : "No database" });
|
|
26
|
+
// 4. Snapshot freshness + commit count
|
|
27
|
+
if (dbOk) {
|
|
28
|
+
const db = openDb(historyDir);
|
|
29
|
+
const meta = db.prepare("SELECT value FROM schema_meta WHERE key = 'last_snapshot'").get();
|
|
30
|
+
if (meta) {
|
|
31
|
+
const hoursAgo = (Date.now() - new Date(meta.value).getTime()) / 3600000;
|
|
32
|
+
checks.push({ name: "snapshot freshness", status: hoursAgo < 24 ? "pass" : "warn", message: `Last snapshot: ${Math.round(hoursAgo)}h ago` });
|
|
33
|
+
const count = db.prepare("SELECT COUNT(*) as c FROM commits").get().c;
|
|
34
|
+
checks.push({ name: "commit count", status: "pass", message: `${count} commits indexed` });
|
|
35
|
+
}
|
|
36
|
+
db.close();
|
|
37
|
+
}
|
|
38
|
+
// 5. .gitignore
|
|
39
|
+
const gitignorePath = join(cwd, ".gitignore");
|
|
40
|
+
if (existsSync(gitignorePath)) {
|
|
41
|
+
const hasEntry = readFileSync(gitignorePath, "utf-8").includes(".git-history");
|
|
42
|
+
checks.push({ name: ".gitignore", status: hasEntry ? "pass" : "warn", message: hasEntry ? ".git-history/ in .gitignore" : ".git-history/ not in .gitignore" });
|
|
43
|
+
}
|
|
44
|
+
// Output
|
|
45
|
+
if (opts.json) {
|
|
46
|
+
write(JSON.stringify({ checks }, null, 2) + "\n");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
write("\ngit-skill doctor\n");
|
|
50
|
+
write("─".repeat(40) + "\n");
|
|
51
|
+
for (const c of checks) {
|
|
52
|
+
const icon = c.status === "pass" ? "✓" : c.status === "warn" ? "!" : "✗";
|
|
53
|
+
write(` ${icon} ${c.name}: ${c.message}\n`);
|
|
54
|
+
}
|
|
55
|
+
write("\n");
|
|
56
|
+
if (checks.some(c => c.status === "fail"))
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { openDb, hasDb } from "../util/db.js";
|
|
4
|
+
import { readConfig } from "../util/config.js";
|
|
5
|
+
import { generateEmbedding } from "../util/embedding.js";
|
|
6
|
+
export function embedCommand() {
|
|
7
|
+
return new Command("embed")
|
|
8
|
+
.description("Generate/refresh embeddings for commit messages")
|
|
9
|
+
.option("--limit <n>", "Max commits to embed", "100")
|
|
10
|
+
.option("--force", "Re-embed commits that already have embeddings")
|
|
11
|
+
.action(async (opts) => {
|
|
12
|
+
const config = readConfig();
|
|
13
|
+
if (!config?.embedding?.enabled || !config.embedding.url) {
|
|
14
|
+
process.stdout.write("Embeddings not configured. Set embedding.enabled and embedding.url in ~/.config/git-skill/config.json\n");
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const historyDir = join(process.cwd(), ".git-history");
|
|
18
|
+
if (!hasDb(historyDir)) {
|
|
19
|
+
process.stdout.write("No .git-history/ database found. Run `git-skill snapshot` first.\n");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const db = openDb(historyDir);
|
|
23
|
+
try {
|
|
24
|
+
const limit = parseInt(opts.limit, 10) || 100;
|
|
25
|
+
let commits;
|
|
26
|
+
if (opts.force) {
|
|
27
|
+
commits = db
|
|
28
|
+
.prepare("SELECT hash, message FROM commits ORDER BY timestamp DESC LIMIT ?")
|
|
29
|
+
.all(limit);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Get commits that don't have embeddings yet
|
|
33
|
+
commits = db
|
|
34
|
+
.prepare(`
|
|
35
|
+
SELECT c.hash, c.message
|
|
36
|
+
FROM commits c
|
|
37
|
+
LEFT JOIN embeddings e ON c.hash = e.commit_hash
|
|
38
|
+
WHERE e.commit_hash IS NULL
|
|
39
|
+
ORDER BY c.timestamp DESC
|
|
40
|
+
LIMIT ?
|
|
41
|
+
`)
|
|
42
|
+
.all(limit);
|
|
43
|
+
}
|
|
44
|
+
if (commits.length === 0) {
|
|
45
|
+
process.stdout.write("No commits to embed.\n");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
process.stdout.write(`Embedding ${commits.length} commits...\n`);
|
|
49
|
+
const insertEmbed = db.prepare(`
|
|
50
|
+
INSERT OR REPLACE INTO embeddings (commit_hash, content_type, vector, model, created_at)
|
|
51
|
+
VALUES (@commitHash, @contentType, @vector, @model, @createdAt)
|
|
52
|
+
`);
|
|
53
|
+
let successCount = 0;
|
|
54
|
+
let failCount = 0;
|
|
55
|
+
for (const commit of commits) {
|
|
56
|
+
const result = await generateEmbedding(commit.message);
|
|
57
|
+
if (result) {
|
|
58
|
+
const buf = Buffer.alloc(result.vector.length * 4);
|
|
59
|
+
const arr = new Float32Array(result.vector);
|
|
60
|
+
Buffer.from(arr.buffer).copy(buf);
|
|
61
|
+
insertEmbed.run({
|
|
62
|
+
commitHash: commit.hash,
|
|
63
|
+
contentType: "message",
|
|
64
|
+
vector: buf,
|
|
65
|
+
model: result.model,
|
|
66
|
+
createdAt: new Date().toISOString(),
|
|
67
|
+
});
|
|
68
|
+
successCount++;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
failCount++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
process.stdout.write(`Done. Embedded: ${successCount}, Failed: ${failCount}\n`);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
db.close();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../../src/commands/embed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
import { openDb, hasDb } from "../util/db.js";
|
|
5
|
+
import { readConfig } from "../util/config.js";
|
|
6
|
+
async function callLlm(url, model, apiKey, commit) {
|
|
7
|
+
const prompt = `Analyze this git commit and respond with ONLY a JSON object (no markdown, no explanation):
|
|
8
|
+
{
|
|
9
|
+
"intent": "one sentence describing what this commit does",
|
|
10
|
+
"reasoning": "why this change was likely made",
|
|
11
|
+
"category": "one of: feature, bugfix, refactor, docs, chore, test, style, perf",
|
|
12
|
+
"alternatives_considered": "brief note on alternatives (optional)",
|
|
13
|
+
"session_context": "broader context about what was being worked on (optional)"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Commit hash: ${commit.hash.slice(0, 7)}
|
|
17
|
+
Author: ${commit.author}
|
|
18
|
+
Date: ${commit.timestamp.slice(0, 10)}
|
|
19
|
+
Message: ${commit.message}`;
|
|
20
|
+
const headers = { "Content-Type": "application/json" };
|
|
21
|
+
if (apiKey)
|
|
22
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(url, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers,
|
|
27
|
+
body: JSON.stringify({
|
|
28
|
+
model,
|
|
29
|
+
messages: [{ role: "user", content: prompt }],
|
|
30
|
+
max_tokens: 300,
|
|
31
|
+
temperature: 0,
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok)
|
|
35
|
+
return null;
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
const text = data.choices?.[0]?.message?.content ?? data.content?.[0]?.text ?? "";
|
|
38
|
+
// Strip potential markdown code fences
|
|
39
|
+
const cleaned = text.replace(/^```(?:json)?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
|
|
40
|
+
return JSON.parse(cleaned);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function resolveEnvVar(val) {
|
|
47
|
+
if (!val)
|
|
48
|
+
return undefined;
|
|
49
|
+
const match = val.match(/^\$\{(.+)\}$/);
|
|
50
|
+
return match ? process.env[match[1]] : val;
|
|
51
|
+
}
|
|
52
|
+
export function enrichCommand() {
|
|
53
|
+
return new Command("enrich")
|
|
54
|
+
.description("Backfill LLM enrichments for commit history")
|
|
55
|
+
.argument("[range]", "Git range to filter commits (e.g. v1.0..v1.1)")
|
|
56
|
+
.option("--dry-run", "Show what would be enriched without doing it")
|
|
57
|
+
.option("--limit <n>", "Max commits to enrich", "50")
|
|
58
|
+
.action(async (range, opts) => {
|
|
59
|
+
const historyDir = join(process.cwd(), ".git-history");
|
|
60
|
+
if (!hasDb(historyDir)) {
|
|
61
|
+
process.stdout.write("No .git-history/ database found. Run `git-skill snapshot` first.\n");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const db = openDb(historyDir);
|
|
65
|
+
try {
|
|
66
|
+
const limit = parseInt(opts.limit, 10) || 50;
|
|
67
|
+
// Get unenriched commits
|
|
68
|
+
let commits = db
|
|
69
|
+
.prepare(`
|
|
70
|
+
SELECT c.hash, c.message, c.author, c.timestamp
|
|
71
|
+
FROM commits c
|
|
72
|
+
LEFT JOIN enrichments e ON c.hash = e.commit_hash
|
|
73
|
+
WHERE e.commit_hash IS NULL
|
|
74
|
+
ORDER BY c.timestamp DESC
|
|
75
|
+
LIMIT ?
|
|
76
|
+
`)
|
|
77
|
+
.all(limit);
|
|
78
|
+
// Filter by range if provided
|
|
79
|
+
if (range) {
|
|
80
|
+
try {
|
|
81
|
+
const rangeHashes = execSync(`git rev-list ${range}`, {
|
|
82
|
+
cwd: process.cwd(),
|
|
83
|
+
encoding: "utf-8",
|
|
84
|
+
timeout: 10000,
|
|
85
|
+
}).trim().split("\n").filter(Boolean);
|
|
86
|
+
const rangeSet = new Set(rangeHashes);
|
|
87
|
+
commits = commits.filter((c) => rangeSet.has(c.hash));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
process.stdout.write(`Warning: could not resolve range "${range}"\n`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (commits.length === 0) {
|
|
94
|
+
process.stdout.write("No commits to enrich.\n");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const config = readConfig();
|
|
98
|
+
const enrichEnabled = config?.enrichment?.enabled && config.enrichment.url;
|
|
99
|
+
if (opts.dryRun) {
|
|
100
|
+
process.stdout.write(`Would enrich ${commits.length} commit${commits.length !== 1 ? "s" : ""}:\n`);
|
|
101
|
+
for (const c of commits.slice(0, 10)) {
|
|
102
|
+
process.stdout.write(` ${c.hash.slice(0, 7)} ${c.message.slice(0, 60)}\n`);
|
|
103
|
+
}
|
|
104
|
+
if (commits.length > 10) {
|
|
105
|
+
process.stdout.write(` ... and ${commits.length - 10} more\n`);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (!enrichEnabled) {
|
|
110
|
+
process.stdout.write(`Enrichment not configured. Set enrichment.enabled and enrichment.url in ~/.config/git-skill/config.json\n`);
|
|
111
|
+
process.stdout.write(`${commits.length} commit${commits.length !== 1 ? "s" : ""} would be enriched.\n`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
process.stdout.write(`Enriching ${commits.length} commits...\n`);
|
|
115
|
+
const apiKey = resolveEnvVar(config.enrichment.apiKey);
|
|
116
|
+
const insertEnrichment = db.prepare(`
|
|
117
|
+
INSERT OR REPLACE INTO enrichments
|
|
118
|
+
(commit_hash, intent, reasoning, category, alternatives_considered, session_context)
|
|
119
|
+
VALUES
|
|
120
|
+
(@commitHash, @intent, @reasoning, @category, @alternativesConsidered, @sessionContext)
|
|
121
|
+
`);
|
|
122
|
+
let successCount = 0;
|
|
123
|
+
let failCount = 0;
|
|
124
|
+
for (const commit of commits) {
|
|
125
|
+
const result = await callLlm(config.enrichment.url, config.enrichment.model, apiKey, commit);
|
|
126
|
+
if (result) {
|
|
127
|
+
insertEnrichment.run({
|
|
128
|
+
commitHash: commit.hash,
|
|
129
|
+
intent: result.intent ?? null,
|
|
130
|
+
reasoning: result.reasoning ?? null,
|
|
131
|
+
category: result.category ?? null,
|
|
132
|
+
alternativesConsidered: result.alternatives_considered ?? null,
|
|
133
|
+
sessionContext: result.session_context ?? null,
|
|
134
|
+
});
|
|
135
|
+
successCount++;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
failCount++;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
process.stdout.write(`Done. Enriched: ${successCount}, Failed: ${failCount}\n`);
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
db.close();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=enrich.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enrich.js","sourceRoot":"","sources":["../../src/commands/enrich.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { openDb } from "../util/db.js";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
export function expertsCommand() {
|
|
5
|
+
return new Command("experts")
|
|
6
|
+
.description("Show who has most expertise in a path")
|
|
7
|
+
.argument("<path>", "File path or pattern to check expertise for")
|
|
8
|
+
.option("--limit <n>", "Max results", "10")
|
|
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
|
+
// Normalize the path: strip trailing slash for matching
|
|
16
|
+
const normalizedPath = filePath.replace(/\/+$/, "");
|
|
17
|
+
// Match rows where file_pattern equals or starts with the given prefix
|
|
18
|
+
const pattern = `${normalizedPath}%`;
|
|
19
|
+
const sql = `
|
|
20
|
+
SELECT
|
|
21
|
+
author,
|
|
22
|
+
file_pattern,
|
|
23
|
+
commit_count,
|
|
24
|
+
last_touched,
|
|
25
|
+
expertise_score
|
|
26
|
+
FROM author_expertise
|
|
27
|
+
WHERE file_pattern LIKE ?
|
|
28
|
+
ORDER BY expertise_score DESC
|
|
29
|
+
LIMIT ?
|
|
30
|
+
`;
|
|
31
|
+
const rows = db.prepare(sql).all(pattern, limit);
|
|
32
|
+
if (opts.json) {
|
|
33
|
+
process.stdout.write(JSON.stringify(rows, null, 2) + "\n");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (rows.length === 0) {
|
|
37
|
+
process.stdout.write(`No expertise data found for: ${filePath}\n`);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
process.stdout.write(`Experts for: ${filePath}\n`);
|
|
41
|
+
process.stdout.write(`${"─".repeat(80)}\n`);
|
|
42
|
+
const colWidth = Math.max(...rows.map((r) => r.author.length), 10);
|
|
43
|
+
process.stdout.write(`${"Author".padEnd(colWidth)} ${"Commits".padStart(7)} ${"Last Touched".padStart(12)} ${"Score".padStart(8)}\n`);
|
|
44
|
+
process.stdout.write(`${"─".repeat(colWidth + 32)}\n`);
|
|
45
|
+
for (const row of rows) {
|
|
46
|
+
const lastTouched = row.last_touched ? row.last_touched.slice(0, 10) : "unknown";
|
|
47
|
+
process.stdout.write(`${row.author.padEnd(colWidth)} ${String(row.commit_count).padStart(7)} ${lastTouched.padStart(12)} ${row.expertise_score.toFixed(3).padStart(8)}\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
db.close();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=experts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"experts.js","sourceRoot":"","sources":["../../src/commands/experts.ts"],"names":[],"mappings":"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"}
|