codeowners-git 2.1.0 → 2.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.
Files changed (3) hide show
  1. package/README.md +47 -3
  2. package/dist/cli.js +128 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -15,7 +15,7 @@ Managing large-scale migrations in big monorepos with multiple codeowners can be
15
15
  - **Dry-run previews** to see exactly what will happen before committing.
16
16
  - **JSON output** for piping to other tools, scripts, and agent workflows.
17
17
 
18
- > ❗❗ ❗ **Note:** Starting from v2.0.0, this tool works with **staged files**. Stage your changes with `git add` before running commands.
18
+ > ❗❗ ❗ **Note:** Starting from v2.0.0, this tool works with **staged files**. Stage your changes with `git add` before running commands. Alternatively, use `multi-branch --source <branch>` to split directly from an existing branch or PR without staging.
19
19
 
20
20
  https://github.com/user-attachments/assets/7cc0a924-f03e-47f3-baad-63eca9e8e4a8
21
21
 
@@ -221,11 +221,16 @@ Options:
221
221
  - `--append` Add commits to existing branch instead of creating a new one
222
222
  - `--pr` Create a pull request after pushing (requires `--push` and GitHub CLI)
223
223
  - `--draft-pr` Create a draft pull request after pushing (requires `--push` and GitHub CLI)
224
+ - `--pr-body` Custom PR body text (overrides the repo's PR template). Requires `--pr` or `--draft-pr`.
224
225
  - `--exclusive, -e` Only include files where the owner is the sole owner (no co-owned files)
225
226
  - `--co-owned, -c` Only include files with multiple owners (co-owned files)
227
+ - `--source, -s` Source branch or commit to extract changes from (creates a temp branch from the default branch). No staging required.
228
+ - `--compare-main` Compare source against main branch instead of detecting merge-base (use with `--source`)
226
229
  - `--dry-run` Preview the operation without making any changes
227
230
  - `--json` Output results as JSON (suppresses all other output)
228
231
 
232
+ > **Note:** `--source` cannot be used when there are staged changes.
233
+
229
234
  Example:
230
235
 
231
236
  ```bash
@@ -273,6 +278,19 @@ cg branch -i @myteam -b "feature/new" -m "Add feature" --dry-run --json
273
278
 
274
279
  # Normal execution with JSON output
275
280
  cg branch -i @myteam -b "feature/new" -m "Add feature" -p --json
281
+
282
+ # Create a PR with a custom body (overrides repo PR template)
283
+ cg branch -i @myteam -b "feature/new" -m "Add feature" -p --pr --pr-body "## Summary
284
+ Migrated files owned by @myteam.
285
+
286
+ ## Reviewer Notes
287
+ Auto-generated PR from codeowners-git."
288
+
289
+ # Extract a single team's files from an existing branch
290
+ cg branch -s feature/big-migration -i @myteam -b "feature/myteam-migration" -m "Migrate" -p --pr
291
+
292
+ # Preview what would be extracted from a source branch
293
+ cg branch -s origin/feature/big-migration -i @myteam -b "feature/myteam" -m "Migrate" --dry-run
276
294
  ```
277
295
 
278
296
  ### `multi-branch`
@@ -307,12 +325,15 @@ Options:
307
325
  - `--append` Add commits to existing branches instead of creating new ones
308
326
  - `--pr` Create pull requests after pushing (requires `--push` and GitHub CLI)
309
327
  - `--draft-pr` Create draft pull requests after pushing (requires `--push` and GitHub CLI)
328
+ - `--pr-body` Custom PR body text (overrides the repo's PR template). Requires `--pr` or `--draft-pr`. The same body is used for all branches.
310
329
  - `--exclusive, -e` Only include files where each owner is the sole owner (no co-owned files)
311
330
  - `--co-owned, -c` Only include files with multiple owners (co-owned files)
331
+ - `--source, -s` Source branch or commit to split (extracts changes onto a temp branch from the default branch). No staging required — the tool handles extraction automatically.
332
+ - `--compare-main` Compare source against main branch instead of detecting merge-base (use with `--source`)
312
333
  - `--dry-run` Preview the operation without making any changes
313
334
  - `--json` Output results as JSON (suppresses all other output)
314
335
 
315
- > **Note:** You cannot use both `--ignore` and `--include` options at the same time. You also cannot use both `--exclusive` and `--co-owned` options at the same time.
336
+ > **Note:** You cannot use both `--ignore` and `--include` options at the same time. You also cannot use both `--exclusive` and `--co-owned` options at the same time. `--source` cannot be used when there are staged changes.
316
337
 
317
338
  Example:
318
339
 
@@ -370,11 +391,27 @@ cg multi-branch -b "mig" -m "Fix" --dry-run --json | jq '.owners[] | select(.fil
370
391
 
371
392
  # Normal execution with JSON output
372
393
  cg multi-branch -b "feature/migration" -m "Migrate" -p --json
394
+
395
+ # Split an existing branch (e.g., from a PR) into per-team branches
396
+ cg multi-branch -s feature/big-migration -b "migration" -m "Migrate" -p --pr
397
+
398
+ # Split from a remote ref
399
+ cg multi-branch -s origin/feature/big-migration -b "migration" -m "Migrate" -p
400
+
401
+ # Preview a source split with dry-run
402
+ cg multi-branch -s feature/big-migration -b "migration" -m "Migrate" --dry-run
403
+
404
+ # Combine --source with path filtering
405
+ cg multi-branch -s feature/big-migration "src/services/**" -b "migration" -m "Migrate services" -p
406
+
407
+ # Use a custom PR body for all generated PRs
408
+ cg multi-branch -b "migration" -m "Migrate" -p --draft-pr --pr-body "## Summary
409
+ Auto-split migration PR by codeowner."
373
410
  ```
374
411
 
375
412
  This will:
376
413
 
377
- 1. Find all codeowners for the staged files
414
+ 1. Find all codeowners for the staged files (or from `--source` ref if provided)
378
415
  2. Apply any ignore/include filters if specified
379
416
  3. For each codeowner (e.g., @team-a, @team-b):
380
417
  - Create a branch like `feature/new-feature/team-a`
@@ -382,6 +419,13 @@ This will:
382
419
  - Add a commit message like "Add new feature - @team-a"
383
420
  - Push each branch to the remote if the `-p` flag is provided
384
421
 
422
+ When `--source` is provided, the tool automatically:
423
+
424
+ 1. Creates a temporary branch from the default branch (main/master)
425
+ 2. Extracts the changed files from the source ref
426
+ 3. Stages them and runs the normal split logic
427
+ 4. Cleans up the temporary branch and returns to your original branch
428
+
385
429
  ### `extract`
386
430
 
387
431
  Extract file changes from a source branch or commit to your working directory. This is useful when you want to copy changes from another branch to review and then stage them for committing using the `branch` command.
package/dist/cli.js CHANGED
@@ -16372,6 +16372,16 @@ var getChangedFilesBetween = async (source, target) => {
16372
16372
  throw new Error(`Failed to get changed files between ${source} and ${target || "base"}: ${error}`);
16373
16373
  }
16374
16374
  };
16375
+ var stageFiles = async (files) => {
16376
+ if (files.length === 0)
16377
+ return;
16378
+ try {
16379
+ await git.add(files);
16380
+ log.info(`Staged ${files.length} file${files.length !== 1 ? "s" : ""}`);
16381
+ } catch (error) {
16382
+ throw new Error(`Failed to stage files: ${error}`);
16383
+ }
16384
+ };
16375
16385
  var extractFilesFromRef = async (ref, files) => {
16376
16386
  try {
16377
16387
  for (const file of files) {
@@ -16696,11 +16706,16 @@ var createPullRequest = async (options) => {
16696
16706
  });
16697
16707
  };
16698
16708
  var createPRWithTemplate = async (title, branchName, options = {}) => {
16699
- const template = await findPRTemplate();
16700
16709
  let body = "";
16701
- if (template) {
16702
- body = template.content;
16703
- log.info("Using PR template for pull request body");
16710
+ if (options.prBody) {
16711
+ body = options.prBody;
16712
+ log.info("Using provided PR body");
16713
+ } else {
16714
+ const template = await findPRTemplate();
16715
+ if (template) {
16716
+ body = template.content;
16717
+ log.info("Using PR template for pull request body");
16718
+ }
16704
16719
  }
16705
16720
  return createPullRequest({
16706
16721
  title,
@@ -16859,6 +16874,8 @@ var branch = async (options) => {
16859
16874
  let operationState = options.operationState || null;
16860
16875
  const isSubOperation = !!options.operationState;
16861
16876
  let autoRecoverySucceeded = false;
16877
+ let tempBranch = null;
16878
+ let sourceOriginalBranch = null;
16862
16879
  if (options.json && !isSubOperation) {
16863
16880
  setSilent(true);
16864
16881
  }
@@ -16872,7 +16889,37 @@ var branch = async (options) => {
16872
16889
  if (options.pr && options.draftPr) {
16873
16890
  throw new Error("Cannot use both --pr and --draft-pr options");
16874
16891
  }
16875
- if (await hasUnstagedChanges()) {
16892
+ if (options.source && !isSubOperation) {
16893
+ if (await hasStagedChanges()) {
16894
+ throw new Error("Cannot use --source when there are staged changes. " + "Either commit/unstage your changes first, or omit --source to use staged files.");
16895
+ }
16896
+ sourceOriginalBranch = await getCurrentBranch();
16897
+ log.info(`Extracting changes from source: ${options.source}`);
16898
+ const defaultBranch = await getDefaultBranch();
16899
+ const compareTarget = options.compareMain ? defaultBranch : undefined;
16900
+ if (compareTarget) {
16901
+ log.info(`Comparing ${options.source} against ${compareTarget}...`);
16902
+ }
16903
+ let sourceFiles = await getChangedFilesBetween(options.source, compareTarget);
16904
+ if (sourceFiles.length === 0) {
16905
+ throw new Error(`No changed files found in ${options.source}`);
16906
+ }
16907
+ sourceFiles = filterByPathPatterns(sourceFiles, options.pathPattern);
16908
+ if (sourceFiles.length === 0) {
16909
+ throw new Error(options.pathPattern ? `No changed files found matching pattern: ${options.pathPattern}` : `No changed files found in ${options.source}`);
16910
+ }
16911
+ log.info(`Found ${sourceFiles.length} changed file${sourceFiles.length !== 1 ? "s" : ""} in source`);
16912
+ tempBranch = `cg-temp-${Date.now()}`;
16913
+ log.info(`Creating temporary branch "${tempBranch}" from "${defaultBranch}"...`);
16914
+ await checkout(defaultBranch);
16915
+ await createBranch(tempBranch);
16916
+ log.info("Extracting files from source...");
16917
+ await extractFilesFromRef(options.source, sourceFiles);
16918
+ await stageFiles(sourceFiles);
16919
+ log.info(`Files extracted and staged on temporary branch. Proceeding with branch creation...
16920
+ `);
16921
+ }
16922
+ if (!(options.source && !isSubOperation) && await hasUnstagedChanges()) {
16876
16923
  const unstagedFiles = await getUnstagedFiles();
16877
16924
  log.warn("Warning: Unstaged changes detected (these will be ignored):");
16878
16925
  unstagedFiles.forEach((file) => log.warn(` - ${file}`));
@@ -17072,7 +17119,8 @@ Excluded staged files (${excludedFiles.length}):`));
17072
17119
  const defaultBranch = await getDefaultBranch();
17073
17120
  const prResult = await createPRWithTemplate(options.message, options.branch, {
17074
17121
  draft: options.draftPr,
17075
- base: defaultBranch
17122
+ base: defaultBranch,
17123
+ prBody: options.prBody
17076
17124
  });
17077
17125
  if (prResult) {
17078
17126
  prUrl = prResult.url;
@@ -17231,15 +17279,30 @@ Auto-recovery failed. Manual recovery options:`);
17231
17279
  process.exit(1);
17232
17280
  } finally {
17233
17281
  try {
17234
- if (originalBranch) {
17235
- const currentBranch = await getCurrentBranch();
17236
- if (currentBranch !== originalBranch) {
17237
- await checkout(originalBranch);
17282
+ if (tempBranch) {
17283
+ if (sourceOriginalBranch) {
17284
+ const currentBranch = await getCurrentBranch();
17285
+ if (currentBranch !== sourceOriginalBranch) {
17286
+ log.info(`Returning to original branch "${sourceOriginalBranch}"...`);
17287
+ await checkout(sourceOriginalBranch);
17288
+ }
17289
+ }
17290
+ log.info(`Cleaning up temporary branch "${tempBranch}"...`);
17291
+ await deleteBranch(tempBranch, true);
17292
+ } else {
17293
+ if (originalBranch) {
17294
+ const currentBranch = await getCurrentBranch();
17295
+ if (currentBranch !== originalBranch) {
17296
+ await checkout(originalBranch);
17297
+ }
17238
17298
  }
17239
17299
  }
17240
17300
  } catch (finalError) {
17241
17301
  log.error(`Error during final cleanup: ${finalError}`);
17242
17302
  log.info("Some manual cleanup may be required.");
17303
+ if (tempBranch) {
17304
+ log.info(`You can manually delete the temp branch with: git branch -D ${tempBranch}`);
17305
+ }
17243
17306
  }
17244
17307
  }
17245
17308
  };
@@ -18690,6 +18753,8 @@ Operation details:`);
18690
18753
  var import_cli_table33 = __toESM(require_table(), 1);
18691
18754
  var multiBranch = async (options) => {
18692
18755
  let operationState = null;
18756
+ let tempBranch = null;
18757
+ let sourceOriginalBranch = null;
18693
18758
  if (options.json) {
18694
18759
  setSilent(true);
18695
18760
  }
@@ -18706,7 +18771,37 @@ var multiBranch = async (options) => {
18706
18771
  if (options.pr && options.draftPr) {
18707
18772
  throw new Error("Cannot use both --pr and --draft-pr options");
18708
18773
  }
18709
- if (await hasUnstagedChanges()) {
18774
+ if (options.source) {
18775
+ if (await hasStagedChanges()) {
18776
+ throw new Error("Cannot use --source when there are staged changes. " + "Either commit/unstage your changes first, or omit --source to use staged files.");
18777
+ }
18778
+ sourceOriginalBranch = await getCurrentBranch();
18779
+ log.info(`Extracting changes from source: ${options.source}`);
18780
+ const defaultBranch = await getDefaultBranch();
18781
+ const compareTarget = options.compareMain ? defaultBranch : undefined;
18782
+ if (compareTarget) {
18783
+ log.info(`Comparing ${options.source} against ${compareTarget}...`);
18784
+ }
18785
+ let sourceFiles = await getChangedFilesBetween(options.source, compareTarget);
18786
+ if (sourceFiles.length === 0) {
18787
+ throw new Error(`No changed files found in ${options.source}`);
18788
+ }
18789
+ sourceFiles = filterByPathPatterns(sourceFiles, options.pathPattern);
18790
+ if (sourceFiles.length === 0) {
18791
+ throw new Error(options.pathPattern ? `No changed files found matching pattern: ${options.pathPattern}` : `No changed files found in ${options.source}`);
18792
+ }
18793
+ log.info(`Found ${sourceFiles.length} changed file${sourceFiles.length !== 1 ? "s" : ""} in source`);
18794
+ tempBranch = `cg-temp-${Date.now()}`;
18795
+ log.info(`Creating temporary branch "${tempBranch}" from "${defaultBranch}"...`);
18796
+ await checkout(defaultBranch);
18797
+ await createBranch(tempBranch);
18798
+ log.info("Extracting files from source...");
18799
+ await extractFilesFromRef(options.source, sourceFiles);
18800
+ await stageFiles(sourceFiles);
18801
+ log.info(`Files extracted and staged on temporary branch. Proceeding with multi-branch split...
18802
+ `);
18803
+ }
18804
+ if (!options.source && await hasUnstagedChanges()) {
18710
18805
  const unstagedFiles = await getUnstagedFiles();
18711
18806
  log.warn("Warning: Unstaged changes detected (these will be ignored):");
18712
18807
  unstagedFiles.forEach((file) => log.warn(` - ${file}`));
@@ -18926,7 +19021,8 @@ Summary:`));
18926
19021
  pathPattern: options.pathPattern,
18927
19022
  exclusive: options.exclusive,
18928
19023
  coOwned: options.coOwned,
18929
- json: options.json
19024
+ json: options.json,
19025
+ prBody: options.prBody
18930
19026
  });
18931
19027
  results.push(result);
18932
19028
  }
@@ -19042,6 +19138,23 @@ Manual recovery options:`);
19042
19138
  }
19043
19139
  }
19044
19140
  process.exit(1);
19141
+ } finally {
19142
+ if (tempBranch) {
19143
+ try {
19144
+ if (sourceOriginalBranch) {
19145
+ const currentBranch = await getCurrentBranch();
19146
+ if (currentBranch !== sourceOriginalBranch) {
19147
+ log.info(`Returning to original branch "${sourceOriginalBranch}"...`);
19148
+ await checkout(sourceOriginalBranch);
19149
+ }
19150
+ }
19151
+ log.info(`Cleaning up temporary branch "${tempBranch}"...`);
19152
+ await deleteBranch(tempBranch, true);
19153
+ } catch (cleanupError) {
19154
+ log.warn(`Failed to clean up temporary branch "${tempBranch}": ${cleanupError}`);
19155
+ log.info(`You can manually delete it with: git branch -D ${tempBranch}`);
19156
+ }
19157
+ }
19045
19158
  }
19046
19159
  };
19047
19160
 
@@ -19235,7 +19348,7 @@ Next steps:`);
19235
19348
  }
19236
19349
  };
19237
19350
  // package.json
19238
- var version = "2.1.0";
19351
+ var version = "2.2.0";
19239
19352
 
19240
19353
  // src/commands/version.ts
19241
19354
  function getVersion() {
@@ -19297,7 +19410,7 @@ program2.command("list").description("Lists all git changed files by CODEOWNER")
19297
19410
  pathPattern: pattern
19298
19411
  });
19299
19412
  });
19300
- program2.command("branch").description("Create new branch with codeowner changes").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-i, --include <patterns>", "Code owner pattern to filter files").requiredOption("-b, --branch <branch>", "Branch name").requiredOption("-m, --message <message>", "Commit message").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branch to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep the created branch even if operation fails").option("--append", "Add commits to existing branch instead of creating a new one").option("--pr", "Create a pull request after pushing (requires --push)").option("--draft-pr", "Create a draft pull request after pushing (requires --push)").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").action((pattern, options) => {
19413
+ program2.command("branch").description("Create new branch with codeowner changes").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-i, --include <patterns>", "Code owner pattern to filter files").requiredOption("-b, --branch <branch>", "Branch name").requiredOption("-m, --message <message>", "Commit message").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branch to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep the created branch even if operation fails").option("--append", "Add commits to existing branch instead of creating a new one").option("--pr", "Create a pull request after pushing (requires --push)").option("--draft-pr", "Create a draft pull request after pushing (requires --push)").option("--pr-body <body>", "Custom PR body text (overrides repo PR template, requires --pr or --draft-pr)").option("-e, --exclusive", "Only include files where the owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").option("-s, --source <source>", "Source branch or commit to extract changes from (creates a temp branch from the default branch)").option("--compare-main", "Compare source against main branch instead of detecting merge-base (use with --source)").action((pattern, options) => {
19301
19414
  if (options.exclusive && options.coOwned) {
19302
19415
  console.error("Error: Cannot use both --exclusive and --co-owned options");
19303
19416
  process.exit(1);
@@ -19307,7 +19420,7 @@ program2.command("branch").description("Create new branch with codeowner changes
19307
19420
  pathPattern: pattern
19308
19421
  });
19309
19422
  });
19310
- program2.command("multi-branch").description("Create branches for all codeowners").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-b, --branch <branch>", "Base branch name (will be suffixed with codeowner name)").requiredOption("-m, --message <message>", "Base commit message (will be suffixed with codeowner name)").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branches to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name pattern (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep created branches even if operation fails").option("-d, --default-owner <defaultOwner>", "Default owner to use when no codeowners are found for changed files").option("--ignore <patterns>", "Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')").option("--include <patterns>", "Comma-separated patterns to include codeowners (e.g., 'team-*,@org/*')").option("--append", "Add commits to existing branches instead of creating new ones").option("--pr", "Create pull requests after pushing (requires --push)").option("--draft-pr", "Create draft pull requests after pushing (requires --push)").option("-e, --exclusive", "Only include files where each owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").action((pattern, options) => {
19423
+ program2.command("multi-branch").description("Create branches for all codeowners").argument("[pattern]", "Path pattern to filter files (micromatch syntax, comma-separated)").requiredOption("-b, --branch <branch>", "Base branch name (will be suffixed with codeowner name)").requiredOption("-m, --message <message>", "Base commit message (will be suffixed with codeowner name)").option("-n, --no-verify", "Skip lint-staged or any other ci checks").option("-p, --push", "Push branches to remote after commit").option("-r, --remote <remote>", "Remote name to push to", "origin").option("-u, --upstream <upstream>", "Upstream branch name pattern (defaults to local branch name)").option("-f, --force", "Force push to remote").option("-k, --keep-branch-on-failure", "Keep created branches even if operation fails").option("-d, --default-owner <defaultOwner>", "Default owner to use when no codeowners are found for changed files").option("--ignore <patterns>", "Comma-separated patterns to exclude codeowners (e.g., 'team-a,team-b')").option("--include <patterns>", "Comma-separated patterns to include codeowners (e.g., 'team-*,@org/*')").option("--append", "Add commits to existing branches instead of creating new ones").option("--pr", "Create pull requests after pushing (requires --push)").option("--draft-pr", "Create draft pull requests after pushing (requires --push)").option("--pr-body <body>", "Custom PR body text (overrides repo PR template, requires --pr or --draft-pr)").option("-e, --exclusive", "Only include files where each owner is the sole owner (no co-owners)").option("-c, --co-owned", "Only include files with multiple owners (co-owned files)").option("--dry-run", "Preview the operation without making any changes").option("--json", "Output results as JSON (suppresses all other output)").option("-s, --source <source>", "Source branch or commit to split (extracts changes onto a temp branch from the default branch)").option("--compare-main", "Compare source against main branch instead of detecting merge-base (use with --source)").action((pattern, options) => {
19311
19424
  if (options.exclusive && options.coOwned) {
19312
19425
  console.error("Error: Cannot use both --exclusive and --co-owned options");
19313
19426
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeowners-git",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "module": "src/cli.ts",
5
5
  "type": "module",
6
6
  "private": false,