@clafoutis/cli 1.2.0 → 1.2.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
@@ -2,7 +2,7 @@
2
2
  import * as p2 from '@clack/prompts';
3
3
  import { Command } from 'commander';
4
4
  import fs from 'fs';
5
- import path2 from 'path';
5
+ import path from 'path';
6
6
  import { logger } from '@clafoutis/shared';
7
7
  import { generate } from '@clafoutis/generators/figma';
8
8
  import { generate as generate$1 } from '@clafoutis/generators/tailwind';
@@ -88,6 +88,28 @@ function tokensDirNotFoundError(tokensDir) {
88
88
  );
89
89
  }
90
90
 
91
+ // src/utils/cwd.ts
92
+ function resolveCommandCwd(cwdOverride) {
93
+ const cwd = cwdOverride?.trim() ? cwdOverride : process.cwd();
94
+ return path.resolve(cwd);
95
+ }
96
+ function resolveInCwd(commandCwd, targetPath) {
97
+ return path.isAbsolute(targetPath) ? path.resolve(targetPath) : path.resolve(commandCwd, targetPath);
98
+ }
99
+ function displayPath(commandCwd, absolutePath) {
100
+ const rel = path.relative(commandCwd, absolutePath);
101
+ return rel && !rel.startsWith("..") ? rel : absolutePath;
102
+ }
103
+ function validateCwdOption(cwdOverride) {
104
+ if (cwdOverride !== void 0 && cwdOverride.trim() === "") {
105
+ throw new ClafoutisError(
106
+ "Invalid --cwd value",
107
+ "--cwd cannot be empty",
108
+ "Provide a valid directory path, for example: --cwd ./packages/design-system"
109
+ );
110
+ }
111
+ }
112
+
91
113
  // src/commands/format.ts
92
114
  function loadTokenFiles(dirPath) {
93
115
  const files = [];
@@ -96,7 +118,7 @@ function loadTokenFiles(dirPath) {
96
118
  return;
97
119
  }
98
120
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
99
- const fullPath = path2.join(dir, entry.name);
121
+ const fullPath = path.join(dir, entry.name);
100
122
  const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
101
123
  if (entry.isDirectory()) {
102
124
  walk(fullPath, relativePath);
@@ -120,7 +142,9 @@ function formatJson(content) {
120
142
  return JSON.stringify(parsed, null, 2) + "\n";
121
143
  }
122
144
  function formatCommand(options) {
123
- const tokensDir = options.tokens || "./tokens";
145
+ validateCwdOption(options.cwd);
146
+ const commandCwd = resolveCommandCwd(options.cwd);
147
+ const tokensDir = resolveInCwd(commandCwd, options.tokens || "./tokens");
124
148
  if (!fs.existsSync(tokensDir)) {
125
149
  throw tokensDirNotFoundError(tokensDir);
126
150
  }
@@ -260,8 +284,8 @@ function findUnknownFields(config, schemaProperties, prefix = "") {
260
284
  }
261
285
  return unknown;
262
286
  }
263
- function hasField(config, path6) {
264
- const parts = path6.split(".");
287
+ function hasField(config, path8) {
288
+ const parts = path8.split(".");
265
289
  let current = config;
266
290
  for (const part of parts) {
267
291
  if (current && typeof current === "object" && part in current) {
@@ -590,11 +614,11 @@ function getConsumerGitignore() {
590
614
  `;
591
615
  }
592
616
  var __filename$1 = fileURLToPath(import.meta.url);
593
- var __dirname$1 = path2.dirname(__filename$1);
617
+ var __dirname$1 = path.dirname(__filename$1);
594
618
  function getTokensDir() {
595
- const devPath = path2.resolve(__dirname$1, "tokens");
619
+ const devPath = path.resolve(__dirname$1, "tokens");
596
620
  if (fs.existsSync(devPath)) return devPath;
597
- const distPath = path2.resolve(__dirname$1, "templates", "tokens");
621
+ const distPath = path.resolve(__dirname$1, "templates", "tokens");
598
622
  if (fs.existsSync(distPath)) return distPath;
599
623
  throw new Error(
600
624
  `Starter token templates not found. Searched:
@@ -605,8 +629,8 @@ function getTokensDir() {
605
629
  function walkTokensDir(dir, base = "") {
606
630
  const result = [];
607
631
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
608
- const relPath = base ? path2.join(base, entry.name) : entry.name;
609
- const fullPath = path2.join(dir, entry.name);
632
+ const relPath = base ? path.join(base, entry.name) : entry.name;
633
+ const fullPath = path.join(dir, entry.name);
610
634
  if (entry.isDirectory()) {
611
635
  result.push(...walkTokensDir(fullPath, relPath));
612
636
  } else if (entry.name.endsWith(".json")) {
@@ -632,6 +656,7 @@ on:
632
656
  branches: [main]
633
657
  paths:
634
658
  - 'tokens/**'
659
+ - '.clafoutis/producer.json'
635
660
 
636
661
  jobs:
637
662
  release:
@@ -657,6 +682,18 @@ jobs:
657
682
  - name: Generate tokens
658
683
  run: npx clafoutis generate
659
684
 
685
+ - name: Commit generated build artifacts
686
+ run: |
687
+ if [ -z "$(git status --porcelain build)" ]; then
688
+ echo "No build changes to commit"
689
+ exit 0
690
+ fi
691
+ git config user.name "github-actions[bot]"
692
+ git config user.email "github-actions[bot]@users.noreply.github.com"
693
+ git add build
694
+ git commit -m "chore: update generated build artifacts"
695
+ git push
696
+
660
697
  - name: Get next version
661
698
  id: version
662
699
  run: |
@@ -740,15 +777,17 @@ async function initCommand(options) {
740
777
  "Choose one: --producer for design system repos, --consumer for application repos"
741
778
  );
742
779
  }
780
+ validateCwdOption(options.cwd);
781
+ const commandCwd = resolveCommandCwd(options.cwd);
743
782
  const isInteractive = !options.nonInteractive && process.stdin.isTTY;
744
783
  const isDryRun = options.dryRun ?? false;
745
784
  if (isInteractive) {
746
- await runInteractiveInit(options, isDryRun);
785
+ await runInteractiveInit(options, isDryRun, commandCwd);
747
786
  } else {
748
- await runNonInteractiveInit(options, isDryRun);
787
+ await runNonInteractiveInit(options, isDryRun, commandCwd);
749
788
  }
750
789
  }
751
- async function runInteractiveInit(options, isDryRun) {
790
+ async function runInteractiveInit(options, isDryRun, commandCwd) {
752
791
  showIntro(isDryRun);
753
792
  let mode;
754
793
  if (options.producer) {
@@ -768,13 +807,23 @@ async function runInteractiveInit(options, isDryRun) {
768
807
  if (!answers) {
769
808
  return;
770
809
  }
771
- await createProducerConfig(answers, options.force ?? false, isDryRun);
810
+ await createProducerConfig(
811
+ answers,
812
+ options.force ?? false,
813
+ isDryRun,
814
+ commandCwd
815
+ );
772
816
  } else {
773
817
  const answers = await runConsumerWizard();
774
818
  if (!answers) {
775
819
  return;
776
820
  }
777
- await createConsumerConfig(answers, options.force ?? false, isDryRun);
821
+ await createConsumerConfig(
822
+ answers,
823
+ options.force ?? false,
824
+ isDryRun,
825
+ commandCwd
826
+ );
778
827
  }
779
828
  if (isDryRun) {
780
829
  showOutro("No files were written. Remove --dry-run to apply changes.");
@@ -782,7 +831,7 @@ async function runInteractiveInit(options, isDryRun) {
782
831
  showOutro("Setup complete!");
783
832
  }
784
833
  }
785
- async function runNonInteractiveInit(options, isDryRun) {
834
+ async function runNonInteractiveInit(options, isDryRun, commandCwd) {
786
835
  if (!options.producer && !options.consumer) {
787
836
  throw new ClafoutisError(
788
837
  "Mode required",
@@ -805,7 +854,12 @@ async function runNonInteractiveInit(options, isDryRun) {
805
854
  output: options.output ?? "./build",
806
855
  workflow: options.workflow ?? true
807
856
  };
808
- await createProducerConfig(answers, options.force ?? false, isDryRun);
857
+ await createProducerConfig(
858
+ answers,
859
+ options.force ?? false,
860
+ isDryRun,
861
+ commandCwd
862
+ );
809
863
  } else {
810
864
  const errors = validateConsumerFlags(options);
811
865
  if (errors.length > 0) {
@@ -859,11 +913,16 @@ async function runNonInteractiveInit(options, isDryRun) {
859
913
  repo: options.repo,
860
914
  files
861
915
  };
862
- await createConsumerConfig(answers, options.force ?? false, isDryRun);
916
+ await createConsumerConfig(
917
+ answers,
918
+ options.force ?? false,
919
+ isDryRun,
920
+ commandCwd
921
+ );
863
922
  }
864
923
  }
865
- async function createProducerConfig(answers, force, dryRun) {
866
- const configPath = ".clafoutis/producer.json";
924
+ async function createProducerConfig(answers, force, dryRun, commandCwd) {
925
+ const configPath = resolveInCwd(commandCwd, ".clafoutis/producer.json");
867
926
  if (!force && await fileExists(configPath)) {
868
927
  throw new ClafoutisError(
869
928
  "Configuration already exists",
@@ -896,7 +955,10 @@ async function createProducerConfig(answers, force, dryRun) {
896
955
  ];
897
956
  const starterTokens = getAllStarterTokens();
898
957
  for (const token of starterTokens) {
899
- const tokenPath = path2.join(answers.tokens, token.path);
958
+ const tokenPath = resolveInCwd(
959
+ commandCwd,
960
+ path.join(answers.tokens, token.path)
961
+ );
900
962
  if (!force && await fileExists(tokenPath)) {
901
963
  continue;
902
964
  }
@@ -907,7 +969,7 @@ async function createProducerConfig(answers, force, dryRun) {
907
969
  });
908
970
  }
909
971
  if (answers.workflow) {
910
- const workflowPath = getWorkflowPath();
972
+ const workflowPath = resolveInCwd(commandCwd, getWorkflowPath());
911
973
  if (force || !await fileExists(workflowPath)) {
912
974
  filesToCreate.push({
913
975
  path: workflowPath,
@@ -916,22 +978,23 @@ async function createProducerConfig(answers, force, dryRun) {
916
978
  });
917
979
  }
918
980
  }
919
- if (force || !await fileExists(".gitignore")) {
981
+ const gitignorePath = resolveInCwd(commandCwd, ".gitignore");
982
+ if (force || !await fileExists(gitignorePath)) {
920
983
  filesToCreate.push({
921
- path: ".gitignore",
984
+ path: gitignorePath,
922
985
  content: getProducerGitignore(),
923
986
  description: "Ignore build artifacts and release-assets"
924
987
  });
925
988
  }
926
989
  if (dryRun) {
927
- showDryRunOutput(filesToCreate);
990
+ showDryRunOutput(filesToCreate, commandCwd);
928
991
  } else {
929
- await writeFiles(filesToCreate);
992
+ await writeFiles(filesToCreate, commandCwd);
930
993
  showNextSteps("producer", answers);
931
994
  }
932
995
  }
933
- async function createConsumerConfig(answers, force, dryRun) {
934
- const configPath = ".clafoutis/consumer.json";
996
+ async function createConsumerConfig(answers, force, dryRun, commandCwd) {
997
+ const configPath = resolveInCwd(commandCwd, ".clafoutis/consumer.json");
935
998
  if (!force && await fileExists(configPath)) {
936
999
  throw new ClafoutisError(
937
1000
  "Configuration already exists",
@@ -951,7 +1014,7 @@ async function createConsumerConfig(answers, force, dryRun) {
951
1014
  description: `repo: "${answers.repo}"`
952
1015
  }
953
1016
  ];
954
- const gitignorePath = ".gitignore";
1017
+ const gitignorePath = resolveInCwd(commandCwd, ".gitignore");
955
1018
  const consumerIgnore = getConsumerGitignore();
956
1019
  if (await fileExists(gitignorePath)) {
957
1020
  const existingContent = await fs2.readFile(gitignorePath, "utf-8");
@@ -970,30 +1033,30 @@ async function createConsumerConfig(answers, force, dryRun) {
970
1033
  });
971
1034
  }
972
1035
  if (dryRun) {
973
- showDryRunOutput(filesToCreate);
1036
+ showDryRunOutput(filesToCreate, commandCwd);
974
1037
  } else {
975
- await writeFiles(filesToCreate);
1038
+ await writeFiles(filesToCreate, commandCwd);
976
1039
  showNextSteps("consumer", answers);
977
1040
  }
978
1041
  }
979
- function showDryRunOutput(files) {
1042
+ function showDryRunOutput(files, commandCwd) {
980
1043
  log3.message("");
981
1044
  log3.step("Would create the following files:");
982
1045
  log3.message("");
983
1046
  for (const file of files) {
984
- log3.message(` ${file.path}`);
1047
+ log3.message(` ${displayPath(commandCwd, file.path)}`);
985
1048
  if (file.description) {
986
1049
  log3.message(` \u2514\u2500 ${file.description}`);
987
1050
  }
988
1051
  }
989
1052
  log3.message("");
990
1053
  }
991
- async function writeFiles(files) {
1054
+ async function writeFiles(files, commandCwd) {
992
1055
  for (const file of files) {
993
- const dir = path2.dirname(file.path);
1056
+ const dir = path.dirname(file.path);
994
1057
  await fs2.mkdir(dir, { recursive: true });
995
1058
  await fs2.writeFile(file.path, file.content);
996
- log3.success(`Created ${file.path}`);
1059
+ log3.success(`Created ${displayPath(commandCwd, file.path)}`);
997
1060
  }
998
1061
  }
999
1062
  function showNextSteps(mode, answers) {
@@ -1018,15 +1081,20 @@ function showNextSteps(mode, answers) {
1018
1081
  }
1019
1082
 
1020
1083
  // src/commands/generate.ts
1021
- async function loadPlugin(pluginPath) {
1022
- const absolutePath = path2.resolve(process.cwd(), pluginPath);
1084
+ async function loadPlugin(pluginPath, commandCwd) {
1085
+ const absolutePath = resolveInCwd(commandCwd, pluginPath);
1023
1086
  if (pluginPath.endsWith(".ts")) {
1024
1087
  register();
1025
1088
  }
1026
1089
  return import(pathToFileURL(absolutePath).href);
1027
1090
  }
1028
1091
  async function generateCommand(options) {
1029
- const configPath = options.config || ".clafoutis/producer.json";
1092
+ validateCwdOption(options.cwd);
1093
+ const commandCwd = resolveCommandCwd(options.cwd);
1094
+ const configPath = resolveInCwd(
1095
+ commandCwd,
1096
+ options.config || ".clafoutis/producer.json"
1097
+ );
1030
1098
  let config = await readProducerConfig(configPath);
1031
1099
  if (!config) {
1032
1100
  if (await fileExists(configPath)) {
@@ -1039,7 +1107,7 @@ async function generateCommand(options) {
1039
1107
  if (process.stdin.isTTY) {
1040
1108
  const shouldRunWizard = await offerWizard("producer");
1041
1109
  if (shouldRunWizard) {
1042
- await initCommand({ producer: true });
1110
+ await initCommand({ producer: true, cwd: commandCwd });
1043
1111
  config = await readProducerConfig(configPath);
1044
1112
  if (!config) {
1045
1113
  throw configNotFoundError(configPath, false);
@@ -1065,8 +1133,8 @@ async function generateCommand(options) {
1065
1133
  if (options.output) {
1066
1134
  config.output = options.output;
1067
1135
  }
1068
- const tokensDir = path2.resolve(process.cwd(), config.tokens || "./tokens");
1069
- const outputDir = path2.resolve(process.cwd(), config.output || "./build");
1136
+ const tokensDir = resolveInCwd(commandCwd, config.tokens || "./tokens");
1137
+ const outputDir = resolveInCwd(commandCwd, config.output || "./build");
1070
1138
  if (!await fileExists(tokensDir)) {
1071
1139
  throw tokensDirNotFoundError(tokensDir);
1072
1140
  }
@@ -1092,7 +1160,7 @@ async function generateCommand(options) {
1092
1160
  let generatorModule;
1093
1161
  if (typeof value === "string") {
1094
1162
  try {
1095
- generatorModule = await loadPlugin(value);
1163
+ generatorModule = await loadPlugin(value, commandCwd);
1096
1164
  } catch (err) {
1097
1165
  const errorMessage = err instanceof Error ? err.message : String(err);
1098
1166
  throw pluginLoadError(value, errorMessage);
@@ -1105,8 +1173,12 @@ async function generateCommand(options) {
1105
1173
  }
1106
1174
  } else {
1107
1175
  const builtInGenerators = {
1108
- tailwind: { generate: () => generate$1() },
1109
- figma: { generate: () => generate() }
1176
+ tailwind: {
1177
+ generate: async (ctx) => generate$1(commandCwd, ctx.outputDir)
1178
+ },
1179
+ figma: {
1180
+ generate: async (ctx) => generate(commandCwd, ctx.outputDir)
1181
+ }
1110
1182
  };
1111
1183
  if (!builtInGenerators[name]) {
1112
1184
  throw generatorNotFoundError(name);
@@ -1115,7 +1187,7 @@ async function generateCommand(options) {
1115
1187
  }
1116
1188
  const context = {
1117
1189
  tokensDir,
1118
- outputDir: path2.join(outputDir, name),
1190
+ outputDir: path.join(outputDir, name),
1119
1191
  config,
1120
1192
  StyleDictionary
1121
1193
  };
@@ -1139,10 +1211,18 @@ async function generateCommand(options) {
1139
1211
  logger.success("Generation complete");
1140
1212
  }
1141
1213
  var CACHE_DIR = ".clafoutis";
1142
- var CACHE_FILE = `${CACHE_DIR}/cache`;
1143
- async function readCache() {
1214
+ var CACHE_FILE = "cache";
1215
+ function getCachePaths(commandCwd) {
1216
+ const dir = path.resolve(commandCwd, CACHE_DIR);
1217
+ return {
1218
+ dir,
1219
+ file: path.join(dir, CACHE_FILE)
1220
+ };
1221
+ }
1222
+ async function readCache(commandCwd = process.cwd()) {
1223
+ const { file } = getCachePaths(commandCwd);
1144
1224
  try {
1145
- return (await fs2.readFile(CACHE_FILE, "utf-8")).trim();
1225
+ return (await fs2.readFile(file, "utf-8")).trim();
1146
1226
  } catch (err) {
1147
1227
  if (err instanceof Error && err.code === "ENOENT") {
1148
1228
  return null;
@@ -1150,9 +1230,10 @@ async function readCache() {
1150
1230
  throw err;
1151
1231
  }
1152
1232
  }
1153
- async function writeCache(version) {
1154
- await fs2.mkdir(CACHE_DIR, { recursive: true });
1155
- await fs2.writeFile(CACHE_FILE, version);
1233
+ async function writeCache(version, commandCwd = process.cwd()) {
1234
+ const { dir, file } = getCachePaths(commandCwd);
1235
+ await fs2.mkdir(dir, { recursive: true });
1236
+ await fs2.writeFile(file, version);
1156
1237
  }
1157
1238
  async function downloadRelease(config) {
1158
1239
  const token = process.env.CLAFOUTIS_REPO_TOKEN;
@@ -1218,18 +1299,23 @@ async function downloadRelease(config) {
1218
1299
  }
1219
1300
 
1220
1301
  // src/commands/sync.ts
1221
- async function writeOutput(config, files) {
1302
+ async function writeOutput(config, files, commandCwd) {
1222
1303
  for (const [assetName, content] of files) {
1223
1304
  const configPath = config.files[assetName];
1224
1305
  if (!configPath) continue;
1225
- const outputPath = path2.resolve(process.cwd(), configPath);
1226
- await fs2.mkdir(path2.dirname(outputPath), { recursive: true });
1306
+ const outputPath = resolveInCwd(commandCwd, configPath);
1307
+ await fs2.mkdir(path.dirname(outputPath), { recursive: true });
1227
1308
  await fs2.writeFile(outputPath, content);
1228
1309
  logger.success(`Written: ${outputPath}`);
1229
1310
  }
1230
1311
  }
1231
1312
  async function syncCommand(options) {
1232
- const configPath = options.config || ".clafoutis/consumer.json";
1313
+ validateCwdOption(options.cwd);
1314
+ const commandCwd = resolveCommandCwd(options.cwd);
1315
+ const configPath = resolveInCwd(
1316
+ commandCwd,
1317
+ options.config || ".clafoutis/consumer.json"
1318
+ );
1233
1319
  let config = await readConfig(configPath);
1234
1320
  if (!config) {
1235
1321
  if (await fileExists(configPath)) {
@@ -1242,7 +1328,7 @@ async function syncCommand(options) {
1242
1328
  if (process.stdin.isTTY) {
1243
1329
  const shouldRunWizard = await offerWizard("consumer");
1244
1330
  if (shouldRunWizard) {
1245
- await initCommand({ consumer: true });
1331
+ await initCommand({ consumer: true, cwd: commandCwd });
1246
1332
  config = await readConfig(configPath);
1247
1333
  if (!config) {
1248
1334
  throw configNotFoundError(configPath, true);
@@ -1255,7 +1341,7 @@ async function syncCommand(options) {
1255
1341
  }
1256
1342
  }
1257
1343
  validateConsumerConfig(config);
1258
- const cachedVersion = await readCache();
1344
+ const cachedVersion = await readCache(commandCwd);
1259
1345
  const isLatest = config.version === "latest";
1260
1346
  logger.info(`Repo: ${config.repo}`);
1261
1347
  logger.info(`Pinned: ${config.version}`);
@@ -1269,7 +1355,7 @@ async function syncCommand(options) {
1269
1355
  }
1270
1356
  return;
1271
1357
  }
1272
- const resolveOutputPaths = () => Object.values(config.files).map((p5) => path2.resolve(process.cwd(), p5));
1358
+ const resolveOutputPaths = () => Object.values(config.files).map((p5) => resolveInCwd(commandCwd, p5));
1273
1359
  if (!isLatest && !options.force && config.version === cachedVersion) {
1274
1360
  const outputPaths = resolveOutputPaths();
1275
1361
  const existsResults = await Promise.all(
@@ -1294,14 +1380,14 @@ async function syncCommand(options) {
1294
1380
  return;
1295
1381
  }
1296
1382
  }
1297
- await writeOutput(config, files);
1298
- await writeCache(resolvedTag);
1383
+ await writeOutput(config, files, commandCwd);
1384
+ await writeCache(resolvedTag, commandCwd);
1299
1385
  logger.success(`Synced to ${resolvedTag}`);
1300
1386
  if (config.postSync) {
1301
- await runPostSync(config.postSync);
1387
+ await runPostSync(config.postSync, commandCwd);
1302
1388
  }
1303
1389
  }
1304
- async function runPostSync(command) {
1390
+ async function runPostSync(command, commandCwd) {
1305
1391
  logger.info(`Running postSync: ${command}`);
1306
1392
  const isWindows = process.platform === "win32";
1307
1393
  const shell = isWindows ? "cmd.exe" : "/bin/sh";
@@ -1309,7 +1395,8 @@ async function runPostSync(command) {
1309
1395
  return new Promise((resolve, reject) => {
1310
1396
  const child = spawn(shell, shellArgs, {
1311
1397
  stdio: ["inherit", "pipe", "pipe"],
1312
- env: process.env
1398
+ env: process.env,
1399
+ cwd: commandCwd
1313
1400
  });
1314
1401
  let stdout = "";
1315
1402
  let stderr = "";
@@ -1406,23 +1493,23 @@ program.command("generate").description("Generate platform outputs from design t
1406
1493
  "-c, --config <path>",
1407
1494
  "Path to config file",
1408
1495
  ".clafoutis/producer.json"
1409
- ).option("--tailwind", "Generate Tailwind output").option("--figma", "Generate Figma variables").option("-o, --output <dir>", "Output directory", "./build").option("--dry-run", "Preview changes without writing files").action(withErrorHandling(generateCommand));
1496
+ ).option("--tailwind", "Generate Tailwind output").option("--figma", "Generate Figma variables").option("-o, --output <dir>", "Output directory", "./build").option("--cwd <path>", "Run command as if from this directory").option("--dry-run", "Preview changes without writing files").action(withErrorHandling(generateCommand));
1410
1497
  program.command("sync").description("Sync design tokens from GitHub Release (for consumers)").option("-f, --force", "Force sync even if versions match").option(
1411
1498
  "-c, --config <path>",
1412
1499
  "Path to config file",
1413
1500
  ".clafoutis/consumer.json"
1414
- ).option("--dry-run", "Preview changes without writing files").action(withErrorHandling(syncCommand));
1501
+ ).option("--cwd <path>", "Run command as if from this directory").option("--dry-run", "Preview changes without writing files").action(withErrorHandling(syncCommand));
1415
1502
  program.command("init").description("Initialize Clafoutis configuration").option("--producer", "Set up as a design token producer").option("--consumer", "Set up as a design token consumer").option("-r, --repo <repo>", "GitHub repo for consumer mode (org/name)").option("-t, --tokens <path>", "Token directory path (default: ./tokens)").option("-o, --output <path>", "Output directory path (default: ./build)").option(
1416
1503
  "-g, --generators <list>",
1417
1504
  "Comma-separated generators: tailwind, figma"
1418
1505
  ).option("--workflow", "Create GitHub Actions workflow (default: true)").option("--no-workflow", "Skip GitHub Actions workflow").option(
1419
1506
  "--files <mapping>",
1420
1507
  "File mappings for consumer: asset:dest,asset:dest"
1421
- ).option("--force", "Overwrite existing configuration").option("--dry-run", "Preview changes without writing files").option("--non-interactive", "Skip prompts, use defaults or flags").action(withErrorHandling(initCommand));
1508
+ ).option("--force", "Overwrite existing configuration").option("--dry-run", "Preview changes without writing files").option("--non-interactive", "Skip prompts, use defaults or flags").option("--cwd <path>", "Run command as if from this directory").action(withErrorHandling(initCommand));
1422
1509
  program.command("format").description("Format token JSON files for consistent formatting").option("-t, --tokens <path>", "Token directory path", "./tokens").option(
1423
1510
  "--check",
1424
1511
  "Check formatting without modifying files (fails if unformatted)"
1425
- ).option("--dry-run", "Preview changes without writing files").action(withErrorHandling(formatCommand));
1512
+ ).option("--cwd <path>", "Run command as if from this directory").option("--dry-run", "Preview changes without writing files").action(withErrorHandling(formatCommand));
1426
1513
  program.parse();
1427
1514
  //# sourceMappingURL=index.js.map
1428
1515
  //# sourceMappingURL=index.js.map