agent-kitchen 0.7.0 → 0.9.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/README.md +10 -7
- package/dist/cli.js +13 -9
- package/dist/commands/sync.d.ts +6 -3
- package/dist/commands/sync.js +139 -58
- package/dist/commit-texts.js +1 -1
- package/dist/menu.d.ts +6 -8
- package/dist/menu.js +13 -18
- package/dist/pr-clone.d.ts +68 -0
- package/dist/pr-clone.js +254 -0
- package/dist/progress.d.ts +6 -6
- package/dist/progress.js +8 -8
- package/dist/repo.d.ts +7 -0
- package/dist/repo.js +10 -0
- package/dist/texts/catalog.d.ts +10 -2
- package/dist/texts/es.js +8 -1
- package/package.json +1 -1
- package/dist/git-finish.d.ts +0 -53
- package/dist/git-finish.js +0 -252
package/dist/pr-clone.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file What `apply --pr` does with git. It fetches origin and clones the repo into a temporary
|
|
3
|
+
* folder at `origin/<base>`, so the steps see what the base has today and the person's checkout
|
|
4
|
+
* is never touched: not its branch, its files, nor its node_modules. A clone and not a worktree,
|
|
5
|
+
* because `issue-flow apply` refuses to run in a worktree. The clone shares the repo's objects
|
|
6
|
+
* (`--shared`), points its origin where the repo's does, and commits with the repo's hooks,
|
|
7
|
+
* identity and signing. The commit goes from the clone into the repo's objects and the repo
|
|
8
|
+
* pushes it straight to a new branch of origin, with its own credentials, so no local branch
|
|
9
|
+
* is made. Then github-rest opens the PR in draft. git runs its hooks; nothing here skips them.
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import { existsSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
14
|
+
import { isAbsolute, join, relative } from "node:path";
|
|
15
|
+
import { COMMIT_TITLE as TITLE, prBody } from "./commit-texts.js";
|
|
16
|
+
import { KitError } from "./output.js";
|
|
17
|
+
import { FLOW_CONFIG } from "./piece.js";
|
|
18
|
+
import { dependencyVersion, readJson } from "./repo.js";
|
|
19
|
+
import { captureNpxWithout, isRelease, npmLatest } from "./tools.js";
|
|
20
|
+
/** github-rest on npm, which opens the PR (REST, never gh's GraphQL). */
|
|
21
|
+
const GITHUB_REST = "@ramon-sg/github-rest";
|
|
22
|
+
/** The settings of the repo a commit made in the clone needs: who signs it and how. */
|
|
23
|
+
const IDENTITY = ["user.name", "user.email", "user.signingkey", "commit.gpgsign", "gpg.format", "gpg.program", "gpg.openpgp.program", "gpg.x509.program", "gpg.ssh.program", "gpg.ssh.allowedsignersfile"];
|
|
24
|
+
/**
|
|
25
|
+
* Runs git in a folder.
|
|
26
|
+
* @param cwd - The folder.
|
|
27
|
+
* @param args - The arguments.
|
|
28
|
+
* @returns How it went.
|
|
29
|
+
*/
|
|
30
|
+
function git(cwd, args) {
|
|
31
|
+
const result = spawnSync("git", args, { cwd, encoding: "utf8" });
|
|
32
|
+
return { ok: result.status === 0, out: (result.stdout ?? "").trimEnd(), err: (result.stderr ?? "").trim() };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The base branch, as issue-flow takes it: `baseBranch` of `.agents/flow.json`, or origin's
|
|
36
|
+
* default as git knows it, or `main`.
|
|
37
|
+
* @param root - The repo.
|
|
38
|
+
* @returns The branch.
|
|
39
|
+
* @example baseBranch(root) // "main"
|
|
40
|
+
*/
|
|
41
|
+
export function baseBranch(root) {
|
|
42
|
+
const flow = readJson(root, FLOW_CONFIG);
|
|
43
|
+
if (typeof flow?.baseBranch === "string" && flow.baseBranch !== "")
|
|
44
|
+
return flow.baseBranch;
|
|
45
|
+
const head = git(root, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
46
|
+
return head.ok && head.out.startsWith("origin/") ? head.out.slice("origin/".length) : "main";
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Origin's URL.
|
|
50
|
+
* @param root - The repo.
|
|
51
|
+
* @returns The URL, or null if the repo has no origin.
|
|
52
|
+
*/
|
|
53
|
+
export function originUrl(root) {
|
|
54
|
+
const url = git(root, ["remote", "get-url", "origin"]);
|
|
55
|
+
return url.ok && url.out !== "" ? url.out : null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The repo's other worktrees that live inside it, relative to it.
|
|
59
|
+
* @param root - The repo.
|
|
60
|
+
* @returns Their folders, like `.claude/worktrees/feat+1-x`.
|
|
61
|
+
*/
|
|
62
|
+
function innerWorktrees(root) {
|
|
63
|
+
const list = git(root, ["worktree", "list", "--porcelain"]);
|
|
64
|
+
if (!list.ok)
|
|
65
|
+
return [];
|
|
66
|
+
return list.out
|
|
67
|
+
.split("\n")
|
|
68
|
+
.filter((line) => line.startsWith("worktree "))
|
|
69
|
+
.map((line) => relative(root, line.slice("worktree ".length)))
|
|
70
|
+
.filter((path) => path !== "" && !path.startsWith(".."));
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The files that differ from HEAD, tracked or not, one by one, without the repo's worktrees.
|
|
74
|
+
* @param root - The repo.
|
|
75
|
+
* @returns Their paths.
|
|
76
|
+
* @example changedPaths(root) // ["package.json", "package-lock.json"]
|
|
77
|
+
*/
|
|
78
|
+
export function changedPaths(root) {
|
|
79
|
+
const status = git(root, ["status", "--porcelain", "-uall", "-z"]);
|
|
80
|
+
if (!status.ok)
|
|
81
|
+
throw new KitError(`no pude leer git status: ${status.err}`);
|
|
82
|
+
const worktrees = innerWorktrees(root);
|
|
83
|
+
const entries = status.out.split("\0").filter((entry) => entry !== "");
|
|
84
|
+
const paths = [];
|
|
85
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
86
|
+
const entry = entries[index] ?? "";
|
|
87
|
+
if (/[RC]/.test(entry.slice(0, 2)))
|
|
88
|
+
index += 1;
|
|
89
|
+
const path = entry.slice(3).replace(/\/$/, "");
|
|
90
|
+
if (!worktrees.some((tree) => path === tree || path.startsWith(`${tree}/`)))
|
|
91
|
+
paths.push(path);
|
|
92
|
+
}
|
|
93
|
+
return paths;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* How many commits the current branch is behind its upstream, as the last fetch saw it.
|
|
97
|
+
* @param root - The repo.
|
|
98
|
+
* @returns The count, or 0 without an upstream.
|
|
99
|
+
*/
|
|
100
|
+
export function behindUpstream(root) {
|
|
101
|
+
const count = git(root, ["rev-list", "--count", "HEAD..@{upstream}"]);
|
|
102
|
+
return count.ok ? Number(count.out) || 0 : 0;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The branch for today's changes: a new name, also against origin's branches.
|
|
106
|
+
* @param root - The repo, just fetched.
|
|
107
|
+
* @param date - Today.
|
|
108
|
+
* @returns `chore/agent-kitchen-apply-AAAA-MM-DD`, or with `-2`, `-3`… if taken.
|
|
109
|
+
* @example branchName(root, new Date()) // "chore/agent-kitchen-apply-2026-11-01"
|
|
110
|
+
*/
|
|
111
|
+
export function branchName(root, date) {
|
|
112
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
113
|
+
const name = `chore/agent-kitchen-apply-${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
114
|
+
const taken = (candidate) => git(root, ["rev-parse", "-q", "--verify", `refs/heads/${candidate}`]).ok || git(root, ["rev-parse", "-q", "--verify", `refs/remotes/origin/${candidate}`]).ok;
|
|
115
|
+
if (!taken(name))
|
|
116
|
+
return name;
|
|
117
|
+
let n = 2;
|
|
118
|
+
while (taken(`${name}-${n}`))
|
|
119
|
+
n += 1;
|
|
120
|
+
return `${name}-${n}`;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The hooks the clone runs: the repo's `core.hooksPath` as is when it is relative and the
|
|
124
|
+
* folder is in the clone (committed, like `.githooks`), or else the folder the repo uses,
|
|
125
|
+
* absolute (`.git/hooks`, or a generated one like husky's).
|
|
126
|
+
* @param root - The repo.
|
|
127
|
+
* @param clone - The clone.
|
|
128
|
+
* @returns The folder.
|
|
129
|
+
*/
|
|
130
|
+
function hooksPath(root, clone) {
|
|
131
|
+
const own = git(root, ["config", "--get", "core.hooksPath"]);
|
|
132
|
+
if (own.ok && own.out !== "" && !isAbsolute(own.out) && existsSync(join(clone, own.out)))
|
|
133
|
+
return own.out;
|
|
134
|
+
return git(root, ["rev-parse", "--path-format=absolute", "--git-path", "hooks"]).out;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fetches origin and clones the repo at `origin/<base>` into a temporary folder.
|
|
138
|
+
* @param root - The repo.
|
|
139
|
+
* @param base - The base branch.
|
|
140
|
+
* @returns The clone's folder.
|
|
141
|
+
* @throws KitError if the repo has no origin, the fetch fails or origin has no such branch.
|
|
142
|
+
*/
|
|
143
|
+
export function cloneFromOrigin(root, base) {
|
|
144
|
+
const url = originUrl(root);
|
|
145
|
+
if (url === null)
|
|
146
|
+
throw new KitError("--pr necesita un remoto origin, y este repo no tiene.");
|
|
147
|
+
const fetched = git(root, ["fetch", "-q", "--prune", "origin"]);
|
|
148
|
+
if (!fetched.ok)
|
|
149
|
+
throw new KitError(`no pude bajar origin con git fetch; revisa la conexión o las credenciales y corre otra vez.${fetched.err === "" ? "" : `\n${fetched.err}`}`);
|
|
150
|
+
const head = git(root, ["rev-parse", "-q", "--verify", `refs/remotes/origin/${base}^{commit}`]);
|
|
151
|
+
if (!head.ok)
|
|
152
|
+
throw new KitError(`origin no tiene la rama ${base}.`);
|
|
153
|
+
const clone = realpathSync(mkdtempSync(join(tmpdir(), "agent-kitchen-pr-")));
|
|
154
|
+
const steps = [
|
|
155
|
+
["clone", "-q", "--shared", "--no-checkout", root, clone],
|
|
156
|
+
["-C", clone, "remote", "set-url", "origin", url],
|
|
157
|
+
["-C", clone, "update-ref", `refs/remotes/origin/${base}`, head.out],
|
|
158
|
+
["-C", clone, "symbolic-ref", "refs/remotes/origin/HEAD", `refs/remotes/origin/${base}`],
|
|
159
|
+
["-C", clone, "checkout", "-q", "--detach", head.out],
|
|
160
|
+
];
|
|
161
|
+
for (const args of steps) {
|
|
162
|
+
const done = git(root, args);
|
|
163
|
+
if (!done.ok) {
|
|
164
|
+
rmSync(clone, { recursive: true, force: true });
|
|
165
|
+
throw new KitError(`no pude preparar la copia temporal: ${done.err}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const key of IDENTITY) {
|
|
169
|
+
const value = git(root, ["config", "--get", key]);
|
|
170
|
+
if (value.ok && value.out !== "")
|
|
171
|
+
git(clone, ["config", key, value.out]);
|
|
172
|
+
}
|
|
173
|
+
git(clone, ["config", "core.hooksPath", hooksPath(root, clone)]);
|
|
174
|
+
return clone;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Deletes the clone.
|
|
178
|
+
* @param clone - Its folder.
|
|
179
|
+
*/
|
|
180
|
+
export function removeClone(clone) {
|
|
181
|
+
rmSync(clone, { recursive: true, force: true });
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The version of github-rest to open the PR with: the repo's exact one, or npm's latest.
|
|
185
|
+
* @param root - The checkout to read package.json from.
|
|
186
|
+
* @returns The version, or null if neither is known.
|
|
187
|
+
*/
|
|
188
|
+
async function githubRestVersion(root) {
|
|
189
|
+
const own = dependencyVersion(readJson(root, "package.json"), GITHUB_REST);
|
|
190
|
+
if (own !== null && isRelease(own))
|
|
191
|
+
return own;
|
|
192
|
+
return npmLatest(GITHUB_REST);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Opens the PR in draft with github-rest, without GH_REPO, so the repo is origin's.
|
|
196
|
+
* @param root - The repo.
|
|
197
|
+
* @param clone - The clone, whose package.json says which github-rest the base uses.
|
|
198
|
+
* @param branch - The branch.
|
|
199
|
+
* @param base - The base.
|
|
200
|
+
* @param steps - The steps, for the body.
|
|
201
|
+
* @returns The PR's URL, or why it failed.
|
|
202
|
+
*/
|
|
203
|
+
async function openPr(root, clone, branch, base, steps) {
|
|
204
|
+
const version = await githubRestVersion(clone);
|
|
205
|
+
if (version === null)
|
|
206
|
+
return { error: `no sé qué versión de ${GITHUB_REST} usar.` };
|
|
207
|
+
const dir = mkdtempSync(join(tmpdir(), "agent-kitchen-body-"));
|
|
208
|
+
const bodyFile = join(dir, "body.md");
|
|
209
|
+
writeFileSync(bodyFile, prBody(steps));
|
|
210
|
+
try {
|
|
211
|
+
const result = await captureNpxWithout(root, [`${GITHUB_REST}@${version}`, "pr", "create", "--head", branch, "--base", base, "--title", TITLE, "--body-file", bodyFile, "--draft"], ["GH_REPO"]);
|
|
212
|
+
const url = result.out.trim().split("\n").at(-1) ?? "";
|
|
213
|
+
return result.status === 0 && url !== "" ? { url } : { error: `${result.err}${result.out}`.trim() || "github-rest falló sin decir por qué." };
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
rmSync(dir, { recursive: true, force: true });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* What the person can run to open the PR by hand.
|
|
221
|
+
* @param branch - The branch.
|
|
222
|
+
* @param base - The base.
|
|
223
|
+
* @returns The command.
|
|
224
|
+
*/
|
|
225
|
+
function prCommand(branch, base) {
|
|
226
|
+
return `npx @ramon-sg/github-rest pr create --head ${branch} --base ${base} --title "${TITLE}" --body "..." --draft`;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Commits the clone's changes, pushes them from the repo to a new branch of origin and opens
|
|
230
|
+
* the PR. Until the push works, the commit lives only in the clone, which the caller keeps.
|
|
231
|
+
* @param root - The repo.
|
|
232
|
+
* @param clone - The clone, with the changes.
|
|
233
|
+
* @param options - The base, the steps that ran (for the commit and the PR) and the files to commit (the ones shown).
|
|
234
|
+
* @returns The lines to show, whether it all worked and whether the changes reached origin.
|
|
235
|
+
* @example await publish(root, clone, { base: "main", steps, paths })
|
|
236
|
+
*/
|
|
237
|
+
export async function publish(root, clone, options) {
|
|
238
|
+
const { base, steps, paths } = options;
|
|
239
|
+
const add = git(clone, ["--literal-pathspecs", "add", "-A", "--", ...paths]);
|
|
240
|
+
const done = add.ok ? git(clone, ["commit", "-q", "-m", TITLE, "-m", steps.map((step) => `- ${step}`).join("\n")]) : add;
|
|
241
|
+
if (!done.ok)
|
|
242
|
+
return { ok: false, pushed: false, lines: ["No pude hacer el commit.", done.err || done.out].filter((line) => line !== "") };
|
|
243
|
+
const sha = git(clone, ["rev-parse", "HEAD"]).out;
|
|
244
|
+
const branch = branchName(root, new Date());
|
|
245
|
+
// git asks for credentials on the terminal itself, so its output can stay captured.
|
|
246
|
+
const fetched = git(root, ["fetch", "-q", clone, "HEAD"]);
|
|
247
|
+
const pushed = fetched.ok ? git(root, ["push", "-q", "origin", `${sha}:refs/heads/${branch}`]) : fetched;
|
|
248
|
+
if (!pushed.ok)
|
|
249
|
+
return { ok: false, pushed: false, lines: [`No pude subir la rama ${branch} a origin.`, pushed.err].filter((line) => line !== "") };
|
|
250
|
+
const pr = await openPr(root, clone, branch, base, steps);
|
|
251
|
+
if ("url" in pr)
|
|
252
|
+
return { ok: true, pushed: true, lines: [`PR en draft: ${pr.url}`] };
|
|
253
|
+
return { ok: false, pushed: true, lines: [`La rama ${branch} está en origin, pero no pude abrir el PR: ${pr.error}`, `Falta: ${prCommand(branch, base)}`] };
|
|
254
|
+
}
|
package/dist/progress.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ export interface Progress {
|
|
|
6
6
|
stop: (text: StepText, ok: boolean) => void;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* Progress as plain lines: the start, a line now and then while it
|
|
9
|
+
* Progress as plain lines: the start (marked ◒, like the spinner), a line now and then while it
|
|
10
|
+
* runs, and the end (marked ✓ or ✗).
|
|
10
11
|
* @param out - Where to write.
|
|
11
12
|
* @param options - Whether to print while it runs, how often, and the clock.
|
|
12
13
|
* @returns The progress.
|
|
@@ -18,12 +19,11 @@ export declare function plainProgress(out: Output, options: {
|
|
|
18
19
|
now?: () => number;
|
|
19
20
|
}): Progress;
|
|
20
21
|
/**
|
|
21
|
-
* How progress is shown: a spinner in a terminal, plain lines otherwise
|
|
22
|
-
*
|
|
23
|
-
*
|
|
22
|
+
* How progress is shown: a spinner in a terminal, plain lines otherwise. Only for the steps,
|
|
23
|
+
* after every question: a spinner shown before a question leaves the second question deaf to
|
|
24
|
+
* the keyboard (@clack/prompts 1.8.1).
|
|
24
25
|
* @param out - Where to write.
|
|
25
26
|
* @param verbose - Whether the tools print on the terminal.
|
|
26
|
-
* @param plain - Whether a question may come after the steps (`--commit`).
|
|
27
27
|
* @returns The progress.
|
|
28
28
|
*/
|
|
29
|
-
export declare function progressFor(out: Output, verbose: boolean
|
|
29
|
+
export declare function progressFor(out: Output, verbose: boolean): Progress;
|
package/dist/progress.js
CHANGED
|
@@ -20,7 +20,8 @@ function endLine(text, ok, ms) {
|
|
|
20
20
|
return `${ok ? said.done : ui.failed(said.menu)} (${ui.duration(ms)})`;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Progress as plain lines: the start, a line now and then while it
|
|
23
|
+
* Progress as plain lines: the start (marked ◒, like the spinner), a line now and then while it
|
|
24
|
+
* runs, and the end (marked ✓ or ✗).
|
|
24
25
|
* @param out - Where to write.
|
|
25
26
|
* @param options - Whether to print while it runs, how often, and the clock.
|
|
26
27
|
* @returns The progress.
|
|
@@ -33,7 +34,7 @@ export function plainProgress(out, options) {
|
|
|
33
34
|
return {
|
|
34
35
|
start: (text) => {
|
|
35
36
|
started = now();
|
|
36
|
-
out.stdout(
|
|
37
|
+
out.stdout(`◒ ${phrases(text).doing}\n`);
|
|
37
38
|
if (options.ticks)
|
|
38
39
|
timer = setInterval(() => out.stdout(`${ui.still(ui.duration(now() - started))}\n`), options.tickMs ?? TICK_MS);
|
|
39
40
|
},
|
|
@@ -45,16 +46,15 @@ export function plainProgress(out, options) {
|
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
/**
|
|
48
|
-
* How progress is shown: a spinner in a terminal, plain lines otherwise
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* How progress is shown: a spinner in a terminal, plain lines otherwise. Only for the steps,
|
|
50
|
+
* after every question: a spinner shown before a question leaves the second question deaf to
|
|
51
|
+
* the keyboard (@clack/prompts 1.8.1).
|
|
51
52
|
* @param out - Where to write.
|
|
52
53
|
* @param verbose - Whether the tools print on the terminal.
|
|
53
|
-
* @param plain - Whether a question may come after the steps (`--commit`).
|
|
54
54
|
* @returns The progress.
|
|
55
55
|
*/
|
|
56
|
-
export function progressFor(out, verbose
|
|
57
|
-
if (out.interactive && !verbose
|
|
56
|
+
export function progressFor(out, verbose) {
|
|
57
|
+
if (out.interactive && !verbose) {
|
|
58
58
|
const spin = spinner();
|
|
59
59
|
let started = 0;
|
|
60
60
|
return {
|
package/dist/repo.d.ts
CHANGED
|
@@ -46,6 +46,13 @@ export declare function dependencyVersion(pkg: Record<string, unknown> | null, n
|
|
|
46
46
|
* @example packageManager(root) // "pnpm"
|
|
47
47
|
*/
|
|
48
48
|
export declare function packageManager(root: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* The text of the repo's package.json, to tell later whether a run changed it.
|
|
51
|
+
* @param root - The repo.
|
|
52
|
+
* @returns Its text, or null if there is none.
|
|
53
|
+
* @example packageText(root) // '{ "devDependencies": … }'
|
|
54
|
+
*/
|
|
55
|
+
export declare function packageText(root: string): string | null;
|
|
49
56
|
/**
|
|
50
57
|
* The version of a package installed in the repo's `node_modules`.
|
|
51
58
|
* @param root - The repo.
|
package/dist/repo.js
CHANGED
|
@@ -115,6 +115,16 @@ export function packageManager(root) {
|
|
|
115
115
|
return field;
|
|
116
116
|
return LOCKFILES.find(([file]) => existsSync(join(root, file)))?.[1] ?? "npm";
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* The text of the repo's package.json, to tell later whether a run changed it.
|
|
120
|
+
* @param root - The repo.
|
|
121
|
+
* @returns Its text, or null if there is none.
|
|
122
|
+
* @example packageText(root) // '{ "devDependencies": … }'
|
|
123
|
+
*/
|
|
124
|
+
export function packageText(root) {
|
|
125
|
+
const file = join(root, "package.json");
|
|
126
|
+
return existsSync(file) ? readFileSync(file, "utf8") : null;
|
|
127
|
+
}
|
|
118
128
|
/**
|
|
119
129
|
* The version of a package installed in the repo's `node_modules`.
|
|
120
130
|
* @param root - The repo.
|
package/dist/texts/catalog.d.ts
CHANGED
|
@@ -37,8 +37,8 @@ export interface Catalog {
|
|
|
37
37
|
suggested: (dryRun: boolean) => string;
|
|
38
38
|
/** The last line when every step worked or some failed. */
|
|
39
39
|
summary: (failed: string[]) => string;
|
|
40
|
-
/** What is left to do by hand after the steps,
|
|
41
|
-
next: (
|
|
40
|
+
/** What is left to do by hand after the steps, in the checkout; with the manager if its install failed. */
|
|
41
|
+
next: (failedInstall: string | null) => string;
|
|
42
42
|
/** A new piece left out because no one was asked about it. */
|
|
43
43
|
undecided: (names: string[]) => string;
|
|
44
44
|
/** The repo's own skills left out of a pick. */
|
|
@@ -47,4 +47,12 @@ export interface Catalog {
|
|
|
47
47
|
saved: (file: string, commits: boolean) => string;
|
|
48
48
|
/** The closing line when no step ran: the person left, or picked none. */
|
|
49
49
|
noSteps: (why: "leave" | "none", wrote: boolean) => string;
|
|
50
|
+
/** With --pr, before the clone: from which branch of origin. */
|
|
51
|
+
cloning: (base: string) => string;
|
|
52
|
+
/** With --pr, the heading of the files that go in the PR. */
|
|
53
|
+
changes: string;
|
|
54
|
+
/** With --pr, nothing changed. */
|
|
55
|
+
noChanges: string;
|
|
56
|
+
/** With --pr, why no PR was opened and where the changes stay. */
|
|
57
|
+
prNotOpened: (why: "step" | "install" | "git", clone: string) => string;
|
|
50
58
|
}
|
package/dist/texts/es.js
CHANGED
|
@@ -127,7 +127,7 @@ export const es = {
|
|
|
127
127
|
onlyOptional: (menus) => `Todo al día. Opcional, para elegir en una terminal: ${menus.join("; ")}.`,
|
|
128
128
|
suggested: (dryRun) => `Pasos sugeridos${dryRun ? "" : " (para hacerlos, corre agent-kitchen apply en una terminal, o con --yes)"}:`,
|
|
129
129
|
summary: (failed) => (failed.length === 0 ? "Listo." : `Terminó, pero fallaron: ${failed.join("; ")}.`),
|
|
130
|
-
next: (
|
|
130
|
+
next: (failedInstall) => `Falta: ${failedInstall === null ? "" : `corre '${failedInstall} install', `}revisa el diff y haz commit.`,
|
|
131
131
|
undecided: (names) => `Pieza nueva sin decisión: ${list(names)}. Corre agent-kitchen apply en una terminal para elegir.`,
|
|
132
132
|
ownSkillsKept: (names) => `Se dejan fuera las skills que el repo ya tiene como propias: ${list(names)}. Para reemplazarlas, corre agent-kitchen apply en una terminal.`,
|
|
133
133
|
saved: (file, commits) => `Guardé qué piezas usa el repo en ${file}${commits ? "" : "; commitéalo"}.`,
|
|
@@ -136,4 +136,11 @@ export const es = {
|
|
|
136
136
|
return wrote ? "No corrí ningún paso; solo guardé qué piezas usa el repo." : "No se cambió nada.";
|
|
137
137
|
return wrote ? "No elegiste pasos; solo guardé qué piezas usa el repo." : "No elegiste nada; no se cambió nada.";
|
|
138
138
|
},
|
|
139
|
+
cloning: (base) => `Trabajo en una copia temporal de origin/${base}; tu copia no se toca.`,
|
|
140
|
+
changes: "Cambios del PR:",
|
|
141
|
+
noChanges: "No cambió ningún archivo: no hay PR que abrir.",
|
|
142
|
+
prNotOpened: (why, clone) => {
|
|
143
|
+
const reason = { step: "No abrí el PR porque algún paso falló.", install: "No abrí el PR porque falló el install.", git: "" }[why];
|
|
144
|
+
return `${reason === "" ? "" : `${reason} `}Los cambios quedan en la copia temporal ${clone}; bórrala con rm -rf cuando termines.`;
|
|
145
|
+
},
|
|
139
146
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-kitchen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "One command to install, update and check a repo's agent tooling: the issue flow and its skills, the general skills, Claude cloud sessions, claude-repo-guard and the installed-versions rule in AGENTS.md.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/dist/git-finish.d.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/** What to do with the changes: leave them, commit on the base, on a branch, or a branch with a PR. */
|
|
2
|
-
export type FinishChoice = "leave" | "base" | "branch" | "pr";
|
|
3
|
-
/**
|
|
4
|
-
* The base branch, as issue-flow takes it: `baseBranch` of `.agents/flow.json`, or origin's
|
|
5
|
-
* default as git knows it, or `main`.
|
|
6
|
-
* @param root - The repo.
|
|
7
|
-
* @returns The branch.
|
|
8
|
-
* @example baseBranch(root) // "main"
|
|
9
|
-
*/
|
|
10
|
-
export declare function baseBranch(root: string): string;
|
|
11
|
-
/**
|
|
12
|
-
* The files that differ from HEAD, tracked or not, one by one, without the repo's worktrees.
|
|
13
|
-
* @param root - The repo.
|
|
14
|
-
* @returns Their paths.
|
|
15
|
-
* @example changedPaths(root) // ["package.json", "package-lock.json"]
|
|
16
|
-
*/
|
|
17
|
-
export declare function changedPaths(root: string): string[];
|
|
18
|
-
/**
|
|
19
|
-
* Checks, before any step, that the repo has no changes and is on its base branch.
|
|
20
|
-
* @param root - The repo.
|
|
21
|
-
* @param base - The base branch.
|
|
22
|
-
* @throws KitError saying what is wrong and what to do.
|
|
23
|
-
*/
|
|
24
|
-
export declare function checkReady(root: string, base: string): void;
|
|
25
|
-
/**
|
|
26
|
-
* The branch for today's changes: a new name, also against origin's branches git knows of.
|
|
27
|
-
* @param root - The repo.
|
|
28
|
-
* @param date - Today.
|
|
29
|
-
* @returns `chore/agent-kitchen-apply-AAAA-MM-DD`, or with `-2`, `-3`… if taken.
|
|
30
|
-
* @example branchName(root, new Date()) // "chore/agent-kitchen-apply-2026-11-01"
|
|
31
|
-
*/
|
|
32
|
-
export declare function branchName(root: string, date: Date): string;
|
|
33
|
-
/** What `finish` needs to know. */
|
|
34
|
-
export interface FinishOptions {
|
|
35
|
-
base: string;
|
|
36
|
-
branch: string;
|
|
37
|
-
steps: string[];
|
|
38
|
-
paths: string[];
|
|
39
|
-
manager: string;
|
|
40
|
-
packageChanged?: boolean;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Does what the person chose with the changes.
|
|
44
|
-
* @param root - The repo, on the base, with the changes of the run.
|
|
45
|
-
* @param choice - What to do.
|
|
46
|
-
* @param options - The base, the new branch's name, the steps that ran, the files to commit (the ones shown), the package manager and whether package.json changed.
|
|
47
|
-
* @returns The lines to show and whether it all worked.
|
|
48
|
-
* @example await finish(root, "pr", { base: "main", branch: "chore/agent-kitchen-apply-2026-11-01", steps, manager: "npm" })
|
|
49
|
-
*/
|
|
50
|
-
export declare function finish(root: string, choice: FinishChoice, options: FinishOptions): Promise<{
|
|
51
|
-
ok: boolean;
|
|
52
|
-
lines: string[];
|
|
53
|
-
}>;
|