pi-graphite 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,121 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { runGt } from "../lib/exec";
3
+ import { formatResult, 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 = formatResult(trunk);
46
+ const fl = formatResult(log);
47
+ return {
48
+ content: [
49
+ {
50
+ type: "text",
51
+ text: [renderText("gt trunk", ft), renderText("gt log short", fl)].join("\n\n"),
52
+ },
53
+ ],
54
+ details: { trunk: ft, log: fl },
55
+ };
56
+ }
57
+
58
+ if (p.action === "init") {
59
+ const args = ["init"];
60
+ if (p.trunk) args.push("--trunk", p.trunk);
61
+ if (p.reset) args.push("--reset");
62
+ const r = await runGt(args, { cwd, signal });
63
+ const f = formatResult(r);
64
+ return {
65
+ content: [{ type: "text", text: renderText("gt init", f) }],
66
+ details: { result: f },
67
+ };
68
+ }
69
+
70
+ if (p.action === "set_trunk") {
71
+ if (!p.trunk) throw new Error("action=set_trunk requires `trunk`.");
72
+ const args = ["trunk"];
73
+ if (p.addAdditionalTrunk) args.push("--add");
74
+ // gt trunk --add prompts for trunk name; not all gt versions accept positional.
75
+ // Fall back to gt init --trunk for non-additional sets.
76
+ if (!p.addAdditionalTrunk) {
77
+ const r = await runGt(["init", "--trunk", p.trunk], { cwd, signal });
78
+ const f = formatResult(r);
79
+ return {
80
+ content: [
81
+ { type: "text", text: renderText(`gt init --trunk ${p.trunk}`, f) },
82
+ ],
83
+ details: { result: f },
84
+ };
85
+ }
86
+ // Additional trunk path: run interactively-allowed since --add prompts.
87
+ const r = await runGt(args, {
88
+ cwd,
89
+ signal,
90
+ allowInteractive: false,
91
+ });
92
+ const f = formatResult(r);
93
+ return {
94
+ content: [{ type: "text", text: renderText("gt trunk --add", f) }],
95
+ details: { result: f, note: "Additional trunk addition may require interactive input." },
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
+ return {
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: [
109
+ renderText("gt trunk", formatResult(trunk)),
110
+ renderText("gt trunk --all", formatResult(trunkAll)),
111
+ ].join("\n\n"),
112
+ },
113
+ ],
114
+ details: {
115
+ trunk: formatResult(trunk),
116
+ trunkAll: formatResult(trunkAll),
117
+ },
118
+ };
119
+ },
120
+ });
121
+ }
@@ -0,0 +1,176 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { runGt } from "../lib/exec";
3
+ import { formatResult, 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, 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
+ const sub = p.mode === "short" ? ["log", "short"] : p.mode === "long" ? ["log", "long"] : ["log"];
41
+ const args = [...sub];
42
+ if (p.scope === "all_trunks") args.push("--all");
43
+ if (p.scope === "current_stack") args.push("--stack");
44
+ if (p.showUntracked) args.push("--show-untracked");
45
+ if (p.reverse) args.push("--reverse");
46
+ if (p.steps != null) args.push("--steps", String(p.steps));
47
+ const r = await runGt(args, { cwd: p.cwd, signal });
48
+ const f = formatResult(r);
49
+ return {
50
+ content: [{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) }],
51
+ details: { result: f },
52
+ };
53
+ },
54
+ });
55
+ }
56
+
57
+ /* ----------------------------- stack_restack ----------------------------- */
58
+
59
+ export function registerStackRestack(pi: ExtensionAPI) {
60
+ pi.registerTool({
61
+ name: "graphite_stack_restack",
62
+ label: "Graphite: restack",
63
+ description:
64
+ "Restack the current stack (or a chosen branch / scope) so each branch contains its parent's history. Local mutation only.",
65
+ promptSnippet:
66
+ "graphite_stack_restack: rebase stack so each branch contains parent history",
67
+ promptGuidelines: [
68
+ "Use graphite_stack_restack after editing a downstack branch to bring descendants up to date.",
69
+ ],
70
+ parameters: Type.Object({
71
+ cwd: CwdParam,
72
+ scope: Type.Optional(
73
+ StringEnum(["current_stack", "downstack", "upstack", "only"] as const, {
74
+ description:
75
+ "Default current_stack (no scope flag). downstack=--downstack, upstack=--upstack, only=--only.",
76
+ }),
77
+ ),
78
+ branch: Type.Optional(
79
+ Type.String({ description: "Branch to restack from (default: current branch)." }),
80
+ ),
81
+ }),
82
+ async execute(_id, p, signal): Promise<ToolReturn> {
83
+ const args = ["restack"];
84
+ if (p.branch) args.push("--branch", p.branch);
85
+ if (p.scope === "downstack") args.push("--downstack");
86
+ else if (p.scope === "upstack") args.push("--upstack");
87
+ else if (p.scope === "only") args.push("--only");
88
+ const r = await runGt(args, { cwd: p.cwd, signal });
89
+ const f = formatResult(r);
90
+ return {
91
+ content: [{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) }],
92
+ details: { result: f },
93
+ };
94
+ },
95
+ });
96
+ }
97
+
98
+ /* --------------------------- stack_reorganize --------------------------- */
99
+
100
+ export function registerStackReorganize(pi: ExtensionAPI) {
101
+ pi.registerTool({
102
+ name: "graphite_stack_reorganize",
103
+ label: "Graphite: stack reorganize",
104
+ description:
105
+ "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).",
106
+ promptSnippet:
107
+ "graphite_stack_reorganize: move / fold / split-by-file branches",
108
+ parameters: Type.Object({
109
+ cwd: CwdParam,
110
+ action: StringEnum(["move_branch", "fold", "split_by_file"] as const),
111
+ onto: Type.Optional(Type.String({ description: "Target parent branch (action=move_branch)." })),
112
+ source: Type.Optional(
113
+ Type.String({ description: "Source branch to move (default current, action=move_branch)." }),
114
+ ),
115
+ onlyMove: Type.Optional(
116
+ Type.Boolean({ description: "action=move_branch: leave descendants behind (--only)." }),
117
+ ),
118
+ foldKeep: Type.Optional(
119
+ Type.Boolean({ description: "action=fold: keep current branch name (--keep)." }),
120
+ ),
121
+ foldStack: Type.Optional(
122
+ Type.Boolean({ description: "action=fold: fold the entire stack into one branch (--stack)." }),
123
+ ),
124
+ foldClose: Type.Optional(
125
+ Type.Boolean({ description: "action=fold: close associated PRs on GitHub (--close). Requires confirmRemote." }),
126
+ ),
127
+ filePatterns: Type.Optional(
128
+ Type.Array(Type.String(), {
129
+ description: "action=split_by_file: one or more pathspecs (-f). Repeat-flag style.",
130
+ }),
131
+ ),
132
+ confirmRemote: Type.Optional(Type.Boolean()),
133
+ }),
134
+ async execute(_id, p, signal): Promise<ToolReturn> {
135
+ if (p.action === "move_branch") {
136
+ if (!p.onto) throw new Error("action=move_branch requires `onto`.");
137
+ const args = ["move", "--onto", p.onto];
138
+ if (p.source) args.push("--source", p.source);
139
+ if (p.onlyMove) args.push("--only");
140
+ const r = await runGt(args, { cwd: p.cwd, signal });
141
+ const f = formatResult(r);
142
+ return {
143
+ content: [{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) }],
144
+ details: { result: f },
145
+ };
146
+ }
147
+
148
+ if (p.action === "fold") {
149
+ if (p.foldClose) requireConfirm(p.confirmRemote, "fold --close");
150
+ const args = ["fold"];
151
+ if (p.foldKeep) args.push("--keep");
152
+ if (p.foldStack) args.push("--stack");
153
+ if (p.foldClose) args.push("--close");
154
+ const r = await runGt(args, { cwd: p.cwd, signal });
155
+ const f = formatResult(r);
156
+ return {
157
+ content: [{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) }],
158
+ details: { result: f },
159
+ };
160
+ }
161
+
162
+ // split_by_file
163
+ if (!p.filePatterns || p.filePatterns.length === 0) {
164
+ throw new Error("action=split_by_file requires `filePatterns` (one or more pathspecs).");
165
+ }
166
+ const args = ["split", "--by-file"];
167
+ for (const pat of p.filePatterns) args.push("-f", pat);
168
+ const r = await runGt(args, { cwd: p.cwd, signal });
169
+ const f = formatResult(r);
170
+ return {
171
+ content: [{ type: "text", text: renderText(`gt ${args.join(" ")}`, f) }],
172
+ details: { result: f },
173
+ };
174
+ },
175
+ });
176
+ }
package/index.js DELETED
@@ -1,3 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {};