@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,134 +0,0 @@
1
- /**
2
- * @file `issue-flow init`: sets a repo up for the flow, and can run again to fill in what
3
- * is missing. It writes `.agents/flow.json` (from `paseo.json` if the repo has one, plus the
4
- * recipes it picks), the flow's dev dependencies and scripts in `package.json`, and the
5
- * `SessionStart`, `WorktreeCreate` and `WorktreeRemove` hooks. Only the flow's own files:
6
- * the skills, `claude-cloud` and `claude-repo-guard` are installed by `agent-kitchen`,
7
- * which runs this command.
8
- */
9
- import { confirm } from "../ask.js";
10
- import { CONFIG_PATH, isObject } from "../config-base.js";
11
- import { parseConfig } from "../config.js";
12
- import { checkoutRoot, locateRepo } from "../git.js";
13
- import { UsageError, warn } from "../output.js";
14
- import { KEPT, withFlowPackages } from "../package-edits.js";
15
- import { applyRecipe, detectedRecipes, loadRecipes, projectPrefix } from "../recipes.js";
16
- import { registerHooks } from "../settings-hooks.js";
17
- import { FLOW_REPO, ownVersions, packageManager, readJson, SETTINGS_FILE, SetupError, writeJson } from "../setup-files.js";
18
- /**
19
- * The URL of the config's schema at a version.
20
- * @param version - This package's version.
21
- * @returns The URL.
22
- * @example schemaUrl("0.1.0") // "https://raw.githubusercontent.com/ramon-sg/issue-flow/v0.1.0/schema.json"
23
- */
24
- export function schemaUrl(version) {
25
- return `https://raw.githubusercontent.com/${FLOW_REPO}/v${version}/schema.json`;
26
- }
27
- /**
28
- * A new config: the schema, and the branch rules of `paseo.json` if the repo has one.
29
- * @param root - The repo.
30
- * @param version - This package's version.
31
- * @returns The config and what it took from `paseo.json`.
32
- */
33
- function freshConfig(root, version) {
34
- const raw = { $schema: schemaUrl(version) };
35
- const issues = readJson(root, "paseo.json")?.project;
36
- const paseo = isObject(issues) && isObject(issues.issues) ? issues.issues : null;
37
- if (paseo === null)
38
- return { raw, changes: ["new"] };
39
- const branch = {};
40
- if (typeof paseo.branchTemplate === "string")
41
- branch.template = paseo.branchTemplate;
42
- if (isObject(paseo.types))
43
- branch.types = paseo.types;
44
- if (Object.keys(branch).length > 0)
45
- raw.branch = branch;
46
- return { raw, changes: ["new, with the branch rules of paseo.json"] };
47
- }
48
- /**
49
- * The recipes to apply: the ones named with `--recipe`, or each one the repo's files
50
- * suggest, after asking (all of them with `--yes`).
51
- * @param root - The repo.
52
- * @param options - The options.
53
- * @param out - Where to ask.
54
- * @returns The recipes.
55
- * @throws UsageError if `--recipe` names one that does not exist.
56
- */
57
- async function pickRecipes(root, options, out) {
58
- const recipes = loadRecipes();
59
- if (options.recipes !== null) {
60
- const unknown = options.recipes.find((name) => !recipes.some((recipe) => recipe.name === name));
61
- if (unknown !== undefined)
62
- throw new UsageError(`unknown recipe ${unknown}; there are ${recipes.map((recipe) => recipe.name).join(", ")}.`);
63
- return recipes.filter((recipe) => options.recipes?.includes(recipe.name));
64
- }
65
- const picked = [];
66
- for (const recipe of recipes.filter((item) => detectedRecipes(root, recipes).includes(item.name)))
67
- if (options.yes || (await confirm(out, `Recipe ${recipe.name}: ${recipe.description} Add it?`)))
68
- picked.push(recipe);
69
- return picked;
70
- }
71
- /**
72
- * What `init` would write: the config, `package.json` and the settings' hooks.
73
- * @param root - The repo.
74
- * @param versions - This package's versions.
75
- * @param recipes - The recipes to apply.
76
- * @returns The files that change.
77
- * @throws ConfigError if the resulting config is invalid; SetupError if a file is broken.
78
- */
79
- function plan(root, versions, recipes) {
80
- const existing = readJson(root, CONFIG_PATH);
81
- const start = existing === null ? freshConfig(root, versions.issueFlow) : { raw: existing, changes: [] };
82
- let { raw } = start;
83
- const configChanges = [...start.changes];
84
- for (const recipe of recipes) {
85
- const applied = applyRecipe(raw, recipe, projectPrefix(root));
86
- raw = applied.raw;
87
- configChanges.push(...applied.changes.map((change) => (change.startsWith(KEPT) ? `${change} (${recipe.name})` : `${recipe.name}: ${change}`)));
88
- }
89
- parseConfig(raw);
90
- const pkg = withFlowPackages(readJson(root, "package.json"), versions, false);
91
- const hooks = registerHooks(readJson(root, SETTINGS_FILE), versions.issueFlow);
92
- const file = (path, value, lines) => ({
93
- path,
94
- value,
95
- changes: lines.filter((line) => !line.startsWith(KEPT)).map((line) => `${path}: ${line}`),
96
- kept: lines.filter((line) => line.startsWith(KEPT)).map((line) => `${path}: ${line}`),
97
- });
98
- return [file(CONFIG_PATH, raw, configChanges), file("package.json", pkg.pkg, pkg.changes), file(SETTINGS_FILE, hooks.settings, hooks.changes)];
99
- }
100
- /**
101
- * Sets the repo up for the flow; see the file's header.
102
- * @param options - The options.
103
- * @param out - Where to write and ask.
104
- * @returns 0, or 1 if the files were not written.
105
- * @throws SetupError outside the main checkout or with a broken file; ConfigError if the
106
- * config would be invalid; UsageError for an unknown recipe.
107
- * @example await init({ cwd, yes: true, recipes: null }, out)
108
- */
109
- export async function init(options, out) {
110
- const located = locateRepo(options.cwd);
111
- if (located === null)
112
- throw new SetupError("run init inside a checkout of the repo.");
113
- if (checkoutRoot(options.cwd) !== located.mainRoot)
114
- throw new SetupError(`run init in the main checkout, ${located.mainRoot}, not in a worktree.`);
115
- const root = located.mainRoot;
116
- const versions = ownVersions();
117
- const files = plan(root, versions, await pickRecipes(root, options, out)).filter((file) => file.changes.length > 0 || file.kept.length > 0);
118
- for (const line of files.flatMap((file) => file.kept))
119
- warn(out, line);
120
- const lines = files.flatMap((file) => file.changes);
121
- if (lines.length === 0)
122
- out.stdout("The config, package.json and the hooks are already set up.\n");
123
- else
124
- out.stdout(`issue-flow ${versions.issueFlow} will write:\n${lines.map((line) => ` ${line}\n`).join("")}`);
125
- if (lines.length > 0 && !options.yes && !(await confirm(out, "Write it?"))) {
126
- warn(out, "nothing written.");
127
- return 1;
128
- }
129
- for (const file of files.filter((item) => item.changes.length > 0))
130
- writeJson(root, file.path, file.value);
131
- const manager = packageManager(root, readJson(root, "package.json"));
132
- out.stdout(`Next: run '${manager} install', and commit .agents/, .claude/ and package.json.\n`);
133
- return 0;
134
- }
@@ -1,16 +0,0 @@
1
- import { type Output } from "../output.js";
2
- /** Options of `issue-flow upgrade`. */
3
- export interface UpgradeOptions {
4
- cwd: string;
5
- dryRun: boolean;
6
- }
7
- /**
8
- * Moves the repo's flow files to this version; see the file's header.
9
- * @param options - The options.
10
- * @param out - Where to write.
11
- * @returns 0.
12
- * @throws SetupError outside the main checkout, if a file is broken, or if the repo is
13
- * already at a newer version (a stale local copy must not move it back).
14
- * @example upgrade({ cwd, dryRun: false }, out)
15
- */
16
- export declare function upgrade(options: UpgradeOptions, out: Output): number;
@@ -1,72 +0,0 @@
1
- /**
2
- * @file `issue-flow upgrade [--dry-run]`: moves the repo's flow files to the version that
3
- * runs: `@ramon-sg/issue-flow`, the hooks and the config's schema to it, and `github-rest`
4
- * and `skills-manager` up to the versions it was tested with only if they fall short (a
5
- * newer one, or a range that allows it, is kept and reported). `agent-kitchen` runs it as `npx @ramon-sg/issue-flow@<version> upgrade` and then
6
- * moves the flow's skills to the same tag, because the skills call this CLI.
7
- */
8
- import { CONFIG_PATH } from "../config-base.js";
9
- import { checkoutRoot, locateRepo } from "../git.js";
10
- import { warn } from "../output.js";
11
- import { dependencyVersion, isNewer, KEPT, withFlowPackages } from "../package-edits.js";
12
- import { hookVersions, registerHooks } from "../settings-hooks.js";
13
- import { FLOW_REPO, ownVersions, PACKAGE, packageManager, readJson, SETTINGS_FILE, SetupError, writeJson } from "../setup-files.js";
14
- import { schemaUrl } from "./init.js";
15
- /** The start of the schema URL `init` writes, at any version. */
16
- const SCHEMA_PREFIX = `https://raw.githubusercontent.com/${FLOW_REPO}/`;
17
- /**
18
- * The newest version of the flow the repo already has, in package.json or a hook.
19
- * @param root - The repo.
20
- * @returns The version, or null if it has none.
21
- */
22
- function repoVersion(root) {
23
- const found = [dependencyVersion(readJson(root, "package.json"), PACKAGE), ...Object.values(hookVersions(readJson(root, SETTINGS_FILE)))];
24
- return found.filter((version) => version !== null).reduce((newest, version) => (newest === null || isNewer(version, newest) ? version : newest), null);
25
- }
26
- /**
27
- * Moves the repo's flow files to this version; see the file's header.
28
- * @param options - The options.
29
- * @param out - Where to write.
30
- * @returns 0.
31
- * @throws SetupError outside the main checkout, if a file is broken, or if the repo is
32
- * already at a newer version (a stale local copy must not move it back).
33
- * @example upgrade({ cwd, dryRun: false }, out)
34
- */
35
- export function upgrade(options, out) {
36
- const located = locateRepo(options.cwd);
37
- if (located === null)
38
- throw new SetupError("run upgrade inside a checkout of the repo.");
39
- const root = located.mainRoot;
40
- if (checkoutRoot(options.cwd) !== root)
41
- throw new SetupError(`run upgrade in the main checkout, ${root}, not in a worktree.`);
42
- const versions = ownVersions();
43
- const current = repoVersion(root);
44
- if (current !== null && isNewer(current, versions.issueFlow))
45
- throw new SetupError(`the repo is at issue-flow ${current}, newer than this ${versions.issueFlow}; run 'npx ${PACKAGE}@${current.replace(/^[\^~=]/, "")} upgrade' or 'npx agent-kitchen apply'.`);
46
- const writes = [];
47
- const lines = [];
48
- const pkg = withFlowPackages(readJson(root, "package.json"), versions, true);
49
- const kept = pkg.changes.filter((change) => change.startsWith(KEPT));
50
- const moved = pkg.changes.filter((change) => !change.startsWith(KEPT));
51
- for (const change of kept)
52
- warn(out, `package.json: ${change}`);
53
- if (moved.length > 0)
54
- writes.push({ path: "package.json", value: pkg.pkg });
55
- lines.push(...moved.map((change) => `package.json: ${change}`));
56
- const hooks = registerHooks(readJson(root, SETTINGS_FILE), versions.issueFlow);
57
- if (hooks.changes.length > 0)
58
- writes.push({ path: SETTINGS_FILE, value: hooks.settings });
59
- lines.push(...hooks.changes.map((change) => `${SETTINGS_FILE}: ${change}`));
60
- const config = readJson(root, CONFIG_PATH);
61
- if (config !== null && typeof config.$schema === "string" && config.$schema.startsWith(SCHEMA_PREFIX) && config.$schema !== schemaUrl(versions.issueFlow)) {
62
- writes.push({ path: CONFIG_PATH, value: { ...config, $schema: schemaUrl(versions.issueFlow) } });
63
- lines.push(`${CONFIG_PATH}: $schema at v${versions.issueFlow}`);
64
- }
65
- out.stdout(lines.length === 0 ? `Already at issue-flow ${versions.issueFlow}.\n` : `To issue-flow ${versions.issueFlow}:\n${lines.map((line) => ` ${line}\n`).join("")}`);
66
- if (options.dryRun || writes.length === 0)
67
- return 0;
68
- for (const write of writes)
69
- writeJson(root, write.path, write.value);
70
- out.stdout(`Next: run '${packageManager(root, readJson(root, "package.json"))} install', review the diff and commit it.\n`);
71
- return 0;
72
- }