@keywaysh/cli 0.1.11 → 0.1.13

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 (2) hide show
  1. package/dist/cli.js +191 -71
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -12,13 +12,14 @@ import pc11 from "picocolors";
12
12
 
13
13
  // src/cmds/init.ts
14
14
  import pc6 from "picocolors";
15
- import prompts4 from "prompts";
15
+ import prompts5 from "prompts";
16
16
 
17
17
  // src/utils/git.ts
18
18
  import { execSync } from "child_process";
19
19
  import fs from "fs";
20
20
  import path from "path";
21
21
  import pc from "picocolors";
22
+ import prompts from "prompts";
22
23
  function getCurrentRepoFullName() {
23
24
  try {
24
25
  if (!isGitRepository()) {
@@ -87,9 +88,47 @@ function checkEnvGitignore() {
87
88
  return true;
88
89
  }
89
90
  }
90
- function warnIfEnvNotGitignored() {
91
- if (!checkEnvGitignore()) {
92
- console.log(pc.yellow("\u26A0\uFE0F .env files are not in .gitignore - secrets may be committed"));
91
+ function addEnvToGitignore() {
92
+ try {
93
+ const gitRoot = execSync("git rev-parse --show-toplevel", {
94
+ encoding: "utf-8",
95
+ stdio: "pipe"
96
+ }).trim();
97
+ const gitignorePath = path.join(gitRoot, ".gitignore");
98
+ const envEntry = ".env*";
99
+ if (fs.existsSync(gitignorePath)) {
100
+ const content = fs.readFileSync(gitignorePath, "utf-8");
101
+ const newContent = content.endsWith("\n") ? `${content}${envEntry}
102
+ ` : `${content}
103
+ ${envEntry}
104
+ `;
105
+ fs.writeFileSync(gitignorePath, newContent);
106
+ } else {
107
+ fs.writeFileSync(gitignorePath, `${envEntry}
108
+ `);
109
+ }
110
+ return true;
111
+ } catch {
112
+ return false;
113
+ }
114
+ }
115
+ async function warnIfEnvNotGitignored() {
116
+ if (checkEnvGitignore()) {
117
+ return;
118
+ }
119
+ console.log(pc.yellow("\u26A0\uFE0F .env files are not in .gitignore - secrets may be committed"));
120
+ const { addToGitignore } = await prompts({
121
+ type: "confirm",
122
+ name: "addToGitignore",
123
+ message: "Add .env* to .gitignore?",
124
+ initial: true
125
+ });
126
+ if (addToGitignore) {
127
+ if (addEnvToGitignore()) {
128
+ console.log(pc.green("\u2713 Added .env* to .gitignore"));
129
+ } else {
130
+ console.log(pc.red("\u2717 Failed to update .gitignore"));
131
+ }
93
132
  }
94
133
  }
95
134
 
@@ -101,7 +140,7 @@ var INTERNAL_POSTHOG_HOST = "https://eu.i.posthog.com";
101
140
  // package.json
102
141
  var package_default = {
103
142
  name: "@keywaysh/cli",
104
- version: "0.1.11",
143
+ version: "0.1.13",
105
144
  description: "One link to all your secrets",
106
145
  type: "module",
107
146
  bin: {
@@ -409,9 +448,10 @@ async function deleteConnection(accessToken, connectionId) {
409
448
  });
410
449
  await handleResponse(response);
411
450
  }
412
- function getProviderAuthUrl(provider, redirectUri) {
413
- const params = redirectUri ? `?redirect_uri=${encodeURIComponent(redirectUri)}` : "";
414
- return `${API_BASE_URL}/v1/integrations/${provider}/authorize${params}`;
451
+ function getProviderAuthUrl(provider, accessToken, redirectUri) {
452
+ const params = new URLSearchParams({ token: accessToken });
453
+ if (redirectUri) params.set("redirect_uri", redirectUri);
454
+ return `${API_BASE_URL}/v1/integrations/${provider}/authorize?${params}`;
415
455
  }
416
456
  async function getConnectionProjects(accessToken, connectionId) {
417
457
  const response = await fetchWithTimeout(`${API_BASE_URL}/v1/integrations/connections/${connectionId}/projects`, {
@@ -718,7 +758,7 @@ var AnalyticsEvents = {
718
758
  // src/cmds/readme.ts
719
759
  import fs3 from "fs";
720
760
  import path3 from "path";
721
- import prompts from "prompts";
761
+ import prompts2 from "prompts";
722
762
  import pc2 from "picocolors";
723
763
  import balanced from "balanced-match";
724
764
  function generateBadge(repo) {
@@ -813,7 +853,7 @@ async function ensureReadme(repoName, cwd) {
813
853
  console.log(pc2.yellow('No README found. Run "keyway readme add-badge" from a repo with a README.'));
814
854
  return null;
815
855
  }
816
- const { confirm } = await prompts(
856
+ const { confirm } = await prompts2(
817
857
  {
818
858
  type: "confirm",
819
859
  name: "confirm",
@@ -863,12 +903,12 @@ async function addBadgeToReadme(silent = false) {
863
903
  import pc5 from "picocolors";
864
904
  import fs4 from "fs";
865
905
  import path4 from "path";
866
- import prompts3 from "prompts";
906
+ import prompts4 from "prompts";
867
907
 
868
908
  // src/cmds/login.ts
869
909
  import pc4 from "picocolors";
870
910
  import readline from "readline";
871
- import prompts2 from "prompts";
911
+ import prompts3 from "prompts";
872
912
 
873
913
  // src/utils/helpers.ts
874
914
  import pc3 from "picocolors";
@@ -1009,7 +1049,7 @@ async function runTokenLogin() {
1009
1049
  await openUrl(url);
1010
1050
  console.log(pc4.gray("Select the detected repo (or scope manually)."));
1011
1051
  console.log(pc4.gray("Permissions: Metadata \u2192 Read-only; Account permissions: None."));
1012
- const { token } = await prompts2(
1052
+ const { token } = await prompts3(
1013
1053
  {
1014
1054
  type: "password",
1015
1055
  name: "token",
@@ -1127,7 +1167,7 @@ async function pushCommand(options) {
1127
1167
  }
1128
1168
  }
1129
1169
  if (!environment && !envFile && isInteractive2 && candidates.length > 0) {
1130
- const { choice } = await prompts3(
1170
+ const { choice } = await prompts4(
1131
1171
  {
1132
1172
  type: "select",
1133
1173
  name: "choice",
@@ -1150,7 +1190,7 @@ async function pushCommand(options) {
1150
1190
  envFile = choice.file;
1151
1191
  environment = choice.env;
1152
1192
  } else if (choice === "custom") {
1153
- const { fileInput } = await prompts3(
1193
+ const { fileInput } = await prompts4(
1154
1194
  {
1155
1195
  type: "text",
1156
1196
  name: "fileInput",
@@ -1200,7 +1240,7 @@ async function pushCommand(options) {
1200
1240
  if (!isInteractive3) {
1201
1241
  throw new Error("Confirmation required. Re-run with --yes in non-interactive environments.");
1202
1242
  }
1203
- const { confirm } = await prompts3(
1243
+ const { confirm } = await prompts4(
1204
1244
  {
1205
1245
  type: "confirm",
1206
1246
  name: "confirm",
@@ -1312,23 +1352,20 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1312
1352
  if (!allowPrompt || !isInteractive()) {
1313
1353
  throw new Error('No Keyway session found. Run "keyway login" to authenticate.');
1314
1354
  }
1355
+ const deviceStart = await startDeviceLogin(repoFullName);
1356
+ const installUrl = deviceStart.githubAppInstallUrl || "https://github.com/apps/keyway/installations/new";
1315
1357
  console.log("");
1316
- console.log(pc6.gray(" Keyway uses a GitHub App for secure access."));
1317
- console.log(pc6.gray(" Installing the app will also log you in."));
1318
- console.log("");
1319
- const { shouldProceed } = await prompts4({
1358
+ const { shouldProceed } = await prompts5({
1320
1359
  type: "confirm",
1321
1360
  name: "shouldProceed",
1322
- message: "Open browser to install Keyway & sign in?",
1361
+ message: "Open browser to sign in?",
1323
1362
  initial: true
1324
1363
  });
1325
1364
  if (!shouldProceed) {
1326
1365
  throw new Error('Setup required. Run "keyway init" when ready.');
1327
1366
  }
1328
- const deviceStart = await startDeviceLogin(repoFullName);
1329
- const installUrl = deviceStart.githubAppInstallUrl || "https://github.com/apps/keyway/installations/new";
1330
- await openUrl(installUrl);
1331
- console.log(pc6.blue("\u23F3 Waiting for installation & authorization..."));
1367
+ await openUrl(deviceStart.verificationUriComplete);
1368
+ console.log(pc6.blue("\u23F3 Waiting for authorization..."));
1332
1369
  console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1333
1370
  const pollIntervalMs = Math.max((deviceStart.interval ?? 5) * 1e3, POLL_INTERVAL_MS);
1334
1371
  const startTime = Date.now();
@@ -1337,30 +1374,73 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1337
1374
  while (Date.now() - startTime < POLL_TIMEOUT_MS) {
1338
1375
  await sleep(pollIntervalMs);
1339
1376
  try {
1340
- if (!accessToken) {
1341
- const result = await pollDeviceLogin(deviceStart.deviceCode);
1342
- if (result.status === "approved" && result.keywayToken) {
1343
- accessToken = result.keywayToken;
1344
- await saveAuthToken(result.keywayToken, {
1345
- githubLogin: result.githubLogin,
1346
- expiresAt: result.expiresAt
1377
+ const result = await pollDeviceLogin(deviceStart.deviceCode);
1378
+ if (result.status === "approved" && result.keywayToken) {
1379
+ accessToken = result.keywayToken;
1380
+ await saveAuthToken(result.keywayToken, {
1381
+ githubLogin: result.githubLogin,
1382
+ expiresAt: result.expiresAt
1383
+ });
1384
+ console.log(pc6.green("\u2713 Signed in!"));
1385
+ if (result.githubLogin) {
1386
+ identifyUser(result.githubLogin, {
1387
+ github_username: result.githubLogin,
1388
+ login_method: "device_flow"
1347
1389
  });
1348
- console.log(pc6.green("\u2713 Signed in!"));
1349
- if (result.githubLogin) {
1350
- identifyUser(result.githubLogin, {
1351
- github_username: result.githubLogin,
1352
- login_method: "github_app"
1353
- });
1354
- }
1355
1390
  }
1391
+ break;
1356
1392
  }
1357
- if (accessToken) {
1358
- const installStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1359
- if (installStatus.installed) {
1360
- console.log(pc6.green("\u2713 GitHub App installed!"));
1361
- console.log("");
1362
- return accessToken;
1363
- }
1393
+ consecutiveErrors = 0;
1394
+ process.stdout.write(pc6.gray("."));
1395
+ } catch (error) {
1396
+ consecutiveErrors++;
1397
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1398
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
1399
+ throw new Error(`Login failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
1400
+ }
1401
+ }
1402
+ }
1403
+ if (!accessToken) {
1404
+ console.log("");
1405
+ console.log(pc6.yellow("\u26A0 Timed out waiting for sign in."));
1406
+ throw new Error("Sign in timed out. Please try again.");
1407
+ }
1408
+ const installStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1409
+ if (installStatus.installed) {
1410
+ console.log(pc6.green("\u2713 GitHub App installed"));
1411
+ console.log("");
1412
+ return accessToken;
1413
+ }
1414
+ console.log("");
1415
+ console.log(pc6.yellow("\u26A0 GitHub App not installed on this repository"));
1416
+ console.log(pc6.gray(" The Keyway GitHub App is required for secure access."));
1417
+ console.log("");
1418
+ const { shouldInstall } = await prompts5({
1419
+ type: "confirm",
1420
+ name: "shouldInstall",
1421
+ message: "Open browser to install GitHub App?",
1422
+ initial: true
1423
+ });
1424
+ if (!shouldInstall) {
1425
+ console.log(pc6.gray(`
1426
+ Install later: ${installUrl}`));
1427
+ throw new Error("GitHub App installation required.");
1428
+ }
1429
+ await openUrl(installUrl);
1430
+ console.log(pc6.blue("\u23F3 Waiting for GitHub App installation..."));
1431
+ console.log(pc6.gray(' Add this repository and click "Install"'));
1432
+ console.log(pc6.gray(" Then return here - the CLI will detect it automatically"));
1433
+ console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1434
+ const installStartTime = Date.now();
1435
+ consecutiveErrors = 0;
1436
+ while (Date.now() - installStartTime < POLL_TIMEOUT_MS) {
1437
+ await sleep(POLL_INTERVAL_MS);
1438
+ try {
1439
+ const pollStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1440
+ if (pollStatus.installed) {
1441
+ console.log(pc6.green("\u2713 GitHub App installed!"));
1442
+ console.log("");
1443
+ return accessToken;
1364
1444
  }
1365
1445
  consecutiveErrors = 0;
1366
1446
  process.stdout.write(pc6.gray("."));
@@ -1368,14 +1448,14 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1368
1448
  consecutiveErrors++;
1369
1449
  if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1370
1450
  const errorMsg = error instanceof Error ? error.message : "Unknown error";
1371
- throw new Error(`Setup failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
1451
+ throw new Error(`Installation check failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
1372
1452
  }
1373
1453
  }
1374
1454
  }
1375
1455
  console.log("");
1376
- console.log(pc6.yellow("\u26A0 Timed out waiting for setup."));
1456
+ console.log(pc6.yellow("\u26A0 Timed out waiting for installation."));
1377
1457
  console.log(pc6.gray(` Install the GitHub App: ${installUrl}`));
1378
- throw new Error("Setup timed out. Please try again.");
1458
+ throw new Error("GitHub App installation timed out.");
1379
1459
  }
1380
1460
  async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1381
1461
  const [repoOwner, repoName] = repoFullName.split("/");
@@ -1404,7 +1484,7 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1404
1484
  console.log(pc6.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
1405
1485
  throw new Error("GitHub App installation required.");
1406
1486
  }
1407
- const { shouldInstall } = await prompts4({
1487
+ const { shouldInstall } = await prompts5({
1408
1488
  type: "confirm",
1409
1489
  name: "shouldInstall",
1410
1490
  message: "Open browser to install Keyway GitHub App?",
@@ -1478,7 +1558,7 @@ async function initCommand(options = {}) {
1478
1558
  if (envCandidates.length > 0 && isInteractive2) {
1479
1559
  console.log(pc6.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
1480
1560
  `));
1481
- const { shouldPush } = await prompts4({
1561
+ const { shouldPush } = await prompts5({
1482
1562
  type: "confirm",
1483
1563
  name: "shouldPush",
1484
1564
  message: "Push secrets now?",
@@ -1536,7 +1616,7 @@ async function initCommand(options = {}) {
1536
1616
  import pc7 from "picocolors";
1537
1617
  import fs5 from "fs";
1538
1618
  import path5 from "path";
1539
- import prompts5 from "prompts";
1619
+ import prompts6 from "prompts";
1540
1620
  async function pullCommand(options) {
1541
1621
  try {
1542
1622
  const environment = options.env || "development";
@@ -1561,7 +1641,7 @@ async function pullCommand(options) {
1561
1641
  } else if (!isInteractive2) {
1562
1642
  throw new Error(`File ${envFile} exists. Re-run with --yes to overwrite or choose a different --file.`);
1563
1643
  } else {
1564
- const { confirm } = await prompts5(
1644
+ const { confirm } = await prompts6(
1565
1645
  {
1566
1646
  type: "confirm",
1567
1647
  name: "confirm",
@@ -1921,7 +2001,7 @@ Summary: ${formatSummary(results)}`);
1921
2001
 
1922
2002
  // src/cmds/connect.ts
1923
2003
  import pc9 from "picocolors";
1924
- import prompts6 from "prompts";
2004
+ import prompts7 from "prompts";
1925
2005
  var TOKEN_AUTH_PROVIDERS = ["railway"];
1926
2006
  function getTokenCreationUrl(provider) {
1927
2007
  switch (provider) {
@@ -1938,7 +2018,7 @@ async function connectWithTokenFlow(accessToken, provider, displayName) {
1938
2018
  console.log(pc9.yellow(` Do NOT use "No workspace" - it won't have access to your projects.`));
1939
2019
  }
1940
2020
  await openUrl(tokenUrl);
1941
- const { token } = await prompts6({
2021
+ const { token } = await prompts7({
1942
2022
  type: "password",
1943
2023
  name: "token",
1944
2024
  message: `${displayName} API Token:`
@@ -1970,7 +2050,7 @@ async function connectWithTokenFlow(accessToken, provider, displayName) {
1970
2050
  }
1971
2051
  }
1972
2052
  async function connectWithOAuthFlow(accessToken, provider, displayName) {
1973
- const authUrl = getProviderAuthUrl(provider);
2053
+ const authUrl = getProviderAuthUrl(provider, accessToken);
1974
2054
  const startTime = /* @__PURE__ */ new Date();
1975
2055
  await openUrl(authUrl);
1976
2056
  console.log(pc9.gray("Waiting for authorization..."));
@@ -2015,7 +2095,7 @@ async function connectCommand(provider, options = {}) {
2015
2095
  const { connections } = await getConnections(accessToken);
2016
2096
  const existingConnection = connections.find((c) => c.provider === provider.toLowerCase());
2017
2097
  if (existingConnection) {
2018
- const { reconnect } = await prompts6({
2098
+ const { reconnect } = await prompts7({
2019
2099
  type: "confirm",
2020
2100
  name: "reconnect",
2021
2101
  message: `You're already connected to ${providerInfo.displayName}. Reconnect?`,
@@ -2087,7 +2167,7 @@ async function disconnectCommand(provider, options = {}) {
2087
2167
  return;
2088
2168
  }
2089
2169
  const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
2090
- const { confirm } = await prompts6({
2170
+ const { confirm } = await prompts7({
2091
2171
  type: "confirm",
2092
2172
  name: "confirm",
2093
2173
  message: `Disconnect from ${providerName}?`,
@@ -2117,7 +2197,7 @@ async function disconnectCommand(provider, options = {}) {
2117
2197
 
2118
2198
  // src/cmds/sync.ts
2119
2199
  import pc10 from "picocolors";
2120
- import prompts7 from "prompts";
2200
+ import prompts8 from "prompts";
2121
2201
  function mapToVercelEnvironment(keywayEnv) {
2122
2202
  const mapping = {
2123
2203
  production: "production",
@@ -2136,12 +2216,24 @@ function mapToRailwayEnvironment(keywayEnv) {
2136
2216
  };
2137
2217
  return mapping[keywayEnv.toLowerCase()] || "production";
2138
2218
  }
2219
+ function mapToNetlifyEnvironment(keywayEnv) {
2220
+ const mapping = {
2221
+ production: "production",
2222
+ staging: "branch-deploy",
2223
+ preview: "deploy-preview",
2224
+ dev: "dev",
2225
+ development: "dev"
2226
+ };
2227
+ return mapping[keywayEnv.toLowerCase()] || "production";
2228
+ }
2139
2229
  function mapToProviderEnvironment(provider, keywayEnv) {
2140
2230
  switch (provider.toLowerCase()) {
2141
2231
  case "vercel":
2142
2232
  return mapToVercelEnvironment(keywayEnv);
2143
2233
  case "railway":
2144
2234
  return mapToRailwayEnvironment(keywayEnv);
2235
+ case "netlify":
2236
+ return mapToNetlifyEnvironment(keywayEnv);
2145
2237
  default:
2146
2238
  return keywayEnv;
2147
2239
  }
@@ -2236,7 +2328,7 @@ async function promptProjectSelection(projects, repoFullName) {
2236
2328
  }
2237
2329
  return { title, value: p.id };
2238
2330
  });
2239
- const { projectChoice } = await prompts7({
2331
+ const { projectChoice } = await prompts8({
2240
2332
  type: "select",
2241
2333
  name: "projectChoice",
2242
2334
  message: "Select a project:",
@@ -2263,13 +2355,39 @@ async function syncCommand(provider, options = {}) {
2263
2355
  process.exit(1);
2264
2356
  }
2265
2357
  console.log(pc10.gray(`Repository: ${repoFullName}`));
2358
+ const vaultExists = await checkVaultExists(accessToken, repoFullName);
2359
+ if (!vaultExists) {
2360
+ console.log(pc10.yellow(`
2361
+ No vault found for ${repoFullName}.`));
2362
+ const { shouldCreate } = await prompts8({
2363
+ type: "confirm",
2364
+ name: "shouldCreate",
2365
+ message: "Create vault now?",
2366
+ initial: true
2367
+ });
2368
+ if (!shouldCreate) {
2369
+ console.log(pc10.gray("Cancelled. Run `keyway init` to create a vault first."));
2370
+ process.exit(0);
2371
+ }
2372
+ console.log(pc10.gray("\nCreating vault..."));
2373
+ try {
2374
+ await initVault(repoFullName, accessToken);
2375
+ console.log(pc10.green(`\u2713 Vault created for ${repoFullName}
2376
+ `));
2377
+ } catch (error) {
2378
+ const message = error instanceof Error ? error.message : "Failed to create vault";
2379
+ console.error(pc10.red(`
2380
+ \u2717 ${message}`));
2381
+ process.exit(1);
2382
+ }
2383
+ }
2266
2384
  let { connections } = await getConnections(accessToken);
2267
2385
  let connection = connections.find((c) => c.provider === provider.toLowerCase());
2268
2386
  if (!connection) {
2269
2387
  const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1);
2270
2388
  console.log(pc10.yellow(`
2271
2389
  Not connected to ${providerDisplayName}.`));
2272
- const { shouldConnect } = await prompts7({
2390
+ const { shouldConnect } = await prompts8({
2273
2391
  type: "confirm",
2274
2392
  name: "shouldConnect",
2275
2393
  message: `Connect to ${providerDisplayName} now?`,
@@ -2328,7 +2446,7 @@ Connection to ${providerDisplayName} failed.`));
2328
2446
  } else if (autoMatch && autoMatch.matchType === "partial_name") {
2329
2447
  const partialDisplayName = getProjectDisplayName(autoMatch.project);
2330
2448
  console.log(pc10.yellow(`Detected project: ${partialDisplayName} (partial match)`));
2331
- const { useDetected } = await prompts7({
2449
+ const { useDetected } = await prompts8({
2332
2450
  type: "confirm",
2333
2451
  name: "useDetected",
2334
2452
  message: `Use ${partialDisplayName}?`,
@@ -2352,7 +2470,7 @@ Connection to ${providerDisplayName} failed.`));
2352
2470
  console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2353
2471
  }
2354
2472
  console.log("");
2355
- const { continueAnyway } = await prompts7({
2473
+ const { continueAnyway } = await prompts8({
2356
2474
  type: "confirm",
2357
2475
  name: "continueAnyway",
2358
2476
  message: "Continue anyway?",
@@ -2383,7 +2501,7 @@ Connection to ${providerDisplayName} failed.`));
2383
2501
  console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2384
2502
  }
2385
2503
  console.log("");
2386
- const { continueAnyway } = await prompts7({
2504
+ const { continueAnyway } = await prompts8({
2387
2505
  type: "confirm",
2388
2506
  name: "continueAnyway",
2389
2507
  message: "Are you sure you want to sync with this project?",
@@ -2404,7 +2522,7 @@ Connection to ${providerDisplayName} failed.`));
2404
2522
  if (needsEnvPrompt || needsDirectionPrompt) {
2405
2523
  if (needsEnvPrompt) {
2406
2524
  const vaultEnvs = await getVaultEnvironments(accessToken, repoFullName);
2407
- const { selectedEnv } = await prompts7({
2525
+ const { selectedEnv } = await prompts8({
2408
2526
  type: "select",
2409
2527
  name: "selectedEnv",
2410
2528
  message: "Keyway environment:",
@@ -2428,7 +2546,7 @@ Connection to ${providerDisplayName} failed.`));
2428
2546
  providerEnv = selectedProject.environments[0];
2429
2547
  console.log(pc10.gray(`Using ${providerName} environment: ${providerEnv}`));
2430
2548
  } else {
2431
- const { selectedProviderEnv } = await prompts7({
2549
+ const { selectedProviderEnv } = await prompts8({
2432
2550
  type: "select",
2433
2551
  name: "selectedProviderEnv",
2434
2552
  message: `${providerName} environment:`,
@@ -2474,7 +2592,7 @@ Connection to ${providerDisplayName} failed.`));
2474
2592
  } else if (diff.providerCount === 0 && diff.keywayCount > 0) {
2475
2593
  defaultDirection = 0;
2476
2594
  }
2477
- const { selectedDirection } = await prompts7({
2595
+ const { selectedDirection } = await prompts8({
2478
2596
  type: "select",
2479
2597
  name: "selectedDirection",
2480
2598
  message: "Sync direction:",
@@ -2505,7 +2623,7 @@ Connection to ${providerDisplayName} failed.`));
2505
2623
  console.log(pc10.yellow(`
2506
2624
  \u26A0\uFE0F Your Keyway vault is empty for "${keywayEnv}", but ${providerName} has ${status.providerSecretCount} secrets.`));
2507
2625
  console.log(pc10.gray(` (Use --environment to sync a different environment)`));
2508
- const { importFirst } = await prompts7({
2626
+ const { importFirst } = await prompts8({
2509
2627
  type: "confirm",
2510
2628
  name: "importFirst",
2511
2629
  message: `Import secrets from ${providerName} first?`,
@@ -2596,7 +2714,7 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2596
2714
  console.log("");
2597
2715
  if (!skipConfirm) {
2598
2716
  const target = direction === "push" ? providerName : "Keyway";
2599
- const { confirm } = await prompts7({
2717
+ const { confirm } = await prompts8({
2600
2718
  type: "confirm",
2601
2719
  name: "confirm",
2602
2720
  message: `Apply ${totalChanges} changes to ${target}?`,
@@ -2654,7 +2772,6 @@ ${pc11.gray(TAGLINE)}
2654
2772
  `);
2655
2773
  };
2656
2774
  showBanner();
2657
- warnIfEnvNotGitignored();
2658
2775
  program.name("keyway").description(TAGLINE).version(package_default.version);
2659
2776
  program.command("init").description("Initialize a vault for the current repository").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (options) => {
2660
2777
  await initCommand(options);
@@ -2686,7 +2803,10 @@ program.command("disconnect <provider>").description("Disconnect from a provider
2686
2803
  program.command("sync <provider>").description("Sync secrets with a provider (e.g., vercel)").option("--push", "Export secrets from Keyway to provider").option("--pull", "Import secrets from provider to Keyway").option("-e, --environment <env>", "Keyway environment (default: production)").option("--provider-env <env>", "Provider environment (default: production)").option("--project <project>", "Provider project name or ID").option("--allow-delete", "Allow deleting secrets not in source").option("-y, --yes", "Skip confirmation prompt").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (provider, options) => {
2687
2804
  await syncCommand(provider, options);
2688
2805
  });
2689
- program.parseAsync().catch((error) => {
2806
+ (async () => {
2807
+ await warnIfEnvNotGitignored();
2808
+ await program.parseAsync();
2809
+ })().catch((error) => {
2690
2810
  console.error(pc11.red("Error:"), error.message || error);
2691
2811
  process.exit(1);
2692
2812
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keywaysh/cli",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "One link to all your secrets",
5
5
  "type": "module",
6
6
  "bin": {