pi-graphite 0.5.0 → 0.6.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 +5 -2
- package/package.json +2 -2
- package/skills/graphite/SKILL.md +32 -4
- package/src/index.ts +7 -2
- package/src/tools/move.ts +155 -0
package/README.md
CHANGED
|
@@ -9,8 +9,10 @@ graphite_status → (graphite_setup if needed) → graphite_sync → graphite_na
|
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
The extension wraps `gt` only. It deliberately does **not** call `gh`, edit
|
|
12
|
-
PR titles/bodies, fetch review comments, or perform stack surgery
|
|
13
|
-
|
|
12
|
+
PR titles/bodies, fetch review comments, or perform interactive stack surgery
|
|
13
|
+
(split / fold / squash / reorder). Reparenting a tracked branch IS supported
|
|
14
|
+
via `graphite_move` (`gt move --source --onto`, non-interactive). For the
|
|
15
|
+
remaining surgery flows, run the underlying `gt`
|
|
14
16
|
or `gh` command yourself in your own terminal; the agent should not invoke
|
|
15
17
|
them from bash, as their defaults open interactive prompts, hunk pickers,
|
|
16
18
|
or editors that will hang non-interactive sessions.
|
|
@@ -48,6 +50,7 @@ agent loads it on demand.
|
|
|
48
50
|
| `graphite_sync` | Start-of-day / after-merge cleanup + restack | `gt sync` |
|
|
49
51
|
| `graphite_get` | Pull a branch / stack from the remote | `gt get <branch>` |
|
|
50
52
|
| `graphite_navigate` | Move around the stack | `gt checkout`, `gt up`/`down`/`top`/`bottom` |
|
|
53
|
+
| `graphite_move` | Reparent a tracked branch + restack descendants (dry-run by default) | `gt move --source --onto` |
|
|
51
54
|
| `graphite_change` | Create / amend a stacked branch | `gt create -am`, `gt modify -am`, `gt modify --into`, `gt absorb` |
|
|
52
55
|
| `graphite_submit` | Push the entire stack and open/update PRs (dry-run by default) | `gt submit --stack --no-edit --no-ai` |
|
|
53
56
|
| `graphite_recover` | Continue / abort / undo / restack | `gt continue`, `gt abort`, `gt undo`, `gt restack` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-graphite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Opinionated pi tools + skill for stacked PR workflows with the Graphite (gt) CLI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"repository": {
|
|
14
14
|
"type": "git",
|
|
15
|
-
"url": "git+
|
|
15
|
+
"url": "git+https://github.com/tianrendong/pi-graphite.git"
|
|
16
16
|
},
|
|
17
17
|
"bugs": {
|
|
18
18
|
"url": "https://github.com/tianrendong/pi-graphite/issues"
|
package/skills/graphite/SKILL.md
CHANGED
|
@@ -25,10 +25,14 @@ Do not use it for:
|
|
|
25
25
|
- editing PR titles / bodies / labels / reviewers metadata — prefer a
|
|
26
26
|
dedicated `gh` tool/extension; see the `gh` rule below
|
|
27
27
|
- reading PR review comments or CI status — same
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
- reparenting a tracked branch onto a different parent — use
|
|
29
|
+
`graphite_move` (it runs `gt move --source --onto` non-interactively and
|
|
30
|
+
rebases descendants). Do NOT use `track_branch --force` for this; that only
|
|
31
|
+
rewrites tracking metadata and leaves commits unrebased.
|
|
32
|
+
- rewriting history beyond create/amend/move (split / fold / squash /
|
|
33
|
+
reorder). The extension does not expose those, and they prompt
|
|
34
|
+
interactively (base selectors, hunk pickers, editors) and will hang. Ask
|
|
35
|
+
the user to run them in their own terminal.
|
|
32
36
|
|
|
33
37
|
## Tools
|
|
34
38
|
|
|
@@ -41,6 +45,7 @@ The extension registers these tools. Prefer them over `gt`/`git`/`gh` in bash.
|
|
|
41
45
|
| `graphite_sync` | `gt sync` — pull trunk, drop merged branches, restack |
|
|
42
46
|
| `graphite_get` | `gt get <branch>` — pull a branch / stack from the remote |
|
|
43
47
|
| `graphite_navigate` | `gt checkout` / `up` / `down` / `top` / `bottom` / trunk |
|
|
48
|
+
| `graphite_move` | `gt move --source --onto` — reparent a tracked branch + restack descendants (dry-run by default) |
|
|
44
49
|
| `graphite_change` | `gt create` / `gt modify` / `gt modify --into` / `gt absorb` |
|
|
45
50
|
| `graphite_submit` | `gt submit --stack --no-edit` (dry-run by default) |
|
|
46
51
|
| `graphite_recover` | `gt continue` / `gt abort` / `gt undo` / `gt restack` |
|
|
@@ -212,6 +217,24 @@ graphite_status({ cwd })
|
|
|
212
217
|
Use `graphite_sync` instead when trunk itself may have advanced on the remote.
|
|
213
218
|
If restack halts on a conflict, follow the conflict recipe.
|
|
214
219
|
|
|
220
|
+
### Reparent a tracked branch onto a new parent
|
|
221
|
+
|
|
222
|
+
When an existing tracked branch is stacked on the wrong parent and you need a
|
|
223
|
+
real rebase (not just a metadata fix):
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
graphite_status({ cwd }) # confirm shapes
|
|
227
|
+
graphite_move({ cwd, branch: "<branch>", parent: "<new-parent>" }) # dry-run plan
|
|
228
|
+
# review the plan, then apply:
|
|
229
|
+
graphite_move({ cwd, branch: "<branch>", parent: "<new-parent>", apply: true, confirmDestructive: true })
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
The move rebases `<branch>` and all of its descendants onto `<new-parent>`.
|
|
233
|
+
If it halts on a conflict, follow the conflict recipe (resolve →
|
|
234
|
+
`graphite_recover action="continue"`). To chain several reparents (e.g.
|
|
235
|
+
rebuild a stack A→B→C), move the lowest branch first, then each next branch
|
|
236
|
+
onto its new parent, running `graphite_status` between steps.
|
|
237
|
+
|
|
215
238
|
### Pull a branch / stack from the remote
|
|
216
239
|
|
|
217
240
|
To check out a teammate's branch, or re-pull a branch that changed remotely:
|
|
@@ -261,6 +284,11 @@ graphite_recover({ cwd, action: "undo" })
|
|
|
261
284
|
- **Restack vs sync.** Use `graphite_recover action="restack"` to rebase the
|
|
262
285
|
stack onto each parent's latest commit when no remote pull is needed. Use
|
|
263
286
|
`graphite_sync` when trunk may have advanced remotely (it pulls + restacks).
|
|
287
|
+
- **Reparent with `graphite_move`, not `track_branch --force`.**
|
|
288
|
+
`graphite_move` runs `gt move --source --onto`, which rebases commits and
|
|
289
|
+
restacks descendants. `track_branch --force` only rewrites tracking
|
|
290
|
+
metadata and leaves the stack in an inconsistent shape. Always dry-run
|
|
291
|
+
first; `apply:true` requires `confirmDestructive:true`.
|
|
264
292
|
- **Pull remote branches with `graphite_get`.** `graphite_sync` only touches
|
|
265
293
|
trunk + already-tracked local branches; use `graphite_get` to download a
|
|
266
294
|
branch/stack from the remote.
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { registerSetup } from "./tools/setup";
|
|
|
5
5
|
import { registerSync } from "./tools/sync";
|
|
6
6
|
import { registerGet } from "./tools/get";
|
|
7
7
|
import { registerNavigate } from "./tools/navigate";
|
|
8
|
+
import { registerMove } from "./tools/move";
|
|
8
9
|
import { registerChange } from "./tools/change";
|
|
9
10
|
import { registerSubmit } from "./tools/submit";
|
|
10
11
|
import { registerRecover } from "./tools/recover";
|
|
@@ -19,6 +20,7 @@ import { registerRecover } from "./tools/recover";
|
|
|
19
20
|
* graphite_sync — start-of-day / after-merge cleanup + restack
|
|
20
21
|
* graphite_get — pull a branch / stack from the remote
|
|
21
22
|
* graphite_navigate — move to the branch / PR you want to mutate
|
|
23
|
+
* graphite_move — reparent an existing tracked branch (stack surgery)
|
|
22
24
|
* graphite_change — create or amend a stacked branch
|
|
23
25
|
* graphite_submit — push the whole stack and open/update PRs
|
|
24
26
|
* graphite_recover — continue / abort / undo / restack
|
|
@@ -40,8 +42,10 @@ import { registerRecover } from "./tools/recover";
|
|
|
40
42
|
* `apply:true` AND `confirmRemote:true`.
|
|
41
43
|
* - graphite_sync with force / deleteAll requires `confirmDestructive:true`.
|
|
42
44
|
* - This extension wraps `gt` only. It deliberately does not call `gh`,
|
|
43
|
-
* touch PR titles/bodies, run reviews, or do stack surgery
|
|
44
|
-
* (split/fold/
|
|
45
|
+
* touch PR titles/bodies, run reviews, or do interactive stack surgery
|
|
46
|
+
* (split/fold/squash/reorder). Reparenting via `gt move` IS exposed
|
|
47
|
+
* (graphite_move) because it is non-interactive with explicit
|
|
48
|
+
* --source/--onto. Use the gt CLI or another tool for the rest.
|
|
45
49
|
*/
|
|
46
50
|
export default function (pi: ExtensionAPI) {
|
|
47
51
|
registerStatus(pi);
|
|
@@ -49,6 +53,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
49
53
|
registerSync(pi);
|
|
50
54
|
registerGet(pi);
|
|
51
55
|
registerNavigate(pi);
|
|
56
|
+
registerMove(pi);
|
|
52
57
|
registerChange(pi);
|
|
53
58
|
registerSubmit(pi);
|
|
54
59
|
registerRecover(pi);
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { runGt } from "../lib/exec";
|
|
3
|
+
import { assertSafeRef, flagEq, shellJoin } from "../lib/argv";
|
|
4
|
+
import {
|
|
5
|
+
ensureAllSuccess,
|
|
6
|
+
ensureSuccess,
|
|
7
|
+
renderText,
|
|
8
|
+
} from "../lib/result";
|
|
9
|
+
import {
|
|
10
|
+
CwdParam,
|
|
11
|
+
Type,
|
|
12
|
+
requireConfirm,
|
|
13
|
+
type ToolReturn,
|
|
14
|
+
} from "../lib/schema";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* graphite_move — `gt move --source <branch> --onto <parent>`.
|
|
18
|
+
*
|
|
19
|
+
* Reparent an existing, already-tracked branch onto a new parent and restack
|
|
20
|
+
* the moved branch plus all of its descendants. This is the safe stack-surgery
|
|
21
|
+
* primitive that was previously missing: track_branch --force only rewrites
|
|
22
|
+
* tracking metadata, it does NOT rebase commits, so it leaves the stack in an
|
|
23
|
+
* inconsistent shape. graphite_move performs a real rebase.
|
|
24
|
+
*
|
|
25
|
+
* Wrapper guarantees:
|
|
26
|
+
* 1. Dry-run plan first (apply defaults to false; no mutation).
|
|
27
|
+
* 2. Both `branch` and `parent` are explicit and required.
|
|
28
|
+
* 3. Ambiguous / nonsensical moves are rejected up front (empty refs,
|
|
29
|
+
* flag-injection, branch == parent). gt rejects cycles itself.
|
|
30
|
+
* 4. On apply, gt rebases the moved branch and all descendants.
|
|
31
|
+
* 5. Conflicts surface as a failure with a conflictHalted hint, routing the
|
|
32
|
+
* agent to graphite_recover continue/abort.
|
|
33
|
+
* 6. apply:true requires confirmDestructive:true.
|
|
34
|
+
* 7. On a successful apply, the resulting stack is shown (gt log --stack +
|
|
35
|
+
* gt info) so the new shape is visible without a second call.
|
|
36
|
+
*/
|
|
37
|
+
export function registerMove(pi: ExtensionAPI) {
|
|
38
|
+
pi.registerTool({
|
|
39
|
+
name: "graphite_move",
|
|
40
|
+
label: "Graphite: move (reparent)",
|
|
41
|
+
description:
|
|
42
|
+
"Reparent an already-tracked branch onto a new parent and rebase it plus all descendants via `gt move --source <branch> --onto <parent>`. Defaults to a dry-run plan; pass apply:true with confirmDestructive:true to actually rebase. Unlike track_branch --force, this rewrites commits, not just tracking metadata.",
|
|
43
|
+
promptSnippet:
|
|
44
|
+
"graphite_move: reparent a tracked branch onto a new parent (`gt move --source --onto`)",
|
|
45
|
+
promptGuidelines: [
|
|
46
|
+
"Use graphite_move to reparent an existing tracked branch onto a different parent. It rebases the moved branch and all its descendants. Do NOT use graphite_setup track_branch --force for this — that only rewrites tracking metadata and leaves commits unrebased.",
|
|
47
|
+
"Always call graphite_move with apply:false (default) first to review the dry-run plan, then call again with apply:true and confirmDestructive:true to actually rebase.",
|
|
48
|
+
"Pass explicit branch and explicit parent. The wrapper never picks them interactively.",
|
|
49
|
+
"If the move halts on a conflict, follow the hint and use graphite_recover action=continue (or abort).",
|
|
50
|
+
],
|
|
51
|
+
parameters: Type.Object({
|
|
52
|
+
cwd: CwdParam,
|
|
53
|
+
branch: Type.String({
|
|
54
|
+
description: "The already-tracked branch to reparent (gt move --source).",
|
|
55
|
+
}),
|
|
56
|
+
parent: Type.String({
|
|
57
|
+
description: "The new parent branch to move `branch` onto (gt move --onto).",
|
|
58
|
+
}),
|
|
59
|
+
apply: Type.Optional(
|
|
60
|
+
Type.Boolean({
|
|
61
|
+
description:
|
|
62
|
+
"false => dry-run plan only, no mutation (default). true => actually rebase (requires confirmDestructive).",
|
|
63
|
+
}),
|
|
64
|
+
),
|
|
65
|
+
confirmDestructive: Type.Optional(Type.Boolean()),
|
|
66
|
+
}),
|
|
67
|
+
async execute(_id, p, signal): Promise<ToolReturn> {
|
|
68
|
+
const branch = assertSafeRef(p.branch, "branch");
|
|
69
|
+
const parent = assertSafeRef(p.parent, "parent");
|
|
70
|
+
if (branch === parent) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`graphite_move: branch and parent are the same (${JSON.stringify(branch)}). ` +
|
|
73
|
+
`A branch cannot be its own parent.`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const apply = p.apply === true;
|
|
78
|
+
|
|
79
|
+
// --- dry-run: show current stack + the planned operation, no mutation.
|
|
80
|
+
if (!apply) {
|
|
81
|
+
const log = await runGt(["log", "--stack"], { cwd: p.cwd, signal });
|
|
82
|
+
const [fl] = await ensureAllSuccess(
|
|
83
|
+
[{ label: "gt log --stack", result: log, requireStdout: true }],
|
|
84
|
+
p.cwd,
|
|
85
|
+
);
|
|
86
|
+
const planned = `gt ${shellJoin([
|
|
87
|
+
"move",
|
|
88
|
+
flagEq("--source", branch),
|
|
89
|
+
flagEq("--onto", parent),
|
|
90
|
+
])}`;
|
|
91
|
+
const plan = [
|
|
92
|
+
`[graphite_move] dry-run (no changes made)`,
|
|
93
|
+
``,
|
|
94
|
+
`Planned: reparent ${branch} onto ${parent}.`,
|
|
95
|
+
`This will rebase ${branch} and ALL of its descendants onto ${parent}.`,
|
|
96
|
+
`Command that would run: ${planned}`,
|
|
97
|
+
``,
|
|
98
|
+
`To apply: call graphite_move again with apply:true and confirmDestructive:true.`,
|
|
99
|
+
`If it halts on a conflict, resolve files then graphite_recover action="continue".`,
|
|
100
|
+
``,
|
|
101
|
+
`--- current stack ---`,
|
|
102
|
+
renderText("gt log --stack", fl),
|
|
103
|
+
].join("\n");
|
|
104
|
+
return {
|
|
105
|
+
content: [{ type: "text", text: plan }],
|
|
106
|
+
details: { apply: false, branch, parent, log: fl },
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- apply: real rebase. Destructive, so require confirmation.
|
|
111
|
+
requireConfirm(
|
|
112
|
+
p.confirmDestructive,
|
|
113
|
+
`gt move --source ${branch} --onto ${parent} (rebases the branch and its descendants)`,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const args = [
|
|
117
|
+
"move",
|
|
118
|
+
flagEq("--source", branch),
|
|
119
|
+
flagEq("--onto", parent),
|
|
120
|
+
];
|
|
121
|
+
const label = `gt ${shellJoin(args)}`;
|
|
122
|
+
const r = await runGt(args, { cwd: p.cwd, signal });
|
|
123
|
+
const f = await ensureSuccess(label, r, p.cwd, { mutating: true });
|
|
124
|
+
|
|
125
|
+
// Requirement 7: show the resulting stack after a successful move.
|
|
126
|
+
const [log, info] = await Promise.all([
|
|
127
|
+
runGt(["log", "--stack"], { cwd: p.cwd, signal }),
|
|
128
|
+
runGt(["info"], { cwd: p.cwd, signal }),
|
|
129
|
+
]);
|
|
130
|
+
const [fl, fi] = await ensureAllSuccess(
|
|
131
|
+
[
|
|
132
|
+
{ label: "gt log --stack", result: log, requireStdout: true },
|
|
133
|
+
{ label: "gt info", result: info, requireStdout: true },
|
|
134
|
+
],
|
|
135
|
+
p.cwd,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
content: [
|
|
140
|
+
{
|
|
141
|
+
type: "text",
|
|
142
|
+
text: [
|
|
143
|
+
renderText(label, f),
|
|
144
|
+
``,
|
|
145
|
+
`--- stack after move ---`,
|
|
146
|
+
renderText("gt log --stack", fl),
|
|
147
|
+
renderText("gt info", fi),
|
|
148
|
+
].join("\n"),
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
details: { apply: true, branch, parent, result: f, log: fl, info: fi },
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
}
|