iosm-cli 0.2.6 → 0.2.8
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/CHANGELOG.md +54 -1
- package/README.md +7 -7
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +9 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-profiles.d.ts.map +1 -1
- package/dist/core/agent-profiles.js +12 -1
- package/dist/core/agent-profiles.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +40 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +3 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +39 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +168 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shadow-guard.js +1 -1
- package/dist/core/shadow-guard.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +91 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/fetch.d.ts +56 -0
- package/dist/core/tools/fetch.d.ts.map +1 -0
- package/dist/core/tools/fetch.js +272 -0
- package/dist/core/tools/fetch.js.map +1 -0
- package/dist/core/tools/fs-ops.d.ts +54 -0
- package/dist/core/tools/fs-ops.d.ts.map +1 -0
- package/dist/core/tools/fs-ops.js +206 -0
- package/dist/core/tools/fs-ops.js.map +1 -0
- package/dist/core/tools/git-common.d.ts +45 -0
- package/dist/core/tools/git-common.d.ts.map +1 -0
- package/dist/core/tools/git-common.js +185 -0
- package/dist/core/tools/git-common.js.map +1 -0
- package/dist/core/tools/git-read.d.ts +62 -0
- package/dist/core/tools/git-read.d.ts.map +1 -0
- package/dist/core/tools/git-read.js +215 -0
- package/dist/core/tools/git-read.js.map +1 -0
- package/dist/core/tools/git-write.d.ts +75 -0
- package/dist/core/tools/git-write.d.ts.map +1 -0
- package/dist/core/tools/git-write.js +298 -0
- package/dist/core/tools/git-write.js.map +1 -0
- package/dist/core/tools/index.d.ts +90 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +32 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/task.js +1 -1
- package/dist/core/tools/task.js.map +1 -1
- package/dist/core/tools/web-search.d.ts +72 -0
- package/dist/core/tools/web-search.d.ts.map +1 -0
- package/dist/core/tools/web-search.js +702 -0
- package/dist/core/tools/web-search.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +7 -2
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/mcp-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/mcp-selector.js +3 -1
- package/dist/modes/interactive/components/mcp-selector.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +12 -2
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +11 -0
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/scoped-models-selector.js +16 -5
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +4 -2
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +25 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +182 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +7 -2
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +7 -2
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +7 -2
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +18 -3
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +8 -0
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +184 -24
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/cli-reference.md +19 -1
- package/docs/configuration.md +11 -2
- package/docs/development-and-testing.md +1 -1
- package/docs/interactive-mode.md +2 -2
- package/docs/rpc-json-sdk.md +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { resolveToCwd } from "./path-utils.js";
|
|
3
|
+
import { DEFAULT_GIT_TIMEOUT_SECONDS, normalizePositiveInt, normalizeRefLike, requireRefLike, resolveGitCommandOptions, runGitAndFormatOutput, } from "./git-common.js";
|
|
4
|
+
const gitReadSchema = Type.Object({
|
|
5
|
+
action: Type.Union([
|
|
6
|
+
Type.Literal("status"),
|
|
7
|
+
Type.Literal("diff"),
|
|
8
|
+
Type.Literal("log"),
|
|
9
|
+
Type.Literal("blame"),
|
|
10
|
+
Type.Literal("show"),
|
|
11
|
+
Type.Literal("branch_list"),
|
|
12
|
+
Type.Literal("remote_list"),
|
|
13
|
+
Type.Literal("rev_parse"),
|
|
14
|
+
], { description: "Git action: status | diff | log | blame | show | branch_list | remote_list | rev_parse." }),
|
|
15
|
+
path: Type.Optional(Type.String({ description: "Repository working directory (default: current directory)." })),
|
|
16
|
+
file: Type.Optional(Type.String({ description: "Optional file path for diff/log/show, required for blame." })),
|
|
17
|
+
base: Type.Optional(Type.String({ description: "Optional base ref/commit for diff." })),
|
|
18
|
+
head: Type.Optional(Type.String({ description: "Optional head ref/commit for diff (requires base)." })),
|
|
19
|
+
staged: Type.Optional(Type.Boolean({ description: "For diff action: compare staged changes." })),
|
|
20
|
+
context: Type.Optional(Type.Number({ description: "For diff action: unified context lines (default: 3)." })),
|
|
21
|
+
porcelain: Type.Optional(Type.Boolean({
|
|
22
|
+
description: "For status action: use short porcelain format with branch (default: true).",
|
|
23
|
+
})),
|
|
24
|
+
untracked: Type.Optional(Type.Boolean({ description: "For status action: include untracked files (default: true)." })),
|
|
25
|
+
limit: Type.Optional(Type.Number({ description: "For log action: max entries (default: 20, max: 200)." })),
|
|
26
|
+
since: Type.Optional(Type.String({ description: "For log action: git --since value (e.g. '2 weeks ago')." })),
|
|
27
|
+
ref: Type.Optional(Type.String({ description: "For blame/show action: optional ref/commit (show defaults to HEAD)." })),
|
|
28
|
+
line_start: Type.Optional(Type.Number({ description: "For blame action: range start line (1-indexed)." })),
|
|
29
|
+
line_end: Type.Optional(Type.Number({ description: "For blame action: range end line (1-indexed)." })),
|
|
30
|
+
all: Type.Optional(Type.Boolean({ description: "For branch_list action: include remote branches (default: true)." })),
|
|
31
|
+
verbose: Type.Optional(Type.Boolean({
|
|
32
|
+
description: "For branch_list/remote_list action: include verbose output (branch_list default: false, remote_list default: true).",
|
|
33
|
+
})),
|
|
34
|
+
target: Type.Optional(Type.String({ description: "For rev_parse action: ref/commit to resolve (default: HEAD)." })),
|
|
35
|
+
short: Type.Optional(Type.Boolean({ description: "For rev_parse action: output short hash (default: false)." })),
|
|
36
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 30)." })),
|
|
37
|
+
});
|
|
38
|
+
const DEFAULT_LOG_LIMIT = 20;
|
|
39
|
+
const MAX_LOG_LIMIT = 200;
|
|
40
|
+
function buildStatusArgs(input) {
|
|
41
|
+
const porcelain = input.porcelain ?? true;
|
|
42
|
+
const includeUntracked = input.untracked ?? true;
|
|
43
|
+
const args = ["status"];
|
|
44
|
+
if (porcelain) {
|
|
45
|
+
args.push("--short", "--branch");
|
|
46
|
+
}
|
|
47
|
+
if (!includeUntracked) {
|
|
48
|
+
args.push("--untracked-files=no");
|
|
49
|
+
}
|
|
50
|
+
return args;
|
|
51
|
+
}
|
|
52
|
+
function buildDiffArgs(input) {
|
|
53
|
+
if (input.head && !input.base) {
|
|
54
|
+
throw new Error("git_read diff requires base when head is provided.");
|
|
55
|
+
}
|
|
56
|
+
const context = normalizePositiveInt(input.context, 3, "context");
|
|
57
|
+
const base = normalizeRefLike(input.base, "base");
|
|
58
|
+
const head = normalizeRefLike(input.head, "head");
|
|
59
|
+
const args = ["diff", "--no-color", `--unified=${context}`];
|
|
60
|
+
if (input.staged) {
|
|
61
|
+
args.push("--staged");
|
|
62
|
+
}
|
|
63
|
+
if (base && head) {
|
|
64
|
+
args.push(`${base}..${head}`);
|
|
65
|
+
}
|
|
66
|
+
else if (base) {
|
|
67
|
+
args.push(base);
|
|
68
|
+
}
|
|
69
|
+
if (input.file) {
|
|
70
|
+
args.push("--", input.file);
|
|
71
|
+
}
|
|
72
|
+
return args;
|
|
73
|
+
}
|
|
74
|
+
function buildLogArgs(input) {
|
|
75
|
+
const limit = Math.min(MAX_LOG_LIMIT, normalizePositiveInt(input.limit, DEFAULT_LOG_LIMIT, "limit"));
|
|
76
|
+
const args = [
|
|
77
|
+
"log",
|
|
78
|
+
"--no-color",
|
|
79
|
+
`--max-count=${limit}`,
|
|
80
|
+
"--date=iso",
|
|
81
|
+
"--pretty=format:%H%x09%ad%x09%an%x09%s",
|
|
82
|
+
];
|
|
83
|
+
if (input.since) {
|
|
84
|
+
args.push(`--since=${input.since}`);
|
|
85
|
+
}
|
|
86
|
+
if (input.file) {
|
|
87
|
+
args.push("--", input.file);
|
|
88
|
+
}
|
|
89
|
+
return args;
|
|
90
|
+
}
|
|
91
|
+
function buildBlameArgs(input) {
|
|
92
|
+
if (!input.file) {
|
|
93
|
+
throw new Error("git_read blame requires file.");
|
|
94
|
+
}
|
|
95
|
+
const hasLineStart = input.line_start !== undefined;
|
|
96
|
+
const hasLineEnd = input.line_end !== undefined;
|
|
97
|
+
if (hasLineStart !== hasLineEnd) {
|
|
98
|
+
throw new Error("git_read blame requires both line_start and line_end when specifying a range.");
|
|
99
|
+
}
|
|
100
|
+
const args = ["blame", "--line-porcelain"];
|
|
101
|
+
const ref = normalizeRefLike(input.ref, "ref");
|
|
102
|
+
if (ref) {
|
|
103
|
+
args.push(ref);
|
|
104
|
+
}
|
|
105
|
+
if (hasLineStart && hasLineEnd) {
|
|
106
|
+
const lineStart = normalizePositiveInt(input.line_start, 1, "line_start");
|
|
107
|
+
const lineEnd = normalizePositiveInt(input.line_end, 1, "line_end");
|
|
108
|
+
if (lineEnd < lineStart) {
|
|
109
|
+
throw new Error("line_end must be greater than or equal to line_start.");
|
|
110
|
+
}
|
|
111
|
+
args.push("-L", `${lineStart},${lineEnd}`);
|
|
112
|
+
}
|
|
113
|
+
args.push("--", input.file);
|
|
114
|
+
return args;
|
|
115
|
+
}
|
|
116
|
+
function buildShowArgs(input) {
|
|
117
|
+
const ref = requireRefLike(input.ref ?? "HEAD", "ref");
|
|
118
|
+
const args = ["show", "--no-color", ref];
|
|
119
|
+
if (input.file) {
|
|
120
|
+
args.push("--", input.file);
|
|
121
|
+
}
|
|
122
|
+
return args;
|
|
123
|
+
}
|
|
124
|
+
function buildBranchListArgs(input) {
|
|
125
|
+
const includeAll = input.all ?? true;
|
|
126
|
+
const verbose = input.verbose ?? false;
|
|
127
|
+
const args = ["branch", "--list"];
|
|
128
|
+
if (includeAll) {
|
|
129
|
+
args.push("--all");
|
|
130
|
+
}
|
|
131
|
+
if (verbose) {
|
|
132
|
+
args.push("--verbose");
|
|
133
|
+
}
|
|
134
|
+
return args;
|
|
135
|
+
}
|
|
136
|
+
function buildRemoteListArgs(input) {
|
|
137
|
+
const verbose = input.verbose ?? true;
|
|
138
|
+
const args = ["remote"];
|
|
139
|
+
if (verbose) {
|
|
140
|
+
args.push("-v");
|
|
141
|
+
}
|
|
142
|
+
return args;
|
|
143
|
+
}
|
|
144
|
+
function buildRevParseArgs(input) {
|
|
145
|
+
const target = requireRefLike(input.target ?? "HEAD", "target");
|
|
146
|
+
const args = ["rev-parse"];
|
|
147
|
+
if (input.short) {
|
|
148
|
+
args.push("--short");
|
|
149
|
+
}
|
|
150
|
+
args.push(target);
|
|
151
|
+
return args;
|
|
152
|
+
}
|
|
153
|
+
function buildGitReadArgs(input) {
|
|
154
|
+
switch (input.action) {
|
|
155
|
+
case "status":
|
|
156
|
+
return buildStatusArgs(input);
|
|
157
|
+
case "diff":
|
|
158
|
+
return buildDiffArgs(input);
|
|
159
|
+
case "log":
|
|
160
|
+
return buildLogArgs(input);
|
|
161
|
+
case "blame":
|
|
162
|
+
return buildBlameArgs(input);
|
|
163
|
+
case "show":
|
|
164
|
+
return buildShowArgs(input);
|
|
165
|
+
case "branch_list":
|
|
166
|
+
return buildBranchListArgs(input);
|
|
167
|
+
case "remote_list":
|
|
168
|
+
return buildRemoteListArgs(input);
|
|
169
|
+
case "rev_parse":
|
|
170
|
+
return buildRevParseArgs(input);
|
|
171
|
+
default:
|
|
172
|
+
throw new Error(`Unsupported git_read action: ${input.action}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export function createGitReadTool(cwd, options) {
|
|
176
|
+
const { hasCommand, runCommand } = resolveGitCommandOptions(options);
|
|
177
|
+
return {
|
|
178
|
+
name: "git_read",
|
|
179
|
+
label: "git_read",
|
|
180
|
+
description: "Structured read-only git introspection. Actions: status | diff | log | blame | show | branch_list | remote_list | rev_parse. Uses safe argv execution without shell interpolation.",
|
|
181
|
+
parameters: gitReadSchema,
|
|
182
|
+
execute: async (_toolCallId, input, signal) => {
|
|
183
|
+
if (!hasCommand("git")) {
|
|
184
|
+
throw new Error("git command is not available.");
|
|
185
|
+
}
|
|
186
|
+
const repoCwd = resolveToCwd(input.path || ".", cwd);
|
|
187
|
+
const args = buildGitReadArgs(input);
|
|
188
|
+
const timeoutSeconds = normalizePositiveInt(input.timeout, DEFAULT_GIT_TIMEOUT_SECONDS, "timeout");
|
|
189
|
+
const result = await runGitAndFormatOutput({
|
|
190
|
+
toolName: "git_read",
|
|
191
|
+
action: input.action,
|
|
192
|
+
args,
|
|
193
|
+
cwd: repoCwd,
|
|
194
|
+
timeoutSeconds,
|
|
195
|
+
runCommand,
|
|
196
|
+
signal,
|
|
197
|
+
});
|
|
198
|
+
const details = {
|
|
199
|
+
action: input.action,
|
|
200
|
+
command: "git",
|
|
201
|
+
args,
|
|
202
|
+
cwd: repoCwd,
|
|
203
|
+
exitCode: result.exitCode,
|
|
204
|
+
captureTruncated: result.captureTruncated || undefined,
|
|
205
|
+
truncation: result.truncation,
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
content: [{ type: "text", text: result.output }],
|
|
209
|
+
details,
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
export const gitReadTool = createGitReadTool(process.cwd());
|
|
215
|
+
//# sourceMappingURL=git-read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-read.js","sourceRoot":"","sources":["../../../src/core/tools/git-read.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EACN,2BAA2B,EAC3B,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,wBAAwB,EACxB,qBAAqB,GAErB,MAAM,iBAAiB,CAAC;AAGzB,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC;IACjC,MAAM,EAAE,IAAI,CAAC,KAAK,CACjB;QACC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACnB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;KACzB,EACD,EAAE,WAAW,EAAE,yFAAyF,EAAE,CAC1G;IACD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAAC,CAAC;IAC/G,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC,CAAC;IAC9G,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,oCAAoC,EAAE,CAAC,CAAC;IACvF,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAC;IACvG,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC,CAAC;IAChG,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC,CAAC;IAC5G,SAAS,EAAE,IAAI,CAAC,QAAQ,CACvB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,4EAA4E;KACzF,CAAC,CACF;IACD,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,6DAA6D,EAAE,CAAC,CAAC;IACtH,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC,CAAC;IAC1G,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC,CAAC;IAC7G,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,qEAAqE,EAAE,CAAC,CAAC;IACvH,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,iDAAiD,EAAE,CAAC,CAAC;IAC1G,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,+CAA+C,EAAE,CAAC,CAAC;IACtG,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,kEAAkE,EAAE,CAAC,CAAC;IACrH,OAAO,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EAAE,qHAAqH;KAClI,CAAC,CACF;IACD,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC,CAAC;IACnH,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,2DAA2D,EAAE,CAAC,CAAC;IAChH,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC,CAAC;CACzF,CAAC,CAAC;AAIH,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,aAAa,GAAG,GAAG,CAAC;AAgB1B,SAAS,eAAe,CAAC,KAAuB;IAC/C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC;IAC1C,MAAM,gBAAgB,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC;IACjD,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,IAAI,SAAS,EAAE,CAAC;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,KAAuB;IAC7C,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IAClE,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,aAAa,OAAO,EAAE,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvB,CAAC;IACD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;IAC/B,CAAC;SAAM,IAAI,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,YAAY,CAAC,KAAuB;IAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,oBAAoB,CAAC,KAAK,CAAC,KAAK,EAAE,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC;IACrG,MAAM,IAAI,GAAG;QACZ,KAAK;QACL,YAAY;QACZ,eAAe,KAAK,EAAE;QACtB,YAAY;QACZ,wCAAwC;KACxC,CAAC;IACF,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,WAAW,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,cAAc,CAAC,KAAuB;IAC9C,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC;IACpD,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC;IAChD,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,+EAA+E,CAAC,CAAC;IAClG,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/C,IAAI,GAAG,EAAE,CAAC;QACT,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;IACD,IAAI,YAAY,IAAI,UAAU,EAAE,CAAC;QAChC,MAAM,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC;QAC1E,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;QACpE,IAAI,OAAO,GAAG,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,KAAuB;IAC7C,MAAM,GAAG,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,MAAM,EAAE,KAAK,CAAC,CAAC;IACvD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAuB;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;IACvC,MAAM,IAAI,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAClC,IAAI,UAAU,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpB,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACb,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAuB;IACnD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,IAAI,OAAO,EAAE,CAAC;QACb,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAuB;IACjD,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3B,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtB,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClB,OAAO,IAAI,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAuB;IAChD,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QACtB,KAAK,QAAQ;YACZ,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAC/B,KAAK,MAAM;YACV,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,KAAK;YACT,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;QAC5B,KAAK,OAAO;YACX,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC;QAC9B,KAAK,MAAM;YACV,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;QAC7B,KAAK,aAAa;YACjB,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,aAAa;YACjB,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACnC,KAAK,WAAW;YACf,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACjC;YACC,MAAM,IAAI,KAAK,CAAC,gCAAiC,KAA4B,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1F,CAAC;AACF,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,OAA4B;IAC1E,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;IAErE,OAAO;QACN,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;QACjB,WAAW,EACV,oLAAoL;QACrL,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,KAAK,EAAE,WAAmB,EAAE,KAAuB,EAAE,MAAoB,EAAE,EAAE;YACrF,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAClD,CAAC;YAED,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACrD,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,cAAc,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,EAAE,2BAA2B,EAAE,SAAS,CAAC,CAAC;YACnG,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC;gBAC1C,QAAQ,EAAE,UAAU;gBACpB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,IAAI;gBACJ,GAAG,EAAE,OAAO;gBACZ,cAAc;gBACd,UAAU;gBACV,MAAM;aACN,CAAC,CAAC;YAEH,MAAM,OAAO,GAAuB;gBACnC,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,OAAO,EAAE,KAAK;gBACd,IAAI;gBACJ,GAAG,EAAE,OAAO;gBACZ,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,SAAS;gBACtD,UAAU,EAAE,MAAM,CAAC,UAAU;aAC7B,CAAC;YAEF,OAAO;gBACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;gBAChD,OAAO;aACP,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC","sourcesContent":["import type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { resolveToCwd } from \"./path-utils.js\";\nimport {\n\tDEFAULT_GIT_TIMEOUT_SECONDS,\n\tnormalizePositiveInt,\n\tnormalizeRefLike,\n\trequireRefLike,\n\tresolveGitCommandOptions,\n\trunGitAndFormatOutput,\n\ttype GitCommandOptions,\n} from \"./git-common.js\";\nimport type { TruncationResult } from \"./truncate.js\";\n\nconst gitReadSchema = Type.Object({\n\taction: Type.Union(\n\t\t[\n\t\t\tType.Literal(\"status\"),\n\t\t\tType.Literal(\"diff\"),\n\t\t\tType.Literal(\"log\"),\n\t\t\tType.Literal(\"blame\"),\n\t\t\tType.Literal(\"show\"),\n\t\t\tType.Literal(\"branch_list\"),\n\t\t\tType.Literal(\"remote_list\"),\n\t\t\tType.Literal(\"rev_parse\"),\n\t\t],\n\t\t{ description: \"Git action: status | diff | log | blame | show | branch_list | remote_list | rev_parse.\" },\n\t),\n\tpath: Type.Optional(Type.String({ description: \"Repository working directory (default: current directory).\" })),\n\tfile: Type.Optional(Type.String({ description: \"Optional file path for diff/log/show, required for blame.\" })),\n\tbase: Type.Optional(Type.String({ description: \"Optional base ref/commit for diff.\" })),\n\thead: Type.Optional(Type.String({ description: \"Optional head ref/commit for diff (requires base).\" })),\n\tstaged: Type.Optional(Type.Boolean({ description: \"For diff action: compare staged changes.\" })),\n\tcontext: Type.Optional(Type.Number({ description: \"For diff action: unified context lines (default: 3).\" })),\n\tporcelain: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"For status action: use short porcelain format with branch (default: true).\",\n\t\t}),\n\t),\n\tuntracked: Type.Optional(Type.Boolean({ description: \"For status action: include untracked files (default: true).\" })),\n\tlimit: Type.Optional(Type.Number({ description: \"For log action: max entries (default: 20, max: 200).\" })),\n\tsince: Type.Optional(Type.String({ description: \"For log action: git --since value (e.g. '2 weeks ago').\" })),\n\tref: Type.Optional(Type.String({ description: \"For blame/show action: optional ref/commit (show defaults to HEAD).\" })),\n\tline_start: Type.Optional(Type.Number({ description: \"For blame action: range start line (1-indexed).\" })),\n\tline_end: Type.Optional(Type.Number({ description: \"For blame action: range end line (1-indexed).\" })),\n\tall: Type.Optional(Type.Boolean({ description: \"For branch_list action: include remote branches (default: true).\" })),\n\tverbose: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription: \"For branch_list/remote_list action: include verbose output (branch_list default: false, remote_list default: true).\",\n\t\t}),\n\t),\n\ttarget: Type.Optional(Type.String({ description: \"For rev_parse action: ref/commit to resolve (default: HEAD).\" })),\n\tshort: Type.Optional(Type.Boolean({ description: \"For rev_parse action: output short hash (default: false).\" })),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (default: 30).\" })),\n});\n\nexport type GitReadToolInput = Static<typeof gitReadSchema>;\n\nconst DEFAULT_LOG_LIMIT = 20;\nconst MAX_LOG_LIMIT = 200;\n\ntype GitReadAction = GitReadToolInput[\"action\"];\n\nexport interface GitReadToolDetails {\n\taction: GitReadAction;\n\tcommand: string;\n\targs: string[];\n\tcwd: string;\n\texitCode: number;\n\tcaptureTruncated?: boolean;\n\ttruncation?: TruncationResult;\n}\n\nexport interface GitReadToolOptions extends GitCommandOptions {}\n\nfunction buildStatusArgs(input: GitReadToolInput): string[] {\n\tconst porcelain = input.porcelain ?? true;\n\tconst includeUntracked = input.untracked ?? true;\n\tconst args = [\"status\"];\n\tif (porcelain) {\n\t\targs.push(\"--short\", \"--branch\");\n\t}\n\tif (!includeUntracked) {\n\t\targs.push(\"--untracked-files=no\");\n\t}\n\treturn args;\n}\n\nfunction buildDiffArgs(input: GitReadToolInput): string[] {\n\tif (input.head && !input.base) {\n\t\tthrow new Error(\"git_read diff requires base when head is provided.\");\n\t}\n\tconst context = normalizePositiveInt(input.context, 3, \"context\");\n\tconst base = normalizeRefLike(input.base, \"base\");\n\tconst head = normalizeRefLike(input.head, \"head\");\n\tconst args = [\"diff\", \"--no-color\", `--unified=${context}`];\n\tif (input.staged) {\n\t\targs.push(\"--staged\");\n\t}\n\tif (base && head) {\n\t\targs.push(`${base}..${head}`);\n\t} else if (base) {\n\t\targs.push(base);\n\t}\n\tif (input.file) {\n\t\targs.push(\"--\", input.file);\n\t}\n\treturn args;\n}\n\nfunction buildLogArgs(input: GitReadToolInput): string[] {\n\tconst limit = Math.min(MAX_LOG_LIMIT, normalizePositiveInt(input.limit, DEFAULT_LOG_LIMIT, \"limit\"));\n\tconst args = [\n\t\t\"log\",\n\t\t\"--no-color\",\n\t\t`--max-count=${limit}`,\n\t\t\"--date=iso\",\n\t\t\"--pretty=format:%H%x09%ad%x09%an%x09%s\",\n\t];\n\tif (input.since) {\n\t\targs.push(`--since=${input.since}`);\n\t}\n\tif (input.file) {\n\t\targs.push(\"--\", input.file);\n\t}\n\treturn args;\n}\n\nfunction buildBlameArgs(input: GitReadToolInput): string[] {\n\tif (!input.file) {\n\t\tthrow new Error(\"git_read blame requires file.\");\n\t}\n\tconst hasLineStart = input.line_start !== undefined;\n\tconst hasLineEnd = input.line_end !== undefined;\n\tif (hasLineStart !== hasLineEnd) {\n\t\tthrow new Error(\"git_read blame requires both line_start and line_end when specifying a range.\");\n\t}\n\tconst args = [\"blame\", \"--line-porcelain\"];\n\tconst ref = normalizeRefLike(input.ref, \"ref\");\n\tif (ref) {\n\t\targs.push(ref);\n\t}\n\tif (hasLineStart && hasLineEnd) {\n\t\tconst lineStart = normalizePositiveInt(input.line_start, 1, \"line_start\");\n\t\tconst lineEnd = normalizePositiveInt(input.line_end, 1, \"line_end\");\n\t\tif (lineEnd < lineStart) {\n\t\t\tthrow new Error(\"line_end must be greater than or equal to line_start.\");\n\t\t}\n\t\targs.push(\"-L\", `${lineStart},${lineEnd}`);\n\t}\n\targs.push(\"--\", input.file);\n\treturn args;\n}\n\nfunction buildShowArgs(input: GitReadToolInput): string[] {\n\tconst ref = requireRefLike(input.ref ?? \"HEAD\", \"ref\");\n\tconst args = [\"show\", \"--no-color\", ref];\n\tif (input.file) {\n\t\targs.push(\"--\", input.file);\n\t}\n\treturn args;\n}\n\nfunction buildBranchListArgs(input: GitReadToolInput): string[] {\n\tconst includeAll = input.all ?? true;\n\tconst verbose = input.verbose ?? false;\n\tconst args = [\"branch\", \"--list\"];\n\tif (includeAll) {\n\t\targs.push(\"--all\");\n\t}\n\tif (verbose) {\n\t\targs.push(\"--verbose\");\n\t}\n\treturn args;\n}\n\nfunction buildRemoteListArgs(input: GitReadToolInput): string[] {\n\tconst verbose = input.verbose ?? true;\n\tconst args = [\"remote\"];\n\tif (verbose) {\n\t\targs.push(\"-v\");\n\t}\n\treturn args;\n}\n\nfunction buildRevParseArgs(input: GitReadToolInput): string[] {\n\tconst target = requireRefLike(input.target ?? \"HEAD\", \"target\");\n\tconst args = [\"rev-parse\"];\n\tif (input.short) {\n\t\targs.push(\"--short\");\n\t}\n\targs.push(target);\n\treturn args;\n}\n\nfunction buildGitReadArgs(input: GitReadToolInput): string[] {\n\tswitch (input.action) {\n\t\tcase \"status\":\n\t\t\treturn buildStatusArgs(input);\n\t\tcase \"diff\":\n\t\t\treturn buildDiffArgs(input);\n\t\tcase \"log\":\n\t\t\treturn buildLogArgs(input);\n\t\tcase \"blame\":\n\t\t\treturn buildBlameArgs(input);\n\t\tcase \"show\":\n\t\t\treturn buildShowArgs(input);\n\t\tcase \"branch_list\":\n\t\t\treturn buildBranchListArgs(input);\n\t\tcase \"remote_list\":\n\t\t\treturn buildRemoteListArgs(input);\n\t\tcase \"rev_parse\":\n\t\t\treturn buildRevParseArgs(input);\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported git_read action: ${(input as { action: string }).action}`);\n\t}\n}\n\nexport function createGitReadTool(cwd: string, options?: GitReadToolOptions): AgentTool<typeof gitReadSchema> {\n\tconst { hasCommand, runCommand } = resolveGitCommandOptions(options);\n\n\treturn {\n\t\tname: \"git_read\",\n\t\tlabel: \"git_read\",\n\t\tdescription:\n\t\t\t\"Structured read-only git introspection. Actions: status | diff | log | blame | show | branch_list | remote_list | rev_parse. Uses safe argv execution without shell interpolation.\",\n\t\tparameters: gitReadSchema,\n\t\texecute: async (_toolCallId: string, input: GitReadToolInput, signal?: AbortSignal) => {\n\t\t\tif (!hasCommand(\"git\")) {\n\t\t\t\tthrow new Error(\"git command is not available.\");\n\t\t\t}\n\n\t\t\tconst repoCwd = resolveToCwd(input.path || \".\", cwd);\n\t\t\tconst args = buildGitReadArgs(input);\n\t\t\tconst timeoutSeconds = normalizePositiveInt(input.timeout, DEFAULT_GIT_TIMEOUT_SECONDS, \"timeout\");\n\t\t\tconst result = await runGitAndFormatOutput({\n\t\t\t\ttoolName: \"git_read\",\n\t\t\t\taction: input.action,\n\t\t\t\targs,\n\t\t\t\tcwd: repoCwd,\n\t\t\t\ttimeoutSeconds,\n\t\t\t\trunCommand,\n\t\t\t\tsignal,\n\t\t\t});\n\n\t\t\tconst details: GitReadToolDetails = {\n\t\t\t\taction: input.action,\n\t\t\t\tcommand: \"git\",\n\t\t\t\targs,\n\t\t\t\tcwd: repoCwd,\n\t\t\t\texitCode: result.exitCode,\n\t\t\t\tcaptureTruncated: result.captureTruncated || undefined,\n\t\t\t\ttruncation: result.truncation,\n\t\t\t};\n\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: result.output }],\n\t\t\t\tdetails,\n\t\t\t};\n\t\t},\n\t};\n}\n\nexport const gitReadTool = createGitReadTool(process.cwd());\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
|
2
|
+
import { type Static } from "@sinclair/typebox";
|
|
3
|
+
import { type GitCommandOptions } from "./git-common.js";
|
|
4
|
+
import type { ToolPermissionGuard } from "./permissions.js";
|
|
5
|
+
import type { TruncationResult } from "./truncate.js";
|
|
6
|
+
declare const gitWriteSchema: import("@sinclair/typebox").TObject<{
|
|
7
|
+
action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"add">, import("@sinclair/typebox").TLiteral<"restore">, import("@sinclair/typebox").TLiteral<"reset_index">, import("@sinclair/typebox").TLiteral<"commit">, import("@sinclair/typebox").TLiteral<"switch">, import("@sinclair/typebox").TLiteral<"branch_create">, import("@sinclair/typebox").TLiteral<"fetch">, import("@sinclair/typebox").TLiteral<"pull">, import("@sinclair/typebox").TLiteral<"push">, import("@sinclair/typebox").TLiteral<"stash_push">, import("@sinclair/typebox").TLiteral<"stash_pop">, import("@sinclair/typebox").TLiteral<"stash_apply">, import("@sinclair/typebox").TLiteral<"stash_drop">, import("@sinclair/typebox").TLiteral<"stash_list">]>;
|
|
8
|
+
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
9
|
+
all: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
10
|
+
update: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
11
|
+
files: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
12
|
+
staged: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
13
|
+
source: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
14
|
+
ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
15
|
+
message: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
16
|
+
allow_empty: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
17
|
+
branch: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
18
|
+
start_point: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
19
|
+
remote: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
20
|
+
ff_only: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
21
|
+
prune: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
22
|
+
set_upstream: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
23
|
+
tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
24
|
+
include_untracked: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
25
|
+
keep_index: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
26
|
+
stash_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
27
|
+
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
28
|
+
timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
29
|
+
}>;
|
|
30
|
+
export type GitWriteToolInput = Static<typeof gitWriteSchema>;
|
|
31
|
+
type GitWriteAction = GitWriteToolInput["action"];
|
|
32
|
+
export interface GitWriteToolDetails {
|
|
33
|
+
action: GitWriteAction;
|
|
34
|
+
command: string;
|
|
35
|
+
args: string[];
|
|
36
|
+
cwd: string;
|
|
37
|
+
exitCode: number;
|
|
38
|
+
captureTruncated?: boolean;
|
|
39
|
+
truncation?: TruncationResult;
|
|
40
|
+
}
|
|
41
|
+
export interface GitWriteRuntimeConfig {
|
|
42
|
+
networkEnabled: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface GitWriteToolOptions extends GitCommandOptions {
|
|
45
|
+
permissionGuard?: ToolPermissionGuard;
|
|
46
|
+
resolveRuntimeConfig?: () => Partial<GitWriteRuntimeConfig> | GitWriteRuntimeConfig;
|
|
47
|
+
resolveGithubToken?: () => string | undefined;
|
|
48
|
+
}
|
|
49
|
+
export declare function createGitWriteTool(cwd: string, options?: GitWriteToolOptions): AgentTool<typeof gitWriteSchema>;
|
|
50
|
+
export declare const gitWriteTool: AgentTool<import("@sinclair/typebox").TObject<{
|
|
51
|
+
action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"add">, import("@sinclair/typebox").TLiteral<"restore">, import("@sinclair/typebox").TLiteral<"reset_index">, import("@sinclair/typebox").TLiteral<"commit">, import("@sinclair/typebox").TLiteral<"switch">, import("@sinclair/typebox").TLiteral<"branch_create">, import("@sinclair/typebox").TLiteral<"fetch">, import("@sinclair/typebox").TLiteral<"pull">, import("@sinclair/typebox").TLiteral<"push">, import("@sinclair/typebox").TLiteral<"stash_push">, import("@sinclair/typebox").TLiteral<"stash_pop">, import("@sinclair/typebox").TLiteral<"stash_apply">, import("@sinclair/typebox").TLiteral<"stash_drop">, import("@sinclair/typebox").TLiteral<"stash_list">]>;
|
|
52
|
+
path: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
53
|
+
all: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
54
|
+
update: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
55
|
+
files: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>>;
|
|
56
|
+
staged: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
57
|
+
source: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
58
|
+
ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
59
|
+
message: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
60
|
+
allow_empty: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
61
|
+
branch: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
62
|
+
start_point: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
63
|
+
remote: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
64
|
+
ff_only: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
65
|
+
prune: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
66
|
+
set_upstream: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
67
|
+
tags: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
68
|
+
include_untracked: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
69
|
+
keep_index: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
|
70
|
+
stash_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
|
71
|
+
limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
72
|
+
timeout: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
|
|
73
|
+
}>, any>;
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=git-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-write.d.ts","sourceRoot":"","sources":["../../../src/core/tools/git-write.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EASN,KAAK,iBAAiB,EACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;EAgDlB,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAO9D,KAAK,cAAc,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AAElD,MAAM,WAAW,mBAAmB;IACnC,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,WAAW,qBAAqB;IACrC,cAAc,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAC7D,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,GAAG,qBAAqB,CAAC;IACpF,kBAAkB,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;CAC9C;AA6MD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC,OAAO,cAAc,CAAC,CA0E/G;AAED,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;QAAoC,CAAC"}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { resolveToCwd } from "./path-utils.js";
|
|
3
|
+
import { DEFAULT_GIT_TIMEOUT_SECONDS, normalizeFiles, normalizePositiveInt, normalizeRefLike, normalizeRequiredString, requireRefLike, resolveGitCommandOptions, runGitAndFormatOutput, } from "./git-common.js";
|
|
4
|
+
const gitWriteSchema = Type.Object({
|
|
5
|
+
action: Type.Union([
|
|
6
|
+
Type.Literal("add"),
|
|
7
|
+
Type.Literal("restore"),
|
|
8
|
+
Type.Literal("reset_index"),
|
|
9
|
+
Type.Literal("commit"),
|
|
10
|
+
Type.Literal("switch"),
|
|
11
|
+
Type.Literal("branch_create"),
|
|
12
|
+
Type.Literal("fetch"),
|
|
13
|
+
Type.Literal("pull"),
|
|
14
|
+
Type.Literal("push"),
|
|
15
|
+
Type.Literal("stash_push"),
|
|
16
|
+
Type.Literal("stash_pop"),
|
|
17
|
+
Type.Literal("stash_apply"),
|
|
18
|
+
Type.Literal("stash_drop"),
|
|
19
|
+
Type.Literal("stash_list"),
|
|
20
|
+
], {
|
|
21
|
+
description: "Git write action: add | restore | reset_index | commit | switch | branch_create | fetch | pull | push | stash_push | stash_pop | stash_apply | stash_drop | stash_list.",
|
|
22
|
+
}),
|
|
23
|
+
path: Type.Optional(Type.String({ description: "Repository working directory (default: current directory)." })),
|
|
24
|
+
all: Type.Optional(Type.Boolean({ description: "For add action: stage all tracked and untracked changes." })),
|
|
25
|
+
update: Type.Optional(Type.Boolean({ description: "For add action: stage modified/deleted tracked files." })),
|
|
26
|
+
files: Type.Optional(Type.Array(Type.String(), { description: "For add/restore/reset_index action: file paths." })),
|
|
27
|
+
staged: Type.Optional(Type.Boolean({ description: "For restore action: restore index instead of working tree." })),
|
|
28
|
+
source: Type.Optional(Type.String({ description: "For restore action: source ref (e.g., HEAD)." })),
|
|
29
|
+
ref: Type.Optional(Type.String({ description: "For reset_index action: reference commit (default: HEAD)." })),
|
|
30
|
+
message: Type.Optional(Type.String({ description: "For commit/stash_push action: message." })),
|
|
31
|
+
allow_empty: Type.Optional(Type.Boolean({ description: "For commit action: allow empty commit." })),
|
|
32
|
+
branch: Type.Optional(Type.String({ description: "For switch/branch_create action: branch name." })),
|
|
33
|
+
start_point: Type.Optional(Type.String({ description: "For branch_create action: start point (default: HEAD)." })),
|
|
34
|
+
remote: Type.Optional(Type.String({ description: "For fetch/pull/push action: remote name (default: origin)." })),
|
|
35
|
+
ff_only: Type.Optional(Type.Boolean({ description: "For pull action: require fast-forward merge (default: true)." })),
|
|
36
|
+
prune: Type.Optional(Type.Boolean({ description: "For fetch action: prune deleted remote refs." })),
|
|
37
|
+
set_upstream: Type.Optional(Type.Boolean({ description: "For push action: set upstream for branch." })),
|
|
38
|
+
tags: Type.Optional(Type.Boolean({ description: "For fetch/push action: include tags." })),
|
|
39
|
+
include_untracked: Type.Optional(Type.Boolean({ description: "For stash_push action: include untracked files." })),
|
|
40
|
+
keep_index: Type.Optional(Type.Boolean({ description: "For stash_push action: keep index unchanged." })),
|
|
41
|
+
stash_ref: Type.Optional(Type.String({ description: "For stash_pop/stash_apply/stash_drop action: stash ref (default: stash@{0})." })),
|
|
42
|
+
limit: Type.Optional(Type.Number({ description: "For stash_list action: max entries (default: 20, max: 200)." })),
|
|
43
|
+
timeout: Type.Optional(Type.Number({ description: "Timeout in seconds (default: 30)." })),
|
|
44
|
+
});
|
|
45
|
+
const DEFAULT_STASH_LIST_LIMIT = 20;
|
|
46
|
+
const MAX_STASH_LIST_LIMIT = 200;
|
|
47
|
+
const DEFAULT_REMOTE = "origin";
|
|
48
|
+
const DEFAULT_GIT_WRITE_NETWORK_ENABLED = false;
|
|
49
|
+
function normalizeGitWriteRuntimeConfig(config) {
|
|
50
|
+
return {
|
|
51
|
+
networkEnabled: typeof config?.networkEnabled === "boolean"
|
|
52
|
+
? config.networkEnabled
|
|
53
|
+
: DEFAULT_GIT_WRITE_NETWORK_ENABLED,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function buildAddArgs(input) {
|
|
57
|
+
const useAll = input.all === true;
|
|
58
|
+
const useUpdate = input.update === true;
|
|
59
|
+
const hasFiles = (input.files?.length ?? 0) > 0;
|
|
60
|
+
const modeCount = (useAll ? 1 : 0) + (useUpdate ? 1 : 0) + (hasFiles ? 1 : 0);
|
|
61
|
+
if (modeCount !== 1) {
|
|
62
|
+
throw new Error("git_write add requires exactly one mode: all=true, update=true, or files[].");
|
|
63
|
+
}
|
|
64
|
+
const args = ["add"];
|
|
65
|
+
if (useAll) {
|
|
66
|
+
args.push("--all");
|
|
67
|
+
return args;
|
|
68
|
+
}
|
|
69
|
+
if (useUpdate) {
|
|
70
|
+
args.push("--update");
|
|
71
|
+
return args;
|
|
72
|
+
}
|
|
73
|
+
const files = normalizeFiles(input.files, "files");
|
|
74
|
+
args.push("--", ...files);
|
|
75
|
+
return args;
|
|
76
|
+
}
|
|
77
|
+
function buildRestoreArgs(input) {
|
|
78
|
+
const files = normalizeFiles(input.files, "files");
|
|
79
|
+
const args = ["restore"];
|
|
80
|
+
if (input.staged) {
|
|
81
|
+
args.push("--staged");
|
|
82
|
+
}
|
|
83
|
+
const source = normalizeRefLike(input.source, "source");
|
|
84
|
+
if (source) {
|
|
85
|
+
args.push(`--source=${source}`);
|
|
86
|
+
}
|
|
87
|
+
args.push("--", ...files);
|
|
88
|
+
return args;
|
|
89
|
+
}
|
|
90
|
+
function buildResetIndexArgs(input) {
|
|
91
|
+
const files = normalizeFiles(input.files, "files");
|
|
92
|
+
const ref = requireRefLike(input.ref ?? "HEAD", "ref");
|
|
93
|
+
return ["reset", ref, "--", ...files];
|
|
94
|
+
}
|
|
95
|
+
function buildCommitArgs(input) {
|
|
96
|
+
const message = normalizeRequiredString(input.message, "message");
|
|
97
|
+
const args = ["commit", "-m", message];
|
|
98
|
+
if (input.allow_empty) {
|
|
99
|
+
args.push("--allow-empty");
|
|
100
|
+
}
|
|
101
|
+
return args;
|
|
102
|
+
}
|
|
103
|
+
function buildSwitchArgs(input) {
|
|
104
|
+
const branch = requireRefLike(input.branch, "branch");
|
|
105
|
+
return ["switch", branch];
|
|
106
|
+
}
|
|
107
|
+
function buildBranchCreateArgs(input) {
|
|
108
|
+
const branch = requireRefLike(input.branch, "branch");
|
|
109
|
+
const startPoint = requireRefLike(input.start_point ?? "HEAD", "start_point");
|
|
110
|
+
return ["branch", branch, startPoint];
|
|
111
|
+
}
|
|
112
|
+
function buildFetchArgs(input) {
|
|
113
|
+
const remote = requireRefLike(input.remote ?? DEFAULT_REMOTE, "remote");
|
|
114
|
+
const args = ["fetch"];
|
|
115
|
+
if (input.prune) {
|
|
116
|
+
args.push("--prune");
|
|
117
|
+
}
|
|
118
|
+
if (input.tags) {
|
|
119
|
+
args.push("--tags");
|
|
120
|
+
}
|
|
121
|
+
args.push(remote);
|
|
122
|
+
return args;
|
|
123
|
+
}
|
|
124
|
+
function buildPullArgs(input) {
|
|
125
|
+
const remote = requireRefLike(input.remote ?? DEFAULT_REMOTE, "remote");
|
|
126
|
+
const args = ["pull"];
|
|
127
|
+
if (input.ff_only ?? true) {
|
|
128
|
+
args.push("--ff-only");
|
|
129
|
+
}
|
|
130
|
+
args.push(remote);
|
|
131
|
+
const branch = normalizeRefLike(input.branch, "branch");
|
|
132
|
+
if (branch) {
|
|
133
|
+
args.push(branch);
|
|
134
|
+
}
|
|
135
|
+
return args;
|
|
136
|
+
}
|
|
137
|
+
function buildPushArgs(input) {
|
|
138
|
+
const remote = requireRefLike(input.remote ?? DEFAULT_REMOTE, "remote");
|
|
139
|
+
const args = ["push"];
|
|
140
|
+
if (input.set_upstream) {
|
|
141
|
+
args.push("--set-upstream");
|
|
142
|
+
}
|
|
143
|
+
if (input.tags) {
|
|
144
|
+
args.push("--tags");
|
|
145
|
+
}
|
|
146
|
+
args.push(remote);
|
|
147
|
+
const branch = normalizeRefLike(input.branch, "branch");
|
|
148
|
+
if (branch) {
|
|
149
|
+
args.push(branch);
|
|
150
|
+
}
|
|
151
|
+
return args;
|
|
152
|
+
}
|
|
153
|
+
function buildStashPushArgs(input) {
|
|
154
|
+
const args = ["stash", "push"];
|
|
155
|
+
if (input.include_untracked) {
|
|
156
|
+
args.push("--include-untracked");
|
|
157
|
+
}
|
|
158
|
+
if (input.keep_index) {
|
|
159
|
+
args.push("--keep-index");
|
|
160
|
+
}
|
|
161
|
+
if (input.message !== undefined) {
|
|
162
|
+
const message = normalizeRequiredString(input.message, "message");
|
|
163
|
+
args.push("-m", message);
|
|
164
|
+
}
|
|
165
|
+
return args;
|
|
166
|
+
}
|
|
167
|
+
function buildStashEntryActionArgs(input, action) {
|
|
168
|
+
const stashRef = requireRefLike(input.stash_ref ?? "stash@{0}", "stash_ref");
|
|
169
|
+
return ["stash", action, stashRef];
|
|
170
|
+
}
|
|
171
|
+
function buildStashListArgs(input) {
|
|
172
|
+
const limit = Math.min(MAX_STASH_LIST_LIMIT, normalizePositiveInt(input.limit, DEFAULT_STASH_LIST_LIMIT, "limit"));
|
|
173
|
+
return ["stash", "list", `--max-count=${limit}`, "--date=iso", "--pretty=format:%gd%x09%H%x09%ad%x09%s"];
|
|
174
|
+
}
|
|
175
|
+
function buildGitWriteArgs(input) {
|
|
176
|
+
switch (input.action) {
|
|
177
|
+
case "add":
|
|
178
|
+
return buildAddArgs(input);
|
|
179
|
+
case "restore":
|
|
180
|
+
return buildRestoreArgs(input);
|
|
181
|
+
case "reset_index":
|
|
182
|
+
return buildResetIndexArgs(input);
|
|
183
|
+
case "commit":
|
|
184
|
+
return buildCommitArgs(input);
|
|
185
|
+
case "switch":
|
|
186
|
+
return buildSwitchArgs(input);
|
|
187
|
+
case "branch_create":
|
|
188
|
+
return buildBranchCreateArgs(input);
|
|
189
|
+
case "fetch":
|
|
190
|
+
return buildFetchArgs(input);
|
|
191
|
+
case "pull":
|
|
192
|
+
return buildPullArgs(input);
|
|
193
|
+
case "push":
|
|
194
|
+
return buildPushArgs(input);
|
|
195
|
+
case "stash_push":
|
|
196
|
+
return buildStashPushArgs(input);
|
|
197
|
+
case "stash_pop":
|
|
198
|
+
return buildStashEntryActionArgs(input, "pop");
|
|
199
|
+
case "stash_apply":
|
|
200
|
+
return buildStashEntryActionArgs(input, "apply");
|
|
201
|
+
case "stash_drop":
|
|
202
|
+
return buildStashEntryActionArgs(input, "drop");
|
|
203
|
+
case "stash_list":
|
|
204
|
+
return buildStashListArgs(input);
|
|
205
|
+
default:
|
|
206
|
+
throw new Error(`Unsupported git_write action: ${input.action}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function isNetworkAction(action) {
|
|
210
|
+
return action === "fetch" || action === "pull" || action === "push";
|
|
211
|
+
}
|
|
212
|
+
function buildGitNetworkEnv(githubToken) {
|
|
213
|
+
const env = {
|
|
214
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
215
|
+
};
|
|
216
|
+
const token = githubToken?.trim();
|
|
217
|
+
if (!token) {
|
|
218
|
+
return env;
|
|
219
|
+
}
|
|
220
|
+
const basic = Buffer.from(`x-access-token:${token}`, "utf-8").toString("base64");
|
|
221
|
+
const existingCountRaw = process.env.GIT_CONFIG_COUNT;
|
|
222
|
+
const existingCountParsed = existingCountRaw ? Number.parseInt(existingCountRaw, 10) : Number.NaN;
|
|
223
|
+
const existingCount = Number.isFinite(existingCountParsed) && existingCountParsed >= 0 ? existingCountParsed : 0;
|
|
224
|
+
const keyIndex = existingCount;
|
|
225
|
+
env.GIT_CONFIG_COUNT = String(existingCount + 1);
|
|
226
|
+
env[`GIT_CONFIG_KEY_${keyIndex}`] = "http.https://github.com/.extraheader";
|
|
227
|
+
env[`GIT_CONFIG_VALUE_${keyIndex}`] = `AUTHORIZATION: basic ${basic}`;
|
|
228
|
+
return env;
|
|
229
|
+
}
|
|
230
|
+
export function createGitWriteTool(cwd, options) {
|
|
231
|
+
const { hasCommand, runCommand } = resolveGitCommandOptions(options);
|
|
232
|
+
const permissionGuard = options?.permissionGuard;
|
|
233
|
+
const resolveRuntimeConfig = options?.resolveRuntimeConfig;
|
|
234
|
+
const resolveGithubToken = options?.resolveGithubToken;
|
|
235
|
+
return {
|
|
236
|
+
name: "git_write",
|
|
237
|
+
label: "git_write",
|
|
238
|
+
description: "Structured git mutation tool. Actions: add, restore, reset_index, commit, switch, branch_create, fetch, pull, push, stash_push, stash_pop, stash_apply, stash_drop, stash_list. Network actions are disabled by default and require explicit runtime enablement.",
|
|
239
|
+
parameters: gitWriteSchema,
|
|
240
|
+
execute: async (_toolCallId, input, signal) => {
|
|
241
|
+
if (!hasCommand("git")) {
|
|
242
|
+
throw new Error("git command is not available.");
|
|
243
|
+
}
|
|
244
|
+
const runtimeConfig = normalizeGitWriteRuntimeConfig(resolveRuntimeConfig?.());
|
|
245
|
+
const networkAction = isNetworkAction(input.action);
|
|
246
|
+
if (networkAction && !runtimeConfig.networkEnabled) {
|
|
247
|
+
throw new Error("git_write network actions are disabled. Enable Github tools network access in settings.");
|
|
248
|
+
}
|
|
249
|
+
const repoCwd = resolveToCwd(input.path || ".", cwd);
|
|
250
|
+
const args = buildGitWriteArgs(input);
|
|
251
|
+
const timeoutSeconds = normalizePositiveInt(input.timeout, DEFAULT_GIT_TIMEOUT_SECONDS, "timeout");
|
|
252
|
+
const env = networkAction ? buildGitNetworkEnv(resolveGithubToken?.()) : undefined;
|
|
253
|
+
if (permissionGuard) {
|
|
254
|
+
const allowed = await permissionGuard({
|
|
255
|
+
toolName: "git_write",
|
|
256
|
+
cwd,
|
|
257
|
+
input: {
|
|
258
|
+
action: input.action,
|
|
259
|
+
path: input.path,
|
|
260
|
+
repoCwd,
|
|
261
|
+
args,
|
|
262
|
+
timeoutSeconds,
|
|
263
|
+
networkAction,
|
|
264
|
+
},
|
|
265
|
+
summary: `git ${args.join(" ")}`,
|
|
266
|
+
});
|
|
267
|
+
if (!allowed) {
|
|
268
|
+
throw new Error("Permission denied for git_write operation.");
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const result = await runGitAndFormatOutput({
|
|
272
|
+
toolName: "git_write",
|
|
273
|
+
action: input.action,
|
|
274
|
+
args,
|
|
275
|
+
cwd: repoCwd,
|
|
276
|
+
timeoutSeconds,
|
|
277
|
+
runCommand,
|
|
278
|
+
signal,
|
|
279
|
+
env,
|
|
280
|
+
});
|
|
281
|
+
const details = {
|
|
282
|
+
action: input.action,
|
|
283
|
+
command: "git",
|
|
284
|
+
args,
|
|
285
|
+
cwd: repoCwd,
|
|
286
|
+
exitCode: result.exitCode,
|
|
287
|
+
captureTruncated: result.captureTruncated || undefined,
|
|
288
|
+
truncation: result.truncation,
|
|
289
|
+
};
|
|
290
|
+
return {
|
|
291
|
+
content: [{ type: "text", text: result.output }],
|
|
292
|
+
details,
|
|
293
|
+
};
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
export const gitWriteTool = createGitWriteTool(process.cwd());
|
|
298
|
+
//# sourceMappingURL=git-write.js.map
|