wdyt 0.1.13 → 0.1.15

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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Git diff context module for wdyt
3
+ *
4
+ * Gathers git context for code reviews including diff stats,
5
+ * commit history, and changed files.
6
+ */
7
+
8
+ import { spawn } from "child_process";
9
+
10
+ /** Options for getting git diff context */
11
+ export interface GitDiffOptions {
12
+ /** Base branch/commit to diff against (default: "main") */
13
+ base?: string;
14
+ /** Target ref to compare (default: "HEAD") */
15
+ head?: string;
16
+ /** Working directory (defaults to cwd) */
17
+ cwd?: string;
18
+ }
19
+
20
+ /** Git diff context result */
21
+ export interface GitDiffContext {
22
+ /** Diff stat summary */
23
+ diffStat: string;
24
+ /** Commit history */
25
+ commits: string[];
26
+ /** List of changed files */
27
+ changedFiles: string[];
28
+ /** Current branch name */
29
+ branch: string;
30
+ }
31
+
32
+ /**
33
+ * Execute a git command and return stdout
34
+ */
35
+ async function execGit(
36
+ args: string[],
37
+ cwd: string
38
+ ): Promise<{ stdout: string; stderr: string; exitCode: number }> {
39
+ return new Promise((resolve) => {
40
+ const process = spawn("git", args, {
41
+ cwd,
42
+ stdio: ["ignore", "pipe", "pipe"],
43
+ });
44
+
45
+ let stdout = "";
46
+ let stderr = "";
47
+
48
+ process.stdout.on("data", (data) => {
49
+ stdout += data.toString();
50
+ });
51
+
52
+ process.stderr.on("data", (data) => {
53
+ stderr += data.toString();
54
+ });
55
+
56
+ process.on("close", (exitCode) => {
57
+ resolve({ stdout, stderr, exitCode: exitCode ?? 0 });
58
+ });
59
+
60
+ process.on("error", () => {
61
+ resolve({ stdout: "", stderr: "Failed to spawn git", exitCode: 1 });
62
+ });
63
+ });
64
+ }
65
+
66
+ /**
67
+ * Get git diff --stat between base and head
68
+ */
69
+ export async function getDiffStat(
70
+ options: GitDiffOptions = {}
71
+ ): Promise<string> {
72
+ const { base = "main", head = "HEAD", cwd = process.cwd() } = options;
73
+
74
+ const { stdout, exitCode } = await execGit(
75
+ ["diff", "--stat", `${base}...${head}`],
76
+ cwd
77
+ );
78
+
79
+ if (exitCode !== 0) {
80
+ return "";
81
+ }
82
+
83
+ return stdout.trim();
84
+ }
85
+
86
+ /**
87
+ * Get commit history between base and head
88
+ */
89
+ export async function getCommits(
90
+ options: GitDiffOptions = {}
91
+ ): Promise<string[]> {
92
+ const { base = "main", head = "HEAD", cwd = process.cwd() } = options;
93
+
94
+ const { stdout, exitCode } = await execGit(
95
+ ["log", "--oneline", `${base}..${head}`],
96
+ cwd
97
+ );
98
+
99
+ if (exitCode !== 0) {
100
+ return [];
101
+ }
102
+
103
+ return stdout
104
+ .trim()
105
+ .split("\n")
106
+ .filter((line) => line.trim() !== "");
107
+ }
108
+
109
+ /**
110
+ * Get list of changed files between base and head
111
+ */
112
+ export async function getChangedFiles(
113
+ options: GitDiffOptions = {}
114
+ ): Promise<string[]> {
115
+ const { base = "main", head = "HEAD", cwd = process.cwd() } = options;
116
+
117
+ const { stdout, exitCode } = await execGit(
118
+ ["diff", "--name-only", `${base}...${head}`],
119
+ cwd
120
+ );
121
+
122
+ if (exitCode !== 0) {
123
+ return [];
124
+ }
125
+
126
+ return stdout
127
+ .trim()
128
+ .split("\n")
129
+ .filter((line) => line.trim() !== "");
130
+ }
131
+
132
+ /**
133
+ * Get current branch name
134
+ */
135
+ export async function getBranchName(cwd: string = process.cwd()): Promise<string> {
136
+ const { stdout, exitCode } = await execGit(
137
+ ["rev-parse", "--abbrev-ref", "HEAD"],
138
+ cwd
139
+ );
140
+
141
+ if (exitCode !== 0) {
142
+ return "";
143
+ }
144
+
145
+ return stdout.trim();
146
+ }
147
+
148
+ /**
149
+ * Get full git diff context
150
+ */
151
+ export async function getGitDiffContext(
152
+ options: GitDiffOptions = {}
153
+ ): Promise<GitDiffContext> {
154
+ const { cwd = process.cwd() } = options;
155
+
156
+ // Run all git commands in parallel
157
+ const [diffStat, commits, changedFiles, branch] = await Promise.all([
158
+ getDiffStat(options),
159
+ getCommits(options),
160
+ getChangedFiles(options),
161
+ getBranchName(cwd),
162
+ ]);
163
+
164
+ return {
165
+ diffStat,
166
+ commits,
167
+ changedFiles,
168
+ branch,
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Format git diff context as XML block (matching flowctl format)
174
+ */
175
+ export function formatDiffContextXml(context: GitDiffContext): string {
176
+ const sections: string[] = [];
177
+
178
+ if (context.diffStat) {
179
+ sections.push(`<diff_summary>\n${context.diffStat}\n</diff_summary>`);
180
+ }
181
+
182
+ if (context.commits.length > 0) {
183
+ sections.push(`<commits>\n${context.commits.join("\n")}\n</commits>`);
184
+ }
185
+
186
+ if (context.changedFiles.length > 0) {
187
+ sections.push(
188
+ `<changed_files>\n${context.changedFiles.join("\n")}\n</changed_files>`
189
+ );
190
+ }
191
+
192
+ return sections.join("\n\n");
193
+ }
194
+
195
+ /**
196
+ * Get formatted git diff context as XML
197
+ *
198
+ * This is the main entry point for getting review context.
199
+ */
200
+ export async function getFormattedDiffContext(
201
+ options: GitDiffOptions = {}
202
+ ): Promise<string> {
203
+ const context = await getGitDiffContext(options);
204
+ return formatDiffContextXml(context);
205
+ }