@compilr-dev/agents-coding 0.0.1
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/README.md +788 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.js +75 -0
- package/dist/skills/index.d.ts +39 -0
- package/dist/skills/index.js +322 -0
- package/dist/tools/git/branch.d.ts +17 -0
- package/dist/tools/git/branch.js +264 -0
- package/dist/tools/git/commit.d.ts +23 -0
- package/dist/tools/git/commit.js +280 -0
- package/dist/tools/git/diff.d.ts +19 -0
- package/dist/tools/git/diff.js +221 -0
- package/dist/tools/git/index.d.ts +10 -0
- package/dist/tools/git/index.js +11 -0
- package/dist/tools/git/log.d.ts +19 -0
- package/dist/tools/git/log.js +235 -0
- package/dist/tools/git/stash.d.ts +17 -0
- package/dist/tools/git/stash.js +294 -0
- package/dist/tools/git/status.d.ts +19 -0
- package/dist/tools/git/status.js +160 -0
- package/dist/tools/git/types.d.ts +293 -0
- package/dist/tools/git/types.js +4 -0
- package/dist/tools/git/utils.d.ts +58 -0
- package/dist/tools/git/utils.js +197 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +5 -0
- package/dist/tools/project/detect.d.ts +19 -0
- package/dist/tools/project/detect.js +341 -0
- package/dist/tools/project/find-root.d.ts +21 -0
- package/dist/tools/project/find-root.js +239 -0
- package/dist/tools/project/index.d.ts +6 -0
- package/dist/tools/project/index.js +5 -0
- package/dist/tools/project/types.d.ts +83 -0
- package/dist/tools/project/types.js +4 -0
- package/dist/tools/runners/build.d.ts +19 -0
- package/dist/tools/runners/build.js +306 -0
- package/dist/tools/runners/format.d.ts +19 -0
- package/dist/tools/runners/format.js +376 -0
- package/dist/tools/runners/index.d.ts +9 -0
- package/dist/tools/runners/index.js +9 -0
- package/dist/tools/runners/lint.d.ts +19 -0
- package/dist/tools/runners/lint.js +356 -0
- package/dist/tools/runners/test.d.ts +19 -0
- package/dist/tools/runners/test.js +386 -0
- package/dist/tools/runners/types.d.ts +97 -0
- package/dist/tools/runners/types.js +4 -0
- package/dist/tools/runners/utils.d.ts +69 -0
- package/dist/tools/runners/utils.js +179 -0
- package/dist/tools/search/definition.d.ts +19 -0
- package/dist/tools/search/definition.js +305 -0
- package/dist/tools/search/index.d.ts +8 -0
- package/dist/tools/search/index.js +8 -0
- package/dist/tools/search/references.d.ts +19 -0
- package/dist/tools/search/references.js +179 -0
- package/dist/tools/search/todos.d.ts +19 -0
- package/dist/tools/search/todos.js +269 -0
- package/dist/tools/search/types.d.ts +132 -0
- package/dist/tools/search/types.js +4 -0
- package/dist/tools/search/utils.d.ts +45 -0
- package/dist/tools/search/utils.js +152 -0
- package/package.json +88 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Stash Tool
|
|
3
|
+
* Stash and restore changes
|
|
4
|
+
*/
|
|
5
|
+
import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
|
|
6
|
+
import { runGitCommand, isGitRepository, isDirectory } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Git stash tool
|
|
9
|
+
*/
|
|
10
|
+
export const gitStashTool = defineTool({
|
|
11
|
+
name: 'git_stash',
|
|
12
|
+
description: 'Manage git stashes. Actions: push (save changes), pop (apply and remove), ' +
|
|
13
|
+
'apply (apply without removing), list (show all stashes), drop (remove stash), ' +
|
|
14
|
+
'clear (remove all stashes), show (display stash contents).',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
path: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Working directory path (default: current directory)',
|
|
21
|
+
},
|
|
22
|
+
action: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
enum: ['push', 'pop', 'apply', 'list', 'drop', 'clear', 'show'],
|
|
25
|
+
description: 'Action to perform',
|
|
26
|
+
},
|
|
27
|
+
message: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Stash message (for push action)',
|
|
30
|
+
},
|
|
31
|
+
includeUntracked: {
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
description: 'Include untracked files (for push action)',
|
|
34
|
+
},
|
|
35
|
+
index: {
|
|
36
|
+
type: 'number',
|
|
37
|
+
description: 'Stash index (for pop/apply/drop/show, default: 0)',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['action'],
|
|
41
|
+
},
|
|
42
|
+
execute: executeGitStash,
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Execute git stash
|
|
46
|
+
*/
|
|
47
|
+
async function executeGitStash(input) {
|
|
48
|
+
const targetPath = input.path ?? process.cwd();
|
|
49
|
+
// Check if path exists
|
|
50
|
+
if (!(await isDirectory(targetPath))) {
|
|
51
|
+
return createErrorResult(`Directory not found: ${targetPath}`);
|
|
52
|
+
}
|
|
53
|
+
// Check if it's a git repository
|
|
54
|
+
if (!(await isGitRepository(targetPath))) {
|
|
55
|
+
return createErrorResult(`Not a git repository: ${targetPath}`);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
switch (input.action) {
|
|
59
|
+
case 'push':
|
|
60
|
+
return await stashPush(targetPath, input.message, input.includeUntracked ?? false);
|
|
61
|
+
case 'pop':
|
|
62
|
+
return await stashPop(targetPath, input.index ?? 0);
|
|
63
|
+
case 'apply':
|
|
64
|
+
return await stashApply(targetPath, input.index ?? 0);
|
|
65
|
+
case 'list':
|
|
66
|
+
return await stashList(targetPath);
|
|
67
|
+
case 'drop':
|
|
68
|
+
return await stashDrop(targetPath, input.index ?? 0);
|
|
69
|
+
case 'clear':
|
|
70
|
+
return await stashClear(targetPath);
|
|
71
|
+
case 'show':
|
|
72
|
+
return await stashShow(targetPath, input.index ?? 0);
|
|
73
|
+
default:
|
|
74
|
+
return createErrorResult(`Unknown action: ${String(input.action)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Push changes to stash
|
|
83
|
+
*/
|
|
84
|
+
async function stashPush(repoPath, message, includeUntracked = false) {
|
|
85
|
+
const args = ['stash', 'push'];
|
|
86
|
+
if (message) {
|
|
87
|
+
args.push('-m', message);
|
|
88
|
+
}
|
|
89
|
+
if (includeUntracked) {
|
|
90
|
+
args.push('-u');
|
|
91
|
+
}
|
|
92
|
+
const result = await runGitCommand(args, { cwd: repoPath });
|
|
93
|
+
if (!result.success) {
|
|
94
|
+
return createErrorResult(`Failed to stash changes: ${result.stderr}`);
|
|
95
|
+
}
|
|
96
|
+
// Check if anything was stashed
|
|
97
|
+
if (result.stdout.includes('No local changes to save')) {
|
|
98
|
+
return createErrorResult('No local changes to stash');
|
|
99
|
+
}
|
|
100
|
+
const stashResult = {
|
|
101
|
+
message: message ? `Stashed changes: ${message}` : 'Stashed changes',
|
|
102
|
+
};
|
|
103
|
+
return createSuccessResult(stashResult);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Pop stash (apply and remove)
|
|
107
|
+
*/
|
|
108
|
+
async function stashPop(repoPath, index) {
|
|
109
|
+
const stashRef = `stash@{${String(index)}}`;
|
|
110
|
+
const result = await runGitCommand(['stash', 'pop', stashRef], { cwd: repoPath });
|
|
111
|
+
if (!result.success) {
|
|
112
|
+
if (result.stderr.includes('No stash entries found')) {
|
|
113
|
+
return createErrorResult('No stash entries found');
|
|
114
|
+
}
|
|
115
|
+
if (result.stderr.includes('does not exist')) {
|
|
116
|
+
return createErrorResult(`Stash ${stashRef} does not exist`);
|
|
117
|
+
}
|
|
118
|
+
return createErrorResult(`Failed to pop stash: ${result.stderr}`);
|
|
119
|
+
}
|
|
120
|
+
const stashResult = {
|
|
121
|
+
message: `Applied and removed ${stashRef}`,
|
|
122
|
+
};
|
|
123
|
+
return createSuccessResult(stashResult);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Apply stash without removing
|
|
127
|
+
*/
|
|
128
|
+
async function stashApply(repoPath, index) {
|
|
129
|
+
const stashRef = `stash@{${String(index)}}`;
|
|
130
|
+
const result = await runGitCommand(['stash', 'apply', stashRef], { cwd: repoPath });
|
|
131
|
+
if (!result.success) {
|
|
132
|
+
if (result.stderr.includes('No stash entries found')) {
|
|
133
|
+
return createErrorResult('No stash entries found');
|
|
134
|
+
}
|
|
135
|
+
if (result.stderr.includes('does not exist')) {
|
|
136
|
+
return createErrorResult(`Stash ${stashRef} does not exist`);
|
|
137
|
+
}
|
|
138
|
+
return createErrorResult(`Failed to apply stash: ${result.stderr}`);
|
|
139
|
+
}
|
|
140
|
+
const stashResult = {
|
|
141
|
+
message: `Applied ${stashRef} (stash still exists)`,
|
|
142
|
+
};
|
|
143
|
+
return createSuccessResult(stashResult);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* List all stashes
|
|
147
|
+
*/
|
|
148
|
+
async function stashList(repoPath) {
|
|
149
|
+
const result = await runGitCommand(['stash', 'list'], { cwd: repoPath });
|
|
150
|
+
if (!result.success) {
|
|
151
|
+
return createErrorResult(`Failed to list stashes: ${result.stderr}`);
|
|
152
|
+
}
|
|
153
|
+
const stashes = [];
|
|
154
|
+
if (result.stdout) {
|
|
155
|
+
// Parse stash list output
|
|
156
|
+
// Format: stash@{0}: On branch: message
|
|
157
|
+
const lines = result.stdout.split('\n').filter(Boolean);
|
|
158
|
+
for (const line of lines) {
|
|
159
|
+
const match = line.match(/^(stash@\{(\d+)\}): (?:On|WIP on) ([^:]+): (.+)$/);
|
|
160
|
+
if (match) {
|
|
161
|
+
stashes.push({
|
|
162
|
+
ref: match[1],
|
|
163
|
+
index: parseInt(match[2], 10),
|
|
164
|
+
branch: match[3],
|
|
165
|
+
message: match[4],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Fallback for different formats
|
|
170
|
+
const simpleMatch = line.match(/^(stash@\{(\d+)\}): (.+)$/);
|
|
171
|
+
if (simpleMatch) {
|
|
172
|
+
stashes.push({
|
|
173
|
+
ref: simpleMatch[1],
|
|
174
|
+
index: parseInt(simpleMatch[2], 10),
|
|
175
|
+
branch: 'unknown',
|
|
176
|
+
message: simpleMatch[3],
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const stashResult = {
|
|
183
|
+
stashes,
|
|
184
|
+
message: `Found ${String(stashes.length)} stash(es)`,
|
|
185
|
+
};
|
|
186
|
+
return createSuccessResult(stashResult);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Drop a stash
|
|
190
|
+
*/
|
|
191
|
+
async function stashDrop(repoPath, index) {
|
|
192
|
+
const stashRef = `stash@{${String(index)}}`;
|
|
193
|
+
const result = await runGitCommand(['stash', 'drop', stashRef], { cwd: repoPath });
|
|
194
|
+
if (!result.success) {
|
|
195
|
+
if (result.stderr.includes('No stash entries found')) {
|
|
196
|
+
return createErrorResult('No stash entries found');
|
|
197
|
+
}
|
|
198
|
+
if (result.stderr.includes('does not exist')) {
|
|
199
|
+
return createErrorResult(`Stash ${stashRef} does not exist`);
|
|
200
|
+
}
|
|
201
|
+
return createErrorResult(`Failed to drop stash: ${result.stderr}`);
|
|
202
|
+
}
|
|
203
|
+
const stashResult = {
|
|
204
|
+
message: `Dropped ${stashRef}`,
|
|
205
|
+
};
|
|
206
|
+
return createSuccessResult(stashResult);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Clear all stashes
|
|
210
|
+
*/
|
|
211
|
+
async function stashClear(repoPath) {
|
|
212
|
+
// First check if there are any stashes
|
|
213
|
+
const listResult = await runGitCommand(['stash', 'list'], { cwd: repoPath });
|
|
214
|
+
if (!listResult.stdout) {
|
|
215
|
+
return createErrorResult('No stash entries to clear');
|
|
216
|
+
}
|
|
217
|
+
const result = await runGitCommand(['stash', 'clear'], { cwd: repoPath });
|
|
218
|
+
if (!result.success) {
|
|
219
|
+
return createErrorResult(`Failed to clear stashes: ${result.stderr}`);
|
|
220
|
+
}
|
|
221
|
+
const stashResult = {
|
|
222
|
+
message: 'Cleared all stashes',
|
|
223
|
+
};
|
|
224
|
+
return createSuccessResult(stashResult);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Show stash contents
|
|
228
|
+
*/
|
|
229
|
+
async function stashShow(repoPath, index) {
|
|
230
|
+
const stashRef = `stash@{${String(index)}}`;
|
|
231
|
+
// Get stat summary
|
|
232
|
+
const statResult = await runGitCommand(['stash', 'show', stashRef], { cwd: repoPath });
|
|
233
|
+
if (!statResult.success) {
|
|
234
|
+
if (statResult.stderr.includes('No stash entries found')) {
|
|
235
|
+
return createErrorResult('No stash entries found');
|
|
236
|
+
}
|
|
237
|
+
if (statResult.stderr.includes('does not exist')) {
|
|
238
|
+
return createErrorResult(`Stash ${stashRef} does not exist`);
|
|
239
|
+
}
|
|
240
|
+
return createErrorResult(`Failed to show stash: ${statResult.stderr}`);
|
|
241
|
+
}
|
|
242
|
+
// Get diff
|
|
243
|
+
const diffResult = await runGitCommand(['stash', 'show', '-p', stashRef], { cwd: repoPath });
|
|
244
|
+
const stashResult = {
|
|
245
|
+
diff: diffResult.success ? diffResult.stdout : undefined,
|
|
246
|
+
message: statResult.stdout,
|
|
247
|
+
};
|
|
248
|
+
return createSuccessResult(stashResult);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Factory function to create git stash tool with custom options
|
|
252
|
+
*/
|
|
253
|
+
export function createGitStashTool(options) {
|
|
254
|
+
return defineTool({
|
|
255
|
+
name: 'git_stash',
|
|
256
|
+
description: 'Manage git stashes. Actions: push, pop, apply, list, drop, clear, show.',
|
|
257
|
+
inputSchema: {
|
|
258
|
+
type: 'object',
|
|
259
|
+
properties: {
|
|
260
|
+
path: {
|
|
261
|
+
type: 'string',
|
|
262
|
+
description: 'Working directory path (default: current directory)',
|
|
263
|
+
},
|
|
264
|
+
action: {
|
|
265
|
+
type: 'string',
|
|
266
|
+
enum: ['push', 'pop', 'apply', 'list', 'drop', 'clear', 'show'],
|
|
267
|
+
description: 'Action to perform',
|
|
268
|
+
},
|
|
269
|
+
message: {
|
|
270
|
+
type: 'string',
|
|
271
|
+
description: 'Stash message (for push)',
|
|
272
|
+
},
|
|
273
|
+
includeUntracked: {
|
|
274
|
+
type: 'boolean',
|
|
275
|
+
description: 'Include untracked files (for push)',
|
|
276
|
+
},
|
|
277
|
+
index: {
|
|
278
|
+
type: 'number',
|
|
279
|
+
description: 'Stash index (default: 0)',
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
required: ['action'],
|
|
283
|
+
},
|
|
284
|
+
execute: async (input) => {
|
|
285
|
+
let targetPath = input.path ?? '.';
|
|
286
|
+
// Resolve relative paths
|
|
287
|
+
if (options?.baseDir && !targetPath.startsWith('/')) {
|
|
288
|
+
const nodePath = await import('node:path');
|
|
289
|
+
targetPath = nodePath.join(options.baseDir, targetPath);
|
|
290
|
+
}
|
|
291
|
+
return executeGitStash({ ...input, path: targetPath });
|
|
292
|
+
},
|
|
293
|
+
});
|
|
294
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Status Tool
|
|
3
|
+
* Shows the working tree status with structured output
|
|
4
|
+
*/
|
|
5
|
+
import type { Tool } from '@compilr-dev/agents';
|
|
6
|
+
import type { GitStatusInput } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Git status tool
|
|
9
|
+
*/
|
|
10
|
+
export declare const gitStatusTool: Tool<GitStatusInput>;
|
|
11
|
+
/**
|
|
12
|
+
* Factory function to create git status tool with custom options
|
|
13
|
+
*/
|
|
14
|
+
export declare function createGitStatusTool(options?: {
|
|
15
|
+
/** Base directory for relative paths */
|
|
16
|
+
baseDir?: string;
|
|
17
|
+
/** Default include untracked setting */
|
|
18
|
+
defaultIncludeUntracked?: boolean;
|
|
19
|
+
}): Tool<GitStatusInput>;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Status Tool
|
|
3
|
+
* Shows the working tree status with structured output
|
|
4
|
+
*/
|
|
5
|
+
import { defineTool, createSuccessResult, createErrorResult } from '@compilr-dev/agents';
|
|
6
|
+
import { runGitCommand, isGitRepository, isDirectory, parsePorcelainStatus, parseBranchInfo, xyToStatusType, } from './utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Git status tool
|
|
9
|
+
*/
|
|
10
|
+
export const gitStatusTool = defineTool({
|
|
11
|
+
name: 'git_status',
|
|
12
|
+
description: 'Get the current git working tree status. ' +
|
|
13
|
+
'Returns branch name, tracking info, staged changes, unstaged changes, and untracked files. ' +
|
|
14
|
+
'Use to understand what changes exist before committing.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
path: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Working directory path (default: current directory)',
|
|
21
|
+
},
|
|
22
|
+
includeUntracked: {
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
description: 'Include untracked files (default: true)',
|
|
25
|
+
},
|
|
26
|
+
short: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
description: 'Return short format status string as well (default: false)',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: [],
|
|
32
|
+
},
|
|
33
|
+
execute: executeGitStatus,
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Execute git status
|
|
37
|
+
*/
|
|
38
|
+
async function executeGitStatus(input) {
|
|
39
|
+
const targetPath = input.path ?? process.cwd();
|
|
40
|
+
const includeUntracked = input.includeUntracked ?? true;
|
|
41
|
+
const short = input.short ?? false;
|
|
42
|
+
// Check if path exists
|
|
43
|
+
if (!(await isDirectory(targetPath))) {
|
|
44
|
+
return createErrorResult(`Directory not found: ${targetPath}`);
|
|
45
|
+
}
|
|
46
|
+
// Check if it's a git repository
|
|
47
|
+
if (!(await isGitRepository(targetPath))) {
|
|
48
|
+
return createErrorResult(`Not a git repository: ${targetPath}`);
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
// Run git status with porcelain v2 format for structured output
|
|
52
|
+
const args = ['status', '--porcelain=v2', '--branch'];
|
|
53
|
+
if (includeUntracked) {
|
|
54
|
+
args.push('-uall');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
args.push('-uno');
|
|
58
|
+
}
|
|
59
|
+
const result = await runGitCommand(args, { cwd: targetPath });
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
return createErrorResult(`git status failed: ${result.stderr}`);
|
|
62
|
+
}
|
|
63
|
+
// Parse branch info
|
|
64
|
+
const branchInfo = parseBranchInfo(result.stdout);
|
|
65
|
+
// Parse file entries
|
|
66
|
+
const entries = parsePorcelainStatus(result.stdout);
|
|
67
|
+
// Categorize entries
|
|
68
|
+
const staged = [];
|
|
69
|
+
const modified = [];
|
|
70
|
+
const untracked = [];
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
if (entry.type === 'untracked') {
|
|
73
|
+
untracked.push(entry.path);
|
|
74
|
+
}
|
|
75
|
+
else if (entry.staged && entry.xy[0] !== '.') {
|
|
76
|
+
// Staged changes (first char of XY)
|
|
77
|
+
staged.push({
|
|
78
|
+
path: entry.path,
|
|
79
|
+
status: xyToStatusType(entry.xy, true),
|
|
80
|
+
oldPath: entry.oldPath,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Unstaged changes (second char of XY)
|
|
84
|
+
if (entry.xy[1] !== '.' && entry.xy[1] !== '?') {
|
|
85
|
+
modified.push({
|
|
86
|
+
path: entry.path,
|
|
87
|
+
status: xyToStatusType(entry.xy, false),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Get short status if requested
|
|
92
|
+
let shortStatus;
|
|
93
|
+
if (short) {
|
|
94
|
+
const shortResult = await runGitCommand(['status', '-sb'], { cwd: targetPath });
|
|
95
|
+
if (shortResult.success) {
|
|
96
|
+
shortStatus = shortResult.stdout;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const statusResult = {
|
|
100
|
+
branch: branchInfo.branch,
|
|
101
|
+
tracking: branchInfo.upstream
|
|
102
|
+
? {
|
|
103
|
+
remote: branchInfo.upstream,
|
|
104
|
+
ahead: branchInfo.ahead,
|
|
105
|
+
behind: branchInfo.behind,
|
|
106
|
+
}
|
|
107
|
+
: undefined,
|
|
108
|
+
staged,
|
|
109
|
+
modified,
|
|
110
|
+
untracked,
|
|
111
|
+
isClean: staged.length === 0 && modified.length === 0 && untracked.length === 0,
|
|
112
|
+
shortStatus,
|
|
113
|
+
};
|
|
114
|
+
return createSuccessResult(statusResult);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Factory function to create git status tool with custom options
|
|
122
|
+
*/
|
|
123
|
+
export function createGitStatusTool(options) {
|
|
124
|
+
return defineTool({
|
|
125
|
+
name: 'git_status',
|
|
126
|
+
description: 'Get the current git working tree status. ' +
|
|
127
|
+
'Returns branch name, tracking info, staged changes, unstaged changes, and untracked files.',
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: {
|
|
131
|
+
path: {
|
|
132
|
+
type: 'string',
|
|
133
|
+
description: 'Working directory path (default: current directory)',
|
|
134
|
+
},
|
|
135
|
+
includeUntracked: {
|
|
136
|
+
type: 'boolean',
|
|
137
|
+
description: `Include untracked files (default: ${String(options?.defaultIncludeUntracked ?? true)})`,
|
|
138
|
+
},
|
|
139
|
+
short: {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
description: 'Return short format status string as well (default: false)',
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
required: [],
|
|
145
|
+
},
|
|
146
|
+
execute: async (input) => {
|
|
147
|
+
let targetPath = input.path ?? '.';
|
|
148
|
+
// Resolve relative paths
|
|
149
|
+
if (options?.baseDir && !targetPath.startsWith('/')) {
|
|
150
|
+
const nodePath = await import('node:path');
|
|
151
|
+
targetPath = nodePath.join(options.baseDir, targetPath);
|
|
152
|
+
}
|
|
153
|
+
return executeGitStatus({
|
|
154
|
+
...input,
|
|
155
|
+
path: targetPath,
|
|
156
|
+
includeUntracked: input.includeUntracked ?? options?.defaultIncludeUntracked ?? true,
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|