@iodes/releasekit 0.2.0 → 0.2.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.
- package/README.md +6 -6
- package/dist/cli.js +3 -4
- package/dist/install.d.ts +17 -1
- package/dist/install.js +6 -1
- package/dist/migrate-config.d.ts +10 -0
- package/dist/migrate-config.js +44 -0
- package/dist/project.d.ts +1 -0
- package/dist/project.js +11 -8
- package/dist/setup.d.ts +14 -3
- package/dist/setup.js +38 -47
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -53,17 +53,17 @@ Run inside your product's Git repository:
|
|
|
53
53
|
releasekit init
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
Setup asks for agent tools first, then image themes. In the tool list, use ↑/↓ to move, Space to select or clear a tool, and Enter to continue. No tools are selected initially; A toggles all and I inverts the selection. Select at least one tool to continue. Pressing Enter with no selection displays an error and keeps the tool picker open. For image themes, use ↑/↓ to choose **Both dark and light** (the default), **Dark only**, or **Light only**, then press Enter. The picker explains each highlighted choice: both themes prepares matching versions for dark and light backgrounds; a single theme prepares one version for the chosen background. This default applies to generated release-note illustrations in new drafts and can be changed later. Ctrl+C cancels before configuration is written. Product name defaults to the repository folder name, and the original language defaults to English with no translations. Language selection remains part of the first draft workflow.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
The original command still initializes immediately with no questions:
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
|
-
releasekit init --
|
|
61
|
+
releasekit init --tools codex,claude,cursor --themes both
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
Use `--tools codex` to install only selected tools, or `--tools none` to install no skills. Explicit `--tools` and `--themes` options skip their corresponding questions. With `--json`, `--no-interactive`, or non-terminal input/output, setup skips all questions and uses all supported tools and both themes unless explicitly overridden. Optional `--product`, `--source-locale`, and `--locales` overrides remain available without adding questions; `--locales` must include the original language with no duplicates.
|
|
65
65
|
|
|
66
|
-
Edit `releasekit/config.yaml` later to change defaults for new drafts. Existing releases retain their saved settings.
|
|
66
|
+
Settings are saved to `releasekit/config.yaml`, and setup prints invocation hints for the installed tools. You can also initialize another existing Git repository with `releasekit init ./your-product` (relative to `--cwd`, if supplied). Re-running init preserves existing configuration and directs you to `releasekit update` for managed skills. Edit `releasekit/config.yaml` later to change defaults for new drafts. Existing releases retain their saved settings.
|
|
67
67
|
|
|
68
68
|
### 3. Ask your agent
|
|
69
69
|
|
|
@@ -135,7 +135,7 @@ releasekit update # Refresh installed skills; preserve user
|
|
|
135
135
|
|
|
136
136
|
`list` shows release identities, saved draft/ready state, note counts, and dates. `status` checks content with the same validation used by `validate`, displays errors and warnings, and suggests the next step. A ready label alone does not guarantee that files still pass validation. Status is read-only and exits successfully even when drafts have pending work; use `validate` for a failing exit code when content is invalid. Before initialization, `status` points to `init`.
|
|
137
137
|
|
|
138
|
-
Running `releasekit update` refreshes skills for all supported tools (Codex, Claude Code, and Cursor), regardless of the saved init selection. It installs missing skills, preserves project configuration and locally modified files, and never prompts for tool selection. The result shows the project, supported tools, updated files, already-current files, and preserved edits, followed by invocation hints and the next command. Conflicts produce exit code 1. `--json` returns only the structured result, including `written`, `unchanged`, and `conflicts` arrays. This command refreshes bundled project skills; it does not upgrade the CLI package itself.
|
|
138
|
+
Running `releasekit update` refreshes skills for all supported tools (Codex, Claude Code, and Cursor), regardless of the saved init selection. It installs missing skills, preserves current project configuration and locally modified files, and never prompts for tool selection. For older configurations containing `history.limit`, update first saves the original to `releasekit/config.yaml.before-update`, removes only that obsolete setting, and then refreshes skills. Other settings, comments on retained entries, and line endings are preserved. The result reports the migration and backup path. Use `export --limit` for an explicit export limit; omitting it exports all releases. Unrelated invalid settings still need correction and are never silently discarded. The result shows the project, supported tools, updated files, already-current files, and preserved edits, followed by invocation hints and the next command. Conflicts produce exit code 1. `--json` returns only the structured result, including `written`, `unchanged`, and `conflicts` arrays. This command refreshes bundled project skills; it does not upgrade the CLI package itself.
|
|
139
139
|
|
|
140
140
|
Setup, update, prepare, list, status, and validate print readable summaries. `--json` preserves structured output for automation; command and option errors are JSON objects on stderr with a nonzero exit code. Help and version output remain plain text. Other content operations retain their detailed JSON results. Run `releasekit <command> --help` for options and `releasekit --help` for the workflow overview.
|
|
141
141
|
|
package/dist/cli.js
CHANGED
|
@@ -52,7 +52,7 @@ program.command('init [directory]').description('Initialize content and install
|
|
|
52
52
|
let setup = { ...options, tools: options.tools === undefined ? undefined : parseTools(options.tools),
|
|
53
53
|
locales: options.locales === undefined ? undefined : commaList(options.locales) };
|
|
54
54
|
if (program.opts().interactive && !program.opts().json && process.stdin.isTTY && process.stdout.isTTY)
|
|
55
|
-
setup = await interactiveSetup(
|
|
55
|
+
setup = await interactiveSetup(setup);
|
|
56
56
|
const result = await initProject(instance, setup);
|
|
57
57
|
emit(result, [`Initialized ReleaseKit in ${instance.root}`, `Configuration: ${result.config}`,
|
|
58
58
|
`Installed skills for: ${result.tools.join(', ') || 'none'}`, ...result.tools.map(tool => result.hints[tool]),
|
|
@@ -64,10 +64,9 @@ program.command('init [directory]').description('Initialize content and install
|
|
|
64
64
|
program.command('update').description('Refresh skills for all supported tools while preserving user edits')
|
|
65
65
|
.action(async () => {
|
|
66
66
|
const instance = project();
|
|
67
|
-
const config = await instance.config();
|
|
68
|
-
if (!program.opts().json)
|
|
69
|
-
console.log(`ReleaseKit skill update — ${config.product}\nProject: ${instance.root}\nTools: codex, claude, cursor\n`);
|
|
70
67
|
const result = await updateProject(instance);
|
|
68
|
+
if (!program.opts().json)
|
|
69
|
+
console.log(`ReleaseKit skill update — ${result.product}\nProject: ${instance.root}\nTools: codex, claude, cursor\n`);
|
|
71
70
|
emit(result, formatUpdate(result));
|
|
72
71
|
if (result.conflicts.length)
|
|
73
72
|
process.exitCode = 1;
|
package/dist/install.d.ts
CHANGED
|
@@ -12,6 +12,22 @@ export declare function installSkills(project: Project, tools?: ProjectConfig['t
|
|
|
12
12
|
};
|
|
13
13
|
}>;
|
|
14
14
|
export declare function updateProject(project: Project): Promise<{
|
|
15
|
+
product: string;
|
|
16
|
+
migrations: string[];
|
|
17
|
+
backup: null;
|
|
18
|
+
tools: ("claude" | "codex" | "cursor")[];
|
|
19
|
+
written: string[];
|
|
20
|
+
conflicts: string[];
|
|
21
|
+
unchanged: string[];
|
|
22
|
+
hints: {
|
|
23
|
+
codex: string;
|
|
24
|
+
claude: string;
|
|
25
|
+
cursor: string;
|
|
26
|
+
};
|
|
27
|
+
} | {
|
|
28
|
+
product: string;
|
|
29
|
+
migrations: string[];
|
|
30
|
+
backup: string;
|
|
15
31
|
tools: ("claude" | "codex" | "cursor")[];
|
|
16
32
|
written: string[];
|
|
17
33
|
conflicts: string[];
|
|
@@ -22,7 +38,7 @@ export declare function updateProject(project: Project): Promise<{
|
|
|
22
38
|
cursor: string;
|
|
23
39
|
};
|
|
24
40
|
}>;
|
|
25
|
-
export declare function formatUpdate(result: Awaited<ReturnType<typeof
|
|
41
|
+
export declare function formatUpdate(result: Awaited<ReturnType<typeof updateProject>>): string;
|
|
26
42
|
export interface InitOptions {
|
|
27
43
|
product?: string;
|
|
28
44
|
tools?: ProjectConfig['tools'];
|
package/dist/install.js
CHANGED
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { Project } from './project.js';
|
|
6
|
+
import { migrateConfig } from './migrate-config.js';
|
|
6
7
|
import { configSchema, defaultConfig } from './model.js';
|
|
7
8
|
import { exists, within, write, writeYaml, digest } from './files.js';
|
|
8
9
|
const resources = fileURLToPath(new URL('../kit/', import.meta.url));
|
|
@@ -52,11 +53,15 @@ export async function installSkills(project, tools) {
|
|
|
52
53
|
} };
|
|
53
54
|
}
|
|
54
55
|
export async function updateProject(project) {
|
|
55
|
-
|
|
56
|
+
const migration = await migrateConfig(project);
|
|
57
|
+
return { ...await installSkills(project, ['codex', 'claude', 'cursor']), ...migration };
|
|
56
58
|
}
|
|
57
59
|
export function formatUpdate(result) {
|
|
58
60
|
const lines = [result.conflicts.length ? 'Update needs attention.' : result.tools.length ? 'Skills are up to date.' : 'No agent tools selected.',
|
|
59
61
|
` Updated: ${result.written.length} files`, ` Already current: ${result.unchanged.length} files`, ` Modified files preserved: ${result.conflicts.length}`];
|
|
62
|
+
if (result.migrations.length) {
|
|
63
|
+
lines.push('', ...result.migrations.map(item => ` Configuration: ${item}`), ` Original configuration: ${result.backup}`);
|
|
64
|
+
}
|
|
60
65
|
if (result.conflicts.length) {
|
|
61
66
|
lines.push('', ...result.conflicts.map(file => ` ! ${file}`), 'Compare these files with the installed package templates and merge the changes you want to keep.');
|
|
62
67
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { parseDocument } from 'yaml';
|
|
3
|
+
import { Project, parseProjectConfig } from './project.js';
|
|
4
|
+
import { canonical, exists, parseYaml, write } from './files.js';
|
|
5
|
+
export async function migrateConfig(project) {
|
|
6
|
+
const file = await project.content('config.yaml');
|
|
7
|
+
if (!(await exists(file)))
|
|
8
|
+
throw new Error('ReleaseKit is not initialized. Run releasekit init first.');
|
|
9
|
+
const original = await fs.readFile(file, 'utf8');
|
|
10
|
+
const raw = parseYaml(original);
|
|
11
|
+
const migrations = [];
|
|
12
|
+
if (raw && typeof raw === 'object' && 'history' in raw && raw.history && typeof raw.history === 'object' && 'limit' in raw.history) {
|
|
13
|
+
delete raw.history.limit;
|
|
14
|
+
migrations.push('Removed history.limit; use export --limit to limit exported releases. Omit --limit to export all releases.');
|
|
15
|
+
}
|
|
16
|
+
// Validate the complete proposed configuration before touching any files.
|
|
17
|
+
const config = parseProjectConfig(raw);
|
|
18
|
+
if (!migrations.length)
|
|
19
|
+
return { product: config.product, migrations, backup: null };
|
|
20
|
+
const document = parseDocument(original);
|
|
21
|
+
document.deleteIn(['history', 'limit']);
|
|
22
|
+
let migrated = document.toString();
|
|
23
|
+
if (original.includes('\r\n'))
|
|
24
|
+
migrated = migrated.replace(/\r?\n/g, '\r\n');
|
|
25
|
+
if (original.startsWith('\uFEFF') && !migrated.startsWith('\uFEFF'))
|
|
26
|
+
migrated = '\uFEFF' + migrated;
|
|
27
|
+
if (canonical(parseYaml(migrated)) !== canonical(raw))
|
|
28
|
+
throw new Error('Configuration migration would change unrelated settings. No files were changed.');
|
|
29
|
+
const backup = await project.content('config.yaml.before-update');
|
|
30
|
+
try {
|
|
31
|
+
await fs.writeFile(backup, original, { flag: 'wx' });
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error.code !== 'EEXIST')
|
|
35
|
+
throw error;
|
|
36
|
+
if (await fs.readFile(backup, 'utf8') !== original) {
|
|
37
|
+
throw new Error(`A different configuration backup already exists at ${backup}. Move it aside before running update again; no files were overwritten.`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (await fs.readFile(file, 'utf8') !== original)
|
|
41
|
+
throw new Error('Configuration changed during update. Run update again; the configuration was not overwritten.');
|
|
42
|
+
await write(file, migrated);
|
|
43
|
+
return { product: config.product, migrations, backup };
|
|
44
|
+
}
|
package/dist/project.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type HistoryStart, type ProjectConfig, type Release, type ReleaseId, type ReleaseRef } from './model.js';
|
|
2
2
|
export declare function checkLimit(limit?: number): void;
|
|
3
3
|
export declare function linearHistory(releases: Release[]): Release[];
|
|
4
|
+
export declare function parseProjectConfig(raw: unknown): ProjectConfig;
|
|
4
5
|
export declare class Project {
|
|
5
6
|
readonly root: string;
|
|
6
7
|
constructor(root: string);
|
package/dist/project.js
CHANGED
|
@@ -46,6 +46,16 @@ export function linearHistory(releases) {
|
|
|
46
46
|
throw new Error('Disconnected release history or cycle found.');
|
|
47
47
|
return chain;
|
|
48
48
|
}
|
|
49
|
+
export function parseProjectConfig(raw) {
|
|
50
|
+
if (raw && typeof raw === 'object' && 'history' in raw && raw.history && typeof raw.history === 'object' && 'limit' in raw.history) {
|
|
51
|
+
throw new Error('Run releasekit update to migrate this configuration automatically. Remove history.limit from config.yaml; use export --limit instead. Omit --limit to export all releases.');
|
|
52
|
+
}
|
|
53
|
+
const config = configSchema.parse(raw);
|
|
54
|
+
if (!config.locales.includes(config.sourceLocale) || new Set(config.locales).size !== config.locales.length) {
|
|
55
|
+
throw new Error('Project locales must be unique and include the source locale.');
|
|
56
|
+
}
|
|
57
|
+
return config;
|
|
58
|
+
}
|
|
49
59
|
export class Project {
|
|
50
60
|
root;
|
|
51
61
|
constructor(root) { this.root = path.resolve(root); }
|
|
@@ -56,14 +66,7 @@ export class Project {
|
|
|
56
66
|
if (!(await exists(file)))
|
|
57
67
|
throw new Error('ReleaseKit is not initialized. Run releasekit init first.');
|
|
58
68
|
const raw = parseYaml(await fs.readFile(file, 'utf8'));
|
|
59
|
-
|
|
60
|
-
throw new Error('Remove history.limit from config.yaml; use export --limit instead. Omit --limit to export all releases.');
|
|
61
|
-
}
|
|
62
|
-
const config = configSchema.parse(raw);
|
|
63
|
-
if (!config.locales.includes(config.sourceLocale) || new Set(config.locales).size !== config.locales.length) {
|
|
64
|
-
throw new Error('Project locales must be unique and include the source locale.');
|
|
65
|
-
}
|
|
66
|
-
return config;
|
|
69
|
+
return parseProjectConfig(raw);
|
|
67
70
|
}
|
|
68
71
|
async requireChannel(channel) {
|
|
69
72
|
ref({ channel, version: 'check' });
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
import { checkbox, select } from '@inquirer/prompts';
|
|
2
|
+
import { type ProjectConfig } from './model.js';
|
|
1
3
|
import type { InitOptions } from './install.js';
|
|
2
4
|
export declare const commaList: (value: string) => string[];
|
|
3
5
|
export declare function parseTools(value: string): ("claude" | "codex" | "cursor")[];
|
|
4
|
-
type
|
|
5
|
-
export declare function
|
|
6
|
-
|
|
6
|
+
type Theme = ProjectConfig['visuals']['themes'];
|
|
7
|
+
export declare function selectAgentTools(context?: Parameters<typeof checkbox>[1]): Promise<("claude" | "codex" | "cursor")[]> & {
|
|
8
|
+
cancel: () => void;
|
|
9
|
+
};
|
|
10
|
+
interface SetupPrompts {
|
|
11
|
+
tools?: () => Promise<ProjectConfig['tools']>;
|
|
12
|
+
theme?: () => Promise<Theme>;
|
|
13
|
+
}
|
|
14
|
+
export declare function selectImageThemes(context?: Parameters<typeof select>[1]): Promise<"both" | "dark" | "light"> & {
|
|
15
|
+
cancel: () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function interactiveSetup(options: InitOptions, prompts?: SetupPrompts): Promise<InitOptions>;
|
|
7
18
|
export {};
|
package/dist/setup.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { stdin, stdout } from 'node:process';
|
|
4
|
-
import { configSchema, defaultConfig } from './model.js';
|
|
1
|
+
import { checkbox, select } from '@inquirer/prompts';
|
|
2
|
+
import { configSchema } from './model.js';
|
|
5
3
|
export const commaList = (value) => value.split(',').map(s => s.trim()).filter(Boolean);
|
|
6
4
|
export function parseTools(value) {
|
|
7
5
|
if (value === 'none')
|
|
@@ -11,54 +9,47 @@ export function parseTools(value) {
|
|
|
11
9
|
throw new Error('Choose codex, claude, cursor, or none.');
|
|
12
10
|
return configSchema.shape.tools.parse(values);
|
|
13
11
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
const product = options.product ?? await field('Product name', defaults.product, value => configSchema.shape.product.parse(value));
|
|
29
|
-
const tools = options.tools ?? await field('Agent tools (codex, claude, cursor; comma-separated, or none)', defaults.tools.join(','), parseTools);
|
|
30
|
-
const sourceLocale = options.sourceLocale ?? await field('Original language (locale code)', defaults.sourceLocale, value => configSchema.shape.sourceLocale.parse(value));
|
|
31
|
-
const locales = options.locales ?? await field('All languages, including the original (comma-separated locale codes)', sourceLocale, value => {
|
|
32
|
-
const values = configSchema.shape.locales.parse(commaList(value));
|
|
33
|
-
if (!values.includes(sourceLocale) || new Set(values).size !== values.length)
|
|
34
|
-
throw new Error(`Languages must be unique and include ${sourceLocale}.`);
|
|
35
|
-
return values;
|
|
36
|
-
});
|
|
37
|
-
const themes = options.themes ?? await field('Image themes (both, dark, light)', defaults.visuals.themes, value => configSchema.shape.visuals.shape.themes.parse(value));
|
|
38
|
-
return { product, tools, sourceLocale, locales, themes };
|
|
12
|
+
export function selectAgentTools(context) {
|
|
13
|
+
(context?.output ?? process.stdout).write('Choose at least one coding agent to receive ReleaseKit skills.\nUse Space to select or clear tools, then Enter to continue.\n\n');
|
|
14
|
+
return checkbox({
|
|
15
|
+
message: 'Which agent tools do you use?',
|
|
16
|
+
choices: [
|
|
17
|
+
{ name: 'Codex', value: 'codex', checked: false },
|
|
18
|
+
{ name: 'Claude Code', value: 'claude', checked: false },
|
|
19
|
+
{ name: 'Cursor', value: 'cursor', checked: false },
|
|
20
|
+
],
|
|
21
|
+
required: true,
|
|
22
|
+
loop: false,
|
|
23
|
+
shortcuts: { all: 'a', invert: 'i' },
|
|
24
|
+
}, context);
|
|
39
25
|
}
|
|
40
|
-
export function
|
|
41
|
-
|
|
26
|
+
export function selectImageThemes(context) {
|
|
27
|
+
(context?.output ?? process.stdout).write('Choose the theme variants for generated release-note illustrations.\nThis default applies to new drafts and can be changed later.\n\n');
|
|
28
|
+
return select({
|
|
29
|
+
message: 'Which image themes should new drafts use?',
|
|
30
|
+
choices: [
|
|
31
|
+
{ name: 'Both dark and light (recommended)', value: 'both',
|
|
32
|
+
description: 'Two matching versions of each illustration, one for dark backgrounds and one for light. Choose this when your product supports both themes.' },
|
|
33
|
+
{ name: 'Dark only', value: 'dark',
|
|
34
|
+
description: 'One version of each illustration for dark backgrounds. Choose this when your release notes are always shown in a dark theme.' },
|
|
35
|
+
{ name: 'Light only', value: 'light',
|
|
36
|
+
description: 'One version of each illustration for light backgrounds. Choose this when your release notes are always shown in a light theme.' },
|
|
37
|
+
],
|
|
38
|
+
default: 'both',
|
|
39
|
+
loop: false,
|
|
40
|
+
}, context);
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const abort = new AbortController();
|
|
46
|
-
const cancel = () => abort.abort();
|
|
47
|
-
terminal.on('SIGINT', cancel);
|
|
48
|
-
terminal.on('close', cancel);
|
|
42
|
+
// All other settings retain their explicit values or initProject defaults.
|
|
43
|
+
export async function interactiveSetup(options, prompts = {}) {
|
|
49
44
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return terminal.question(question, { signal: abort.signal });
|
|
54
|
-
}, message => stdout.write(`${message}\n`));
|
|
45
|
+
const tools = options.tools ?? await (prompts.tools ?? selectAgentTools)();
|
|
46
|
+
const themes = options.themes ?? await (prompts.theme ?? selectImageThemes)();
|
|
47
|
+
return { ...options, tools, themes };
|
|
55
48
|
}
|
|
56
49
|
catch (error) {
|
|
57
|
-
if (
|
|
58
|
-
throw new Error(cancelled);
|
|
50
|
+
if (error instanceof Error && ['ExitPromptError', 'AbortPromptError'].includes(error.name)) {
|
|
51
|
+
throw new Error('Setup cancelled. No configuration was written.');
|
|
52
|
+
}
|
|
59
53
|
throw error;
|
|
60
54
|
}
|
|
61
|
-
finally {
|
|
62
|
-
terminal.close();
|
|
63
|
-
}
|
|
64
55
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iodes/releasekit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Git-based visual release notes and portable agent skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"prepack": "npm run build"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@inquirer/prompts": "^7.10.1",
|
|
43
44
|
"commander": "^15.0.0",
|
|
44
45
|
"sharp": "^0.35.4",
|
|
45
46
|
"yaml": "^2.9.0",
|