@ramon-sg/issue-flow 0.0.0-stage → 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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +304 -2
  3. package/dist/ask.d.ts +28 -0
  4. package/dist/ask.js +73 -0
  5. package/dist/branch-name.d.ts +113 -0
  6. package/dist/branch-name.js +226 -0
  7. package/dist/claude-namer.d.ts +51 -0
  8. package/dist/claude-namer.js +140 -0
  9. package/dist/cli-setup.d.ts +22 -0
  10. package/dist/cli-setup.js +26 -0
  11. package/dist/cli-worktree.d.ts +74 -0
  12. package/dist/cli-worktree.js +123 -0
  13. package/dist/cli.d.ts +132 -0
  14. package/dist/cli.js +348 -0
  15. package/dist/commands/adapter.d.ts +49 -0
  16. package/dist/commands/adapter.js +231 -0
  17. package/dist/commands/claude-hooks.d.ts +37 -0
  18. package/dist/commands/claude-hooks.js +162 -0
  19. package/dist/commands/doctor.d.ts +10 -0
  20. package/dist/commands/doctor.js +124 -0
  21. package/dist/commands/init.d.ts +27 -0
  22. package/dist/commands/init.js +213 -0
  23. package/dist/commands/issue-branch.d.ts +36 -0
  24. package/dist/commands/issue-branch.js +182 -0
  25. package/dist/commands/issue-start.d.ts +11 -0
  26. package/dist/commands/issue-start.js +361 -0
  27. package/dist/commands/pr-worktree.d.ts +31 -0
  28. package/dist/commands/pr-worktree.js +122 -0
  29. package/dist/commands/session.d.ts +15 -0
  30. package/dist/commands/session.js +102 -0
  31. package/dist/commands/upgrade.d.ts +16 -0
  32. package/dist/commands/upgrade.js +115 -0
  33. package/dist/commands/worktree.d.ts +82 -0
  34. package/dist/commands/worktree.js +244 -0
  35. package/dist/config-base.d.ts +32 -0
  36. package/dist/config-base.js +47 -0
  37. package/dist/config.d.ts +63 -0
  38. package/dist/config.js +230 -0
  39. package/dist/docker.d.ts +53 -0
  40. package/dist/docker.js +153 -0
  41. package/dist/git.d.ts +130 -0
  42. package/dist/git.js +192 -0
  43. package/dist/guard-config.d.ts +16 -0
  44. package/dist/guard-config.js +38 -0
  45. package/dist/issue-text.d.ts +128 -0
  46. package/dist/issue-text.js +210 -0
  47. package/dist/launch.d.ts +90 -0
  48. package/dist/launch.js +158 -0
  49. package/dist/output.d.ts +33 -0
  50. package/dist/output.js +37 -0
  51. package/dist/package-edits.d.ts +35 -0
  52. package/dist/package-edits.js +78 -0
  53. package/dist/plan-doc.d.ts +26 -0
  54. package/dist/plan-doc.js +93 -0
  55. package/dist/ports.d.ts +90 -0
  56. package/dist/ports.js +257 -0
  57. package/dist/propose.d.ts +42 -0
  58. package/dist/propose.js +111 -0
  59. package/dist/recipes.d.ts +43 -0
  60. package/dist/recipes.js +123 -0
  61. package/dist/session-lock.d.ts +32 -0
  62. package/dist/session-lock.js +69 -0
  63. package/dist/settings-hooks.d.ts +31 -0
  64. package/dist/settings-hooks.js +133 -0
  65. package/dist/setup-files.d.ts +89 -0
  66. package/dist/setup-files.js +138 -0
  67. package/dist/start-modes.d.ts +162 -0
  68. package/dist/start-modes.js +277 -0
  69. package/dist/start-place.d.ts +61 -0
  70. package/dist/start-place.js +301 -0
  71. package/dist/start-state.d.ts +72 -0
  72. package/dist/start-state.js +13 -0
  73. package/dist/start-summary.d.ts +42 -0
  74. package/dist/start-summary.js +217 -0
  75. package/dist/stop-apps.d.ts +14 -0
  76. package/dist/stop-apps.js +127 -0
  77. package/dist/usage.d.ts +5 -0
  78. package/dist/usage.js +54 -0
  79. package/dist/work-state.d.ts +51 -0
  80. package/dist/work-state.js +58 -0
  81. package/dist/worktree-config.d.ts +35 -0
  82. package/dist/worktree-config.js +139 -0
  83. package/dist/worktree-steps.d.ts +53 -0
  84. package/dist/worktree-steps.js +210 -0
  85. package/package.json +20 -4
  86. package/recipes/bun.json +5 -0
  87. package/recipes/direnv.json +17 -0
  88. package/recipes/docker-compose.json +30 -0
  89. package/recipes/npm.json +5 -0
  90. package/recipes/pnpm.json +5 -0
  91. package/recipes/staging-main.json +5 -0
  92. package/schema.json +281 -0
package/dist/cli.d.ts ADDED
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file `issue-flow` CLI. Warnings on stderr; exits 0 on success, 1 if GitHub, an edit or
4
+ * the start of an issue failed (or was declined) and 2 on bad usage or a bad
5
+ * `.agents/flow.json`.
6
+ */
7
+ import { parseArgs } from "node:util";
8
+ import { type FlowConfig } from "./config.js";
9
+ import { type Output } from "./output.js";
10
+ /** Options of the CLI. */
11
+ declare const OPTIONS: {
12
+ readonly repo: {
13
+ readonly type: "string";
14
+ readonly short: "R";
15
+ };
16
+ readonly json: {
17
+ readonly type: "boolean";
18
+ };
19
+ readonly help: {
20
+ readonly type: "boolean";
21
+ readonly short: "h";
22
+ };
23
+ readonly missing: {
24
+ readonly type: "boolean";
25
+ };
26
+ readonly limit: {
27
+ readonly type: "string";
28
+ };
29
+ readonly namer: {
30
+ readonly type: "string";
31
+ };
32
+ readonly yes: {
33
+ readonly type: "boolean";
34
+ readonly short: "y";
35
+ };
36
+ readonly "dry-run": {
37
+ readonly type: "boolean";
38
+ };
39
+ readonly branch: {
40
+ readonly type: "boolean";
41
+ };
42
+ readonly resume: {
43
+ readonly type: "string";
44
+ };
45
+ readonly new: {
46
+ readonly type: "boolean";
47
+ };
48
+ readonly "no-launch": {
49
+ readonly type: "boolean";
50
+ };
51
+ readonly "ask-permissions": {
52
+ readonly type: "boolean";
53
+ };
54
+ readonly free: {
55
+ readonly type: "boolean";
56
+ };
57
+ readonly plan: {
58
+ readonly type: "boolean";
59
+ };
60
+ readonly cloud: {
61
+ readonly type: "boolean";
62
+ };
63
+ readonly here: {
64
+ readonly type: "boolean";
65
+ };
66
+ readonly teleport: {
67
+ readonly type: "boolean";
68
+ };
69
+ readonly adopt: {
70
+ readonly type: "boolean";
71
+ };
72
+ readonly only: {
73
+ readonly type: "string";
74
+ };
75
+ readonly path: {
76
+ readonly type: "string";
77
+ };
78
+ readonly next: {
79
+ readonly type: "boolean";
80
+ };
81
+ readonly "no-setup": {
82
+ readonly type: "boolean";
83
+ };
84
+ readonly recipe: {
85
+ readonly type: "string";
86
+ };
87
+ readonly "no-cloud": {
88
+ readonly type: "boolean";
89
+ };
90
+ readonly "no-guard": {
91
+ readonly type: "boolean";
92
+ };
93
+ readonly "no-skills": {
94
+ readonly type: "boolean";
95
+ };
96
+ readonly to: {
97
+ readonly type: "string";
98
+ };
99
+ };
100
+ /** The parsed flags. */
101
+ export type Flags = ReturnType<typeof parseArgs<{
102
+ options: typeof OPTIONS;
103
+ allowPositionals: true;
104
+ strict: true;
105
+ }>>["values"];
106
+ /**
107
+ * The repo of the command: `-R`, then the config's `repo`, then `GH_REPO`, then origin.
108
+ * @param flag - The `-R` value.
109
+ * @param config - The config.
110
+ * @param cwd - Where the command runs.
111
+ * @returns `owner/repo`.
112
+ * @throws UsageError if `-R` or `GH_REPO` is malformed; GitHubError if there is no repo.
113
+ */
114
+ export declare function repoOf(flag: string | undefined, config: FlowConfig, cwd: string): string;
115
+ /**
116
+ * Checks that only the command's own flags were given.
117
+ * @param command - The command (`start`, `worktree prune`, `session`).
118
+ * @param flags - The flags.
119
+ * @throws UsageError if one belongs to another command.
120
+ * @example ownFlags("worktree ports", { next: true })
121
+ */
122
+ export declare function ownFlags(command: string, flags: Flags): void;
123
+ /**
124
+ * Runs the CLI.
125
+ * @param argv - Arguments without `node` or the script.
126
+ * @param out - Where to write.
127
+ * @param cwd - Where it runs.
128
+ * @returns The exit code.
129
+ * @throws UsageError, ConfigError or GitHubError, which the entry point handles.
130
+ */
131
+ export declare function main(argv: string[], out: Output, cwd: string): Promise<number>;
132
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file `issue-flow` CLI. Warnings on stderr; exits 0 on success, 1 if GitHub, an edit or
4
+ * the start of an issue failed (or was declined) and 2 on bad usage or a bad
5
+ * `.agents/flow.json`.
6
+ */
7
+ import { parseArgs } from "node:util";
8
+ import { GitHubError, InvalidRepoEnvError, resolveRepo } from "@ramon-sg/github-rest";
9
+ import { branchMissing, branchOf } from "./commands/issue-branch.js";
10
+ import { issueStart } from "./commands/issue-start.js";
11
+ import { ADAPTER_COMMANDS, HOOK_COMMANDS, runAdapter, runHook, runSession, runWorktree, WORKTREE_COMMANDS } from "./cli-worktree.js";
12
+ import { runSetupCommand, SETUP_COMMANDS } from "./cli-setup.js";
13
+ import { prWorktree } from "./commands/pr-worktree.js";
14
+ import { ConfigError } from "./config-base.js";
15
+ import { loadConfig } from "./config.js";
16
+ import { processOutput, UsageError } from "./output.js";
17
+ import { USAGE } from "./usage.js";
18
+ /** Options of the CLI. */
19
+ const OPTIONS = {
20
+ repo: { type: "string", short: "R" },
21
+ json: { type: "boolean" },
22
+ help: { type: "boolean", short: "h" },
23
+ missing: { type: "boolean" },
24
+ limit: { type: "string" },
25
+ namer: { type: "string" },
26
+ yes: { type: "boolean", short: "y" },
27
+ "dry-run": { type: "boolean" },
28
+ branch: { type: "boolean" },
29
+ resume: { type: "string" },
30
+ new: { type: "boolean" },
31
+ "no-launch": { type: "boolean" },
32
+ "ask-permissions": { type: "boolean" },
33
+ free: { type: "boolean" },
34
+ plan: { type: "boolean" },
35
+ cloud: { type: "boolean" },
36
+ here: { type: "boolean" },
37
+ teleport: { type: "boolean" },
38
+ adopt: { type: "boolean" },
39
+ only: { type: "string" },
40
+ path: { type: "string" },
41
+ next: { type: "boolean" },
42
+ "no-setup": { type: "boolean" },
43
+ recipe: { type: "string" },
44
+ "no-cloud": { type: "boolean" },
45
+ "no-guard": { type: "boolean" },
46
+ "no-skills": { type: "boolean" },
47
+ to: { type: "string" },
48
+ };
49
+ /** Flags each command takes, besides `--repo`, `--help` and `--namer`. */
50
+ const COMMAND_FLAGS = {
51
+ branch: ["json", "missing", "limit", "yes", "dry-run"],
52
+ start: [
53
+ "yes",
54
+ "dry-run",
55
+ "branch",
56
+ "resume",
57
+ "new",
58
+ "no-launch",
59
+ "ask-permissions",
60
+ "free",
61
+ "plan",
62
+ "cloud",
63
+ "here",
64
+ "teleport",
65
+ "adopt",
66
+ ],
67
+ "worktree setup": ["only"],
68
+ "worktree teardown": ["path"],
69
+ "worktree ports": ["next"],
70
+ "worktree cleanup": ["path"],
71
+ "worktree prune": ["only", "yes"],
72
+ session: [],
73
+ "pr worktree": ["no-setup"],
74
+ "adapter setup": ["only"],
75
+ "adapter issue": [],
76
+ "adapter archive": [],
77
+ "hook worktree-create": [],
78
+ "hook worktree-remove": [],
79
+ init: ["yes", "recipe", "no-cloud", "no-guard", "no-skills"],
80
+ doctor: [],
81
+ upgrade: ["to", "dry-run"],
82
+ };
83
+ /** An issue number: digits only. */
84
+ const NUMBER = /^\d+$/;
85
+ /** A positive whole number. */
86
+ const POSITIVE = /^[1-9]\d*$/;
87
+ /** `owner/repo`. */
88
+ const REPO = /^[^/\s]+\/[^/\s]+$/;
89
+ /**
90
+ * The repo of the command: `-R`, then the config's `repo`, then `GH_REPO`, then origin.
91
+ * @param flag - The `-R` value.
92
+ * @param config - The config.
93
+ * @param cwd - Where the command runs.
94
+ * @returns `owner/repo`.
95
+ * @throws UsageError if `-R` or `GH_REPO` is malformed; GitHubError if there is no repo.
96
+ */
97
+ export function repoOf(flag, config, cwd) {
98
+ if (flag !== undefined && !REPO.test(flag))
99
+ throw new UsageError(`-R expects owner/repo: ${flag}`);
100
+ let repo;
101
+ try {
102
+ repo = resolveRepo(flag ?? config.repo ?? undefined, cwd);
103
+ }
104
+ catch (error) {
105
+ if (error instanceof InvalidRepoEnvError)
106
+ throw new UsageError(error.message);
107
+ throw error;
108
+ }
109
+ if (repo === null)
110
+ throw new GitHubError("can't tell which repo this is: run inside a checkout whose origin is on GitHub, pass -R owner/repo, set GH_REPO or \"repo\" in .agents/flow.json.", 0);
111
+ return repo;
112
+ }
113
+ /**
114
+ * The `--namer` value.
115
+ * @param value - The flag, or undefined.
116
+ * @returns The namer, or undefined to use the config's.
117
+ * @throws UsageError if it is not claude or title.
118
+ */
119
+ function namerOf(value) {
120
+ if (value === undefined || value === "claude" || value === "title")
121
+ return value;
122
+ throw new UsageError(`--namer expects claude or title: ${value}`);
123
+ }
124
+ /**
125
+ * Checks that only the command's own flags were given.
126
+ * @param command - The command (`start`, `worktree prune`, `session`).
127
+ * @param flags - The flags.
128
+ * @throws UsageError if one belongs to another command.
129
+ * @example ownFlags("worktree ports", { next: true })
130
+ */
131
+ export function ownFlags(command, flags) {
132
+ const shared = command === "branch" || command === "start" ? ["repo", "help", "namer"] : ["repo", "help"];
133
+ const allowed = new Set([...shared, ...(COMMAND_FLAGS[command] ?? [])]);
134
+ const other = Object.keys(flags).find((flag) => !allowed.has(flag));
135
+ const name = command === "branch" || command === "start" ? `issue ${command}` : command;
136
+ if (other !== undefined)
137
+ throw new UsageError(`--${other} does not go with ${name}.`);
138
+ }
139
+ /**
140
+ * The permission mode of `issue start`.
141
+ * @param flags - The flags.
142
+ * @returns The mode.
143
+ * @throws UsageError if both were given.
144
+ */
145
+ function permissionsOf(flags) {
146
+ if (flags["ask-permissions"] && flags.free)
147
+ throw new UsageError("use --ask-permissions or --free, not both.");
148
+ if (flags.free)
149
+ return "free";
150
+ return flags["ask-permissions"] ? "ask" : "default";
151
+ }
152
+ /**
153
+ * Checks the flags of `issue start`.
154
+ * @param flags - The flags.
155
+ * @param rest - The positionals after the command.
156
+ * @returns The issue number.
157
+ * @throws UsageError if they do not fit.
158
+ */
159
+ function startArgs(flags, rest) {
160
+ const [text, ...extra] = rest;
161
+ if (text === undefined || extra.length > 0 || !NUMBER.test(text))
162
+ throw new UsageError("issue start needs one issue number.");
163
+ if (flags.resume !== undefined && flags.new)
164
+ throw new UsageError("--resume does not go with --new.");
165
+ const conflicting = flags.branch || flags["dry-run"] || flags.yes || flags.resume !== undefined || flags.new;
166
+ if (flags.plan && conflicting)
167
+ throw new UsageError("--plan does not go with --branch, --dry-run, --yes, --resume nor --new.");
168
+ cloudArgs(flags);
169
+ return Number(text);
170
+ }
171
+ /** The flags that do not go together with each way of starting, besides each other. */
172
+ const EXCLUSIVE = {
173
+ cloud: ["branch", "plan", "ask-permissions", "free"],
174
+ here: ["branch", "plan", "ask-permissions", "free"],
175
+ teleport: ["plan", "new", "no-launch"],
176
+ adopt: ["branch", "plan", "resume", "new", "ask-permissions", "free"],
177
+ };
178
+ /**
179
+ * Checks `--cloud`, `--here`, `--teleport` and `--adopt`: at most one, and none with the
180
+ * flags it has no use for (only `--teleport` opens Claude here, and it needs a branch).
181
+ * @param flags - The flags.
182
+ * @throws UsageError if they do not fit.
183
+ */
184
+ function cloudArgs(flags) {
185
+ const ways = Object.keys(EXCLUSIVE).filter((way) => flags[way] === true);
186
+ if (ways.length > 1)
187
+ throw new UsageError(`use one of --${ways.join(", --")}, not several.`);
188
+ const [way] = ways;
189
+ if (way === undefined)
190
+ return;
191
+ const other = EXCLUSIVE[way].find((flag) => flags[flag] !== undefined && flags[flag] !== false);
192
+ if (other !== undefined)
193
+ throw new UsageError(`--${way} does not go with --${other}.`);
194
+ }
195
+ /**
196
+ * Runs the CLI.
197
+ * @param argv - Arguments without `node` or the script.
198
+ * @param out - Where to write.
199
+ * @param cwd - Where it runs.
200
+ * @returns The exit code.
201
+ * @throws UsageError, ConfigError or GitHubError, which the entry point handles.
202
+ */
203
+ export async function main(argv, out, cwd) {
204
+ const dashes = argv.indexOf("--");
205
+ const command = dashes === -1 ? null : argv.slice(dashes + 1);
206
+ let parsed;
207
+ try {
208
+ parsed = parseArgs({ args: dashes === -1 ? argv : argv.slice(0, dashes), options: OPTIONS, allowPositionals: true, strict: true });
209
+ }
210
+ catch (error) {
211
+ throw new UsageError(error instanceof Error ? error.message : String(error));
212
+ }
213
+ const { values: flags, positionals } = parsed;
214
+ if (flags.help) {
215
+ out.stdout(`${USAGE}\n`);
216
+ return 0;
217
+ }
218
+ const [group, sub, ...rest] = positionals;
219
+ if (command !== null && (group !== "pr" || sub !== "worktree"))
220
+ throw new UsageError("-- only goes with pr worktree.");
221
+ if (group === "pr")
222
+ return runPr(sub, rest, flags, { out, cwd, command });
223
+ if (group !== undefined && SETUP_COMMANDS.includes(group)) {
224
+ ownFlags(group, flags);
225
+ return runSetupCommand(group, flags, [sub, ...rest].filter((item) => item !== undefined), { out, cwd });
226
+ }
227
+ if (group === "adapter" || group === "hook") {
228
+ const commands = group === "adapter" ? ADAPTER_COMMANDS : HOOK_COMMANDS;
229
+ if (sub === undefined || !commands.includes(sub))
230
+ throw new UsageError(`${group} expects ${commands.join(", ")}.`);
231
+ ownFlags(`${group} ${sub}`, flags);
232
+ if (group === "hook") {
233
+ if (rest.length > 0)
234
+ throw new UsageError(`hook ${sub} takes no arguments.`);
235
+ return runHook(sub, { out, cwd });
236
+ }
237
+ return runAdapter(sub, flags, rest, { out, cwd, repo: () => repoOf(flags.repo, loadConfig(cwd), cwd) });
238
+ }
239
+ return runIssue(group, sub, rest, flags, { out, cwd });
240
+ }
241
+ /**
242
+ * Runs `issue-flow pr worktree <n>`.
243
+ * @param sub - The word after `pr`.
244
+ * @param rest - The positionals after it.
245
+ * @param flags - The flags.
246
+ * @param run - Where it writes and runs, and the command after `--` (null without `--`).
247
+ * @returns The exit code.
248
+ * @throws UsageError if the arguments do not fit.
249
+ */
250
+ async function runPr(sub, rest, flags, run) {
251
+ if (sub !== "worktree")
252
+ throw new UsageError("pr expects worktree.");
253
+ ownFlags("pr worktree", flags);
254
+ const [text, ...extra] = rest;
255
+ if (text === undefined || extra.length > 0 || !NUMBER.test(text))
256
+ throw new UsageError("pr worktree needs one PR number.");
257
+ const config = loadConfig(run.cwd);
258
+ return prWorktree({ repo: repoOf(flags.repo, config, run.cwd), config, cwd: run.cwd, number: Number(text), setup: !(flags["no-setup"] ?? false), command: run.command }, run.out);
259
+ }
260
+ /**
261
+ * Runs the `issue`, `worktree` and `session` commands.
262
+ * @param group - The first word.
263
+ * @param command - The second word.
264
+ * @param rest - The positionals after them.
265
+ * @param flags - The flags.
266
+ * @param run - Where it writes and runs.
267
+ * @returns The exit code.
268
+ * @throws UsageError, ConfigError or GitHubError.
269
+ */
270
+ async function runIssue(group, command, rest, flags, run) {
271
+ const { out, cwd } = run;
272
+ const positionals = [group, command, ...rest].filter((item) => item !== undefined);
273
+ if (group === "session") {
274
+ ownFlags("session", flags);
275
+ if (positionals.length > 1)
276
+ throw new UsageError("session takes no arguments.");
277
+ return runSession(out, cwd);
278
+ }
279
+ if (group === "worktree") {
280
+ if (command === undefined || !WORKTREE_COMMANDS.includes(command))
281
+ throw new UsageError(`worktree expects ${WORKTREE_COMMANDS.join(", ")}.`);
282
+ ownFlags(`worktree ${command}`, flags);
283
+ if (rest.length > 0)
284
+ throw new UsageError(`worktree ${command} takes no arguments.`);
285
+ const config = loadConfig(cwd);
286
+ return runWorktree(command, flags, { out, cwd, config, repo: () => repoOf(flags.repo, config, cwd) });
287
+ }
288
+ if (group !== "issue" || (command !== "branch" && command !== "start"))
289
+ throw new UsageError(group === undefined ? "missing the command." : `unknown command: ${positionals.join(" ")}`);
290
+ ownFlags(command, flags);
291
+ if (flags.yes && flags["dry-run"])
292
+ throw new UsageError("use --yes or --dry-run, not both.");
293
+ const namer = namerOf(flags.namer);
294
+ if (command === "start") {
295
+ const number = startArgs(flags, rest);
296
+ const permissions = permissionsOf(flags);
297
+ const config = loadConfig(cwd);
298
+ return issueStart({
299
+ repo: repoOf(flags.repo, config, cwd),
300
+ config,
301
+ cwd,
302
+ number,
303
+ ...(namer === undefined ? {} : { namer }),
304
+ dryRun: flags["dry-run"] ?? false,
305
+ yes: flags.yes ?? false,
306
+ resume: flags.resume ?? null,
307
+ forceNew: flags.new ?? false,
308
+ branchMode: flags.branch ?? false,
309
+ launch: !(flags["no-launch"] ?? false),
310
+ permissions,
311
+ plan: flags.plan ?? false,
312
+ cloud: flags.cloud ?? false,
313
+ here: flags.here ?? false,
314
+ teleport: flags.teleport ?? false,
315
+ adopt: flags.adopt ?? false,
316
+ remoteSession: process.env.CLAUDE_CODE_REMOTE_SESSION_ID || null,
317
+ }, out);
318
+ }
319
+ if (flags.limit !== undefined && !POSITIVE.test(flags.limit))
320
+ throw new UsageError(`--limit expects a positive number: ${flags.limit}`);
321
+ if (flags.missing && rest.length > 0)
322
+ throw new UsageError("--missing takes no issue numbers.");
323
+ if (!flags.missing && (rest.length === 0 || !rest.every((text) => NUMBER.test(text))))
324
+ throw new UsageError("issue branch needs issue numbers, or --missing.");
325
+ if (!flags.missing && (flags.yes || flags["dry-run"] || flags.limit !== undefined))
326
+ throw new UsageError("--yes, --dry-run and --limit go with --missing.");
327
+ const config = loadConfig(cwd);
328
+ const options = {
329
+ repo: repoOf(flags.repo, config, cwd),
330
+ config,
331
+ cwd,
332
+ ...(namer === undefined ? {} : { namer }),
333
+ json: flags.json ?? false,
334
+ ...(flags.limit === undefined ? {} : { limit: Number(flags.limit) }),
335
+ yes: flags.yes ?? false,
336
+ dryRun: flags["dry-run"] ?? false,
337
+ };
338
+ return flags.missing ? branchMissing(options, out) : branchOf(rest.map(Number), options, out);
339
+ }
340
+ const out = processOutput();
341
+ main(process.argv.slice(2), out, process.cwd()).then((code) => {
342
+ process.exitCode = code;
343
+ }, (error) => {
344
+ const usage = error instanceof UsageError;
345
+ const config = error instanceof ConfigError;
346
+ out.stderr(`issue-flow: ${error instanceof Error ? error.message : String(error)}\n${usage ? `${USAGE}\n` : ""}`);
347
+ process.exitCode = usage || config ? 2 : 1;
348
+ });
@@ -0,0 +1,49 @@
1
+ import { type FlowConfig } from "../config.js";
2
+ import { type Output } from "../output.js";
3
+ /** Orca's CLI, which knows which issue a workspace came from; `ISSUE_FLOW_ORCA_CLI` changes it. */
4
+ export declare const ORCA_CLI = "orca";
5
+ /** Options of `issue-flow adapter`. */
6
+ export interface AdapterOptions {
7
+ config: FlowConfig;
8
+ cwd: string;
9
+ only: string | null;
10
+ url: string | null;
11
+ repo: () => string;
12
+ }
13
+ /**
14
+ * The number of an issue of the repo by its URL; a slash, an anchor or a query may follow.
15
+ * @param url - The URL.
16
+ * @param repo - `owner/repo`.
17
+ * @returns The number, or null if it is not an issue of the repo.
18
+ * @example issueOfUrl("https://github.com/acme/widgets/issues/214#top", "acme/widgets") // 214
19
+ */
20
+ export declare function issueOfUrl(url: string, repo: string): number | null;
21
+ /**
22
+ * `adapter setup [--only <step>]`.
23
+ * @param options - The options.
24
+ * @param out - Where to write.
25
+ * @returns Always 0.
26
+ * @throws StartError outside git; UsageError if `--only` names no step.
27
+ * @example await adapterSetup(options, out)
28
+ */
29
+ export declare function adapterSetup(options: AdapterOptions, out: Output): Promise<number>;
30
+ /**
31
+ * `adapter issue [<url>]`.
32
+ * @param options - The options.
33
+ * @param out - Where to write.
34
+ * @returns The code of `issue start` with a URL, 0 without one.
35
+ * @throws StartError outside git or if the URL is not an issue of the repo.
36
+ * @example await adapterIssue({ ...options, url: "https://github.com/acme/widgets/issues/214" }, out)
37
+ */
38
+ export declare function adapterIssue(options: AdapterOptions, out: Output): Promise<number>;
39
+ /**
40
+ * `adapter archive`. With git's link broken (the main checkout moved, or its record of the
41
+ * worktree was deleted) it cannot tell a worktree, but it still frees the ports and the
42
+ * Docker of the folder, with the config of the main checkout Orca or Paseo name: without
43
+ * git it cannot be the main checkout, which they never archive.
44
+ * @param options - The options.
45
+ * @param out - Where to write.
46
+ * @returns Always 0.
47
+ * @example await adapterArchive(options, out)
48
+ */
49
+ export declare function adapterArchive(options: AdapterOptions, out: Output): Promise<number>;