claude-plugin-viban 1.3.11 → 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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +209 -310
- package/assets/viban.png +0 -0
- package/bin/viban +35 -1488
- package/commands/assign.md +3 -5
- package/package.json +1 -1
- package/scripts/providers/github.sh +8 -0
- package/scripts/sync.sh +118 -21
- package/scripts/sync_create.sh +1 -0
- package/skills/assign/SKILL.md +7 -0
- package/skills/parallel-assign/SKILL.md +7 -0
package/commands/assign.md
CHANGED
|
@@ -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
|
|
14
|
-
3.
|
|
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
|
@@ -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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
662
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
663
|
+
main "$@"
|
|
664
|
+
fi
|
package/scripts/sync_create.sh
CHANGED
package/skills/assign/SKILL.md
CHANGED
|
@@ -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
|