xtra-cli 0.1.10 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,7 @@ exports.accessCommand = new commander_1.Command("access")
29
29
  .action(async (options) => {
30
30
  let { project, secret, duration, reason } = options;
31
31
  if (!project) {
32
- project = (0, config_1.getConfigValue)("project");
32
+ project = (0, config_1.getRcConfig)().project;
33
33
  }
34
34
  if (!project) {
35
35
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
@@ -97,7 +97,7 @@ exports.accessCommand = new commander_1.Command("access")
97
97
  if (result.expiresAt) {
98
98
  console.log(chalk_1.default.blue(`Access granted until: ${new Date(result.expiresAt).toLocaleString()}`));
99
99
  }
100
- // Audit log privileged action
100
+ // Audit log — privileged action
101
101
  logAuditSafe(options.decision === "approved" ? "ACCESS_APPROVED" : "ACCESS_REJECTED", null, { requestId, decision: options.decision, expiresAt: result.expiresAt });
102
102
  }
103
103
  catch (error) {
@@ -22,9 +22,8 @@ exports.branchCommand
22
22
  // Access parent options (project ID)
23
23
  const parentOpts = exports.branchCommand.opts();
24
24
  let { project } = parentOpts;
25
- if (!project) {
26
- project = (0, config_1.getConfigValue)("project");
27
- }
25
+ if (!project)
26
+ project = (0, config_1.getRcConfig)().project;
28
27
  if (!project) {
29
28
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
30
29
  process.exit(1);
@@ -78,9 +77,8 @@ exports.branchCommand
78
77
  const parentOpts = exports.branchCommand.opts();
79
78
  let { project } = parentOpts;
80
79
  const { description } = options;
81
- if (!project) {
82
- project = (0, config_1.getConfigValue)("project");
83
- }
80
+ if (!project)
81
+ project = (0, config_1.getRcConfig)().project;
84
82
  if (!project) {
85
83
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
86
84
  process.exit(1);
@@ -110,9 +108,8 @@ exports.branchCommand
110
108
  const parentOpts = exports.branchCommand.opts();
111
109
  let { project } = parentOpts;
112
110
  const { yes } = options;
113
- if (!project) {
114
- project = (0, config_1.getConfigValue)("project");
115
- }
111
+ if (!project)
112
+ project = (0, config_1.getRcConfig)().project;
116
113
  if (!project) {
117
114
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
118
115
  process.exit(1);
@@ -176,9 +173,8 @@ exports.branchCommand
176
173
  const parentOpts = exports.branchCommand.opts();
177
174
  let { project } = parentOpts;
178
175
  const { newName, description } = options;
179
- if (!project) {
180
- project = (0, config_1.getConfigValue)("project");
181
- }
176
+ if (!project)
177
+ project = (0, config_1.getRcConfig)().project;
182
178
  if (!project) {
183
179
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
184
180
  process.exit(1);
@@ -17,9 +17,8 @@ exports.checkoutCommand = new commander_1.Command("checkout")
17
17
  .action(async (branchName, options) => {
18
18
  let { project } = options;
19
19
  // Try to get project from config if not provided
20
- if (!project) {
21
- project = (0, config_1.getConfigValue)("project");
22
- }
20
+ if (!project)
21
+ project = (0, config_1.getRcConfig)().project;
23
22
  if (!project) {
24
23
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
25
24
  process.exit(1);
@@ -60,7 +59,7 @@ exports.checkoutCommand = new commander_1.Command("checkout")
60
59
  // Save to config
61
60
  (0, config_1.setConfig)("branch", selectedBranch);
62
61
  (0, config_1.setConfig)("project", project);
63
- console.log(chalk_1.default.green(`✔ Switched to branch '${selectedBranch}'`));
62
+ console.log(chalk_1.default.green(`✔ Switched to branch '${selectedBranch}'`));
64
63
  }
65
64
  catch (error) {
66
65
  spinner.fail("Failed to fetch branches");
@@ -55,11 +55,10 @@ exports.diffCommand = new commander_1.Command("diff")
55
55
  .action(async (env1, env2, options) => {
56
56
  let { project, env, branch, show } = options;
57
57
  // Use config fallback
58
- if (!project) {
59
- project = (0, config_1.getConfigValue)("project");
60
- }
58
+ if (!project)
59
+ project = (0, config_1.getRcConfig)().project;
61
60
  if (!branch) {
62
- branch = (0, config_1.getConfigValue)("branch") || "main";
61
+ branch = (0, config_1.getRcConfig)().branch || "main";
63
62
  }
64
63
  if (!project) {
65
64
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
@@ -155,7 +154,7 @@ function compareSecrets(source, target, show) {
155
154
  }
156
155
  });
157
156
  if (!hasDiff) {
158
- console.log(chalk_1.default.green(" No differences found."));
157
+ console.log(chalk_1.default.green("✔ No differences found."));
159
158
  }
160
159
  else if (!show) {
161
160
  console.log(chalk_1.default.gray("\n(Use --show to reveal secret values)"));
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.doctorCommand = void 0;
40
40
  /**
41
- * doctor.ts Diagnose common xtra-cli setup issues
41
+ * doctor.ts — Diagnose common xtra-cli setup issues
42
42
  *
43
43
  * Runs a series of self-diagnostic checks and reports
44
44
  * pass / warn / fail for each one.
@@ -49,65 +49,65 @@ const fs = __importStar(require("fs"));
49
49
  const path = __importStar(require("path"));
50
50
  const axios_1 = __importDefault(require("axios"));
51
51
  const config_1 = require("../lib/config");
52
- const PASS = chalk_1.default.green(" ");
53
- const FAIL = chalk_1.default.red(" ");
54
- const WARN = chalk_1.default.yellow(" ");
52
+ const PASS = chalk_1.default.green(" ✔");
53
+ const FAIL = chalk_1.default.red(" ✗");
54
+ const WARN = chalk_1.default.yellow(" âš ");
55
55
  async function runChecks() {
56
56
  const checks = [];
57
57
  const config = (0, config_1.getConfig)();
58
- // ── 1. Node Version ─────────────────────────────────────────────────────────
58
+ // ── 1. Node Version ─────────────────────────────────────────────────────────
59
59
  const nodeMajor = parseInt(process.versions.node.split(".")[0]);
60
60
  checks.push({
61
61
  name: "Node.js Version",
62
62
  status: nodeMajor >= 18 ? "pass" : "fail",
63
63
  message: `v${process.versions.node} ${nodeMajor >= 18 ? "(OK)" : "(requires >=18)"}`,
64
64
  });
65
- // ── 2. Auth Token ────────────────────────────────────────────────────────────
65
+ // ── 2. Auth Token ────────────────────────────────────────────────────────────
66
66
  const token = (0, config_1.getAuthToken)();
67
67
  checks.push({
68
68
  name: "Auth Token",
69
69
  status: token ? "pass" : "fail",
70
- message: token ? `Set (${token.substring(0, 8)}...)` : "Not set run 'xtra login'",
70
+ message: token ? `Set (${token.substring(0, 8)}...)` : "Not set — run 'xtra login'",
71
71
  });
72
- // ── 3. API URL Configured ────────────────────────────────────────────────────
72
+ // ── 3. API URL Configured ────────────────────────────────────────────────────
73
73
  const apiUrl = config.apiUrl || process.env.XTRA_API_URL;
74
74
  checks.push({
75
75
  name: "API URL",
76
76
  status: apiUrl ? "pass" : "warn",
77
77
  message: apiUrl || "Using default (https://xtra-security.vercel.app/api)",
78
78
  });
79
- // ── 4. API Reachability ──────────────────────────────────────────────────────
79
+ // ── 4. API Reachability ──────────────────────────────────────────────────────
80
80
  try {
81
81
  const res = await axios_1.default.get(`${config.apiUrl}/health`, { timeout: 4000 });
82
82
  checks.push({
83
83
  name: "API Connectivity",
84
84
  status: res.status === 200 ? "pass" : "warn",
85
- message: `${config.apiUrl} ${res.status} ${res.statusText}`,
85
+ message: `${config.apiUrl} → ${res.status} ${res.statusText}`,
86
86
  });
87
87
  }
88
88
  catch (e) {
89
89
  const msg = e.code === "ECONNREFUSED"
90
- ? "Connection refused is the server running?"
90
+ ? "Connection refused — is the server running?"
91
91
  : e.code === "ETIMEDOUT"
92
- ? "Timed out server may be slow or unreachable"
92
+ ? "Timed out — server may be slow or unreachable"
93
93
  : e.message;
94
94
  checks.push({ name: "API Connectivity", status: "fail", message: msg });
95
95
  }
96
- // ── 5. Active Project ────────────────────────────────────────────────────────
97
- const project = (0, config_1.getConfigValue)("project");
96
+ // ── 5. Active Project ────────────────────────────────────────────────────────
97
+ const project = (0, config_1.getRcConfig)().project;
98
98
  checks.push({
99
99
  name: "Active Project",
100
100
  status: project ? "pass" : "warn",
101
- message: project ? project : "Not set run 'xtra project set <id>'",
101
+ message: project ? project : "Not set — run 'xtra project set <id>'",
102
102
  });
103
- // ── 6. Active Branch ─────────────────────────────────────────────────────────
104
- const branch = (0, config_1.getConfigValue)("branch");
103
+ // ── 6. Active Branch ─────────────────────────────────────────────────────────
104
+ const branch = (0, config_1.getRcConfig)().branch;
105
105
  checks.push({
106
106
  name: "Active Branch",
107
107
  status: "pass",
108
108
  message: branch || "main (default)",
109
109
  });
110
- // ── 7. .xtrarc / xtra.json ─────────────────────────────────────────────────
110
+ // ── 7. .xtrarc / xtra.json ─────────────────────────────────────────────────
111
111
  const rcPath = path.join(process.cwd(), ".xtrarc");
112
112
  const jsonPath = path.join(process.cwd(), "xtra.json");
113
113
  const hasProjectConfig = fs.existsSync(rcPath) || fs.existsSync(jsonPath);
@@ -116,21 +116,21 @@ async function runChecks() {
116
116
  status: hasProjectConfig ? "pass" : "warn",
117
117
  message: hasProjectConfig
118
118
  ? fs.existsSync(rcPath) ? ".xtrarc found" : "xtra.json found"
119
- : "Not found run 'xtra init' to create one",
119
+ : "Not found — run 'xtra init' to create one",
120
120
  });
121
- // ── 8. .env file present ────────────────────────────────────────────────────
121
+ // ── 8. .env file present ────────────────────────────────────────────────────
122
122
  const envPath = path.join(process.cwd(), ".env");
123
123
  checks.push({
124
124
  name: ".env File",
125
125
  status: "pass",
126
126
  message: fs.existsSync(envPath) ? ".env found" : "No .env (not required if using xtra run)",
127
127
  });
128
- // ── 9. XTRA_MACHINE_TOKEN (for CI) ─────────────────────────────────────────
128
+ // ── 9. XTRA_MACHINE_TOKEN (for CI) ─────────────────────────────────────────
129
129
  const machineToken = process.env.XTRA_MACHINE_TOKEN;
130
130
  checks.push({
131
131
  name: "CI Machine Token",
132
- status: "pass", // Always pass optional
133
- message: machineToken ? "XTRA_MACHINE_TOKEN is set " : "Not set (only needed for CI/CD pipelines)",
132
+ status: "pass", // Always pass — optional
133
+ message: machineToken ? "XTRA_MACHINE_TOKEN is set ✔" : "Not set (only needed for CI/CD pipelines)",
134
134
  });
135
135
  return checks;
136
136
  }
@@ -139,7 +139,7 @@ exports.doctorCommand = new commander_1.Command("doctor")
139
139
  .option("--json", "Output results as JSON", false)
140
140
  .action(async (options) => {
141
141
  if (!options.json) {
142
- console.log(chalk_1.default.bold("\n🩺 xtra doctor running diagnostics...\n"));
142
+ console.log(chalk_1.default.bold("\n🩺 xtra doctor — running diagnostics...\n"));
143
143
  }
144
144
  const checks = await runChecks();
145
145
  if (options.json) {
@@ -164,7 +164,7 @@ exports.doctorCommand = new commander_1.Command("doctor")
164
164
  }
165
165
  console.log();
166
166
  if (failures === 0 && warnings === 0) {
167
- console.log(chalk_1.default.green.bold(" All checks passed! Your CLI is ready to use."));
167
+ console.log(chalk_1.default.green.bold(" ✅ All checks passed! Your CLI is ready to use."));
168
168
  }
169
169
  else {
170
170
  if (failures > 0)
@@ -21,9 +21,9 @@ exports.envCommand
21
21
  .action(async (options) => {
22
22
  try {
23
23
  let projectId = options.project;
24
- const branch = options.branch || (0, config_1.getConfigValue)("branch") || "main";
24
+ const branch = options.branch || (0, config_1.getRcConfig)().branch || "main";
25
25
  if (!projectId) {
26
- projectId = (0, config_1.getConfigValue)("project");
26
+ projectId = (0, config_1.getRcConfig)().project;
27
27
  if (!projectId) {
28
28
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
29
29
  process.exit(1);
@@ -21,11 +21,10 @@ exports.exportCommand = new commander_1.Command("export")
21
21
  .action(async (options) => {
22
22
  let { project, env, branch, format, output } = options;
23
23
  // Use config fallback
24
- if (!project) {
25
- project = (0, config_1.getConfigValue)("project");
26
- }
24
+ if (!project)
25
+ project = (0, config_1.getRcConfig)().project;
27
26
  if (!branch) {
28
- branch = (0, config_1.getConfigValue)("branch") || "main";
27
+ branch = (0, config_1.getRcConfig)().branch || "main";
29
28
  }
30
29
  // Normalize Env
31
30
  const envMap = { dev: "development", stg: "staging", prod: "production" };
@@ -63,7 +62,7 @@ exports.exportCommand = new commander_1.Command("export")
63
62
  if (output) {
64
63
  const outputPath = path_1.default.resolve(process.cwd(), output);
65
64
  fs_1.default.writeFileSync(outputPath, content, "utf-8");
66
- console.log(chalk_1.default.green(`✔ Secrets exported to ${outputPath}`));
65
+ console.log(chalk_1.default.green(`✔ Secrets exported to ${outputPath}`));
67
66
  }
68
67
  else {
69
68
  console.log(content);
@@ -80,11 +80,10 @@ exports.generateCommand = new commander_1.Command("generate")
80
80
  .action(async (options) => {
81
81
  let { project, env, branch, output, format, force } = options;
82
82
  // Use config fallback
83
- if (!project) {
84
- project = (0, config_1.getConfigValue)("project");
85
- }
83
+ if (!project)
84
+ project = (0, config_1.getRcConfig)().project;
86
85
  if (!branch) {
87
- branch = (0, config_1.getConfigValue)("branch") || "main";
86
+ branch = (0, config_1.getRcConfig)().branch || "main";
88
87
  }
89
88
  // Normalize Env
90
89
  const envMap = { dev: "development", stg: "staging", prod: "production" };
@@ -18,7 +18,7 @@ exports.historyCommand
18
18
  try {
19
19
  let projectId = options.project;
20
20
  if (!projectId) {
21
- projectId = (0, config_1.getConfigValue)("project");
21
+ projectId = (0, config_1.getRcConfig)().project;
22
22
  if (!projectId) {
23
23
  console.error(chalk_1.default.red("Error: Project ID not found. Use -p <id> or run 'xtra project set' first."));
24
24
  process.exit(1);
@@ -56,7 +56,7 @@ exports.rollbackCommand
56
56
  try {
57
57
  let projectId = options.project;
58
58
  if (!projectId) {
59
- projectId = (0, config_1.getConfigValue)("project");
59
+ projectId = (0, config_1.getRcConfig)().project;
60
60
  if (!projectId) {
61
61
  console.error(chalk_1.default.red("Error: Project ID not found. Use -p <id> or run 'xtra project set' first."));
62
62
  process.exit(1);
@@ -24,11 +24,10 @@ exports.importCommand = new commander_1.Command("import")
24
24
  .action(async (file, options) => {
25
25
  let { project, env, branch, format, prefix } = options;
26
26
  // Use config fallback
27
- if (!project) {
28
- project = (0, config_1.getConfigValue)("project");
29
- }
27
+ if (!project)
28
+ project = (0, config_1.getRcConfig)().project;
30
29
  if (!branch) {
31
- branch = (0, config_1.getConfigValue)("branch") || "main";
30
+ branch = (0, config_1.getRcConfig)().branch || "main";
32
31
  }
33
32
  // Normalize Env
34
33
  const envMap = { dev: "development", stg: "staging", prod: "production" };
@@ -23,7 +23,7 @@ exports.integrationCommand
23
23
  try {
24
24
  let projectId = options.project;
25
25
  if (!projectId) {
26
- projectId = (0, config_1.getConfigValue)("project");
26
+ projectId = (0, config_1.getRcConfig)().project;
27
27
  if (!projectId) {
28
28
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
29
29
  process.exit(1);
@@ -48,7 +48,7 @@ exports.integrationCommand
48
48
  name: "repo",
49
49
  message: "Select a repository to sync to:",
50
50
  choices: repos.map((r) => ({
51
- name: `${r.fullName} ${r.private ? "(🔒)" : ""}`,
51
+ name: `${r.fullName} ${r.private ? "(🔒)" : ""}`,
52
52
  value: r.id.toString(),
53
53
  short: r.fullName
54
54
  }))
@@ -84,10 +84,10 @@ exports.integrationCommand
84
84
  // Show details
85
85
  result.results.forEach((r) => {
86
86
  if (r.success) {
87
- console.log(chalk_1.default.green(`✓ ${r.key}`));
87
+ console.log(chalk_1.default.green(`✓ ${r.key}`));
88
88
  }
89
89
  else {
90
- console.log(chalk_1.default.red(`✗ ${r.key}: ${r.error}`));
90
+ console.log(chalk_1.default.red(`✗ ${r.key}: ${r.error}`));
91
91
  }
92
92
  });
93
93
  }
@@ -109,7 +109,7 @@ exports.kubernetesCommand
109
109
  try {
110
110
  let projectId = options.project;
111
111
  if (!projectId) {
112
- projectId = (0, config_1.getConfigValue)("project");
112
+ projectId = (0, config_1.getRcConfig)().project;
113
113
  if (!projectId) {
114
114
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
115
115
  process.exit(1);
@@ -153,7 +153,7 @@ exports.kubernetesCommand
153
153
  }
154
154
  let projectId = options.project;
155
155
  if (!projectId) {
156
- projectId = (0, config_1.getConfigValue)("project");
156
+ projectId = (0, config_1.getRcConfig)().project;
157
157
  if (!projectId) {
158
158
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
159
159
  process.exit(1);
@@ -38,12 +38,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.localCommand = void 0;
40
40
  /**
41
- * local.ts Toggle between cloud/local-only mode for offline development
41
+ * local.ts — Toggle between cloud/local-only mode for offline development
42
42
  *
43
43
  * In "local" mode:
44
44
  * - xtra run reads from .env.local instead of calling the API
45
45
  * - A flag XTRA_LOCAL_MODE=true is written to the CLI config
46
- * - All API calls are bypassed completely offline capable
46
+ * - All API calls are bypassed — completely offline capable
47
47
  *
48
48
  * Usage:
49
49
  * xtra local on # Enable local mode (reads from .env.local)
@@ -73,11 +73,11 @@ This allows fully offline development without any API calls.
73
73
  Examples:
74
74
  $ xtra local status # Check current mode
75
75
  $ xtra local on # Enable offline (local) mode
76
- $ xtra local off # Disable back to cloud mode
77
- $ xtra local sync # Pull cloud secrets .env.local
76
+ $ xtra local off # Disable — back to cloud mode
77
+ $ xtra local sync # Pull cloud secrets → .env.local
78
78
  $ xtra local sync -p proj -e production # Pull production to .env.local
79
79
  `);
80
- // ── status ────────────────────────────────────────────────────────────────────
80
+ // ── status ────────────────────────────────────────────────────────────────────
81
81
  exports.localCommand
82
82
  .command("status")
83
83
  .description("Show current cloud/local mode")
@@ -86,7 +86,7 @@ exports.localCommand
86
86
  const envFilePath = path.join(process.cwd(), LOCAL_ENV_FILE);
87
87
  const hasLocalFile = fs.existsSync(envFilePath);
88
88
  console.log(chalk_1.default.bold("\nMode Status:\n"));
89
- console.log(` Mode : ${mode ? chalk_1.default.yellow("🔌 LOCAL (offline)") : chalk_1.default.green(" CLOUD")}`);
89
+ console.log(` Mode : ${mode ? chalk_1.default.yellow("🔌 LOCAL (offline)") : chalk_1.default.green("☁ CLOUD")}`);
90
90
  console.log(` .env.local : ${hasLocalFile ? chalk_1.default.green("Found") : chalk_1.default.gray("Not found")}`);
91
91
  console.log(` Config flag : ${chalk_1.default.gray((0, config_1.getConfigValue)("localMode") ? "true" : "false")}`);
92
92
  console.log(` Env var : ${chalk_1.default.gray(process.env.XTRA_LOCAL_MODE || "(not set)")}`);
@@ -97,34 +97,34 @@ exports.localCommand
97
97
  else {
98
98
  console.log(chalk_1.default.gray(" Run 'xtra local off' to switch back to cloud mode."));
99
99
  if (!hasLocalFile) {
100
- console.log(chalk_1.default.yellow(" No .env.local file found. Run 'xtra local sync' to pull secrets."));
100
+ console.log(chalk_1.default.yellow(" âš  No .env.local file found. Run 'xtra local sync' to pull secrets."));
101
101
  }
102
102
  }
103
103
  console.log();
104
104
  });
105
- // ── on ────────────────────────────────────────────────────────────────────────
105
+ // ── on ────────────────────────────────────────────────────────────────────────
106
106
  exports.localCommand
107
107
  .command("on")
108
- .description("Enable local mode secrets read from .env.local")
108
+ .description("Enable local mode — secrets read from .env.local")
109
109
  .action(() => {
110
110
  (0, config_1.setConfig)("localMode", true);
111
- console.log(chalk_1.default.yellow("🔌 Local mode ENABLED."));
111
+ console.log(chalk_1.default.yellow("🔌 Local mode ENABLED."));
112
112
  console.log(chalk_1.default.gray(" 'xtra run' will now read from .env.local instead of the cloud."));
113
113
  const localFilePath = path.join(process.cwd(), LOCAL_ENV_FILE);
114
114
  if (!fs.existsSync(localFilePath)) {
115
- console.log(chalk_1.default.yellow(` No .env.local file found. Run 'xtra local sync' to populate it.`));
115
+ console.log(chalk_1.default.yellow(` âš  No .env.local file found. Run 'xtra local sync' to populate it.`));
116
116
  }
117
117
  });
118
- // ── off ───────────────────────────────────────────────────────────────────────
118
+ // ── off ───────────────────────────────────────────────────────────────────────
119
119
  exports.localCommand
120
120
  .command("off")
121
- .description("Disable local mode secrets fetched from cloud again")
121
+ .description("Disable local mode — secrets fetched from cloud again")
122
122
  .action(() => {
123
123
  (0, config_1.setConfig)("localMode", false);
124
- console.log(chalk_1.default.green(" Cloud mode ENABLED."));
124
+ console.log(chalk_1.default.green("☁ Cloud mode ENABLED."));
125
125
  console.log(chalk_1.default.gray(" 'xtra run' will now fetch secrets from XtraSecurity Cloud."));
126
126
  });
127
- // ── sync ─────────────────────────────────────────────────────────────────────
127
+ // ── sync ─────────────────────────────────────────────────────────────────────
128
128
  exports.localCommand
129
129
  .command("sync")
130
130
  .description("Pull cloud secrets to .env.local for offline use")
@@ -138,7 +138,7 @@ exports.localCommand
138
138
  const envMap = { dev: "development", stg: "staging", prod: "production" };
139
139
  env = envMap[env] || env;
140
140
  if (!project)
141
- project = (0, config_1.getConfigValue)("project");
141
+ project = (0, config_1.getRcConfig)().project;
142
142
  if (!project) {
143
143
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id>."));
144
144
  process.exit(1);
@@ -146,7 +146,7 @@ exports.localCommand
146
146
  const outputPath = path.resolve(process.cwd(), output);
147
147
  // Warn if production
148
148
  if (env === "production" && !overwrite) {
149
- console.log(chalk_1.default.red(`⚠ You are syncing PRODUCTION secrets to ${output}.`));
149
+ console.log(chalk_1.default.red(`âš  You are syncing PRODUCTION secrets to ${output}.`));
150
150
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
151
151
  const confirmed = await new Promise(resolve => {
152
152
  rl.question(chalk_1.default.yellow("Type 'yes' to confirm: "), (ans) => {
@@ -170,14 +170,14 @@ exports.localCommand
170
170
  }
171
171
  // Write dotenv format
172
172
  const lines = [
173
- `# xtra local sync ${env}/${branch}`,
173
+ `# xtra local sync — ${env}/${branch}`,
174
174
  `# Generated: ${new Date().toISOString()}`,
175
175
  `# DO NOT COMMIT THIS FILE`,
176
176
  "",
177
177
  ...Object.entries(secrets).map(([k, v]) => `${k}=${v}`)
178
178
  ];
179
179
  fs.writeFileSync(outputPath, lines.join("\n") + "\n", "utf8");
180
- console.log(chalk_1.default.green(`✅ Synced ${count} secrets to ${output}`));
180
+ console.log(chalk_1.default.green(`✅ Synced ${count} secrets to ${output}`));
181
181
  console.log(chalk_1.default.gray(" Run 'xtra local on' to switch to local mode."));
182
182
  // Remind about .gitignore
183
183
  const gitignorePath = path.join(process.cwd(), ".gitignore");
@@ -187,7 +187,7 @@ exports.localCommand
187
187
  }
188
188
  catch (_) { }
189
189
  if (!gitignore.includes(output)) {
190
- console.log(chalk_1.default.yellow(`\n Remember to add '${output}' to your .gitignore!`));
190
+ console.log(chalk_1.default.yellow(`\n âš  Remember to add '${output}' to your .gitignore!`));
191
191
  }
192
192
  }
193
193
  catch (e) {
@@ -19,9 +19,8 @@ exports.rollbackCommand = new commander_1.Command("rollback")
19
19
  .action(async (key, options) => {
20
20
  let { project, env } = options;
21
21
  // Use config fallback
22
- if (!project) {
23
- project = (0, config_1.getConfigValue)("project");
24
- }
22
+ if (!project)
23
+ project = (0, config_1.getRcConfig)().project;
25
24
  if (!project) {
26
25
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
27
26
  process.exit(1);
@@ -20,9 +20,8 @@ exports.rotateCommand = new commander_1.Command("rotate")
20
20
  .action(async (key, options) => {
21
21
  let { project, env, strategy, promote, value } = options;
22
22
  // Use config fallback
23
- if (!project) {
24
- project = (0, config_1.getConfigValue)("project");
25
- }
23
+ if (!project)
24
+ project = (0, config_1.getRcConfig)().project;
26
25
  // Normalize Env
27
26
  const envMap = { dev: "development", stg: "staging", prod: "production" };
28
27
  env = envMap[env] || env;
@@ -36,7 +35,7 @@ exports.rotateCommand = new commander_1.Command("rotate")
36
35
  const { confirmProd } = await inquirer.prompt([{
37
36
  type: "confirm",
38
37
  name: "confirmProd",
39
- message: chalk_1.default.red(`⚠ You are about to rotate a secret in PRODUCTION (${key}). Proceed?`),
38
+ message: chalk_1.default.red(`âš  You are about to rotate a secret in PRODUCTION (${key}). Proceed?`),
40
39
  default: false,
41
40
  }]);
42
41
  if (!confirmProd) {
@@ -65,26 +65,16 @@ Examples:
65
65
  .action(async (command, args, options) => {
66
66
  // console.log("Run Options:", options);
67
67
  let { project, env, branch, shell: useShell } = options;
68
- // ── Load .xtrarc from current directory (project-local config) ─────────
69
- const rcPath = path.resolve(process.cwd(), ".xtrarc");
70
- let rcConfig = {};
71
- if (fs.existsSync(rcPath)) {
72
- try {
73
- rcConfig = JSON.parse(fs.readFileSync(rcPath, "utf8"));
74
- }
75
- catch (_) { }
76
- }
68
+ // Load .xtrarc from CWD first (project-local config), then fall back to global conf store
69
+ const rc = (0, config_1.getRcConfig)();
77
70
  // Use active branch from config if not specified
78
- if (!branch) {
79
- branch = rcConfig.branch || (0, config_1.getConfigValue)("branch") || "main";
80
- }
71
+ if (!branch)
72
+ branch = rc.branch;
81
73
  // Normalize Env
82
74
  const envMap = { dev: "development", stg: "staging", prod: "production" };
83
- env = envMap[env] || env || rcConfig.env || (0, config_1.getConfigValue)("env") || "development";
84
- // We might want to support default project in config later
85
- if (!project) {
86
- project = rcConfig.project || (0, config_1.getConfigValue)("project");
87
- }
75
+ env = envMap[env] || env || rc.env;
76
+ if (!project)
77
+ project = rc.project;
88
78
  if (!project) {
89
79
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
90
80
  process.exit(1);
@@ -28,19 +28,20 @@ Examples:
28
28
  .action(async (options) => {
29
29
  const parentOpts = exports.secretsCommand.opts();
30
30
  let { project, env, branch } = parentOpts;
31
- // Use active branch from config if not specified
32
- if (!branch) {
33
- branch = (0, config_1.getConfigValue)("branch") || "main";
34
- }
31
+ // Load .xtrarc from CWD (local project config has priority over global conf store)
32
+ const rc = (0, config_1.getRcConfig)();
33
+ if (!project)
34
+ project = rc.project;
35
+ if (!branch)
36
+ branch = rc.branch;
37
+ if (!env || env === "development")
38
+ env = rc.env; // only override if still at default
35
39
  // Normalize Env
36
40
  const envMap = { dev: "development", stg: "staging", prod: "production" };
37
41
  env = envMap[env] || env;
38
42
  const { show } = options;
39
43
  if (!project) {
40
- project = (0, config_1.getConfigValue)("project");
41
- }
42
- if (!project) {
43
- console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or checkout a branch."));
44
+ console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run `xtra init` first."));
44
45
  process.exit(1);
45
46
  }
46
47
  const spinner = (0, ora_1.default)(`Fetching secrets for ${env} (branch: ${branch})...`).start();
@@ -5,10 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.simulateCommand = void 0;
7
7
  /**
8
- * simulate.ts Dry-run mode for xtra run
8
+ * simulate.ts — Dry-run mode for xtra run
9
9
  *
10
10
  * Fetches secrets and shows exactly what `xtra run` would inject into
11
- * the process environment without actually executing the command.
11
+ * the process environment — without actually executing the command.
12
12
  * Safe to use in any environment.
13
13
  */
14
14
  const commander_1 = require("commander");
@@ -30,7 +30,7 @@ exports.simulateCommand = new commander_1.Command("simulate")
30
30
  const envMap = { dev: "development", stg: "staging", prod: "production" };
31
31
  env = envMap[env] || env;
32
32
  if (!project)
33
- project = (0, config_1.getConfigValue)("project");
33
+ project = (0, config_1.getRcConfig)().project;
34
34
  if (!project) {
35
35
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
36
36
  process.exit(1);
@@ -47,7 +47,7 @@ exports.simulateCommand = new commander_1.Command("simulate")
47
47
  process.exit(1);
48
48
  }
49
49
  const count = Object.keys(secrets).length;
50
- console.log(chalk_1.default.bold(`\n🔬 Simulation xtra run ${command ? chalk_1.default.cyan(command) : "(no command specified)"}\n`));
50
+ console.log(chalk_1.default.bold(`\n🔬 Simulation — xtra run ${command ? chalk_1.default.cyan(command) : "(no command specified)"}\n`));
51
51
  console.log(chalk_1.default.gray(` Project : ${project}`));
52
52
  console.log(chalk_1.default.gray(` Env : ${env}`));
53
53
  console.log(chalk_1.default.gray(` Branch : ${branch}`));
@@ -61,16 +61,16 @@ exports.simulateCommand = new commander_1.Command("simulate")
61
61
  ];
62
62
  for (const [key, value] of Object.entries(secrets)) {
63
63
  const localVal = process.env[key];
64
- const displayVal = showValues ? chalk_1.default.cyan(value) : chalk_1.default.gray("••••••••");
64
+ const displayVal = showValues ? chalk_1.default.cyan(value) : chalk_1.default.gray("••••••••");
65
65
  let status = chalk_1.default.green("Injected");
66
66
  let localDisplay = "-";
67
67
  if (showDiff && localVal !== undefined) {
68
- localDisplay = showValues ? chalk_1.default.yellow(localVal) : chalk_1.default.gray("••••••••");
68
+ localDisplay = showValues ? chalk_1.default.yellow(localVal) : chalk_1.default.gray("••••••••");
69
69
  if (localVal === value) {
70
70
  status = chalk_1.default.gray("Same");
71
71
  }
72
72
  else {
73
- status = chalk_1.default.yellow(" Override");
73
+ status = chalk_1.default.yellow("âš¡ Override");
74
74
  }
75
75
  }
76
76
  else if (showDiff) {
@@ -87,6 +87,6 @@ exports.simulateCommand = new commander_1.Command("simulate")
87
87
  if (!showDiff) {
88
88
  console.log(chalk_1.default.gray(" Tip: use --diff to compare against your local process.env"));
89
89
  }
90
- console.log(chalk_1.default.bold(`\n Simulation complete. ${count} secret(s) would be injected.`));
90
+ console.log(chalk_1.default.bold(`\n ✅ Simulation complete. ${count} secret(s) would be injected.`));
91
91
  console.log(chalk_1.default.gray(" Run without 'simulate' to actually execute the command.\n"));
92
92
  });
@@ -20,11 +20,10 @@ exports.statusCommand = new commander_1.Command("status")
20
20
  .action(async (options) => {
21
21
  let { project, env, branch } = options;
22
22
  // Use config fallback
23
- if (!project) {
24
- project = (0, config_1.getConfigValue)("project");
25
- }
23
+ if (!project)
24
+ project = (0, config_1.getRcConfig)().project;
26
25
  if (!branch) {
27
- branch = (0, config_1.getConfigValue)("branch") || "main";
26
+ branch = (0, config_1.getRcConfig)().branch || "main";
28
27
  }
29
28
  if (!project) {
30
29
  console.error(chalk_1.default.red("Error: Project ID is required. Use -p <id> or run 'xtra project set' first."));
@@ -76,15 +75,15 @@ exports.statusCommand = new commander_1.Command("status")
76
75
  console.log("");
77
76
  console.log((0, table_1.table)(rows));
78
77
  if (diffCount === 0) {
79
- console.log(chalk_1.default.green(" Everything is in sync."));
78
+ console.log(chalk_1.default.green("✔ Everything is in sync."));
80
79
  }
81
80
  else {
82
- console.log(chalk_1.default.yellow(`⚠ Found ${diffCount} difference(s).\n`));
81
+ console.log(chalk_1.default.yellow(`âš  Found ${diffCount} difference(s).\n`));
83
82
  console.log(chalk_1.default.bold(" Next steps:"));
84
- console.log(chalk_1.default.gray(" xtra generate ") + chalk_1.default.white("# pull cloud secrets .env (merge)"));
85
- console.log(chalk_1.default.gray(" xtra generate -f json ") + chalk_1.default.white("# pull cloud secrets secrets.json"));
83
+ console.log(chalk_1.default.gray(" xtra generate ") + chalk_1.default.white("# pull cloud secrets → .env (merge)"));
84
+ console.log(chalk_1.default.gray(" xtra generate -f json ") + chalk_1.default.white("# pull cloud secrets → secrets.json"));
86
85
  console.log(chalk_1.default.gray(" xtra run node app.js ") + chalk_1.default.white("# inject secrets at runtime (no file)"));
87
- console.log(chalk_1.default.gray(" xtra local sync ") + chalk_1.default.white("# pull cloud secrets .env.local (offline mode)"));
86
+ console.log(chalk_1.default.gray(" xtra local sync ") + chalk_1.default.white("# pull cloud secrets → .env.local (offline mode)"));
88
87
  }
89
88
  }
90
89
  catch (error) {
@@ -1,15 +1,15 @@
1
1
  "use strict";
2
2
  /**
3
- * template.ts Secret Templating Engine for xtra-cli
3
+ * template.ts — Secret Templating Engine for xtra-cli
4
4
  *
5
5
  * Reads a template file containing {{ secrets.KEY }} placeholders,
6
6
  * fetches secrets from XtraSecurity Cloud, substitutes all placeholders,
7
7
  * and writes the rendered output to the specified file (or stdout).
8
8
  *
9
9
  * Supported placeholder syntaxes:
10
- * {{ secrets.DATABASE_URL }} fetches DATABASE_URL from secrets
11
- * {{ secrets.PORT | 3000 }} with fallback default value "3000"
12
- * {{ env.NODE_ENV }} reads from local process environment
10
+ * {{ secrets.DATABASE_URL }} → fetches DATABASE_URL from secrets
11
+ * {{ secrets.PORT | 3000 }} → with fallback default value "3000"
12
+ * {{ env.NODE_ENV }} → reads from local process environment
13
13
  *
14
14
  * Example:
15
15
  * # config.yaml.tpl
@@ -70,7 +70,7 @@ const chalk_1 = __importDefault(require("chalk"));
70
70
  const ora_1 = __importDefault(require("ora"));
71
71
  const fs = __importStar(require("fs"));
72
72
  const path = __importStar(require("path"));
73
- // ─── Placeholder Regex ────────────────────────────────────────────────────────
73
+ // ─── Placeholder Regex ────────────────────────────────────────────────────────
74
74
  // Matches: {{ secrets.KEY }}, {{ secrets.KEY | default }}, {{ env.KEY | default }}
75
75
  const PLACEHOLDER_RE = /\{\{\s*(secrets|env)\.([A-Z0-9_a-z]+)(?:\s*\|\s*([^}]*?))?\s*\}\}/g;
76
76
  function renderTemplate(template, secrets, processEnv) {
@@ -91,18 +91,18 @@ function renderTemplate(template, secrets, processEnv) {
91
91
  }
92
92
  if (defaultVal !== undefined) {
93
93
  const trimmedDefault = defaultVal.trim();
94
- usedDefaults.push(`${source}.${key} "${trimmedDefault}"`);
94
+ usedDefaults.push(`${source}.${key} → "${trimmedDefault}"`);
95
95
  replacedCount++;
96
96
  return trimmedDefault;
97
97
  }
98
98
  missingKeys.push(`${source}.${key}`);
99
- return `{{ ${source}.${key} }}`; // Leave unreplaced user will see it clearly
99
+ return `{{ ${source}.${key} }}`; // Leave unreplaced — user will see it clearly
100
100
  });
101
101
  return { output, replacedCount, missingKeys, usedDefaults };
102
102
  }
103
- // ─── Command ──────────────────────────────────────────────────────────────────
103
+ // ─── Command ──────────────────────────────────────────────────────────────────
104
104
  exports.templateCommand = new commander_1.Command("template")
105
- .description("Secret templating engine inject secrets into config file templates")
105
+ .description("Secret templating engine — inject secrets into config file templates")
106
106
  .addHelpText("after", `
107
107
  Template Syntax:
108
108
  {{ secrets.KEY }} Replace with secret value
@@ -116,7 +116,7 @@ Examples:
116
116
  $ xtra template check config.yaml.tpl -p proj123 -e production
117
117
  $ xtra template list config.yaml.tpl
118
118
  `);
119
- // ── render ────────────────────────────────────────────────────────────────────
119
+ // ── render ────────────────────────────────────────────────────────────────────
120
120
  exports.templateCommand
121
121
  .command("render <templateFile>")
122
122
  .description("Render a template file by injecting secrets and environment variables")
@@ -131,7 +131,7 @@ exports.templateCommand
131
131
  const envMap = { dev: "development", stg: "staging", prod: "production" };
132
132
  env = envMap[env] || env;
133
133
  if (!project)
134
- project = (0, config_1.getConfigValue)("project");
134
+ project = (0, config_1.getRcConfig)().project;
135
135
  if (!project) {
136
136
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
137
137
  process.exit(1);
@@ -163,12 +163,12 @@ exports.templateCommand
163
163
  // Render
164
164
  const { output: rendered, replacedCount, missingKeys, usedDefaults } = renderTemplate(template, secrets, process.env);
165
165
  // Report
166
- console.log(chalk_1.default.green(`✅ Replaced ${replacedCount} placeholder(s).`));
166
+ console.log(chalk_1.default.green(`✅ Replaced ${replacedCount} placeholder(s).`));
167
167
  if (usedDefaults.length > 0) {
168
- console.log(chalk_1.default.yellow(`⚠ Used defaults for: ${usedDefaults.join(", ")}`));
168
+ console.log(chalk_1.default.yellow(`âš  Used defaults for: ${usedDefaults.join(", ")}`));
169
169
  }
170
170
  if (missingKeys.length > 0) {
171
- console.log(chalk_1.default.red(`✗ Unresolved placeholders: ${missingKeys.join(", ")}`));
171
+ console.log(chalk_1.default.red(`✗ Unresolved placeholders: ${missingKeys.join(", ")}`));
172
172
  if (strict) {
173
173
  console.error(chalk_1.default.red("Aborting due to --strict mode."));
174
174
  process.exit(1);
@@ -178,7 +178,7 @@ exports.templateCommand
178
178
  if (output) {
179
179
  const outPath = path.resolve(process.cwd(), output);
180
180
  fs.writeFileSync(outPath, rendered, "utf8");
181
- console.log(chalk_1.default.blue(`→ Written to: ${outPath}`));
181
+ console.log(chalk_1.default.blue(`→ Written to: ${outPath}`));
182
182
  }
183
183
  else {
184
184
  // Print to stdout (so user can pipe it)
@@ -196,7 +196,7 @@ exports.templateCommand
196
196
  }
197
197
  catch (e) { }
198
198
  });
199
- // ── check ─────────────────────────────────────────────────────────────────────
199
+ // ── check ─────────────────────────────────────────────────────────────────────
200
200
  exports.templateCommand
201
201
  .command("check <templateFile>")
202
202
  .description("Validate that all template placeholders have matching secrets (dry-run)")
@@ -208,7 +208,7 @@ exports.templateCommand
208
208
  const envMap = { dev: "development", stg: "staging", prod: "production" };
209
209
  env = envMap[env] || env;
210
210
  if (!project)
211
- project = (0, config_1.getConfigValue)("project");
211
+ project = (0, config_1.getRcConfig)().project;
212
212
  if (!project) {
213
213
  console.error(chalk_1.default.red("Error: Project ID required."));
214
214
  process.exit(1);
@@ -242,14 +242,14 @@ exports.templateCommand
242
242
  }
243
243
  if (missingKeys.length > 0) {
244
244
  console.log(chalk_1.default.red(`\n Missing secrets (no default):`));
245
- missingKeys.forEach(k => console.log(chalk_1.default.red(` ${k}`)));
245
+ missingKeys.forEach(k => console.log(chalk_1.default.red(` ✗ ${k}`)));
246
246
  process.exit(1);
247
247
  }
248
248
  else {
249
- console.log(chalk_1.default.green("\n All placeholders are resolvable!"));
249
+ console.log(chalk_1.default.green("\n ✅ All placeholders are resolvable!"));
250
250
  }
251
251
  });
252
- // ── list ──────────────────────────────────────────────────────────────────────
252
+ // ── list ──────────────────────────────────────────────────────────────────────
253
253
  exports.templateCommand
254
254
  .command("list <templateFile>")
255
255
  .description("List all placeholders found in a template file (no API call needed)")
@@ -269,7 +269,7 @@ exports.templateCommand
269
269
  matches.forEach(m => {
270
270
  const [, source, key, def] = m;
271
271
  const defaultHint = def ? chalk_1.default.gray(` (default: "${def.trim()}")`) : "";
272
- const icon = source === "secrets" ? "🔒" : "📦";
272
+ const icon = source === "secrets" ? "🔒" : "📦";
273
273
  console.log(` ${icon} ${chalk_1.default.cyan(`{{ ${source}.${key} }}`)}${defaultHint}`);
274
274
  });
275
275
  console.log(`\n Total: ${chalk_1.default.bold(matches.length)} placeholder(s)\n`);
@@ -42,39 +42,39 @@ const chalk_1 = __importDefault(require("chalk"));
42
42
  const readline = __importStar(require("readline"));
43
43
  const api_1 = require("../lib/api");
44
44
  const ENVS = ["development", "staging", "production"];
45
- // ─── Rendering ────────────────────────────────────────────────────────────────
45
+ // ─── Rendering ────────────────────────────────────────────────────────────────
46
46
  function clear() { process.stdout.write("\x1Bc"); }
47
47
  function renderHeader() {
48
- console.log(chalk_1.default.cyan("╔═══════════════════════════════════════════════════════════╗"));
49
- console.log(chalk_1.default.cyan("") + chalk_1.default.bold.cyan(" 🔒 XtraSecurity Interactive Shell ") + chalk_1.default.cyan(""));
50
- console.log(chalk_1.default.cyan("╚═══════════════════════════════════════════════════════════╝"));
51
- console.log(chalk_1.default.gray(" ↑↓ Navigate Enter Select Tab Switch Panel Q Quit\n"));
48
+ console.log(chalk_1.default.cyan("╔═══════════════════════════════════════════════════════════╗"));
49
+ console.log(chalk_1.default.cyan("║") + chalk_1.default.bold.cyan(" 🔒 XtraSecurity — Interactive Shell ") + chalk_1.default.cyan("║"));
50
+ console.log(chalk_1.default.cyan("╚═══════════════════════════════════════════════════════════╝"));
51
+ console.log(chalk_1.default.gray(" ↑↓ Navigate • Enter Select • Tab Switch Panel • Q Quit\n"));
52
52
  }
53
53
  function renderList(title, items, selectedIdx, active) {
54
54
  const border = active ? chalk_1.default.cyan : chalk_1.default.gray;
55
- console.log(border(`┌─ ${title} ${"".repeat(Math.max(0, 28 - title.length))}┐`));
55
+ console.log(border(`┌─ ${title} ${"─".repeat(Math.max(0, 28 - title.length))}┐`));
56
56
  items.forEach((item, i) => {
57
57
  const isSelected = i === selectedIdx;
58
- const icon = isSelected ? (active ? chalk_1.default.cyan(" ") : chalk_1.default.gray(" ")) : " ";
58
+ const icon = isSelected ? (active ? chalk_1.default.cyan("â–¶ ") : chalk_1.default.gray("â–¶ ")) : " ";
59
59
  const text = isSelected && active ? chalk_1.default.bold.cyan(item) : isSelected ? chalk_1.default.cyan(item) : chalk_1.default.white(item);
60
- console.log(border("") + ` ${icon}${text}`.padEnd(30) + border(""));
60
+ console.log(border("│") + ` ${icon}${text}`.padEnd(30) + border("│"));
61
61
  });
62
- console.log(border(`└${"".repeat(32)}┘`));
62
+ console.log(border(`└${"─".repeat(32)}┘`));
63
63
  }
64
64
  function renderSecrets(secrets, selectedIdx, active) {
65
65
  const border = active ? chalk_1.default.cyan : chalk_1.default.gray;
66
66
  const COLS = [36, 12, 20];
67
- const sep = "".repeat(COLS[0]) + "" + "".repeat(COLS[1]) + "" + "".repeat(COLS[2]);
68
- console.log(border(`┌${sep}┐`));
67
+ const sep = "─".repeat(COLS[0]) + "┬" + "─".repeat(COLS[1]) + "┬" + "─".repeat(COLS[2]);
68
+ console.log(border(`┌${sep}┐`));
69
69
  const header = [
70
70
  chalk_1.default.bold.yellow("KEY".padEnd(COLS[0])),
71
71
  chalk_1.default.bold.yellow("VALUE".padEnd(COLS[1])),
72
72
  chalk_1.default.bold.yellow("UPDATED".padEnd(COLS[2]))
73
- ].join(border(""));
74
- console.log(border("") + header + border(""));
75
- console.log(border(`├${sep}┤`));
73
+ ].join(border("│"));
74
+ console.log(border("│") + header + border("│"));
75
+ console.log(border(`├${sep}┤`));
76
76
  if (secrets.length === 0) {
77
- console.log(border("") + chalk_1.default.gray(" No secrets found.".padEnd(COLS[0] + COLS[1] + COLS[2] + 2)) + border(""));
77
+ console.log(border("│") + chalk_1.default.gray(" No secrets found.".padEnd(COLS[0] + COLS[1] + COLS[2] + 2)) + border("│"));
78
78
  }
79
79
  else {
80
80
  secrets.forEach((s, i) => {
@@ -87,16 +87,16 @@ function renderSecrets(secrets, selectedIdx, active) {
87
87
  fmt(s.key, COLS[0]),
88
88
  fmt("*".repeat(8), COLS[1]),
89
89
  fmt(new Date(s.updatedAt).toLocaleDateString(), COLS[2]),
90
- ].join(border(""));
91
- console.log(border("") + row + border(""));
90
+ ].join(border("│"));
91
+ console.log(border("│") + row + border("│"));
92
92
  });
93
93
  }
94
- console.log(border(`└${"".repeat(COLS[0])}┴${"".repeat(COLS[1])}┴${"".repeat(COLS[2])}┘`));
94
+ console.log(border(`└${"─".repeat(COLS[0])}┴${"─".repeat(COLS[1])}┴${"─".repeat(COLS[2])}┘`));
95
95
  }
96
96
  function renderStatus(msg) {
97
- console.log("\n" + chalk_1.default.gray(" ") + chalk_1.default.white(msg));
97
+ console.log("\n" + chalk_1.default.gray(" ● ") + chalk_1.default.white(msg));
98
98
  }
99
- // ─── Main TUI Loop ────────────────────────────────────────────────────────────
99
+ // ─── Main TUI Loop ────────────────────────────────────────────────────────────
100
100
  async function runUI() {
101
101
  let screen = "project";
102
102
  let projects = [];
@@ -110,7 +110,7 @@ async function runUI() {
110
110
  try {
111
111
  projects = await api_1.api.getProjects();
112
112
  status = projects.length > 0
113
- ? `Loaded ${projects.length} projects. Use ↑↓ and Enter to navigate.`
113
+ ? `Loaded ${projects.length} projects. Use ↑↓ and Enter to navigate.`
114
114
  : "No projects found. Try running `xtra login`.";
115
115
  }
116
116
  catch (e) {
@@ -145,7 +145,7 @@ async function runUI() {
145
145
  renderList("ENVIRONMENT", ENVS, envIdx, screen === "env");
146
146
  console.log();
147
147
  if (loading) {
148
- console.log(chalk_1.default.cyan(" Loading secrets..."));
148
+ console.log(chalk_1.default.cyan(" â ‹ Loading secrets..."));
149
149
  }
150
150
  else {
151
151
  renderSecrets(secrets, secretIdx, screen === "secrets");
@@ -164,7 +164,7 @@ async function runUI() {
164
164
  if (process.stdin.isTTY)
165
165
  process.stdin.setRawMode(false);
166
166
  clear();
167
- console.log(chalk_1.default.cyan("Goodbye! 👋"));
167
+ console.log(chalk_1.default.cyan("Goodbye! 👋"));
168
168
  process.exit(0);
169
169
  }
170
170
  // Tab to cycle panels
@@ -210,7 +210,7 @@ async function runUI() {
210
210
  else
211
211
  draw();
212
212
  }
213
- // ─── Commander Command ────────────────────────────────────────────────────────
213
+ // ─── Commander Command ────────────────────────────────────────────────────────
214
214
  exports.uiCommand = new commander_1.Command("ui")
215
215
  .description("Launch interactive TUI secrets dashboard (arrow keys to navigate, Q to quit)")
216
216
  .action(async () => {
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.watchCommand = void 0;
7
7
  /**
8
- * watch.ts Live reload secrets in dev mode
8
+ * watch.ts — Live reload secrets in dev mode
9
9
  *
10
10
  * Polls XtraSecurity Cloud for secret changes at a configurable interval.
11
11
  * When a change is detected, restarts the child process with fresh secrets.
@@ -26,7 +26,7 @@ function hashSecrets(secrets) {
26
26
  }
27
27
  async function startProcess(command, args, secrets, useShell) {
28
28
  if (child) {
29
- process.stdout.write(chalk_1.default.yellow("\n [watch] Secret change detected restarting...\n"));
29
+ process.stdout.write(chalk_1.default.yellow("\n [watch] Secret change detected — restarting...\n"));
30
30
  child.kill("SIGTERM");
31
31
  // Give it 500ms to terminate gracefully
32
32
  await new Promise(r => setTimeout(r, 500));
@@ -50,7 +50,7 @@ async function startProcess(command, args, secrets, useShell) {
50
50
  });
51
51
  }
52
52
  exports.watchCommand = new commander_1.Command("watch")
53
- .description("Live reload auto-restart process when secrets change in cloud")
53
+ .description("Live reload — auto-restart process when secrets change in cloud")
54
54
  .option("-p, --project <id>", "Project ID")
55
55
  .option("-e, --env <environment>", "Environment", "development")
56
56
  .option("-b, --branch <branch>", "Branch", "main")
@@ -69,19 +69,19 @@ Examples:
69
69
  const envMap = { dev: "development", stg: "staging", prod: "production" };
70
70
  env = envMap[env] || env;
71
71
  if (!project)
72
- project = (0, config_1.getConfigValue)("project");
72
+ project = (0, config_1.getRcConfig)().project;
73
73
  if (!project) {
74
74
  console.error(chalk_1.default.red("Error: Project ID required. Use -p <id> or run 'xtra project set'."));
75
75
  process.exit(1);
76
76
  }
77
- // Block production watch too dangerous
77
+ // Block production watch — too dangerous
78
78
  if (env === "production") {
79
- console.error(chalk_1.default.red("xtra watch is not allowed in PRODUCTION for safety reasons."));
79
+ console.error(chalk_1.default.red("âš  xtra watch is not allowed in PRODUCTION for safety reasons."));
80
80
  console.error(chalk_1.default.gray(" Use xtra run for one-shot production injection."));
81
81
  process.exit(1);
82
82
  }
83
83
  const pollMs = Math.max(3, parseInt(interval)) * 1000;
84
- console.log(chalk_1.default.bold(`\n👁 xtra watch watching ${env}/${branch} (every ${interval}s)\n`));
84
+ console.log(chalk_1.default.bold(`\n👁 xtra watch — watching ${env}/${branch} (every ${interval}s)\n`));
85
85
  console.log(chalk_1.default.gray(` Press Ctrl+C to stop.\n`));
86
86
  // Graceful shutdown
87
87
  process.on("SIGINT", () => {
@@ -110,12 +110,12 @@ Examples:
110
110
  await startProcess(command, args, secrets, useShell);
111
111
  }
112
112
  else {
113
- process.stdout.write(chalk_1.default.gray(` [watch] ${new Date().toLocaleTimeString()} no changes\r`));
113
+ process.stdout.write(chalk_1.default.gray(` [watch] ${new Date().toLocaleTimeString()} — no changes\r`));
114
114
  }
115
115
  }
116
116
  catch (e) {
117
117
  process.stdout.write(chalk_1.default.yellow(`\n [watch] Poll failed: ${e.message}\n`));
118
- // Don't exit keep trying
118
+ // Don't exit — keep trying
119
119
  }
120
120
  }, pollMs);
121
121
  });
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getProjectConfig = exports.getAuthToken = exports.clearConfig = exports.setConfig = exports.getConfigValue = exports.getConfig = void 0;
6
+ exports.getRcConfig = exports.getProjectConfig = exports.getAuthToken = exports.clearConfig = exports.setConfig = exports.getConfigValue = exports.getConfig = void 0;
7
7
  const conf_1 = __importDefault(require("conf"));
8
8
  const PRODUCTION_API_URL = "https://xtra-security.vercel.app/api";
9
9
  const config = new conf_1.default({
@@ -47,3 +47,24 @@ const getProjectConfig = async () => {
47
47
  }
48
48
  };
49
49
  exports.getProjectConfig = getProjectConfig;
50
+ /**
51
+ * Reads .xtrarc from the current working directory.
52
+ * Returns project/env/branch with fallback to the global conf store.
53
+ * All commands should use this instead of calling getConfigValue() directly.
54
+ */
55
+ const getRcConfig = () => {
56
+ let rc = {};
57
+ try {
58
+ const rcPath = path_1.default.join(process.cwd(), ".xtrarc");
59
+ if (fs_1.default.existsSync(rcPath)) {
60
+ rc = JSON.parse(fs_1.default.readFileSync(rcPath, "utf-8"));
61
+ }
62
+ }
63
+ catch (_) { }
64
+ return {
65
+ project: rc.project || config.get("project") || "",
66
+ env: rc.env || config.get("env") || "development",
67
+ branch: rc.branch || config.get("branch") || "main",
68
+ };
69
+ };
70
+ exports.getRcConfig = getRcConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xtra-cli",
3
- "version": "0.1.10",
3
+ "version": "0.2.1",
4
4
  "description": "CLI for XtraSecurity Platform",
5
5
  "main": "dist/index.js",
6
6
  "bin": {