@pmelab/gtd 31.0.0 → 32.0.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/.claude-plugin/plugin.json +1 -1
- package/README.md +23 -22
- package/claude/hooks/ship.ts +7 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -339,28 +339,29 @@ asks, and moves the process on only when you say so; a review you leave
|
|
|
339
339
|
untouched signs off. While a step runs you can keep asking; requests for changes
|
|
340
340
|
wait for the next rest. When the process finishes, Claude offers to squash it
|
|
341
341
|
and open its pull request(s) (or run `/gtd ship`; `GTD_SHIP_MODE` picks one
|
|
342
|
-
|
|
343
|
-
[driver doc](docs/driver.md)); a process
|
|
344
|
-
`Closes #N` on its top pull request; a
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
a slice
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
342
|
+
commit per slice under one pull request (the default) or, set to `stack`, one
|
|
343
|
+
stacked pull request per slice, see the [driver doc](docs/driver.md)); a process
|
|
344
|
+
started for an issue ships with `Closes #N` on its top pull request; a closed
|
|
345
|
+
pull request whose branch was deleted from origin is left behind and a new one
|
|
346
|
+
opens; on a branch an earlier ship in this clone stacked (unless
|
|
347
|
+
`GTD_SHIP_MODE=commits`), a later process, such as review fixes, lands one
|
|
348
|
+
commit per slice on the top pull request and keeps the stack, and re-running
|
|
349
|
+
`/gtd ship` after a lower stacked pull request merged restacks the rest (it
|
|
350
|
+
refuses, with nothing changed, when that pull request merged anywhere but the
|
|
351
|
+
default branch (unless into a lower slice branch before that one merged into
|
|
352
|
+
it), a slice branch is checked out in another worktree, an unrelated local
|
|
353
|
+
branch sits inside the rebased commits, or a slice's change is already upstream;
|
|
354
|
+
a failed rebase is rolled back; to tear a stack down from its worktree, delete
|
|
355
|
+
the top branch with `git push origin --delete`, since
|
|
356
|
+
`gh pr close --delete-branch` cannot). A process can change hands at any gate:
|
|
357
|
+
ask Claude to hand it off (or run `/gtd throw @dev`) and it opens a draft pull
|
|
358
|
+
request assigned to them, and `/gtd catch <pr>` picks it up exactly where it
|
|
359
|
+
waits, so whoever wrote the requirements can hand the build to someone else.
|
|
360
|
+
Ship, hand-off and throw answer at once and run in the background, with progress
|
|
361
|
+
on the status line; they report by waking Claude, a toast, a transcript line and
|
|
362
|
+
a push notification, and a failed push or `gh` call reports as a failure. While
|
|
363
|
+
a ship or hand-off runs or is queued, `/gtd catch`, the doors,
|
|
364
|
+
`/gtd <requirements>` and bare `/gtd` wait for its report. See
|
|
364
365
|
[Inside Claude Code](https://github.com/pmelab/gtd/blob/main/docs/driver.md#inside-claude-code-the-gtd-mod).
|
|
365
366
|
|
|
366
367
|
### Then let an agent build your own
|
package/claude/hooks/ship.ts
CHANGED
|
@@ -49,7 +49,7 @@ export function costTrailers(log: string) {
|
|
|
49
49
|
export type ShipMode = "stack" | "commits"
|
|
50
50
|
|
|
51
51
|
export function shipMode(raw: string | undefined): ShipMode | { refusal: string } {
|
|
52
|
-
if (!raw) return "
|
|
52
|
+
if (!raw) return "commits"
|
|
53
53
|
if (raw === "stack" || raw === "commits") return raw
|
|
54
54
|
return { refusal: `GTD_SHIP_MODE must be 'stack' or 'commits', not '${raw}'.` }
|
|
55
55
|
}
|
|
@@ -209,8 +209,8 @@ async function preflight(git: Git, ok: Ok) {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
export async function ship(io: ShipIo, isDry: boolean, rawMode?: string): Promise<Shipped> {
|
|
212
|
-
const
|
|
213
|
-
if (typeof
|
|
212
|
+
const valid = shipMode(rawMode)
|
|
213
|
+
if (typeof valid === "object") return fail(valid.refusal)
|
|
214
214
|
const git = async (...args: string[]) => (await io.run(["git", ...args])).out.trim()
|
|
215
215
|
const ok = async (...args: string[]) => (await io.run(["git", ...args])).code === 0
|
|
216
216
|
|
|
@@ -223,6 +223,10 @@ export async function ship(io: ShipIo, isDry: boolean, rawMode?: string): Promis
|
|
|
223
223
|
const live = names(await git("config", "--local", "--get", STACK_KEY(branch)))
|
|
224
224
|
const retired = names(await git("config", "--local", "--get", RETIRED_KEY(branch)))
|
|
225
225
|
const recorded = [...live, ...retired]
|
|
226
|
+
// Unset keeps a live stack stacking: commits mode would retarget its top PR
|
|
227
|
+
// and strand the lower ones. A dead record ships as commits.
|
|
228
|
+
const stacked = !rawMode && live.length > 0 && (await chainsToHead(git, ok, branch, live))
|
|
229
|
+
const mode: ShipMode = stacked ? "stack" : valid
|
|
226
230
|
const squashed = await squash(io, git, ok, branch, baseBranch, isDry, mode)
|
|
227
231
|
if (!squashed.ok) return squashed
|
|
228
232
|
// Until the PR step succeeds the record only moves aside: a retry must not
|