@grunnverk/commands-publish 1.5.1 → 1.5.2

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/dist/index.js CHANGED
@@ -1,13 +1,16 @@
1
- import { getDryRunLogger, findDevelopmentBranch, KODRDRIV_DEFAULTS, incrementPatchVersion, incrementMajorVersion, incrementMinorVersion, DEFAULT_TO_COMMIT_ALIAS, Log, DEFAULT_MAX_DIFF_BYTES, Diff, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY, toAIConfig, createStorageAdapter, createLoggerAdapter, getOutputPath, getTimestampedResponseFilename, getTimestampedRequestFilename, filterContent, getTimestampedReleaseNotesFilename, improveContentWithLLM, validateReleaseSummary, runGitWithLock, calculateBranchDependentVersion, checkIfTagExists, confirmVersionInteractively, getLogger, ValidationError } from '@grunnverk/core';
2
- import { getCurrentBranch, run, localBranchExists, safeJsonParse, validatePackageJson, getDefaultFromRef, remoteBranchExists, runSecure, runWithDryRunSupport, validateGitRef, safeSyncBranchWithRemote, isBranchInSyncWithRemote } from '@grunnverk/git-tools';
1
+ import { getDryRunLogger, findDevelopmentBranch, KODRDRIV_DEFAULTS, incrementPatchVersion, incrementMajorVersion, incrementMinorVersion, getLogger, isDevelopmentVersion, DEFAULT_TO_COMMIT_ALIAS, Log, DEFAULT_MAX_DIFF_BYTES, Diff, DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY, toAIConfig, createStorageAdapter, createLoggerAdapter, getOutputPath, getTimestampedResponseFilename, getTimestampedRequestFilename, filterContent, getTimestampedReleaseNotesFilename, improveContentWithLLM, validateReleaseSummary, runGitWithLock, calculateBranchDependentVersion, checkIfTagExists, confirmVersionInteractively, ValidationError } from '@grunnverk/core';
2
+ import { getCurrentBranch, run, localBranchExists, safeJsonParse, validatePackageJson, getGitStatusSummary, getLinkedDependencies, getDefaultFromRef, remoteBranchExists, runSecure, runWithDryRunSupport, validateGitRef, safeSyncBranchWithRemote, isBranchInSyncWithRemote } from '@grunnverk/git-tools';
3
3
  import { createStorage, calculateTargetVersion, incrementPatchVersion as incrementPatchVersion$1 } from '@grunnverk/shared';
4
- import path from 'path';
4
+ import { scanForPackageJsonFiles } from '@grunnverk/tree-core';
5
+ import * as GitHub from '@grunnverk/github-tools';
6
+ import { getOctokit } from '@grunnverk/github-tools';
7
+ import fs, { readFile } from 'fs/promises';
8
+ import * as path from 'path';
9
+ import path__default from 'path';
5
10
  import * as Commit from '@grunnverk/commands-git';
6
11
  import { Formatter } from '@riotprompt/riotprompt';
7
12
  import 'dotenv/config';
8
13
  import { runAgenticRelease, requireTTY, generateReflectionReport, getUserChoice, STANDARD_CHOICES, getLLMFeedbackInEditor, editContentInEditor, createCompletionWithRetry, createReleasePrompt, runAgenticPublish, formatAgenticPublishResult } from '@grunnverk/ai-service';
9
- import * as GitHub from '@grunnverk/github-tools';
10
- import fs from 'fs/promises';
11
14
 
12
15
  /**
13
16
  * Create retroactive working branch tags for past releases
@@ -84,7 +87,7 @@ import fs from 'fs/promises';
84
87
  }
85
88
  /**
86
89
  * Execute the development command
87
- */ const execute$2 = async (runConfig)=>{
90
+ */ const execute$3 = async (runConfig)=>{
88
91
  const isDryRun = runConfig.dryRun || false;
89
92
  const logger = getDryRunLogger(isDryRun);
90
93
  logger.info('DEV_BRANCH_NAVIGATION: Navigating to working branch for development | Purpose: Start development cycle | Next: Version bump and sync');
@@ -340,10 +343,18 @@ import fs from 'fs/promises';
340
343
  try {
341
344
  const packageJson = JSON.parse(await fs.readFile('package.json', 'utf-8'));
342
345
  const currentVersion = packageJson.version;
343
- // If current version already has the dev tag, we're done
346
+ // If current version already has the dev tag, check if base version is published
344
347
  if (currentVersion.includes(`-${prereleaseTag}.`)) {
345
- logger.info(`DEV_ALREADY_DEV_VERSION: Already on working branch with development version | Branch: ${workingBranch} | Version: ${currentVersion} | Status: no-bump-needed`);
346
- return 'Already on working branch with development version';
348
+ // Check if base version is already published on npm
349
+ const baseVersion = currentVersion.split('-')[0];
350
+ const { getNpmPublishedVersion } = await import('@grunnverk/core');
351
+ const publishedVersion = await getNpmPublishedVersion(packageJson.name);
352
+ if (publishedVersion !== baseVersion) {
353
+ logger.info(`DEV_ALREADY_DEV_VERSION: Already on working branch with development version | Branch: ${workingBranch} | Version: ${currentVersion} | Status: no-bump-needed`);
354
+ return 'Already on working branch with development version';
355
+ }
356
+ logger.warn(`DEV_VERSION_CONFLICT: Base version already published | Current: ${currentVersion} | Published: ${publishedVersion} | Action: Will bump to next dev version`);
357
+ // Continue with version bump logic
347
358
  }
348
359
  } catch {
349
360
  logger.debug('Could not check current version, proceeding with version bump');
@@ -518,6 +529,384 @@ import fs from 'fs/promises';
518
529
  }
519
530
  };
520
531
 
532
+ /**
533
+ * Default patterns for subprojects to exclude from scanning
534
+ */ const DEFAULT_EXCLUDE_SUBPROJECTS = [
535
+ 'doc/',
536
+ 'docs/',
537
+ 'examples/',
538
+ 'test-*/'
539
+ ];
540
+ /**
541
+ * Execute check-development command
542
+ */ async function execute$2(config) {
543
+ var _config_validateReleaseWorkflow;
544
+ var _config_tree_directories, _config_tree;
545
+ const logger = getLogger();
546
+ // Get directory from config - check multiple possible locations
547
+ const directory = config.directory || ((_config_tree = config.tree) === null || _config_tree === void 0 ? void 0 : (_config_tree_directories = _config_tree.directories) === null || _config_tree_directories === void 0 ? void 0 : _config_tree_directories[0]) || process.cwd();
548
+ // Get validateRelease flag - controls merge conflicts and open PRs checks
549
+ const validateRelease = (_config_validateReleaseWorkflow = config.validateReleaseWorkflow) !== null && _config_validateReleaseWorkflow !== void 0 ? _config_validateReleaseWorkflow : false;
550
+ logger.info(`Checking development readiness in ${directory}${validateRelease ? ' (full release validation)' : ' (quick check)'}`);
551
+ // Build exclusion patterns
552
+ const excludedPatterns = [
553
+ '**/node_modules/**',
554
+ '**/dist/**',
555
+ '**/build/**',
556
+ '**/.git/**',
557
+ ...DEFAULT_EXCLUDE_SUBPROJECTS.map((pattern)=>{
558
+ const normalized = pattern.endsWith('/') ? pattern.slice(0, -1) : pattern;
559
+ return `**/${normalized}/**`;
560
+ })
561
+ ];
562
+ // Determine if this is a tree or single package
563
+ const packageJsonFiles = await scanForPackageJsonFiles(directory, excludedPatterns);
564
+ const isTree = packageJsonFiles.length > 1;
565
+ logger.info(`Detected ${isTree ? 'tree' : 'single package'} with ${packageJsonFiles.length} package(s)`);
566
+ const checks = {
567
+ branch: {
568
+ passed: true,
569
+ issues: []
570
+ },
571
+ remoteSync: {
572
+ passed: true,
573
+ issues: []
574
+ },
575
+ mergeConflicts: {
576
+ passed: true,
577
+ issues: [],
578
+ warnings: []
579
+ },
580
+ devVersion: {
581
+ passed: true,
582
+ issues: []
583
+ },
584
+ linkStatus: {
585
+ passed: true,
586
+ issues: [],
587
+ warnings: []
588
+ },
589
+ openPRs: {
590
+ passed: true,
591
+ issues: [],
592
+ warnings: []
593
+ },
594
+ releaseWorkflow: {
595
+ passed: true,
596
+ issues: [],
597
+ warnings: []
598
+ }
599
+ };
600
+ const packagesToCheck = isTree ? packageJsonFiles : [
601
+ path.join(directory, 'package.json')
602
+ ];
603
+ // Build a set of all local package names for link status checking
604
+ const localPackageNames = new Set();
605
+ for (const pkgJsonPath of packagesToCheck){
606
+ try {
607
+ const pkgJsonContent = await readFile(pkgJsonPath, 'utf-8');
608
+ const pkgJson = JSON.parse(pkgJsonContent);
609
+ if (pkgJson.name) {
610
+ localPackageNames.add(pkgJson.name);
611
+ }
612
+ } catch {
613
+ // Skip packages we can't read
614
+ }
615
+ }
616
+ for (const pkgJsonPath of packagesToCheck){
617
+ var _pkgJson_repository;
618
+ const pkgDir = path.dirname(pkgJsonPath);
619
+ const pkgJsonContent = await readFile(pkgJsonPath, 'utf-8');
620
+ const pkgJson = JSON.parse(pkgJsonContent);
621
+ const pkgName = pkgJson.name || path.basename(pkgDir);
622
+ // 1. Check branch status
623
+ try {
624
+ const gitStatus = await getGitStatusSummary(pkgDir);
625
+ if (gitStatus.branch === 'main' || gitStatus.branch === 'master') {
626
+ checks.branch.passed = false;
627
+ checks.branch.issues.push(`${pkgName} is on ${gitStatus.branch} branch`);
628
+ }
629
+ } catch (error) {
630
+ checks.branch.issues.push(`${pkgName}: Could not check branch - ${error.message || error}`);
631
+ }
632
+ // 2. Check remote sync status
633
+ try {
634
+ await run('git fetch', {
635
+ cwd: pkgDir
636
+ });
637
+ const { stdout: statusOutput } = await run('git status -sb', {
638
+ cwd: pkgDir
639
+ });
640
+ if (statusOutput.includes('behind')) {
641
+ checks.remoteSync.passed = false;
642
+ const match = statusOutput.match(/behind (\d+)/);
643
+ const count = match ? match[1] : 'some';
644
+ checks.remoteSync.issues.push(`${pkgName} is ${count} commits behind remote`);
645
+ }
646
+ } catch (error) {
647
+ checks.remoteSync.issues.push(`${pkgName}: Could not check remote sync - ${error.message || error}`);
648
+ }
649
+ // 3. Check for merge conflicts with target branch (main) - ALWAYS CHECK THIS
650
+ try {
651
+ const gitStatus = await getGitStatusSummary(pkgDir);
652
+ const currentBranch = gitStatus.branch;
653
+ const targetBranch = 'main'; // The branch we'll merge into during publish
654
+ // Skip if we're already on main
655
+ if (currentBranch !== 'main' && currentBranch !== 'master') {
656
+ // Fetch latest to ensure we have up-to-date refs
657
+ await run('git fetch origin', {
658
+ cwd: pkgDir
659
+ });
660
+ // Try a test merge to detect conflicts
661
+ // Use --no-commit --no-ff to simulate the merge without actually doing it
662
+ try {
663
+ // Check if there would be conflicts using git merge --no-commit --no-ff
664
+ // This is safer as it doesn't modify the working tree
665
+ await run(`git merge --no-commit --no-ff origin/${targetBranch}`, {
666
+ cwd: pkgDir
667
+ });
668
+ // If we get here, check if there are conflicts
669
+ const { stdout: statusAfterMerge } = await run('git status --porcelain', {
670
+ cwd: pkgDir
671
+ });
672
+ if (statusAfterMerge.includes('UU ') || statusAfterMerge.includes('AA ') || statusAfterMerge.includes('DD ') || statusAfterMerge.includes('AU ') || statusAfterMerge.includes('UA ') || statusAfterMerge.includes('DU ') || statusAfterMerge.includes('UD ')) {
673
+ checks.mergeConflicts.passed = false;
674
+ checks.mergeConflicts.issues.push(`${pkgName}: Merge conflicts detected with ${targetBranch} branch`);
675
+ }
676
+ // Abort the test merge (only if there's actually a merge in progress)
677
+ try {
678
+ await run('git merge --abort', {
679
+ cwd: pkgDir
680
+ });
681
+ } catch {
682
+ // Ignore - there might not be a merge to abort if it was a fast-forward
683
+ }
684
+ } catch (mergeError) {
685
+ var _mergeError_message, _mergeError_stderr;
686
+ // Abort any partial merge
687
+ try {
688
+ await run('git merge --abort', {
689
+ cwd: pkgDir
690
+ });
691
+ } catch {
692
+ // Ignore abort errors
693
+ }
694
+ // If merge failed, there are likely conflicts
695
+ if (((_mergeError_message = mergeError.message) === null || _mergeError_message === void 0 ? void 0 : _mergeError_message.includes('CONFLICT')) || ((_mergeError_stderr = mergeError.stderr) === null || _mergeError_stderr === void 0 ? void 0 : _mergeError_stderr.includes('CONFLICT'))) {
696
+ checks.mergeConflicts.passed = false;
697
+ checks.mergeConflicts.issues.push(`${pkgName}: Merge conflicts detected with ${targetBranch} branch`);
698
+ } else {
699
+ // Some other error - log as warning
700
+ checks.mergeConflicts.warnings.push(`${pkgName}: Could not check for merge conflicts - ${mergeError.message || mergeError}`);
701
+ }
702
+ }
703
+ }
704
+ } catch (error) {
705
+ checks.mergeConflicts.warnings.push(`${pkgName}: Could not check for merge conflicts - ${error.message || error}`);
706
+ }
707
+ // 4. Check dev version status
708
+ const version = pkgJson.version;
709
+ if (!version) {
710
+ checks.devVersion.issues.push(`${pkgName}: No version field in package.json`);
711
+ } else if (!isDevelopmentVersion(version)) {
712
+ checks.devVersion.passed = false;
713
+ checks.devVersion.issues.push(`${pkgName} has non-dev version: ${version}`);
714
+ } else {
715
+ // Check if base version exists on npm
716
+ const baseVersion = version.split('-')[0];
717
+ try {
718
+ const { stdout } = await run(`npm view ${pkgName}@${baseVersion} version`, {
719
+ cwd: pkgDir
720
+ });
721
+ if (stdout.trim() === baseVersion) {
722
+ checks.devVersion.passed = false;
723
+ checks.devVersion.issues.push(`${pkgName}: Base version ${baseVersion} already published (current: ${version})`);
724
+ }
725
+ } catch {
726
+ // Version doesn't exist on npm, which is good
727
+ }
728
+ }
729
+ // 5. Check link status (warning only - links are recommended but not required)
730
+ if (pkgJson.dependencies || pkgJson.devDependencies) {
731
+ try {
732
+ const linkedDeps = await getLinkedDependencies(pkgDir);
733
+ const allDeps = {
734
+ ...pkgJson.dependencies,
735
+ ...pkgJson.devDependencies
736
+ };
737
+ const localDeps = Object.keys(allDeps).filter((dep)=>localPackageNames.has(dep));
738
+ const unlinkedLocal = localDeps.filter((dep)=>!linkedDeps.has(dep));
739
+ if (unlinkedLocal.length > 0) {
740
+ // Don't fail the check, just warn - links are recommended but not required
741
+ checks.linkStatus.warnings.push(`${pkgName}: Local dependencies not linked (recommended): ${unlinkedLocal.join(', ')}`);
742
+ }
743
+ } catch (error) {
744
+ checks.linkStatus.warnings.push(`${pkgName}: Could not check link status - ${error.message || error}`);
745
+ }
746
+ }
747
+ // 6. Check for open PRs from working branch - only if validateRelease is true
748
+ if (validateRelease && ((_pkgJson_repository = pkgJson.repository) === null || _pkgJson_repository === void 0 ? void 0 : _pkgJson_repository.url)) {
749
+ try {
750
+ const gitStatus = await getGitStatusSummary(pkgDir);
751
+ const currentBranch = gitStatus.branch;
752
+ // Extract owner/repo from repository URL
753
+ const repoUrl = pkgJson.repository.url;
754
+ const match = repoUrl.match(/github\.com[:/]([^/]+)\/([^/.]+)/);
755
+ if (match) {
756
+ const [, owner, repo] = match;
757
+ try {
758
+ const octokit = getOctokit();
759
+ const { data: openPRs } = await octokit.pulls.list({
760
+ owner,
761
+ repo,
762
+ state: 'open',
763
+ head: `${owner}:${currentBranch}`
764
+ });
765
+ if (openPRs.length > 0) {
766
+ checks.openPRs.passed = false;
767
+ for (const pr of openPRs){
768
+ const prInfo = `PR #${pr.number}: ${pr.title} (${pr.html_url})`;
769
+ checks.openPRs.issues.push(`${pkgName}: ${prInfo}`);
770
+ }
771
+ }
772
+ } catch (prError) {
773
+ var _prError_message;
774
+ // Only log if it's not a 404 (repo might not exist on GitHub)
775
+ if (!((_prError_message = prError.message) === null || _prError_message === void 0 ? void 0 : _prError_message.includes('404')) && (!prError.status || prError.status !== 404)) {
776
+ checks.openPRs.warnings.push(`${pkgName}: Could not check PRs - ${prError.message || prError}`);
777
+ }
778
+ }
779
+ }
780
+ } catch (error) {
781
+ // Don't fail the check if we can't check PRs
782
+ checks.openPRs.warnings.push(`${pkgName}: Could not check for open PRs - ${error.message || error}`);
783
+ }
784
+ }
785
+ // 7. Check release workflow readiness (validate build, test, publish dry-run)
786
+ // This check validates that the package can be successfully published
787
+ // Only run if explicitly requested via config flag
788
+ if (config.validateReleaseWorkflow) {
789
+ try {
790
+ logger.info(`${pkgName}: Validating release workflow readiness...`);
791
+ // Check 1: Build succeeds
792
+ try {
793
+ await run('npm run build', {
794
+ cwd: pkgDir
795
+ });
796
+ logger.debug(`${pkgName}: Build check passed`);
797
+ } catch (buildError) {
798
+ checks.releaseWorkflow.passed = false;
799
+ checks.releaseWorkflow.issues.push(`${pkgName}: Build fails - ${buildError.message || buildError}`);
800
+ }
801
+ // Check 2: Tests pass
802
+ try {
803
+ await run('npm test', {
804
+ cwd: pkgDir
805
+ });
806
+ logger.debug(`${pkgName}: Test check passed`);
807
+ } catch (testError) {
808
+ checks.releaseWorkflow.passed = false;
809
+ checks.releaseWorkflow.issues.push(`${pkgName}: Tests fail - ${testError.message || testError}`);
810
+ }
811
+ // Check 3: Publish dry-run succeeds
812
+ try {
813
+ await run('npm publish --dry-run', {
814
+ cwd: pkgDir
815
+ });
816
+ logger.debug(`${pkgName}: Publish dry-run check passed`);
817
+ } catch (publishError) {
818
+ checks.releaseWorkflow.passed = false;
819
+ checks.releaseWorkflow.issues.push(`${pkgName}: Publish dry-run fails - ${publishError.message || publishError}`);
820
+ }
821
+ // Check 4: NPM_TOKEN environment variable
822
+ if (!process.env.NPM_TOKEN) {
823
+ checks.releaseWorkflow.warnings.push(`${pkgName}: NPM_TOKEN environment variable not set (required for publishing)`);
824
+ }
825
+ // Check 5: GitHub workflow file exists
826
+ const workflowPath = path.join(pkgDir, '.github', 'workflows', 'npm-publish.yml');
827
+ try {
828
+ await readFile(workflowPath, 'utf-8');
829
+ logger.debug(`${pkgName}: GitHub workflow file exists`);
830
+ } catch {
831
+ checks.releaseWorkflow.warnings.push(`${pkgName}: GitHub workflow file not found at .github/workflows/npm-publish.yml`);
832
+ }
833
+ } catch (error) {
834
+ checks.releaseWorkflow.warnings.push(`${pkgName}: Could not validate release workflow - ${error.message || error}`);
835
+ }
836
+ }
837
+ }
838
+ // Build summary - linkStatus and releaseWorkflow are not included in allPassed (recommendations)
839
+ // mergeConflicts is ALWAYS checked (critical for preventing post-merge failures)
840
+ // openPRs is only checked when validateRelease is true
841
+ const allPassed = checks.branch.passed && checks.remoteSync.passed && checks.mergeConflicts.passed && checks.devVersion.passed && (validateRelease ? checks.openPRs.passed : true);
842
+ const hasWarnings = checks.linkStatus.warnings.length > 0 || checks.mergeConflicts.warnings.length > 0 || checks.openPRs.warnings.length > 0 || checks.releaseWorkflow.warnings.length > 0;
843
+ // Log results
844
+ let summary = `\n${'='.repeat(60)}\n`;
845
+ summary += `Development Readiness Check${validateRelease ? ' (Full Release Validation)' : ' (Quick Check)'}\n`;
846
+ summary += `${'='.repeat(60)}\n\n`;
847
+ summary += `Type: ${isTree ? 'Tree (monorepo)' : 'Single package'}\n`;
848
+ summary += `Packages checked: ${packagesToCheck.length}\n\n`;
849
+ if (allPassed) {
850
+ summary += `✅ Status: READY FOR DEVELOPMENT\n\n`;
851
+ summary += `All required checks passed:\n`;
852
+ summary += ` ✓ Branch status\n`;
853
+ summary += ` ✓ Remote sync\n`;
854
+ summary += ` ✓ No merge conflicts with main\n`;
855
+ summary += ` ✓ Dev versions\n`;
856
+ if (validateRelease) {
857
+ summary += ` ✓ No open PRs\n`;
858
+ }
859
+ if (!hasWarnings) {
860
+ summary += ` ✓ All local dependencies linked\n`;
861
+ }
862
+ } else {
863
+ summary += `⚠️ Status: NOT READY\n\n`;
864
+ if (!checks.branch.passed) {
865
+ summary += `❌ Branch Issues:\n`;
866
+ checks.branch.issues.forEach((issue)=>summary += ` - ${issue}\n`);
867
+ summary += `\n`;
868
+ }
869
+ if (!checks.remoteSync.passed) {
870
+ summary += `❌ Remote Sync Issues:\n`;
871
+ checks.remoteSync.issues.forEach((issue)=>summary += ` - ${issue}\n`);
872
+ summary += `\n`;
873
+ }
874
+ if (!checks.mergeConflicts.passed) {
875
+ summary += `❌ Merge Conflict Issues:\n`;
876
+ checks.mergeConflicts.issues.forEach((issue)=>summary += ` - ${issue}\n`);
877
+ summary += `\n`;
878
+ }
879
+ if (!checks.devVersion.passed) {
880
+ summary += `❌ Dev Version Issues:\n`;
881
+ checks.devVersion.issues.forEach((issue)=>summary += ` - ${issue}\n`);
882
+ summary += `\n`;
883
+ }
884
+ if (validateRelease && !checks.openPRs.passed) {
885
+ summary += `❌ Open PR Issues:\n`;
886
+ checks.openPRs.issues.forEach((issue)=>summary += ` - ${issue}\n`);
887
+ summary += `\n`;
888
+ }
889
+ if (!checks.releaseWorkflow.passed) {
890
+ summary += `❌ Release Workflow Issues:\n`;
891
+ checks.releaseWorkflow.issues.forEach((issue)=>summary += ` - ${issue}\n`);
892
+ summary += `\n`;
893
+ }
894
+ }
895
+ // Log warnings separately (non-blocking)
896
+ if (hasWarnings) {
897
+ summary += `⚠️ Recommendations:\n`;
898
+ checks.linkStatus.warnings.forEach((warning)=>summary += ` - ${warning}\n`);
899
+ checks.mergeConflicts.warnings.forEach((warning)=>summary += ` - ${warning}\n`);
900
+ if (validateRelease) {
901
+ checks.openPRs.warnings.forEach((warning)=>summary += ` - ${warning}\n`);
902
+ }
903
+ checks.releaseWorkflow.warnings.forEach((warning)=>summary += ` - ${warning}\n`);
904
+ summary += `\n`;
905
+ }
906
+ summary += `${'='.repeat(60)}\n`;
907
+ return summary;
908
+ }
909
+
521
910
  // Helper function to read context files
522
911
  async function readContextFiles(contextFiles, logger) {
523
912
  if (!contextFiles || contextFiles.length === 0) {
@@ -873,7 +1262,7 @@ const execute$1 = async (runConfig)=>{
873
1262
 
874
1263
  const scanNpmrcForEnvVars = async (storage)=>{
875
1264
  const logger = getLogger();
876
- const npmrcPath = path.join(process.cwd(), '.npmrc');
1265
+ const npmrcPath = path__default.join(process.cwd(), '.npmrc');
877
1266
  const envVars = [];
878
1267
  if (await storage.exists(npmrcPath)) {
879
1268
  try {
@@ -903,7 +1292,7 @@ const scanNpmrcForEnvVars = async (storage)=>{
903
1292
  * development artifacts and sensitive files.
904
1293
  */ const checkGitignorePatterns = async (storage, isDryRun)=>{
905
1294
  const logger = getDryRunLogger(isDryRun);
906
- const gitignorePath = path.join(process.cwd(), '.gitignore');
1295
+ const gitignorePath = path__default.join(process.cwd(), '.gitignore');
907
1296
  // Required patterns that must be present in .gitignore
908
1297
  const requiredPatterns = [
909
1298
  'node_modules',
@@ -995,7 +1384,7 @@ const scanNpmrcForEnvVars = async (storage)=>{
995
1384
  * and cleans them up if found by removing package-lock.json and regenerating it.
996
1385
  */ const cleanupNpmLinkReferences = async (isDryRun)=>{
997
1386
  const logger = getDryRunLogger(isDryRun);
998
- const packageLockPath = path.join(process.cwd(), 'package-lock.json');
1387
+ const packageLockPath = path__default.join(process.cwd(), 'package-lock.json');
999
1388
  try {
1000
1389
  // Check if package-lock.json exists
1001
1390
  try {
@@ -1239,7 +1628,7 @@ const runPrechecks = async (runConfig, targetBranch)=>{
1239
1628
  }
1240
1629
  // Check if prepublishOnly script exists in package.json
1241
1630
  logger.info('PRECHECK_PREPUBLISH: Checking for prepublishOnly script in package.json | Requirement: Must exist to run pre-flight checks | Expected: clean, lint, build, test');
1242
- const packageJsonPath = path.join(process.cwd(), 'package.json');
1631
+ const packageJsonPath = path__default.join(process.cwd(), 'package.json');
1243
1632
  if (!await storage.exists(packageJsonPath)) {
1244
1633
  if (!isDryRun) {
1245
1634
  throw new Error('package.json not found in current directory.');
@@ -1421,6 +1810,63 @@ const execute = async (runConfig)=>{
1421
1810
  currentBranch = 'mock-branch';
1422
1811
  } else {
1423
1812
  currentBranch = await GitHub.getCurrentBranchName();
1813
+ }
1814
+ // Determine target branch EARLY (before expensive operations) for early necessity check
1815
+ let targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
1816
+ let branchDependentVersioning = false;
1817
+ // Check for branches configuration
1818
+ if (runConfig.branches && runConfig.branches[currentBranch]) {
1819
+ branchDependentVersioning = true;
1820
+ const branchConfig = runConfig.branches[currentBranch];
1821
+ if (branchConfig.targetBranch) {
1822
+ targetBranch = branchConfig.targetBranch;
1823
+ }
1824
+ logger.info(`BRANCH_DEPENDENT_TARGETING: Branch-specific configuration active | Source: ${currentBranch} | Target: ${targetBranch} | Feature: Branch-dependent versioning and targeting`);
1825
+ logger.info(`BRANCH_CONFIGURATION_SOURCE: Current branch | Branch: ${currentBranch} | Type: source`);
1826
+ logger.info(`BRANCH_CONFIGURATION_TARGET: Target branch for publish | Branch: ${targetBranch} | Type: destination`);
1827
+ // Look at target branch config to show version strategy
1828
+ const targetBranchConfig = runConfig.branches[targetBranch];
1829
+ if (targetBranchConfig === null || targetBranchConfig === void 0 ? void 0 : targetBranchConfig.version) {
1830
+ const versionType = targetBranchConfig.version.type;
1831
+ const versionTag = targetBranchConfig.version.tag;
1832
+ const versionIncrement = targetBranchConfig.version.increment;
1833
+ logger.info(`VERSION_STRATEGY: Target branch version configuration | Branch: ${targetBranch} | Type: ${versionType} | Tag: ${versionTag || 'none'} | Increment: ${versionIncrement ? 'enabled' : 'disabled'}`);
1834
+ }
1835
+ } else {
1836
+ logger.debug(`BRANCH_TARGETING_DEFAULT: No branch-specific configuration found | Branch: ${currentBranch} | Action: Using default target | Target: ${targetBranch}`);
1837
+ }
1838
+ // Handle --sync-target flag
1839
+ if ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.syncTarget) {
1840
+ await handleTargetBranchSyncRecovery(runConfig, targetBranch);
1841
+ return; // Exit after sync operation
1842
+ }
1843
+ // OPTIMIZATION: Early check if release is necessary BEFORE expensive operations
1844
+ // This can save 2-4 minutes for packages with no changes by skipping:
1845
+ // - git fetch (~30-60s)
1846
+ // - current branch sync (~30-60s)
1847
+ // - target branch setup (~30-60s)
1848
+ // - prechecks (~30-60s)
1849
+ if (!isDryRun) {
1850
+ logger.info('RELEASE_NECESSITY_CHECK_EARLY: Quick check if release is required | Comparison: current branch vs target | Target: ' + targetBranch + ' | Purpose: Skip expensive operations for unchanged packages');
1851
+ try {
1852
+ const necessity = await isReleaseNecessaryComparedToTarget(targetBranch, isDryRun);
1853
+ if (!necessity.necessary) {
1854
+ logger.info(`\nRELEASE_SKIPPED_EARLY: No meaningful changes detected, skipping publish | Reason: ${necessity.reason} | Target: ${targetBranch} | Time saved: ~2-4 minutes`);
1855
+ // Emit a machine-readable marker so tree mode can detect skip and avoid propagating versions
1856
+ // CRITICAL: Use console.log to write to stdout (logger.info goes to stderr via winston)
1857
+ // eslint-disable-next-line no-console
1858
+ console.log('KODRDRIV_PUBLISH_SKIPPED');
1859
+ return;
1860
+ } else {
1861
+ logger.verbose(`RELEASE_PROCEEDING_EARLY: Meaningful changes detected, continuing with full publish workflow | Reason: ${necessity.reason} | Target: ${targetBranch}`);
1862
+ }
1863
+ } catch (error) {
1864
+ // If early check fails (e.g., target branch doesn't exist yet), continue with normal flow
1865
+ logger.verbose(`RELEASE_NECESSITY_CHECK_EARLY_SKIPPED: Unable to perform early check | Error: ${error.message} | Action: Continuing with full workflow | Reason: Target branch may not exist yet or other setup needed`);
1866
+ }
1867
+ }
1868
+ // Now perform expensive operations only if we're proceeding with publish
1869
+ if (!isDryRun) {
1424
1870
  // Fetch latest remote information to avoid conflicts
1425
1871
  logger.info('GIT_FETCH_STARTING: Fetching latest remote information | Remote: origin | Purpose: Avoid conflicts during publish | Command: git fetch origin');
1426
1872
  try {
@@ -1457,35 +1903,6 @@ const execute = async (runConfig)=>{
1457
1903
  }
1458
1904
  }
1459
1905
  }
1460
- // Determine target branch and version strategy based on branch configuration
1461
- let targetBranch = ((_runConfig_publish = runConfig.publish) === null || _runConfig_publish === void 0 ? void 0 : _runConfig_publish.targetBranch) || 'main';
1462
- let branchDependentVersioning = false;
1463
- // Check for branches configuration
1464
- if (runConfig.branches && runConfig.branches[currentBranch]) {
1465
- branchDependentVersioning = true;
1466
- const branchConfig = runConfig.branches[currentBranch];
1467
- if (branchConfig.targetBranch) {
1468
- targetBranch = branchConfig.targetBranch;
1469
- }
1470
- logger.info(`BRANCH_DEPENDENT_TARGETING: Branch-specific configuration active | Source: ${currentBranch} | Target: ${targetBranch} | Feature: Branch-dependent versioning and targeting`);
1471
- logger.info(`BRANCH_CONFIGURATION_SOURCE: Current branch | Branch: ${currentBranch} | Type: source`);
1472
- logger.info(`BRANCH_CONFIGURATION_TARGET: Target branch for publish | Branch: ${targetBranch} | Type: destination`);
1473
- // Look at target branch config to show version strategy
1474
- const targetBranchConfig = runConfig.branches[targetBranch];
1475
- if (targetBranchConfig === null || targetBranchConfig === void 0 ? void 0 : targetBranchConfig.version) {
1476
- const versionType = targetBranchConfig.version.type;
1477
- const versionTag = targetBranchConfig.version.tag;
1478
- const versionIncrement = targetBranchConfig.version.increment;
1479
- logger.info(`VERSION_STRATEGY: Target branch version configuration | Branch: ${targetBranch} | Type: ${versionType} | Tag: ${versionTag || 'none'} | Increment: ${versionIncrement ? 'enabled' : 'disabled'}`);
1480
- }
1481
- } else {
1482
- logger.debug(`BRANCH_TARGETING_DEFAULT: No branch-specific configuration found | Branch: ${currentBranch} | Action: Using default target | Target: ${targetBranch}`);
1483
- }
1484
- // Handle --sync-target flag
1485
- if ((_runConfig_publish1 = runConfig.publish) === null || _runConfig_publish1 === void 0 ? void 0 : _runConfig_publish1.syncTarget) {
1486
- await handleTargetBranchSyncRecovery(runConfig, targetBranch);
1487
- return; // Exit after sync operation
1488
- }
1489
1906
  // Check if target branch exists and create it if needed
1490
1907
  logger.info(`TARGET_BRANCH_CHECK: Verifying target branch existence | Branch: ${targetBranch} | Action: Create if missing | Source: Current HEAD`);
1491
1908
  if (isDryRun) {
@@ -1540,24 +1957,6 @@ const execute = async (runConfig)=>{
1540
1957
  }
1541
1958
  // Run prechecks before starting any work
1542
1959
  await runPrechecks(runConfig, targetBranch);
1543
- // Early check: determine if a release is necessary compared to target branch
1544
- logger.info('RELEASE_NECESSITY_CHECK: Evaluating if release is required | Comparison: current branch vs target | Target: ' + targetBranch + ' | Purpose: Avoid unnecessary publishes');
1545
- try {
1546
- const necessity = await isReleaseNecessaryComparedToTarget(targetBranch, isDryRun);
1547
- if (!necessity.necessary) {
1548
- logger.info(`\nRELEASE_SKIPPED: No meaningful changes detected, skipping publish | Reason: ${necessity.reason} | Target: ${targetBranch}`);
1549
- // Emit a machine-readable marker so tree mode can detect skip and avoid propagating versions
1550
- // CRITICAL: Use console.log to write to stdout (logger.info goes to stderr via winston)
1551
- // eslint-disable-next-line no-console
1552
- console.log('KODRDRIV_PUBLISH_SKIPPED');
1553
- return;
1554
- } else {
1555
- logger.verbose(`RELEASE_PROCEEDING: Meaningful changes detected, continuing with publish | Reason: ${necessity.reason} | Target: ${targetBranch}`);
1556
- }
1557
- } catch (error) {
1558
- // On unexpected errors, proceed with publish to avoid false negatives blocking releases
1559
- logger.verbose(`RELEASE_NECESSITY_CHECK_ERROR: Unable to determine release necessity | Error: ${error.message} | Action: Proceeding conservatively with publish | Rationale: Avoid blocking valid releases`);
1560
- }
1561
1960
  logger.info('RELEASE_PROCESS_STARTING: Initiating release workflow | Target: ' + targetBranch + ' | Phase: dependency updates and version management');
1562
1961
  let pr = null;
1563
1962
  let newVersion = ''; // Will be set during version determination phase
@@ -1788,9 +2187,11 @@ const execute = async (runConfig)=>{
1788
2187
  logger.info('');
1789
2188
  if ((_runConfig_publish16 = runConfig.publish) === null || _runConfig_publish16 === void 0 ? void 0 : _runConfig_publish16.skipAlreadyPublished) {
1790
2189
  logger.info('PUBLISH_SKIPPED_FLAG: Skipping package due to flag | Flag: --skip-already-published | Version: ' + proposedVersion + ' | Status: skipped');
1791
- // Emit skip marker for tree mode detection
2190
+ // Emit skip marker for tree mode detection with reason
1792
2191
  // eslint-disable-next-line no-console
1793
2192
  console.log('KODRDRIV_PUBLISH_SKIPPED');
2193
+ // eslint-disable-next-line no-console
2194
+ console.log('KODRDRIV_PUBLISH_SKIP_REASON:already-published');
1794
2195
  return; // Exit without error
1795
2196
  } else {
1796
2197
  throw new Error(`Version ${proposedVersion} already published. Use --skip-already-published to continue.`);
@@ -2568,7 +2969,7 @@ const execute = async (runConfig)=>{
2568
2969
  * Check for required scripts in package.json
2569
2970
  */ async function checkScripts(result, _isDryRun) {
2570
2971
  const storage = createStorage();
2571
- const packageJsonPath = path.join(process.cwd(), 'package.json');
2972
+ const packageJsonPath = path__default.join(process.cwd(), 'package.json');
2572
2973
  try {
2573
2974
  var _packageJson_scripts;
2574
2975
  if (!await storage.exists(packageJsonPath)) {
@@ -2610,7 +3011,7 @@ const execute = async (runConfig)=>{
2610
3011
  if (runConfig.tree) {
2611
3012
  const storage = createStorage();
2612
3013
  const rootPath = process.cwd();
2613
- const packageJsonPath = path.join(rootPath, 'package.json');
3014
+ const packageJsonPath = path__default.join(rootPath, 'package.json');
2614
3015
  try {
2615
3016
  if (!await storage.exists(packageJsonPath)) {
2616
3017
  result.errors.push({
@@ -2848,5 +3249,233 @@ const execute = async (runConfig)=>{
2848
3249
  };
2849
3250
  }
2850
3251
 
2851
- export { createDryRunReport, execute$2 as development, logDryRunReport, logTreeDryRunReport, logValidationResults, execute as publish, execute$1 as release, runPreflightValidation, throwIfValidationFailed };
3252
+ const logger = getLogger();
3253
+ /**
3254
+ * Get the checkpoint file path for a directory
3255
+ */ function getCheckpointPath(workingDirectory) {
3256
+ return path__default.join(workingDirectory, '.kodrdriv-publish-checkpoint.json');
3257
+ }
3258
+ /**
3259
+ * Save a checkpoint
3260
+ */ async function saveCheckpoint(checkpoint) {
3261
+ const storage = createStorage();
3262
+ const checkpointPath = getCheckpointPath(checkpoint.workingDirectory);
3263
+ try {
3264
+ await storage.writeFile(checkpointPath, JSON.stringify(checkpoint, null, 2), 'utf-8');
3265
+ logger.debug(`CHECKPOINT_SAVED: Saved publish checkpoint | Phase: ${checkpoint.phase} | Package: ${checkpoint.packageName} | Version: ${checkpoint.version}`);
3266
+ } catch (error) {
3267
+ logger.warn(`CHECKPOINT_SAVE_FAILED: Failed to save checkpoint | Error: ${error.message}`);
3268
+ }
3269
+ }
3270
+ /**
3271
+ * Load the most recent checkpoint
3272
+ */ async function loadCheckpoint(workingDirectory) {
3273
+ const storage = createStorage();
3274
+ const checkpointPath = getCheckpointPath(workingDirectory);
3275
+ try {
3276
+ if (!await storage.exists(checkpointPath)) {
3277
+ return null;
3278
+ }
3279
+ const content = await storage.readFile(checkpointPath, 'utf-8');
3280
+ const checkpoint = JSON.parse(content);
3281
+ logger.debug(`CHECKPOINT_LOADED: Loaded publish checkpoint | Phase: ${checkpoint.phase} | Package: ${checkpoint.packageName} | Version: ${checkpoint.version}`);
3282
+ return checkpoint;
3283
+ } catch (error) {
3284
+ logger.warn(`CHECKPOINT_LOAD_FAILED: Failed to load checkpoint | Error: ${error.message}`);
3285
+ return null;
3286
+ }
3287
+ }
3288
+ /**
3289
+ * Delete checkpoint (cleanup after successful publish)
3290
+ */ async function deleteCheckpoint(workingDirectory) {
3291
+ const storage = createStorage();
3292
+ const checkpointPath = getCheckpointPath(workingDirectory);
3293
+ try {
3294
+ if (await storage.exists(checkpointPath)) {
3295
+ await storage.deleteFile(checkpointPath);
3296
+ logger.debug('CHECKPOINT_DELETED: Removed publish checkpoint after successful completion');
3297
+ }
3298
+ } catch (error) {
3299
+ logger.warn(`CHECKPOINT_DELETE_FAILED: Failed to delete checkpoint | Error: ${error.message}`);
3300
+ }
3301
+ }
3302
+ /**
3303
+ * Update an existing checkpoint with new data
3304
+ */ async function updateCheckpoint(workingDirectory, updates) {
3305
+ const existing = await loadCheckpoint(workingDirectory);
3306
+ if (!existing) {
3307
+ logger.warn('CHECKPOINT_UPDATE_FAILED: No existing checkpoint to update');
3308
+ return;
3309
+ }
3310
+ const updated = {
3311
+ ...existing,
3312
+ ...updates,
3313
+ timestamp: new Date().toISOString()
3314
+ };
3315
+ await saveCheckpoint(updated);
3316
+ }
3317
+ /**
3318
+ * Determine the appropriate recovery strategy based on checkpoint
3319
+ */ function analyzeRecoveryStrategy(checkpoint) {
3320
+ const phase = checkpoint.phase;
3321
+ // Phase: initialized, validated, pr-created
3322
+ // These are safe - nothing permanent has happened yet
3323
+ if (phase === 'initialized' || phase === 'validated' || phase === 'pr-created') {
3324
+ return {
3325
+ recoverable: true,
3326
+ action: 'continue',
3327
+ explanation: 'Publish can be continued or restarted safely',
3328
+ steps: [
3329
+ 'Fix any issues that caused the failure',
3330
+ 'Run: kodrdriv publish --continue',
3331
+ 'Or restart: kodrdriv publish'
3332
+ ],
3333
+ risks: [
3334
+ 'None - no permanent changes have been made'
3335
+ ]
3336
+ };
3337
+ }
3338
+ // Phase: pr-merged (but not published)
3339
+ // This is the danger zone - merge is permanent but npm publish hasn't happened
3340
+ if (phase === 'pr-merged' && !checkpoint.npmPublished) {
3341
+ return {
3342
+ recoverable: true,
3343
+ action: 'rollback',
3344
+ explanation: 'PR merged but npm publish failed - can rollback the merge',
3345
+ steps: [
3346
+ 'Revert the merge commit on main branch',
3347
+ 'Delete any git tags that were created',
3348
+ 'Reset working branch to clean state',
3349
+ 'Increment version for next attempt',
3350
+ 'Run: kodrdriv publish --rollback'
3351
+ ],
3352
+ risks: [
3353
+ 'Creates a revert commit in main branch history',
3354
+ 'Any work based on the merge will need to be rebased',
3355
+ 'Tags will be deleted and recreated on next publish'
3356
+ ]
3357
+ };
3358
+ }
3359
+ // Phase: tagged, npm-publishing (but not completed)
3360
+ // Tags exist but npm publish incomplete
3361
+ if ((phase === 'tagged' || phase === 'npm-publishing') && !checkpoint.npmPublished) {
3362
+ return {
3363
+ recoverable: true,
3364
+ action: 'rollback',
3365
+ explanation: 'Tags created but npm publish incomplete - can rollback',
3366
+ steps: [
3367
+ 'Delete git tags',
3368
+ 'Revert merge commit if it exists',
3369
+ 'Reset working branch',
3370
+ 'Increment version',
3371
+ 'Run: kodrdriv publish --rollback'
3372
+ ],
3373
+ risks: [
3374
+ 'Tags will be deleted and recreated',
3375
+ 'Merge commit will be reverted if it exists'
3376
+ ]
3377
+ };
3378
+ }
3379
+ // Phase: npm-published or completed
3380
+ // Package is published - can't rollback npm
3381
+ if (checkpoint.npmPublished || phase === 'npm-published' || phase === 'completed') {
3382
+ return {
3383
+ recoverable: false,
3384
+ action: 'fix-forward',
3385
+ explanation: 'Package already published to npm - must fix forward',
3386
+ steps: [
3387
+ 'Package is live on npm - cannot unpublish',
3388
+ 'If there are issues, publish a patch version',
3389
+ 'Run: kodrdriv publish --target-version patch',
3390
+ 'Or manually fix and increment version'
3391
+ ],
3392
+ risks: [
3393
+ 'Cannot undo npm publish',
3394
+ 'Must publish new version to fix issues'
3395
+ ]
3396
+ };
3397
+ }
3398
+ // Phase: failed (generic failure)
3399
+ if (phase === 'failed') {
3400
+ // Determine based on what was completed
3401
+ if (checkpoint.npmPublished) {
3402
+ return analyzeRecoveryStrategy({
3403
+ ...checkpoint,
3404
+ phase: 'npm-published'
3405
+ });
3406
+ } else if (checkpoint.prNumber) {
3407
+ return analyzeRecoveryStrategy({
3408
+ ...checkpoint,
3409
+ phase: 'pr-merged'
3410
+ });
3411
+ } else {
3412
+ return analyzeRecoveryStrategy({
3413
+ ...checkpoint,
3414
+ phase: 'initialized'
3415
+ });
3416
+ }
3417
+ }
3418
+ // Unknown phase or state
3419
+ return {
3420
+ recoverable: false,
3421
+ action: 'none',
3422
+ explanation: 'Unable to determine recovery strategy from checkpoint',
3423
+ steps: [
3424
+ 'Manually inspect the repository state',
3425
+ 'Check: git status, git log, npm view <package>',
3426
+ 'Determine what was completed',
3427
+ 'Contact support if needed'
3428
+ ],
3429
+ risks: [
3430
+ 'Manual intervention required'
3431
+ ]
3432
+ };
3433
+ }
3434
+ /**
3435
+ * Get a human-readable summary of the checkpoint
3436
+ */ function getCheckpointSummary(checkpoint) {
3437
+ const lines = [];
3438
+ lines.push('='.repeat(60));
3439
+ lines.push('Publish Checkpoint Summary');
3440
+ lines.push('='.repeat(60));
3441
+ lines.push('');
3442
+ lines.push(`Package: ${checkpoint.packageName}`);
3443
+ lines.push(`Version: ${checkpoint.version}`);
3444
+ lines.push(`Phase: ${checkpoint.phase}`);
3445
+ lines.push(`Branch: ${checkpoint.branch}`);
3446
+ lines.push(`Timestamp: ${checkpoint.timestamp}`);
3447
+ lines.push('');
3448
+ if (checkpoint.prNumber) {
3449
+ lines.push(`Pull Request: #${checkpoint.prNumber}`);
3450
+ if (checkpoint.prUrl) {
3451
+ lines.push(`PR URL: ${checkpoint.prUrl}`);
3452
+ }
3453
+ }
3454
+ if (checkpoint.tags.length > 0) {
3455
+ lines.push(`Tags Created: ${checkpoint.tags.join(', ')}`);
3456
+ }
3457
+ if (checkpoint.npmPublished) {
3458
+ lines.push('✅ npm Published: Yes');
3459
+ } else {
3460
+ lines.push('❌ npm Published: No');
3461
+ }
3462
+ if (checkpoint.workflowRunUrl) {
3463
+ lines.push(`Workflow: ${checkpoint.workflowRunUrl}`);
3464
+ }
3465
+ if (checkpoint.error) {
3466
+ lines.push('');
3467
+ lines.push('Error:');
3468
+ lines.push(` ${checkpoint.error}`);
3469
+ }
3470
+ lines.push('');
3471
+ lines.push('='.repeat(60));
3472
+ return lines.join('\n');
3473
+ }
3474
+ /**
3475
+ * Check if a checkpoint indicates a failed publish
3476
+ */ function isFailedPublish(checkpoint) {
3477
+ return checkpoint.phase === 'failed' || checkpoint.phase === 'pr-merged' && !checkpoint.npmPublished || checkpoint.phase === 'tagged' && !checkpoint.npmPublished || checkpoint.phase === 'npm-publishing' && !checkpoint.npmPublished;
3478
+ }
3479
+
3480
+ export { analyzeRecoveryStrategy, execute$2 as checkDevelopment, createDryRunReport, deleteCheckpoint, execute$3 as development, getCheckpointSummary, isFailedPublish, loadCheckpoint, logDryRunReport, logTreeDryRunReport, logValidationResults, execute as publish, execute$1 as release, runPreflightValidation, saveCheckpoint, throwIfValidationFailed, updateCheckpoint };
2852
3481
  //# sourceMappingURL=index.js.map