@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,2 @@
1
+ import { Command } from "commander";
2
+ export declare function approveCommand(): Command;
@@ -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,2 @@
1
+ import { Command } from "commander";
2
+ export declare function blameCommand(): Command;
@@ -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":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAazC,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+D,CAAC;IACtF,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,aAAa,GAAG,EAAE,CAAC;IACvB,IAAI,gBAAgB,GAAG,EAAE,CAAC;IAC1B,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,6DAA6D;QAC7D,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC5D,IAAI,WAAW,EAAE,CAAC;YAChB,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;YAC7B,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3C,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9D,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9B,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/C,gBAAgB,GAAG,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YAC5D,SAAS;QACX,CAAC;QACD,2DAA2D;QAC3D,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE;gBACtB,IAAI,EAAE,WAAW;gBACjB,MAAM,EAAE,aAAa;gBACrB,SAAS,EAAE,gBAAgB;aAC5B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;SACxB,WAAW,CAAC,yDAAyD,CAAC;SACtE,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;SACxC,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,CAAC,QAAgB,EAAE,IAAwB,EAAE,EAAE;QACrD,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,4BAA4B;YAC5B,IAAI,eAAuB,CAAC;YAC5B,IAAI,CAAC;gBACH,eAAe,GAAG,QAAQ,CAAC,yBAAyB,QAAQ,EAAE,EAAE;oBAC9D,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;oBAClB,QAAQ,EAAE,OAAO;iBAClB,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,QAAQ,IAAI,CAAC,CAAC;gBAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,MAAM,OAAO,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;YAEhD,oBAAoB;YACpB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAEtE,+BAA+B;YAC/B,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;YACjD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAgD,CAAC;YAE/E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACrD,MAAM,UAAU,GAAG,EAAE;qBAClB,OAAO,CAAC,oDAAoD,YAAY,GAAG,CAAC;qBAC5E,GAAG,CAAC,GAAG,MAAM,CAA6C,CAAC;gBAC9D,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;oBAC7B,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBAC5C,CAAC;gBAED,MAAM,UAAU,GAAG,EAAE;qBAClB,OAAO,CAAC,+EAA+E,YAAY,GAAG,CAAC;qBACvG,GAAG,CAAC,GAAG,MAAM,CAAqE,CAAC;gBACtF,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;oBAC7B,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACtF,CAAC;YACH,CAAC;YAED,8CAA8C;YAC9C,MAAM,OAAO,GAAiB,EAAE,CAAC;YACjC,IAAI,YAAY,GAAsB,IAAI,CAAC;YAE3C,MAAM,WAAW,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAEvE,KAAK,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;gBAC1C,IAAI,YAAY,IAAI,YAAY,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,YAAY,CAAC,OAAO,KAAK,OAAO,GAAG,CAAC,EAAE,CAAC;oBAC5F,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;gBACjC,CAAC;qBAAM,CAAC;oBACN,IAAI,YAAY;wBAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;oBAC7C,YAAY,GAAG;wBACb,IAAI,EAAE,IAAI,CAAC,IAAI;wBACf,MAAM,EAAE,IAAI,CAAC,MAAM;wBACnB,SAAS,EAAE,IAAI,CAAC,SAAS;wBACzB,SAAS,EAAE,OAAO;wBAClB,OAAO,EAAE,OAAO;wBAChB,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;wBACtC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM;wBAC7C,QAAQ,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ;qBAClD,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,IAAI,YAAY;gBAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAE7C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;gBAC9D,OAAO;YACT,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,QAAQ,IAAI,CAAC,CAAC;gBAC/D,OAAO;YACT,CAAC;YAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,QAAQ,IAAI,CAAC,CAAC;YAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;YAC5C,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACpC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC1C,MAAM,KAAK,GACT,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,OAAO;oBAC/B,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,EAAE;oBACvB,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,cAAc,CAAC;gBAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;gBACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;gBACvD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,aAAa,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;gBACtE,CAAC;gBACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,eAAe,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function captureCommand(): Command;
@@ -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,2 @@
1
+ import { Command } from "commander";
2
+ export declare function couplingCommand(): Command;
@@ -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,2 @@
1
+ import { Command } from "commander";
2
+ export declare function cronCommand(): Command;
@@ -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":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAEpD,SAAS,KAAK,CAAC,GAAW,IAAU,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEhE,MAAM,WAAW,GAAG,aAAa,CAAC;AAElC,SAAS,UAAU;IACjB,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,OAAe;IACjC,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE;QACzC,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;KAChC,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,gBAAgB,GAAG,0BAA0B,WAAW,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;SACvB,WAAW,CAAC,qCAAqC,CAAC;SAClD,MAAM,CAAC,UAAU,EAAE,yBAAyB,CAAC;SAC7C,MAAM,CAAC,UAAU,EAAE,mBAAmB,CAAC;SACvC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACf,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAE7B,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;YACvE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,KAAK,CAAC,kCAAkC,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,mCAAmC,CAAC,CAAC;gBAC3C,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACxC,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtF,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5C,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACzC,OAAO;QACT,CAAC;QAED,iBAAiB;QACjB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1B,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;QAEpC,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAClC,yBAAyB;YACzB,MAAM,OAAO,GAAG,OAAO;iBACpB,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;iBAChD,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,UAAU,CAAC,OAAO,CAAC,CAAC;YACpB,KAAK,CAAC,0BAA0B,GAAG,KAAK,CAAC,CAAC;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,UAAU,CAAC,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC;YAClD,KAAK,CAAC,8CAA8C,GAAG,KAAK,CAAC,CAAC;QAChE,CAAC;QAED,KAAK,CAAC,UAAU,QAAQ,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function decisionsCommand(): Command;
@@ -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"}
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function diffSummaryCommand(): Command;