@keywaysh/cli 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +19 -0
  2. package/dist/cli.js +348 -263
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -8,17 +8,18 @@ import {
8
8
 
9
9
  // src/cli.ts
10
10
  import { Command } from "commander";
11
- import pc11 from "picocolors";
11
+ import pc12 from "picocolors";
12
12
 
13
13
  // src/cmds/init.ts
14
- import pc6 from "picocolors";
15
- import prompts4 from "prompts";
14
+ import pc7 from "picocolors";
15
+ import prompts6 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.12",
143
+ version: "0.1.14",
105
144
  description: "One link to all your secrets",
106
145
  type: "module",
107
146
  bin: {
@@ -719,7 +758,7 @@ var AnalyticsEvents = {
719
758
  // src/cmds/readme.ts
720
759
  import fs3 from "fs";
721
760
  import path3 from "path";
722
- import prompts from "prompts";
761
+ import prompts2 from "prompts";
723
762
  import pc2 from "picocolors";
724
763
  import balanced from "balanced-match";
725
764
  function generateBadge(repo) {
@@ -814,7 +853,7 @@ async function ensureReadme(repoName, cwd) {
814
853
  console.log(pc2.yellow('No README found. Run "keyway readme add-badge" from a repo with a README.'));
815
854
  return null;
816
855
  }
817
- const { confirm } = await prompts(
856
+ const { confirm } = await prompts2(
818
857
  {
819
858
  type: "confirm",
820
859
  name: "confirm",
@@ -861,15 +900,15 @@ async function addBadgeToReadme(silent = false) {
861
900
  }
862
901
 
863
902
  // src/cmds/push.ts
864
- import pc5 from "picocolors";
865
- import fs4 from "fs";
866
- import path4 from "path";
867
- import prompts3 from "prompts";
903
+ import pc6 from "picocolors";
904
+ import fs5 from "fs";
905
+ import path5 from "path";
906
+ import prompts5 from "prompts";
868
907
 
869
908
  // src/cmds/login.ts
870
909
  import pc4 from "picocolors";
871
910
  import readline from "readline";
872
- import prompts2 from "prompts";
911
+ import prompts3 from "prompts";
873
912
 
874
913
  // src/utils/helpers.ts
875
914
  import pc3 from "picocolors";
@@ -1010,7 +1049,7 @@ async function runTokenLogin() {
1010
1049
  await openUrl(url);
1011
1050
  console.log(pc4.gray("Select the detected repo (or scope manually)."));
1012
1051
  console.log(pc4.gray("Permissions: Metadata \u2192 Read-only; Account permissions: None."));
1013
- const { token } = await prompts2(
1052
+ const { token } = await prompts3(
1014
1053
  {
1015
1054
  type: "password",
1016
1055
  name: "token",
@@ -1073,9 +1112,34 @@ async function logoutCommand() {
1073
1112
  console.log(pc4.gray(`Auth cache cleared: ${getAuthFilePath()}`));
1074
1113
  }
1075
1114
 
1115
+ // src/utils/env.ts
1116
+ import fs4 from "fs";
1117
+ import path4 from "path";
1118
+ import pc5 from "picocolors";
1119
+ import prompts4 from "prompts";
1120
+ async function promptCreateEnvFile() {
1121
+ const { createEnv } = await prompts4({
1122
+ type: "confirm",
1123
+ name: "createEnv",
1124
+ message: "No .env file found. Create one?",
1125
+ initial: true
1126
+ }, {
1127
+ onCancel: () => {
1128
+ throw new Error("Cancelled by user.");
1129
+ }
1130
+ });
1131
+ if (!createEnv) {
1132
+ return false;
1133
+ }
1134
+ const envFilePath = path4.join(process.cwd(), ".env");
1135
+ fs4.writeFileSync(envFilePath, "# Add your environment variables here\n# Example: API_KEY=your-api-key\n");
1136
+ console.log(pc5.green("\u2713 Created .env file"));
1137
+ return true;
1138
+ }
1139
+
1076
1140
  // src/cmds/push.ts
1077
1141
  function deriveEnvFromFile(file) {
1078
- const base = path4.basename(file);
1142
+ const base = path5.basename(file);
1079
1143
  const match = base.match(/\.env(?:\.(.+))?$/);
1080
1144
  if (match) {
1081
1145
  return match[1] || "development";
@@ -1084,15 +1148,15 @@ function deriveEnvFromFile(file) {
1084
1148
  }
1085
1149
  function discoverEnvCandidates(cwd) {
1086
1150
  try {
1087
- const entries = fs4.readdirSync(cwd);
1151
+ const entries = fs5.readdirSync(cwd);
1088
1152
  const hasEnvLocal = entries.includes(".env.local");
1089
1153
  if (hasEnvLocal) {
1090
- console.log(pc5.gray("\u2139\uFE0F Detected .env.local \u2014 not synced by design (machine-specific secrets)"));
1154
+ console.log(pc6.gray("\u2139\uFE0F Detected .env.local \u2014 not synced by design (machine-specific secrets)"));
1091
1155
  }
1092
1156
  const candidates = entries.filter((name) => name.startsWith(".env") && name !== ".env.local").map((name) => {
1093
- const fullPath = path4.join(cwd, name);
1157
+ const fullPath = path5.join(cwd, name);
1094
1158
  try {
1095
- const stat = fs4.statSync(fullPath);
1159
+ const stat = fs5.statSync(fullPath);
1096
1160
  if (!stat.isFile()) return null;
1097
1161
  return { file: name, env: deriveEnvFromFile(name) };
1098
1162
  } catch {
@@ -1113,13 +1177,21 @@ function discoverEnvCandidates(cwd) {
1113
1177
  }
1114
1178
  async function pushCommand(options) {
1115
1179
  try {
1116
- console.log(pc5.blue("\u{1F510} Pushing secrets to Keyway...\n"));
1180
+ console.log(pc6.blue("\u{1F510} Pushing secrets to Keyway...\n"));
1117
1181
  const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
1118
1182
  let environment = options.env;
1119
1183
  let envFile = options.file;
1120
1184
  const candidates = discoverEnvCandidates(process.cwd());
1121
1185
  if (candidates.length === 0 && !envFile) {
1122
- throw new Error("No .env file found. Create a .env file first, or use --file <path> to specify one.");
1186
+ if (!isInteractive2) {
1187
+ throw new Error("No .env file found. Create a .env file first, or use --file <path> to specify one.");
1188
+ }
1189
+ const created = await promptCreateEnvFile();
1190
+ if (!created) {
1191
+ throw new Error("No .env file found.");
1192
+ }
1193
+ console.log(pc6.gray(" Add your variables and run keyway push again\n"));
1194
+ return;
1123
1195
  }
1124
1196
  if (environment && !envFile) {
1125
1197
  const match = candidates.find((c) => c.env === environment);
@@ -1128,7 +1200,7 @@ async function pushCommand(options) {
1128
1200
  }
1129
1201
  }
1130
1202
  if (!environment && !envFile && isInteractive2 && candidates.length > 0) {
1131
- const { choice } = await prompts3(
1203
+ const { choice } = await prompts5(
1132
1204
  {
1133
1205
  type: "select",
1134
1206
  name: "choice",
@@ -1151,15 +1223,15 @@ async function pushCommand(options) {
1151
1223
  envFile = choice.file;
1152
1224
  environment = choice.env;
1153
1225
  } else if (choice === "custom") {
1154
- const { fileInput } = await prompts3(
1226
+ const { fileInput } = await prompts5(
1155
1227
  {
1156
1228
  type: "text",
1157
1229
  name: "fileInput",
1158
1230
  message: "Path to env file:",
1159
1231
  validate: (value) => {
1160
1232
  if (!value) return "Path is required";
1161
- const resolved = path4.resolve(process.cwd(), value);
1162
- if (!fs4.existsSync(resolved)) return `File not found: ${value}`;
1233
+ const resolved = path5.resolve(process.cwd(), value);
1234
+ if (!fs5.existsSync(resolved)) return `File not found: ${value}`;
1163
1235
  return true;
1164
1236
  }
1165
1237
  },
@@ -1179,11 +1251,11 @@ async function pushCommand(options) {
1179
1251
  if (!envFile) {
1180
1252
  envFile = ".env";
1181
1253
  }
1182
- const envFilePath = path4.resolve(process.cwd(), envFile);
1183
- if (!fs4.existsSync(envFilePath)) {
1254
+ const envFilePath = path5.resolve(process.cwd(), envFile);
1255
+ if (!fs5.existsSync(envFilePath)) {
1184
1256
  throw new Error(`File not found: ${envFile}`);
1185
1257
  }
1186
- const content = fs4.readFileSync(envFilePath, "utf-8");
1258
+ const content = fs5.readFileSync(envFilePath, "utf-8");
1187
1259
  if (content.trim().length === 0) {
1188
1260
  throw new Error(`File is empty: ${envFile}`);
1189
1261
  }
@@ -1191,17 +1263,17 @@ async function pushCommand(options) {
1191
1263
  const trimmed = line.trim();
1192
1264
  return trimmed.length > 0 && !trimmed.startsWith("#");
1193
1265
  });
1194
- console.log(`File: ${pc5.cyan(envFile)}`);
1195
- console.log(`Environment: ${pc5.cyan(environment)}`);
1196
- console.log(`Variables: ${pc5.cyan(lines.length.toString())}`);
1266
+ console.log(`File: ${pc6.cyan(envFile)}`);
1267
+ console.log(`Environment: ${pc6.cyan(environment)}`);
1268
+ console.log(`Variables: ${pc6.cyan(lines.length.toString())}`);
1197
1269
  const repoFullName = getCurrentRepoFullName();
1198
- console.log(`Repository: ${pc5.cyan(repoFullName)}`);
1270
+ console.log(`Repository: ${pc6.cyan(repoFullName)}`);
1199
1271
  if (!options.yes) {
1200
1272
  const isInteractive3 = process.stdin.isTTY && process.stdout.isTTY;
1201
1273
  if (!isInteractive3) {
1202
1274
  throw new Error("Confirmation required. Re-run with --yes in non-interactive environments.");
1203
1275
  }
1204
- const { confirm } = await prompts3(
1276
+ const { confirm } = await prompts5(
1205
1277
  {
1206
1278
  type: "confirm",
1207
1279
  name: "confirm",
@@ -1215,7 +1287,7 @@ async function pushCommand(options) {
1215
1287
  }
1216
1288
  );
1217
1289
  if (!confirm) {
1218
- console.log(pc5.yellow("Push aborted."));
1290
+ console.log(pc6.yellow("Push aborted."));
1219
1291
  return;
1220
1292
  }
1221
1293
  }
@@ -1227,13 +1299,13 @@ async function pushCommand(options) {
1227
1299
  });
1228
1300
  console.log("\nUploading secrets...");
1229
1301
  const response = await pushSecrets(repoFullName, environment, content, accessToken);
1230
- console.log(pc5.green("\n\u2713 " + response.message));
1302
+ console.log(pc6.green("\n\u2713 " + response.message));
1231
1303
  if (response.stats) {
1232
1304
  const { created, updated, deleted } = response.stats;
1233
1305
  const parts = [];
1234
- if (created > 0) parts.push(pc5.green(`+${created} created`));
1235
- if (updated > 0) parts.push(pc5.yellow(`~${updated} updated`));
1236
- if (deleted > 0) parts.push(pc5.red(`-${deleted} deleted`));
1306
+ if (created > 0) parts.push(pc6.green(`+${created} created`));
1307
+ if (updated > 0) parts.push(pc6.yellow(`~${updated} updated`));
1308
+ if (deleted > 0) parts.push(pc6.red(`-${deleted} deleted`));
1237
1309
  if (parts.length > 0) {
1238
1310
  console.log(`Stats: ${parts.join(", ")}`);
1239
1311
  }
@@ -1242,7 +1314,7 @@ async function pushCommand(options) {
1242
1314
  Your secrets are now encrypted and stored securely.`);
1243
1315
  const dashboardLink = `https://www.keyway.sh/dashboard/vaults/${repoFullName}`;
1244
1316
  console.log(`
1245
- ${pc5.blue("\u2394")} Dashboard: ${pc5.underline(dashboardLink)}`);
1317
+ ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(dashboardLink)}`);
1246
1318
  await shutdownAnalytics();
1247
1319
  } catch (error) {
1248
1320
  let message;
@@ -1255,7 +1327,7 @@ ${pc5.blue("\u2394")} Dashboard: ${pc5.underline(dashboardLink)}`);
1255
1327
  const availableEnvs = envNotFoundMatch[2];
1256
1328
  message = `Environment '${requestedEnv}' does not exist in this vault.`;
1257
1329
  hint = `Available environments: ${availableEnvs}
1258
- Use ${pc5.cyan(`keyway push --env <environment>`)} to specify one, or create '${requestedEnv}' via the dashboard.`;
1330
+ Use ${pc6.cyan(`keyway push --env <environment>`)} to specify one, or create '${requestedEnv}' via the dashboard.`;
1259
1331
  }
1260
1332
  if (error.statusCode === 403 && (error.upgradeUrl || message.toLowerCase().includes("read-only"))) {
1261
1333
  const upgradeMessage = message.toLowerCase().includes("read-only") ? "This vault is read-only on your current plan." : message;
@@ -1278,10 +1350,10 @@ Use ${pc5.cyan(`keyway push --env <environment>`)} to specify one, or create '${
1278
1350
  error: message
1279
1351
  });
1280
1352
  await shutdownAnalytics();
1281
- console.error(pc5.red(`
1353
+ console.error(pc6.red(`
1282
1354
  \u2717 ${message}`));
1283
1355
  if (hint) {
1284
- console.error(pc5.gray(`
1356
+ console.error(pc6.gray(`
1285
1357
  ${hint}`));
1286
1358
  }
1287
1359
  process.exit(1);
@@ -1316,7 +1388,7 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1316
1388
  const deviceStart = await startDeviceLogin(repoFullName);
1317
1389
  const installUrl = deviceStart.githubAppInstallUrl || "https://github.com/apps/keyway/installations/new";
1318
1390
  console.log("");
1319
- const { shouldProceed } = await prompts4({
1391
+ const { shouldProceed } = await prompts6({
1320
1392
  type: "confirm",
1321
1393
  name: "shouldProceed",
1322
1394
  message: "Open browser to sign in?",
@@ -1326,8 +1398,8 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1326
1398
  throw new Error('Setup required. Run "keyway init" when ready.');
1327
1399
  }
1328
1400
  await openUrl(deviceStart.verificationUriComplete);
1329
- console.log(pc6.blue("\u23F3 Waiting for authorization..."));
1330
- console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1401
+ console.log(pc7.blue("\u23F3 Waiting for authorization..."));
1402
+ console.log(pc7.gray(" (Press Ctrl+C to cancel)\n"));
1331
1403
  const pollIntervalMs = Math.max((deviceStart.interval ?? 5) * 1e3, POLL_INTERVAL_MS);
1332
1404
  const startTime = Date.now();
1333
1405
  let accessToken = null;
@@ -1342,7 +1414,7 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1342
1414
  githubLogin: result.githubLogin,
1343
1415
  expiresAt: result.expiresAt
1344
1416
  });
1345
- console.log(pc6.green("\u2713 Signed in!"));
1417
+ console.log(pc7.green("\u2713 Signed in!"));
1346
1418
  if (result.githubLogin) {
1347
1419
  identifyUser(result.githubLogin, {
1348
1420
  github_username: result.githubLogin,
@@ -1352,7 +1424,7 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1352
1424
  break;
1353
1425
  }
1354
1426
  consecutiveErrors = 0;
1355
- process.stdout.write(pc6.gray("."));
1427
+ process.stdout.write(pc7.gray("."));
1356
1428
  } catch (error) {
1357
1429
  consecutiveErrors++;
1358
1430
  if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
@@ -1363,35 +1435,35 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1363
1435
  }
1364
1436
  if (!accessToken) {
1365
1437
  console.log("");
1366
- console.log(pc6.yellow("\u26A0 Timed out waiting for sign in."));
1438
+ console.log(pc7.yellow("\u26A0 Timed out waiting for sign in."));
1367
1439
  throw new Error("Sign in timed out. Please try again.");
1368
1440
  }
1369
1441
  const installStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1370
1442
  if (installStatus.installed) {
1371
- console.log(pc6.green("\u2713 GitHub App installed"));
1443
+ console.log(pc7.green("\u2713 GitHub App installed"));
1372
1444
  console.log("");
1373
1445
  return accessToken;
1374
1446
  }
1375
1447
  console.log("");
1376
- console.log(pc6.yellow("\u26A0 GitHub App not installed on this repository"));
1377
- console.log(pc6.gray(" The Keyway GitHub App is required for secure access."));
1448
+ console.log(pc7.yellow("\u26A0 GitHub App not installed on this repository"));
1449
+ console.log(pc7.gray(" The Keyway GitHub App is required for secure access."));
1378
1450
  console.log("");
1379
- const { shouldInstall } = await prompts4({
1451
+ const { shouldInstall } = await prompts6({
1380
1452
  type: "confirm",
1381
1453
  name: "shouldInstall",
1382
1454
  message: "Open browser to install GitHub App?",
1383
1455
  initial: true
1384
1456
  });
1385
1457
  if (!shouldInstall) {
1386
- console.log(pc6.gray(`
1458
+ console.log(pc7.gray(`
1387
1459
  Install later: ${installUrl}`));
1388
1460
  throw new Error("GitHub App installation required.");
1389
1461
  }
1390
1462
  await openUrl(installUrl);
1391
- console.log(pc6.blue("\u23F3 Waiting for GitHub App installation..."));
1392
- console.log(pc6.gray(' Add this repository and click "Install"'));
1393
- console.log(pc6.gray(" Then return here - the CLI will detect it automatically"));
1394
- console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1463
+ console.log(pc7.blue("\u23F3 Waiting for GitHub App installation..."));
1464
+ console.log(pc7.gray(' Add this repository and click "Install"'));
1465
+ console.log(pc7.gray(" Then return here - the CLI will detect it automatically"));
1466
+ console.log(pc7.gray(" (Press Ctrl+C to cancel)\n"));
1395
1467
  const installStartTime = Date.now();
1396
1468
  consecutiveErrors = 0;
1397
1469
  while (Date.now() - installStartTime < POLL_TIMEOUT_MS) {
@@ -1399,12 +1471,12 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1399
1471
  try {
1400
1472
  const pollStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1401
1473
  if (pollStatus.installed) {
1402
- console.log(pc6.green("\u2713 GitHub App installed!"));
1474
+ console.log(pc7.green("\u2713 GitHub App installed!"));
1403
1475
  console.log("");
1404
1476
  return accessToken;
1405
1477
  }
1406
1478
  consecutiveErrors = 0;
1407
- process.stdout.write(pc6.gray("."));
1479
+ process.stdout.write(pc7.gray("."));
1408
1480
  } catch (error) {
1409
1481
  consecutiveErrors++;
1410
1482
  if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
@@ -1414,8 +1486,8 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1414
1486
  }
1415
1487
  }
1416
1488
  console.log("");
1417
- console.log(pc6.yellow("\u26A0 Timed out waiting for installation."));
1418
- console.log(pc6.gray(` Install the GitHub App: ${installUrl}`));
1489
+ console.log(pc7.yellow("\u26A0 Timed out waiting for installation."));
1490
+ console.log(pc7.gray(` Install the GitHub App: ${installUrl}`));
1419
1491
  throw new Error("GitHub App installation timed out.");
1420
1492
  }
1421
1493
  async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
@@ -1425,7 +1497,7 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1425
1497
  status = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1426
1498
  } catch (error) {
1427
1499
  if (error instanceof APIError && error.statusCode === 401) {
1428
- console.log(pc6.yellow("\n\u26A0 Session expired or invalid. Clearing credentials..."));
1500
+ console.log(pc7.yellow("\n\u26A0 Session expired or invalid. Clearing credentials..."));
1429
1501
  const { clearAuth: clearAuth2 } = await import("./auth-QLPQ24HZ.js");
1430
1502
  clearAuth2();
1431
1503
  return null;
@@ -1436,29 +1508,29 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1436
1508
  return accessToken;
1437
1509
  }
1438
1510
  console.log("");
1439
- console.log(pc6.yellow("\u26A0 GitHub App not installed for this repository"));
1511
+ console.log(pc7.yellow("\u26A0 GitHub App not installed for this repository"));
1440
1512
  console.log("");
1441
- console.log(pc6.gray(" The Keyway GitHub App is required to securely manage secrets."));
1442
- console.log(pc6.gray(" It only requests minimal permissions (repository metadata)."));
1513
+ console.log(pc7.gray(" The Keyway GitHub App is required to securely manage secrets."));
1514
+ console.log(pc7.gray(" It only requests minimal permissions (repository metadata)."));
1443
1515
  console.log("");
1444
1516
  if (!isInteractive()) {
1445
- console.log(pc6.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
1517
+ console.log(pc7.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
1446
1518
  throw new Error("GitHub App installation required.");
1447
1519
  }
1448
- const { shouldInstall } = await prompts4({
1520
+ const { shouldInstall } = await prompts6({
1449
1521
  type: "confirm",
1450
1522
  name: "shouldInstall",
1451
1523
  message: "Open browser to install Keyway GitHub App?",
1452
1524
  initial: true
1453
1525
  });
1454
1526
  if (!shouldInstall) {
1455
- console.log(pc6.gray(`
1527
+ console.log(pc7.gray(`
1456
1528
  You can install later: ${status.installUrl}`));
1457
1529
  throw new Error("GitHub App installation required.");
1458
1530
  }
1459
1531
  await openUrl(status.installUrl);
1460
- console.log(pc6.blue("\u23F3 Waiting for GitHub App installation..."));
1461
- console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1532
+ console.log(pc7.blue("\u23F3 Waiting for GitHub App installation..."));
1533
+ console.log(pc7.gray(" (Press Ctrl+C to cancel)\n"));
1462
1534
  const startTime = Date.now();
1463
1535
  let consecutiveErrors = 0;
1464
1536
  while (Date.now() - startTime < POLL_TIMEOUT_MS) {
@@ -1466,12 +1538,12 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1466
1538
  try {
1467
1539
  const pollStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1468
1540
  if (pollStatus.installed) {
1469
- console.log(pc6.green("\u2713 GitHub App installed!"));
1541
+ console.log(pc7.green("\u2713 GitHub App installed!"));
1470
1542
  console.log("");
1471
1543
  return accessToken;
1472
1544
  }
1473
1545
  consecutiveErrors = 0;
1474
- process.stdout.write(pc6.gray("."));
1546
+ process.stdout.write(pc7.gray("."));
1475
1547
  } catch (error) {
1476
1548
  consecutiveErrors++;
1477
1549
  if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
@@ -1481,35 +1553,35 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1481
1553
  }
1482
1554
  }
1483
1555
  console.log("");
1484
- console.log(pc6.yellow("\u26A0 Timed out waiting for installation."));
1485
- console.log(pc6.gray(` You can install the GitHub App later: ${status.installUrl}`));
1556
+ console.log(pc7.yellow("\u26A0 Timed out waiting for installation."));
1557
+ console.log(pc7.gray(` You can install the GitHub App later: ${status.installUrl}`));
1486
1558
  throw new Error("GitHub App installation timed out.");
1487
1559
  }
1488
1560
  async function initCommand(options = {}) {
1489
1561
  try {
1490
1562
  const repoFullName = getCurrentRepoFullName();
1491
1563
  const dashboardLink = `${DASHBOARD_URL}/${repoFullName}`;
1492
- console.log(pc6.blue("\u{1F510} Initializing Keyway vault...\n"));
1493
- console.log(` ${pc6.gray("Repository:")} ${pc6.white(repoFullName)}`);
1564
+ console.log(pc7.blue("\u{1F510} Initializing Keyway vault...\n"));
1565
+ console.log(` ${pc7.gray("Repository:")} ${pc7.white(repoFullName)}`);
1494
1566
  const accessToken = await ensureLoginAndGitHubApp(repoFullName, {
1495
1567
  allowPrompt: options.loginPrompt !== false
1496
1568
  });
1497
1569
  trackEvent(AnalyticsEvents.CLI_INIT, { repoFullName, githubAppInstalled: true });
1498
1570
  const vaultExists = await checkVaultExists(accessToken, repoFullName);
1499
1571
  if (vaultExists) {
1500
- console.log(pc6.green("\n\u2713 Already initialized!\n"));
1501
- console.log(` ${pc6.yellow("\u2192")} Run ${pc6.cyan("keyway push")} to sync your secrets`);
1502
- console.log(` ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(dashboardLink)}`);
1572
+ console.log(pc7.green("\n\u2713 Already initialized!\n"));
1573
+ console.log(` ${pc7.yellow("\u2192")} Run ${pc7.cyan("keyway push")} to sync your secrets`);
1574
+ console.log(` ${pc7.blue("\u2394")} Dashboard: ${pc7.underline(dashboardLink)}`);
1503
1575
  console.log("");
1504
1576
  await shutdownAnalytics();
1505
1577
  return;
1506
1578
  }
1507
1579
  await initVault(repoFullName, accessToken);
1508
- console.log(pc6.green("\u2713 Vault created!"));
1580
+ console.log(pc7.green("\u2713 Vault created!"));
1509
1581
  try {
1510
1582
  const badgeAdded = await addBadgeToReadme(true);
1511
1583
  if (badgeAdded) {
1512
- console.log(pc6.green("\u2713 Badge added to README.md"));
1584
+ console.log(pc7.green("\u2713 Badge added to README.md"));
1513
1585
  }
1514
1586
  } catch {
1515
1587
  }
@@ -1517,9 +1589,9 @@ async function initCommand(options = {}) {
1517
1589
  const envCandidates = discoverEnvCandidates(process.cwd());
1518
1590
  const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
1519
1591
  if (envCandidates.length > 0 && isInteractive2) {
1520
- console.log(pc6.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
1592
+ console.log(pc7.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
1521
1593
  `));
1522
- const { shouldPush } = await prompts4({
1594
+ const { shouldPush } = await prompts6({
1523
1595
  type: "confirm",
1524
1596
  name: "shouldPush",
1525
1597
  message: "Push secrets now?",
@@ -1531,25 +1603,36 @@ async function initCommand(options = {}) {
1531
1603
  return;
1532
1604
  }
1533
1605
  }
1534
- console.log(pc6.dim("\u2500".repeat(50)));
1606
+ console.log(pc7.dim("\u2500".repeat(50)));
1535
1607
  console.log("");
1536
1608
  if (envCandidates.length === 0) {
1537
- console.log(`${pc6.yellow("\u26A0")} No .env file found - your vault is empty`);
1538
- console.log(` Next: Create ${pc6.cyan(".env")} and run ${pc6.cyan("keyway push")}
1609
+ if (isInteractive2) {
1610
+ const created = await promptCreateEnvFile();
1611
+ if (created) {
1612
+ console.log(` Add your variables and run ${pc7.cyan("keyway push")}
1539
1613
  `);
1614
+ } else {
1615
+ console.log(` Next: Create ${pc7.cyan(".env")} and run ${pc7.cyan("keyway push")}
1616
+ `);
1617
+ }
1618
+ } else {
1619
+ console.log(`${pc7.yellow("\u26A0")} No .env file found - your vault is empty`);
1620
+ console.log(` Next: Create ${pc7.cyan(".env")} and run ${pc7.cyan("keyway push")}
1621
+ `);
1622
+ }
1540
1623
  } else {
1541
- console.log(` ${pc6.yellow("\u2192")} Run ${pc6.cyan("keyway push")} to sync your secrets
1624
+ console.log(` ${pc7.yellow("\u2192")} Run ${pc7.cyan("keyway push")} to sync your secrets
1542
1625
  `);
1543
1626
  }
1544
- console.log(` ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(dashboardLink)}`);
1627
+ console.log(` ${pc7.blue("\u2394")} Dashboard: ${pc7.underline(dashboardLink)}`);
1545
1628
  console.log("");
1546
1629
  await shutdownAnalytics();
1547
1630
  } catch (error) {
1548
1631
  if (error instanceof APIError) {
1549
1632
  if (error.statusCode === 409) {
1550
- console.log(pc6.green("\n\u2713 Already initialized!\n"));
1551
- console.log(` ${pc6.yellow("\u2192")} Run ${pc6.cyan("keyway push")} to sync your secrets`);
1552
- console.log(` ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(`${DASHBOARD_URL}/${getCurrentRepoFullName()}`)}`);
1633
+ console.log(pc7.green("\n\u2713 Already initialized!\n"));
1634
+ console.log(` ${pc7.yellow("\u2192")} Run ${pc7.cyan("keyway push")} to sync your secrets`);
1635
+ console.log(` ${pc7.blue("\u2394")} Dashboard: ${pc7.underline(`${DASHBOARD_URL}/${getCurrentRepoFullName()}`)}`);
1553
1636
  console.log("");
1554
1637
  await shutdownAnalytics();
1555
1638
  return;
@@ -1567,25 +1650,25 @@ async function initCommand(options = {}) {
1567
1650
  error: message
1568
1651
  });
1569
1652
  await shutdownAnalytics();
1570
- console.error(pc6.red(`
1653
+ console.error(pc7.red(`
1571
1654
  \u2717 ${message}`));
1572
1655
  process.exit(1);
1573
1656
  }
1574
1657
  }
1575
1658
 
1576
1659
  // src/cmds/pull.ts
1577
- import pc7 from "picocolors";
1578
- import fs5 from "fs";
1579
- import path5 from "path";
1580
- import prompts5 from "prompts";
1660
+ import pc8 from "picocolors";
1661
+ import fs6 from "fs";
1662
+ import path6 from "path";
1663
+ import prompts7 from "prompts";
1581
1664
  async function pullCommand(options) {
1582
1665
  try {
1583
1666
  const environment = options.env || "development";
1584
1667
  const envFile = options.file || ".env";
1585
- console.log(pc7.blue("\u{1F510} Pulling secrets from Keyway...\n"));
1586
- console.log(`Environment: ${pc7.cyan(environment)}`);
1668
+ console.log(pc8.blue("\u{1F510} Pulling secrets from Keyway...\n"));
1669
+ console.log(`Environment: ${pc8.cyan(environment)}`);
1587
1670
  const repoFullName = getCurrentRepoFullName();
1588
- console.log(`Repository: ${pc7.cyan(repoFullName)}`);
1671
+ console.log(`Repository: ${pc8.cyan(repoFullName)}`);
1589
1672
  const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
1590
1673
  trackEvent(AnalyticsEvents.CLI_PULL, {
1591
1674
  repoFullName,
@@ -1593,16 +1676,16 @@ async function pullCommand(options) {
1593
1676
  });
1594
1677
  console.log("\nDownloading secrets...");
1595
1678
  const response = await pullSecrets(repoFullName, environment, accessToken);
1596
- const envFilePath = path5.resolve(process.cwd(), envFile);
1597
- if (fs5.existsSync(envFilePath)) {
1679
+ const envFilePath = path6.resolve(process.cwd(), envFile);
1680
+ if (fs6.existsSync(envFilePath)) {
1598
1681
  const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
1599
1682
  if (options.yes) {
1600
- console.log(pc7.yellow(`
1683
+ console.log(pc8.yellow(`
1601
1684
  \u26A0 Overwriting existing file: ${envFile}`));
1602
1685
  } else if (!isInteractive2) {
1603
1686
  throw new Error(`File ${envFile} exists. Re-run with --yes to overwrite or choose a different --file.`);
1604
1687
  } else {
1605
- const { confirm } = await prompts5(
1688
+ const { confirm } = await prompts7(
1606
1689
  {
1607
1690
  type: "confirm",
1608
1691
  name: "confirm",
@@ -1616,21 +1699,21 @@ async function pullCommand(options) {
1616
1699
  }
1617
1700
  );
1618
1701
  if (!confirm) {
1619
- console.log(pc7.yellow("Pull aborted."));
1702
+ console.log(pc8.yellow("Pull aborted."));
1620
1703
  return;
1621
1704
  }
1622
1705
  }
1623
1706
  }
1624
- fs5.writeFileSync(envFilePath, response.content, "utf-8");
1707
+ fs6.writeFileSync(envFilePath, response.content, "utf-8");
1625
1708
  const lines = response.content.split("\n").filter((line) => {
1626
1709
  const trimmed = line.trim();
1627
1710
  return trimmed.length > 0 && !trimmed.startsWith("#");
1628
1711
  });
1629
- console.log(pc7.green(`
1712
+ console.log(pc8.green(`
1630
1713
  \u2713 Secrets downloaded successfully`));
1631
1714
  console.log(`
1632
- File: ${pc7.cyan(envFile)}`);
1633
- console.log(`Variables: ${pc7.cyan(lines.length.toString())}`);
1715
+ File: ${pc8.cyan(envFile)}`);
1716
+ console.log(`Variables: ${pc8.cyan(lines.length.toString())}`);
1634
1717
  await shutdownAnalytics();
1635
1718
  } catch (error) {
1636
1719
  const message = error instanceof APIError ? `API ${error.statusCode}: ${error.message}` : error instanceof Error ? truncateMessage(error.message) : "Unknown error";
@@ -1639,14 +1722,14 @@ File: ${pc7.cyan(envFile)}`);
1639
1722
  error: message
1640
1723
  });
1641
1724
  await shutdownAnalytics();
1642
- console.error(pc7.red(`
1725
+ console.error(pc8.red(`
1643
1726
  \u2717 ${message}`));
1644
1727
  process.exit(1);
1645
1728
  }
1646
1729
  }
1647
1730
 
1648
1731
  // src/cmds/doctor.ts
1649
- import pc8 from "picocolors";
1732
+ import pc9 from "picocolors";
1650
1733
 
1651
1734
  // src/core/doctor.ts
1652
1735
  import { execSync as execSync2 } from "child_process";
@@ -1900,9 +1983,9 @@ async function runAllChecks(options = {}) {
1900
1983
  // src/cmds/doctor.ts
1901
1984
  function formatSummary(results) {
1902
1985
  const parts = [
1903
- pc8.green(`${results.summary.pass} passed`),
1904
- results.summary.warn > 0 ? pc8.yellow(`${results.summary.warn} warnings`) : null,
1905
- results.summary.fail > 0 ? pc8.red(`${results.summary.fail} failed`) : null
1986
+ pc9.green(`${results.summary.pass} passed`),
1987
+ results.summary.warn > 0 ? pc9.yellow(`${results.summary.warn} warnings`) : null,
1988
+ results.summary.fail > 0 ? pc9.red(`${results.summary.fail} failed`) : null
1906
1989
  ].filter(Boolean);
1907
1990
  return parts.join(", ");
1908
1991
  }
@@ -1919,20 +2002,20 @@ async function doctorCommand(options = {}) {
1919
2002
  process.stdout.write(JSON.stringify(results, null, 0) + "\n");
1920
2003
  process.exit(results.exitCode);
1921
2004
  }
1922
- console.log(pc8.cyan("\n\u{1F50D} Keyway Doctor - Environment Check\n"));
2005
+ console.log(pc9.cyan("\n\u{1F50D} Keyway Doctor - Environment Check\n"));
1923
2006
  results.checks.forEach((check) => {
1924
- const icon = check.status === "pass" ? pc8.green("\u2713") : check.status === "warn" ? pc8.yellow("!") : pc8.red("\u2717");
1925
- const detail = check.detail ? pc8.dim(` \u2014 ${check.detail}`) : "";
2007
+ const icon = check.status === "pass" ? pc9.green("\u2713") : check.status === "warn" ? pc9.yellow("!") : pc9.red("\u2717");
2008
+ const detail = check.detail ? pc9.dim(` \u2014 ${check.detail}`) : "";
1926
2009
  console.log(` ${icon} ${check.name}${detail}`);
1927
2010
  });
1928
2011
  console.log(`
1929
2012
  Summary: ${formatSummary(results)}`);
1930
2013
  if (results.summary.fail > 0) {
1931
- console.log(pc8.red("\u26A0 Some checks failed. Please resolve the issues above before using Keyway."));
2014
+ console.log(pc9.red("\u26A0 Some checks failed. Please resolve the issues above before using Keyway."));
1932
2015
  } else if (results.summary.warn > 0) {
1933
- console.log(pc8.yellow("\u26A0 Some warnings detected. Keyway should work but consider addressing them."));
2016
+ console.log(pc9.yellow("\u26A0 Some warnings detected. Keyway should work but consider addressing them."));
1934
2017
  } else {
1935
- console.log(pc8.green("\u2728 All checks passed! Your environment is ready for Keyway."));
2018
+ console.log(pc9.green("\u2728 All checks passed! Your environment is ready for Keyway."));
1936
2019
  }
1937
2020
  process.exit(results.exitCode);
1938
2021
  } catch (error) {
@@ -1953,7 +2036,7 @@ Summary: ${formatSummary(results)}`);
1953
2036
  };
1954
2037
  process.stdout.write(JSON.stringify(errorResult, null, 0) + "\n");
1955
2038
  } else {
1956
- console.error(pc8.red(`
2039
+ console.error(pc9.red(`
1957
2040
  \u2717 ${message}`));
1958
2041
  }
1959
2042
  process.exit(1);
@@ -1961,8 +2044,8 @@ Summary: ${formatSummary(results)}`);
1961
2044
  }
1962
2045
 
1963
2046
  // src/cmds/connect.ts
1964
- import pc9 from "picocolors";
1965
- import prompts6 from "prompts";
2047
+ import pc10 from "picocolors";
2048
+ import prompts8 from "prompts";
1966
2049
  var TOKEN_AUTH_PROVIDERS = ["railway"];
1967
2050
  function getTokenCreationUrl(provider) {
1968
2051
  switch (provider) {
@@ -1975,37 +2058,37 @@ function getTokenCreationUrl(provider) {
1975
2058
  async function connectWithTokenFlow(accessToken, provider, displayName) {
1976
2059
  const tokenUrl = getTokenCreationUrl(provider);
1977
2060
  if (provider === "railway") {
1978
- console.log(pc9.yellow("\nTip: Select the workspace containing your projects."));
1979
- console.log(pc9.yellow(` Do NOT use "No workspace" - it won't have access to your projects.`));
2061
+ console.log(pc10.yellow("\nTip: Select the workspace containing your projects."));
2062
+ console.log(pc10.yellow(` Do NOT use "No workspace" - it won't have access to your projects.`));
1980
2063
  }
1981
2064
  await openUrl(tokenUrl);
1982
- const { token } = await prompts6({
2065
+ const { token } = await prompts8({
1983
2066
  type: "password",
1984
2067
  name: "token",
1985
2068
  message: `${displayName} API Token:`
1986
2069
  });
1987
2070
  if (!token) {
1988
- console.log(pc9.gray("Cancelled."));
2071
+ console.log(pc10.gray("Cancelled."));
1989
2072
  return false;
1990
2073
  }
1991
- console.log(pc9.gray("\nValidating token..."));
2074
+ console.log(pc10.gray("\nValidating token..."));
1992
2075
  try {
1993
2076
  const result = await connectWithToken(accessToken, provider, token);
1994
2077
  if (result.success) {
1995
- console.log(pc9.green(`
2078
+ console.log(pc10.green(`
1996
2079
  \u2713 Connected to ${displayName}!`));
1997
- console.log(pc9.gray(` Account: ${result.user.username}`));
2080
+ console.log(pc10.gray(` Account: ${result.user.username}`));
1998
2081
  if (result.user.teamName) {
1999
- console.log(pc9.gray(` Team: ${result.user.teamName}`));
2082
+ console.log(pc10.gray(` Team: ${result.user.teamName}`));
2000
2083
  }
2001
2084
  return true;
2002
2085
  } else {
2003
- console.log(pc9.red("\n\u2717 Connection failed."));
2086
+ console.log(pc10.red("\n\u2717 Connection failed."));
2004
2087
  return false;
2005
2088
  }
2006
2089
  } catch (error) {
2007
2090
  const message = error instanceof Error ? error.message : "Token validation failed";
2008
- console.log(pc9.red(`
2091
+ console.log(pc10.red(`
2009
2092
  \u2717 ${message}`));
2010
2093
  return false;
2011
2094
  }
@@ -2014,7 +2097,7 @@ async function connectWithOAuthFlow(accessToken, provider, displayName) {
2014
2097
  const authUrl = getProviderAuthUrl(provider, accessToken);
2015
2098
  const startTime = /* @__PURE__ */ new Date();
2016
2099
  await openUrl(authUrl);
2017
- console.log(pc9.gray("Waiting for authorization..."));
2100
+ console.log(pc10.gray("Waiting for authorization..."));
2018
2101
  const maxAttempts = 60;
2019
2102
  let attempts = 0;
2020
2103
  while (attempts < maxAttempts) {
@@ -2026,15 +2109,15 @@ async function connectWithOAuthFlow(accessToken, provider, displayName) {
2026
2109
  (c) => c.provider === provider && new Date(c.createdAt) > startTime
2027
2110
  );
2028
2111
  if (newConn) {
2029
- console.log(pc9.green(`
2112
+ console.log(pc10.green(`
2030
2113
  \u2713 Connected to ${displayName}!`));
2031
2114
  return true;
2032
2115
  }
2033
2116
  } catch {
2034
2117
  }
2035
2118
  }
2036
- console.log(pc9.red("\n\u2717 Authorization timeout."));
2037
- console.log(pc9.gray("Run `keyway connections` to check if the connection was established."));
2119
+ console.log(pc10.red("\n\u2717 Authorization timeout."));
2120
+ console.log(pc10.gray("Run `keyway connections` to check if the connection was established."));
2038
2121
  return false;
2039
2122
  }
2040
2123
  async function connectCommand(provider, options = {}) {
@@ -2044,30 +2127,30 @@ async function connectCommand(provider, options = {}) {
2044
2127
  const providerInfo = providers.find((p) => p.name === provider.toLowerCase());
2045
2128
  if (!providerInfo) {
2046
2129
  const available = providers.map((p) => p.name).join(", ");
2047
- console.error(pc9.red(`Unknown provider: ${provider}`));
2048
- console.log(pc9.gray(`Available providers: ${available || "none"}`));
2130
+ console.error(pc10.red(`Unknown provider: ${provider}`));
2131
+ console.log(pc10.gray(`Available providers: ${available || "none"}`));
2049
2132
  process.exit(1);
2050
2133
  }
2051
2134
  if (!providerInfo.configured) {
2052
- console.error(pc9.red(`Provider ${providerInfo.displayName} is not configured on the server.`));
2053
- console.log(pc9.gray("Contact your administrator to enable this integration."));
2135
+ console.error(pc10.red(`Provider ${providerInfo.displayName} is not configured on the server.`));
2136
+ console.log(pc10.gray("Contact your administrator to enable this integration."));
2054
2137
  process.exit(1);
2055
2138
  }
2056
2139
  const { connections } = await getConnections(accessToken);
2057
2140
  const existingConnection = connections.find((c) => c.provider === provider.toLowerCase());
2058
2141
  if (existingConnection) {
2059
- const { reconnect } = await prompts6({
2142
+ const { reconnect } = await prompts8({
2060
2143
  type: "confirm",
2061
2144
  name: "reconnect",
2062
2145
  message: `You're already connected to ${providerInfo.displayName}. Reconnect?`,
2063
2146
  initial: false
2064
2147
  });
2065
2148
  if (!reconnect) {
2066
- console.log(pc9.gray("Keeping existing connection."));
2149
+ console.log(pc10.gray("Keeping existing connection."));
2067
2150
  return;
2068
2151
  }
2069
2152
  }
2070
- console.log(pc9.blue(`
2153
+ console.log(pc10.blue(`
2071
2154
  Connecting to ${providerInfo.displayName}...
2072
2155
  `));
2073
2156
  let connected = false;
@@ -2086,7 +2169,7 @@ Connecting to ${providerInfo.displayName}...
2086
2169
  command: "connect",
2087
2170
  error: truncateMessage(message)
2088
2171
  });
2089
- console.error(pc9.red(`
2172
+ console.error(pc10.red(`
2090
2173
  \u2717 ${message}`));
2091
2174
  process.exit(1);
2092
2175
  }
@@ -2096,24 +2179,24 @@ async function connectionsCommand(options = {}) {
2096
2179
  const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
2097
2180
  const { connections } = await getConnections(accessToken);
2098
2181
  if (connections.length === 0) {
2099
- console.log(pc9.gray("No provider connections found."));
2100
- console.log(pc9.gray("\nConnect to a provider with: keyway connect <provider>"));
2101
- console.log(pc9.gray("Available providers: vercel, railway"));
2182
+ console.log(pc10.gray("No provider connections found."));
2183
+ console.log(pc10.gray("\nConnect to a provider with: keyway connect <provider>"));
2184
+ console.log(pc10.gray("Available providers: vercel, railway"));
2102
2185
  return;
2103
2186
  }
2104
- console.log(pc9.blue("\n\u{1F4E1} Provider Connections\n"));
2187
+ console.log(pc10.blue("\n\u{1F4E1} Provider Connections\n"));
2105
2188
  for (const conn of connections) {
2106
2189
  const providerName = conn.provider.charAt(0).toUpperCase() + conn.provider.slice(1);
2107
- const teamInfo = conn.providerTeamId ? pc9.gray(` (Team: ${conn.providerTeamId})`) : "";
2190
+ const teamInfo = conn.providerTeamId ? pc10.gray(` (Team: ${conn.providerTeamId})`) : "";
2108
2191
  const date = new Date(conn.createdAt).toLocaleDateString();
2109
- console.log(` ${pc9.green("\u25CF")} ${pc9.bold(providerName)}${teamInfo}`);
2110
- console.log(pc9.gray(` Connected: ${date}`));
2111
- console.log(pc9.gray(` ID: ${conn.id}`));
2192
+ console.log(` ${pc10.green("\u25CF")} ${pc10.bold(providerName)}${teamInfo}`);
2193
+ console.log(pc10.gray(` Connected: ${date}`));
2194
+ console.log(pc10.gray(` ID: ${conn.id}`));
2112
2195
  console.log("");
2113
2196
  }
2114
2197
  } catch (error) {
2115
2198
  const message = error instanceof Error ? error.message : "Failed to list connections";
2116
- console.error(pc9.red(`
2199
+ console.error(pc10.red(`
2117
2200
  \u2717 ${message}`));
2118
2201
  process.exit(1);
2119
2202
  }
@@ -2124,22 +2207,22 @@ async function disconnectCommand(provider, options = {}) {
2124
2207
  const { connections } = await getConnections(accessToken);
2125
2208
  const connection = connections.find((c) => c.provider === provider.toLowerCase());
2126
2209
  if (!connection) {
2127
- console.log(pc9.gray(`No connection found for provider: ${provider}`));
2210
+ console.log(pc10.gray(`No connection found for provider: ${provider}`));
2128
2211
  return;
2129
2212
  }
2130
2213
  const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
2131
- const { confirm } = await prompts6({
2214
+ const { confirm } = await prompts8({
2132
2215
  type: "confirm",
2133
2216
  name: "confirm",
2134
2217
  message: `Disconnect from ${providerName}?`,
2135
2218
  initial: false
2136
2219
  });
2137
2220
  if (!confirm) {
2138
- console.log(pc9.gray("Cancelled."));
2221
+ console.log(pc10.gray("Cancelled."));
2139
2222
  return;
2140
2223
  }
2141
2224
  await deleteConnection(accessToken, connection.id);
2142
- console.log(pc9.green(`
2225
+ console.log(pc10.green(`
2143
2226
  \u2713 Disconnected from ${providerName}`));
2144
2227
  trackEvent(AnalyticsEvents.CLI_DISCONNECT, {
2145
2228
  provider: provider.toLowerCase()
@@ -2150,15 +2233,15 @@ async function disconnectCommand(provider, options = {}) {
2150
2233
  command: "disconnect",
2151
2234
  error: truncateMessage(message)
2152
2235
  });
2153
- console.error(pc9.red(`
2236
+ console.error(pc10.red(`
2154
2237
  \u2717 ${message}`));
2155
2238
  process.exit(1);
2156
2239
  }
2157
2240
  }
2158
2241
 
2159
2242
  // src/cmds/sync.ts
2160
- import pc10 from "picocolors";
2161
- import prompts7 from "prompts";
2243
+ import pc11 from "picocolors";
2244
+ import prompts9 from "prompts";
2162
2245
  function mapToVercelEnvironment(keywayEnv) {
2163
2246
  const mapping = {
2164
2247
  production: "production",
@@ -2202,36 +2285,36 @@ function mapToProviderEnvironment(provider, keywayEnv) {
2202
2285
  function displayDiffSummary(diff, providerName) {
2203
2286
  const totalDiff = diff.onlyInKeyway.length + diff.onlyInProvider.length + diff.different.length;
2204
2287
  if (totalDiff === 0 && diff.same.length > 0) {
2205
- console.log(pc10.green(`
2288
+ console.log(pc11.green(`
2206
2289
  \u2713 Already in sync (${diff.same.length} secrets)`));
2207
2290
  return;
2208
2291
  }
2209
- console.log(pc10.blue("\n\u{1F4CA} Comparison Summary\n"));
2210
- console.log(pc10.gray(` Keyway: ${diff.keywayCount} secrets | ${providerName}: ${diff.providerCount} secrets
2292
+ console.log(pc11.blue("\n\u{1F4CA} Comparison Summary\n"));
2293
+ console.log(pc11.gray(` Keyway: ${diff.keywayCount} secrets | ${providerName}: ${diff.providerCount} secrets
2211
2294
  `));
2212
2295
  if (diff.onlyInKeyway.length > 0) {
2213
- console.log(pc10.cyan(` \u2192 ${diff.onlyInKeyway.length} only in Keyway`));
2214
- diff.onlyInKeyway.slice(0, 3).forEach((key) => console.log(pc10.gray(` ${key}`)));
2296
+ console.log(pc11.cyan(` \u2192 ${diff.onlyInKeyway.length} only in Keyway`));
2297
+ diff.onlyInKeyway.slice(0, 3).forEach((key) => console.log(pc11.gray(` ${key}`)));
2215
2298
  if (diff.onlyInKeyway.length > 3) {
2216
- console.log(pc10.gray(` ... and ${diff.onlyInKeyway.length - 3} more`));
2299
+ console.log(pc11.gray(` ... and ${diff.onlyInKeyway.length - 3} more`));
2217
2300
  }
2218
2301
  }
2219
2302
  if (diff.onlyInProvider.length > 0) {
2220
- console.log(pc10.magenta(` \u2190 ${diff.onlyInProvider.length} only in ${providerName}`));
2221
- diff.onlyInProvider.slice(0, 3).forEach((key) => console.log(pc10.gray(` ${key}`)));
2303
+ console.log(pc11.magenta(` \u2190 ${diff.onlyInProvider.length} only in ${providerName}`));
2304
+ diff.onlyInProvider.slice(0, 3).forEach((key) => console.log(pc11.gray(` ${key}`)));
2222
2305
  if (diff.onlyInProvider.length > 3) {
2223
- console.log(pc10.gray(` ... and ${diff.onlyInProvider.length - 3} more`));
2306
+ console.log(pc11.gray(` ... and ${diff.onlyInProvider.length - 3} more`));
2224
2307
  }
2225
2308
  }
2226
2309
  if (diff.different.length > 0) {
2227
- console.log(pc10.yellow(` \u2260 ${diff.different.length} with different values`));
2228
- diff.different.slice(0, 3).forEach((key) => console.log(pc10.gray(` ${key}`)));
2310
+ console.log(pc11.yellow(` \u2260 ${diff.different.length} with different values`));
2311
+ diff.different.slice(0, 3).forEach((key) => console.log(pc11.gray(` ${key}`)));
2229
2312
  if (diff.different.length > 3) {
2230
- console.log(pc10.gray(` ... and ${diff.different.length - 3} more`));
2313
+ console.log(pc11.gray(` ... and ${diff.different.length - 3} more`));
2231
2314
  }
2232
2315
  }
2233
2316
  if (diff.same.length > 0) {
2234
- console.log(pc10.gray(` = ${diff.same.length} identical`));
2317
+ console.log(pc11.gray(` = ${diff.same.length} identical`));
2235
2318
  }
2236
2319
  console.log("");
2237
2320
  }
@@ -2278,25 +2361,25 @@ async function promptProjectSelection(projects, repoFullName) {
2278
2361
  let title = displayName;
2279
2362
  const badges = [];
2280
2363
  if (p.linkedRepo?.toLowerCase() === repoFullName.toLowerCase()) {
2281
- badges.push(pc10.green("\u2190 linked"));
2364
+ badges.push(pc11.green("\u2190 linked"));
2282
2365
  } else if (p.name.toLowerCase() === repoName || p.serviceName?.toLowerCase() === repoName) {
2283
- badges.push(pc10.green("\u2190 same name"));
2366
+ badges.push(pc11.green("\u2190 same name"));
2284
2367
  } else if (p.linkedRepo) {
2285
- badges.push(pc10.gray(`\u2192 ${p.linkedRepo}`));
2368
+ badges.push(pc11.gray(`\u2192 ${p.linkedRepo}`));
2286
2369
  }
2287
2370
  if (badges.length > 0) {
2288
2371
  title = `${displayName} ${badges.join(" ")}`;
2289
2372
  }
2290
2373
  return { title, value: p.id };
2291
2374
  });
2292
- const { projectChoice } = await prompts7({
2375
+ const { projectChoice } = await prompts9({
2293
2376
  type: "select",
2294
2377
  name: "projectChoice",
2295
2378
  message: "Select a project:",
2296
2379
  choices
2297
2380
  });
2298
2381
  if (!projectChoice) {
2299
- console.log(pc10.gray("Cancelled."));
2382
+ console.log(pc11.gray("Cancelled."));
2300
2383
  process.exit(0);
2301
2384
  }
2302
2385
  return projects.find((p) => p.id === projectChoice);
@@ -2304,40 +2387,40 @@ async function promptProjectSelection(projects, repoFullName) {
2304
2387
  async function syncCommand(provider, options = {}) {
2305
2388
  try {
2306
2389
  if (options.pull && options.allowDelete) {
2307
- console.error(pc10.red("Error: --allow-delete cannot be used with --pull"));
2308
- console.log(pc10.gray("The --allow-delete flag is only for push operations."));
2390
+ console.error(pc11.red("Error: --allow-delete cannot be used with --pull"));
2391
+ console.log(pc11.gray("The --allow-delete flag is only for push operations."));
2309
2392
  process.exit(1);
2310
2393
  }
2311
2394
  const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
2312
2395
  const repoFullName = detectGitRepo();
2313
2396
  if (!repoFullName) {
2314
- console.error(pc10.red("Could not detect Git repository."));
2315
- console.log(pc10.gray("Run this command from a Git repository directory."));
2397
+ console.error(pc11.red("Could not detect Git repository."));
2398
+ console.log(pc11.gray("Run this command from a Git repository directory."));
2316
2399
  process.exit(1);
2317
2400
  }
2318
- console.log(pc10.gray(`Repository: ${repoFullName}`));
2401
+ console.log(pc11.gray(`Repository: ${repoFullName}`));
2319
2402
  const vaultExists = await checkVaultExists(accessToken, repoFullName);
2320
2403
  if (!vaultExists) {
2321
- console.log(pc10.yellow(`
2404
+ console.log(pc11.yellow(`
2322
2405
  No vault found for ${repoFullName}.`));
2323
- const { shouldCreate } = await prompts7({
2406
+ const { shouldCreate } = await prompts9({
2324
2407
  type: "confirm",
2325
2408
  name: "shouldCreate",
2326
2409
  message: "Create vault now?",
2327
2410
  initial: true
2328
2411
  });
2329
2412
  if (!shouldCreate) {
2330
- console.log(pc10.gray("Cancelled. Run `keyway init` to create a vault first."));
2413
+ console.log(pc11.gray("Cancelled. Run `keyway init` to create a vault first."));
2331
2414
  process.exit(0);
2332
2415
  }
2333
- console.log(pc10.gray("\nCreating vault..."));
2416
+ console.log(pc11.gray("\nCreating vault..."));
2334
2417
  try {
2335
2418
  await initVault(repoFullName, accessToken);
2336
- console.log(pc10.green(`\u2713 Vault created for ${repoFullName}
2419
+ console.log(pc11.green(`\u2713 Vault created for ${repoFullName}
2337
2420
  `));
2338
2421
  } catch (error) {
2339
2422
  const message = error instanceof Error ? error.message : "Failed to create vault";
2340
- console.error(pc10.red(`
2423
+ console.error(pc11.red(`
2341
2424
  \u2717 ${message}`));
2342
2425
  process.exit(1);
2343
2426
  }
@@ -2346,16 +2429,16 @@ No vault found for ${repoFullName}.`));
2346
2429
  let connection = connections.find((c) => c.provider === provider.toLowerCase());
2347
2430
  if (!connection) {
2348
2431
  const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1);
2349
- console.log(pc10.yellow(`
2432
+ console.log(pc11.yellow(`
2350
2433
  Not connected to ${providerDisplayName}.`));
2351
- const { shouldConnect } = await prompts7({
2434
+ const { shouldConnect } = await prompts9({
2352
2435
  type: "confirm",
2353
2436
  name: "shouldConnect",
2354
2437
  message: `Connect to ${providerDisplayName} now?`,
2355
2438
  initial: true
2356
2439
  });
2357
2440
  if (!shouldConnect) {
2358
- console.log(pc10.gray("Cancelled."));
2441
+ console.log(pc11.gray("Cancelled."));
2359
2442
  process.exit(0);
2360
2443
  }
2361
2444
  await connectCommand(provider, { loginPrompt: false });
@@ -2363,7 +2446,7 @@ Not connected to ${providerDisplayName}.`));
2363
2446
  connections = refreshed.connections;
2364
2447
  connection = connections.find((c) => c.provider === provider.toLowerCase());
2365
2448
  if (!connection) {
2366
- console.error(pc10.red(`
2449
+ console.error(pc11.red(`
2367
2450
  Connection to ${providerDisplayName} failed.`));
2368
2451
  process.exit(1);
2369
2452
  }
@@ -2371,7 +2454,7 @@ Connection to ${providerDisplayName} failed.`));
2371
2454
  }
2372
2455
  const { projects } = await getConnectionProjects(accessToken, connection.id);
2373
2456
  if (projects.length === 0) {
2374
- console.error(pc10.red(`No projects found in your ${provider} account.`));
2457
+ console.error(pc11.red(`No projects found in your ${provider} account.`));
2375
2458
  process.exit(1);
2376
2459
  }
2377
2460
  let selectedProject;
@@ -2380,21 +2463,21 @@ Connection to ${providerDisplayName} failed.`));
2380
2463
  (p) => p.id === options.project || p.name.toLowerCase() === options.project?.toLowerCase() || p.serviceName?.toLowerCase() === options.project?.toLowerCase()
2381
2464
  );
2382
2465
  if (!found) {
2383
- console.error(pc10.red(`Project not found: ${options.project}`));
2384
- console.log(pc10.gray("Available projects:"));
2385
- projects.forEach((p) => console.log(pc10.gray(` - ${getProjectDisplayName(p)}`)));
2466
+ console.error(pc11.red(`Project not found: ${options.project}`));
2467
+ console.log(pc11.gray("Available projects:"));
2468
+ projects.forEach((p) => console.log(pc11.gray(` - ${getProjectDisplayName(p)}`)));
2386
2469
  process.exit(1);
2387
2470
  }
2388
2471
  selectedProject = found;
2389
2472
  if (!projectMatchesRepo(selectedProject, repoFullName)) {
2390
2473
  console.log("");
2391
- console.log(pc10.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2392
- console.log(pc10.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2393
- console.log(pc10.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2394
- console.log(pc10.yellow(` Current repo: ${repoFullName}`));
2395
- console.log(pc10.yellow(` Selected project: ${getProjectDisplayName(selectedProject)}`));
2474
+ console.log(pc11.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2475
+ console.log(pc11.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2476
+ console.log(pc11.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2477
+ console.log(pc11.yellow(` Current repo: ${repoFullName}`));
2478
+ console.log(pc11.yellow(` Selected project: ${getProjectDisplayName(selectedProject)}`));
2396
2479
  if (selectedProject.linkedRepo) {
2397
- console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2480
+ console.log(pc11.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2398
2481
  }
2399
2482
  console.log("");
2400
2483
  }
@@ -2403,11 +2486,11 @@ Connection to ${providerDisplayName} failed.`));
2403
2486
  if (autoMatch && (autoMatch.matchType === "linked_repo" || autoMatch.matchType === "exact_name")) {
2404
2487
  selectedProject = autoMatch.project;
2405
2488
  const matchReason = autoMatch.matchType === "linked_repo" ? `linked to ${repoFullName}` : "exact name match";
2406
- console.log(pc10.green(`\u2713 Auto-selected project: ${getProjectDisplayName(selectedProject)} (${matchReason})`));
2489
+ console.log(pc11.green(`\u2713 Auto-selected project: ${getProjectDisplayName(selectedProject)} (${matchReason})`));
2407
2490
  } else if (autoMatch && autoMatch.matchType === "partial_name") {
2408
2491
  const partialDisplayName = getProjectDisplayName(autoMatch.project);
2409
- console.log(pc10.yellow(`Detected project: ${partialDisplayName} (partial match)`));
2410
- const { useDetected } = await prompts7({
2492
+ console.log(pc11.yellow(`Detected project: ${partialDisplayName} (partial match)`));
2493
+ const { useDetected } = await prompts9({
2411
2494
  type: "confirm",
2412
2495
  name: "useDetected",
2413
2496
  message: `Use ${partialDisplayName}?`,
@@ -2422,30 +2505,30 @@ Connection to ${providerDisplayName} failed.`));
2422
2505
  selectedProject = projects[0];
2423
2506
  if (!projectMatchesRepo(selectedProject, repoFullName)) {
2424
2507
  console.log("");
2425
- console.log(pc10.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2426
- console.log(pc10.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2427
- console.log(pc10.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2428
- console.log(pc10.yellow(` Current repo: ${repoFullName}`));
2429
- console.log(pc10.yellow(` Only project: ${getProjectDisplayName(selectedProject)}`));
2508
+ console.log(pc11.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2509
+ console.log(pc11.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2510
+ console.log(pc11.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2511
+ console.log(pc11.yellow(` Current repo: ${repoFullName}`));
2512
+ console.log(pc11.yellow(` Only project: ${getProjectDisplayName(selectedProject)}`));
2430
2513
  if (selectedProject.linkedRepo) {
2431
- console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2514
+ console.log(pc11.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2432
2515
  }
2433
2516
  console.log("");
2434
- const { continueAnyway } = await prompts7({
2517
+ const { continueAnyway } = await prompts9({
2435
2518
  type: "confirm",
2436
2519
  name: "continueAnyway",
2437
2520
  message: "Continue anyway?",
2438
2521
  initial: false
2439
2522
  });
2440
2523
  if (!continueAnyway) {
2441
- console.log(pc10.gray("Cancelled."));
2524
+ console.log(pc11.gray("Cancelled."));
2442
2525
  process.exit(0);
2443
2526
  }
2444
2527
  }
2445
2528
  } else {
2446
- console.log(pc10.yellow(`
2529
+ console.log(pc11.yellow(`
2447
2530
  \u26A0\uFE0F No matching project found for ${repoFullName}`));
2448
- console.log(pc10.gray("Select a project manually:\n"));
2531
+ console.log(pc11.gray("Select a project manually:\n"));
2449
2532
  selectedProject = await promptProjectSelection(projects, repoFullName);
2450
2533
  }
2451
2534
  }
@@ -2453,23 +2536,23 @@ Connection to ${providerDisplayName} failed.`));
2453
2536
  const autoMatch = findMatchingProject(projects, repoFullName);
2454
2537
  if (autoMatch && autoMatch.project.id !== selectedProject.id) {
2455
2538
  console.log("");
2456
- console.log(pc10.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2457
- console.log(pc10.yellow("\u2502 \u26A0\uFE0F WARNING: You selected a different project \u2502"));
2458
- console.log(pc10.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2459
- console.log(pc10.yellow(` Current repo: ${repoFullName}`));
2460
- console.log(pc10.yellow(` Selected project: ${getProjectDisplayName(selectedProject)}`));
2539
+ console.log(pc11.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2540
+ console.log(pc11.yellow("\u2502 \u26A0\uFE0F WARNING: You selected a different project \u2502"));
2541
+ console.log(pc11.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2542
+ console.log(pc11.yellow(` Current repo: ${repoFullName}`));
2543
+ console.log(pc11.yellow(` Selected project: ${getProjectDisplayName(selectedProject)}`));
2461
2544
  if (selectedProject.linkedRepo) {
2462
- console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2545
+ console.log(pc11.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2463
2546
  }
2464
2547
  console.log("");
2465
- const { continueAnyway } = await prompts7({
2548
+ const { continueAnyway } = await prompts9({
2466
2549
  type: "confirm",
2467
2550
  name: "continueAnyway",
2468
2551
  message: "Are you sure you want to sync with this project?",
2469
2552
  initial: false
2470
2553
  });
2471
2554
  if (!continueAnyway) {
2472
- console.log(pc10.gray("Cancelled."));
2555
+ console.log(pc11.gray("Cancelled."));
2473
2556
  process.exit(0);
2474
2557
  }
2475
2558
  }
@@ -2483,7 +2566,7 @@ Connection to ${providerDisplayName} failed.`));
2483
2566
  if (needsEnvPrompt || needsDirectionPrompt) {
2484
2567
  if (needsEnvPrompt) {
2485
2568
  const vaultEnvs = await getVaultEnvironments(accessToken, repoFullName);
2486
- const { selectedEnv } = await prompts7({
2569
+ const { selectedEnv } = await prompts9({
2487
2570
  type: "select",
2488
2571
  name: "selectedEnv",
2489
2572
  message: "Keyway environment:",
@@ -2491,7 +2574,7 @@ Connection to ${providerDisplayName} failed.`));
2491
2574
  initial: Math.max(0, vaultEnvs.indexOf("production"))
2492
2575
  });
2493
2576
  if (!selectedEnv) {
2494
- console.log(pc10.gray("Cancelled."));
2577
+ console.log(pc11.gray("Cancelled."));
2495
2578
  process.exit(0);
2496
2579
  }
2497
2580
  keywayEnv = selectedEnv;
@@ -2505,9 +2588,9 @@ Connection to ${providerDisplayName} failed.`));
2505
2588
  providerEnv = mappedEnv;
2506
2589
  } else if (selectedProject.environments.length === 1) {
2507
2590
  providerEnv = selectedProject.environments[0];
2508
- console.log(pc10.gray(`Using ${providerName} environment: ${providerEnv}`));
2591
+ console.log(pc11.gray(`Using ${providerName} environment: ${providerEnv}`));
2509
2592
  } else {
2510
- const { selectedProviderEnv } = await prompts7({
2593
+ const { selectedProviderEnv } = await prompts9({
2511
2594
  type: "select",
2512
2595
  name: "selectedProviderEnv",
2513
2596
  message: `${providerName} environment:`,
@@ -2517,7 +2600,7 @@ Connection to ${providerDisplayName} failed.`));
2517
2600
  ))
2518
2601
  });
2519
2602
  if (!selectedProviderEnv) {
2520
- console.log(pc10.gray("Cancelled."));
2603
+ console.log(pc11.gray("Cancelled."));
2521
2604
  process.exit(0);
2522
2605
  }
2523
2606
  providerEnv = selectedProviderEnv;
@@ -2531,7 +2614,7 @@ Connection to ${providerDisplayName} failed.`));
2531
2614
  if (needsDirectionPrompt) {
2532
2615
  const effectiveKeywayEnv = keywayEnv || "production";
2533
2616
  const effectiveProviderEnv = providerEnv || mapToProviderEnvironment(provider, effectiveKeywayEnv);
2534
- console.log(pc10.gray("\nComparing secrets..."));
2617
+ console.log(pc11.gray("\nComparing secrets..."));
2535
2618
  diff = await getSyncDiff(accessToken, repoFullName, {
2536
2619
  connectionId: connection.id,
2537
2620
  projectId: selectedProject.id,
@@ -2553,7 +2636,7 @@ Connection to ${providerDisplayName} failed.`));
2553
2636
  } else if (diff.providerCount === 0 && diff.keywayCount > 0) {
2554
2637
  defaultDirection = 0;
2555
2638
  }
2556
- const { selectedDirection } = await prompts7({
2639
+ const { selectedDirection } = await prompts9({
2557
2640
  type: "select",
2558
2641
  name: "selectedDirection",
2559
2642
  message: "Sync direction:",
@@ -2564,7 +2647,7 @@ Connection to ${providerDisplayName} failed.`));
2564
2647
  initial: defaultDirection
2565
2648
  });
2566
2649
  if (!selectedDirection) {
2567
- console.log(pc10.gray("Cancelled."));
2650
+ console.log(pc11.gray("Cancelled."));
2568
2651
  process.exit(0);
2569
2652
  }
2570
2653
  direction = selectedDirection;
@@ -2581,10 +2664,10 @@ Connection to ${providerDisplayName} failed.`));
2581
2664
  keywayEnv
2582
2665
  );
2583
2666
  if (status.isFirstSync && direction === "push" && status.vaultIsEmpty && status.providerHasSecrets) {
2584
- console.log(pc10.yellow(`
2667
+ console.log(pc11.yellow(`
2585
2668
  \u26A0\uFE0F Your Keyway vault is empty for "${keywayEnv}", but ${providerName} has ${status.providerSecretCount} secrets.`));
2586
- console.log(pc10.gray(` (Use --environment to sync a different environment)`));
2587
- const { importFirst } = await prompts7({
2669
+ console.log(pc11.gray(` (Use --environment to sync a different environment)`));
2670
+ const { importFirst } = await prompts9({
2588
2671
  type: "confirm",
2589
2672
  name: "importFirst",
2590
2673
  message: `Import secrets from ${providerName} first?`,
@@ -2625,7 +2708,7 @@ Connection to ${providerDisplayName} failed.`));
2625
2708
  command: "sync",
2626
2709
  error: truncateMessage(message)
2627
2710
  });
2628
- console.error(pc10.red(`
2711
+ console.error(pc11.red(`
2629
2712
  \u2717 ${message}`));
2630
2713
  process.exit(1);
2631
2714
  }
@@ -2644,49 +2727,49 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2644
2727
  });
2645
2728
  const totalChanges = preview.toCreate.length + preview.toUpdate.length + preview.toDelete.length;
2646
2729
  if (totalChanges === 0) {
2647
- console.log(pc10.green("\n\u2713 Already in sync. No changes needed."));
2730
+ console.log(pc11.green("\n\u2713 Already in sync. No changes needed."));
2648
2731
  return;
2649
2732
  }
2650
- console.log(pc10.blue("\n\u{1F4CB} Sync Preview\n"));
2733
+ console.log(pc11.blue("\n\u{1F4CB} Sync Preview\n"));
2651
2734
  if (preview.toCreate.length > 0) {
2652
- console.log(pc10.green(` + ${preview.toCreate.length} to create`));
2653
- preview.toCreate.slice(0, 5).forEach((key) => console.log(pc10.gray(` ${key}`)));
2735
+ console.log(pc11.green(` + ${preview.toCreate.length} to create`));
2736
+ preview.toCreate.slice(0, 5).forEach((key) => console.log(pc11.gray(` ${key}`)));
2654
2737
  if (preview.toCreate.length > 5) {
2655
- console.log(pc10.gray(` ... and ${preview.toCreate.length - 5} more`));
2738
+ console.log(pc11.gray(` ... and ${preview.toCreate.length - 5} more`));
2656
2739
  }
2657
2740
  }
2658
2741
  if (preview.toUpdate.length > 0) {
2659
- console.log(pc10.yellow(` ~ ${preview.toUpdate.length} to update`));
2660
- preview.toUpdate.slice(0, 5).forEach((key) => console.log(pc10.gray(` ${key}`)));
2742
+ console.log(pc11.yellow(` ~ ${preview.toUpdate.length} to update`));
2743
+ preview.toUpdate.slice(0, 5).forEach((key) => console.log(pc11.gray(` ${key}`)));
2661
2744
  if (preview.toUpdate.length > 5) {
2662
- console.log(pc10.gray(` ... and ${preview.toUpdate.length - 5} more`));
2745
+ console.log(pc11.gray(` ... and ${preview.toUpdate.length - 5} more`));
2663
2746
  }
2664
2747
  }
2665
2748
  if (preview.toDelete.length > 0) {
2666
- console.log(pc10.red(` - ${preview.toDelete.length} to delete`));
2667
- preview.toDelete.slice(0, 5).forEach((key) => console.log(pc10.gray(` ${key}`)));
2749
+ console.log(pc11.red(` - ${preview.toDelete.length} to delete`));
2750
+ preview.toDelete.slice(0, 5).forEach((key) => console.log(pc11.gray(` ${key}`)));
2668
2751
  if (preview.toDelete.length > 5) {
2669
- console.log(pc10.gray(` ... and ${preview.toDelete.length - 5} more`));
2752
+ console.log(pc11.gray(` ... and ${preview.toDelete.length - 5} more`));
2670
2753
  }
2671
2754
  }
2672
2755
  if (preview.toSkip.length > 0) {
2673
- console.log(pc10.gray(` \u25CB ${preview.toSkip.length} unchanged`));
2756
+ console.log(pc11.gray(` \u25CB ${preview.toSkip.length} unchanged`));
2674
2757
  }
2675
2758
  console.log("");
2676
2759
  if (!skipConfirm) {
2677
2760
  const target = direction === "push" ? providerName : "Keyway";
2678
- const { confirm } = await prompts7({
2761
+ const { confirm } = await prompts9({
2679
2762
  type: "confirm",
2680
2763
  name: "confirm",
2681
2764
  message: `Apply ${totalChanges} changes to ${target}?`,
2682
2765
  initial: true
2683
2766
  });
2684
2767
  if (!confirm) {
2685
- console.log(pc10.gray("Cancelled."));
2768
+ console.log(pc11.gray("Cancelled."));
2686
2769
  return;
2687
2770
  }
2688
2771
  }
2689
- console.log(pc10.blue("\n\u23F3 Syncing...\n"));
2772
+ console.log(pc11.blue("\n\u23F3 Syncing...\n"));
2690
2773
  const result = await executeSync(accessToken, repoFullName, {
2691
2774
  connectionId,
2692
2775
  projectId: project.id,
@@ -2698,11 +2781,11 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2698
2781
  allowDelete
2699
2782
  });
2700
2783
  if (result.success) {
2701
- console.log(pc10.green("\u2713 Sync complete"));
2702
- console.log(pc10.gray(` Created: ${result.stats.created}`));
2703
- console.log(pc10.gray(` Updated: ${result.stats.updated}`));
2784
+ console.log(pc11.green("\u2713 Sync complete"));
2785
+ console.log(pc11.gray(` Created: ${result.stats.created}`));
2786
+ console.log(pc11.gray(` Updated: ${result.stats.updated}`));
2704
2787
  if (result.stats.deleted > 0) {
2705
- console.log(pc10.gray(` Deleted: ${result.stats.deleted}`));
2788
+ console.log(pc11.gray(` Deleted: ${result.stats.deleted}`));
2706
2789
  }
2707
2790
  trackEvent(AnalyticsEvents.CLI_SYNC, {
2708
2791
  provider,
@@ -2712,7 +2795,7 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2712
2795
  deleted: result.stats.deleted
2713
2796
  });
2714
2797
  } else {
2715
- console.error(pc10.red(`
2798
+ console.error(pc11.red(`
2716
2799
  \u2717 ${result.error}`));
2717
2800
  process.exit(1);
2718
2801
  }
@@ -2720,20 +2803,19 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2720
2803
 
2721
2804
  // src/cli.ts
2722
2805
  process.on("unhandledRejection", (reason) => {
2723
- console.error(pc11.red("Unhandled error:"), reason);
2806
+ console.error(pc12.red("Unhandled error:"), reason);
2724
2807
  process.exit(1);
2725
2808
  });
2726
2809
  var program = new Command();
2727
2810
  var TAGLINE = "Sync secrets with your team and infra";
2728
2811
  var showBanner = () => {
2729
- const text = pc11.bold(pc11.cyan("Keyway CLI"));
2812
+ const text = pc12.bold(pc12.cyan("Keyway CLI"));
2730
2813
  console.log(`
2731
2814
  ${text}
2732
- ${pc11.gray(TAGLINE)}
2815
+ ${pc12.gray(TAGLINE)}
2733
2816
  `);
2734
2817
  };
2735
2818
  showBanner();
2736
- warnIfEnvNotGitignored();
2737
2819
  program.name("keyway").description(TAGLINE).version(package_default.version);
2738
2820
  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) => {
2739
2821
  await initCommand(options);
@@ -2765,7 +2847,10 @@ program.command("disconnect <provider>").description("Disconnect from a provider
2765
2847
  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) => {
2766
2848
  await syncCommand(provider, options);
2767
2849
  });
2768
- program.parseAsync().catch((error) => {
2769
- console.error(pc11.red("Error:"), error.message || error);
2850
+ (async () => {
2851
+ await warnIfEnvNotGitignored();
2852
+ await program.parseAsync();
2853
+ })().catch((error) => {
2854
+ console.error(pc12.red("Error:"), error.message || error);
2770
2855
  process.exit(1);
2771
2856
  });