claude-plugin-viban 1.3.12 → 1.3.13

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.
@@ -4,15 +4,13 @@ description: "Assign first backlog issue — clarify if unclear, then finish"
4
4
 
5
5
  > Tip: Run `/clear` before `/viban:assign` for a clean context.
6
6
 
7
- Run `/viban:assign` to pick up the next backlog issue.
7
+ Run `/viban:assign` to pick up and resolve the next backlog issue.
8
8
 
9
9
  Usage: `/viban:assign`
10
10
 
11
11
  What it does:
12
12
  1. Assigns the first backlog issue (by priority) to the current session
13
- 2. If the issue description is unclear or lacks context, interviews you to gather missing information and updates the issue
14
- 3. That's it no implementation, no branch creation
15
-
16
- This command is for **assignment and clarification only**. Use other tools to start working on the assigned issue.
13
+ 2. If the issue description is unclear, interviews you to gather missing information
14
+ 3. Analyzes, implements, verifies, and ships the fix (branch + PR + review)
17
15
 
18
16
  **IMPORTANT:** Never read or write `viban.json` directly — always use `viban` CLI commands.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin-viban",
3
- "version": "1.3.12",
3
+ "version": "1.3.13",
4
4
  "description": "Terminal Kanban TUI for AI-human collaborative issue tracking",
5
5
  "main": "bin/viban",
6
6
  "bin": {
@@ -248,6 +248,14 @@ provider_close_issue() {
248
248
  }
249
249
  }
250
250
 
251
+ provider_push_comment() {
252
+ local repo="$1" remote_id="$2" body="$3"
253
+ gh issue comment "$remote_id" --repo "$repo" --body "$body" 2>/dev/null || {
254
+ echo "Warning: Failed to push comment to issue #$remote_id"
255
+ return 1
256
+ }
257
+ }
258
+
251
259
  provider_ensure_labels() {
252
260
  local repo="$1"
253
261
 
package/scripts/sync.sh CHANGED
@@ -22,6 +22,7 @@ load_provider() {
22
22
  echo "Error: Provider script not found: $PROVIDER_SCRIPT"
23
23
  return 1
24
24
  fi
25
+ # shellcheck source=/dev/null
25
26
  source "$PROVIDER_SCRIPT"
26
27
 
27
28
  # Validate provider interface
@@ -29,6 +30,7 @@ load_provider() {
29
30
  provider_name provider_check_deps provider_check_auth
30
31
  provider_detect_config provider_fetch_issues provider_create_issue
31
32
  provider_update_issue provider_close_issue provider_ensure_labels
33
+ provider_push_comment
32
34
  )
33
35
  for func in "${required_funcs[@]}"; do
34
36
  if ! declare -f "$func" &>/dev/null; then
@@ -70,6 +72,90 @@ set_issue_meta() {
70
72
  write_sync_meta "$meta"
71
73
  }
72
74
 
75
+ # ============================================================
76
+ # Body Enrichment (blocked_by, sub-tasks)
77
+ # ============================================================
78
+
79
+ # Build enriched body with blocked_by and sub-task metadata for push
80
+ build_enriched_body() {
81
+ local card_id="$1"
82
+ jq -r --argjson cid "$card_id" '
83
+ . as $root |
84
+ ($root.issues[] | select((.id|tonumber)==$cid)) as $card |
85
+ ($card.description // "") as $desc |
86
+ ([$card.blocked_by // [] | .[] | . as $bid |
87
+ ($root.issues[] | select((.id|tonumber)==($bid|tonumber)) | .external_id // null) as $ext |
88
+ if ($ext != null and ($ext | startswith("github:"))) then
89
+ "#" + ($ext | ltrimstr("github:"))
90
+ else
91
+ "viban:#" + ($bid|tostring)
92
+ end
93
+ ] | join(", ")) as $blocked_refs |
94
+ ([$root.issues[] | select((.parent_id // -1) == $cid) |
95
+ (if .external_id != null and (.external_id | startswith("github:")) then
96
+ "#" + (.external_id | ltrimstr("github:"))
97
+ else
98
+ "viban:#" + (.id|tostring)
99
+ end) as $ref |
100
+ if .status == "done" then "- [x] " + $ref + ": " + .title
101
+ else "- [ ] " + $ref + ": " + .title
102
+ end
103
+ ]) as $subtask_lines |
104
+ if ($blocked_refs == "" and ($subtask_lines | length) == 0) then
105
+ $desc
106
+ else
107
+ $desc + "\n\n<!-- viban:meta:start -->\n---\n" +
108
+ (if $blocked_refs != "" then "**Blocked by:** " + $blocked_refs + "\n" else "" end) +
109
+ (if ($subtask_lines | length) > 0 then "**Sub-tasks:**\n" + ($subtask_lines | join("\n")) + "\n" else "" end) +
110
+ "<!-- viban:meta:end -->"
111
+ end
112
+ ' "$VIBAN_JSON"
113
+ }
114
+
115
+ # Strip viban metadata sections from body (used during pull)
116
+ strip_viban_meta() {
117
+ local body="$1"
118
+ if [[ "$body" != *"<!-- viban:meta:start -->"* ]]; then
119
+ printf '%s' "$body"
120
+ return
121
+ fi
122
+ printf '%s\n' "$body" | awk '
123
+ /<!-- viban:meta:start -->/ { exit }
124
+ { lines[++n] = $0 }
125
+ END {
126
+ while (n > 0 && lines[n] ~ /^[[:space:]]*$/) n--
127
+ for (i = 1; i <= n; i++) print lines[i]
128
+ }
129
+ '
130
+ }
131
+
132
+ # Push new comments to remote issue (tracks count to avoid duplicates)
133
+ push_new_comments() {
134
+ local repo="$1" remote_id="$2" viban_id="$3" card_json="$4"
135
+ local comment_count
136
+ comment_count=$(echo "$card_json" | jq '.comments // [] | length')
137
+ [[ "$comment_count" -eq 0 ]] && return 0
138
+ local meta synced_count
139
+ meta=$(get_issue_meta "$viban_id")
140
+ synced_count=$(echo "$meta" | jq -r '.synced_comment_count // 0')
141
+
142
+ if [[ "$comment_count" -gt "$synced_count" ]]; then
143
+ local comments
144
+ comments=$(echo "$card_json" | jq '.comments // []')
145
+ for ci in $(seq "$synced_count" $((comment_count - 1))); do
146
+ local comment_text comment_at
147
+ comment_text=$(echo "$comments" | jq -r ".[$ci].text")
148
+ comment_at=$(echo "$comments" | jq -r ".[$ci].created_at")
149
+ provider_push_comment "$repo" "$remote_id" "**[viban @ ${comment_at}]** ${comment_text}" || true
150
+ done
151
+ local meta_full
152
+ meta_full=$(read_sync_meta)
153
+ meta_full=$(echo "$meta_full" | jq --arg vid "$viban_id" --argjson cc "$comment_count" \
154
+ '.issues[$vid].synced_comment_count = $cc')
155
+ write_sync_meta "$meta_full"
156
+ fi
157
+ }
158
+
73
159
  # ============================================================
74
160
  # Sync Init
75
161
  # ============================================================
@@ -135,7 +221,7 @@ sync_pull() {
135
221
  local remote_id title description status priority type remote_updated
136
222
  remote_id=$(echo "$issue" | jq -r '.remote_id')
137
223
  title=$(echo "$issue" | jq -r '.title')
138
- description=$(echo "$issue" | jq -r '.description // ""')
224
+ description=$(strip_viban_meta "$(echo "$issue" | jq -r '.description // ""')")
139
225
  status=$(echo "$issue" | jq -r '.status')
140
226
  priority=$(echo "$issue" | jq -r '.priority // "P3"')
141
227
  type=$(echo "$issue" | jq -r '.type // ""')
@@ -152,7 +238,7 @@ sync_pull() {
152
238
  # New remote issue -> import (skip already-closed issues)
153
239
  if [[ "$status" == "done" ]]; then
154
240
  echo " == ${ext_id} \"${title}\" (closed, skipped)"
155
- ((unchanged++))
241
+ ((unchanged++)) || true
156
242
  continue
157
243
  fi
158
244
  if [[ "$dry_run" == "true" ]]; then
@@ -187,7 +273,7 @@ sync_pull() {
187
273
  set_issue_meta "$viban_id" "$remote_id" "$remote_updated" "$now"
188
274
  echo " <- ${ext_id} \"${title}\" (new card created)"
189
275
  fi
190
- ((pulled++))
276
+ ((pulled++)) || true
191
277
  else
192
278
  # Existing card - check for changes
193
279
  local viban_id viban_updated
@@ -208,7 +294,7 @@ sync_pull() {
208
294
  write_sync_meta "$meta_tmp"
209
295
  echo " <- ${ext_id} \"${title}\" (closed remotely, card removed)"
210
296
  fi
211
- ((pulled++))
297
+ ((pulled++)) || true
212
298
  continue
213
299
  fi
214
300
 
@@ -220,7 +306,7 @@ sync_pull() {
220
306
  # First time seeing this linked card in sync - record and skip
221
307
  set_issue_meta "$viban_id" "$remote_id" "$remote_updated" "$viban_updated"
222
308
  echo " == ${ext_id} \"${title}\" (tracking started)"
223
- ((unchanged++))
309
+ ((unchanged++)) || true
224
310
  continue
225
311
  fi
226
312
 
@@ -234,7 +320,7 @@ sync_pull() {
234
320
 
235
321
  if [[ "$remote_changed" == "false" && "$viban_changed" == "false" ]]; then
236
322
  echo " == ${ext_id} \"${title}\" (no changes)"
237
- ((unchanged++))
323
+ ((unchanged++)) || true
238
324
  elif [[ "$remote_changed" == "true" && "$viban_changed" == "false" ]]; then
239
325
  # Only remote changed -> pull
240
326
  if [[ "$dry_run" == "true" ]]; then
@@ -257,11 +343,11 @@ sync_pull() {
257
343
  set_issue_meta "$viban_id" "$remote_id" "$remote_updated" "$now"
258
344
  echo " <- ${ext_id} \"${title}\" (pulled remote changes)"
259
345
  fi
260
- ((updated++))
346
+ ((updated++)) || true
261
347
  elif [[ "$remote_changed" == "false" && "$viban_changed" == "true" ]]; then
262
348
  # Only viban changed -> will be handled in push phase
263
349
  echo " == ${ext_id} \"${title}\" (local changes, will push)"
264
- ((unchanged++))
350
+ ((unchanged++)) || true
265
351
  else
266
352
  # Both changed -> conflict resolution (remote wins by default)
267
353
  if [[ "$dry_run" == "true" ]]; then
@@ -284,7 +370,7 @@ sync_pull() {
284
370
  set_issue_meta "$viban_id" "$remote_id" "$remote_updated" "$now"
285
371
  echo " !! ${ext_id} \"${title}\" (conflict: remote wins)"
286
372
  fi
287
- ((pulled++))
373
+ ((pulled++)) || true
288
374
  fi
289
375
  fi
290
376
  done
@@ -319,7 +405,7 @@ sync_push() {
319
405
  local viban_id ext_id remote_id title status viban_updated
320
406
  viban_id=$(echo "$card" | jq -r '.id')
321
407
  ext_id=$(echo "$card" | jq -r '.external_id')
322
- remote_id="${ext_id#${provider_prefix}}"
408
+ remote_id="${ext_id#"${provider_prefix}"}"
323
409
  title=$(echo "$card" | jq -r '.title')
324
410
  status=$(echo "$card" | jq -r '.status')
325
411
  viban_updated=$(echo "$card" | jq -r '.updated_at')
@@ -328,7 +414,7 @@ sync_push() {
328
414
  local meta
329
415
  meta=$(get_issue_meta "$viban_id")
330
416
  if [[ -z "$meta" ]]; then
331
- ((unchanged++))
417
+ ((unchanged++)) || true
332
418
  continue
333
419
  fi
334
420
 
@@ -336,23 +422,28 @@ sync_push() {
336
422
  last_viban_updated=$(echo "$meta" | jq -r '.viban_updated_at')
337
423
 
338
424
  if [[ "$viban_updated" == "$last_viban_updated" ]]; then
339
- ((unchanged++))
425
+ ((unchanged++)) || true
340
426
  continue
341
427
  fi
342
428
 
343
429
  # Viban card changed since last sync -> push
344
430
  if [[ "$dry_run" == "true" ]]; then
345
431
  echo " -> ${ext_id} \"${title}\" (will push local changes)"
346
- ((pushed++))
432
+ ((pushed++)) || true
347
433
  else
348
- echo "$card" | jq '{
434
+ local enriched_body
435
+ enriched_body=$(build_enriched_body "$viban_id")
436
+ echo "$card" | jq --arg body "$enriched_body" '{
349
437
  title: .title,
350
- description: (.description // ""),
438
+ description: $body,
351
439
  status: .status,
352
440
  priority: (.priority // "P3"),
353
441
  type: (.type // "")
354
442
  }' | provider_update_issue "$repo" "$remote_id"
355
443
 
444
+ # Push new comments
445
+ push_new_comments "$repo" "$remote_id" "$viban_id" "$card"
446
+
356
447
  local now remote_updated
357
448
  now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
358
449
  # Fetch updated remote timestamp
@@ -360,7 +451,7 @@ sync_push() {
360
451
 
361
452
  set_issue_meta "$viban_id" "$remote_id" "$remote_updated" "$viban_updated"
362
453
  echo " -> ${ext_id} \"${title}\" (pushed)"
363
- ((pushed++))
454
+ ((pushed++)) || true
364
455
  fi
365
456
  done
366
457
 
@@ -384,12 +475,14 @@ sync_push() {
384
475
 
385
476
  if [[ "$dry_run" == "true" ]]; then
386
477
  echo " -> (new) #${viban_id} \"${title}\" (will create remote issue)"
387
- ((pushed++))
478
+ ((pushed++)) || true
388
479
  else
389
480
  local new_remote_id
390
- new_remote_id=$(echo "$card" | jq '{
481
+ local enriched_body
482
+ enriched_body=$(build_enriched_body "$viban_id")
483
+ new_remote_id=$(echo "$card" | jq --arg body "$enriched_body" '{
391
484
  title: .title,
392
- description: (.description // ""),
485
+ description: $body,
393
486
  status: .status,
394
487
  priority: (.priority // "P3"),
395
488
  type: (.type // "")
@@ -408,8 +501,10 @@ sync_push() {
408
501
  "$VIBAN_JSON" > "${VIBAN_JSON}.tmp" && mv "${VIBAN_JSON}.tmp" "$VIBAN_JSON"
409
502
 
410
503
  set_issue_meta "$viban_id" "$new_remote_id" "$now" "$now"
504
+ # Push comments for newly created issue
505
+ push_new_comments "$repo" "$new_remote_id" "$viban_id" "$card"
411
506
  echo " -> ${ext_id} #${viban_id} \"${title}\" (created remote issue)"
412
- ((pushed++))
507
+ ((pushed++)) || true
413
508
  fi
414
509
  done
415
510
  fi
@@ -564,4 +659,6 @@ main() {
564
659
  fi
565
660
  }
566
661
 
567
- main "$@"
662
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
663
+ main "$@"
664
+ fi
@@ -21,6 +21,7 @@ PROVIDER_SCRIPT="${VIBAN_SCRIPT_DIR}/scripts/providers/${VIBAN_PROVIDER}.sh"
21
21
  if [[ ! -f "$PROVIDER_SCRIPT" ]]; then
22
22
  exit 1
23
23
  fi
24
+ # shellcheck source=/dev/null
24
25
  source "$PROVIDER_SCRIPT"
25
26
 
26
27
  # Read card from viban.json
@@ -11,6 +11,13 @@ Assign the first backlog issue and execute the full resolution workflow. If the
11
11
 
12
12
  ---
13
13
 
14
+ ## Output Rules
15
+
16
+ - **Do NOT output any preamble.** No "Your Task:", "I'll now...", "Let me...", or task summaries before starting work.
17
+ - Start executing Step 0 immediately and silently.
18
+
19
+ ---
20
+
14
21
  ## Step 0: Read Workflow (CRITICAL)
15
22
 
16
23
  Check in priority order — first match wins:
@@ -13,6 +13,13 @@ Parallel resolution of independent backlog issues via git worktrees.
13
13
 
14
14
  ---
15
15
 
16
+ ## Output Rules
17
+
18
+ - **Do NOT output any preamble.** No "Your Task:", "I'll now...", "Let me...", or task summaries before starting work.
19
+ - Start executing Phase 0 immediately and silently.
20
+
21
+ ---
22
+
16
23
  ## Phase 0: SETUP
17
24
 
18
25
  ### 0.1 Read Workflow