@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.
Files changed (112) hide show
  1. package/dist/commands/approve.d.ts +2 -0
  2. package/dist/commands/approve.js +56 -0
  3. package/dist/commands/approve.js.map +1 -0
  4. package/dist/commands/blame.d.ts +2 -0
  5. package/dist/commands/blame.js +139 -0
  6. package/dist/commands/blame.js.map +1 -0
  7. package/dist/commands/capture.d.ts +2 -0
  8. package/dist/commands/capture.js +68 -0
  9. package/dist/commands/capture.js.map +1 -0
  10. package/dist/commands/coupling.d.ts +2 -0
  11. package/dist/commands/coupling.js +48 -0
  12. package/dist/commands/coupling.js.map +1 -0
  13. package/dist/commands/cron.d.ts +2 -0
  14. package/dist/commands/cron.js +70 -0
  15. package/dist/commands/cron.js.map +1 -0
  16. package/dist/commands/decisions.d.ts +2 -0
  17. package/dist/commands/decisions.js +62 -0
  18. package/dist/commands/decisions.js.map +1 -0
  19. package/dist/commands/diff-summary.d.ts +2 -0
  20. package/dist/commands/diff-summary.js +151 -0
  21. package/dist/commands/diff-summary.js.map +1 -0
  22. package/dist/commands/docs.d.ts +3 -0
  23. package/dist/commands/docs.js +38 -0
  24. package/dist/commands/docs.js.map +1 -0
  25. package/dist/commands/doctor.d.ts +2 -0
  26. package/dist/commands/doctor.js +60 -0
  27. package/dist/commands/doctor.js.map +1 -0
  28. package/dist/commands/embed.d.ts +2 -0
  29. package/dist/commands/embed.js +81 -0
  30. package/dist/commands/embed.js.map +1 -0
  31. package/dist/commands/enrich.d.ts +2 -0
  32. package/dist/commands/enrich.js +148 -0
  33. package/dist/commands/enrich.js.map +1 -0
  34. package/dist/commands/experts.d.ts +2 -0
  35. package/dist/commands/experts.js +55 -0
  36. package/dist/commands/experts.js.map +1 -0
  37. package/dist/commands/hotspots.d.ts +2 -0
  38. package/dist/commands/hotspots.js +46 -0
  39. package/dist/commands/hotspots.js.map +1 -0
  40. package/dist/commands/init.d.ts +2 -0
  41. package/dist/commands/init.js +91 -0
  42. package/dist/commands/init.js.map +1 -0
  43. package/dist/commands/install.d.ts +2 -0
  44. package/dist/commands/install.js +59 -0
  45. package/dist/commands/install.js.map +1 -0
  46. package/dist/commands/metric.d.ts +2 -0
  47. package/dist/commands/metric.js +26 -0
  48. package/dist/commands/metric.js.map +1 -0
  49. package/dist/commands/regression.d.ts +2 -0
  50. package/dist/commands/regression.js +166 -0
  51. package/dist/commands/regression.js.map +1 -0
  52. package/dist/commands/release-notes.d.ts +2 -0
  53. package/dist/commands/release-notes.js +244 -0
  54. package/dist/commands/release-notes.js.map +1 -0
  55. package/dist/commands/search.d.ts +2 -0
  56. package/dist/commands/search.js +93 -0
  57. package/dist/commands/search.js.map +1 -0
  58. package/dist/commands/snapshot.d.ts +6 -0
  59. package/dist/commands/snapshot.js +154 -0
  60. package/dist/commands/snapshot.js.map +1 -0
  61. package/dist/commands/timeline.d.ts +2 -0
  62. package/dist/commands/timeline.js +78 -0
  63. package/dist/commands/timeline.js.map +1 -0
  64. package/dist/commands/trends.d.ts +2 -0
  65. package/dist/commands/trends.js +92 -0
  66. package/dist/commands/trends.js.map +1 -0
  67. package/dist/commands/uninstall.d.ts +2 -0
  68. package/dist/commands/uninstall.js +79 -0
  69. package/dist/commands/uninstall.js.map +1 -0
  70. package/dist/commands/update.d.ts +2 -0
  71. package/dist/commands/update.js +59 -0
  72. package/dist/commands/update.js.map +1 -0
  73. package/dist/commands/why.d.ts +2 -0
  74. package/dist/commands/why.js +111 -0
  75. package/dist/commands/why.js.map +1 -0
  76. package/dist/index.d.ts +2 -0
  77. package/dist/index.js +65 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/templates/walkthrough.d.ts +1 -0
  80. package/dist/templates/walkthrough.js +13 -0
  81. package/dist/templates/walkthrough.js.map +1 -0
  82. package/dist/util/analytics.d.ts +32 -0
  83. package/dist/util/analytics.js +308 -0
  84. package/dist/util/analytics.js.map +1 -0
  85. package/dist/util/claude-md.d.ts +2 -0
  86. package/dist/util/claude-md.js +41 -0
  87. package/dist/util/claude-md.js.map +1 -0
  88. package/dist/util/config.d.ts +21 -0
  89. package/dist/util/config.js +26 -0
  90. package/dist/util/config.js.map +1 -0
  91. package/dist/util/db.d.ts +3 -0
  92. package/dist/util/db.js +183 -0
  93. package/dist/util/db.js.map +1 -0
  94. package/dist/util/detect.d.ts +2 -0
  95. package/dist/util/detect.js +14 -0
  96. package/dist/util/detect.js.map +1 -0
  97. package/dist/util/embedding.d.ts +6 -0
  98. package/dist/util/embedding.js +48 -0
  99. package/dist/util/embedding.js.map +1 -0
  100. package/dist/util/git.d.ts +45 -0
  101. package/dist/util/git.js +191 -0
  102. package/dist/util/git.js.map +1 -0
  103. package/dist/util/hooks.d.ts +3 -0
  104. package/dist/util/hooks.js +43 -0
  105. package/dist/util/hooks.js.map +1 -0
  106. package/dist/util/metrics.d.ts +2 -0
  107. package/dist/util/metrics.js +42 -0
  108. package/dist/util/metrics.js.map +1 -0
  109. package/dist/util/search-hybrid.d.ts +10 -0
  110. package/dist/util/search-hybrid.js +33 -0
  111. package/dist/util/search-hybrid.js.map +1 -0
  112. package/package.json +35 -0
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Populates file_evolution table: file_path, first_seen, last_modified, total_commits, total_churn
3
+ */
4
+ export function computeFileEvolution(db) {
5
+ db.exec("DELETE FROM file_evolution");
6
+ db.exec(`
7
+ INSERT INTO file_evolution (file_path, first_seen, last_modified, total_commits, total_churn)
8
+ SELECT
9
+ cf.file_path,
10
+ MIN(c.timestamp) AS first_seen,
11
+ MAX(c.timestamp) AS last_modified,
12
+ COUNT(DISTINCT c.hash) AS total_commits,
13
+ SUM(cf.insertions + cf.deletions) AS total_churn
14
+ FROM commit_files cf
15
+ JOIN commits c ON c.hash = cf.commit_hash
16
+ GROUP BY cf.file_path
17
+ `);
18
+ }
19
+ /**
20
+ * Populates churn_hotspots: file_path, period (strftime week), commits, insertions, deletions, unique_authors
21
+ */
22
+ export function computeChurnHotspots(db) {
23
+ db.exec("DELETE FROM churn_hotspots");
24
+ db.exec(`
25
+ INSERT INTO churn_hotspots (file_path, period, commits, insertions, deletions, unique_authors)
26
+ SELECT
27
+ cf.file_path,
28
+ strftime('%Y-W%W', c.timestamp) AS period,
29
+ COUNT(DISTINCT c.hash) AS commits,
30
+ SUM(cf.insertions) AS insertions,
31
+ SUM(cf.deletions) AS deletions,
32
+ COUNT(DISTINCT c.author) AS unique_authors
33
+ FROM commit_files cf
34
+ JOIN commits c ON c.hash = cf.commit_hash
35
+ GROUP BY cf.file_path, strftime('%Y-W%W', c.timestamp)
36
+ `);
37
+ }
38
+ /**
39
+ * Populates coupling: file_a, file_b, co_commit_count, coupling_score
40
+ * Uses Jaccard-style scoring: co_commit_count / max(total_commits_a, total_commits_b)
41
+ */
42
+ export function computeCoupling(db) {
43
+ db.exec("DELETE FROM coupling");
44
+ // Get eligible commits: more than 1 file but no more than 50
45
+ const eligibleCommits = db
46
+ .prepare(`SELECT hash FROM commits WHERE files_changed > 1 AND files_changed <= 50`)
47
+ .all();
48
+ if (eligibleCommits.length === 0)
49
+ return;
50
+ // Get files per commit
51
+ const getFiles = db.prepare(`SELECT file_path FROM commit_files WHERE commit_hash = ?`);
52
+ // Count co-occurrences
53
+ const coCommitCounts = new Map();
54
+ const fileTotalCommits = new Map();
55
+ for (const { hash } of eligibleCommits) {
56
+ const files = getFiles.all(hash).map((r) => r.file_path);
57
+ // Track per-file commit counts
58
+ for (const f of files) {
59
+ fileTotalCommits.set(f, (fileTotalCommits.get(f) ?? 0) + 1);
60
+ }
61
+ // Generate all pairs (sorted so file_a < file_b lexicographically)
62
+ for (let i = 0; i < files.length; i++) {
63
+ for (let j = i + 1; j < files.length; j++) {
64
+ const a = files[i] < files[j] ? files[i] : files[j];
65
+ const b = files[i] < files[j] ? files[j] : files[i];
66
+ const key = `${a}\0${b}`;
67
+ coCommitCounts.set(key, (coCommitCounts.get(key) ?? 0) + 1);
68
+ }
69
+ }
70
+ }
71
+ const insertCoupling = db.prepare(`
72
+ INSERT INTO coupling (file_a, file_b, co_commit_count, coupling_score)
73
+ VALUES (?, ?, ?, ?)
74
+ `);
75
+ const insertAll = db.transaction(() => {
76
+ for (const [key, count] of coCommitCounts) {
77
+ if (count < 2)
78
+ continue;
79
+ const [a, b] = key.split("\0");
80
+ const totalA = fileTotalCommits.get(a) ?? 1;
81
+ const totalB = fileTotalCommits.get(b) ?? 1;
82
+ const score = count / Math.max(totalA, totalB);
83
+ insertCoupling.run(a, b, count, score);
84
+ }
85
+ });
86
+ insertAll();
87
+ }
88
+ /**
89
+ * Populates decision_points: commit_hash, type, impact_score, files_affected
90
+ */
91
+ export function computeDecisionPoints(db) {
92
+ db.exec("DELETE FROM decision_points");
93
+ const commits = db
94
+ .prepare(`SELECT hash, message, insertions, deletions, files_changed, timestamp FROM commits`)
95
+ .all();
96
+ const getFiles = db.prepare(`SELECT file_path, status FROM commit_files WHERE commit_hash = ?`);
97
+ const depFiles = new Set([
98
+ "package.json",
99
+ "requirements.txt",
100
+ "Cargo.toml",
101
+ "go.mod",
102
+ ]);
103
+ const configPatterns = [/\.env(\.|$)/i, /\.config(\.|$)/i];
104
+ const insert = db.prepare(`
105
+ INSERT INTO decision_points (commit_hash, type, impact_score, files_affected)
106
+ VALUES (?, ?, ?, ?)
107
+ `);
108
+ const checkNewDir = db.prepare(`
109
+ SELECT COUNT(*) as c FROM commit_files cf
110
+ JOIN commits cm ON cm.hash = cf.commit_hash
111
+ WHERE cf.file_path LIKE ? AND cm.hash != ? AND cm.timestamp < ?
112
+ `);
113
+ const insertAll = db.transaction(() => {
114
+ for (const commit of commits) {
115
+ const types = [];
116
+ const impact = (commit.insertions ?? 0) + (commit.deletions ?? 0);
117
+ // Revert detection
118
+ if (/revert/i.test(commit.message)) {
119
+ types.push("revert");
120
+ }
121
+ // Big refactor: >= 20 files changed
122
+ if ((commit.files_changed ?? 0) >= 20) {
123
+ types.push("big_refactor");
124
+ }
125
+ // Major removal: deletions > insertions * 2 AND deletions > 20
126
+ if ((commit.deletions ?? 0) > (commit.insertions ?? 0) * 2 &&
127
+ (commit.deletions ?? 0) > 20) {
128
+ types.push("major_removal");
129
+ }
130
+ // File-based detections
131
+ const files = getFiles.all(commit.hash);
132
+ const filePaths = files.map((f) => f.file_path);
133
+ const fileNames = filePaths.map((p) => p.split("/").pop() ?? p);
134
+ // Dependency change
135
+ if (fileNames.some((n) => depFiles.has(n))) {
136
+ types.push("dependency_change");
137
+ }
138
+ // Config change
139
+ if (filePaths.some((p) => configPatterns.some((re) => re.test(p)))) {
140
+ types.push("config_change");
141
+ }
142
+ // Architecture change: new top-level src dirs created
143
+ const newDirs = new Set();
144
+ for (const f of files) {
145
+ if (f.status === "A") {
146
+ const parts = f.file_path.split("/");
147
+ // e.g. src/core/app.ts -> top-level dir under src is "src/core"
148
+ if (parts.length >= 3 && parts[0] === "src") {
149
+ newDirs.add(`${parts[0]}/${parts[1]}`);
150
+ }
151
+ }
152
+ }
153
+ if (newDirs.size > 0) {
154
+ // Check if any of these dirs are brand new (no prior commits touching them)
155
+ for (const srcDir of newDirs) {
156
+ const prior = checkNewDir.get(`${srcDir}/%`, commit.hash, commit.timestamp);
157
+ if (prior.c === 0) {
158
+ types.push("architecture_change");
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ // Insert one row per detected type
164
+ for (const type of types) {
165
+ insert.run(commit.hash, type, impact, commit.files_changed ?? 0);
166
+ }
167
+ }
168
+ });
169
+ insertAll();
170
+ }
171
+ /**
172
+ * Populates author_expertise: author, file_pattern (directory), commit_count, last_touched, expertise_score
173
+ * Groups by author and first 2 path segments.
174
+ */
175
+ export function computeAuthorExpertise(db) {
176
+ db.exec("DELETE FROM author_expertise");
177
+ db.exec(`
178
+ INSERT INTO author_expertise (author, file_pattern, commit_count, last_touched, expertise_score)
179
+ SELECT
180
+ c.author,
181
+ CASE
182
+ WHEN instr(cf.file_path, '/') = 0 THEN cf.file_path
183
+ ELSE
184
+ CASE
185
+ WHEN instr(substr(cf.file_path, instr(cf.file_path, '/') + 1), '/') = 0
186
+ THEN cf.file_path
187
+ ELSE
188
+ substr(cf.file_path, 1,
189
+ instr(cf.file_path, '/') +
190
+ instr(substr(cf.file_path, instr(cf.file_path, '/') + 1), '/') - 1
191
+ )
192
+ END
193
+ END AS dir_prefix,
194
+ COUNT(DISTINCT c.hash) AS commit_count,
195
+ MAX(c.timestamp) AS last_touched,
196
+ CAST(COUNT(DISTINCT c.hash) AS REAL) / (
197
+ SELECT CAST(COUNT(DISTINCT c2.hash) AS REAL)
198
+ FROM commits c2
199
+ JOIN commit_files cf2 ON cf2.commit_hash = c2.hash
200
+ WHERE cf2.file_path LIKE
201
+ CASE
202
+ WHEN instr(cf.file_path, '/') = 0 THEN cf.file_path || '%'
203
+ ELSE
204
+ CASE
205
+ WHEN instr(substr(cf.file_path, instr(cf.file_path, '/') + 1), '/') = 0
206
+ THEN cf.file_path || '%'
207
+ ELSE
208
+ substr(cf.file_path, 1,
209
+ instr(cf.file_path, '/') +
210
+ instr(substr(cf.file_path, instr(cf.file_path, '/') + 1), '/') - 1
211
+ ) || '%'
212
+ END
213
+ END
214
+ ) AS expertise_score
215
+ FROM commit_files cf
216
+ JOIN commits c ON c.hash = cf.commit_hash
217
+ GROUP BY c.author,
218
+ CASE
219
+ WHEN instr(cf.file_path, '/') = 0 THEN cf.file_path
220
+ ELSE
221
+ CASE
222
+ WHEN instr(substr(cf.file_path, instr(cf.file_path, '/') + 1), '/') = 0
223
+ THEN cf.file_path
224
+ ELSE
225
+ substr(cf.file_path, 1,
226
+ instr(cf.file_path, '/') +
227
+ instr(substr(cf.file_path, instr(cf.file_path, '/') + 1), '/') - 1
228
+ )
229
+ END
230
+ END
231
+ `);
232
+ }
233
+ /**
234
+ * Populates trends: metric_name, period, value, delta, direction
235
+ * Computes weekly stats and compares to previous period.
236
+ */
237
+ export function computeTrends(db) {
238
+ db.exec("DELETE FROM trends");
239
+ // Compute weekly stats
240
+ const weeklyStats = db
241
+ .prepare(`SELECT
242
+ strftime('%Y-W%W', timestamp) AS period,
243
+ COUNT(*) AS commits,
244
+ AVG(files_changed) AS avg_files,
245
+ SUM(insertions + deletions) AS total_churn,
246
+ SUM(CASE WHEN lower(message) LIKE '%revert%' THEN 1 ELSE 0 END) AS reverts,
247
+ SUM(CASE WHEN lower(message) LIKE 'fix%' OR lower(message) LIKE '%fix:%' THEN 1 ELSE 0 END) AS fixes
248
+ FROM commits
249
+ GROUP BY strftime('%Y-W%W', timestamp)
250
+ ORDER BY period`)
251
+ .all();
252
+ if (weeklyStats.length === 0)
253
+ return;
254
+ const insert = db.prepare(`
255
+ INSERT INTO trends (metric_name, period, value, delta, direction)
256
+ VALUES (?, ?, ?, ?, ?)
257
+ `);
258
+ const metrics = [
259
+ { name: "commits_per_week", getValue: (s) => s.commits },
260
+ { name: "avg_files_per_commit", getValue: (s) => s.avg_files ?? 0 },
261
+ { name: "total_churn", getValue: (s) => s.total_churn ?? 0 },
262
+ {
263
+ name: "revert_rate",
264
+ getValue: (s) => (s.commits > 0 ? s.reverts / s.commits : 0),
265
+ },
266
+ {
267
+ name: "fix_rate",
268
+ getValue: (s) => (s.commits > 0 ? s.fixes / s.commits : 0),
269
+ },
270
+ ];
271
+ const insertAll = db.transaction(() => {
272
+ for (const metric of metrics) {
273
+ let prevValue = null;
274
+ for (const week of weeklyStats) {
275
+ const value = metric.getValue(week);
276
+ let delta = null;
277
+ let direction = null;
278
+ if (prevValue !== null) {
279
+ delta = value - prevValue;
280
+ if (Math.abs(delta) < 0.001) {
281
+ direction = "stable";
282
+ }
283
+ else if (delta > 0) {
284
+ direction = "up";
285
+ }
286
+ else {
287
+ direction = "down";
288
+ }
289
+ }
290
+ insert.run(metric.name, week.period, value, delta, direction);
291
+ prevValue = value;
292
+ }
293
+ }
294
+ });
295
+ insertAll();
296
+ }
297
+ /**
298
+ * Runs all analytics computations in order.
299
+ */
300
+ export function runAllAnalytics(db) {
301
+ computeFileEvolution(db);
302
+ computeChurnHotspots(db);
303
+ computeCoupling(db);
304
+ computeDecisionPoints(db);
305
+ computeAuthorExpertise(db);
306
+ computeTrends(db);
307
+ }
308
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/util/analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function upsertSection(filePath: string, content: string): "created" | "updated" | "unchanged";
2
+ export declare function removeSection(filePath: string): "removed" | "not_found";
@@ -0,0 +1,41 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
+ import { dirname } from "path";
3
+ const MARKER_START = "<!-- git-skill:start -->";
4
+ const MARKER_END = "<!-- git-skill:end -->";
5
+ export function upsertSection(filePath, content) {
6
+ const snippet = `${MARKER_START}\n${content}\n${MARKER_END}`;
7
+ if (!existsSync(filePath)) {
8
+ mkdirSync(dirname(filePath), { recursive: true });
9
+ writeFileSync(filePath, snippet + "\n");
10
+ return "created";
11
+ }
12
+ const existing = readFileSync(filePath, "utf-8");
13
+ const startIdx = existing.indexOf(MARKER_START);
14
+ const endIdx = existing.indexOf(MARKER_END);
15
+ if (startIdx !== -1 && endIdx !== -1) {
16
+ const currentSection = existing.slice(startIdx, endIdx + MARKER_END.length);
17
+ if (currentSection === snippet)
18
+ return "unchanged";
19
+ const updated = existing.slice(0, startIdx) + snippet + existing.slice(endIdx + MARKER_END.length);
20
+ writeFileSync(filePath, updated);
21
+ return "updated";
22
+ }
23
+ const separator = existing.endsWith("\n") ? "\n" : "\n\n";
24
+ writeFileSync(filePath, existing + separator + snippet + "\n");
25
+ return "updated";
26
+ }
27
+ export function removeSection(filePath) {
28
+ if (!existsSync(filePath))
29
+ return "not_found";
30
+ const content = readFileSync(filePath, "utf-8");
31
+ const startIdx = content.indexOf(MARKER_START);
32
+ const endIdx = content.indexOf(MARKER_END);
33
+ if (startIdx === -1 || endIdx === -1)
34
+ return "not_found";
35
+ const before = content.slice(0, startIdx).replace(/\n+$/, "");
36
+ const after = content.slice(endIdx + MARKER_END.length).replace(/^\n+/, "");
37
+ const result = before + (after ? "\n\n" + after : "") + "\n";
38
+ writeFileSync(filePath, result);
39
+ return "removed";
40
+ }
41
+ //# sourceMappingURL=claude-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-md.js","sourceRoot":"","sources":["../../src/util/claude-md.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAChD,MAAM,UAAU,GAAG,wBAAwB,CAAC;AAE5C,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,OAAe;IAC7D,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,OAAO,KAAK,UAAU,EAAE,CAAC;IAE7D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC;QACxC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAE5C,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACrC,MAAM,cAAc,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5E,IAAI,cAAc,KAAK,OAAO;YAAE,OAAO,WAAW,CAAC;QACnD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;QACnG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1D,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;IAC/D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,WAAW,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,MAAM,KAAK,CAAC,CAAC;QAAE,OAAO,WAAW,CAAC;IACzD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IAC7D,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,21 @@
1
+ export interface GitSkillConfig {
2
+ embedding: {
3
+ enabled: boolean;
4
+ provider: string;
5
+ model: string;
6
+ url: string;
7
+ apiKey: string;
8
+ dimensions: number;
9
+ };
10
+ enrichment: {
11
+ enabled: boolean;
12
+ url: string;
13
+ model: string;
14
+ apiKey: string;
15
+ batchSize: number;
16
+ maxTokensPerCommit: number;
17
+ };
18
+ }
19
+ export declare function getDefaultConfig(): GitSkillConfig;
20
+ export declare function readConfig(): GitSkillConfig | null;
21
+ export declare function writeConfig(config: GitSkillConfig): void;
@@ -0,0 +1,26 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
2
+ import { join } from "path";
3
+ import { homedir } from "os";
4
+ const CONFIG_DIR = join(homedir(), ".config", "git-skill");
5
+ const CONFIG_PATH = join(CONFIG_DIR, "config.json");
6
+ export function getDefaultConfig() {
7
+ return {
8
+ embedding: { enabled: false, provider: "openai", model: "text-embedding-3-small", url: "", apiKey: "", dimensions: 1536 },
9
+ enrichment: { enabled: false, url: "", model: "gpt-4o-mini", apiKey: "", batchSize: 10, maxTokensPerCommit: 500 },
10
+ };
11
+ }
12
+ export function readConfig() {
13
+ try {
14
+ if (!existsSync(CONFIG_PATH))
15
+ return null;
16
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ export function writeConfig(config) {
23
+ mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
24
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), { mode: 0o600 });
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/util/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAqB7B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;AAC3D,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpD,MAAM,UAAU,gBAAgB;IAC9B,OAAO;QACL,SAAS,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;QACzH,UAAU,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,kBAAkB,EAAE,GAAG,EAAE;KAClH,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAsB;IAChD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,3 @@
1
+ import Database from "better-sqlite3";
2
+ export declare function openDb(historyDir: string): Database.Database;
3
+ export declare function hasDb(historyDir: string): boolean;
@@ -0,0 +1,183 @@
1
+ import Database from "better-sqlite3";
2
+ import { join } from "path";
3
+ import { existsSync } from "fs";
4
+ const SCHEMA_VERSION = 1;
5
+ export function openDb(historyDir) {
6
+ const dbPath = join(historyDir, "history.db");
7
+ const db = new Database(dbPath);
8
+ db.pragma("journal_mode = WAL");
9
+ db.pragma("busy_timeout = 5000");
10
+ db.pragma("foreign_keys = OFF");
11
+ initSchema(db);
12
+ return db;
13
+ }
14
+ export function hasDb(historyDir) {
15
+ return existsSync(join(historyDir, "history.db"));
16
+ }
17
+ function initSchema(db) {
18
+ const currentVersion = getSchemaVersion(db);
19
+ if (currentVersion >= SCHEMA_VERSION)
20
+ return;
21
+ db.exec(`
22
+ -- Layer 1: Raw Git Data
23
+ CREATE TABLE IF NOT EXISTS commits (
24
+ hash TEXT PRIMARY KEY,
25
+ message TEXT,
26
+ author TEXT,
27
+ email TEXT,
28
+ timestamp TEXT,
29
+ branch TEXT,
30
+ parent_hash TEXT,
31
+ merge_commit INTEGER DEFAULT 0,
32
+ insertions INTEGER DEFAULT 0,
33
+ deletions INTEGER DEFAULT 0,
34
+ files_changed INTEGER DEFAULT 0
35
+ );
36
+
37
+ CREATE TABLE IF NOT EXISTS commit_files (
38
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
39
+ commit_hash TEXT,
40
+ file_path TEXT,
41
+ status TEXT,
42
+ insertions INTEGER DEFAULT 0,
43
+ deletions INTEGER DEFAULT 0,
44
+ old_path TEXT
45
+ );
46
+ CREATE INDEX IF NOT EXISTS idx_commit_files_hash ON commit_files(commit_hash);
47
+ CREATE INDEX IF NOT EXISTS idx_commit_files_path ON commit_files(file_path);
48
+
49
+ CREATE TABLE IF NOT EXISTS branches (
50
+ name TEXT PRIMARY KEY,
51
+ head_hash TEXT,
52
+ created_at TEXT,
53
+ is_active INTEGER DEFAULT 1
54
+ );
55
+
56
+ CREATE TABLE IF NOT EXISTS tags (
57
+ name TEXT PRIMARY KEY,
58
+ hash TEXT,
59
+ timestamp TEXT,
60
+ message TEXT
61
+ );
62
+
63
+ -- Layer 2: Derived Analytics
64
+ CREATE TABLE IF NOT EXISTS file_evolution (
65
+ file_path TEXT PRIMARY KEY,
66
+ first_seen TEXT,
67
+ last_modified TEXT,
68
+ total_commits INTEGER DEFAULT 0,
69
+ total_churn INTEGER DEFAULT 0,
70
+ current_size INTEGER DEFAULT 0,
71
+ growth_rate REAL DEFAULT 0
72
+ );
73
+
74
+ CREATE TABLE IF NOT EXISTS churn_hotspots (
75
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
76
+ file_path TEXT,
77
+ period TEXT,
78
+ commits INTEGER DEFAULT 0,
79
+ insertions INTEGER DEFAULT 0,
80
+ deletions INTEGER DEFAULT 0,
81
+ unique_authors INTEGER DEFAULT 0
82
+ );
83
+ CREATE INDEX IF NOT EXISTS idx_churn_path ON churn_hotspots(file_path);
84
+
85
+ CREATE TABLE IF NOT EXISTS coupling (
86
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
87
+ file_a TEXT,
88
+ file_b TEXT,
89
+ co_commit_count INTEGER DEFAULT 0,
90
+ coupling_score REAL DEFAULT 0
91
+ );
92
+ CREATE INDEX IF NOT EXISTS idx_coupling_a ON coupling(file_a);
93
+ CREATE INDEX IF NOT EXISTS idx_coupling_b ON coupling(file_b);
94
+
95
+ CREATE TABLE IF NOT EXISTS trends (
96
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
97
+ metric_name TEXT,
98
+ period TEXT,
99
+ value REAL,
100
+ delta REAL,
101
+ direction TEXT
102
+ );
103
+ CREATE INDEX IF NOT EXISTS idx_trends_metric ON trends(metric_name);
104
+
105
+ CREATE TABLE IF NOT EXISTS decision_points (
106
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
107
+ commit_hash TEXT,
108
+ type TEXT,
109
+ impact_score REAL DEFAULT 0,
110
+ files_affected INTEGER DEFAULT 0
111
+ );
112
+
113
+ CREATE TABLE IF NOT EXISTS author_expertise (
114
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
115
+ author TEXT,
116
+ file_pattern TEXT,
117
+ commit_count INTEGER DEFAULT 0,
118
+ last_touched TEXT,
119
+ expertise_score REAL DEFAULT 0
120
+ );
121
+ CREATE INDEX IF NOT EXISTS idx_expertise_author ON author_expertise(author);
122
+
123
+ -- Layer 3: LLM Enrichment
124
+ CREATE TABLE IF NOT EXISTS enrichments (
125
+ commit_hash TEXT PRIMARY KEY,
126
+ intent TEXT,
127
+ reasoning TEXT,
128
+ category TEXT,
129
+ alternatives_considered TEXT,
130
+ session_context TEXT
131
+ );
132
+
133
+ -- Search Layer
134
+ CREATE VIRTUAL TABLE IF NOT EXISTS history_fts USING fts5(
135
+ hash, type, path, message, detail,
136
+ tokenize='porter unicode61'
137
+ );
138
+
139
+ CREATE TABLE IF NOT EXISTS embeddings (
140
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
141
+ commit_hash TEXT,
142
+ content_type TEXT,
143
+ vector BLOB,
144
+ model TEXT,
145
+ created_at TEXT
146
+ );
147
+ CREATE INDEX IF NOT EXISTS idx_embeddings_hash ON embeddings(commit_hash);
148
+
149
+ CREATE TABLE IF NOT EXISTS embed_queue (
150
+ commit_hash TEXT PRIMARY KEY,
151
+ queued_at TEXT,
152
+ status TEXT DEFAULT 'pending',
153
+ error TEXT
154
+ );
155
+
156
+ -- Metrics Layer
157
+ CREATE TABLE IF NOT EXISTS metric_values (
158
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
159
+ commit_hash TEXT,
160
+ metric_name TEXT,
161
+ value REAL,
162
+ captured_at TEXT
163
+ );
164
+ CREATE INDEX IF NOT EXISTS idx_metric_values_name ON metric_values(metric_name);
165
+
166
+ -- Infrastructure
167
+ CREATE TABLE IF NOT EXISTS schema_meta (
168
+ key TEXT PRIMARY KEY,
169
+ value TEXT
170
+ );
171
+ `);
172
+ db.prepare("INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', ?)").run(String(SCHEMA_VERSION));
173
+ }
174
+ function getSchemaVersion(db) {
175
+ try {
176
+ const row = db.prepare("SELECT value FROM schema_meta WHERE key = 'schema_version'").get();
177
+ return row ? parseInt(row.value, 10) : 0;
178
+ }
179
+ catch {
180
+ return 0;
181
+ }
182
+ }
183
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/util/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAEhC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB,MAAM,UAAU,MAAM,CAAC,UAAkB;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IAC9C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACjC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,UAAkB;IACtC,OAAO,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,UAAU,CAAC,EAAqB;IACvC,MAAM,cAAc,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC5C,IAAI,cAAc,IAAI,cAAc;QAAE,OAAO;IAE7C,EAAE,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsJP,CAAC,CAAC;IAEH,EAAE,CAAC,OAAO,CAAC,8EAA8E,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AACzH,CAAC;AAED,SAAS,gBAAgB,CAAC,EAAqB;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,4DAA4D,CAAC,CAAC,GAAG,EAAmC,CAAC;QAC5H,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export type ProjectType = "nodejs" | "python" | "rust" | "go" | "generic";
2
+ export declare function detectProjectType(cwd: string): ProjectType;
@@ -0,0 +1,14 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ export function detectProjectType(cwd) {
4
+ if (existsSync(join(cwd, "package.json")))
5
+ return "nodejs";
6
+ if (existsSync(join(cwd, "requirements.txt")) || existsSync(join(cwd, "pyproject.toml")))
7
+ return "python";
8
+ if (existsSync(join(cwd, "Cargo.toml")))
9
+ return "rust";
10
+ if (existsSync(join(cwd, "go.mod")))
11
+ return "go";
12
+ return "generic";
13
+ }
14
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/util/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAI5B,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3D,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC1G,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACvD,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACjD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface EmbeddingResult {
2
+ vector: number[];
3
+ model: string;
4
+ }
5
+ export declare function generateEmbedding(text: string): Promise<EmbeddingResult | null>;
6
+ export declare function cosineSimilarity(a: number[], b: number[]): number;