pi-graphite 0.2.2 → 0.3.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.
- package/README.md +69 -34
- package/package.json +6 -2
- package/skills/graphite/SKILL.md +218 -0
- package/src/index.ts +40 -53
- package/src/lib/argv.ts +59 -0
- package/src/lib/exec.ts +98 -17
- package/src/lib/result.ts +29 -13
- package/src/lib/schema.ts +5 -4
- package/src/tools/change.ts +140 -0
- package/src/tools/navigate.ts +99 -0
- package/src/tools/recover.ts +147 -0
- package/src/tools/setup.ts +121 -0
- package/src/tools/status.ts +55 -0
- package/src/tools/submit.ts +127 -0
- package/src/tools/sync.ts +79 -0
- package/src/tools/branch.ts +0 -428
- package/src/tools/pr.ts +0 -225
- package/src/tools/recovery.ts +0 -53
- package/src/tools/remote.ts +0 -108
- package/src/tools/repo.ts +0 -122
- package/src/tools/stack.ts +0 -185
package/src/tools/recovery.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { runGt } from "../lib/exec";
|
|
3
|
-
import { ensureSuccess, renderText } from "../lib/result";
|
|
4
|
-
import { CwdParam, StringEnum, Type, type ToolReturn } from "../lib/schema";
|
|
5
|
-
|
|
6
|
-
export function registerRecovery(pi: ExtensionAPI) {
|
|
7
|
-
pi.registerTool({
|
|
8
|
-
name: "graphite_recovery",
|
|
9
|
-
label: "Graphite: recovery",
|
|
10
|
-
description:
|
|
11
|
-
"Recover from conflicts or mistakes: continue a halted command, abort it, or undo the most recent Graphite mutation in this worktree.",
|
|
12
|
-
promptSnippet:
|
|
13
|
-
"graphite_recovery: continue / abort / undo Graphite commands",
|
|
14
|
-
promptGuidelines: [
|
|
15
|
-
"After resolving a rebase conflict, run graphite_recovery action=continue to resume the original gt command.",
|
|
16
|
-
"graphite_recovery action=undo only undoes commands run from the current worktree.",
|
|
17
|
-
],
|
|
18
|
-
parameters: Type.Object({
|
|
19
|
-
cwd: CwdParam,
|
|
20
|
-
action: StringEnum(["continue", "abort", "undo"] as const),
|
|
21
|
-
stageAll: Type.Optional(
|
|
22
|
-
Type.Boolean({ description: "action=continue: stage all changes first (--all)." }),
|
|
23
|
-
),
|
|
24
|
-
force: Type.Optional(
|
|
25
|
-
Type.Boolean({ description: "action=abort|undo: skip confirmation prompt (--force)." }),
|
|
26
|
-
),
|
|
27
|
-
}),
|
|
28
|
-
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
29
|
-
let args: string[];
|
|
30
|
-
switch (p.action) {
|
|
31
|
-
case "continue":
|
|
32
|
-
args = ["continue"];
|
|
33
|
-
if (p.stageAll) args.push("--all");
|
|
34
|
-
break;
|
|
35
|
-
case "abort":
|
|
36
|
-
args = ["abort"];
|
|
37
|
-
if (p.force) args.push("--force");
|
|
38
|
-
break;
|
|
39
|
-
case "undo":
|
|
40
|
-
args = ["undo"];
|
|
41
|
-
if (p.force) args.push("--force");
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
const label = `gt ${args.join(" ")}`;
|
|
45
|
-
const r = await runGt(args, { cwd: p.cwd, signal });
|
|
46
|
-
const f = await ensureSuccess(label, r, p.cwd);
|
|
47
|
-
return {
|
|
48
|
-
content: [{ type: "text", text: renderText(label, f) }],
|
|
49
|
-
details: { action: p.action, result: f },
|
|
50
|
-
};
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
}
|
package/src/tools/remote.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { runGt } from "../lib/exec";
|
|
3
|
-
import { ensureSuccess, renderText } from "../lib/result";
|
|
4
|
-
import {
|
|
5
|
-
CwdParam,
|
|
6
|
-
StringEnum,
|
|
7
|
-
Type,
|
|
8
|
-
requireConfirm,
|
|
9
|
-
type ToolReturn,
|
|
10
|
-
} from "../lib/schema";
|
|
11
|
-
|
|
12
|
-
export function registerRemoteSync(pi: ExtensionAPI) {
|
|
13
|
-
pi.registerTool({
|
|
14
|
-
name: "graphite_remote_sync",
|
|
15
|
-
label: "Graphite: remote sync",
|
|
16
|
-
description:
|
|
17
|
-
"Sync local branches with remote: `gt sync` (pull trunk + restack + cleanup) or `gt get` (fetch a branch / PR locally).",
|
|
18
|
-
promptSnippet:
|
|
19
|
-
"graphite_remote_sync: `gt sync` for cleanup+restack, or `gt get` to import a branch/PR",
|
|
20
|
-
promptGuidelines: [
|
|
21
|
-
"Run graphite_remote_sync action=sync at the start of a session to update trunk and restack open stacks.",
|
|
22
|
-
"graphite_remote_sync action=sync with force=true or deleteAll=true is destructive; pass confirmDestructive:true.",
|
|
23
|
-
],
|
|
24
|
-
parameters: Type.Object({
|
|
25
|
-
cwd: CwdParam,
|
|
26
|
-
action: StringEnum(["sync", "get"] as const),
|
|
27
|
-
|
|
28
|
-
// sync
|
|
29
|
-
allTrunks: Type.Optional(Type.Boolean({ description: "sync: --all" })),
|
|
30
|
-
deleteAll: Type.Optional(
|
|
31
|
-
Type.Boolean({
|
|
32
|
-
description:
|
|
33
|
-
"sync|get: delete all merged/closed branches without prompting (--delete-all). Requires confirmDestructive.",
|
|
34
|
-
}),
|
|
35
|
-
),
|
|
36
|
-
force: Type.Optional(
|
|
37
|
-
Type.Boolean({
|
|
38
|
-
description:
|
|
39
|
-
"sync|get: overwrite local branches with remote (--force). Requires confirmDestructive.",
|
|
40
|
-
}),
|
|
41
|
-
),
|
|
42
|
-
restack: Type.Optional(
|
|
43
|
-
Type.Boolean({
|
|
44
|
-
description: "sync|get: restack after fetching (default true; pass false for --no-restack).",
|
|
45
|
-
}),
|
|
46
|
-
),
|
|
47
|
-
|
|
48
|
-
// get
|
|
49
|
-
target: Type.Optional(
|
|
50
|
-
Type.String({ description: "get: branch name or PR number to fetch." }),
|
|
51
|
-
),
|
|
52
|
-
downstack: Type.Optional(
|
|
53
|
-
Type.Boolean({ description: "get: --downstack (don't sync upstack)." }),
|
|
54
|
-
),
|
|
55
|
-
remoteUpstack: Type.Optional(
|
|
56
|
-
Type.Boolean({ description: "get: --remote-upstack (include remote-only upstack)." }),
|
|
57
|
-
),
|
|
58
|
-
checkout: Type.Optional(
|
|
59
|
-
Type.Boolean({ description: "get: check out target after sync (default true; false => --no-checkout)." }),
|
|
60
|
-
),
|
|
61
|
-
unfrozen: Type.Optional(
|
|
62
|
-
Type.Boolean({ description: "get: --unfrozen (new branches editable)." }),
|
|
63
|
-
),
|
|
64
|
-
|
|
65
|
-
confirmDestructive: Type.Optional(Type.Boolean()),
|
|
66
|
-
}),
|
|
67
|
-
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
68
|
-
const args: string[] = [p.action];
|
|
69
|
-
|
|
70
|
-
if (p.action === "sync") {
|
|
71
|
-
if (p.force || p.deleteAll) {
|
|
72
|
-
requireConfirm(
|
|
73
|
-
p.confirmDestructive,
|
|
74
|
-
"gt sync with --force/--delete-all (may overwrite branches)",
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
if (p.allTrunks) args.push("--all");
|
|
78
|
-
if (p.deleteAll) args.push("--delete-all");
|
|
79
|
-
if (p.force) args.push("--force");
|
|
80
|
-
if (p.restack === false) args.push("--no-restack");
|
|
81
|
-
} else {
|
|
82
|
-
if (!p.target) throw new Error("action=get requires `target` (branch name or PR number).");
|
|
83
|
-
args.push(p.target);
|
|
84
|
-
if (p.downstack) args.push("--downstack");
|
|
85
|
-
if (p.remoteUpstack) args.push("--remote-upstack");
|
|
86
|
-
if (p.force) {
|
|
87
|
-
requireConfirm(p.confirmDestructive, "gt get --force (overwrites local branches)");
|
|
88
|
-
args.push("--force");
|
|
89
|
-
}
|
|
90
|
-
if (p.deleteAll) {
|
|
91
|
-
requireConfirm(p.confirmDestructive, "gt get --delete-all");
|
|
92
|
-
args.push("--delete-all");
|
|
93
|
-
}
|
|
94
|
-
if (p.checkout === false) args.push("--no-checkout");
|
|
95
|
-
if (p.restack === false) args.push("--no-restack");
|
|
96
|
-
if (p.unfrozen) args.push("--unfrozen");
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const label = `gt ${args.join(" ")}`;
|
|
100
|
-
const r = await runGt(args, { cwd: p.cwd, signal });
|
|
101
|
-
const f = await ensureSuccess(label, r, p.cwd);
|
|
102
|
-
return {
|
|
103
|
-
content: [{ type: "text", text: renderText(label, f) }],
|
|
104
|
-
details: { action: p.action, result: f },
|
|
105
|
-
};
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
}
|
package/src/tools/repo.ts
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { runGt } from "../lib/exec";
|
|
3
|
-
import { ensureAllSuccess, ensureSuccess, renderText } from "../lib/result";
|
|
4
|
-
import { CwdParam, StringEnum, Type, type ToolReturn } from "../lib/schema";
|
|
5
|
-
|
|
6
|
-
const params = Type.Object({
|
|
7
|
-
cwd: CwdParam,
|
|
8
|
-
action: StringEnum(["status", "init", "set_trunk", "show_config"] as const),
|
|
9
|
-
trunk: Type.Optional(
|
|
10
|
-
Type.String({
|
|
11
|
-
description:
|
|
12
|
-
"Trunk branch name. Required for action=set_trunk. Optional for action=init (otherwise gt prompts).",
|
|
13
|
-
}),
|
|
14
|
-
),
|
|
15
|
-
addAdditionalTrunk: Type.Optional(
|
|
16
|
-
Type.Boolean({
|
|
17
|
-
description:
|
|
18
|
-
"If true with action=set_trunk, add an additional trunk via `gt trunk --add` instead of replacing.",
|
|
19
|
-
}),
|
|
20
|
-
),
|
|
21
|
-
reset: Type.Optional(
|
|
22
|
-
Type.Boolean({
|
|
23
|
-
description: "If true with action=init, untrack all branches (gt init --reset).",
|
|
24
|
-
}),
|
|
25
|
-
),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
export function registerRepo(pi: ExtensionAPI) {
|
|
29
|
-
pi.registerTool({
|
|
30
|
-
name: "graphite_repo",
|
|
31
|
-
label: "Graphite: repo",
|
|
32
|
-
description:
|
|
33
|
-
"Repo-level Graphite operations: status snapshot (log + trunk), init, set/add trunk, and show config.",
|
|
34
|
-
promptSnippet:
|
|
35
|
-
"graphite_repo: inspect repo state, initialize Graphite, configure trunk(s)",
|
|
36
|
-
parameters: params,
|
|
37
|
-
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
38
|
-
const cwd = p.cwd;
|
|
39
|
-
|
|
40
|
-
if (p.action === "status") {
|
|
41
|
-
const [trunk, log] = await Promise.all([
|
|
42
|
-
runGt(["trunk"], { cwd, signal }),
|
|
43
|
-
runGt(["log", "short"], { cwd, signal }),
|
|
44
|
-
]);
|
|
45
|
-
const [ft, fl] = await ensureAllSuccess(
|
|
46
|
-
[
|
|
47
|
-
{ label: "gt trunk", result: trunk },
|
|
48
|
-
{ label: "gt log short", result: log },
|
|
49
|
-
],
|
|
50
|
-
cwd,
|
|
51
|
-
);
|
|
52
|
-
return {
|
|
53
|
-
content: [
|
|
54
|
-
{
|
|
55
|
-
type: "text",
|
|
56
|
-
text: [renderText("gt trunk", ft), renderText("gt log short", fl)].join("\n\n"),
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
details: { trunk: ft, log: fl },
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (p.action === "init") {
|
|
64
|
-
const args = ["init"];
|
|
65
|
-
if (p.trunk) args.push("--trunk", p.trunk);
|
|
66
|
-
if (p.reset) args.push("--reset");
|
|
67
|
-
const r = await runGt(args, { cwd, signal });
|
|
68
|
-
const f = await ensureSuccess(`gt ${args.join(" ")}`, r, cwd);
|
|
69
|
-
return {
|
|
70
|
-
content: [{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) }],
|
|
71
|
-
details: { result: f },
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (p.action === "set_trunk") {
|
|
76
|
-
if (!p.trunk) throw new Error("action=set_trunk requires `trunk`.");
|
|
77
|
-
if (!p.addAdditionalTrunk) {
|
|
78
|
-
const args = ["init", "--trunk", p.trunk];
|
|
79
|
-
const r = await runGt(args, { cwd, signal });
|
|
80
|
-
const f = await ensureSuccess(`gt ${args.join(" ")}`, r, cwd);
|
|
81
|
-
return {
|
|
82
|
-
content: [
|
|
83
|
-
{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) },
|
|
84
|
-
],
|
|
85
|
-
details: { result: f },
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
// `gt trunk --add` is interactive (prompts for the new trunk name).
|
|
89
|
-
// With --no-interactive this will fail; surface that failure rather
|
|
90
|
-
// than silently misleading the caller.
|
|
91
|
-
const r = await runGt(["trunk", "--add"], { cwd, signal });
|
|
92
|
-
const f = await ensureSuccess("gt trunk --add", r, cwd);
|
|
93
|
-
return {
|
|
94
|
-
content: [{ type: "text", text: renderText("gt trunk --add", f) }],
|
|
95
|
-
details: { result: f },
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// show_config
|
|
100
|
-
const [trunk, trunkAll] = await Promise.all([
|
|
101
|
-
runGt(["trunk"], { cwd, signal }),
|
|
102
|
-
runGt(["trunk", "--all"], { cwd, signal }),
|
|
103
|
-
]);
|
|
104
|
-
const [ft, fta] = await ensureAllSuccess(
|
|
105
|
-
[
|
|
106
|
-
{ label: "gt trunk", result: trunk },
|
|
107
|
-
{ label: "gt trunk --all", result: trunkAll },
|
|
108
|
-
],
|
|
109
|
-
cwd,
|
|
110
|
-
);
|
|
111
|
-
return {
|
|
112
|
-
content: [
|
|
113
|
-
{
|
|
114
|
-
type: "text",
|
|
115
|
-
text: [renderText("gt trunk", ft), renderText("gt trunk --all", fta)].join("\n\n"),
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
details: { trunk: ft, trunkAll: fta },
|
|
119
|
-
};
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
}
|
package/src/tools/stack.ts
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { runGt } from "../lib/exec";
|
|
3
|
-
import { ensureSuccess, renderText } from "../lib/result";
|
|
4
|
-
import {
|
|
5
|
-
CwdParam,
|
|
6
|
-
StringEnum,
|
|
7
|
-
Type,
|
|
8
|
-
requireConfirm,
|
|
9
|
-
type ToolReturn,
|
|
10
|
-
} from "../lib/schema";
|
|
11
|
-
|
|
12
|
-
/* ------------------------------ stack_view ------------------------------ */
|
|
13
|
-
|
|
14
|
-
export function registerStackView(pi: ExtensionAPI) {
|
|
15
|
-
pi.registerTool({
|
|
16
|
-
name: "graphite_stack_view",
|
|
17
|
-
label: "Graphite: stack view",
|
|
18
|
-
description:
|
|
19
|
-
"Show the Graphite stack as `gt log` (short/long/full). Read-only.",
|
|
20
|
-
promptSnippet:
|
|
21
|
-
"graphite_stack_view: read-only `gt log` of branches + dependencies",
|
|
22
|
-
parameters: Type.Object({
|
|
23
|
-
cwd: CwdParam,
|
|
24
|
-
mode: Type.Optional(
|
|
25
|
-
StringEnum(["short", "long", "full"] as const, {
|
|
26
|
-
description: "short = `gt log short`, long = `gt log long`, full = `gt log`.",
|
|
27
|
-
}),
|
|
28
|
-
),
|
|
29
|
-
scope: Type.Optional(
|
|
30
|
-
StringEnum(["all_trunks", "current_stack", "default"] as const, {
|
|
31
|
-
description:
|
|
32
|
-
"all_trunks adds --all (only supported on mode=full/default). current_stack adds --stack. default does neither.",
|
|
33
|
-
}),
|
|
34
|
-
),
|
|
35
|
-
showUntracked: Type.Optional(Type.Boolean()),
|
|
36
|
-
reverse: Type.Optional(Type.Boolean()),
|
|
37
|
-
steps: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
38
|
-
}),
|
|
39
|
-
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
40
|
-
// Empirically, current gt rejects `--all` on `gt log short` / `gt log long`
|
|
41
|
-
// ("Unknown argument: all"). Block that combination at the tool layer
|
|
42
|
-
// instead of exposing it to the model.
|
|
43
|
-
if (p.scope === "all_trunks" && (p.mode === "short" || p.mode === "long")) {
|
|
44
|
-
throw new Error(
|
|
45
|
-
"graphite_stack_view: scope='all_trunks' is not supported with mode='short'/'long' " +
|
|
46
|
-
"(current `gt` rejects `--all` on those forms). Omit `mode` (or use mode='full') to use --all.",
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const sub =
|
|
51
|
-
p.mode === "short"
|
|
52
|
-
? ["log", "short"]
|
|
53
|
-
: p.mode === "long"
|
|
54
|
-
? ["log", "long"]
|
|
55
|
-
: ["log"];
|
|
56
|
-
const args = [...sub];
|
|
57
|
-
if (p.scope === "all_trunks") args.push("--all");
|
|
58
|
-
if (p.scope === "current_stack") args.push("--stack");
|
|
59
|
-
if (p.showUntracked) args.push("--show-untracked");
|
|
60
|
-
if (p.reverse) args.push("--reverse");
|
|
61
|
-
if (p.steps != null) args.push("--steps", String(p.steps));
|
|
62
|
-
|
|
63
|
-
const label = `gt ${args.join(" ")}`;
|
|
64
|
-
const r = await runGt(args, { cwd: p.cwd, signal });
|
|
65
|
-
const f = await ensureSuccess(label, r, p.cwd);
|
|
66
|
-
return {
|
|
67
|
-
content: [{ type: "text", text: renderText(label, f) }],
|
|
68
|
-
details: { result: f },
|
|
69
|
-
};
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/* ----------------------------- stack_restack ----------------------------- */
|
|
75
|
-
|
|
76
|
-
export function registerStackRestack(pi: ExtensionAPI) {
|
|
77
|
-
pi.registerTool({
|
|
78
|
-
name: "graphite_stack_restack",
|
|
79
|
-
label: "Graphite: restack",
|
|
80
|
-
description:
|
|
81
|
-
"Restack the current stack (or a chosen branch / scope) so each branch contains its parent's history. Local mutation only.",
|
|
82
|
-
promptSnippet:
|
|
83
|
-
"graphite_stack_restack: rebase stack so each branch contains parent history",
|
|
84
|
-
promptGuidelines: [
|
|
85
|
-
"Use graphite_stack_restack after editing a downstack branch to bring descendants up to date.",
|
|
86
|
-
],
|
|
87
|
-
parameters: Type.Object({
|
|
88
|
-
cwd: CwdParam,
|
|
89
|
-
scope: Type.Optional(
|
|
90
|
-
StringEnum(["current_stack", "downstack", "upstack", "only"] as const, {
|
|
91
|
-
description:
|
|
92
|
-
"Default current_stack (no scope flag). downstack=--downstack, upstack=--upstack, only=--only.",
|
|
93
|
-
}),
|
|
94
|
-
),
|
|
95
|
-
branch: Type.Optional(
|
|
96
|
-
Type.String({ description: "Branch to restack from (default: current branch)." }),
|
|
97
|
-
),
|
|
98
|
-
}),
|
|
99
|
-
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
100
|
-
const args = ["restack"];
|
|
101
|
-
if (p.branch) args.push("--branch", p.branch);
|
|
102
|
-
if (p.scope === "downstack") args.push("--downstack");
|
|
103
|
-
else if (p.scope === "upstack") args.push("--upstack");
|
|
104
|
-
else if (p.scope === "only") args.push("--only");
|
|
105
|
-
|
|
106
|
-
const label = `gt ${args.join(" ")}`;
|
|
107
|
-
const r = await runGt(args, { cwd: p.cwd, signal });
|
|
108
|
-
const f = await ensureSuccess(label, r, p.cwd);
|
|
109
|
-
return {
|
|
110
|
-
content: [{ type: "text", text: renderText(label, f) }],
|
|
111
|
-
details: { result: f },
|
|
112
|
-
};
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/* --------------------------- stack_reorganize --------------------------- */
|
|
118
|
-
|
|
119
|
-
export function registerStackReorganize(pi: ExtensionAPI) {
|
|
120
|
-
pi.registerTool({
|
|
121
|
-
name: "graphite_stack_reorganize",
|
|
122
|
-
label: "Graphite: stack reorganize",
|
|
123
|
-
description:
|
|
124
|
-
"Reorganize the stack: move a branch onto a new parent, fold a branch into its parent, or split by file pathspec. `gt reorder` is intentionally not exposed (editor-only).",
|
|
125
|
-
promptSnippet:
|
|
126
|
-
"graphite_stack_reorganize: move / fold / split-by-file branches",
|
|
127
|
-
parameters: Type.Object({
|
|
128
|
-
cwd: CwdParam,
|
|
129
|
-
action: StringEnum(["move_branch", "fold", "split_by_file"] as const),
|
|
130
|
-
onto: Type.Optional(Type.String({ description: "Target parent branch (action=move_branch)." })),
|
|
131
|
-
source: Type.Optional(
|
|
132
|
-
Type.String({ description: "Source branch to move (default current, action=move_branch)." }),
|
|
133
|
-
),
|
|
134
|
-
onlyMove: Type.Optional(
|
|
135
|
-
Type.Boolean({ description: "action=move_branch: leave descendants behind (--only)." }),
|
|
136
|
-
),
|
|
137
|
-
foldKeep: Type.Optional(
|
|
138
|
-
Type.Boolean({ description: "action=fold: keep current branch name (--keep)." }),
|
|
139
|
-
),
|
|
140
|
-
foldStack: Type.Optional(
|
|
141
|
-
Type.Boolean({ description: "action=fold: fold the entire stack into one branch (--stack)." }),
|
|
142
|
-
),
|
|
143
|
-
foldClose: Type.Optional(
|
|
144
|
-
Type.Boolean({ description: "action=fold: close associated PRs on GitHub (--close). Requires confirmRemote." }),
|
|
145
|
-
),
|
|
146
|
-
filePatterns: Type.Optional(
|
|
147
|
-
Type.Array(Type.String(), {
|
|
148
|
-
description: "action=split_by_file: one or more pathspecs (-f). Repeat-flag style.",
|
|
149
|
-
}),
|
|
150
|
-
),
|
|
151
|
-
confirmRemote: Type.Optional(Type.Boolean()),
|
|
152
|
-
}),
|
|
153
|
-
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
154
|
-
let args: string[];
|
|
155
|
-
if (p.action === "move_branch") {
|
|
156
|
-
if (!p.onto) throw new Error("action=move_branch requires `onto`.");
|
|
157
|
-
args = ["move", "--onto", p.onto];
|
|
158
|
-
if (p.source) args.push("--source", p.source);
|
|
159
|
-
if (p.onlyMove) args.push("--only");
|
|
160
|
-
} else if (p.action === "fold") {
|
|
161
|
-
if (p.foldClose) requireConfirm(p.confirmRemote, "fold --close");
|
|
162
|
-
args = ["fold"];
|
|
163
|
-
if (p.foldKeep) args.push("--keep");
|
|
164
|
-
if (p.foldStack) args.push("--stack");
|
|
165
|
-
if (p.foldClose) args.push("--close");
|
|
166
|
-
} else {
|
|
167
|
-
if (!p.filePatterns || p.filePatterns.length === 0) {
|
|
168
|
-
throw new Error(
|
|
169
|
-
"action=split_by_file requires `filePatterns` (one or more pathspecs).",
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
args = ["split", "--by-file"];
|
|
173
|
-
for (const pat of p.filePatterns) args.push("-f", pat);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const label = `gt ${args.join(" ")}`;
|
|
177
|
-
const r = await runGt(args, { cwd: p.cwd, signal });
|
|
178
|
-
const f = await ensureSuccess(label, r, p.cwd);
|
|
179
|
-
return {
|
|
180
|
-
content: [{ type: "text", text: renderText(label, f) }],
|
|
181
|
-
details: { action: p.action, result: f },
|
|
182
|
-
};
|
|
183
|
-
},
|
|
184
|
-
});
|
|
185
|
-
}
|