@ramon-sg/issue-flow 0.2.7 → 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.
@@ -1,10 +1,10 @@
1
1
  /**
2
- * @file The repo's `package.json` as the flow needs it: `issue-flow`, `github-rest` and
3
- * `skills-manager` as exact dev dependencies (the skills call them with `npx`, which then
4
- * uses the repo's copy), and the scripts to start an issue, prune worktrees and review a PR.
5
- * A dependency or script the repo already has with another value stays as it is, except
6
- * that `upgrade` moves `issue-flow` to the version that runs and raises the other two only
7
- * when they fall short of it: a newer version or a range that allows it is never moved back.
2
+ * @file The repo's `package.json` as the flow needs it: `issue-flow` at the version that
3
+ * runs and `github-rest` as exact dev dependencies (the skills call them with `npx`, which
4
+ * then uses the repo's copy), and the scripts to start an issue, prune worktrees and review
5
+ * a PR. `apply` adds `github-rest` only if it is missing: its version is the repo's to pick,
6
+ * and `doctor` says when it falls short of what the flow needs. A script the repo already
7
+ * has with another value stays as it is.
8
8
  */
9
9
  import { type OwnVersions } from "./setup-files.js";
10
10
  /**
@@ -17,31 +17,20 @@ import { type OwnVersions } from "./setup-files.js";
17
17
  */
18
18
  export declare function isNewer(version: string, than: string): boolean;
19
19
  /**
20
- * Whether the repo's version of a package reaches the one the flow was tested with, so
21
- * `upgrade` keeps it: an exact version as new or newer, a range that allows it or newer
20
+ * Whether the repo's version of a package reaches the one the flow needs, so `apply` and
21
+ * `doctor` accept it: an exact version as new or newer, a range that allows it or newer
22
22
  * (`^1.2.0` for 1.3.0, not `^0.1.0` for 0.3.0), or a source it cannot read (`workspace:*`,
23
23
  * `github:…`, `latest`), which the repo chose on purpose.
24
24
  * @param current - The repo's version or range.
25
- * @param tested - The version the flow was tested with.
26
- * @returns True to keep it.
25
+ * @param tested - The version the flow needs.
26
+ * @returns True if it reaches it.
27
27
  * @example reaches("1.3.0", "1.2.0") // true
28
28
  */
29
29
  export declare function reaches(current: string, tested: string): boolean;
30
- /** A dependency the flow pins: its version, and whether `upgrade` keeps one that reaches it. */
31
- interface Pin {
32
- version: string;
33
- keepsReaching: boolean;
34
- }
35
30
  /** How a change that keeps what the repo had starts. */
36
31
  export declare const KEPT = "kept ";
37
- /**
38
- * The dependencies the flow pins, with their versions. `issue-flow` always follows the
39
- * version that runs; the other two are a floor, so `upgrade` keeps what reaches it.
40
- * @param versions - This package's versions.
41
- * @returns Package to pin.
42
- * @example pinnedPackages(versions)["@ramon-sg/issue-flow"].version // "0.1.0"
43
- */
44
- export declare function pinnedPackages(versions: OwnVersions): Record<string, Pin>;
32
+ /** The package the skills call, which the repo gets if it lacks it. */
33
+ export declare const GITHUB_REST = "@ramon-sg/github-rest";
45
34
  /**
46
35
  * The version of a package in the repo, as a dependency or a dev dependency.
47
36
  * @param pkg - The package.json.
@@ -51,16 +40,15 @@ export declare function pinnedPackages(versions: OwnVersions): Record<string, Pi
51
40
  */
52
41
  export declare function dependencyVersion(pkg: Record<string, unknown> | null, name: string): string | null;
53
42
  /**
54
- * Adds the flow's dev dependencies and scripts that are missing. With `move`, also moves the
55
- * ones already there to these versions (what `upgrade` does).
43
+ * Pins `issue-flow` to the version that runs, adds `github-rest` if it is missing and the
44
+ * scripts that are missing. Nothing else changes: a `github-rest` the repo has stays, with a
45
+ * note if it falls short of the version the flow needs.
56
46
  * @param pkg - The package.json, or null if there is none.
57
- * @param versions - The versions to pin.
58
- * @param move - Whether to move the dependencies already there.
47
+ * @param versions - This package's versions.
59
48
  * @returns The new package.json and what changed or was kept.
60
- * @example withFlowPackages(null, versions, false).changes // ["devDependencies: @ramon-sg/issue-flow 0.1.0", …]
49
+ * @example withFlowPackages(null, versions).changes // ["devDependencies: @ramon-sg/issue-flow 0.1.0", …]
61
50
  */
62
- export declare function withFlowPackages(pkg: Record<string, unknown> | null, versions: OwnVersions, move: boolean): {
51
+ export declare function withFlowPackages(pkg: Record<string, unknown> | null, versions: OwnVersions): {
63
52
  pkg: Record<string, unknown>;
64
53
  changes: string[];
65
54
  };
66
- export {};
@@ -1,10 +1,10 @@
1
1
  /**
2
- * @file The repo's `package.json` as the flow needs it: `issue-flow`, `github-rest` and
3
- * `skills-manager` as exact dev dependencies (the skills call them with `npx`, which then
4
- * uses the repo's copy), and the scripts to start an issue, prune worktrees and review a PR.
5
- * A dependency or script the repo already has with another value stays as it is, except
6
- * that `upgrade` moves `issue-flow` to the version that runs and raises the other two only
7
- * when they fall short of it: a newer version or a range that allows it is never moved back.
2
+ * @file The repo's `package.json` as the flow needs it: `issue-flow` at the version that
3
+ * runs and `github-rest` as exact dev dependencies (the skills call them with `npx`, which
4
+ * then uses the repo's copy), and the scripts to start an issue, prune worktrees and review
5
+ * a PR. `apply` adds `github-rest` only if it is missing: its version is the repo's to pick,
6
+ * and `doctor` says when it falls short of what the flow needs. A script the repo already
7
+ * has with another value stays as it is.
8
8
  */
9
9
  import { PACKAGE } from "./setup-files.js";
10
10
  /** A version: X.Y.Z, with an optional pre-release and build. */
@@ -71,13 +71,13 @@ function ceiling(sign, floor) {
71
71
  return minor > 0 ? { parts: [0, minor + 1, 0], pre: null } : { parts: [0, 0, patch + 1], pre: null };
72
72
  }
73
73
  /**
74
- * Whether the repo's version of a package reaches the one the flow was tested with, so
75
- * `upgrade` keeps it: an exact version as new or newer, a range that allows it or newer
74
+ * Whether the repo's version of a package reaches the one the flow needs, so `apply` and
75
+ * `doctor` accept it: an exact version as new or newer, a range that allows it or newer
76
76
  * (`^1.2.0` for 1.3.0, not `^0.1.0` for 0.3.0), or a source it cannot read (`workspace:*`,
77
77
  * `github:…`, `latest`), which the repo chose on purpose.
78
78
  * @param current - The repo's version or range.
79
- * @param tested - The version the flow was tested with.
80
- * @returns True to keep it.
79
+ * @param tested - The version the flow needs.
80
+ * @returns True if it reaches it.
81
81
  * @example reaches("1.3.0", "1.2.0") // true
82
82
  */
83
83
  export function reaches(current, tested) {
@@ -98,26 +98,14 @@ export function reaches(current, tested) {
98
98
  }
99
99
  /** How a change that keeps what the repo had starts. */
100
100
  export const KEPT = "kept ";
101
- /** The scripts `init` adds. */
101
+ /** The package the skills call, which the repo gets if it lacks it. */
102
+ export const GITHUB_REST = "@ramon-sg/github-rest";
103
+ /** The scripts `apply` adds. */
102
104
  const FLOW_SCRIPTS = {
103
105
  "issue:start": "issue-flow issue start",
104
106
  "worktree:prune": "issue-flow worktree prune",
105
107
  "pr:worktree": "issue-flow pr worktree",
106
108
  };
107
- /**
108
- * The dependencies the flow pins, with their versions. `issue-flow` always follows the
109
- * version that runs; the other two are a floor, so `upgrade` keeps what reaches it.
110
- * @param versions - This package's versions.
111
- * @returns Package to pin.
112
- * @example pinnedPackages(versions)["@ramon-sg/issue-flow"].version // "0.1.0"
113
- */
114
- export function pinnedPackages(versions) {
115
- return {
116
- [PACKAGE]: { version: versions.issueFlow, keepsReaching: false },
117
- "@ramon-sg/github-rest": { version: versions.githubRest, keepsReaching: true },
118
- "@ramon-sg/skills-manager": { version: versions.skillsManager, keepsReaching: true },
119
- };
120
- }
121
109
  /**
122
110
  * Where a package is in the repo's package.json, and its version.
123
111
  * @param pkg - The package.json.
@@ -144,30 +132,30 @@ export function dependencyVersion(pkg, name) {
144
132
  return dependencyOf(pkg, name)?.version ?? null;
145
133
  }
146
134
  /**
147
- * Adds the flow's dev dependencies and scripts that are missing. With `move`, also moves the
148
- * ones already there to these versions (what `upgrade` does).
135
+ * Pins `issue-flow` to the version that runs, adds `github-rest` if it is missing and the
136
+ * scripts that are missing. Nothing else changes: a `github-rest` the repo has stays, with a
137
+ * note if it falls short of the version the flow needs.
149
138
  * @param pkg - The package.json, or null if there is none.
150
- * @param versions - The versions to pin.
151
- * @param move - Whether to move the dependencies already there.
139
+ * @param versions - This package's versions.
152
140
  * @returns The new package.json and what changed or was kept.
153
- * @example withFlowPackages(null, versions, false).changes // ["devDependencies: @ramon-sg/issue-flow 0.1.0", …]
141
+ * @example withFlowPackages(null, versions).changes // ["devDependencies: @ramon-sg/issue-flow 0.1.0", …]
154
142
  */
155
- export function withFlowPackages(pkg, versions, move) {
143
+ export function withFlowPackages(pkg, versions) {
156
144
  const next = pkg === null ? { private: true } : { ...pkg };
157
145
  const scripts = { ...(next.scripts ?? {}) };
158
146
  const changes = [];
159
- for (const [name, { version, keepsReaching }] of Object.entries(pinnedPackages(versions))) {
160
- const current = dependencyOf(next, name);
161
- if (current?.version === version)
162
- continue;
163
- if (current !== null && (!move || (keepsReaching && reaches(current.version, version)))) {
164
- changes.push(`${KEPT}${name} ${current.version} (issue-flow ${versions.issueFlow} was tested with ${version})`);
165
- continue;
166
- }
167
- const field = current?.field ?? "devDependencies";
147
+ const pin = (name, version, field, from) => {
168
148
  next[field] = { ...(next[field] ?? {}), [name]: version };
169
- changes.push(`${field}: ${name} ${current === null ? "" : `${current.version} → `}${version}`);
170
- }
149
+ changes.push(`${field}: ${name} ${from === null ? "" : `${from} → `}${version}`);
150
+ };
151
+ const flow = dependencyOf(next, PACKAGE);
152
+ if (flow?.version !== versions.issueFlow)
153
+ pin(PACKAGE, versions.issueFlow, flow?.field ?? "devDependencies", flow?.version ?? null);
154
+ const rest = dependencyOf(next, GITHUB_REST);
155
+ if (rest === null)
156
+ pin(GITHUB_REST, versions.githubRest, "devDependencies", null);
157
+ else if (!reaches(rest.version, versions.githubRest))
158
+ changes.push(`${KEPT}${GITHUB_REST} ${rest.version} (the skills need ${versions.githubRest} or newer: raise it)`);
171
159
  for (const [name, command] of Object.entries(FLOW_SCRIPTS)) {
172
160
  if (scripts[name] !== undefined)
173
161
  continue;
@@ -1,4 +1,4 @@
1
- /** The hook events `init` registers. */
1
+ /** The hook events `apply` registers. */
2
2
  export declare const HOOK_EVENTS: string[];
3
3
  /**
4
4
  * The command of one of the hooks, at a version.
@@ -1,8 +1,8 @@
1
1
  /**
2
- * @file The Claude Code hooks `init` registers in `.claude/settings.json`: `SessionStart`
2
+ * @file The Claude Code hooks `apply` registers in `.claude/settings.json`: `SessionStart`
3
3
  * (`issue-flow session`) and `WorktreeCreate` and `WorktreeRemove` (`issue-flow hook …`), each
4
4
  * pinned to this package's version. They are found again by their command, so running
5
- * `init` twice changes nothing and `upgrade` only moves the version. The `SessionStart`
5
+ * `apply` twice changes nothing, and a new version only moves the version. The `SessionStart`
6
6
  * command names `claude-cloud session`, which `issue-flow session` runs in the cloud: that
7
7
  * way `claude-cloud init` sees it and does not add its own, and a hook of `claude-cloud`
8
8
  * already there is dropped, so it does not run twice.
@@ -19,7 +19,7 @@ const HOOK_COMMANDS = {
19
19
  WorktreeCreate: { sub: "hook worktree-create", note: "" },
20
20
  WorktreeRemove: { sub: "hook worktree-remove", note: "" },
21
21
  };
22
- /** The hook events `init` registers. */
22
+ /** The hook events `apply` registers. */
23
23
  export const HOOK_EVENTS = Object.keys(HOOK_COMMANDS);
24
24
  /**
25
25
  * The command of one of the hooks, at a version.
@@ -2,20 +2,37 @@
2
2
  export declare const PACKAGE = "@ramon-sg/issue-flow";
3
3
  /** The GitHub repo of this package, whose tags hold the config's schema of each version. */
4
4
  export declare const FLOW_REPO = "ramon-sg/issue-flow";
5
+ /** The start of the config's schema URL, at any version. */
6
+ export declare const SCHEMA_PREFIX = "https://raw.githubusercontent.com/ramon-sg/issue-flow/";
7
+ /**
8
+ * The URL of the config's schema at a version.
9
+ * @param version - This package's version.
10
+ * @returns The URL.
11
+ * @example schemaUrl("0.1.0") // "https://raw.githubusercontent.com/ramon-sg/issue-flow/v0.1.0/schema.json"
12
+ */
13
+ export declare function schemaUrl(version: string): string;
14
+ /**
15
+ * Whether a config's `$schema` is the flow's at another version (one that is not the
16
+ * flow's is the repo's choice and never stale).
17
+ * @param schema - The config's `$schema`.
18
+ * @param version - This package's version.
19
+ * @returns True if `apply` would move it.
20
+ */
21
+ export declare function isStaleSchema(schema: unknown, version: string): boolean;
5
22
  /** Claude Code's settings of the repo. */
6
23
  export declare const SETTINGS_FILE = ".claude/settings.json";
7
- /** Error of `init`, `doctor` or `upgrade`: exits 1 with the message. */
24
+ /** Error of `apply` or `doctor`: exits 1 with the message. */
8
25
  export declare class SetupError extends Error {
9
26
  }
10
27
  /** The versions this package pins in the repo. */
11
28
  export interface OwnVersions {
12
29
  issueFlow: string;
30
+ /** The lowest `github-rest` the flow works with: the floor of its own dependency. */
13
31
  githubRest: string;
14
- skillsManager: string;
15
32
  }
16
33
  /**
17
- * This package's version and the exact versions of `github-rest` and `skills-manager` it
18
- * was built and tested with, from its own `package.json`.
34
+ * This package's version and the lowest `github-rest` it works with, from its own
35
+ * `package.json` (`"^0.3.0"` gives 0.3.0).
19
36
  * @returns The versions.
20
37
  * @throws SetupError if the package.json does not have them.
21
38
  * @example ownVersions().issueFlow // "0.1.0"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file What `init`, `doctor` and `upgrade` share: this package's own versions, the JSON
2
+ * @file What `apply` and `doctor` share: this package's own versions, the JSON
3
3
  * files of the repo they read and write, and its package manager. Every package goes by its
4
4
  * scoped name: the unscoped `issue-flow`, `github-rest` and `skills-manager` on npm belong
5
5
  * to other people.
@@ -12,14 +12,35 @@ import { isObject } from "./config-base.js";
12
12
  export const PACKAGE = "@ramon-sg/issue-flow";
13
13
  /** The GitHub repo of this package, whose tags hold the config's schema of each version. */
14
14
  export const FLOW_REPO = "ramon-sg/issue-flow";
15
+ /** The start of the config's schema URL, at any version. */
16
+ export const SCHEMA_PREFIX = `https://raw.githubusercontent.com/${FLOW_REPO}/`;
17
+ /**
18
+ * The URL of the config's schema at a version.
19
+ * @param version - This package's version.
20
+ * @returns The URL.
21
+ * @example schemaUrl("0.1.0") // "https://raw.githubusercontent.com/ramon-sg/issue-flow/v0.1.0/schema.json"
22
+ */
23
+ export function schemaUrl(version) {
24
+ return `${SCHEMA_PREFIX}v${version}/schema.json`;
25
+ }
26
+ /**
27
+ * Whether a config's `$schema` is the flow's at another version (one that is not the
28
+ * flow's is the repo's choice and never stale).
29
+ * @param schema - The config's `$schema`.
30
+ * @param version - This package's version.
31
+ * @returns True if `apply` would move it.
32
+ */
33
+ export function isStaleSchema(schema, version) {
34
+ return typeof schema === "string" && schema.startsWith(SCHEMA_PREFIX) && schema !== schemaUrl(version);
35
+ }
15
36
  /** Claude Code's settings of the repo. */
16
37
  export const SETTINGS_FILE = ".claude/settings.json";
17
- /** Error of `init`, `doctor` or `upgrade`: exits 1 with the message. */
38
+ /** Error of `apply` or `doctor`: exits 1 with the message. */
18
39
  export class SetupError extends Error {
19
40
  }
20
41
  /**
21
- * This package's version and the exact versions of `github-rest` and `skills-manager` it
22
- * was built and tested with, from its own `package.json`.
42
+ * This package's version and the lowest `github-rest` it works with, from its own
43
+ * `package.json` (`"^0.3.0"` gives 0.3.0).
23
44
  * @returns The versions.
24
45
  * @throws SetupError if the package.json does not have them.
25
46
  * @example ownVersions().issueFlow // "0.1.0"
@@ -27,11 +48,10 @@ export class SetupError extends Error {
27
48
  export function ownVersions() {
28
49
  const file = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
29
50
  const pkg = JSON.parse(readFileSync(file, "utf8"));
30
- const githubRest = pkg.dependencies?.["@ramon-sg/github-rest"];
31
- const skillsManager = pkg.devDependencies?.["@ramon-sg/skills-manager"];
32
- if (pkg.version === undefined || githubRest === undefined || skillsManager === undefined)
33
- throw new SetupError(`${file} lacks the version of issue-flow, github-rest or skills-manager.`);
34
- return { issueFlow: pkg.version, githubRest, skillsManager };
51
+ const githubRest = pkg.dependencies?.["@ramon-sg/github-rest"]?.replace(/^[\^~]/, "");
52
+ if (pkg.version === undefined || githubRest === undefined)
53
+ throw new SetupError(`${file} lacks the version of issue-flow or github-rest.`);
54
+ return { issueFlow: pkg.version, githubRest };
35
55
  }
36
56
  /**
37
57
  * Reads a JSON object of the repo.
@@ -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
- checkout(["-B", branch, `origin/${branch}`]);
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
@@ -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
- const args = found === null ? ["-b", branch, `origin/${base}`] : [branch];
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
- return mustGit(mainRoot, ["checkout", "-q", branch], `could not check out ${branch}`);
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", "-u", "origin", branch], `could not push ${branch}`);
137
- if (!git(workDir, ["rev-parse", "-q", "--verify", "@{u}"]).ok)
138
- git(workDir, ["branch", "-q", `--set-upstream-to=origin/${branch}`, branch]);
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, {
@@ -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 -u + …"
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
  /**
@@ -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 -u + …"
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 -u");
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/dist/usage.d.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * @file The usage text of the CLI, printed with `--help` and after a usage error.
3
3
  */
4
4
  /** Usage text. */
5
- export declare const USAGE = "Usage:\n issue-flow issue branch <n>... [--namer claude|title] [--json] [-R owner/repo]\n issue-flow issue branch --missing [--limit <n>] [--namer claude|title] [--dry-run | --yes] [--json] [-R owner/repo]\n issue-flow issue start <n> [--branch] [--dry-run | --yes] [--resume <branch> | --new] [--no-launch]\n [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --plan [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --cloud | --here | --teleport | --adopt [...]\n issue-flow worktree setup [--only <step>]\n issue-flow worktree teardown [--path <dir>]\n issue-flow worktree ports [--next]\n issue-flow worktree cleanup [--path <dir>]\n issue-flow worktree prune [--only <dir>] [--yes] [-R owner/repo]\n issue-flow pr worktree <n> [--no-setup] [-R owner/repo] [-- [<command>...]]\n issue-flow adapter setup [--only <step>] | issue [<url>] | archive [-R owner/repo]\n issue-flow session\n issue-flow hook worktree-create | worktree-remove\n issue-flow init [--yes] [--recipe <name>,...]\n issue-flow doctor [--json]\n issue-flow upgrade [--dry-run]\n issue-flow labels sync [--update] [--dry-run] [--json] [-R owner/repo]\n\nThe slug comes from the issue's \"Slug de rama\" block, then from Claude, then from the title,\nand the branch from the repo's template (an older \"Rama sugerida\" block is used if it fits).\n--missing adds the slug block to every open issue without a block, after confirming.\n\nissue start makes the branch in a worktree (or, with --branch, in this checkout), the\nstart commit, the push, the draft PR and the assignment, after confirming, and opens\nClaude: in auto mode if the repo has claude-repo-guard, with your settings with\n--ask-permissions, and with no sandbox nor questions with --free. --plan prints the branch\nand the prompt as JSON and changes nothing.\n\n--cloud confirms here and opens a Claude cloud session that starts the issue there.\n--here, inside a cloud session, starts it in this checkout and opens nothing. --teleport\nbrings the cloud session noted in the PR to the worktree. --adopt starts it in the\nworktree another tool (Orca, Paseo) just made, without asking nor opening Claude.\n\nworktree runs the steps of \"worktrees\" in .agents/flow.json: setup prepares this worktree,\nteardown cleans it before it goes (steps, ports, Docker), ports prints or moves its block\nof ports, cleanup deletes the Docker of deleted worktrees, and prune deletes the worktrees\nwhose PR was merged or closed. session is the SessionStart hook of Claude Code.\n\npr worktree makes (or reuses) a worktree on a PR's own branch, to review and fix it; with\n--, it runs the command there (Claude if none). adapter is what Orca and Paseo run: setup\nwhen they make a worktree (its steps and, if it came from an issue, issue start --adopt),\nissue to start one by hand, and archive before they delete it. hook implements Claude\nCode's WorktreeCreate and WorktreeRemove for claude --worktree.\n\ninit sets the flow up in the repo: .agents/flow.json (with the recipes its files suggest),\nthe dev dependencies and scripts, and the hooks. doctor checks them at this version, and\nupgrade moves them to this version. npx agent-kitchen apply runs the three, along with the\nskills, claude-cloud and claude-repo-guard.\n\nlabels sync makes in GitHub the labels the flow wants (\"labels\" in .agents/flow.json: the\nissue types and P1 to P4 by default) that the repo lacks. Names ignore case and are never\nrenamed nor deleted; --update also fixes the color and description of those that differ.";
5
+ export declare const USAGE = "Usage:\n issue-flow issue branch <n>... [--namer claude|title] [--json] [-R owner/repo]\n issue-flow issue branch --missing [--limit <n>] [--namer claude|title] [--dry-run | --yes] [--json] [-R owner/repo]\n issue-flow issue start <n> [--branch] [--dry-run | --yes] [--resume <branch> | --new] [--no-launch]\n [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --plan [--ask-permissions | --free] [--namer claude|title] [-R owner/repo]\n issue-flow issue start <n> --cloud | --here | --teleport | --adopt [...]\n issue-flow worktree setup [--only <step>]\n issue-flow worktree teardown [--path <dir>]\n issue-flow worktree ports [--next]\n issue-flow worktree cleanup [--path <dir>]\n issue-flow worktree prune [--only <dir>] [--yes] [-R owner/repo]\n issue-flow pr worktree <n> [--no-setup] [-R owner/repo] [-- [<command>...]]\n issue-flow adapter setup [--only <step>] | issue [<url>] | archive [-R owner/repo]\n issue-flow session\n issue-flow hook worktree-create | worktree-remove\n issue-flow apply [--yes] [--dry-run] [--json] [--recipe <name>,...]\n issue-flow doctor [--json]\n issue-flow labels sync [--update] [--dry-run] [--json] [-R owner/repo]\n\nThe slug comes from the issue's \"Slug de rama\" block, then from Claude, then from the title,\nand the branch from the repo's template (an older \"Rama sugerida\" block is used if it fits).\n--missing adds the slug block to every open issue without a block, after confirming.\n\nissue start makes the branch in a worktree (or, with --branch, in this checkout), the\nstart commit, the push, the draft PR and the assignment, after confirming, and opens\nClaude: in auto mode if the repo has claude-repo-guard, with your settings with\n--ask-permissions, and with no sandbox nor questions with --free. --plan prints the branch\nand the prompt as JSON and changes nothing.\n\n--cloud confirms here and opens a Claude cloud session that starts the issue there.\n--here, inside a cloud session, starts it in this checkout and opens nothing. --teleport\nbrings the cloud session noted in the PR to the worktree. --adopt starts it in the\nworktree another tool (Orca, Paseo) just made, without asking nor opening Claude.\n\nworktree runs the steps of \"worktrees\" in .agents/flow.json: setup prepares this worktree,\nteardown cleans it before it goes (steps, ports, Docker), ports prints or moves its block\nof ports, cleanup deletes the Docker of deleted worktrees, and prune deletes the worktrees\nwhose PR was merged or closed. session is the SessionStart hook of Claude Code.\n\npr worktree makes (or reuses) a worktree on a PR's own branch, to review and fix it; with\n--, it runs the command there (Claude if none). adapter is what Orca and Paseo run: setup\nwhen they make a worktree (its steps and, if it came from an issue, issue start --adopt),\nissue to start one by hand, and archive before they delete it. hook implements Claude\nCode's WorktreeCreate and WorktreeRemove for claude --worktree.\n\napply leaves the repo as this version needs it: .agents/flow.json (the first time, with\nthe recipes its files suggest), issue-flow at this version and github-rest and the scripts\nif missing in package.json, and the hooks; it changes no other version, and refuses a newer\nissue-flow or a range. doctor checks them. npx agent-kitchen apply runs apply at the version\nit picks, along with the skills, claude-cloud and claude-repo-guard. init and upgrade still\nrun apply, with a warning, until 0.4.0.\n\nlabels sync makes in GitHub the labels the flow wants (\"labels\" in .agents/flow.json: the\nissue types and P1 to P4 by default) that the repo lacks. Names ignore case and are never\nrenamed nor deleted; --update also fixes the color and description of those that differ.";
package/dist/usage.js CHANGED
@@ -18,9 +18,8 @@ export const USAGE = `Usage:
18
18
  issue-flow adapter setup [--only <step>] | issue [<url>] | archive [-R owner/repo]
19
19
  issue-flow session
20
20
  issue-flow hook worktree-create | worktree-remove
21
- issue-flow init [--yes] [--recipe <name>,...]
21
+ issue-flow apply [--yes] [--dry-run] [--json] [--recipe <name>,...]
22
22
  issue-flow doctor [--json]
23
- issue-flow upgrade [--dry-run]
24
23
  issue-flow labels sync [--update] [--dry-run] [--json] [-R owner/repo]
25
24
 
26
25
  The slug comes from the issue's "Slug de rama" block, then from Claude, then from the title,
@@ -49,10 +48,12 @@ when they make a worktree (its steps and, if it came from an issue, issue start
49
48
  issue to start one by hand, and archive before they delete it. hook implements Claude
50
49
  Code's WorktreeCreate and WorktreeRemove for claude --worktree.
51
50
 
52
- init sets the flow up in the repo: .agents/flow.json (with the recipes its files suggest),
53
- the dev dependencies and scripts, and the hooks. doctor checks them at this version, and
54
- upgrade moves them to this version. npx agent-kitchen apply runs the three, along with the
55
- skills, claude-cloud and claude-repo-guard.
51
+ apply leaves the repo as this version needs it: .agents/flow.json (the first time, with
52
+ the recipes its files suggest), issue-flow at this version and github-rest and the scripts
53
+ if missing in package.json, and the hooks; it changes no other version, and refuses a newer
54
+ issue-flow or a range. doctor checks them. npx agent-kitchen apply runs apply at the version
55
+ it picks, along with the skills, claude-cloud and claude-repo-guard. init and upgrade still
56
+ run apply, with a warning, until 0.4.0.
56
57
 
57
58
  labels sync makes in GitHub the labels the flow wants ("labels" in .agents/flow.json: the
58
59
  issue types and P1 to P4 by default) that the repo lacks. Names ignore case and are never
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ramon-sg/issue-flow",
3
- "version": "0.2.7",
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",
@@ -17,6 +17,6 @@
17
17
  "test": "tsc && node --test test/*.test.mjs",
18
18
  "prepublishOnly": "npm test"
19
19
  },
20
- "dependencies": { "@ramon-sg/github-rest": "0.3.0" },
20
+ "dependencies": { "@ramon-sg/github-rest": "^0.3.0" },
21
21
  "devDependencies": { "@ramon-sg/skills-manager": "1.2.0", "@types/node": "^24.0.0", "typescript": "^5.9.3" }
22
22
  }
@@ -1,24 +0,0 @@
1
- import { type Output } from "../output.js";
2
- /** Options of `issue-flow init`. */
3
- export interface InitOptions {
4
- cwd: string;
5
- yes: boolean;
6
- recipes: string[] | null;
7
- }
8
- /**
9
- * The URL of the config's schema at a version.
10
- * @param version - This package's version.
11
- * @returns The URL.
12
- * @example schemaUrl("0.1.0") // "https://raw.githubusercontent.com/ramon-sg/issue-flow/v0.1.0/schema.json"
13
- */
14
- export declare function schemaUrl(version: string): string;
15
- /**
16
- * Sets the repo up for the flow; see the file's header.
17
- * @param options - The options.
18
- * @param out - Where to write and ask.
19
- * @returns 0, or 1 if the files were not written.
20
- * @throws SetupError outside the main checkout or with a broken file; ConfigError if the
21
- * config would be invalid; UsageError for an unknown recipe.
22
- * @example await init({ cwd, yes: true, recipes: null }, out)
23
- */
24
- export declare function init(options: InitOptions, out: Output): Promise<number>;