@ramon-sg/issue-flow 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +297 -2
- package/dist/ask.d.ts +28 -0
- package/dist/ask.js +73 -0
- package/dist/branch-name.d.ts +113 -0
- package/dist/branch-name.js +226 -0
- package/dist/claude-namer.d.ts +51 -0
- package/dist/claude-namer.js +140 -0
- package/dist/cli-setup.d.ts +22 -0
- package/dist/cli-setup.js +26 -0
- package/dist/cli-worktree.d.ts +74 -0
- package/dist/cli-worktree.js +123 -0
- package/dist/cli.d.ts +120 -0
- package/dist/cli.js +344 -0
- package/dist/commands/adapter.d.ts +49 -0
- package/dist/commands/adapter.js +231 -0
- package/dist/commands/claude-hooks.d.ts +37 -0
- package/dist/commands/claude-hooks.js +162 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +90 -0
- package/dist/commands/init.d.ts +24 -0
- package/dist/commands/init.js +136 -0
- package/dist/commands/issue-branch.d.ts +36 -0
- package/dist/commands/issue-branch.js +182 -0
- package/dist/commands/issue-start.d.ts +11 -0
- package/dist/commands/issue-start.js +361 -0
- package/dist/commands/pr-worktree.d.ts +31 -0
- package/dist/commands/pr-worktree.js +122 -0
- package/dist/commands/session.d.ts +15 -0
- package/dist/commands/session.js +102 -0
- package/dist/commands/upgrade.d.ts +16 -0
- package/dist/commands/upgrade.js +85 -0
- package/dist/commands/worktree.d.ts +82 -0
- package/dist/commands/worktree.js +244 -0
- package/dist/config-base.d.ts +32 -0
- package/dist/config-base.js +47 -0
- package/dist/config.d.ts +63 -0
- package/dist/config.js +230 -0
- package/dist/docker.d.ts +53 -0
- package/dist/docker.js +153 -0
- package/dist/git.d.ts +130 -0
- package/dist/git.js +192 -0
- package/dist/issue-text.d.ts +128 -0
- package/dist/issue-text.js +210 -0
- package/dist/launch.d.ts +90 -0
- package/dist/launch.js +158 -0
- package/dist/output.d.ts +33 -0
- package/dist/output.js +37 -0
- package/dist/package-edits.d.ts +35 -0
- package/dist/package-edits.js +78 -0
- package/dist/plan-doc.d.ts +26 -0
- package/dist/plan-doc.js +93 -0
- package/dist/ports.d.ts +90 -0
- package/dist/ports.js +257 -0
- package/dist/propose.d.ts +42 -0
- package/dist/propose.js +111 -0
- package/dist/recipes.d.ts +43 -0
- package/dist/recipes.js +123 -0
- package/dist/session-lock.d.ts +32 -0
- package/dist/session-lock.js +69 -0
- package/dist/settings-hooks.d.ts +31 -0
- package/dist/settings-hooks.js +133 -0
- package/dist/setup-files.d.ts +48 -0
- package/dist/setup-files.js +91 -0
- package/dist/start-modes.d.ts +162 -0
- package/dist/start-modes.js +277 -0
- package/dist/start-place.d.ts +61 -0
- package/dist/start-place.js +301 -0
- package/dist/start-state.d.ts +72 -0
- package/dist/start-state.js +13 -0
- package/dist/start-summary.d.ts +42 -0
- package/dist/start-summary.js +217 -0
- package/dist/stop-apps.d.ts +14 -0
- package/dist/stop-apps.js +127 -0
- package/dist/usage.d.ts +5 -0
- package/dist/usage.js +54 -0
- package/dist/work-state.d.ts +51 -0
- package/dist/work-state.js +58 -0
- package/dist/worktree-config.d.ts +35 -0
- package/dist/worktree-config.js +139 -0
- package/dist/worktree-steps.d.ts +53 -0
- package/dist/worktree-steps.js +210 -0
- package/package.json +20 -4
- package/recipes/bun.json +5 -0
- package/recipes/direnv.json +17 -0
- package/recipes/docker-compose.json +30 -0
- package/recipes/npm.json +5 -0
- package/recipes/pnpm.json +5 -0
- package/recipes/staging-main.json +5 -0
- package/schema.json +281 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The hook events `init` registers. */
|
|
2
|
+
export declare const HOOK_EVENTS: string[];
|
|
3
|
+
/**
|
|
4
|
+
* The command of one of the hooks, at a version.
|
|
5
|
+
* @param event - The hook event.
|
|
6
|
+
* @param version - This package's version.
|
|
7
|
+
* @returns The command.
|
|
8
|
+
* @example hookCommand("WorktreeCreate", "0.1.0") // "npx --yes @ramon-sg/issue-flow@0.1.0 hook worktree-create"
|
|
9
|
+
*/
|
|
10
|
+
export declare function hookCommand(event: string, version: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Registers our hooks at a version: moves the version of the ones already there and adds
|
|
13
|
+
* the missing ones. In `SessionStart` it drops a `claude-cloud session` hook that is not
|
|
14
|
+
* ours.
|
|
15
|
+
* @param settings - The settings, or null if there is no file.
|
|
16
|
+
* @param version - This package's version.
|
|
17
|
+
* @returns The new settings and what changed, one line each.
|
|
18
|
+
* @throws SetupError if `hooks` or an event of it is not what Claude Code expects.
|
|
19
|
+
* @example registerHooks(null, "0.1.0").changes // ["added the SessionStart hook", …]
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerHooks(settings: Record<string, unknown> | null, version: string): {
|
|
22
|
+
settings: Record<string, unknown>;
|
|
23
|
+
changes: string[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The version of each of our hooks in the settings.
|
|
27
|
+
* @param settings - The settings, or null.
|
|
28
|
+
* @returns Event to version, or null if the hook is missing.
|
|
29
|
+
* @example hookVersions(settings).SessionStart // "0.1.0"
|
|
30
|
+
*/
|
|
31
|
+
export declare function hookVersions(settings: Record<string, unknown> | null): Record<string, string | null>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The Claude Code hooks `init` registers in `.claude/settings.json`: `SessionStart`
|
|
3
|
+
* (`issue-flow session`) and `WorktreeCreate` and `WorktreeRemove` (`issue-flow hook …`), each
|
|
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`
|
|
6
|
+
* command names `claude-cloud session`, which `issue-flow session` runs in the cloud: that
|
|
7
|
+
* way `claude-cloud init` sees it and does not add its own, and a hook of `claude-cloud`
|
|
8
|
+
* already there is dropped, so it does not run twice.
|
|
9
|
+
*/
|
|
10
|
+
import { isObject } from "./config-base.js";
|
|
11
|
+
import { PACKAGE, SETTINGS_FILE, SetupError } from "./setup-files.js";
|
|
12
|
+
/** Seconds each hook may take: the setup steps can install dependencies. */
|
|
13
|
+
const HOOK_TIMEOUT_SECONDS = 600;
|
|
14
|
+
/** What `claude-cloud init` looks for in a `SessionStart` command. */
|
|
15
|
+
const CLOUD_MARKER = "claude-cloud session";
|
|
16
|
+
/** The subcommand of each hook, and what follows it. */
|
|
17
|
+
const HOOK_COMMANDS = {
|
|
18
|
+
SessionStart: { sub: "session", note: ` # runs ${CLOUD_MARKER} in the cloud` },
|
|
19
|
+
WorktreeCreate: { sub: "hook worktree-create", note: "" },
|
|
20
|
+
WorktreeRemove: { sub: "hook worktree-remove", note: "" },
|
|
21
|
+
};
|
|
22
|
+
/** The hook events `init` registers. */
|
|
23
|
+
export const HOOK_EVENTS = Object.keys(HOOK_COMMANDS);
|
|
24
|
+
/**
|
|
25
|
+
* The command of one of the hooks, at a version.
|
|
26
|
+
* @param event - The hook event.
|
|
27
|
+
* @param version - This package's version.
|
|
28
|
+
* @returns The command.
|
|
29
|
+
* @example hookCommand("WorktreeCreate", "0.1.0") // "npx --yes @ramon-sg/issue-flow@0.1.0 hook worktree-create"
|
|
30
|
+
*/
|
|
31
|
+
export function hookCommand(event, version) {
|
|
32
|
+
const hook = HOOK_COMMANDS[event];
|
|
33
|
+
if (hook === undefined)
|
|
34
|
+
throw new SetupError(`unknown hook event ${event}.`);
|
|
35
|
+
return `npx --yes ${PACKAGE}@${version} ${hook.sub}${hook.note}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The version of our hook in a command, or null if the command is not ours for that event.
|
|
39
|
+
* @param command - The command.
|
|
40
|
+
* @param event - The event.
|
|
41
|
+
* @returns The version.
|
|
42
|
+
*/
|
|
43
|
+
function ourVersion(command, event) {
|
|
44
|
+
const sub = HOOK_COMMANDS[event]?.sub;
|
|
45
|
+
if (typeof command !== "string" || sub === undefined)
|
|
46
|
+
return null;
|
|
47
|
+
const match = new RegExp(`${PACKAGE.replace("/", "\\/")}@(\\S+) ${sub}(?:\\s|$)`).exec(command);
|
|
48
|
+
return match?.[1] ?? null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The entries of an event in the settings, checked.
|
|
52
|
+
* @param hooks - The `hooks` object.
|
|
53
|
+
* @param event - The event.
|
|
54
|
+
* @returns Its entries (a new array if there were none).
|
|
55
|
+
* @throws SetupError if they are not a list of objects.
|
|
56
|
+
*/
|
|
57
|
+
function entriesOf(hooks, event) {
|
|
58
|
+
const entries = hooks[event] ?? [];
|
|
59
|
+
if (!Array.isArray(entries) || !entries.every(isObject))
|
|
60
|
+
throw new SetupError(`${SETTINGS_FILE}: hooks.${event} is not a list of entries; fix it and run again.`);
|
|
61
|
+
return entries;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The hooks of an entry.
|
|
65
|
+
* @param entry - The entry.
|
|
66
|
+
* @returns Its hooks, or an empty list.
|
|
67
|
+
*/
|
|
68
|
+
function hooksOf(entry) {
|
|
69
|
+
return Array.isArray(entry.hooks) ? entry.hooks.filter(isObject) : [];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Registers our hooks at a version: moves the version of the ones already there and adds
|
|
73
|
+
* the missing ones. In `SessionStart` it drops a `claude-cloud session` hook that is not
|
|
74
|
+
* ours.
|
|
75
|
+
* @param settings - The settings, or null if there is no file.
|
|
76
|
+
* @param version - This package's version.
|
|
77
|
+
* @returns The new settings and what changed, one line each.
|
|
78
|
+
* @throws SetupError if `hooks` or an event of it is not what Claude Code expects.
|
|
79
|
+
* @example registerHooks(null, "0.1.0").changes // ["added the SessionStart hook", …]
|
|
80
|
+
*/
|
|
81
|
+
export function registerHooks(settings, version) {
|
|
82
|
+
const next = { ...(settings ?? {}) };
|
|
83
|
+
const hooks = next.hooks ?? {};
|
|
84
|
+
if (!isObject(hooks))
|
|
85
|
+
throw new SetupError(`${SETTINGS_FILE}: "hooks" is not an object; fix it and run again.`);
|
|
86
|
+
next.hooks = { ...hooks };
|
|
87
|
+
const changes = [];
|
|
88
|
+
for (const event of HOOK_EVENTS) {
|
|
89
|
+
const entries = entriesOf(hooks, event);
|
|
90
|
+
const command = hookCommand(event, version);
|
|
91
|
+
let found = false;
|
|
92
|
+
const kept = entries.map((entry) => {
|
|
93
|
+
const inner = hooksOf(entry).flatMap((hook) => {
|
|
94
|
+
const current = ourVersion(hook.command, event);
|
|
95
|
+
if (current !== null) {
|
|
96
|
+
found = true;
|
|
97
|
+
if (hook.command === command)
|
|
98
|
+
return [hook];
|
|
99
|
+
changes.push(`moved the ${event} hook from ${current} to ${version}`);
|
|
100
|
+
return [{ ...hook, command }];
|
|
101
|
+
}
|
|
102
|
+
if (event === "SessionStart" && typeof hook.command === "string" && hook.command.includes(CLOUD_MARKER)) {
|
|
103
|
+
changes.push(`dropped the ${CLOUD_MARKER} SessionStart hook: issue-flow session runs it`);
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
return [hook];
|
|
107
|
+
});
|
|
108
|
+
return Array.isArray(entry.hooks) ? { ...entry, hooks: inner } : entry;
|
|
109
|
+
}).filter((entry) => !Array.isArray(entry.hooks) || entry.hooks.length > 0);
|
|
110
|
+
if (!found) {
|
|
111
|
+
kept.push({ hooks: [{ type: "command", command, timeout: HOOK_TIMEOUT_SECONDS }] });
|
|
112
|
+
changes.push(`added the ${event} hook`);
|
|
113
|
+
}
|
|
114
|
+
next.hooks[event] = kept;
|
|
115
|
+
}
|
|
116
|
+
return { settings: next, changes };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The version of each of our hooks in the settings.
|
|
120
|
+
* @param settings - The settings, or null.
|
|
121
|
+
* @returns Event to version, or null if the hook is missing.
|
|
122
|
+
* @example hookVersions(settings).SessionStart // "0.1.0"
|
|
123
|
+
*/
|
|
124
|
+
export function hookVersions(settings) {
|
|
125
|
+
const hooks = isObject(settings?.hooks) ? settings.hooks : {};
|
|
126
|
+
const versions = {};
|
|
127
|
+
for (const event of HOOK_EVENTS) {
|
|
128
|
+
const entries = Array.isArray(hooks[event]) ? hooks[event].filter(isObject) : [];
|
|
129
|
+
const commands = entries.flatMap((entry) => hooksOf(entry).map((hook) => hook.command));
|
|
130
|
+
versions[event] = commands.map((command) => ourVersion(command, event)).find((found) => found !== null) ?? null;
|
|
131
|
+
}
|
|
132
|
+
return versions;
|
|
133
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** This package's name on npm. */
|
|
2
|
+
export declare const PACKAGE = "@ramon-sg/issue-flow";
|
|
3
|
+
/** The GitHub repo of this package, whose tags hold the config's schema of each version. */
|
|
4
|
+
export declare const FLOW_REPO = "ramon-sg/issue-flow";
|
|
5
|
+
/** Claude Code's settings of the repo. */
|
|
6
|
+
export declare const SETTINGS_FILE = ".claude/settings.json";
|
|
7
|
+
/** Error of `init`, `doctor` or `upgrade`: exits 1 with the message. */
|
|
8
|
+
export declare class SetupError extends Error {
|
|
9
|
+
}
|
|
10
|
+
/** The versions this package pins in the repo. */
|
|
11
|
+
export interface OwnVersions {
|
|
12
|
+
issueFlow: string;
|
|
13
|
+
githubRest: string;
|
|
14
|
+
skillsManager: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
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`.
|
|
19
|
+
* @returns The versions.
|
|
20
|
+
* @throws SetupError if the package.json does not have them.
|
|
21
|
+
* @example ownVersions().issueFlow // "0.1.0"
|
|
22
|
+
*/
|
|
23
|
+
export declare function ownVersions(): OwnVersions;
|
|
24
|
+
/**
|
|
25
|
+
* Reads a JSON object of the repo.
|
|
26
|
+
* @param root - The repo.
|
|
27
|
+
* @param path - The file, relative to the repo.
|
|
28
|
+
* @returns The object, or null if the file does not exist.
|
|
29
|
+
* @throws SetupError if it is not valid JSON or not an object: it is never overwritten then.
|
|
30
|
+
* @example readJson(root, ".claude/settings.json")
|
|
31
|
+
*/
|
|
32
|
+
export declare function readJson(root: string, path: string): Record<string, unknown> | null;
|
|
33
|
+
/**
|
|
34
|
+
* Writes a JSON file of the repo, indented by two spaces, creating its folder.
|
|
35
|
+
* @param root - The repo.
|
|
36
|
+
* @param path - The file, relative to the repo.
|
|
37
|
+
* @param value - What to write.
|
|
38
|
+
* @example writeJson(root, ".agents/flow.json", { baseBranch: "main" })
|
|
39
|
+
*/
|
|
40
|
+
export declare function writeJson(root: string, path: string, value: unknown): void;
|
|
41
|
+
/**
|
|
42
|
+
* The repo's package manager: the `packageManager` field, then its lockfile, then npm.
|
|
43
|
+
* @param root - The repo.
|
|
44
|
+
* @param pkg - Its package.json, or null.
|
|
45
|
+
* @returns `npm`, `pnpm`, `bun` or `yarn`.
|
|
46
|
+
* @example packageManager(root, { packageManager: "pnpm@9.0.0" }) // "pnpm"
|
|
47
|
+
*/
|
|
48
|
+
export declare function packageManager(root: string, pkg: Record<string, unknown> | null): string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file What `init`, `doctor` and `upgrade` share: this package's own versions, the JSON
|
|
3
|
+
* files of the repo they read and write, and its package manager. Every package goes by its
|
|
4
|
+
* scoped name: the unscoped `issue-flow`, `github-rest` and `skills-manager` on npm belong
|
|
5
|
+
* to other people.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { isObject } from "./config-base.js";
|
|
11
|
+
/** This package's name on npm. */
|
|
12
|
+
export const PACKAGE = "@ramon-sg/issue-flow";
|
|
13
|
+
/** The GitHub repo of this package, whose tags hold the config's schema of each version. */
|
|
14
|
+
export const FLOW_REPO = "ramon-sg/issue-flow";
|
|
15
|
+
/** Claude Code's settings of the repo. */
|
|
16
|
+
export const SETTINGS_FILE = ".claude/settings.json";
|
|
17
|
+
/** Error of `init`, `doctor` or `upgrade`: exits 1 with the message. */
|
|
18
|
+
export class SetupError extends Error {
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
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`.
|
|
23
|
+
* @returns The versions.
|
|
24
|
+
* @throws SetupError if the package.json does not have them.
|
|
25
|
+
* @example ownVersions().issueFlow // "0.1.0"
|
|
26
|
+
*/
|
|
27
|
+
export function ownVersions() {
|
|
28
|
+
const file = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
29
|
+
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 };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Reads a JSON object of the repo.
|
|
38
|
+
* @param root - The repo.
|
|
39
|
+
* @param path - The file, relative to the repo.
|
|
40
|
+
* @returns The object, or null if the file does not exist.
|
|
41
|
+
* @throws SetupError if it is not valid JSON or not an object: it is never overwritten then.
|
|
42
|
+
* @example readJson(root, ".claude/settings.json")
|
|
43
|
+
*/
|
|
44
|
+
export function readJson(root, path) {
|
|
45
|
+
const file = join(root, path);
|
|
46
|
+
if (!existsSync(file))
|
|
47
|
+
return null;
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = JSON.parse(readFileSync(file, "utf8"));
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
throw new SetupError(`${path} is not valid JSON (${error instanceof Error ? error.message : String(error)}); fix it and run again.`);
|
|
54
|
+
}
|
|
55
|
+
if (!isObject(value))
|
|
56
|
+
throw new SetupError(`${path} is not a JSON object; fix it and run again.`);
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Writes a JSON file of the repo, indented by two spaces, creating its folder.
|
|
61
|
+
* @param root - The repo.
|
|
62
|
+
* @param path - The file, relative to the repo.
|
|
63
|
+
* @param value - What to write.
|
|
64
|
+
* @example writeJson(root, ".agents/flow.json", { baseBranch: "main" })
|
|
65
|
+
*/
|
|
66
|
+
export function writeJson(root, path, value) {
|
|
67
|
+
const file = join(root, path);
|
|
68
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
69
|
+
writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
|
|
70
|
+
}
|
|
71
|
+
/** The lockfile of each package manager, in the order they are looked for. */
|
|
72
|
+
const LOCKFILES = [
|
|
73
|
+
["bun.lock", "bun"],
|
|
74
|
+
["bun.lockb", "bun"],
|
|
75
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
76
|
+
["yarn.lock", "yarn"],
|
|
77
|
+
["package-lock.json", "npm"],
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* The repo's package manager: the `packageManager` field, then its lockfile, then npm.
|
|
81
|
+
* @param root - The repo.
|
|
82
|
+
* @param pkg - Its package.json, or null.
|
|
83
|
+
* @returns `npm`, `pnpm`, `bun` or `yarn`.
|
|
84
|
+
* @example packageManager(root, { packageManager: "pnpm@9.0.0" }) // "pnpm"
|
|
85
|
+
*/
|
|
86
|
+
export function packageManager(root, pkg) {
|
|
87
|
+
const field = typeof pkg?.packageManager === "string" ? pkg.packageManager.split("@")[0] : undefined;
|
|
88
|
+
if (field !== undefined && field !== "")
|
|
89
|
+
return field;
|
|
90
|
+
return LOCKFILES.find(([file]) => existsSync(join(root, file)))?.[1] ?? "npm";
|
|
91
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { type StartMode } from "./issue-text.js";
|
|
2
|
+
import { type Output } from "./output.js";
|
|
3
|
+
import { type Adopted, type StartState } from "./start-state.js";
|
|
4
|
+
import type { WorkState } from "./work-state.js";
|
|
5
|
+
/**
|
|
6
|
+
* The fetch of origin. In a cloud session the clone has one branch, so `--here` passes
|
|
7
|
+
* the refspec: without it neither the base nor the issue's branch would come.
|
|
8
|
+
* @param mainRoot - The main checkout.
|
|
9
|
+
* @param here - Whether it runs with `--here`.
|
|
10
|
+
* @returns True if it worked.
|
|
11
|
+
* @example fetchOrigin(root, false)
|
|
12
|
+
*/
|
|
13
|
+
export declare function fetchOrigin(mainRoot: string, here: boolean): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* The branch of the worktree `--adopt` runs in, and whether it is fresh: not in origin and
|
|
16
|
+
* with no commits of its own, so the tool that made the worktree just made it.
|
|
17
|
+
* @param startRoot - The checkout where it runs.
|
|
18
|
+
* @param mainRoot - The main checkout.
|
|
19
|
+
* @param base - The base branch.
|
|
20
|
+
* @returns The branch and whether it is fresh.
|
|
21
|
+
* @throws StartError in the main checkout or on a detached HEAD.
|
|
22
|
+
* @example readAdopted(wt, root, "main") // { current: "ramon/x", fresh: true }
|
|
23
|
+
*/
|
|
24
|
+
export declare function readAdopted(startRoot: string, mainRoot: string, base: string): Adopted;
|
|
25
|
+
/**
|
|
26
|
+
* The issue's work without the adopted branch when it is fresh: it is neither earlier work
|
|
27
|
+
* nor a used name.
|
|
28
|
+
* @param work - The work.
|
|
29
|
+
* @param adopted - The adopted branch.
|
|
30
|
+
* @returns The work.
|
|
31
|
+
* @example withoutFresh(work, { current: "ramon/x", fresh: true })
|
|
32
|
+
*/
|
|
33
|
+
export declare function withoutFresh(work: WorkState, adopted: Adopted): WorkState;
|
|
34
|
+
/**
|
|
35
|
+
* Which branch `--adopt` takes. A live branch of the issue that the worktree is on is
|
|
36
|
+
* resumed. A fresh branch with the issue's number is the new branch as it is, with other
|
|
37
|
+
* branches or without (Paseo lets you edit the name; Orca makes one without the number).
|
|
38
|
+
* Otherwise, with no live branches the branch is renamed to the proposed one, and with
|
|
39
|
+
* some it stops: the issue goes on elsewhere.
|
|
40
|
+
* @param mainRoot - The main checkout.
|
|
41
|
+
* @param work - The issue's work.
|
|
42
|
+
* @param adopted - The adopted branch.
|
|
43
|
+
* @param number - The issue.
|
|
44
|
+
* @returns The branch resumed, or the new one already named, or neither (propose one).
|
|
45
|
+
* @throws StartError if the issue has live branches elsewhere.
|
|
46
|
+
* @example chooseAdopted(root, work, adopted, 214) // { found: null, named: "fix/214-x" }
|
|
47
|
+
*/
|
|
48
|
+
export declare function chooseAdopted(mainRoot: string, work: WorkState, adopted: Adopted, number: number): {
|
|
49
|
+
found: string | null;
|
|
50
|
+
named: string | null;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* The place of `--adopt`: the worktree where it runs. If it is already on the branch there
|
|
54
|
+
* is nothing to check (an `--adopt` repeated or resumed); if not, it must be fresh and
|
|
55
|
+
* without uncommitted changes, so a branch with other work is never renamed. Untracked
|
|
56
|
+
* files do not count: the tool may leave its own.
|
|
57
|
+
* @param state - What it knows: the branch, the start checkout and the adopted branch.
|
|
58
|
+
* @returns The mode and the folder.
|
|
59
|
+
* @throws StartError if the worktree is not fresh.
|
|
60
|
+
* @example placeAdopted({ branch, startRoot, adopted })
|
|
61
|
+
*/
|
|
62
|
+
export declare function placeAdopted(state: {
|
|
63
|
+
branch: string;
|
|
64
|
+
startRoot: string;
|
|
65
|
+
adopted: Adopted;
|
|
66
|
+
}): {
|
|
67
|
+
mode: StartMode;
|
|
68
|
+
workDir: string;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Renames the adopted worktree's branch to the issue's (to the same name, it does
|
|
72
|
+
* nothing). The tool that made the worktree knows it by its path, so the rename does not
|
|
73
|
+
* unlink it.
|
|
74
|
+
* @param state - The start.
|
|
75
|
+
* @throws StartError if git cannot.
|
|
76
|
+
* @example renameAdopted(state)
|
|
77
|
+
*/
|
|
78
|
+
export declare function renameAdopted(state: StartState): void;
|
|
79
|
+
/**
|
|
80
|
+
* The place of `--here`: the checkout where it runs. Stops if the branch is checked out in
|
|
81
|
+
* another one, or this one has uncommitted changes (untracked files do not count: the
|
|
82
|
+
* cloud's hook leaves its own, and git says if one is in the way).
|
|
83
|
+
* @param mainRoot - The main checkout.
|
|
84
|
+
* @param startRoot - The checkout where it runs.
|
|
85
|
+
* @param branch - The branch.
|
|
86
|
+
* @param number - The issue.
|
|
87
|
+
* @returns The mode and the folder.
|
|
88
|
+
* @throws StartError if the place is taken.
|
|
89
|
+
* @example placeHere(root, root, "feat/42-x", 42)
|
|
90
|
+
*/
|
|
91
|
+
export declare function placeHere(mainRoot: string, startRoot: string, branch: string, number: number): {
|
|
92
|
+
mode: StartMode;
|
|
93
|
+
workDir: string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The place of `--cloud`: the cloud session does the work, launched from the main
|
|
97
|
+
* checkout. Stops if the branch is checked out, or exists only locally, where the cloud
|
|
98
|
+
* would not see it.
|
|
99
|
+
* @param mainRoot - The main checkout.
|
|
100
|
+
* @param branch - The branch.
|
|
101
|
+
* @param found - The branch being resumed, or null.
|
|
102
|
+
* @param number - The issue.
|
|
103
|
+
* @returns The mode and the folder.
|
|
104
|
+
* @throws StartError if the cloud cannot take the branch.
|
|
105
|
+
* @example placeCloud(root, "feat/42-x", null, 42)
|
|
106
|
+
*/
|
|
107
|
+
export declare function placeCloud(mainRoot: string, branch: string, found: string | null, number: number): {
|
|
108
|
+
mode: StartMode;
|
|
109
|
+
workDir: string;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* With `--here`, leaves the checkout on the branch without needing a local base: a new one
|
|
113
|
+
* comes from `origin/<base>`, an existing one from `origin/<branch>` (the fetch of every
|
|
114
|
+
* branch already brought it). A branch missing from origin is only local, somewhere else;
|
|
115
|
+
* a local branch with commits origin lacks stops it, so `-B` does not overwrite them.
|
|
116
|
+
* @param state - The start.
|
|
117
|
+
* @throws StartError if git cannot or the branch cannot be brought.
|
|
118
|
+
* @example prepareHere(state)
|
|
119
|
+
*/
|
|
120
|
+
export declare function prepareHere(state: StartState): void;
|
|
121
|
+
/**
|
|
122
|
+
* The cloud session noted in the PR's body, or null (no PR, none noted, or the PR cannot
|
|
123
|
+
* be read).
|
|
124
|
+
* @param repo - The repo.
|
|
125
|
+
* @param pr - The PR, or null.
|
|
126
|
+
* @returns The id, or null.
|
|
127
|
+
* @example await prCloudSession("acme/widgets", 7)
|
|
128
|
+
*/
|
|
129
|
+
export declare function prCloudSession(repo: string, pr: number | null): Promise<string | null>;
|
|
130
|
+
/**
|
|
131
|
+
* The end of `--here`: notes this cloud session in the PR (if it runs in one and the PR
|
|
132
|
+
* does not have it yet), so `--teleport` finds it from any machine, and prints the
|
|
133
|
+
* `Prompt:` line the skill follows. It does not open Claude: it already runs in one.
|
|
134
|
+
* @param state - The start.
|
|
135
|
+
* @param pr - The PR.
|
|
136
|
+
* @param prompt - What the session goes on with.
|
|
137
|
+
* @example await finishHere(state, 900, prompt)
|
|
138
|
+
*/
|
|
139
|
+
export declare function finishHere(state: StartState, pr: number, prompt: string): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* The flags with which the cloud session picks the branch: `--new` if the issue has live
|
|
142
|
+
* branches and a new one was chosen here (`--new`, or `n` in the terminal), `--resume
|
|
143
|
+
* <branch>` if one of several was chosen. With one live branch resumed, none: the session
|
|
144
|
+
* resumes it.
|
|
145
|
+
* @param state - The start.
|
|
146
|
+
* @returns The flags, with a space before each.
|
|
147
|
+
* @throws StartError if the branch has characters a shell would read (anyone who can push
|
|
148
|
+
* names branches).
|
|
149
|
+
* @example cloudFlags(state) // " --resume fix/214-old"
|
|
150
|
+
*/
|
|
151
|
+
export declare function cloudFlags(state: StartState): string;
|
|
152
|
+
/**
|
|
153
|
+
* `--cloud` after the yes: opens the cloud session with the skill that starts the issue
|
|
154
|
+
* there, in the foreground and with the terminal (`claude --cloud` needs one), from the
|
|
155
|
+
* main checkout. Without `claude` or with `--no-launch`, says how to open it.
|
|
156
|
+
* @param state - The start.
|
|
157
|
+
* @param out - Where to write.
|
|
158
|
+
* @returns The exit code.
|
|
159
|
+
* @throws StartError if the session does not open.
|
|
160
|
+
* @example launchCloud(state, out)
|
|
161
|
+
*/
|
|
162
|
+
export declare function launchCloud(state: StartState, out: Output): number;
|