@getjack/jack 0.1.2 → 0.1.3

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 (91) hide show
  1. package/package.json +54 -47
  2. package/src/commands/agents.ts +145 -10
  3. package/src/commands/down.ts +110 -102
  4. package/src/commands/feedback.ts +189 -0
  5. package/src/commands/init.ts +8 -12
  6. package/src/commands/login.ts +88 -0
  7. package/src/commands/logout.ts +14 -0
  8. package/src/commands/logs.ts +21 -0
  9. package/src/commands/mcp.ts +134 -7
  10. package/src/commands/new.ts +43 -17
  11. package/src/commands/open.ts +13 -6
  12. package/src/commands/projects.ts +269 -143
  13. package/src/commands/secrets.ts +413 -0
  14. package/src/commands/services.ts +96 -123
  15. package/src/commands/ship.ts +5 -1
  16. package/src/commands/whoami.ts +31 -0
  17. package/src/index.ts +218 -144
  18. package/src/lib/agent-files.ts +34 -0
  19. package/src/lib/agents.ts +390 -22
  20. package/src/lib/asset-hash.ts +50 -0
  21. package/src/lib/auth/client.ts +115 -0
  22. package/src/lib/auth/constants.ts +5 -0
  23. package/src/lib/auth/guard.ts +57 -0
  24. package/src/lib/auth/index.ts +18 -0
  25. package/src/lib/auth/store.ts +54 -0
  26. package/src/lib/binding-validator.ts +136 -0
  27. package/src/lib/build-helper.ts +211 -0
  28. package/src/lib/cloudflare-api.ts +24 -0
  29. package/src/lib/config.ts +5 -6
  30. package/src/lib/control-plane.ts +295 -0
  31. package/src/lib/debug.ts +3 -1
  32. package/src/lib/deploy-mode.ts +93 -0
  33. package/src/lib/deploy-upload.ts +92 -0
  34. package/src/lib/errors.ts +2 -0
  35. package/src/lib/github.ts +31 -1
  36. package/src/lib/hooks.ts +4 -12
  37. package/src/lib/intent.ts +88 -0
  38. package/src/lib/jsonc.ts +125 -0
  39. package/src/lib/local-paths.test.ts +902 -0
  40. package/src/lib/local-paths.ts +258 -0
  41. package/src/lib/managed-deploy.ts +175 -0
  42. package/src/lib/managed-down.ts +159 -0
  43. package/src/lib/mcp-config.ts +55 -34
  44. package/src/lib/names.ts +9 -29
  45. package/src/lib/project-operations.ts +676 -249
  46. package/src/lib/project-resolver.ts +476 -0
  47. package/src/lib/registry.ts +76 -37
  48. package/src/lib/resources.ts +196 -0
  49. package/src/lib/schema.ts +30 -1
  50. package/src/lib/storage/file-filter.ts +1 -0
  51. package/src/lib/storage/index.ts +5 -1
  52. package/src/lib/telemetry.ts +14 -0
  53. package/src/lib/tty.ts +15 -0
  54. package/src/lib/zip-packager.ts +255 -0
  55. package/src/mcp/resources/index.ts +8 -2
  56. package/src/mcp/server.ts +32 -4
  57. package/src/mcp/tools/index.ts +35 -13
  58. package/src/mcp/types.ts +6 -0
  59. package/src/mcp/utils.ts +1 -1
  60. package/src/templates/index.ts +42 -4
  61. package/src/templates/types.ts +13 -0
  62. package/templates/CLAUDE.md +166 -0
  63. package/templates/api/.jack.json +4 -0
  64. package/templates/api/bun.lock +1 -0
  65. package/templates/api/wrangler.jsonc +5 -0
  66. package/templates/hello/.jack.json +28 -0
  67. package/templates/hello/package.json +10 -0
  68. package/templates/hello/src/index.ts +11 -0
  69. package/templates/hello/tsconfig.json +11 -0
  70. package/templates/hello/wrangler.jsonc +5 -0
  71. package/templates/miniapp/.jack.json +15 -4
  72. package/templates/miniapp/bun.lock +135 -40
  73. package/templates/miniapp/index.html +1 -0
  74. package/templates/miniapp/package.json +3 -1
  75. package/templates/miniapp/public/.well-known/farcaster.json +7 -5
  76. package/templates/miniapp/public/icon.png +0 -0
  77. package/templates/miniapp/public/og.png +0 -0
  78. package/templates/miniapp/schema.sql +8 -0
  79. package/templates/miniapp/src/App.tsx +254 -3
  80. package/templates/miniapp/src/components/ShareSheet.tsx +147 -0
  81. package/templates/miniapp/src/hooks/useAI.ts +35 -0
  82. package/templates/miniapp/src/hooks/useGuestbook.ts +11 -1
  83. package/templates/miniapp/src/hooks/useShare.ts +76 -0
  84. package/templates/miniapp/src/index.css +15 -0
  85. package/templates/miniapp/src/lib/api.ts +2 -1
  86. package/templates/miniapp/src/worker.ts +515 -1
  87. package/templates/miniapp/wrangler.jsonc +15 -3
  88. package/LICENSE +0 -190
  89. package/README.md +0 -55
  90. package/src/commands/cloud.ts +0 -230
  91. package/templates/api/wrangler.toml +0 -3
package/package.json CHANGED
@@ -1,49 +1,56 @@
1
1
  {
2
- "name": "@getjack/jack",
3
- "version": "0.1.2",
4
- "description": "CLI for vibecoders to rapidly deploy to Cloudflare Workers",
5
- "license": "Apache-2.0",
6
- "type": "module",
7
- "bin": {
8
- "jack": "./src/index.ts"
9
- },
10
- "files": [
11
- "src",
12
- "templates"
13
- ],
14
- "engines": {
15
- "bun": ">=1.0.0"
16
- },
17
- "repository": {
18
- "type": "git",
19
- "url": "https://github.com/getjack-org/jack"
20
- },
21
- "publishConfig": {
22
- "access": "public"
23
- },
24
- "keywords": [
25
- "cli",
26
- "cloudflare",
27
- "workers",
28
- "deploy",
29
- "vibecode"
30
- ],
31
- "scripts": {
32
- "dev": "bun run src/index.ts",
33
- "build": "bun build --compile src/index.ts --outfile dist/jack",
34
- "lint": "biome check .",
35
- "format": "biome format --write ."
36
- },
37
- "devDependencies": {
38
- "@biomejs/biome": "^1.9.0",
39
- "bun-types": "latest"
40
- },
41
- "dependencies": {
42
- "@inquirer/prompts": "^7.0.0",
43
- "@modelcontextprotocol/sdk": "^1.25.1",
44
- "meow": "^14.0.0",
45
- "posthog-node": "^5.17.4",
46
- "yocto-spinner": "^1.0.0",
47
- "zod": "^4.2.1"
48
- }
2
+ "name": "@getjack/jack",
3
+ "version": "0.1.3",
4
+ "description": "Ship before you forget why you started. The vibecoder's deployment CLI.",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "bin": {
8
+ "jack": "./src/index.ts"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "templates"
13
+ ],
14
+ "engines": {
15
+ "bun": ">=1.0.0"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/getjack-org/jack"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "keywords": [
25
+ "cli",
26
+ "deploy",
27
+ "vibecoding",
28
+ "vibecode",
29
+ "shipping",
30
+ "edge",
31
+ "serverless",
32
+ "ai-agent",
33
+ "claude",
34
+ "cursor"
35
+ ],
36
+ "scripts": {
37
+ "dev": "bun run src/index.ts",
38
+ "build": "bun build --compile src/index.ts --outfile dist/jack",
39
+ "lint": "biome check .",
40
+ "format": "biome format --write ."
41
+ },
42
+ "devDependencies": {
43
+ "@types/archiver": "^7.0.0",
44
+ "bun-types": "latest"
45
+ },
46
+ "dependencies": {
47
+ "@inquirer/prompts": "^7.0.0",
48
+ "@modelcontextprotocol/sdk": "^1.25.1",
49
+ "archiver": "^7.0.1",
50
+ "human-id": "^4.1.3",
51
+ "meow": "^14.0.0",
52
+ "posthog-node": "^5.17.4",
53
+ "yocto-spinner": "^1.0.0",
54
+ "zod": "^4.2.1"
55
+ }
49
56
  }
@@ -1,8 +1,12 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { regenerateAgentFiles } from "../lib/agent-files.ts";
1
4
  import {
2
5
  AGENT_REGISTRY,
3
6
  addAgent,
4
7
  disableAgent,
5
8
  enableAgent,
9
+ getActiveAgents,
6
10
  getAgentDefinition,
7
11
  getPreferredAgent,
8
12
  pathExists,
@@ -12,12 +16,24 @@ import {
12
16
  updateAgent,
13
17
  } from "../lib/agents.ts";
14
18
  import { readConfig } from "../lib/config.ts";
15
- import { error, info, item, output as outputSpinner, success } from "../lib/output.ts";
19
+ import { error, info, item, output as outputSpinner, success, warn } from "../lib/output.ts";
20
+ import { getProject } from "../lib/registry.ts";
21
+ import { getProjectNameFromDir } from "../lib/storage/index.ts";
22
+ import { resolveTemplate } from "../templates/index.ts";
23
+ import type { Template } from "../templates/types.ts";
16
24
 
17
25
  /**
18
26
  * Main agents command - handles all agent management
19
27
  */
20
- export default async function agents(subcommand?: string, args: string[] = []): Promise<void> {
28
+ interface AgentsOptions {
29
+ project?: string;
30
+ }
31
+
32
+ export default async function agents(
33
+ subcommand?: string,
34
+ args: string[] = [],
35
+ options: AgentsOptions = {},
36
+ ): Promise<void> {
21
37
  if (!subcommand) {
22
38
  return await listAgents();
23
39
  }
@@ -35,9 +51,11 @@ export default async function agents(subcommand?: string, args: string[] = []):
35
51
  return await disableAgentCommand(args);
36
52
  case "prefer":
37
53
  return await preferAgentCommand(args);
54
+ case "refresh":
55
+ return await refreshAgentFilesCommand(options);
38
56
  default:
39
57
  error(`Unknown subcommand: ${subcommand}`);
40
- info("Available: scan, add, remove, enable, disable, prefer");
58
+ info("Available: scan, add, remove, enable, disable, prefer, refresh");
41
59
  process.exit(1);
42
60
  }
43
61
  }
@@ -76,7 +94,7 @@ async function listAgents(): Promise<void> {
76
94
  console.error("");
77
95
  }
78
96
 
79
- info("Commands: jack agents scan | add | remove | enable | disable | prefer");
97
+ info("Commands: jack agents scan | add | remove | enable | disable | prefer | refresh");
80
98
  }
81
99
 
82
100
  /**
@@ -89,6 +107,7 @@ async function scanAndPrompt(): Promise<void> {
89
107
 
90
108
  if (detectionResult.detected.length === 0) {
91
109
  info("No agents detected");
110
+ await listAgents();
92
111
  return;
93
112
  }
94
113
 
@@ -98,6 +117,7 @@ async function scanAndPrompt(): Promise<void> {
98
117
 
99
118
  if (newAgents.length === 0) {
100
119
  success("No new agents found");
120
+ await listAgents();
101
121
  return;
102
122
  }
103
123
 
@@ -119,13 +139,16 @@ async function scanAndPrompt(): Promise<void> {
119
139
  console.error("");
120
140
  success("New agents enabled");
121
141
  info("Future projects will include context files for these agents");
142
+ await listAgents();
122
143
  }
123
144
 
124
145
  function getFlagValues(args: string[], flag: string): string[] {
125
146
  const values: string[] = [];
126
147
  for (let i = 0; i < args.length; i++) {
127
- if (args[i] === flag && args[i + 1]) {
128
- values.push(args[i + 1]);
148
+ const current = args[i];
149
+ const next = args[i + 1];
150
+ if (current === flag && next) {
151
+ values.push(next);
129
152
  i++;
130
153
  }
131
154
  }
@@ -174,7 +197,7 @@ async function addAgentCommand(args: string[]): Promise<void> {
174
197
  type: "cli" as const,
175
198
  command: customCommand,
176
199
  args: customArgs.length ? customArgs : undefined,
177
- }
200
+ }
178
201
  : undefined;
179
202
 
180
203
  await addAgent(agentId, { launch: launchOverride });
@@ -190,9 +213,7 @@ async function addAgentCommand(args: string[]): Promise<void> {
190
213
  if (err instanceof Error) {
191
214
  error(err.message);
192
215
  if (err.message.includes("Could not detect")) {
193
- info(
194
- `Specify launch manually: jack agents add ${agentId} --command /path/to/command`,
195
- );
216
+ info(`Specify launch manually: jack agents add ${agentId} --command /path/to/command`);
196
217
  }
197
218
  }
198
219
  process.exit(1);
@@ -303,3 +324,117 @@ async function preferAgentCommand(args: string[]): Promise<void> {
303
324
  process.exit(1);
304
325
  }
305
326
  }
327
+
328
+ /**
329
+ * Refresh agent context files from template
330
+ */
331
+ async function refreshAgentFilesCommand(options: AgentsOptions = {}): Promise<void> {
332
+ const projectDir = process.cwd();
333
+ let projectName: string;
334
+ let project = null;
335
+
336
+ if (options.project) {
337
+ // When --project is specified, we still need to run from that project's directory
338
+ // since localPath is no longer stored in the registry
339
+ projectName = options.project;
340
+ project = await getProject(projectName);
341
+
342
+ if (!project) {
343
+ error(`Project "${projectName}" not found in registry`);
344
+ info("List projects with: jack projects list");
345
+ process.exit(1);
346
+ }
347
+
348
+ // Verify the current directory matches the project
349
+ try {
350
+ const cwdProjectName = await getProjectNameFromDir(projectDir);
351
+ if (cwdProjectName !== projectName) {
352
+ error(`Current directory is not the project "${projectName}"`);
353
+ info(`Run this command from the ${projectName} project directory`);
354
+ process.exit(1);
355
+ }
356
+ } catch {
357
+ error(`Current directory is not a valid project`);
358
+ info(`Run this command from the ${projectName} project directory`);
359
+ process.exit(1);
360
+ }
361
+ } else {
362
+ // 1. Detect project name from wrangler config
363
+ outputSpinner.start("Detecting project...");
364
+ try {
365
+ projectName = await getProjectNameFromDir(projectDir);
366
+ } catch {
367
+ outputSpinner.stop();
368
+ error("Could not determine project");
369
+ info("Run this command from a project directory, or use --project <name>");
370
+ process.exit(1);
371
+ }
372
+ outputSpinner.stop();
373
+
374
+ // 2. Get project from registry to find template origin
375
+ project = await getProject(projectName);
376
+ if (!project) {
377
+ error(`Project "${projectName}" not found in registry`);
378
+ info("List projects with: jack projects list");
379
+ process.exit(1);
380
+ }
381
+ }
382
+
383
+ if (!project?.template) {
384
+ error("No template lineage found for this project");
385
+ info("This project was created before lineage tracking was added.");
386
+ info("Re-create the project with `jack new` to enable refresh.");
387
+ process.exit(1);
388
+ }
389
+
390
+ // 3. Resolve template (fetch if GitHub, load if builtin)
391
+ outputSpinner.start("Loading template...");
392
+ let template: Template;
393
+ try {
394
+ template = await resolveTemplate(project.template.name);
395
+ } catch (err) {
396
+ outputSpinner.stop();
397
+ error(`Failed to load template: ${project.template.name}`);
398
+ if (err instanceof Error) {
399
+ info(err.message);
400
+ }
401
+ process.exit(1);
402
+ }
403
+ outputSpinner.stop();
404
+
405
+ // 4. Backup existing files
406
+ const agentsMdPath = join(projectDir, "AGENTS.md");
407
+ if (existsSync(agentsMdPath)) {
408
+ const backupPath = `${agentsMdPath}.backup`;
409
+ const content = await Bun.file(agentsMdPath).text();
410
+ await Bun.write(backupPath, content);
411
+ success("Backed up AGENTS.md → AGENTS.md.backup");
412
+ }
413
+
414
+ const claudeMdPath = join(projectDir, "CLAUDE.md");
415
+ if (existsSync(claudeMdPath)) {
416
+ const backupPath = `${claudeMdPath}.backup`;
417
+ const content = await Bun.file(claudeMdPath).text();
418
+ await Bun.write(backupPath, content);
419
+ success("Backed up CLAUDE.md → CLAUDE.md.backup");
420
+ }
421
+
422
+ // 5. Get active agents
423
+ const activeAgents = await getActiveAgents();
424
+ if (activeAgents.length === 0) {
425
+ warn("No active agents configured");
426
+ info("Run: jack agents scan");
427
+ process.exit(1);
428
+ }
429
+
430
+ // 6. Regenerate agent files
431
+ const updatedFiles = await regenerateAgentFiles(projectDir, projectName, template, activeAgents);
432
+
433
+ console.error("");
434
+ success("Refreshed agent context files:");
435
+ for (const file of updatedFiles) {
436
+ item(file);
437
+ }
438
+ console.error("");
439
+ info("Review changes: git diff AGENTS.md");
440
+ }
@@ -1,63 +1,55 @@
1
- import { existsSync } from "node:fs";
2
1
  import { join } from "node:path";
3
- import { select } from "@inquirer/prompts";
4
2
  import {
5
3
  checkWorkerExists,
6
4
  deleteDatabase,
7
5
  deleteWorker,
8
6
  exportDatabase,
9
7
  } from "../lib/cloudflare-api.ts";
8
+ import { fetchProjectResources } from "../lib/control-plane.ts";
9
+ import { promptSelect } from "../lib/hooks.ts";
10
+ import { managedDown } from "../lib/managed-down.ts";
10
11
  import { error, info, item, output, success, warn } from "../lib/output.ts";
11
- import {
12
- type Project,
13
- getProject,
14
- getProjectDatabaseName,
15
- updateProject,
16
- updateProjectDatabase,
17
- } from "../lib/registry.ts";
12
+ import { resolveProject } from "../lib/project-resolver.ts";
13
+ import { type Project, getProject, updateProject } from "../lib/registry.ts";
14
+ import { parseWranglerResources } from "../lib/resources.ts";
18
15
  import { deleteCloudProject, getProjectNameFromDir } from "../lib/storage/index.ts";
19
16
 
20
17
  /**
21
- * Get database name for a project, with fallback to wrangler config
18
+ * Resolve database name for a project.
19
+ * For managed projects: fetch from control plane.
20
+ * For BYO projects: parse from wrangler.jsonc in cwd.
22
21
  */
23
- async function resolveDbName(project: Project): Promise<string | null> {
24
- // First check registry
25
- const dbFromRegistry = getProjectDatabaseName(project);
26
- if (dbFromRegistry) {
27
- return dbFromRegistry;
22
+ async function resolveDatabaseName(project: Project, projectName: string): Promise<string | null> {
23
+ // For managed projects, fetch from control plane
24
+ if (project.deploy_mode === "managed" && project.remote?.project_id) {
25
+ try {
26
+ const resources = await fetchProjectResources(project.remote.project_id);
27
+ const d1 = resources.find((r) => r.resource_type === "d1");
28
+ return d1?.resource_name || null;
29
+ } catch {
30
+ return null;
31
+ }
28
32
  }
29
33
 
30
- // Fallback: read from wrangler config file
31
- if (project.localPath && existsSync(project.localPath)) {
32
- const jsoncPath = join(project.localPath, "wrangler.jsonc");
33
- if (existsSync(jsoncPath)) {
34
- try {
35
- const content = await Bun.file(jsoncPath).text();
36
- const jsonContent = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
37
- const config = JSON.parse(jsonContent);
38
- if (config.d1_databases?.[0]?.database_name) {
39
- return config.d1_databases[0].database_name;
40
- }
41
- } catch {
42
- // Ignore parse errors
43
- }
34
+ // For BYO, parse from wrangler config in cwd
35
+ try {
36
+ let cwdProjectName: string | null = null;
37
+ try {
38
+ cwdProjectName = await getProjectNameFromDir(process.cwd());
39
+ } catch {
40
+ cwdProjectName = null;
44
41
  }
45
42
 
46
- const tomlPath = join(project.localPath, "wrangler.toml");
47
- if (existsSync(tomlPath)) {
48
- try {
49
- const content = await Bun.file(tomlPath).text();
50
- const match = content.match(/database_name\s*=\s*"([^"]+)"/);
51
- if (match?.[1]) {
52
- return match[1];
53
- }
54
- } catch {
55
- // Ignore read errors
56
- }
43
+ if (!cwdProjectName || cwdProjectName !== projectName) {
44
+ warn(`Run this command from the ${projectName} project directory to manage its database.`);
45
+ return null;
57
46
  }
58
- }
59
47
 
60
- return null;
48
+ const resources = await parseWranglerResources(process.cwd());
49
+ return resources.d1?.name || null;
50
+ } catch {
51
+ return null;
52
+ }
61
53
  }
62
54
 
63
55
  export interface DownFlags {
@@ -78,13 +70,58 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
78
70
  }
79
71
  }
80
72
 
81
- // Get project from registry
73
+ // Resolve project from all sources (registry + control plane)
74
+ const resolved = await resolveProject(name);
75
+
76
+ // Check if found only on control plane (orphaned managed project)
77
+ if (resolved?.sources.controlPlane && !resolved.sources.registry) {
78
+ console.error("");
79
+ info(`Found "${name}" on jack cloud, linking locally...`);
80
+ }
81
+
82
+ // Get the registry project (may have been created by resolver cache)
82
83
  const project = await getProject(name);
83
- if (!project) {
84
- warn(`Project '${name}' not found in registry`);
84
+
85
+ if (!resolved && !project) {
86
+ // Not found anywhere
87
+ warn(`Project '${name}' not found`);
85
88
  info("Will attempt to undeploy if deployed");
86
89
  }
87
90
 
91
+ // Check if this is a managed project (either from resolved or registry)
92
+ const isManaged =
93
+ resolved?.remote?.projectId ||
94
+ (project?.deploy_mode === "managed" && project.remote?.project_id);
95
+
96
+ if (isManaged) {
97
+ // Build project object for managedDown if we only have resolved data
98
+ const managedProject: Project = project || {
99
+ workerUrl: resolved?.url || null,
100
+ createdAt: resolved?.createdAt || new Date().toISOString(),
101
+ lastDeployed: resolved?.updatedAt || null,
102
+ status: resolved?.status === "live" ? "live" : "build_failed",
103
+ deploy_mode: "managed",
104
+ remote:
105
+ resolved?.remote && resolved.url
106
+ ? {
107
+ project_id: resolved.remote.projectId,
108
+ project_slug: resolved.slug,
109
+ org_id: resolved.remote.orgId,
110
+ runjack_url: resolved.url,
111
+ }
112
+ : undefined,
113
+ };
114
+
115
+ // Route to managed deletion flow
116
+ const deleteSuccess = await managedDown(managedProject, name, flags);
117
+ if (!deleteSuccess) {
118
+ process.exit(0); // User cancelled
119
+ }
120
+ return;
121
+ }
122
+
123
+ // Continue with existing BYO flow...
124
+
88
125
  // Check if worker exists
89
126
  output.start("Checking deployment...");
90
127
  const workerExists = await checkWorkerExists(name);
@@ -117,7 +154,7 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
117
154
 
118
155
  console.error("");
119
156
  success(`'${name}' undeployed`);
120
- info("Databases and cloud storage were not affected");
157
+ info("Databases and backups were not affected");
121
158
  console.error("");
122
159
  return;
123
160
  }
@@ -128,23 +165,18 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
128
165
  if (project?.workerUrl) {
129
166
  item(`URL: ${project.workerUrl}`);
130
167
  }
131
- const dbName = project ? await resolveDbName(project) : null;
168
+ const dbName = project ? await resolveDatabaseName(project, name) : null;
132
169
  if (dbName) {
133
170
  item(`Database: ${dbName}`);
134
171
  }
135
172
  console.error("");
136
173
 
137
174
  // Confirm undeploy
138
- console.error(" Esc to skip\n");
139
- const action = await select({
140
- message: "Undeploy this project?",
141
- choices: [
142
- { name: "1. Yes", value: "yes" },
143
- { name: "2. No", value: "no" },
144
- ],
145
- });
146
-
147
- if (action === "no") {
175
+ console.error("");
176
+ info("Undeploy this project?");
177
+ const action = await promptSelect(["Yes", "No"]);
178
+
179
+ if (action !== 0) {
148
180
  info("Cancelled");
149
181
  return;
150
182
  }
@@ -157,16 +189,11 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
157
189
  info(`Found database: ${dbName}`);
158
190
 
159
191
  // Ask if they want to export first
160
- console.error(" Esc to skip\n");
161
- const exportAction = await select({
162
- message: `Export database '${dbName}' before deleting?`,
163
- choices: [
164
- { name: "1. Yes", value: "yes" },
165
- { name: "2. No", value: "no" },
166
- ],
167
- });
192
+ console.error("");
193
+ info(`Export database '${dbName}' before deleting?`);
194
+ const exportAction = await promptSelect(["Yes", "No"]);
168
195
 
169
- if (exportAction === "yes") {
196
+ if (exportAction === 0) {
170
197
  const exportPath = join(process.cwd(), `${dbName}-backup.sql`);
171
198
  output.start(`Exporting database to ${exportPath}...`);
172
199
  try {
@@ -176,15 +203,10 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
176
203
  } catch (err) {
177
204
  output.stop();
178
205
  error(`Failed to export database: ${err instanceof Error ? err.message : String(err)}`);
179
- console.error(" Esc to skip\n");
180
- const continueAction = await select({
181
- message: "Continue without exporting?",
182
- choices: [
183
- { name: "1. Yes", value: "yes" },
184
- { name: "2. No", value: "no" },
185
- ],
186
- });
187
- if (continueAction === "no") {
206
+ console.error("");
207
+ info("Continue without exporting?");
208
+ const continueAction = await promptSelect(["Yes", "No"]);
209
+ if (continueAction !== 0) {
188
210
  info("Cancelled");
189
211
  return;
190
212
  }
@@ -192,31 +214,20 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
192
214
  }
193
215
 
194
216
  // Ask if they want to delete the database
195
- console.error(" Esc to skip\n");
196
- const deleteAction = await select({
197
- message: `Delete database '${dbName}'?`,
198
- choices: [
199
- { name: "1. Yes", value: "yes" },
200
- { name: "2. No", value: "no" },
201
- ],
202
- });
217
+ console.error("");
218
+ info(`Delete database '${dbName}'?`);
219
+ const deleteAction = await promptSelect(["Yes", "No"]);
203
220
 
204
- shouldDeleteDb = deleteAction === "yes";
221
+ shouldDeleteDb = deleteAction === 0;
205
222
  }
206
223
 
207
- // Handle R2 backup deletion
224
+ // Handle backup deletion
208
225
  let shouldDeleteR2 = false;
209
226
  if (project) {
210
227
  console.error("");
211
- console.error(" Esc to skip\n");
212
- const deleteR2Action = await select({
213
- message: "Delete cloud backup for this project?",
214
- choices: [
215
- { name: "1. Yes", value: "yes" },
216
- { name: "2. No", value: "no" },
217
- ],
218
- });
219
- shouldDeleteR2 = deleteR2Action === "yes";
228
+ info("Delete backup for this project?");
229
+ const deleteR2Action = await promptSelect(["Yes", "No"]);
230
+ shouldDeleteR2 = deleteR2Action === 0;
220
231
  }
221
232
 
222
233
  // Execute deletions
@@ -243,9 +254,6 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
243
254
  await deleteDatabase(dbName);
244
255
  output.stop();
245
256
  success(`Database '${dbName}' deleted`);
246
-
247
- // Update registry
248
- await updateProjectDatabase(name, null);
249
257
  } catch (err) {
250
258
  output.stop();
251
259
  warn(
@@ -254,20 +262,20 @@ export default async function down(projectName?: string, flags: DownFlags = {}):
254
262
  }
255
263
  }
256
264
 
257
- // Delete cloud backup if requested
265
+ // Delete backup if requested
258
266
  if (shouldDeleteR2) {
259
- output.start("Deleting cloud backup...");
267
+ output.start("Deleting backup...");
260
268
  try {
261
269
  const deleted = await deleteCloudProject(name);
262
270
  output.stop();
263
271
  if (deleted) {
264
- success("Cloud backup deleted");
272
+ success("Backup deleted");
265
273
  } else {
266
- warn("No cloud backup found or already deleted");
274
+ warn("No backup found or already deleted");
267
275
  }
268
276
  } catch (err) {
269
277
  output.stop();
270
- warn(`Failed to delete cloud backup: ${err instanceof Error ? err.message : String(err)}`);
278
+ warn(`Failed to delete backup: ${err instanceof Error ? err.message : String(err)}`);
271
279
  }
272
280
  }
273
281