@cortexmemory/cli 0.1.1 → 0.22.0

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 (103) hide show
  1. package/README.md +8 -0
  2. package/dist/commands/conversations.d.ts +1 -1
  3. package/dist/commands/conversations.d.ts.map +1 -1
  4. package/dist/commands/conversations.js +57 -27
  5. package/dist/commands/conversations.js.map +1 -1
  6. package/dist/commands/convex.d.ts +1 -1
  7. package/dist/commands/convex.d.ts.map +1 -1
  8. package/dist/commands/convex.js +237 -64
  9. package/dist/commands/convex.js.map +1 -1
  10. package/dist/commands/db.d.ts +1 -1
  11. package/dist/commands/db.d.ts.map +1 -1
  12. package/dist/commands/db.js +59 -109
  13. package/dist/commands/db.js.map +1 -1
  14. package/dist/commands/dev.d.ts +8 -8
  15. package/dist/commands/dev.d.ts.map +1 -1
  16. package/dist/commands/dev.js +734 -513
  17. package/dist/commands/dev.js.map +1 -1
  18. package/dist/commands/facts.d.ts +1 -1
  19. package/dist/commands/facts.d.ts.map +1 -1
  20. package/dist/commands/facts.js +50 -21
  21. package/dist/commands/facts.js.map +1 -1
  22. package/dist/commands/init.d.ts +28 -0
  23. package/dist/commands/init.d.ts.map +1 -0
  24. package/dist/commands/init.js +895 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/memory.d.ts +1 -1
  27. package/dist/commands/memory.d.ts.map +1 -1
  28. package/dist/commands/memory.js +64 -27
  29. package/dist/commands/memory.js.map +1 -1
  30. package/dist/commands/setup.d.ts +4 -5
  31. package/dist/commands/setup.d.ts.map +1 -1
  32. package/dist/commands/setup.js +428 -250
  33. package/dist/commands/setup.js.map +1 -1
  34. package/dist/commands/spaces.d.ts +1 -1
  35. package/dist/commands/spaces.d.ts.map +1 -1
  36. package/dist/commands/spaces.js +100 -43
  37. package/dist/commands/spaces.js.map +1 -1
  38. package/dist/commands/status.d.ts +17 -0
  39. package/dist/commands/status.d.ts.map +1 -0
  40. package/dist/commands/status.js +314 -0
  41. package/dist/commands/status.js.map +1 -0
  42. package/dist/commands/users.d.ts +1 -1
  43. package/dist/commands/users.d.ts.map +1 -1
  44. package/dist/commands/users.js +80 -42
  45. package/dist/commands/users.js.map +1 -1
  46. package/dist/index.js +42 -14
  47. package/dist/index.js.map +1 -1
  48. package/dist/types.d.ts +11 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/utils/__tests__/client.test.d.ts +5 -0
  51. package/dist/utils/__tests__/client.test.d.ts.map +1 -0
  52. package/dist/utils/__tests__/client.test.js +88 -0
  53. package/dist/utils/__tests__/client.test.js.map +1 -0
  54. package/dist/utils/__tests__/env-file.test.d.ts +7 -0
  55. package/dist/utils/__tests__/env-file.test.d.ts.map +1 -0
  56. package/dist/utils/__tests__/env-file.test.js +196 -0
  57. package/dist/utils/__tests__/env-file.test.js.map +1 -0
  58. package/dist/utils/__tests__/shell.test.d.ts +7 -0
  59. package/dist/utils/__tests__/shell.test.d.ts.map +1 -0
  60. package/dist/utils/__tests__/shell.test.js +89 -0
  61. package/dist/utils/__tests__/shell.test.js.map +1 -0
  62. package/dist/utils/config.d.ts.map +1 -1
  63. package/dist/utils/config.js +12 -39
  64. package/dist/utils/config.js.map +1 -1
  65. package/dist/utils/deployment-selector.d.ts +50 -0
  66. package/dist/utils/deployment-selector.d.ts.map +1 -0
  67. package/dist/utils/deployment-selector.js +129 -0
  68. package/dist/utils/deployment-selector.js.map +1 -0
  69. package/dist/utils/init/convex-setup.d.ts +30 -0
  70. package/dist/utils/init/convex-setup.d.ts.map +1 -0
  71. package/dist/utils/init/convex-setup.js +225 -0
  72. package/dist/utils/init/convex-setup.js.map +1 -0
  73. package/dist/utils/init/env-generator.d.ts +32 -0
  74. package/dist/utils/init/env-generator.d.ts.map +1 -0
  75. package/dist/utils/init/env-generator.js +210 -0
  76. package/dist/utils/init/env-generator.js.map +1 -0
  77. package/dist/utils/init/file-operations.d.ts +22 -0
  78. package/dist/utils/init/file-operations.d.ts.map +1 -0
  79. package/dist/utils/init/file-operations.js +211 -0
  80. package/dist/utils/init/file-operations.js.map +1 -0
  81. package/dist/utils/init/graph-setup.d.ts +35 -0
  82. package/dist/utils/init/graph-setup.d.ts.map +1 -0
  83. package/dist/utils/init/graph-setup.js +413 -0
  84. package/dist/utils/init/graph-setup.js.map +1 -0
  85. package/dist/utils/init/index.d.ts +11 -0
  86. package/dist/utils/init/index.d.ts.map +1 -0
  87. package/dist/utils/init/index.js +11 -0
  88. package/dist/utils/init/index.js.map +1 -0
  89. package/dist/utils/init/types.d.ts +73 -0
  90. package/dist/utils/init/types.d.ts.map +1 -0
  91. package/dist/utils/init/types.js +5 -0
  92. package/dist/utils/init/types.js.map +1 -0
  93. package/dist/utils/shell.d.ts +60 -0
  94. package/dist/utils/shell.d.ts.map +1 -0
  95. package/dist/utils/shell.js +188 -0
  96. package/dist/utils/shell.js.map +1 -0
  97. package/package.json +25 -20
  98. package/templates/basic/README.md +105 -0
  99. package/templates/basic/dev-runner.mjs +215 -0
  100. package/templates/basic/package-lock.json +1263 -0
  101. package/templates/basic/package.json +22 -0
  102. package/templates/basic/src/index.ts +85 -0
  103. package/templates/basic/tsconfig.json +17 -0
@@ -1,8 +1,7 @@
1
1
  /**
2
- * Setup and Configuration Commands
2
+ * Configuration Commands
3
3
  *
4
- * Commands for setting up and configuring the CLI:
5
- * - setup: Interactive setup wizard
4
+ * Commands for managing CLI configuration:
6
5
  * - config: Configuration management
7
6
  */
8
7
  import prompts from "prompts";
@@ -13,138 +12,13 @@ import { testConnection } from "../utils/client.js";
13
12
  import { printSuccess, printError, printWarning, printInfo, printSection, formatOutput, } from "../utils/formatting.js";
14
13
  import { validateUrl } from "../utils/validation.js";
15
14
  import { addDeploymentToEnv, removeDeploymentFromEnv, getDeploymentEnvKeys, } from "../utils/env-file.js";
15
+ import { getCurrentDeployment, setCurrentDeployment, clearCurrentDeployment, } from "../utils/deployment-selector.js";
16
16
  import { existsSync } from "fs";
17
- import { join } from "path";
17
+ import { join, resolve } from "path";
18
18
  /**
19
- * Register setup and config commands
19
+ * Register config commands
20
20
  */
21
- export function registerSetupCommands(program, _config) {
22
- // setup command
23
- program
24
- .command("setup")
25
- .description("Interactive setup wizard")
26
- .option("--auto", "Auto-configure from environment variables", false)
27
- .option("--local", "Set up local Convex development", false)
28
- .option("--cloud", "Set up cloud Convex deployment", false)
29
- .action(async (options) => {
30
- console.log();
31
- console.log(pc.bold(pc.cyan("🧠 Cortex CLI Setup")));
32
- console.log(pc.dim("Configure your Cortex Memory deployment\n"));
33
- try {
34
- let config = await loadConfig();
35
- if (options.auto) {
36
- // Auto mode: configure from environment variables
37
- await autoSetup(config);
38
- return;
39
- }
40
- // Interactive setup
41
- const setupMode = await prompts({
42
- type: "select",
43
- name: "mode",
44
- message: "What would you like to set up?",
45
- choices: [
46
- {
47
- title: "Local development",
48
- description: "Configure local Convex instance",
49
- value: "local",
50
- },
51
- {
52
- title: "Cloud deployment",
53
- description: "Configure Convex cloud deployment",
54
- value: "cloud",
55
- },
56
- {
57
- title: "Both",
58
- description: "Configure local and cloud deployments",
59
- value: "both",
60
- },
61
- {
62
- title: "View current configuration",
63
- description: "Show existing configuration",
64
- value: "view",
65
- },
66
- ],
67
- initial: options.local ? 0 : options.cloud ? 1 : 2,
68
- });
69
- if (!setupMode.mode) {
70
- printWarning("Setup cancelled");
71
- return;
72
- }
73
- if (setupMode.mode === "view") {
74
- await showConfiguration(config);
75
- return;
76
- }
77
- // Set up local deployment
78
- if (setupMode.mode === "local" || setupMode.mode === "both") {
79
- config = await setupLocalDeployment(config);
80
- }
81
- // Set up cloud deployment
82
- if (setupMode.mode === "cloud" || setupMode.mode === "both") {
83
- config = await setupCloudDeployment(config);
84
- }
85
- // Set default deployment
86
- if (setupMode.mode === "both") {
87
- const defaultChoice = await prompts({
88
- type: "select",
89
- name: "default",
90
- message: "Which deployment should be the default?",
91
- choices: [
92
- { title: "Local", value: "local" },
93
- { title: "Cloud", value: "cloud" },
94
- ],
95
- });
96
- if (defaultChoice.default) {
97
- config.default = defaultChoice.default;
98
- }
99
- }
100
- else {
101
- config.default = setupMode.mode;
102
- }
103
- // Set output format
104
- const formatChoice = await prompts({
105
- type: "select",
106
- name: "format",
107
- message: "Preferred output format?",
108
- choices: [
109
- { title: "Table (human-readable)", value: "table" },
110
- { title: "JSON (machine-readable)", value: "json" },
111
- ],
112
- initial: 0,
113
- });
114
- if (formatChoice.format) {
115
- config.format = formatChoice.format;
116
- }
117
- // Confirm dangerous operations
118
- const confirmChoice = await prompts({
119
- type: "confirm",
120
- name: "confirm",
121
- message: "Require confirmation for dangerous operations (delete, clear)?",
122
- initial: true,
123
- });
124
- config.confirmDangerous = confirmChoice.confirm ?? true;
125
- // Save configuration
126
- await saveUserConfig(config);
127
- console.log();
128
- printSuccess(`Configuration saved to ${getUserConfigPath()}`);
129
- // Test connection
130
- const testChoice = await prompts({
131
- type: "confirm",
132
- name: "test",
133
- message: "Would you like to test the connection?",
134
- initial: true,
135
- });
136
- if (testChoice.test) {
137
- await testAndShowConnection(config, config.default);
138
- }
139
- console.log();
140
- printSuccess("Setup complete! 🎉");
141
- printInfo("Run 'cortex --help' to see available commands");
142
- }
143
- catch (error) {
144
- printError(error instanceof Error ? error.message : "Setup failed");
145
- process.exit(1);
146
- }
147
- });
21
+ export function registerConfigCommands(program, _config) {
148
22
  // config command group
149
23
  const configCmd = program
150
24
  .command("config")
@@ -166,6 +40,75 @@ export function registerSetupCommands(program, _config) {
166
40
  process.exit(1);
167
41
  }
168
42
  });
43
+ // config list - Table view of deployments
44
+ configCmd
45
+ .command("list")
46
+ .description("List all deployments in table format")
47
+ .action(async () => {
48
+ try {
49
+ const config = await loadConfig();
50
+ const deployments = Object.entries(config.deployments);
51
+ if (deployments.length === 0) {
52
+ console.log(pc.yellow("\n No deployments configured\n"));
53
+ printInfo("To get started:");
54
+ console.log(pc.dim(" • Run 'cortex init' to create a new project"));
55
+ console.log(pc.dim(" • Run 'cortex config add-deployment' to add an existing deployment"));
56
+ return;
57
+ }
58
+ console.log();
59
+ // Table header
60
+ const nameWidth = 20;
61
+ const statusWidth = 10;
62
+ const urlWidth = 40;
63
+ const keyWidth = 6;
64
+ const pathWidth = 35;
65
+ console.log(pc.bold(" " +
66
+ "NAME".padEnd(nameWidth) +
67
+ "STATUS".padEnd(statusWidth) +
68
+ "URL".padEnd(urlWidth) +
69
+ "KEY".padEnd(keyWidth) +
70
+ "PROJECT PATH"));
71
+ console.log(pc.dim(" " + "─".repeat(nameWidth + statusWidth + urlWidth + keyWidth + pathWidth)));
72
+ for (const [name, deployment] of deployments) {
73
+ const isDefault = name === config.default;
74
+ // Default deployment is implicitly enabled; others check enabled field
75
+ const isEnabled = deployment.enabled === true || (deployment.enabled === undefined && isDefault);
76
+ const prefix = isDefault ? pc.green("→ ") : " ";
77
+ // Pad BEFORE applying color to avoid ANSI escape code length issues
78
+ const namePadded = name.padEnd(nameWidth - 2);
79
+ const nameDisplay = isDefault ? pc.cyan(namePadded) : namePadded;
80
+ const statusText = isEnabled ? "enabled" : "disabled";
81
+ const statusPadded = statusText.padEnd(statusWidth);
82
+ const statusDisplay = isEnabled ? pc.green(statusPadded) : pc.dim(statusPadded);
83
+ const keyText = deployment.key ? "yes" : "no";
84
+ const keyPadded = keyText.padEnd(keyWidth);
85
+ const keyStatus = deployment.key ? pc.green(keyPadded) : pc.dim(keyPadded);
86
+ const urlDisplay = (deployment.url.length > urlWidth - 2
87
+ ? deployment.url.substring(0, urlWidth - 5) + "..."
88
+ : deployment.url).padEnd(urlWidth);
89
+ const pathDisplay = deployment.projectPath
90
+ ? (deployment.projectPath.length > pathWidth - 2
91
+ ? "..." + deployment.projectPath.slice(-(pathWidth - 5))
92
+ : deployment.projectPath)
93
+ : pc.dim("--");
94
+ console.log(prefix +
95
+ nameDisplay +
96
+ statusDisplay +
97
+ urlDisplay +
98
+ keyStatus +
99
+ pathDisplay);
100
+ }
101
+ console.log();
102
+ if (config.default) {
103
+ console.log(pc.dim(` Default: ${config.default} (→) • Enabled deployments started with 'cortex start'`));
104
+ }
105
+ console.log();
106
+ }
107
+ catch (error) {
108
+ printError(error instanceof Error ? error.message : "Failed to load config");
109
+ process.exit(1);
110
+ }
111
+ });
169
112
  // config set
170
113
  configCmd
171
114
  .command("set <key> <value>")
@@ -196,6 +139,101 @@ export function registerSetupCommands(program, _config) {
196
139
  process.exit(1);
197
140
  }
198
141
  });
142
+ // config set-path - Set project path for a deployment
143
+ configCmd
144
+ .command("set-path <deployment> [path]")
145
+ .description("Set project path for a deployment (enables 'cortex start -d <name>' from anywhere)")
146
+ .action(async (deploymentName, pathArg) => {
147
+ try {
148
+ const config = await loadConfig();
149
+ if (!config.deployments[deploymentName]) {
150
+ printError(`Deployment "${deploymentName}" not found`);
151
+ const names = Object.keys(config.deployments);
152
+ if (names.length > 0) {
153
+ printInfo(`Available deployments: ${names.join(", ")}`);
154
+ }
155
+ process.exit(1);
156
+ }
157
+ // If no path provided, use current directory
158
+ const projectPath = pathArg ? resolve(pathArg) : process.cwd();
159
+ // Verify path exists
160
+ if (!existsSync(projectPath)) {
161
+ printError(`Path does not exist: ${projectPath}`);
162
+ process.exit(1);
163
+ }
164
+ // Update deployment with projectPath
165
+ config.deployments[deploymentName] = {
166
+ ...config.deployments[deploymentName],
167
+ projectPath,
168
+ };
169
+ await saveUserConfig(config);
170
+ printSuccess(`Set project path for "${deploymentName}"`);
171
+ console.log(pc.dim(` Path: ${projectPath}`));
172
+ console.log();
173
+ printInfo(`You can now run: cortex start -d ${deploymentName}`);
174
+ console.log(pc.dim(" This will work from any directory"));
175
+ }
176
+ catch (error) {
177
+ printError(error instanceof Error ? error.message : "Failed to set project path");
178
+ process.exit(1);
179
+ }
180
+ });
181
+ // config enable - Enable a deployment for `cortex start`
182
+ configCmd
183
+ .command("enable <deployment>")
184
+ .description("Enable a deployment (will be started with 'cortex start')")
185
+ .action(async (deploymentName) => {
186
+ try {
187
+ const config = await loadConfig();
188
+ if (!config.deployments[deploymentName]) {
189
+ printError(`Deployment "${deploymentName}" not found`);
190
+ const names = Object.keys(config.deployments);
191
+ if (names.length > 0) {
192
+ printInfo(`Available deployments: ${names.join(", ")}`);
193
+ }
194
+ process.exit(1);
195
+ }
196
+ config.deployments[deploymentName] = {
197
+ ...config.deployments[deploymentName],
198
+ enabled: true,
199
+ };
200
+ await saveUserConfig(config);
201
+ printSuccess(`Enabled deployment "${deploymentName}"`);
202
+ console.log(pc.dim(" Will be started with 'cortex start'"));
203
+ }
204
+ catch (error) {
205
+ printError(error instanceof Error ? error.message : "Failed to enable deployment");
206
+ process.exit(1);
207
+ }
208
+ });
209
+ // config disable - Disable a deployment
210
+ configCmd
211
+ .command("disable <deployment>")
212
+ .description("Disable a deployment (will not be started with 'cortex start')")
213
+ .action(async (deploymentName) => {
214
+ try {
215
+ const config = await loadConfig();
216
+ if (!config.deployments[deploymentName]) {
217
+ printError(`Deployment "${deploymentName}" not found`);
218
+ const names = Object.keys(config.deployments);
219
+ if (names.length > 0) {
220
+ printInfo(`Available deployments: ${names.join(", ")}`);
221
+ }
222
+ process.exit(1);
223
+ }
224
+ config.deployments[deploymentName] = {
225
+ ...config.deployments[deploymentName],
226
+ enabled: false,
227
+ };
228
+ await saveUserConfig(config);
229
+ printSuccess(`Disabled deployment "${deploymentName}"`);
230
+ console.log(pc.dim(" Will NOT be started with 'cortex start'"));
231
+ }
232
+ catch (error) {
233
+ printError(error instanceof Error ? error.message : "Failed to disable deployment");
234
+ process.exit(1);
235
+ }
236
+ });
199
237
  // config deployments
200
238
  configCmd
201
239
  .command("deployments")
@@ -431,6 +469,176 @@ export function registerSetupCommands(program, _config) {
431
469
  process.exit(1);
432
470
  }
433
471
  });
472
+ // config set-key
473
+ configCmd
474
+ .command("set-key [deployment]")
475
+ .description("Set or update the deploy key for a deployment")
476
+ .option("-k, --key <key>", "Deploy key (will prompt if not provided)")
477
+ .option("--json-only", "Only update ~/.cortexrc (skip .env.local)", false)
478
+ .action(async (deploymentArg, options) => {
479
+ try {
480
+ const config = await loadConfig();
481
+ let name = deploymentArg;
482
+ // If no name provided, show interactive selection
483
+ if (!name) {
484
+ const deploymentNames = Object.keys(config.deployments);
485
+ if (deploymentNames.length === 0) {
486
+ printWarning("No deployments configured");
487
+ console.log(pc.dim(" Run 'cortex config add-deployment' to add one\n"));
488
+ return;
489
+ }
490
+ const response = await prompts({
491
+ type: "select",
492
+ name: "name",
493
+ message: "Select deployment to set key for:",
494
+ choices: deploymentNames.map((n) => {
495
+ const dep = config.deployments[n];
496
+ const keyStatus = dep.key ? pc.green("(key set)") : pc.dim("(no key)");
497
+ return {
498
+ title: `${n} ${keyStatus}`,
499
+ description: dep.url,
500
+ value: n,
501
+ };
502
+ }),
503
+ });
504
+ if (!response.name) {
505
+ printWarning("Cancelled");
506
+ return;
507
+ }
508
+ name = response.name;
509
+ }
510
+ if (!config.deployments[name]) {
511
+ printError(`Deployment "${name}" not found`);
512
+ console.log(pc.dim(" Run 'cortex config list' to see available deployments\n"));
513
+ process.exit(1);
514
+ }
515
+ // Get key from option or prompt
516
+ let key = options.key;
517
+ if (!key) {
518
+ const response = await prompts({
519
+ type: "password",
520
+ name: "key",
521
+ message: `Enter deploy key for "${name}":`,
522
+ validate: (v) => v.length > 0 || "Key cannot be empty",
523
+ });
524
+ if (!response.key) {
525
+ printWarning("Cancelled");
526
+ return;
527
+ }
528
+ key = response.key;
529
+ }
530
+ // Update the deployment with the new key
531
+ const deployment = config.deployments[name];
532
+ deployment.key = key;
533
+ await updateDeployment(name, deployment);
534
+ // Also update .env.local (unless --json-only)
535
+ if (!options.jsonOnly) {
536
+ await addDeploymentToEnv(name, deployment.url, key);
537
+ const envKeys = getDeploymentEnvKeys(name);
538
+ printSuccess(`Set deploy key for "${name}"`);
539
+ printInfo(`Updated .env.local: ${envKeys.keyKey}=***`);
540
+ }
541
+ else {
542
+ printSuccess(`Set deploy key for "${name}" in ~/.cortexrc`);
543
+ }
544
+ }
545
+ catch (error) {
546
+ printError(error instanceof Error ? error.message : "Failed to set key");
547
+ process.exit(1);
548
+ }
549
+ });
550
+ // config set-url
551
+ configCmd
552
+ .command("set-url [deployment]")
553
+ .description("Set or update the URL for a deployment")
554
+ .option("-u, --url <url>", "Deployment URL (will prompt if not provided)")
555
+ .option("--json-only", "Only update ~/.cortexrc (skip .env.local)", false)
556
+ .action(async (deploymentArg, options) => {
557
+ try {
558
+ const config = await loadConfig();
559
+ let name = deploymentArg;
560
+ // If no name provided, show interactive selection
561
+ if (!name) {
562
+ const deploymentNames = Object.keys(config.deployments);
563
+ if (deploymentNames.length === 0) {
564
+ printWarning("No deployments configured");
565
+ console.log(pc.dim(" Run 'cortex config add-deployment' to add one\n"));
566
+ return;
567
+ }
568
+ const response = await prompts({
569
+ type: "select",
570
+ name: "name",
571
+ message: "Select deployment to set URL for:",
572
+ choices: deploymentNames.map((n) => {
573
+ const dep = config.deployments[n];
574
+ return {
575
+ title: n,
576
+ description: dep.url,
577
+ value: n,
578
+ };
579
+ }),
580
+ });
581
+ if (!response.name) {
582
+ printWarning("Cancelled");
583
+ return;
584
+ }
585
+ name = response.name;
586
+ }
587
+ if (!config.deployments[name]) {
588
+ printError(`Deployment "${name}" not found`);
589
+ console.log(pc.dim(" Run 'cortex config list' to see available deployments\n"));
590
+ process.exit(1);
591
+ }
592
+ // Get URL from option or prompt
593
+ let url = options.url;
594
+ if (!url) {
595
+ const currentUrl = config.deployments[name].url;
596
+ const response = await prompts({
597
+ type: "text",
598
+ name: "url",
599
+ message: `Enter URL for "${name}":`,
600
+ initial: currentUrl,
601
+ validate: (v) => {
602
+ try {
603
+ new URL(v);
604
+ return true;
605
+ }
606
+ catch {
607
+ return "Please enter a valid URL";
608
+ }
609
+ },
610
+ });
611
+ if (!response.url) {
612
+ printWarning("Cancelled");
613
+ return;
614
+ }
615
+ url = response.url;
616
+ }
617
+ // Validate URL
618
+ validateUrl(url);
619
+ // Update the deployment with the new URL
620
+ const deployment = config.deployments[name];
621
+ const oldUrl = deployment.url;
622
+ deployment.url = url;
623
+ await updateDeployment(name, deployment);
624
+ // Also update .env.local (unless --json-only)
625
+ if (!options.jsonOnly) {
626
+ await addDeploymentToEnv(name, url, deployment.key);
627
+ const envKeys = getDeploymentEnvKeys(name);
628
+ printSuccess(`Updated URL for "${name}"`);
629
+ printInfo(`${oldUrl} → ${url}`);
630
+ printInfo(`Updated .env.local: ${envKeys.urlKey}=${url}`);
631
+ }
632
+ else {
633
+ printSuccess(`Updated URL for "${name}" in ~/.cortexrc`);
634
+ printInfo(`${oldUrl} → ${url}`);
635
+ }
636
+ }
637
+ catch (error) {
638
+ printError(error instanceof Error ? error.message : "Failed to set URL");
639
+ process.exit(1);
640
+ }
641
+ });
434
642
  // config path
435
643
  configCmd
436
644
  .command("path")
@@ -484,134 +692,88 @@ export function registerSetupCommands(program, _config) {
484
692
  }
485
693
  }
486
694
  const defaultConfig = {
487
- deployments: {
488
- local: {
489
- url: "http://127.0.0.1:3210",
490
- deployment: "anonymous:anonymous-cortex-sdk-local",
491
- },
492
- },
493
- default: "local",
695
+ deployments: {},
696
+ default: "",
494
697
  format: "table",
495
698
  confirmDangerous: true,
496
699
  };
497
700
  await saveUserConfig(defaultConfig);
498
701
  printSuccess("Configuration reset to defaults");
702
+ console.log();
703
+ printInfo("No deployments configured. To get started:");
704
+ console.log(pc.dim(" • Run 'cortex init' to create a new project"));
705
+ console.log(pc.dim(" • Run 'cortex config add-deployment' to add an existing deployment"));
499
706
  }
500
707
  catch (error) {
501
708
  printError(error instanceof Error ? error.message : "Reset failed");
502
709
  process.exit(1);
503
710
  }
504
711
  });
505
- }
506
- /**
507
- * Auto-configure from environment variables
508
- */
509
- async function autoSetup(config) {
510
- const spinner = ora("Auto-configuring from environment...").start();
511
- let hasChanges = false;
512
- // Check for local config
513
- const localUrl = process.env.LOCAL_CONVEX_URL;
514
- const localDeployment = process.env.LOCAL_CONVEX_DEPLOYMENT;
515
- if (localUrl) {
516
- config.deployments.local = {
517
- url: localUrl,
518
- deployment: localDeployment,
519
- };
520
- hasChanges = true;
521
- }
522
- // Check for cloud config
523
- const cloudUrl = process.env.CLOUD_CONVEX_URL ?? process.env.CONVEX_URL;
524
- const cloudKey = process.env.CLOUD_CONVEX_DEPLOY_KEY ?? process.env.CONVEX_DEPLOY_KEY;
525
- if (cloudUrl &&
526
- !cloudUrl.includes("localhost") &&
527
- !cloudUrl.includes("127.0.0.1")) {
528
- config.deployments.cloud = {
529
- url: cloudUrl,
530
- key: cloudKey,
531
- };
532
- hasChanges = true;
533
- }
534
- spinner.stop();
535
- if (!hasChanges) {
536
- printWarning("No environment variables found to configure");
537
- printInfo("Set LOCAL_CONVEX_URL or CONVEX_URL environment variables");
538
- return;
539
- }
540
- await saveUserConfig(config);
541
- printSuccess("Auto-configured from environment variables");
542
- await showConfiguration(config);
543
- }
544
- /**
545
- * Set up local deployment
546
- */
547
- async function setupLocalDeployment(config) {
548
- console.log();
549
- console.log(pc.bold("Local Development Setup"));
550
- console.log(pc.dim("Configure connection to local Convex instance\n"));
551
- const urlPrompt = await prompts({
552
- type: "text",
553
- name: "url",
554
- message: "Local Convex URL:",
555
- initial: config.deployments.local?.url ?? "http://127.0.0.1:3210",
556
- validate: (value) => {
557
- try {
558
- new URL(value);
559
- return true;
560
- }
561
- catch {
562
- return "Please enter a valid URL";
563
- }
564
- },
565
- });
566
- const deploymentPrompt = await prompts({
567
- type: "text",
568
- name: "deployment",
569
- message: "Local deployment name (optional):",
570
- initial: config.deployments.local?.deployment ??
571
- "anonymous:anonymous-cortex-sdk-local",
572
- });
573
- config.deployments.local = {
574
- url: urlPrompt.url,
575
- deployment: deploymentPrompt.deployment || undefined,
576
- };
577
- return config;
578
- }
579
- /**
580
- * Set up cloud deployment
581
- */
582
- async function setupCloudDeployment(config) {
583
- console.log();
584
- console.log(pc.bold("Cloud Deployment Setup"));
585
- console.log(pc.dim("Configure connection to Convex cloud\n"));
586
- const urlPrompt = await prompts({
587
- type: "text",
588
- name: "url",
589
- message: "Convex cloud URL:",
590
- initial: config.deployments.cloud?.url ?? "https://your-deployment.convex.cloud",
591
- validate: (value) => {
592
- try {
593
- const url = new URL(value);
594
- if (!url.hostname.includes("convex")) {
595
- return "URL should be a Convex cloud URL";
712
+ // Top-level 'use' command for quick deployment switching
713
+ program
714
+ .command("use [deployment]")
715
+ .description("Set current deployment for all commands (session context)")
716
+ .option("--clear", "Clear the current deployment setting")
717
+ .action(async (deploymentName, options) => {
718
+ try {
719
+ const config = await loadConfig();
720
+ const deployments = Object.keys(config.deployments);
721
+ // Handle --clear flag
722
+ if (options.clear) {
723
+ await clearCurrentDeployment();
724
+ printSuccess("Cleared current deployment");
725
+ console.log(pc.dim(" Commands will now prompt for deployment selection\n"));
726
+ return;
727
+ }
728
+ // If no deployment specified, show current and list available
729
+ if (!deploymentName) {
730
+ const current = await getCurrentDeployment();
731
+ console.log();
732
+ if (current && config.deployments[current]) {
733
+ console.log(pc.bold(" Current deployment: ") + pc.cyan(current));
734
+ }
735
+ else if (current) {
736
+ console.log(pc.yellow(` Current deployment "${current}" no longer exists`));
737
+ }
738
+ else {
739
+ console.log(pc.dim(" No current deployment set"));
740
+ }
741
+ console.log();
742
+ console.log(pc.bold(" Available deployments:"));
743
+ if (deployments.length === 0) {
744
+ console.log(pc.yellow(" No deployments configured"));
745
+ }
746
+ else {
747
+ for (const name of deployments) {
748
+ const isCurrent = name === current;
749
+ const isDefault = name === config.default;
750
+ const prefix = isCurrent ? pc.green("→") : " ";
751
+ const suffix = isDefault ? pc.dim(" (default)") : "";
752
+ console.log(` ${prefix} ${pc.cyan(name)}${suffix}`);
753
+ }
596
754
  }
597
- return true;
755
+ console.log();
756
+ console.log(pc.dim(" Usage: cortex use <deployment>"));
757
+ console.log(pc.dim(" cortex use --clear"));
758
+ console.log();
759
+ return;
598
760
  }
599
- catch {
600
- return "Please enter a valid URL";
761
+ // Validate deployment exists
762
+ if (!config.deployments[deploymentName]) {
763
+ printError(`Deployment "${deploymentName}" not found`);
764
+ console.log(pc.dim(` Available: ${deployments.join(", ")}\n`));
765
+ process.exit(1);
601
766
  }
602
- },
603
- });
604
- const keyPrompt = await prompts({
605
- type: "password",
606
- name: "key",
607
- message: "Convex deploy key (optional):",
608
- initial: config.deployments.cloud?.key ?? "",
767
+ // Set the current deployment
768
+ await setCurrentDeployment(deploymentName);
769
+ printSuccess(`Now using: ${deploymentName}`);
770
+ console.log(pc.dim(` All commands will target this deployment until changed\n`));
771
+ }
772
+ catch (error) {
773
+ printError(error instanceof Error ? error.message : "Failed to set deployment");
774
+ process.exit(1);
775
+ }
609
776
  });
610
- config.deployments.cloud = {
611
- url: urlPrompt.url,
612
- key: keyPrompt.key || undefined,
613
- };
614
- return config;
615
777
  }
616
778
  /**
617
779
  * Show current configuration
@@ -637,11 +799,27 @@ async function showConfiguration(config, format) {
637
799
  "Confirm dangerous ops": config.confirmDangerous ? "Yes" : "No",
638
800
  });
639
801
  console.log(" Deployments:");
640
- for (const [name, deployment] of Object.entries(config.deployments)) {
641
- const isDefault = name === config.default;
642
- const prefix = isDefault ? pc.green("→") : " ";
643
- const keyStatus = deployment.key ? pc.green("(key set)") : "";
644
- console.log(` ${prefix} ${pc.cyan(name)}: ${deployment.url} ${keyStatus}`);
802
+ const deploymentEntries = Object.entries(config.deployments);
803
+ if (deploymentEntries.length === 0) {
804
+ console.log(pc.yellow(" No deployments configured"));
805
+ console.log();
806
+ printInfo("To get started:");
807
+ console.log(pc.dim(" • Run 'cortex init' to create a new project"));
808
+ console.log(pc.dim(" • Run 'cortex config add-deployment' to add an existing deployment"));
809
+ }
810
+ else {
811
+ for (const [name, deployment] of deploymentEntries) {
812
+ const isDefault = name === config.default;
813
+ // Default deployment is implicitly enabled; others check enabled field
814
+ const isEnabled = deployment.enabled === true || (deployment.enabled === undefined && isDefault);
815
+ const prefix = isDefault ? pc.green("→") : " ";
816
+ const statusBadge = isEnabled ? pc.green("[enabled]") : pc.dim("[disabled]");
817
+ const keyStatus = deployment.key ? pc.green("(key set)") : "";
818
+ console.log(` ${prefix} ${pc.cyan(name)}: ${deployment.url} ${statusBadge} ${keyStatus}`);
819
+ if (deployment.projectPath) {
820
+ console.log(pc.dim(` Project: ${deployment.projectPath}`));
821
+ }
822
+ }
645
823
  }
646
824
  console.log();
647
825
  }