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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/bin/cli.js +169 -0
  4. package/bin/postinstall.js +87 -0
  5. package/commands/pfActivity/SKILL.md +725 -0
  6. package/commands/pfAssign/SKILL.md +623 -0
  7. package/commands/pfCloudLink/SKILL.md +192 -0
  8. package/commands/pfCloudList/SKILL.md +222 -0
  9. package/commands/pfCloudNew/SKILL.md +187 -0
  10. package/commands/pfCloudUnlink/SKILL.md +152 -0
  11. package/commands/pfComment/SKILL.md +227 -0
  12. package/commands/pfComments/SKILL.md +159 -0
  13. package/commands/pfConnectionStatus/SKILL.md +433 -0
  14. package/commands/pfDiscord/SKILL.md +740 -0
  15. package/commands/pfGithubBranch/SKILL.md +672 -0
  16. package/commands/pfGithubIssue/SKILL.md +963 -0
  17. package/commands/pfGithubLink/SKILL.md +859 -0
  18. package/commands/pfGithubPr/SKILL.md +1335 -0
  19. package/commands/pfGithubUnlink/SKILL.md +401 -0
  20. package/commands/pfLive/SKILL.md +185 -0
  21. package/commands/pfLogin/SKILL.md +249 -0
  22. package/commands/pfLogout/SKILL.md +155 -0
  23. package/commands/pfMyTasks/SKILL.md +198 -0
  24. package/commands/pfNotificationSettings/SKILL.md +619 -0
  25. package/commands/pfNotifications/SKILL.md +420 -0
  26. package/commands/pfNotificationsClear/SKILL.md +421 -0
  27. package/commands/pfReact/SKILL.md +232 -0
  28. package/commands/pfSlack/SKILL.md +659 -0
  29. package/commands/pfSyncPull/SKILL.md +210 -0
  30. package/commands/pfSyncPush/SKILL.md +299 -0
  31. package/commands/pfSyncStatus/SKILL.md +212 -0
  32. package/commands/pfTeamInvite/SKILL.md +161 -0
  33. package/commands/pfTeamList/SKILL.md +253 -0
  34. package/commands/pfTeamRemove/SKILL.md +115 -0
  35. package/commands/pfTeamRole/SKILL.md +160 -0
  36. package/commands/pfTestWebhooks/SKILL.md +722 -0
  37. package/commands/pfUnassign/SKILL.md +134 -0
  38. package/commands/pfWhoami/SKILL.md +258 -0
  39. package/commands/pfWorkload/SKILL.md +219 -0
  40. package/commands/planExportCsv/SKILL.md +106 -0
  41. package/commands/planExportGithub/SKILL.md +222 -0
  42. package/commands/planExportJson/SKILL.md +159 -0
  43. package/commands/planExportSummary/SKILL.md +158 -0
  44. package/commands/planNew/SKILL.md +641 -0
  45. package/commands/planNext/SKILL.md +1200 -0
  46. package/commands/planSettingsAutoSync/SKILL.md +199 -0
  47. package/commands/planSettingsLanguage/SKILL.md +201 -0
  48. package/commands/planSettingsReset/SKILL.md +237 -0
  49. package/commands/planSettingsShow/SKILL.md +482 -0
  50. package/commands/planSpec/SKILL.md +929 -0
  51. package/commands/planUpdate/SKILL.md +2518 -0
  52. package/commands/team/SKILL.md +740 -0
  53. package/locales/en.json +1499 -0
  54. package/locales/ka.json +1499 -0
  55. package/package.json +48 -0
  56. package/templates/PROJECT_PLAN.template.md +157 -0
  57. package/templates/backend-api.template.md +562 -0
  58. package/templates/frontend-spa.template.md +610 -0
  59. package/templates/fullstack.template.md +397 -0
  60. package/templates/ka/backend-api.template.md +562 -0
  61. package/templates/ka/frontend-spa.template.md +610 -0
  62. package/templates/ka/fullstack.template.md +397 -0
  63. package/templates/sections/architecture.md +21 -0
  64. package/templates/sections/overview.md +15 -0
  65. package/templates/sections/tasks.md +22 -0
  66. 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