@acidgreen-au/ag-cicd-cli 0.0.1 → 0.0.3

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 (3) hide show
  1. package/README.md +32 -0
  2. package/dist/cli.mjs +324 -49
  3. package/package.json +10 -8
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @acidgreen-au/ag-cicd-cli
2
+
3
+ CLI tools for Acidgreen CI/CD workflows. Provides commands for Shopify theme deployment, code quality checks, environment validation, and git automation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm install -g @acidgreen-au/ag-cicd-cli
9
+ ```
10
+
11
+ Or use directly with npx:
12
+
13
+ ```bash
14
+ pnpx @acidgreen-au/ag-cicd-cli <command>
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ ag --help
21
+ ```
22
+
23
+ For help with a specific command:
24
+
25
+ ```bash
26
+ ag <command> --help
27
+ ```
28
+
29
+ ## Requirements
30
+
31
+ - Node.js >= 24.0.0
32
+ - Shopify CLI (for theme commands)
package/dist/cli.mjs CHANGED
@@ -1,13 +1,60 @@
1
1
  #!/usr/bin/env node
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
2
5
  import { program } from "commander";
3
- import { execSync, spawn } from "node:child_process";
4
- import { readFileSync, writeFileSync } from "node:fs";
6
+ import { execSync } from "node:child_process";
7
+ import prompts from "prompts";
8
+ import { parse, stringify } from "smol-toml";
9
+ import concurrently from "concurrently";
5
10
 
6
11
  //#region src/commands/check-tag.ts
12
+ const helpText$9 = `
13
+ Details:
14
+ Reads the version from package.json and checks if a corresponding git tag
15
+ exists. Optionally creates and pushes the tag if it doesn't exist.
16
+
17
+ When --remote is specified, local tags are synced with remote before checking.
18
+
19
+ Used in CI/CD to automatically tag releases when package.json version
20
+ is bumped.
21
+
22
+ Exit Codes:
23
+ 0 Tag exists, or was successfully created
24
+ 1 Tag does not exist (when --create is not specified)
25
+
26
+ Examples:
27
+ $ ag check-tag
28
+ $ ag check-tag --create
29
+ $ ag check-tag --create --push
30
+ $ ag check-tag --create --push --remote origin
31
+ $ ag check-tag --prefix release-
32
+
33
+ GitLab CI/CD Authentication:
34
+ To push tags from GitLab CI/CD, you need to authenticate with the remote.
35
+
36
+ Create a token with write_repository scope in Settings > Access Tokens,
37
+ then add it as a masked CI/CD variable (e.g., GITLAB_TOKEN).
38
+
39
+ $ ag check-tag --create --push \\
40
+ --remote "https://oauth2:\${GITLAB_TOKEN}@\${CI_SERVER_HOST}/\${CI_PROJECT_PATH}.git"`;
41
+ function register$9(program$1) {
42
+ program$1.command("check-tag").description("Check if git tag exists for package.json version").addHelpText("after", helpText$9).option("-p, --package-path <path>", "Path to package.json", "package.json").option("--prefix <prefix>", "Tag prefix", "v").option("-c, --create", "Create the tag if it doesn't exist").option("--push", "Push the tag to remote (requires --create)").option("-r, --remote <remote>", "Remote to push to", "origin").action(checkTagCommand);
43
+ }
7
44
  function getVersionFromPackage(packagePath) {
8
45
  const content = readFileSync(packagePath, "utf-8");
9
46
  return JSON.parse(content).version;
10
47
  }
48
+ function syncTagsFromRemote(remote) {
49
+ execSync(`git fetch "${remote}" --prune --prune-tags --force`, {
50
+ encoding: "utf-8",
51
+ stdio: [
52
+ "pipe",
53
+ "pipe",
54
+ "pipe"
55
+ ]
56
+ });
57
+ }
11
58
  function tagExists(tag) {
12
59
  try {
13
60
  execSync(`git rev-parse --verify "refs/tags/${tag}"`, {
@@ -47,6 +94,7 @@ function checkTag(options) {
47
94
  const packagePath = options.packagePath ?? "package.json";
48
95
  const prefix = options.prefix ?? "v";
49
96
  const remote = options.remote ?? "origin";
97
+ if (options.remote) syncTagsFromRemote(remote);
50
98
  const version = getVersionFromPackage(packagePath);
51
99
  const tag = `${prefix}${version}`;
52
100
  if (tagExists(tag)) return {
@@ -245,6 +293,24 @@ const ALL_CHECKS = [
245
293
  "theme-check",
246
294
  "tsc"
247
295
  ];
296
+ const helpText$8 = `
297
+ Checks:
298
+ biome Runs Biome linter for TypeScript/JavaScript
299
+ prettier Checks file formatting with Prettier
300
+ theme-check Runs Shopify Theme Check for Liquid files
301
+ tsc Runs TypeScript compiler for type errors
302
+
303
+ Output:
304
+ Generates a JSON file compatible with GitLab Code Quality reports.
305
+ Each issue includes severity, file path, and line number.
306
+
307
+ Examples:
308
+ $ ag codequality
309
+ $ ag codequality --checks biome tsc
310
+ $ ag codequality --output gl-code-quality.json --path ./theme`;
311
+ function register$8(program$1) {
312
+ program$1.command("codequality").description("Run code quality checks and output GitLab-compatible JSON").addHelpText("after", helpText$8).option("-o, --output <file>", "Output JSON file path", "codequality.json").option("-p, --path <path>", "Theme directory path", "theme").option("-c, --checks <checks...>", "Specific checks to run (biome, prettier, theme-check, tsc)").action(codequality);
313
+ }
248
314
  function codequality(options) {
249
315
  const { output, path, checks = ALL_CHECKS } = options;
250
316
  const allIssues = [];
@@ -280,6 +346,28 @@ function codequality(options) {
280
346
 
281
347
  //#endregion
282
348
  //#region src/commands/commit-msg.ts
349
+ const helpText$7 = `
350
+ Details:
351
+ Validates that commit messages follow the required format:
352
+ <PREFIX>-<NUMBER> <message>
353
+
354
+ When the branch contains a ticket ID, the commit message must reference
355
+ the same ticket. Branches without ticket IDs (main, development, etc.) skip
356
+ this check.
357
+
358
+ Designed to be called from .husky/commit-msg hook:
359
+ ag commit-msg --file "$1"
360
+
361
+ Exit Codes:
362
+ 0 Commit message is valid
363
+ 1 Commit message is invalid (blocks commit)
364
+
365
+ Examples:
366
+ $ ag commit-msg --file .git/COMMIT_EDITMSG
367
+ $ ag commit-msg -f .git/COMMIT_EDITMSG --prefix PROJ`;
368
+ function register$7(program$1) {
369
+ program$1.command("commit-msg").description("Validate commit message format for git hooks").addHelpText("after", helpText$7).requiredOption("-f, --file <file>", "Path to commit message file").option("-p, --prefix <prefix>", "JIRA project prefix", "AIR").action(commitMsg);
370
+ }
283
371
  function validateCommitMsg(options) {
284
372
  const { file, prefix } = options;
285
373
  const ticketRegex = /* @__PURE__ */ new RegExp(`^${prefix}-[0-9]+`);
@@ -337,6 +425,82 @@ function commitMsg(options) {
337
425
  process.exit(0);
338
426
  }
339
427
 
428
+ //#endregion
429
+ //#region src/commands/config-shopify.ts
430
+ const helpText$6 = `
431
+ Details:
432
+ Creates or updates a shopify.theme.toml configuration file.
433
+
434
+ Without a name argument: Creates the default environment configuration.
435
+ With a name argument: Adds a new named environment, copying path and
436
+ ignore settings from the default environment.
437
+
438
+ Prompts for:
439
+ - Store name (required): Your myshopify.com store URL
440
+ - Theme ID (optional): Target theme ID for deployment
441
+
442
+ Examples:
443
+ $ ag config:shopify # Create/update default environment
444
+ $ ag config:shopify production # Add production environment
445
+ $ ag config:shopify staging --force # Overwrite staging environment`;
446
+ const DEFAULT_IGNORES = [
447
+ "templates/*.json",
448
+ "locales/*.json",
449
+ "config/settings_data.json",
450
+ "src/*",
451
+ "public/*"
452
+ ];
453
+ const DEFAULT_PATH = "theme";
454
+ function register$6(program$1) {
455
+ program$1.command("config:shopify").description("Create or update shopify.theme.toml configuration").addHelpText("after", helpText$6).argument("[name]", "Environment name (default: 'default')").option("-f, --force", "Overwrite existing environment").action(configShopify);
456
+ }
457
+ function loadConfig(configPath) {
458
+ if (!existsSync(configPath)) return { environments: {} };
459
+ return parse(readFileSync(configPath, "utf-8"));
460
+ }
461
+ function saveConfig(configPath, config) {
462
+ writeFileSync(configPath, stringify(config));
463
+ }
464
+ async function configShopify(name, options) {
465
+ const configPath = "shopify.theme.toml";
466
+ const envName = name ?? "default";
467
+ const config = loadConfig(configPath);
468
+ if (!config.environments) config.environments = {};
469
+ if (config.environments[envName] && !options.force) {
470
+ console.error(`Error: Environment '${envName}' already exists. Use --force to overwrite.`);
471
+ process.exit(1);
472
+ }
473
+ if (envName !== "default" && !config.environments.default) {
474
+ console.error("Error: Default environment must be created first. Run 'ag config:shopify' without arguments.");
475
+ process.exit(1);
476
+ }
477
+ const response = await prompts([{
478
+ type: "text",
479
+ name: "store",
480
+ message: "Store name (e.g., my-store.myshopify.com)",
481
+ validate: (value) => value.length > 0 || "Store name is required"
482
+ }, {
483
+ type: "text",
484
+ name: "themeId",
485
+ message: "Theme ID (optional, press Enter to skip)"
486
+ }], { onCancel: () => {
487
+ console.log("\nCancelled.");
488
+ process.exit(0);
489
+ } });
490
+ const defaultEnv = config.environments.default;
491
+ const path = defaultEnv?.path ?? DEFAULT_PATH;
492
+ const ignore = defaultEnv?.ignore ?? DEFAULT_IGNORES;
493
+ const envConfig = {
494
+ store: response.store,
495
+ path,
496
+ ignore
497
+ };
498
+ if (response.themeId) envConfig.theme = response.themeId;
499
+ config.environments[envName] = envConfig;
500
+ saveConfig(configPath, config);
501
+ console.log(`\n${existsSync(configPath) ? "Updated" : "Created"} ${configPath} with '${envName}' environment`);
502
+ }
503
+
340
504
  //#endregion
341
505
  //#region src/utils/shopify.ts
342
506
  function listThemes() {
@@ -375,6 +539,26 @@ const defaultIgnores = [
375
539
 
376
540
  //#endregion
377
541
  //#region src/commands/deploy.ts
542
+ const helpText$5 = `
543
+ Details:
544
+ Pushes theme files to a specific Shopify theme ID. Use this for deploying
545
+ to staging or production themes where you know the target theme ID.
546
+
547
+ Writes deployment info to deploy.env:
548
+ - PREVIEW_URL: Theme preview URL
549
+ - EDITOR_URL: Theme editor URL
550
+ - THEME_ID: Deployed theme ID
551
+
552
+ Environment:
553
+ SHOPIFY_CLI_THEME_TOKEN Shopify CLI authentication token
554
+ SHOPIFY_FLAG_STORE Shopify store URL
555
+
556
+ Examples:
557
+ $ ag deploy --theme-id 123456789
558
+ $ ag deploy -t 123456789 --path ./theme`;
559
+ function register$5(program$1) {
560
+ program$1.command("deploy").description("Deploy theme to an existing Shopify theme by ID").addHelpText("after", helpText$5).requiredOption("-t, --theme-id <id>", "Target Shopify theme ID").option("-p, --path <path>", "Theme directory path", "theme").action(deploy);
561
+ }
378
562
  function deploy(options) {
379
563
  const { themeId, path } = options;
380
564
  const themeIdNum = parseInt(themeId, 10);
@@ -387,6 +571,31 @@ function deploy(options) {
387
571
 
388
572
  //#endregion
389
573
  //#region src/commands/deploy-review.ts
574
+ const helpText$4 = `
575
+ Details:
576
+ Creates or updates an unpublished theme for reviewing branch changes.
577
+ Theme is named "AG Preview: <branch>" for easy identification.
578
+
579
+ On first deploy: Clones the live theme as a starting point.
580
+ On subsequent deploys: Updates the existing preview theme.
581
+
582
+ Writes deployment info to deploy.env:
583
+ - PREVIEW_URL: Theme preview URL
584
+ - EDITOR_URL: Theme editor URL
585
+ - THEME_ID: Preview theme ID
586
+ - EXISTING_THEME_ID: ID if theme already existed
587
+
588
+ Environment:
589
+ SHOPIFY_CLI_THEME_TOKEN Shopify CLI authentication token
590
+ SHOPIFY_FLAG_STORE Shopify store URL
591
+ CI_COMMIT_REF_NAME Git branch name (GitLab CI)
592
+
593
+ Examples:
594
+ $ ag deploy:review --branch feature/new-header
595
+ $ ag deploy:review -b $CI_COMMIT_REF_NAME`;
596
+ function register$4(program$1) {
597
+ program$1.command("deploy:review").description("Deploy a review app theme for the current branch").addHelpText("after", helpText$4).requiredOption("-b, --branch <branch>", "Git branch name").option("-p, --path <path>", "Theme directory path", "theme").action(deployReview);
598
+ }
390
599
  function deployReview(options) {
391
600
  const { branch, path } = options;
392
601
  const themeName = `AG Preview: ${branch}`;
@@ -413,48 +622,71 @@ function deployReview(options) {
413
622
 
414
623
  //#endregion
415
624
  //#region src/commands/dev.ts
416
- function dev(_options, command) {
417
- const themeArgs = command.args;
418
- console.log("Starting development servers...");
419
- const vite = spawn("pnpm", ["exec", "vite"], {
420
- stdio: "inherit",
421
- shell: true
422
- });
423
- const shopify = spawn("pnpm", [
424
- "exec",
425
- "shopify",
426
- "theme",
427
- "dev",
428
- ...themeArgs
429
- ], {
430
- stdio: "inherit",
431
- shell: true
432
- });
433
- const cleanup = () => {
434
- vite.kill();
435
- shopify.kill();
436
- process.exit(0);
437
- };
438
- process.on("SIGINT", cleanup);
439
- process.on("SIGTERM", cleanup);
440
- vite.on("close", (code) => {
441
- if (code !== 0) {
442
- console.error(`Vite exited with code ${code}`);
443
- shopify.kill();
444
- process.exit(code ?? 1);
445
- }
446
- });
447
- shopify.on("close", (code) => {
448
- if (code !== 0) {
449
- console.error(`Shopify theme dev exited with code ${code}`);
450
- vite.kill();
451
- process.exit(code ?? 1);
452
- }
625
+ const helpText$3 = `
626
+ Details:
627
+ Starts both Vite (for frontend asset compilation) and Shopify theme dev
628
+ servers in parallel. All additional arguments are passed to shopify theme dev.
629
+
630
+ Handles graceful shutdown of both processes on Ctrl+C or if either exits.
631
+
632
+ Arguments:
633
+ Any arguments after -- are passed directly to 'shopify theme dev'.
634
+ Common options include --store, --theme, --live-reload, etc.
635
+ Run \`shopify theme dev -h\` for full list of options.
636
+
637
+ Examples:
638
+ $ ag dev
639
+ $ ag dev -- --store my-store
640
+ $ ag dev -- --theme 123456789`;
641
+ function register$3(program$1) {
642
+ program$1.command("dev").description("Start Vite and Shopify theme dev servers concurrently").addHelpText("after", helpText$3).allowUnknownOption().allowExcessArguments().action(dev);
643
+ }
644
+ async function dev(_options, command) {
645
+ const { result } = concurrently([{
646
+ command: "pnpm exec vite",
647
+ name: "vite",
648
+ prefixColor: "cyan"
649
+ }, {
650
+ command: `shopify theme dev ${command.args.join(" ")}`,
651
+ name: "shopify",
652
+ prefixColor: "magenta"
653
+ }], {
654
+ killOthers: ["failure", "success"],
655
+ restartTries: 0
453
656
  });
657
+ try {
658
+ await result;
659
+ } catch {
660
+ process.exit(1);
661
+ }
454
662
  }
455
663
 
456
664
  //#endregion
457
665
  //#region src/commands/release.ts
666
+ const helpText$2 = `
667
+ Details:
668
+ Creates a new theme for production releases. Clones the current live theme
669
+ to preserve any theme editor customizations, then pushes your code changes.
670
+
671
+ Theme is named "AG Release: <name>" for easy identification.
672
+
673
+ Writes deployment info to deploy.env:
674
+ - PREVIEW_URL: Release theme preview URL
675
+ - EDITOR_URL: Release theme editor URL
676
+ - THEME_ID: New release theme ID
677
+ - PUBLISHED_THEME_ID: Current live theme ID (for rollback)
678
+
679
+ Environment:
680
+ SHOPIFY_CLI_THEME_TOKEN Shopify CLI authentication token
681
+ SHOPIFY_FLAG_STORE Shopify store URL
682
+ CI_COMMIT_TAG Git tag (GitLab CI)
683
+
684
+ Examples:
685
+ $ ag release --name v1.2.3
686
+ $ ag release -n $CI_COMMIT_TAG`;
687
+ function register$2(program$1) {
688
+ program$1.command("release").description("Create a release theme by cloning live and pushing changes").addHelpText("after", helpText$2).requiredOption("-n, --name <name>", "Release name (usually git tag)").option("-p, --path <path>", "Theme directory path", "theme").action(release);
689
+ }
458
690
  function release(options) {
459
691
  const { name, path } = options;
460
692
  const themeName = `AG Release: ${name}`;
@@ -471,6 +703,24 @@ function release(options) {
471
703
 
472
704
  //#endregion
473
705
  //#region src/commands/stop-review.ts
706
+ const helpText$1 = `
707
+ Details:
708
+ Finds and deletes the unpublished theme named "AG Preview: <branch>".
709
+ Typically called when a merge request is closed or merged.
710
+
711
+ Safe to run even if the theme doesn't exist.
712
+
713
+ Environment:
714
+ SHOPIFY_CLI_THEME_TOKEN Shopify CLI authentication token
715
+ SHOPIFY_FLAG_STORE Shopify store URL
716
+ CI_COMMIT_REF_NAME Git branch name (GitLab CI)
717
+
718
+ Examples:
719
+ $ ag stop:review --branch feature/new-header
720
+ $ ag stop:review -b $CI_COMMIT_REF_NAME`;
721
+ function register$1(program$1) {
722
+ program$1.command("stop:review").description("Delete the review app theme for a branch").addHelpText("after", helpText$1).requiredOption("-b, --branch <branch>", "Git branch name").action(stopReview);
723
+ }
474
724
  function stopReview(options) {
475
725
  const { branch } = options;
476
726
  const themeName = `AG Preview: ${branch}`;
@@ -485,6 +735,27 @@ function stopReview(options) {
485
735
 
486
736
  //#endregion
487
737
  //#region src/commands/validate-env.ts
738
+ const helpText = `
739
+ Contexts:
740
+ all Check all variables for all contexts (default)
741
+ deploy Check variables for theme deployment
742
+ review Check variables for review app deployment
743
+ release Check variables for release deployment
744
+ codequality Check variables for code quality checks
745
+
746
+ Details:
747
+ Validates that required environment variables are present before
748
+ running CI/CD jobs. Exits with code 1 if any required vars are missing.
749
+
750
+ Token values are redacted in output for security.
751
+
752
+ Examples:
753
+ $ ag validate-env
754
+ $ ag validate-env --context deploy
755
+ $ ag validate-env --vars SHOPIFY_FLAG_STORE CUSTOM_VAR`;
756
+ function register(program$1) {
757
+ program$1.command("validate-env").description("Validate required environment variables are set").addHelpText("after", helpText).option("-c, --context <context>", "Validation context (deploy, review, release, codequality, all)").option("-v, --vars <vars...>", "Specific variables to check").action(validateEnv);
758
+ }
488
759
  const commonVars = [{
489
760
  name: "SHOPIFY_CLI_THEME_TOKEN",
490
761
  required: true,
@@ -568,16 +839,20 @@ function validateEnv(options) {
568
839
 
569
840
  //#endregion
570
841
  //#region src/cli.ts
571
- program.name("agci").description("Acidgreen CI/CD CLI tools").version("1.0.0");
572
- program.command("codequality").description("Run code quality checks and combine output").option("-o, --output <file>", "Output file", "codequality.json").option("-p, --path <path>", "Theme path", "theme").option("-c, --checks <checks...>", "Checks to run (biome, prettier, theme-check, tsc)").action(codequality);
573
- program.command("deploy:review").description("Deploy a review app for the current branch").requiredOption("-b, --branch <branch>", "Branch name (CI_COMMIT_REF_NAME)").option("-p, --path <path>", "Theme path", "theme").action(deployReview);
574
- program.command("stop:review").description("Stop and delete a review app").requiredOption("-b, --branch <branch>", "Branch name (CI_COMMIT_REF_NAME)").action(stopReview);
575
- program.command("deploy").description("Deploy to a specific theme").requiredOption("-t, --theme-id <id>", "Theme ID (SHOPIFY_THEME_ID)").option("-p, --path <path>", "Theme path", "theme").action(deploy);
576
- program.command("release").description("Create a release by cloning live theme and pushing").requiredOption("-n, --name <name>", "Release name/tag").option("-p, --path <path>", "Theme path", "theme").action(release);
577
- program.command("validate-env").description("Validate required environment variables are set").option("-c, --context <context>", "Context to validate (deploy, review, release, codequality, all)").option("-v, --vars <vars...>", "Specific environment variables to check").action(validateEnv);
578
- program.command("dev").description("Start Vite and Shopify theme dev servers concurrently").allowUnknownOption().allowExcessArguments().action(dev);
579
- program.command("commit-msg").description("Validate commit message format (for git hooks)").requiredOption("-f, --file <file>", "Path to commit message file").option("-p, --prefix <prefix>", "JIRA project prefix", "AIR").action(commitMsg);
580
- program.command("check-tag").description("Check if git tag exists for package.json version").option("-p, --package-path <path>", "Path to package.json", "package.json").option("--prefix <prefix>", "Tag prefix", "v").option("-c, --create", "Create the tag if it does not exist").option("--push", "Push the tag to remote (requires --create)").option("-r, --remote <remote>", "Remote to push to", "origin").action(checkTagCommand);
842
+ const __dirname = dirname(fileURLToPath(import.meta.url));
843
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
844
+ const binName = Object.keys(pkg.bin)[0];
845
+ program.name(binName).description("Acidgreen CI/CD CLI tools for Shopify theme development").version(pkg.version);
846
+ register$8(program);
847
+ register$5(program);
848
+ register$4(program);
849
+ register$1(program);
850
+ register$2(program);
851
+ register(program);
852
+ register$3(program);
853
+ register$7(program);
854
+ register$9(program);
855
+ register$6(program);
581
856
  program.parse();
582
857
 
583
858
  //#endregion
package/package.json CHANGED
@@ -1,14 +1,10 @@
1
1
  {
2
2
  "name": "@acidgreen-au/ag-cicd-cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Acidgreen CI/CD CLI tools",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://gitlab.com/acidgreen/internal/agcicd.git"
8
- },
9
5
  "type": "module",
10
6
  "bin": {
11
- "agci": "./dist/cli.mjs"
7
+ "ag": "./dist/cli.mjs"
12
8
  },
13
9
  "main": "./dist/cli.mjs",
14
10
  "types": "./dist/cli.d.mts",
@@ -17,14 +13,20 @@
17
13
  "./package.json": "./package.json"
18
14
  },
19
15
  "files": [
20
- "dist"
16
+ "dist",
17
+ "package.json",
18
+ "README.md"
21
19
  ],
22
20
  "dependencies": {
23
- "commander": "13.1.0"
21
+ "commander": "13.1.0",
22
+ "concurrently": "^9.2.1",
23
+ "prompts": "^2.4.2",
24
+ "smol-toml": "^1.6.0"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@biomejs/biome": "^2.3.10",
27
28
  "@types/node": "24.10.4",
29
+ "@types/prompts": "^2.4.9",
28
30
  "husky": "^9.1.7",
29
31
  "tsdown": "^0.18.2",
30
32
  "typescript": "^5.9.3",