planflow-plugin 0.1.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/LICENSE +21 -0
- package/README.md +93 -0
- package/bin/cli.js +169 -0
- package/bin/postinstall.js +87 -0
- package/commands/pfActivity/SKILL.md +725 -0
- package/commands/pfAssign/SKILL.md +623 -0
- package/commands/pfCloudLink/SKILL.md +192 -0
- package/commands/pfCloudList/SKILL.md +222 -0
- package/commands/pfCloudNew/SKILL.md +187 -0
- package/commands/pfCloudUnlink/SKILL.md +152 -0
- package/commands/pfComment/SKILL.md +227 -0
- package/commands/pfComments/SKILL.md +159 -0
- package/commands/pfConnectionStatus/SKILL.md +433 -0
- package/commands/pfDiscord/SKILL.md +740 -0
- package/commands/pfGithubBranch/SKILL.md +672 -0
- package/commands/pfGithubIssue/SKILL.md +963 -0
- package/commands/pfGithubLink/SKILL.md +859 -0
- package/commands/pfGithubPr/SKILL.md +1335 -0
- package/commands/pfGithubUnlink/SKILL.md +401 -0
- package/commands/pfLive/SKILL.md +185 -0
- package/commands/pfLogin/SKILL.md +249 -0
- package/commands/pfLogout/SKILL.md +155 -0
- package/commands/pfMyTasks/SKILL.md +198 -0
- package/commands/pfNotificationSettings/SKILL.md +619 -0
- package/commands/pfNotifications/SKILL.md +420 -0
- package/commands/pfNotificationsClear/SKILL.md +421 -0
- package/commands/pfReact/SKILL.md +232 -0
- package/commands/pfSlack/SKILL.md +659 -0
- package/commands/pfSyncPull/SKILL.md +210 -0
- package/commands/pfSyncPush/SKILL.md +299 -0
- package/commands/pfSyncStatus/SKILL.md +212 -0
- package/commands/pfTeamInvite/SKILL.md +161 -0
- package/commands/pfTeamList/SKILL.md +253 -0
- package/commands/pfTeamRemove/SKILL.md +115 -0
- package/commands/pfTeamRole/SKILL.md +160 -0
- package/commands/pfTestWebhooks/SKILL.md +722 -0
- package/commands/pfUnassign/SKILL.md +134 -0
- package/commands/pfWhoami/SKILL.md +258 -0
- package/commands/pfWorkload/SKILL.md +219 -0
- package/commands/planExportCsv/SKILL.md +106 -0
- package/commands/planExportGithub/SKILL.md +222 -0
- package/commands/planExportJson/SKILL.md +159 -0
- package/commands/planExportSummary/SKILL.md +158 -0
- package/commands/planNew/SKILL.md +641 -0
- package/commands/planNext/SKILL.md +1200 -0
- package/commands/planSettingsAutoSync/SKILL.md +199 -0
- package/commands/planSettingsLanguage/SKILL.md +201 -0
- package/commands/planSettingsReset/SKILL.md +237 -0
- package/commands/planSettingsShow/SKILL.md +482 -0
- package/commands/planSpec/SKILL.md +929 -0
- package/commands/planUpdate/SKILL.md +2518 -0
- package/commands/team/SKILL.md +740 -0
- package/locales/en.json +1499 -0
- package/locales/ka.json +1499 -0
- package/package.json +48 -0
- package/templates/PROJECT_PLAN.template.md +157 -0
- package/templates/backend-api.template.md +562 -0
- package/templates/frontend-spa.template.md +610 -0
- package/templates/fullstack.template.md +397 -0
- package/templates/ka/backend-api.template.md +562 -0
- package/templates/ka/frontend-spa.template.md +610 -0
- package/templates/ka/fullstack.template.md +397 -0
- package/templates/sections/architecture.md +21 -0
- package/templates/sections/overview.md +15 -0
- package/templates/sections/tasks.md +22 -0
- package/templates/sections/tech-stack.md +19 -0
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pfGithubBranch
|
|
3
|
+
description: Create a git branch from a PlanFlow task ID
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PlanFlow GitHub Branch
|
|
7
|
+
|
|
8
|
+
Create a git branch from a task ID. The branch name is automatically generated from the task ID and title.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
/pfGithubBranch T2.1 # Create branch: feature/T2.1-task-title-slug
|
|
14
|
+
/pfGithubBranch T2.1 --checkout # Create and checkout (default)
|
|
15
|
+
/pfGithubBranch T2.1 --no-checkout # Create without checkout
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Step 0: Load Configuration
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
function getMergedConfig() {
|
|
22
|
+
let globalConfig = {}
|
|
23
|
+
let localConfig = {}
|
|
24
|
+
|
|
25
|
+
const globalPath = expandPath("~/.config/claude/plan-plugin-config.json")
|
|
26
|
+
if (fileExists(globalPath)) {
|
|
27
|
+
try { globalConfig = JSON.parse(readFile(globalPath)) } catch (e) {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (fileExists("./.plan-config.json")) {
|
|
31
|
+
try { localConfig = JSON.parse(readFile("./.plan-config.json")) } catch (e) {}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
...globalConfig,
|
|
36
|
+
...localConfig,
|
|
37
|
+
cloud: {
|
|
38
|
+
...(globalConfig.cloud || {}),
|
|
39
|
+
...(localConfig.cloud || {})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const config = getMergedConfig()
|
|
45
|
+
const language = config.language || "en"
|
|
46
|
+
const cloudConfig = config.cloud || {}
|
|
47
|
+
const isAuthenticated = !!cloudConfig.apiToken
|
|
48
|
+
const apiUrl = cloudConfig.apiUrl || "https://api.planflow.tools"
|
|
49
|
+
const projectId = cloudConfig.projectId || null
|
|
50
|
+
const githubConfig = localConfig.github || {}
|
|
51
|
+
|
|
52
|
+
const t = JSON.parse(readFile(`locales/${language}.json`))
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Step 0.5: Show Notification Badge
|
|
56
|
+
|
|
57
|
+
Only if authenticated AND linked to a project:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
if [ -n "$TOKEN" ] && [ -n "$PROJECT_ID" ]; then
|
|
61
|
+
RESPONSE=$(curl -s --connect-timeout 3 --max-time 5 \
|
|
62
|
+
-X GET \
|
|
63
|
+
-H "Accept: application/json" \
|
|
64
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
65
|
+
"${API_URL}/projects/${PROJECT_ID}/notifications?limit=1&unread=true" 2>/dev/null)
|
|
66
|
+
|
|
67
|
+
if [ $? -eq 0 ]; then
|
|
68
|
+
UNREAD_COUNT=$(echo "$RESPONSE" | grep -o '"unreadCount":[0-9]*' | grep -o '[0-9]*')
|
|
69
|
+
if [ -n "$UNREAD_COUNT" ] && [ "$UNREAD_COUNT" -gt 0 ]; then
|
|
70
|
+
echo "🔔 $UNREAD_COUNT unread notification(s) — /pfNotifications to view"
|
|
71
|
+
echo ""
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Step 1: Parse Arguments
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const args = commandArgs.trim()
|
|
81
|
+
const taskIdPattern = /^T\d+\.\d+$/i
|
|
82
|
+
|
|
83
|
+
// Parse task ID and flags
|
|
84
|
+
const parts = args.split(/\s+/)
|
|
85
|
+
const taskId = parts[0]
|
|
86
|
+
const flags = parts.slice(1)
|
|
87
|
+
|
|
88
|
+
const noCheckout = flags.includes("--no-checkout")
|
|
89
|
+
const shouldCheckout = !noCheckout // Default is to checkout
|
|
90
|
+
|
|
91
|
+
if (!taskId || !taskIdPattern.test(taskId)) {
|
|
92
|
+
showUsageError()
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Step 2: Validate Prerequisites
|
|
97
|
+
|
|
98
|
+
### 2a: Check Authentication
|
|
99
|
+
|
|
100
|
+
If not authenticated:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
104
|
+
│ ❌ ERROR │
|
|
105
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
106
|
+
│ │
|
|
107
|
+
│ {t.commands.sync.notAuthenticated} │
|
|
108
|
+
│ │
|
|
109
|
+
│ You must be logged in to create branches from tasks. │
|
|
110
|
+
│ │
|
|
111
|
+
│ 💡 Next Steps: │
|
|
112
|
+
│ • /pfLogin Sign in to PlanFlow │
|
|
113
|
+
│ │
|
|
114
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 2b: Check Project Link
|
|
118
|
+
|
|
119
|
+
If not linked to a cloud project:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
123
|
+
│ ❌ ERROR │
|
|
124
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
125
|
+
│ │
|
|
126
|
+
│ {t.commands.sync.notLinked} │
|
|
127
|
+
│ │
|
|
128
|
+
│ You must link to a cloud project first. │
|
|
129
|
+
│ │
|
|
130
|
+
│ 💡 Next Steps: │
|
|
131
|
+
│ • /pfCloudLink Link to existing project │
|
|
132
|
+
│ • /pfCloudNew Create new cloud project │
|
|
133
|
+
│ │
|
|
134
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 2c: Check GitHub Integration
|
|
138
|
+
|
|
139
|
+
If no GitHub repository is linked:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
143
|
+
│ ❌ ERROR │
|
|
144
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
145
|
+
│ │
|
|
146
|
+
│ {t.github.notLinked} │
|
|
147
|
+
│ │
|
|
148
|
+
│ No GitHub repository is linked to this project. │
|
|
149
|
+
│ │
|
|
150
|
+
│ 💡 To link a repository: │
|
|
151
|
+
│ • /pfGithubLink owner/repo │
|
|
152
|
+
│ │
|
|
153
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2d: Check Git Repository
|
|
157
|
+
|
|
158
|
+
Verify we're in a git repository:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
162
|
+
echo "❌ Not a git repository"
|
|
163
|
+
echo ""
|
|
164
|
+
echo "Please run this command from within a git repository."
|
|
165
|
+
exit 1
|
|
166
|
+
fi
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 2e: Check for Uncommitted Changes
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
|
173
|
+
echo "⚠️ Warning: You have uncommitted changes"
|
|
174
|
+
echo ""
|
|
175
|
+
echo "Consider committing or stashing your changes before switching branches."
|
|
176
|
+
echo ""
|
|
177
|
+
fi
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Step 3: Fetch Task Details
|
|
181
|
+
|
|
182
|
+
### 3a: Try Cloud API First
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" \
|
|
186
|
+
--connect-timeout 5 \
|
|
187
|
+
--max-time 10 \
|
|
188
|
+
-X GET \
|
|
189
|
+
-H "Accept: application/json" \
|
|
190
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
191
|
+
"${API_URL}/projects/${PROJECT_ID}/tasks/${TASK_ID}")
|
|
192
|
+
|
|
193
|
+
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
|
194
|
+
BODY=$(echo "$RESPONSE" | sed '$d')
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 3b: Fallback to Local PROJECT_PLAN.md
|
|
198
|
+
|
|
199
|
+
If cloud API fails or task not found, parse from local file:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# Extract task title from PROJECT_PLAN.md
|
|
203
|
+
TASK_LINE=$(grep -E "^\s*####\s*\*\*${TASK_ID}\*\*:" PROJECT_PLAN.md | head -1)
|
|
204
|
+
# Or from table format:
|
|
205
|
+
# TASK_LINE=$(grep -E "^\|\s*${TASK_ID}\s*\|" PROJECT_PLAN.md | head -1)
|
|
206
|
+
|
|
207
|
+
if [ -z "$TASK_LINE" ]; then
|
|
208
|
+
echo "❌ Task not found: $TASK_ID"
|
|
209
|
+
exit 1
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
# Extract task title
|
|
213
|
+
TASK_TITLE=$(echo "$TASK_LINE" | sed 's/.*\*\*:\s*//' | sed 's/\s*$//')
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 3c: Generate Branch Name
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Slugify the task title
|
|
220
|
+
slugify() {
|
|
221
|
+
echo "$1" | \
|
|
222
|
+
tr '[:upper:]' '[:lower:]' | \
|
|
223
|
+
sed 's/[^a-z0-9]/-/g' | \
|
|
224
|
+
sed 's/--*/-/g' | \
|
|
225
|
+
sed 's/^-//' | \
|
|
226
|
+
sed 's/-$//' | \
|
|
227
|
+
cut -c1-50 # Limit length
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
TASK_SLUG=$(slugify "$TASK_TITLE")
|
|
231
|
+
BRANCH_NAME="feature/${TASK_ID}-${TASK_SLUG}"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Step 4: Create Branch
|
|
235
|
+
|
|
236
|
+
### 4a: Check if Branch Already Exists
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Check local branches
|
|
240
|
+
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
|
|
241
|
+
BRANCH_EXISTS="local"
|
|
242
|
+
fi
|
|
243
|
+
|
|
244
|
+
# Check remote branches
|
|
245
|
+
if git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME"; then
|
|
246
|
+
BRANCH_EXISTS="remote"
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
if [ -n "$BRANCH_EXISTS" ]; then
|
|
250
|
+
echo "⚠️ Branch already exists ($BRANCH_EXISTS): $BRANCH_NAME"
|
|
251
|
+
echo ""
|
|
252
|
+
if [ "$SHOULD_CHECKOUT" = "true" ]; then
|
|
253
|
+
echo "Checking out existing branch..."
|
|
254
|
+
git checkout "$BRANCH_NAME"
|
|
255
|
+
fi
|
|
256
|
+
exit 0
|
|
257
|
+
fi
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 4b: Create the Branch
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# Create branch from current HEAD
|
|
264
|
+
git branch "$BRANCH_NAME"
|
|
265
|
+
|
|
266
|
+
if [ $? -ne 0 ]; then
|
|
267
|
+
echo "❌ Failed to create branch: $BRANCH_NAME"
|
|
268
|
+
exit 1
|
|
269
|
+
fi
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### 4c: Checkout if Requested
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
if [ "$SHOULD_CHECKOUT" = "true" ]; then
|
|
276
|
+
git checkout "$BRANCH_NAME"
|
|
277
|
+
|
|
278
|
+
if [ $? -ne 0 ]; then
|
|
279
|
+
echo "⚠️ Branch created but checkout failed"
|
|
280
|
+
echo "Branch: $BRANCH_NAME"
|
|
281
|
+
exit 1
|
|
282
|
+
fi
|
|
283
|
+
fi
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Step 5: Show Success Output
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
290
|
+
│ ✅ SUCCESS │
|
|
291
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
292
|
+
│ │
|
|
293
|
+
│ {t.github.branch.created} │
|
|
294
|
+
│ │
|
|
295
|
+
│ ── Branch Created ────────────────────────────────────────────────────── │
|
|
296
|
+
│ │
|
|
297
|
+
│ 📝 Task: T2.1 - Implement login API │
|
|
298
|
+
│ 🌿 Branch: feature/T2.1-implement-login-api │
|
|
299
|
+
│ 📂 Status: Checked out ✓ │
|
|
300
|
+
│ │
|
|
301
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
302
|
+
│ │
|
|
303
|
+
│ 💡 What's Next? │
|
|
304
|
+
│ │
|
|
305
|
+
│ Start working on the task: │
|
|
306
|
+
│ • /planUpdate T2.1 start │
|
|
307
|
+
│ │
|
|
308
|
+
│ When done, create a pull request: │
|
|
309
|
+
│ • git push -u origin feature/T2.1-implement-login-api │
|
|
310
|
+
│ • /pfGithubPr T2.1 │
|
|
311
|
+
│ │
|
|
312
|
+
│ 💡 Tip: Include "Closes T2.1" in your PR description │
|
|
313
|
+
│ to auto-complete the task when the PR is merged! │
|
|
314
|
+
│ │
|
|
315
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Error Handling
|
|
319
|
+
|
|
320
|
+
### Task Not Found
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
324
|
+
│ ❌ ERROR │
|
|
325
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
326
|
+
│ │
|
|
327
|
+
│ {t.github.branch.taskNotFound} │
|
|
328
|
+
│ │
|
|
329
|
+
│ Task not found: T99.1 │
|
|
330
|
+
│ │
|
|
331
|
+
│ Make sure the task exists in your PROJECT_PLAN.md or cloud project. │
|
|
332
|
+
│ │
|
|
333
|
+
│ 💡 Try: │
|
|
334
|
+
│ • /pfSyncPush Sync your local tasks to cloud │
|
|
335
|
+
│ • Check PROJECT_PLAN.md for valid task IDs │
|
|
336
|
+
│ │
|
|
337
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Invalid Task ID Format
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
344
|
+
│ ❌ ERROR │
|
|
345
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
346
|
+
│ │
|
|
347
|
+
│ {t.github.branch.invalidTaskId} │
|
|
348
|
+
│ │
|
|
349
|
+
│ Invalid task ID format: {input} │
|
|
350
|
+
│ │
|
|
351
|
+
│ Task ID should be like: T1.1, T2.3, T10.5 │
|
|
352
|
+
│ │
|
|
353
|
+
│ Usage: │
|
|
354
|
+
│ • /pfGithubBranch T2.1 │
|
|
355
|
+
│ • /pfGithubBranch T2.1 --no-checkout │
|
|
356
|
+
│ │
|
|
357
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Git Error
|
|
361
|
+
|
|
362
|
+
```
|
|
363
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
364
|
+
│ ❌ ERROR │
|
|
365
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
366
|
+
│ │
|
|
367
|
+
│ {t.github.branch.gitError} │
|
|
368
|
+
│ │
|
|
369
|
+
│ Git operation failed. │
|
|
370
|
+
│ │
|
|
371
|
+
│ Error: {error_message} │
|
|
372
|
+
│ │
|
|
373
|
+
│ 💡 Try: │
|
|
374
|
+
│ • Make sure you're in a git repository │
|
|
375
|
+
│ • Check for uncommitted changes │
|
|
376
|
+
│ • Verify you have write permissions │
|
|
377
|
+
│ │
|
|
378
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Network Error
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
╭──────────────────────────────────────────────────────────────────────────────╮
|
|
385
|
+
│ ⚠️ WARNING │
|
|
386
|
+
├──────────────────────────────────────────────────────────────────────────────┤
|
|
387
|
+
│ │
|
|
388
|
+
│ Could not fetch task from cloud. Using local PROJECT_PLAN.md. │
|
|
389
|
+
│ │
|
|
390
|
+
│ Branch created from local task data. │
|
|
391
|
+
│ │
|
|
392
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Translation Keys
|
|
396
|
+
|
|
397
|
+
Add to `locales/en.json` and `locales/ka.json`:
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"github": {
|
|
402
|
+
"branch": {
|
|
403
|
+
"title": "Create Branch",
|
|
404
|
+
"created": "Branch created successfully!",
|
|
405
|
+
"alreadyExists": "Branch already exists.",
|
|
406
|
+
"checkedOut": "Checked out to branch.",
|
|
407
|
+
"taskNotFound": "Task not found.",
|
|
408
|
+
"invalidTaskId": "Invalid task ID format.",
|
|
409
|
+
"gitError": "Git operation failed.",
|
|
410
|
+
"notGitRepo": "Not a git repository.",
|
|
411
|
+
"uncommittedChanges": "You have uncommitted changes.",
|
|
412
|
+
"usage": "Usage: /pfGithubBranch <task-id>",
|
|
413
|
+
"example": "Example: /pfGithubBranch T2.1",
|
|
414
|
+
"task": "Task:",
|
|
415
|
+
"branch": "Branch:",
|
|
416
|
+
"status": "Status:",
|
|
417
|
+
"whatsNext": "What's Next?",
|
|
418
|
+
"startWorking": "Start working on the task:",
|
|
419
|
+
"whenDone": "When done, create a pull request:",
|
|
420
|
+
"autoCompleteTip": "Include \"Closes TX.X\" in your PR description to auto-complete the task!"
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Full Bash Implementation
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
#!/bin/bash
|
|
430
|
+
|
|
431
|
+
# Step 0: Load config
|
|
432
|
+
GLOBAL_CONFIG_PATH="$HOME/.config/claude/plan-plugin-config.json"
|
|
433
|
+
LOCAL_CONFIG_PATH="./.plan-config.json"
|
|
434
|
+
|
|
435
|
+
# Read configs and merge
|
|
436
|
+
if [ -f "$GLOBAL_CONFIG_PATH" ]; then
|
|
437
|
+
API_TOKEN=$(jq -r '.cloud.apiToken // empty' "$GLOBAL_CONFIG_PATH")
|
|
438
|
+
API_URL=$(jq -r '.cloud.apiUrl // "https://api.planflow.tools"' "$GLOBAL_CONFIG_PATH")
|
|
439
|
+
fi
|
|
440
|
+
|
|
441
|
+
if [ -f "$LOCAL_CONFIG_PATH" ]; then
|
|
442
|
+
PROJECT_ID=$(jq -r '.cloud.projectId // empty' "$LOCAL_CONFIG_PATH")
|
|
443
|
+
GITHUB_OWNER=$(jq -r '.github.owner // empty' "$LOCAL_CONFIG_PATH")
|
|
444
|
+
GITHUB_REPO=$(jq -r '.github.repo // empty' "$LOCAL_CONFIG_PATH")
|
|
445
|
+
# Local can override global
|
|
446
|
+
LOCAL_TOKEN=$(jq -r '.cloud.apiToken // empty' "$LOCAL_CONFIG_PATH")
|
|
447
|
+
[ -n "$LOCAL_TOKEN" ] && API_TOKEN="$LOCAL_TOKEN"
|
|
448
|
+
fi
|
|
449
|
+
|
|
450
|
+
# Parse arguments
|
|
451
|
+
TASK_ID="$1"
|
|
452
|
+
shift
|
|
453
|
+
NO_CHECKOUT=false
|
|
454
|
+
|
|
455
|
+
while [[ $# -gt 0 ]]; do
|
|
456
|
+
case "$1" in
|
|
457
|
+
--no-checkout)
|
|
458
|
+
NO_CHECKOUT=true
|
|
459
|
+
shift
|
|
460
|
+
;;
|
|
461
|
+
*)
|
|
462
|
+
shift
|
|
463
|
+
;;
|
|
464
|
+
esac
|
|
465
|
+
done
|
|
466
|
+
|
|
467
|
+
# Validate task ID format
|
|
468
|
+
if [ -z "$TASK_ID" ] || ! echo "$TASK_ID" | grep -qiE '^T[0-9]+\.[0-9]+$'; then
|
|
469
|
+
echo "❌ Invalid task ID format: $TASK_ID"
|
|
470
|
+
echo ""
|
|
471
|
+
echo "Task ID should be like: T1.1, T2.3, T10.5"
|
|
472
|
+
echo ""
|
|
473
|
+
echo "Usage: /pfGithubBranch <task-id>"
|
|
474
|
+
echo "Example: /pfGithubBranch T2.1"
|
|
475
|
+
exit 1
|
|
476
|
+
fi
|
|
477
|
+
|
|
478
|
+
# Normalize task ID to uppercase
|
|
479
|
+
TASK_ID=$(echo "$TASK_ID" | tr '[:lower:]' '[:upper:]')
|
|
480
|
+
|
|
481
|
+
# Validate prerequisites
|
|
482
|
+
if [ -z "$API_TOKEN" ]; then
|
|
483
|
+
echo "❌ Not authenticated. Run /pfLogin first."
|
|
484
|
+
exit 1
|
|
485
|
+
fi
|
|
486
|
+
|
|
487
|
+
if [ -z "$PROJECT_ID" ]; then
|
|
488
|
+
echo "❌ No project linked. Run /pfCloudLink first."
|
|
489
|
+
exit 1
|
|
490
|
+
fi
|
|
491
|
+
|
|
492
|
+
# Check if in git repo
|
|
493
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
494
|
+
echo "❌ Not a git repository"
|
|
495
|
+
echo ""
|
|
496
|
+
echo "Please run this command from within a git repository."
|
|
497
|
+
exit 1
|
|
498
|
+
fi
|
|
499
|
+
|
|
500
|
+
# Check for uncommitted changes
|
|
501
|
+
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
|
|
502
|
+
echo "⚠️ Warning: You have uncommitted changes"
|
|
503
|
+
echo " Consider committing or stashing before switching branches."
|
|
504
|
+
echo ""
|
|
505
|
+
fi
|
|
506
|
+
|
|
507
|
+
# Slugify function
|
|
508
|
+
slugify() {
|
|
509
|
+
echo "$1" | \
|
|
510
|
+
tr '[:upper:]' '[:lower:]' | \
|
|
511
|
+
sed 's/[^a-z0-9]/-/g' | \
|
|
512
|
+
sed 's/--*/-/g' | \
|
|
513
|
+
sed 's/^-//' | \
|
|
514
|
+
sed 's/-$//' | \
|
|
515
|
+
cut -c1-50
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
# Try to get task from cloud API
|
|
519
|
+
TASK_TITLE=""
|
|
520
|
+
if [ -n "$API_TOKEN" ] && [ -n "$PROJECT_ID" ]; then
|
|
521
|
+
RESPONSE=$(curl -s --connect-timeout 5 --max-time 10 \
|
|
522
|
+
-X GET \
|
|
523
|
+
-H "Accept: application/json" \
|
|
524
|
+
-H "Authorization: Bearer $API_TOKEN" \
|
|
525
|
+
"${API_URL}/projects/${PROJECT_ID}/tasks/${TASK_ID}" 2>/dev/null)
|
|
526
|
+
|
|
527
|
+
if [ $? -eq 0 ]; then
|
|
528
|
+
TASK_TITLE=$(echo "$RESPONSE" | jq -r '.data.title // .title // empty' 2>/dev/null)
|
|
529
|
+
fi
|
|
530
|
+
fi
|
|
531
|
+
|
|
532
|
+
# Fallback to local PROJECT_PLAN.md
|
|
533
|
+
if [ -z "$TASK_TITLE" ] && [ -f "PROJECT_PLAN.md" ]; then
|
|
534
|
+
# Try markdown heading format: #### **T1.1**: Task Title
|
|
535
|
+
TASK_LINE=$(grep -E "^\s*####\s*\*\*${TASK_ID}\*\*:" PROJECT_PLAN.md | head -1)
|
|
536
|
+
if [ -n "$TASK_LINE" ]; then
|
|
537
|
+
TASK_TITLE=$(echo "$TASK_LINE" | sed 's/.*\*\*:\s*//' | sed 's/\s*$//')
|
|
538
|
+
else
|
|
539
|
+
# Try table format: | T1.1 | Task Title | ...
|
|
540
|
+
TASK_LINE=$(grep -E "^\|\s*${TASK_ID}\s*\|" PROJECT_PLAN.md | head -1)
|
|
541
|
+
if [ -n "$TASK_LINE" ]; then
|
|
542
|
+
TASK_TITLE=$(echo "$TASK_LINE" | cut -d'|' -f3 | sed 's/^\s*//' | sed 's/\s*$//')
|
|
543
|
+
fi
|
|
544
|
+
fi
|
|
545
|
+
fi
|
|
546
|
+
|
|
547
|
+
if [ -z "$TASK_TITLE" ]; then
|
|
548
|
+
echo "❌ Task not found: $TASK_ID"
|
|
549
|
+
echo ""
|
|
550
|
+
echo "Make sure the task exists in PROJECT_PLAN.md or is synced to cloud."
|
|
551
|
+
echo ""
|
|
552
|
+
echo "💡 Try:"
|
|
553
|
+
echo " • /pfSyncPush to sync local tasks"
|
|
554
|
+
echo " • Check PROJECT_PLAN.md for valid task IDs"
|
|
555
|
+
exit 1
|
|
556
|
+
fi
|
|
557
|
+
|
|
558
|
+
# Generate branch name
|
|
559
|
+
TASK_SLUG=$(slugify "$TASK_TITLE")
|
|
560
|
+
BRANCH_NAME="feature/${TASK_ID}-${TASK_SLUG}"
|
|
561
|
+
|
|
562
|
+
# Check if branch already exists
|
|
563
|
+
BRANCH_EXISTS=""
|
|
564
|
+
if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME" 2>/dev/null; then
|
|
565
|
+
BRANCH_EXISTS="local"
|
|
566
|
+
elif git show-ref --verify --quiet "refs/remotes/origin/$BRANCH_NAME" 2>/dev/null; then
|
|
567
|
+
BRANCH_EXISTS="remote"
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
if [ -n "$BRANCH_EXISTS" ]; then
|
|
571
|
+
echo "⚠️ Branch already exists ($BRANCH_EXISTS): $BRANCH_NAME"
|
|
572
|
+
echo ""
|
|
573
|
+
if [ "$NO_CHECKOUT" = "false" ]; then
|
|
574
|
+
echo "Checking out existing branch..."
|
|
575
|
+
git checkout "$BRANCH_NAME"
|
|
576
|
+
echo ""
|
|
577
|
+
echo "💡 Ready to continue working on:"
|
|
578
|
+
echo " 📝 Task: $TASK_ID - $TASK_TITLE"
|
|
579
|
+
echo " 🌿 Branch: $BRANCH_NAME"
|
|
580
|
+
fi
|
|
581
|
+
exit 0
|
|
582
|
+
fi
|
|
583
|
+
|
|
584
|
+
# Create the branch
|
|
585
|
+
if ! git branch "$BRANCH_NAME" 2>/dev/null; then
|
|
586
|
+
echo "❌ Failed to create branch: $BRANCH_NAME"
|
|
587
|
+
echo ""
|
|
588
|
+
echo "Git error occurred. Check your git configuration."
|
|
589
|
+
exit 1
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
# Checkout if requested
|
|
593
|
+
CHECKOUT_STATUS="Created (not checked out)"
|
|
594
|
+
if [ "$NO_CHECKOUT" = "false" ]; then
|
|
595
|
+
if git checkout "$BRANCH_NAME" 2>/dev/null; then
|
|
596
|
+
CHECKOUT_STATUS="Checked out ✓"
|
|
597
|
+
else
|
|
598
|
+
CHECKOUT_STATUS="Created but checkout failed"
|
|
599
|
+
fi
|
|
600
|
+
fi
|
|
601
|
+
|
|
602
|
+
# Success output
|
|
603
|
+
echo "✅ Branch created successfully!"
|
|
604
|
+
echo ""
|
|
605
|
+
echo "── Branch Created ──────────────────────────────────────────────────────"
|
|
606
|
+
echo ""
|
|
607
|
+
echo " 📝 Task: $TASK_ID - $TASK_TITLE"
|
|
608
|
+
echo " 🌿 Branch: $BRANCH_NAME"
|
|
609
|
+
echo " 📂 Status: $CHECKOUT_STATUS"
|
|
610
|
+
echo ""
|
|
611
|
+
echo "── What's Next? ─────────────────────────────────────────────────────────"
|
|
612
|
+
echo ""
|
|
613
|
+
echo " Start working on the task:"
|
|
614
|
+
echo " • /planUpdate $TASK_ID start"
|
|
615
|
+
echo ""
|
|
616
|
+
echo " When done, push and create a PR:"
|
|
617
|
+
echo " • git push -u origin $BRANCH_NAME"
|
|
618
|
+
echo " • /pfGithubPr $TASK_ID"
|
|
619
|
+
echo ""
|
|
620
|
+
echo " 💡 Tip: Include \"Closes $TASK_ID\" in your PR description"
|
|
621
|
+
echo " to auto-complete the task when merged!"
|
|
622
|
+
echo ""
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
## Testing
|
|
626
|
+
|
|
627
|
+
```bash
|
|
628
|
+
# Test 1: Create branch for valid task
|
|
629
|
+
/pfGithubBranch T2.1
|
|
630
|
+
# Expected: Creates feature/T2.1-task-title-slug and checks out
|
|
631
|
+
|
|
632
|
+
# Test 2: Create without checkout
|
|
633
|
+
/pfGithubBranch T2.1 --no-checkout
|
|
634
|
+
# Expected: Creates branch but stays on current branch
|
|
635
|
+
|
|
636
|
+
# Test 3: Branch already exists
|
|
637
|
+
/pfGithubBranch T2.1
|
|
638
|
+
# Expected: Checks out existing branch with warning
|
|
639
|
+
|
|
640
|
+
# Test 4: Invalid task ID
|
|
641
|
+
/pfGithubBranch invalid
|
|
642
|
+
# Expected: Error with format hint
|
|
643
|
+
|
|
644
|
+
# Test 5: Task not found
|
|
645
|
+
/pfGithubBranch T99.99
|
|
646
|
+
# Expected: Error with suggestions
|
|
647
|
+
|
|
648
|
+
# Test 6: Not in git repo
|
|
649
|
+
cd /tmp && /pfGithubBranch T2.1
|
|
650
|
+
# Expected: Error "Not a git repository"
|
|
651
|
+
|
|
652
|
+
# Test 7: Not authenticated
|
|
653
|
+
# (Clear token first)
|
|
654
|
+
/pfGithubBranch T2.1
|
|
655
|
+
# Expected: Error "Not authenticated"
|
|
656
|
+
|
|
657
|
+
# Test 8: GitHub not linked
|
|
658
|
+
# (Clear github config)
|
|
659
|
+
/pfGithubBranch T2.1
|
|
660
|
+
# Expected: Works using local PROJECT_PLAN.md only
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
## Success Criteria
|
|
664
|
+
|
|
665
|
+
- [ ] Creates branch from task ID with slugified title
|
|
666
|
+
- [ ] Checks out to new branch by default
|
|
667
|
+
- [ ] --no-checkout flag works
|
|
668
|
+
- [ ] Handles existing branches gracefully
|
|
669
|
+
- [ ] Falls back to local PROJECT_PLAN.md when cloud unavailable
|
|
670
|
+
- [ ] Shows helpful next steps after creation
|
|
671
|
+
- [ ] Validates prerequisites (auth, project, git)
|
|
672
|
+
- [ ] Works in both English and Georgian
|