@ramon-sg/issue-flow 0.3.0 → 0.3.1
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/dist/commands/pr-worktree.js +10 -10
- package/dist/git.d.ts +16 -0
- package/dist/git.js +23 -0
- package/dist/start-modes.js +2 -1
- package/dist/start-place.js +11 -7
- package/dist/start-summary.d.ts +1 -1
- package/dist/start-summary.js +2 -2
- package/package.json +1 -1
|
@@ -13,7 +13,7 @@ import { existsSync } from "node:fs";
|
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
import { getPr } from "@ramon-sg/github-rest";
|
|
15
15
|
import { canonicalPath } from "../docker.js";
|
|
16
|
-
import { checkedOutAt, git, hasLocal, locateRepo, worktrees } from "../git.js";
|
|
16
|
+
import { checkedOutAt, git, hasLocal, locateRepo, trackOrigin, untracked, worktrees } from "../git.js";
|
|
17
17
|
import { permissionArgs } from "../launch.js";
|
|
18
18
|
import { warn } from "../output.js";
|
|
19
19
|
import { copyWorktreeIncludes } from "../start-place.js";
|
|
@@ -45,7 +45,7 @@ function mustGit(cwd, args, message) {
|
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Makes the worktree on the PR's branch: the local branch if there is one (moved forward to
|
|
48
|
-
* origin's when it can be), or a new one tracking origin's.
|
|
48
|
+
* origin's when it can be), or a new one; either way tracking origin's when it can.
|
|
49
49
|
* @param mainRoot - The main checkout.
|
|
50
50
|
* @param branch - The PR's branch.
|
|
51
51
|
* @param path - The worktree.
|
|
@@ -57,14 +57,14 @@ function addWorktree(mainRoot, branch, path, out) {
|
|
|
57
57
|
const elsewhere = checkedOutAt(mainRoot, branch);
|
|
58
58
|
if (elsewhere !== null)
|
|
59
59
|
throw new StartError(`${branch} is checked out in ${elsewhere}; review it there, or switch that checkout to another branch.`);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
mustGit(mainRoot, ["worktree", "add", "-q",
|
|
65
|
-
if (!
|
|
66
|
-
warn(out,
|
|
67
|
-
if (!git(path, ["merge", "-q", "--ff-only", `origin/${branch}`]).ok)
|
|
60
|
+
// NOTE: the tracking goes apart, after the worktree: it writes .git/config, which Claude
|
|
61
|
+
// Code's sandbox forbids, and the worktree is still useful without it.
|
|
62
|
+
const local = hasLocal(mainRoot, branch);
|
|
63
|
+
const args = local ? [path, branch] : ["--no-track", "-b", branch, path, `origin/${branch}`];
|
|
64
|
+
mustGit(mainRoot, ["worktree", "add", "-q", ...args], `could not make the worktree of ${branch}`);
|
|
65
|
+
if (!trackOrigin(path, branch))
|
|
66
|
+
warn(out, untracked(branch));
|
|
67
|
+
if (local && !git(path, ["merge", "-q", "--ff-only", `origin/${branch}`]).ok)
|
|
68
68
|
warn(out, `the local ${branch} does not move forward to origin/${branch}: you see other commits than the PR's.`);
|
|
69
69
|
}
|
|
70
70
|
/**
|
package/dist/git.d.ts
CHANGED
|
@@ -28,6 +28,22 @@ export declare function gitLines(cwd: string, args: string[]): string[];
|
|
|
28
28
|
* @example hasRef("/repo", "refs/heads/main")
|
|
29
29
|
*/
|
|
30
30
|
export declare function hasRef(cwd: string, ref: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Makes a branch track origin's of the same name, unless it already does. The upstream lives
|
|
33
|
+
* in `.git/config`, which Claude Code's sandbox forbids writing, so this is the only step that
|
|
34
|
+
* writes it: branches are made with `--no-track` and pushed without `-u`.
|
|
35
|
+
* @param cwd - The checkout.
|
|
36
|
+
* @param branch - The branch.
|
|
37
|
+
* @returns True if it tracks `origin/<branch>` now; false if git could not write it.
|
|
38
|
+
* @example trackOrigin("/repo", "feat/42-x")
|
|
39
|
+
*/
|
|
40
|
+
export declare function trackOrigin(cwd: string, branch: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The warning when a branch cannot track origin's.
|
|
43
|
+
* @param branch - The branch.
|
|
44
|
+
* @returns The warning.
|
|
45
|
+
*/
|
|
46
|
+
export declare function untracked(branch: string): string;
|
|
31
47
|
/**
|
|
32
48
|
* Whether the branch exists locally.
|
|
33
49
|
* @param cwd - The checkout.
|
package/dist/git.js
CHANGED
|
@@ -41,6 +41,29 @@ export function gitLines(cwd, args) {
|
|
|
41
41
|
export function hasRef(cwd, ref) {
|
|
42
42
|
return git(cwd, ["rev-parse", "-q", "--verify", ref]).ok;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Makes a branch track origin's of the same name, unless it already does. The upstream lives
|
|
46
|
+
* in `.git/config`, which Claude Code's sandbox forbids writing, so this is the only step that
|
|
47
|
+
* writes it: branches are made with `--no-track` and pushed without `-u`.
|
|
48
|
+
* @param cwd - The checkout.
|
|
49
|
+
* @param branch - The branch.
|
|
50
|
+
* @returns True if it tracks `origin/<branch>` now; false if git could not write it.
|
|
51
|
+
* @example trackOrigin("/repo", "feat/42-x")
|
|
52
|
+
*/
|
|
53
|
+
export function trackOrigin(cwd, branch) {
|
|
54
|
+
const upstream = git(cwd, ["rev-parse", "-q", "--symbolic-full-name", `${branch}@{u}`]);
|
|
55
|
+
if (upstream.ok && upstream.out === `refs/remotes/origin/${branch}`)
|
|
56
|
+
return true;
|
|
57
|
+
return git(cwd, ["branch", "-q", `--set-upstream-to=origin/${branch}`, branch]).ok;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The warning when a branch cannot track origin's.
|
|
61
|
+
* @param branch - The branch.
|
|
62
|
+
* @returns The warning.
|
|
63
|
+
*/
|
|
64
|
+
export function untracked(branch) {
|
|
65
|
+
return `could not make ${branch} track origin/${branch} (is .git/config read-only, as in a sandbox?); push with 'git push origin HEAD'.`;
|
|
66
|
+
}
|
|
44
67
|
/**
|
|
45
68
|
* Whether the branch exists locally.
|
|
46
69
|
* @param cwd - The checkout.
|
package/dist/start-modes.js
CHANGED
|
@@ -188,7 +188,8 @@ export function prepareHere(state) {
|
|
|
188
188
|
throw new StartError(`branch ${branch} is only local (on another machine or checkout): push it from there and run this again.`);
|
|
189
189
|
if (hasLocal(mainRoot, branch) && git(mainRoot, ["rev-list", "--count", branch, "--not", `origin/${branch}`]).out !== "0")
|
|
190
190
|
throw new StartError(`the local branch ${branch} has commits that are not in origin; push them or sort it out by hand.`);
|
|
191
|
-
|
|
191
|
+
// NOTE: --no-track: publish sets the upstream when it can (see trackOrigin).
|
|
192
|
+
checkout(["--no-track", "-B", branch, `origin/${branch}`]);
|
|
192
193
|
}
|
|
193
194
|
/**
|
|
194
195
|
* The cloud session noted in the PR's body, or null (no PR, none noted, or the PR cannot
|
package/dist/start-place.js
CHANGED
|
@@ -9,7 +9,7 @@ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync } from "node:f
|
|
|
9
9
|
import { dirname, join, relative } from "node:path";
|
|
10
10
|
import { assign, createPr, findPr, getPr, updatePr } from "@ramon-sg/github-rest";
|
|
11
11
|
import { confirm } from "./ask.js";
|
|
12
|
-
import { checkedOutAt, currentBranch, git, gitLines, hasLocal, isClean, onOrigin, safeToDelete } from "./git.js";
|
|
12
|
+
import { checkedOutAt, currentBranch, git, gitLines, hasLocal, isClean, onOrigin, safeToDelete, trackOrigin, untracked } from "./git.js";
|
|
13
13
|
import { bodyToPartOf, dirName, fill, prBody } from "./issue-text.js";
|
|
14
14
|
import { forgetSession } from "./launch.js";
|
|
15
15
|
import { warn } from "./output.js";
|
|
@@ -75,7 +75,9 @@ export function prepareBranch(state) {
|
|
|
75
75
|
if (state.mode === "worktree") {
|
|
76
76
|
if (existsSync(state.workDir))
|
|
77
77
|
return;
|
|
78
|
-
|
|
78
|
+
// NOTE: --no-track, since tracking writes .git/config, which Claude Code's sandbox
|
|
79
|
+
// forbids; publish sets the branch's upstream when it can (see trackOrigin).
|
|
80
|
+
const args = found === null ? ["--no-track", "-b", branch, `origin/${base}`] : hasLocal(mainRoot, branch) ? [branch] : ["--no-track", "-b", branch, `origin/${branch}`];
|
|
79
81
|
const result = git(mainRoot, ["worktree", "add", "-q", state.workDir, ...args]);
|
|
80
82
|
if (!result.ok)
|
|
81
83
|
throw new StartError(`could not create the worktree: ${result.err}. If a sandbox stopped it, run in a terminal: ${again(state.options.number, "--yes")}`);
|
|
@@ -85,8 +87,10 @@ export function prepareBranch(state) {
|
|
|
85
87
|
return;
|
|
86
88
|
if (!isClean(mainRoot))
|
|
87
89
|
throw new StartError("the main checkout has uncommitted changes; commit or stash them first. No stash is made.");
|
|
88
|
-
if (found !== null)
|
|
89
|
-
|
|
90
|
+
if (found !== null) {
|
|
91
|
+
const args = hasLocal(mainRoot, branch) ? [branch] : ["--no-track", "-b", branch, `origin/${branch}`];
|
|
92
|
+
return mustGit(mainRoot, ["checkout", "-q", ...args], `could not check out ${branch}`);
|
|
93
|
+
}
|
|
90
94
|
if (currentBranch(mainRoot) !== base)
|
|
91
95
|
throw new StartError(`the main checkout is not on ${base}.`);
|
|
92
96
|
if (git(mainRoot, ["rev-list", "--count", `origin/${base}..${base}`]).out !== "0")
|
|
@@ -133,9 +137,9 @@ export async function publish(state) {
|
|
|
133
137
|
mustGit(workDir, ["commit", "-q", "--allow-empty", "--only", "-m", message], "could not make the start commit");
|
|
134
138
|
}
|
|
135
139
|
if (commit || !onOrigin(workDir, branch))
|
|
136
|
-
mustGit(workDir, ["push", "-q", "
|
|
137
|
-
if (!
|
|
138
|
-
|
|
140
|
+
mustGit(workDir, ["push", "-q", "origin", branch], `could not push ${branch}`);
|
|
141
|
+
if (!trackOrigin(workDir, branch))
|
|
142
|
+
warn(state.out, untracked(branch));
|
|
139
143
|
let number = state.pr?.number ?? null;
|
|
140
144
|
if (number === null) {
|
|
141
145
|
const created = await createPr(options.repo, {
|
package/dist/start-summary.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export declare function describeBranch(state: StartState, branch: string, withPr
|
|
|
22
22
|
* The actions it will take, joined with " + ".
|
|
23
23
|
* @param state - The start.
|
|
24
24
|
* @returns The text.
|
|
25
|
-
* @example plannedActions(state) // "create the branch from origin/main + empty commit + push
|
|
25
|
+
* @example plannedActions(state) // "create the branch from origin/main + empty commit + push + …"
|
|
26
26
|
*/
|
|
27
27
|
export declare function plannedActions(state: StartState): string;
|
|
28
28
|
/**
|
package/dist/start-summary.js
CHANGED
|
@@ -53,7 +53,7 @@ export function describeBranch(state, branch, withPr) {
|
|
|
53
53
|
* The actions it will take, joined with " + ".
|
|
54
54
|
* @param state - The start.
|
|
55
55
|
* @returns The text.
|
|
56
|
-
* @example plannedActions(state) // "create the branch from origin/main + empty commit + push
|
|
56
|
+
* @example plannedActions(state) // "create the branch from origin/main + empty commit + push + …"
|
|
57
57
|
*/
|
|
58
58
|
export function plannedActions(state) {
|
|
59
59
|
if (state.mode === "cloud")
|
|
@@ -65,7 +65,7 @@ export function plannedActions(state) {
|
|
|
65
65
|
if (commit)
|
|
66
66
|
actions.push("empty commit");
|
|
67
67
|
if (commit || !onOrigin(mainRoot, branch))
|
|
68
|
-
actions.push("push
|
|
68
|
+
actions.push("push");
|
|
69
69
|
if (state.pr === null) {
|
|
70
70
|
const first = prBody(state.options.config.start.prBody, number, state.kind, state.issue.url).split("\n")[0];
|
|
71
71
|
actions.push(`draft PR (${first})`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ramon-sg/issue-flow",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Issue-to-PR workflow for any repo: names each issue's branch, creates its worktree, opens the draft PR and launches Claude Code, locally and in Claude's cloud.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|