zdev 0.1.4 → 0.2.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.
- package/dist/index.js +165 -3
- package/package.json +1 -1
- package/src/commands/pr.ts +201 -0
- package/src/commands/start.ts +9 -4
- package/src/config.ts +2 -0
- package/src/index.ts +19 -0
package/dist/index.js
CHANGED
|
@@ -2515,10 +2515,13 @@ async function start(featureName, projectPath = ".", options = {}) {
|
|
|
2515
2515
|
console.log(` Run: zdev stop ${featureName} --project ${fullPath}`);
|
|
2516
2516
|
process.exit(1);
|
|
2517
2517
|
}
|
|
2518
|
-
|
|
2518
|
+
const hasOrigin = run("git", ["remote", "get-url", "origin"], { cwd: fullPath });
|
|
2519
|
+
if (hasOrigin.success) {
|
|
2520
|
+
console.log(`
|
|
2519
2521
|
\uD83D\uDCE5 Fetching latest from origin...`);
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
+
if (!gitFetch(fullPath)) {
|
|
2523
|
+
console.error(` Failed to fetch, continuing anyway...`);
|
|
2524
|
+
}
|
|
2522
2525
|
}
|
|
2523
2526
|
console.log(`
|
|
2524
2527
|
\uD83C\uDF33 Creating worktree...`);
|
|
@@ -2660,6 +2663,8 @@ async function start(featureName, projectPath = ".", options = {}) {
|
|
|
2660
2663
|
frontendPort: ports.frontend,
|
|
2661
2664
|
convexPort: ports.convex,
|
|
2662
2665
|
funnelPath: routePath,
|
|
2666
|
+
worktreePath,
|
|
2667
|
+
publicUrl: publicUrl || undefined,
|
|
2663
2668
|
pids: {
|
|
2664
2669
|
frontend: frontendPid,
|
|
2665
2670
|
convex: convexPid
|
|
@@ -3066,6 +3071,155 @@ Commands:`);
|
|
|
3066
3071
|
}
|
|
3067
3072
|
}
|
|
3068
3073
|
|
|
3074
|
+
// src/commands/pr.ts
|
|
3075
|
+
import { resolve as resolve8, basename as basename4 } from "path";
|
|
3076
|
+
async function pr(featureName, projectPath = ".", options = {}) {
|
|
3077
|
+
const fullPath = resolve8(projectPath);
|
|
3078
|
+
let worktreePath = fullPath;
|
|
3079
|
+
let allocation;
|
|
3080
|
+
let projectName = getRepoName(fullPath) || basename4(fullPath);
|
|
3081
|
+
const config = loadConfig();
|
|
3082
|
+
if (featureName) {
|
|
3083
|
+
const allocKey = `${projectName}-${featureName}`;
|
|
3084
|
+
allocation = config.allocations[allocKey];
|
|
3085
|
+
if (!allocation) {
|
|
3086
|
+
const found = Object.entries(config.allocations).find(([key, alloc]) => key.endsWith(`-${featureName}`));
|
|
3087
|
+
if (found) {
|
|
3088
|
+
allocation = found[1];
|
|
3089
|
+
projectName = allocation.project;
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
if (allocation) {
|
|
3093
|
+
worktreePath = allocation.worktreePath || resolve8(config.worktreesDir, `${projectName}-${featureName}`);
|
|
3094
|
+
}
|
|
3095
|
+
} else {
|
|
3096
|
+
const cwd = process.cwd();
|
|
3097
|
+
const found = Object.entries(config.allocations).find(([_, alloc]) => alloc.worktreePath === cwd || cwd.startsWith(alloc.worktreePath || ""));
|
|
3098
|
+
if (found) {
|
|
3099
|
+
allocation = found[1];
|
|
3100
|
+
featureName = found[0].split("-").slice(1).join("-");
|
|
3101
|
+
projectName = allocation.project;
|
|
3102
|
+
worktreePath = allocation.worktreePath || cwd;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
if (!isGitRepo(worktreePath)) {
|
|
3106
|
+
console.error(`❌ Not a git repository: ${worktreePath}`);
|
|
3107
|
+
process.exit(1);
|
|
3108
|
+
}
|
|
3109
|
+
const branchResult = run("git", ["branch", "--show-current"], { cwd: worktreePath });
|
|
3110
|
+
if (!branchResult.success || !branchResult.stdout.trim()) {
|
|
3111
|
+
console.error("❌ Could not determine current branch");
|
|
3112
|
+
process.exit(1);
|
|
3113
|
+
}
|
|
3114
|
+
const branch = branchResult.stdout.trim();
|
|
3115
|
+
console.log(`\uD83D\uDC02 Creating PR for: ${branch}`);
|
|
3116
|
+
if (allocation) {
|
|
3117
|
+
console.log(` Project: ${projectName}`);
|
|
3118
|
+
console.log(` Feature: ${featureName}`);
|
|
3119
|
+
}
|
|
3120
|
+
const ghCheck = run("which", ["gh"]);
|
|
3121
|
+
if (!ghCheck.success) {
|
|
3122
|
+
console.error("❌ GitHub CLI (gh) not found. Install: https://cli.github.com");
|
|
3123
|
+
process.exit(1);
|
|
3124
|
+
}
|
|
3125
|
+
const authCheck = run("gh", ["auth", "status"], { cwd: worktreePath });
|
|
3126
|
+
if (!authCheck.success) {
|
|
3127
|
+
console.error("❌ Not authenticated with GitHub. Run: gh auth login");
|
|
3128
|
+
process.exit(1);
|
|
3129
|
+
}
|
|
3130
|
+
console.log(`
|
|
3131
|
+
\uD83D\uDCE4 Pushing branch...`);
|
|
3132
|
+
const pushResult = run("git", ["push", "-u", "origin", branch], { cwd: worktreePath });
|
|
3133
|
+
if (!pushResult.success) {
|
|
3134
|
+
if (!pushResult.stderr.includes("Everything up-to-date")) {
|
|
3135
|
+
console.error(` Failed to push: ${pushResult.stderr}`);
|
|
3136
|
+
process.exit(1);
|
|
3137
|
+
}
|
|
3138
|
+
console.log(` Already up to date`);
|
|
3139
|
+
} else {
|
|
3140
|
+
console.log(` Pushed to origin/${branch}`);
|
|
3141
|
+
}
|
|
3142
|
+
let title = options.title;
|
|
3143
|
+
if (!title) {
|
|
3144
|
+
const featureForTitle = featureName || branch.replace(/^feature\//, "");
|
|
3145
|
+
title = featureForTitle.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
3146
|
+
}
|
|
3147
|
+
let body = options.body || "";
|
|
3148
|
+
if (allocation && allocation.publicUrl) {
|
|
3149
|
+
const previewSection = `## Preview
|
|
3150
|
+
\uD83D\uDD17 ${allocation.publicUrl}
|
|
3151
|
+
|
|
3152
|
+
`;
|
|
3153
|
+
body = previewSection + body;
|
|
3154
|
+
} else if (config.devDomain && featureName && projectName) {
|
|
3155
|
+
const previewUrl = `https://${projectName}-${featureName}.${config.devDomain}`;
|
|
3156
|
+
const previewSection = `## Preview
|
|
3157
|
+
\uD83D\uDD17 ${previewUrl}
|
|
3158
|
+
|
|
3159
|
+
`;
|
|
3160
|
+
body = previewSection + body;
|
|
3161
|
+
}
|
|
3162
|
+
if (!body.includes("Created with zdev")) {
|
|
3163
|
+
body = body.trim() + `
|
|
3164
|
+
|
|
3165
|
+
---
|
|
3166
|
+
*Created with [zdev](https://github.com/5hanth/zdev)*`;
|
|
3167
|
+
}
|
|
3168
|
+
const existingPr = run("gh", ["pr", "view", branch, "--json", "url"], { cwd: worktreePath });
|
|
3169
|
+
if (existingPr.success) {
|
|
3170
|
+
try {
|
|
3171
|
+
const prData = JSON.parse(existingPr.stdout);
|
|
3172
|
+
console.log(`
|
|
3173
|
+
✅ PR already exists!`);
|
|
3174
|
+
console.log(`
|
|
3175
|
+
\uD83D\uDD17 ${prData.url}`);
|
|
3176
|
+
return;
|
|
3177
|
+
} catch {}
|
|
3178
|
+
}
|
|
3179
|
+
console.log(`
|
|
3180
|
+
\uD83D\uDCDD Creating pull request...`);
|
|
3181
|
+
const prArgs = ["pr", "create", "--title", title, "--body", body];
|
|
3182
|
+
if (options.draft) {
|
|
3183
|
+
prArgs.push("--draft");
|
|
3184
|
+
}
|
|
3185
|
+
if (options.web) {
|
|
3186
|
+
prArgs.push("--web");
|
|
3187
|
+
const webResult = run("gh", prArgs, { cwd: worktreePath });
|
|
3188
|
+
if (!webResult.success) {
|
|
3189
|
+
console.error(` Failed: ${webResult.stderr}`);
|
|
3190
|
+
process.exit(1);
|
|
3191
|
+
}
|
|
3192
|
+
console.log(` Opened in browser`);
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
3195
|
+
const prResult = run("gh", prArgs, { cwd: worktreePath });
|
|
3196
|
+
if (!prResult.success) {
|
|
3197
|
+
if (prResult.stderr.includes("already exists")) {
|
|
3198
|
+
console.log(` PR already exists for this branch`);
|
|
3199
|
+
const viewResult = run("gh", ["pr", "view", "--json", "url"], { cwd: worktreePath });
|
|
3200
|
+
if (viewResult.success) {
|
|
3201
|
+
try {
|
|
3202
|
+
const prData = JSON.parse(viewResult.stdout);
|
|
3203
|
+
console.log(`
|
|
3204
|
+
\uD83D\uDD17 ${prData.url}`);
|
|
3205
|
+
} catch {}
|
|
3206
|
+
}
|
|
3207
|
+
return;
|
|
3208
|
+
}
|
|
3209
|
+
console.error(` Failed: ${prResult.stderr}`);
|
|
3210
|
+
process.exit(1);
|
|
3211
|
+
}
|
|
3212
|
+
const prUrl = prResult.stdout.trim();
|
|
3213
|
+
console.log(`
|
|
3214
|
+
✅ Pull request created!`);
|
|
3215
|
+
console.log(`
|
|
3216
|
+
\uD83D\uDD17 ${prUrl}`);
|
|
3217
|
+
if (allocation?.publicUrl) {
|
|
3218
|
+
console.log(`
|
|
3219
|
+
\uD83D\uDCF1 Preview: ${allocation.publicUrl}`);
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
|
|
3069
3223
|
// src/index.ts
|
|
3070
3224
|
import { readFileSync as readFileSync5 } from "fs";
|
|
3071
3225
|
import { fileURLToPath } from "url";
|
|
@@ -3111,6 +3265,14 @@ seedCmd.command("import [path]").description("Import seed data into current work
|
|
|
3111
3265
|
program2.command("config").description("View and manage zdev configuration").option("-a, --add <pattern>", "Add a file pattern to auto-copy").option("-r, --remove <pattern>", "Remove a file pattern").option("-s, --set <key=value>", "Set a config value (devDomain, dockerHostIp, traefikConfigDir)").option("-l, --list", "List current configuration").action(async (options) => {
|
|
3112
3266
|
await configCmd(options);
|
|
3113
3267
|
});
|
|
3268
|
+
program2.command("pr [feature]").description("Create a pull request for a feature branch").option("-p, --project <path>", "Project path", ".").option("-t, --title <title>", "PR title (auto-generated if not specified)").option("-b, --body <body>", "PR body (preview URL auto-added)").option("-d, --draft", "Create as draft PR").option("-w, --web", "Open in browser instead of CLI").action(async (feature, options) => {
|
|
3269
|
+
await pr(feature, options.project, {
|
|
3270
|
+
title: options.title,
|
|
3271
|
+
body: options.body,
|
|
3272
|
+
draft: options.draft,
|
|
3273
|
+
web: options.web
|
|
3274
|
+
});
|
|
3275
|
+
});
|
|
3114
3276
|
program2.command("status").description("Show zdev status (alias for list)").action(async () => {
|
|
3115
3277
|
await list({});
|
|
3116
3278
|
});
|
package/package.json
CHANGED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { resolve, basename } from "path";
|
|
3
|
+
import { run, isGitRepo, getRepoName } from "../utils.js";
|
|
4
|
+
import { loadConfig, type WorktreeAllocation } from "../config.js";
|
|
5
|
+
|
|
6
|
+
export interface PrOptions {
|
|
7
|
+
title?: string;
|
|
8
|
+
body?: string;
|
|
9
|
+
draft?: boolean;
|
|
10
|
+
web?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function pr(
|
|
14
|
+
featureName: string | undefined,
|
|
15
|
+
projectPath: string = ".",
|
|
16
|
+
options: PrOptions = {}
|
|
17
|
+
): Promise<void> {
|
|
18
|
+
const fullPath = resolve(projectPath);
|
|
19
|
+
|
|
20
|
+
// Check if we're in a worktree or need to find one
|
|
21
|
+
let worktreePath = fullPath;
|
|
22
|
+
let allocation: WorktreeAllocation | undefined;
|
|
23
|
+
let projectName = getRepoName(fullPath) || basename(fullPath);
|
|
24
|
+
|
|
25
|
+
const config = loadConfig();
|
|
26
|
+
|
|
27
|
+
// If featureName provided, find the allocation
|
|
28
|
+
if (featureName) {
|
|
29
|
+
const allocKey = `${projectName}-${featureName}`;
|
|
30
|
+
allocation = config.allocations[allocKey];
|
|
31
|
+
|
|
32
|
+
if (!allocation) {
|
|
33
|
+
// Try to find by just feature name
|
|
34
|
+
const found = Object.entries(config.allocations).find(
|
|
35
|
+
([key, alloc]) => key.endsWith(`-${featureName}`)
|
|
36
|
+
);
|
|
37
|
+
if (found) {
|
|
38
|
+
allocation = found[1];
|
|
39
|
+
projectName = allocation.project;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (allocation) {
|
|
44
|
+
worktreePath = allocation.worktreePath || resolve(config.worktreesDir, `${projectName}-${featureName}`);
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
// Try to detect from current directory
|
|
48
|
+
const cwd = process.cwd();
|
|
49
|
+
const found = Object.entries(config.allocations).find(
|
|
50
|
+
([_, alloc]) => alloc.worktreePath === cwd || cwd.startsWith(alloc.worktreePath || "")
|
|
51
|
+
);
|
|
52
|
+
if (found) {
|
|
53
|
+
allocation = found[1];
|
|
54
|
+
featureName = found[0].split("-").slice(1).join("-");
|
|
55
|
+
projectName = allocation.project;
|
|
56
|
+
worktreePath = allocation.worktreePath || cwd;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!isGitRepo(worktreePath)) {
|
|
61
|
+
console.error(`❌ Not a git repository: ${worktreePath}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Get current branch
|
|
66
|
+
const branchResult = run("git", ["branch", "--show-current"], { cwd: worktreePath });
|
|
67
|
+
if (!branchResult.success || !branchResult.stdout.trim()) {
|
|
68
|
+
console.error("❌ Could not determine current branch");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const branch = branchResult.stdout.trim();
|
|
72
|
+
|
|
73
|
+
console.log(`🐂 Creating PR for: ${branch}`);
|
|
74
|
+
if (allocation) {
|
|
75
|
+
console.log(` Project: ${projectName}`);
|
|
76
|
+
console.log(` Feature: ${featureName}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check if gh CLI is available
|
|
80
|
+
const ghCheck = run("which", ["gh"]);
|
|
81
|
+
if (!ghCheck.success) {
|
|
82
|
+
console.error("❌ GitHub CLI (gh) not found. Install: https://cli.github.com");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check if authenticated
|
|
87
|
+
const authCheck = run("gh", ["auth", "status"], { cwd: worktreePath });
|
|
88
|
+
if (!authCheck.success) {
|
|
89
|
+
console.error("❌ Not authenticated with GitHub. Run: gh auth login");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Push branch if not pushed
|
|
94
|
+
console.log(`\n📤 Pushing branch...`);
|
|
95
|
+
const pushResult = run("git", ["push", "-u", "origin", branch], { cwd: worktreePath });
|
|
96
|
+
if (!pushResult.success) {
|
|
97
|
+
// Check if it's just "already up to date"
|
|
98
|
+
if (!pushResult.stderr.includes("Everything up-to-date")) {
|
|
99
|
+
console.error(` Failed to push: ${pushResult.stderr}`);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
console.log(` Already up to date`);
|
|
103
|
+
} else {
|
|
104
|
+
console.log(` Pushed to origin/${branch}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Build PR title
|
|
108
|
+
let title = options.title;
|
|
109
|
+
if (!title) {
|
|
110
|
+
// Generate from feature name or branch
|
|
111
|
+
const featureForTitle = featureName || branch.replace(/^feature\//, "");
|
|
112
|
+
title = featureForTitle
|
|
113
|
+
.split(/[-_]/)
|
|
114
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
115
|
+
.join(" ");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Build PR body
|
|
119
|
+
let body = options.body || "";
|
|
120
|
+
|
|
121
|
+
// Add preview URL if we have allocation
|
|
122
|
+
if (allocation && allocation.publicUrl) {
|
|
123
|
+
const previewSection = `## Preview\n🔗 ${allocation.publicUrl}\n\n`;
|
|
124
|
+
body = previewSection + body;
|
|
125
|
+
} else if (config.devDomain && featureName && projectName) {
|
|
126
|
+
// Try to construct preview URL
|
|
127
|
+
const previewUrl = `https://${projectName}-${featureName}.${config.devDomain}`;
|
|
128
|
+
const previewSection = `## Preview\n🔗 ${previewUrl}\n\n`;
|
|
129
|
+
body = previewSection + body;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Add footer
|
|
133
|
+
if (!body.includes("Created with zdev")) {
|
|
134
|
+
body = body.trim() + "\n\n---\n*Created with [zdev](https://github.com/5hanth/zdev)*";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check if PR already exists
|
|
138
|
+
const existingPr = run("gh", ["pr", "view", branch, "--json", "url"], { cwd: worktreePath });
|
|
139
|
+
if (existingPr.success) {
|
|
140
|
+
try {
|
|
141
|
+
const prData = JSON.parse(existingPr.stdout);
|
|
142
|
+
console.log(`\n✅ PR already exists!`);
|
|
143
|
+
console.log(`\n🔗 ${prData.url}`);
|
|
144
|
+
return;
|
|
145
|
+
} catch {
|
|
146
|
+
// Continue to create
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Create PR
|
|
151
|
+
console.log(`\n📝 Creating pull request...`);
|
|
152
|
+
|
|
153
|
+
const prArgs = ["pr", "create", "--title", title, "--body", body];
|
|
154
|
+
|
|
155
|
+
if (options.draft) {
|
|
156
|
+
prArgs.push("--draft");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (options.web) {
|
|
160
|
+
prArgs.push("--web");
|
|
161
|
+
const webResult = run("gh", prArgs, { cwd: worktreePath });
|
|
162
|
+
if (!webResult.success) {
|
|
163
|
+
console.error(` Failed: ${webResult.stderr}`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
console.log(` Opened in browser`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const prResult = run("gh", prArgs, { cwd: worktreePath });
|
|
171
|
+
|
|
172
|
+
if (!prResult.success) {
|
|
173
|
+
// Check if it's because PR already exists
|
|
174
|
+
if (prResult.stderr.includes("already exists")) {
|
|
175
|
+
console.log(` PR already exists for this branch`);
|
|
176
|
+
const viewResult = run("gh", ["pr", "view", "--json", "url"], { cwd: worktreePath });
|
|
177
|
+
if (viewResult.success) {
|
|
178
|
+
try {
|
|
179
|
+
const prData = JSON.parse(viewResult.stdout);
|
|
180
|
+
console.log(`\n🔗 ${prData.url}`);
|
|
181
|
+
} catch {
|
|
182
|
+
// Ignore
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
console.error(` Failed: ${prResult.stderr}`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Extract PR URL from output
|
|
192
|
+
const prUrl = prResult.stdout.trim();
|
|
193
|
+
|
|
194
|
+
console.log(`\n✅ Pull request created!`);
|
|
195
|
+
console.log(`\n🔗 ${prUrl}`);
|
|
196
|
+
|
|
197
|
+
// Show preview URL again for easy access
|
|
198
|
+
if (allocation?.publicUrl) {
|
|
199
|
+
console.log(`\n📱 Preview: ${allocation.publicUrl}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/commands/start.ts
CHANGED
|
@@ -105,10 +105,13 @@ export async function start(
|
|
|
105
105
|
process.exit(1);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
// Fetch latest
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
console.
|
|
108
|
+
// Fetch latest (only if origin remote exists)
|
|
109
|
+
const hasOrigin = run("git", ["remote", "get-url", "origin"], { cwd: fullPath });
|
|
110
|
+
if (hasOrigin.success) {
|
|
111
|
+
console.log(`\n📥 Fetching latest from origin...`);
|
|
112
|
+
if (!gitFetch(fullPath)) {
|
|
113
|
+
console.error(` Failed to fetch, continuing anyway...`);
|
|
114
|
+
}
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
// Create worktree
|
|
@@ -298,6 +301,8 @@ export async function start(
|
|
|
298
301
|
frontendPort: ports.frontend,
|
|
299
302
|
convexPort: ports.convex,
|
|
300
303
|
funnelPath: routePath,
|
|
304
|
+
worktreePath,
|
|
305
|
+
publicUrl: publicUrl || undefined,
|
|
301
306
|
pids: {
|
|
302
307
|
frontend: frontendPid,
|
|
303
308
|
convex: convexPid,
|
package/src/config.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { list } from "./commands/list.js";
|
|
|
8
8
|
import { clean } from "./commands/clean.js";
|
|
9
9
|
import { seedExport, seedImport } from "./commands/seed.js";
|
|
10
10
|
import { configCmd } from "./commands/config.js";
|
|
11
|
+
import { pr } from "./commands/pr.js";
|
|
11
12
|
import { readFileSync } from "fs";
|
|
12
13
|
import { fileURLToPath } from "url";
|
|
13
14
|
import { dirname, join } from "path";
|
|
@@ -124,6 +125,24 @@ program
|
|
|
124
125
|
await configCmd(options);
|
|
125
126
|
});
|
|
126
127
|
|
|
128
|
+
// zdev pr
|
|
129
|
+
program
|
|
130
|
+
.command("pr [feature]")
|
|
131
|
+
.description("Create a pull request for a feature branch")
|
|
132
|
+
.option("-p, --project <path>", "Project path", ".")
|
|
133
|
+
.option("-t, --title <title>", "PR title (auto-generated if not specified)")
|
|
134
|
+
.option("-b, --body <body>", "PR body (preview URL auto-added)")
|
|
135
|
+
.option("-d, --draft", "Create as draft PR")
|
|
136
|
+
.option("-w, --web", "Open in browser instead of CLI")
|
|
137
|
+
.action(async (feature, options) => {
|
|
138
|
+
await pr(feature, options.project, {
|
|
139
|
+
title: options.title,
|
|
140
|
+
body: options.body,
|
|
141
|
+
draft: options.draft,
|
|
142
|
+
web: options.web,
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
127
146
|
// zdev status (alias for list)
|
|
128
147
|
program
|
|
129
148
|
.command("status")
|