epicshop 6.81.0 → 6.82.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/dist/cli.js CHANGED
@@ -28,6 +28,15 @@ function formatHelp(helpText) {
28
28
  .replace(/--[\w-]+/g, (match) => chalk.yellow(match))
29
29
  .replace(/-\w(?=\s|,)/g, (match) => chalk.yellow(match));
30
30
  }
31
+ function resolveWorkshopContextCwd(explicitWorkshopDir) {
32
+ const fromFlag = explicitWorkshopDir?.trim()
33
+ ? explicitWorkshopDir.trim()
34
+ : undefined;
35
+ if (fromFlag)
36
+ return fromFlag;
37
+ const fromEnv = process.env.EPICSHOP_CONTEXT_CWD;
38
+ return fromEnv?.trim() ? fromEnv.trim() : undefined;
39
+ }
31
40
  // Set up yargs CLI
32
41
  const cli = yargs(args)
33
42
  .scriptName('epicshop')
@@ -767,6 +776,11 @@ const cli = yargs(args)
767
776
  type: 'boolean',
768
777
  description: 'Output saved playgrounds as JSON (saved list)',
769
778
  default: false,
779
+ })
780
+ .option('workshop-dir', {
781
+ alias: 'w',
782
+ type: 'string',
783
+ description: 'Path to a workshop directory to use as context (instead of the current working directory)',
770
784
  })
771
785
  .option('silent', {
772
786
  alias: 's',
@@ -784,9 +798,9 @@ const cli = yargs(args)
784
798
  .example('$0 playground saved 2026.01.18_11.12.00_01.01.problem', 'Set playground from a saved copy');
785
799
  }, async (argv) => {
786
800
  const { findWorkshopRoot } = await import("./commands/workshops.js");
787
- const workshopRoot = await findWorkshopRoot();
801
+ const workshopRoot = await findWorkshopRoot(resolveWorkshopContextCwd(argv.workshopDir));
788
802
  if (!workshopRoot) {
789
- console.error(chalk.red('❌ Not inside a workshop directory. Please cd into a workshop first.'));
803
+ console.error(chalk.red('❌ Workshop not found. Please cd into a workshop directory or pass --workshop-dir.'));
790
804
  process.exit(1);
791
805
  }
792
806
  const originalCwd = process.cwd();
@@ -903,6 +917,11 @@ const cli = yargs(args)
903
917
  type: 'boolean',
904
918
  description: 'Output as JSON',
905
919
  default: false,
920
+ })
921
+ .option('workshop-dir', {
922
+ alias: 'w',
923
+ type: 'string',
924
+ description: 'Path to a workshop directory to use as context (instead of the current working directory)',
906
925
  })
907
926
  .option('silent', {
908
927
  alias: 's',
@@ -917,9 +936,9 @@ const cli = yargs(args)
917
936
  .example('$0 progress update 01-01-problem --incomplete', 'Mark lesson as incomplete');
918
937
  }, async (argv) => {
919
938
  const { findWorkshopRoot } = await import("./commands/workshops.js");
920
- const workshopRoot = await findWorkshopRoot();
939
+ const workshopRoot = await findWorkshopRoot(resolveWorkshopContextCwd(argv.workshopDir));
921
940
  if (!workshopRoot) {
922
- console.error(chalk.red('❌ Not inside a workshop directory. Please cd into a workshop first.'));
941
+ console.error(chalk.red('❌ Workshop not found. Please cd into a workshop directory or pass --workshop-dir.'));
923
942
  process.exit(1);
924
943
  }
925
944
  const originalCwd = process.cwd();
@@ -970,14 +989,19 @@ const cli = yargs(args)
970
989
  type: 'boolean',
971
990
  description: 'Run without output logs',
972
991
  default: false,
992
+ })
993
+ .option('workshop-dir', {
994
+ alias: 'w',
995
+ type: 'string',
996
+ description: 'Path to a workshop directory to use as context (instead of the current working directory)',
973
997
  })
974
998
  .example('$0 diff', 'Select apps to diff (defaults to playground vs solution)')
975
999
  .example('$0 diff 01.02.problem 01.02.solution', 'Show diff between two apps');
976
1000
  }, async (argv) => {
977
1001
  const { findWorkshopRoot } = await import("./commands/workshops.js");
978
- const workshopRoot = await findWorkshopRoot();
1002
+ const workshopRoot = await findWorkshopRoot(resolveWorkshopContextCwd(argv.workshopDir));
979
1003
  if (!workshopRoot) {
980
- console.error(chalk.red('❌ Not inside a workshop directory. Please cd into a workshop first.'));
1004
+ console.error(chalk.red('❌ Workshop not found. Please cd into a workshop directory or pass --workshop-dir.'));
981
1005
  process.exit(1);
982
1006
  }
983
1007
  const originalCwd = process.cwd();
@@ -1027,6 +1051,11 @@ const cli = yargs(args)
1027
1051
  type: 'boolean',
1028
1052
  description: 'Run without output logs',
1029
1053
  default: false,
1054
+ })
1055
+ .option('workshop-dir', {
1056
+ alias: 'w',
1057
+ type: 'string',
1058
+ description: 'Path to a workshop directory to use as context (instead of the current working directory)',
1030
1059
  })
1031
1060
  .example('$0 exercises', 'List all exercises with progress')
1032
1061
  .example('$0 exercises 1', 'Show details for exercise 1')
@@ -1034,9 +1063,9 @@ const cli = yargs(args)
1034
1063
  .example('$0 exercises --json', 'Output exercises as JSON');
1035
1064
  }, async (argv) => {
1036
1065
  const { findWorkshopRoot } = await import("./commands/workshops.js");
1037
- const workshopRoot = await findWorkshopRoot();
1066
+ const workshopRoot = await findWorkshopRoot(resolveWorkshopContextCwd(argv.workshopDir));
1038
1067
  if (!workshopRoot) {
1039
- console.error(chalk.red('❌ Not inside a workshop directory. Please cd into a workshop first.'));
1068
+ console.error(chalk.red('❌ Workshop not found. Please cd into a workshop directory or pass --workshop-dir.'));
1040
1069
  process.exit(1);
1041
1070
  }
1042
1071
  const originalCwd = process.cwd();
@@ -1087,7 +1116,7 @@ try {
1087
1116
  if (args.length === 0) {
1088
1117
  // Check if we're inside a workshop first
1089
1118
  const { findWorkshopRoot } = await import("./commands/workshops.js");
1090
- const workshopRoot = await findWorkshopRoot();
1119
+ const workshopRoot = await findWorkshopRoot(resolveWorkshopContextCwd());
1091
1120
  // Get workshop title if we're inside one
1092
1121
  let workshopTitle = null;
1093
1122
  if (workshopRoot) {
@@ -1495,7 +1524,7 @@ try {
1495
1524
  }
1496
1525
  case 'exercises': {
1497
1526
  const { findWorkshopRoot } = await import("./commands/workshops.js");
1498
- const wsRoot = await findWorkshopRoot();
1527
+ const wsRoot = await findWorkshopRoot(resolveWorkshopContextCwd());
1499
1528
  if (!wsRoot) {
1500
1529
  console.error(chalk.red('❌ Not inside a workshop directory'));
1501
1530
  process.exit(1);
@@ -1515,7 +1544,7 @@ try {
1515
1544
  }
1516
1545
  case 'playground': {
1517
1546
  const { findWorkshopRoot } = await import("./commands/workshops.js");
1518
- const wsRoot = await findWorkshopRoot();
1547
+ const wsRoot = await findWorkshopRoot(resolveWorkshopContextCwd());
1519
1548
  if (!wsRoot) {
1520
1549
  console.error(chalk.red('❌ Not inside a workshop directory'));
1521
1550
  process.exit(1);
@@ -1535,7 +1564,7 @@ try {
1535
1564
  }
1536
1565
  case 'progress': {
1537
1566
  const { findWorkshopRoot } = await import("./commands/workshops.js");
1538
- const wsRoot = await findWorkshopRoot();
1567
+ const wsRoot = await findWorkshopRoot(resolveWorkshopContextCwd());
1539
1568
  if (!wsRoot) {
1540
1569
  console.error(chalk.red('❌ Not inside a workshop directory'));
1541
1570
  process.exit(1);
@@ -1555,7 +1584,7 @@ try {
1555
1584
  }
1556
1585
  case 'diff': {
1557
1586
  const { findWorkshopRoot } = await import("./commands/workshops.js");
1558
- const wsRoot = await findWorkshopRoot();
1587
+ const wsRoot = await findWorkshopRoot(resolveWorkshopContextCwd());
1559
1588
  if (!wsRoot) {
1560
1589
  console.error(chalk.red('❌ Not inside a workshop directory'));
1561
1590
  process.exit(1);
@@ -4,7 +4,7 @@ import '@epic-web/workshop-utils/init-env';
4
4
  * looking for a package.json with an epicshop field.
5
5
  * Returns the workshop root path if found, null otherwise.
6
6
  */
7
- export declare function findWorkshopRoot(): Promise<string | null>;
7
+ export declare function findWorkshopRoot(startDir?: string): Promise<string | null>;
8
8
  /**
9
9
  * Check if the current working directory is inside a workshop
10
10
  * (has epicshop config in package.json in current dir or any parent)
@@ -38,8 +38,21 @@ const EPIC_SITES = [
38
38
  * looking for a package.json with an epicshop field.
39
39
  * Returns the workshop root path if found, null otherwise.
40
40
  */
41
- export async function findWorkshopRoot() {
42
- let currentDir = process.cwd();
41
+ export async function findWorkshopRoot(startDir) {
42
+ let currentDir = startDir
43
+ ? path.resolve(resolvePathWithTilde(startDir))
44
+ : process.cwd();
45
+ // If the user gave us a file path (or something that isn't a directory),
46
+ // treat the containing folder as the start point.
47
+ try {
48
+ const stat = await fs.promises.stat(currentDir);
49
+ if (!stat.isDirectory()) {
50
+ currentDir = path.dirname(currentDir);
51
+ }
52
+ }
53
+ catch {
54
+ // If the path doesn't exist, we still attempt to walk upward from it.
55
+ }
43
56
  const root = path.parse(currentDir).root;
44
57
  while (currentDir !== root) {
45
58
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epicshop",
3
- "version": "6.81.0",
3
+ "version": "6.82.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -99,7 +99,7 @@
99
99
  "build:watch": "nx watch --projects=epicshop -- nx run \\$NX_PROJECT_NAME:build"
100
100
  },
101
101
  "dependencies": {
102
- "@epic-web/workshop-utils": "6.81.0",
102
+ "@epic-web/workshop-utils": "6.82.1",
103
103
  "@inquirer/prompts": "^8.2.0",
104
104
  "@sentry/node": "^10.38.0",
105
105
  "chalk": "^5.6.2",