claude-plugin-viban 1.1.1 → 1.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "viban",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "Terminal Kanban TUI for AI-human collaborative issue tracking",
5
5
  "author": {
6
6
  "name": "happy-nut"
package/README.md CHANGED
@@ -53,6 +53,7 @@ This separation keeps your workflow clean and prevents context switching.
53
53
  - python3 (macOS/Linux built-in)
54
54
  - [gum](https://github.com/charmbracelet/gum)
55
55
  - [jq](https://jqlang.github.io/jq/)
56
+ - [gh](https://cli.github.com/) (optional, for GitHub Issues sync)
56
57
 
57
58
  > **Tip:** If using Claude Code, run `/viban:setup` to install all dependencies automatically.
58
59
 
@@ -122,6 +123,7 @@ viban assign [session-id] # Assign top backlog issue
122
123
  viban review [id] # Move issue to review
123
124
  viban done <id> # Mark issue as done
124
125
  viban get <id> # Get issue details (JSON)
126
+ viban sync # Sync with external tracker
125
127
  viban help # Show help message
126
128
  ```
127
129
 
@@ -175,6 +177,60 @@ Analyzes a problem and creates a properly structured viban issue:
175
177
  - Feature requests
176
178
  - Converting free-form descriptions to structured issues
177
179
 
180
+ #### `/viban:sync` - Sync with external tracker
181
+
182
+ Two-way sync between your viban board and an external issue tracker (currently GitHub Issues):
183
+
184
+ 1. Checks sync configuration (or initializes on first run)
185
+ 2. Shows a dry-run preview of changes
186
+ 3. Asks for confirmation before syncing
187
+ 4. Reports sync results
188
+
189
+ **Use cases:**
190
+ - Importing GitHub Issues into your local board
191
+ - Keeping remote issues in sync with local status changes
192
+ - Team collaboration where some members use GitHub and others use viban
193
+
194
+ ## External Tracker Sync
195
+
196
+ viban can sync two-way with external issue trackers. Currently supported: **GitHub Issues**.
197
+
198
+ ### Quick Start
199
+
200
+ ```bash
201
+ # First time: initialize sync (auto-detects provider from git remote)
202
+ viban sync --init
203
+
204
+ # Preview what will change
205
+ viban sync --dry-run
206
+
207
+ # Run sync
208
+ viban sync
209
+ ```
210
+
211
+ > If using Claude Code, run `/viban:sync` for a guided experience with dry-run preview and confirmation.
212
+
213
+ ### How It Works
214
+
215
+ - **First sync** imports all open remote issues as backlog cards with external IDs (e.g. `github:42`)
216
+ - **Subsequent syncs** pull remote changes and push local status updates
217
+ - **New local cards** are NOT pushed unless `--push-new` is specified (local-first default)
218
+ - **Conflicts** (both sides changed) resolve to remote-wins by default
219
+
220
+ ### Status-to-Label Mapping
221
+
222
+ | viban status | GitHub label |
223
+ |-------------|-------------|
224
+ | `backlog` | *(no label)* |
225
+ | `in_progress` | `in-progress` |
226
+ | `review` | `review` |
227
+ | `done` | *(issue closed)* |
228
+
229
+ ### Requirements
230
+
231
+ - [gh CLI](https://cli.github.com/) installed and authenticated (`gh auth login`)
232
+ - Repository must have a GitHub remote
233
+
178
234
  ## Configuration
179
235
 
180
236
  ### Data Location (viban.json)
@@ -184,18 +240,14 @@ viban stores issues in `viban.json` with the following priority:
184
240
  | Priority | Location | When Used |
185
241
  |----------|----------|-----------|
186
242
  | 1 | `$VIBAN_DATA_DIR` | Explicit override via environment variable |
187
- | 2 | `.git/` (git common dir) | In a git repository (shared across worktrees) |
188
- | 3 | `.viban/` | Non-git directories (fallback) |
243
+ | 2 | `.viban/` | Default (all projects) |
189
244
 
190
- **Why Git Common Dir?**
191
- - Shared across git worktrees (parallel work sessions)
192
- - Survives branch switches
193
- - Single source of truth for the repository
245
+ **Auto-Migration:** If viban detects `viban.json` or `sync.json` in `.git/` (legacy location), it automatically moves them to `.viban/`.
194
246
 
195
- **For Non-Git Projects:**
247
+ **For Any Project:**
196
248
  ```bash
197
249
  # viban will automatically create .viban/viban.json in current directory
198
- cd /path/to/non-git-project
250
+ cd /path/to/project
199
251
  viban add "First issue" "Description" P2 feat
200
252
  # Creates: /path/to/non-git-project/.viban/viban.json
201
253
  ```
@@ -289,13 +341,22 @@ claude-plugin-viban/
289
341
  │ └── viban # Main TUI/CLI script
290
342
  ├── docs/
291
343
  │ └── CLAUDE.md # Claude Code integration guide
344
+ ├── commands/
345
+ │ └── sync.md # /viban:sync command
292
346
  ├── scripts/
293
347
  │ ├── check-deps.sh # Dependency checker
348
+ │ ├── sync.sh # Core sync engine (provider-agnostic)
349
+ │ ├── providers/
350
+ │ │ └── github.sh # GitHub Issues provider
294
351
  │ └── tui_coprocess.py # Persistent Python coprocess for TUI rendering
295
352
  ├── skills/
296
353
  │ ├── assign/SKILL.md # /viban:assign skill
297
354
  │ ├── setup/SKILL.md # /viban:setup skill
355
+ │ ├── sync/SKILL.md # /viban:sync skill
298
356
  │ └── task/SKILL.md # /viban:task skill
357
+ ├── tests/
358
+ │ ├── run_all.zsh # Test runner
359
+ │ └── test_sync.zsh # Sync engine tests
299
360
  ├── LICENSE # MIT License
300
361
  ├── package.json # NPM package config
301
362
  └── README.md # This file
package/bin/viban CHANGED
@@ -121,6 +121,8 @@ IN_TUI=false
121
121
  cleanup() {
122
122
  # Skip cleanup in subshells — EXIT trap fires in $() command substitutions
123
123
  [[ ${ZSH_SUBSHELL:-0} -gt 0 ]] && return
124
+ # Kill background sync if running
125
+ [[ -n "${_sync_pid:-}" ]] && kill "$_sync_pid" 2>/dev/null && wait "$_sync_pid" 2>/dev/null
124
126
  _stop_coproc
125
127
  printf '\033[?25h\033[0m'
126
128
  stty echo 2>/dev/null
@@ -193,24 +195,14 @@ export TERM=${TERM:-xterm-256color}
193
195
  # ============================================================
194
196
  # Priority:
195
197
  # 1. VIBAN_DATA_DIR env var (explicit override)
196
- # 2. Git common dir (shared across worktrees)
197
- # 3. Project root .viban/ directory (non-git projects)
198
+ # 2. Project root .viban/ directory
198
199
 
199
200
  VIBAN_DATA_DIR="${VIBAN_DATA_DIR:-}"
200
201
  VIBAN_IS_GIT_REPO=false
202
+ git rev-parse --git-dir &>/dev/null && VIBAN_IS_GIT_REPO=true
201
203
 
202
204
  if [[ -z "$VIBAN_DATA_DIR" ]]; then
203
- # Try git common dir first (shared across worktrees)
204
- if _git_common="$(git rev-parse --git-common-dir 2>/dev/null)"; then
205
- VIBAN_IS_GIT_REPO=true
206
- if [[ -d "$_git_common" ]]; then
207
- VIBAN_DATA_DIR="$(cd "$_git_common" && pwd)"
208
- fi
209
- fi
210
- # Fallback: project root .viban directory
211
- if [[ -z "$VIBAN_DATA_DIR" ]]; then
212
- VIBAN_DATA_DIR="${PWD}/.viban"
213
- fi
205
+ VIBAN_DATA_DIR="${PWD}/.viban"
214
206
  fi
215
207
 
216
208
  VIBAN_JSON="${VIBAN_DATA_DIR}/viban.json"
@@ -218,13 +210,16 @@ VIBAN_JSON="${VIBAN_DATA_DIR}/viban.json"
218
210
  # Ensure data directory exists
219
211
  mkdir -p "$VIBAN_DATA_DIR"
220
212
 
221
- # Auto-migrate: .viban/viban.json -> .git/ when git repo is initialized
222
- if $VIBAN_IS_GIT_REPO; then
223
- _legacy_json="${PWD}/.viban/viban.json"
224
- if [[ -f "$_legacy_json" && ! -f "$VIBAN_JSON" ]]; then
225
- mv "$_legacy_json" "$VIBAN_JSON"
226
- rmdir "${PWD}/.viban" 2>/dev/null
227
- echo "✓ Migrated viban data from .viban/ to git directory"
213
+ # Auto-migrate: .git/viban.json -> .viban/ (legacy location)
214
+ if _git_common="$(git rev-parse --git-common-dir 2>/dev/null)"; then
215
+ _git_dir="$(cd "$_git_common" && pwd)"
216
+ if [[ -f "${_git_dir}/viban.json" && ! -f "$VIBAN_JSON" ]]; then
217
+ mv "${_git_dir}/viban.json" "$VIBAN_JSON"
218
+ echo "✓ Migrated viban.json from .git/ to .viban/"
219
+ fi
220
+ if [[ -f "${_git_dir}/sync.json" && ! -f "${VIBAN_DATA_DIR}/sync.json" ]]; then
221
+ mv "${_git_dir}/sync.json" "${VIBAN_DATA_DIR}/sync.json"
222
+ echo "✓ Migrated sync.json from .git/ to .viban/"
228
223
  fi
229
224
  fi
230
225
 
@@ -336,8 +331,9 @@ init_json() {
336
331
  if [[ ! -f "$VIBAN_JSON" ]]; then
337
332
  local max_wt_id=0
338
333
  if [[ -d "$VIBAN_DATA_DIR/worktrees" ]]; then
334
+ local wt_id
339
335
  for d in "$VIBAN_DATA_DIR/worktrees/"*(/N); do
340
- local wt_id="${d:t}"
336
+ wt_id="${d:t}"
341
337
  [[ "$wt_id" =~ ^[0-9]+$ ]] && (( wt_id > max_wt_id )) && max_wt_id=$wt_id
342
338
  done
343
339
  fi
@@ -700,14 +696,15 @@ build_column_lines() {
700
696
  done <<< "$_batch_output"
701
697
  else
702
698
  # All-ASCII fast path - no Python needed
699
+ local _t _mw _fc _d
703
700
  for (( _i=1; _i<=_n; _i++ )); do
704
- local _t="${_titles[$_i]}" _mw=${_title_max_ws[$_i]}
701
+ _t="${_titles[$_i]}" _mw=${_title_max_ws[$_i]}
705
702
  (( ${#_t} > _mw )) && _t="${_t:0:$_mw}"
706
703
  _short_titles+=("$_t")
707
- local _fc="${_title_pfxs[$_i]}${_t}"
704
+ _fc="${_title_pfxs[$_i]}${_t}"
708
705
  _title_cws+=(${#_fc})
709
706
 
710
- local _d="${_descs[$_i]}"
707
+ _d="${_descs[$_i]}"
711
708
  (( ${#_d} > _desc_max_w )) && _d="${_d:0:$_desc_max_w}"
712
709
  _short_descs+=("$_d")
713
710
  _desc_cws+=($((2 + ${#_d})))
@@ -717,29 +714,34 @@ build_column_lines() {
717
714
 
718
715
  # --- Pass 3: Render cards ---
719
716
  local shown=0
717
+ local id priority issue_type ext_id did
718
+ local spinner_prefix title_content title_pad
719
+ local desc_content desc_pad
720
+ local priority_tag priority_color type_tag type_color tags_w tags_pad
721
+ local border_color text_color desc_color
720
722
  for (( _i=1; _i<=_n; _i++ )); do
721
- local id="${_ids[$_i]}"
722
- local priority="${_priorities[$_i]}"
723
- local issue_type="${_types[$_i]}"
724
- local ext_id="${_ext_ids[$_i]}"
725
- local did; did=$(display_id "$id" "$ext_id")
723
+ id="${_ids[$_i]}"
724
+ priority="${_priorities[$_i]}"
725
+ issue_type="${_types[$_i]}"
726
+ ext_id="${_ext_ids[$_i]}"
727
+ did=$(display_id "$id" "$ext_id")
726
728
 
727
729
  # Title line
728
- local spinner_prefix=""
730
+ spinner_prefix=""
729
731
  [[ "$st" == "in_progress" ]] && spinner_prefix="${SPINNER_FRAMES[$((SPINNER_IDX % ${#SPINNER_FRAMES[@]} + 1))]} "
730
- local title_content=" ${spinner_prefix}${did} ${_short_titles[$_i]}"
731
- local title_pad=$((card_inner - ${_title_cws[$_i]}))
732
+ title_content=" ${spinner_prefix}${did} ${_short_titles[$_i]}"
733
+ title_pad=$((card_inner - ${_title_cws[$_i]}))
732
734
  (( title_pad < 0 )) && title_pad=0
733
735
 
734
736
  # Description line
735
- local desc_content=" ${_short_descs[$_i]}"
736
- local desc_pad=$((card_inner - ${_desc_cws[$_i]}))
737
+ desc_content=" ${_short_descs[$_i]}"
738
+ desc_pad=$((card_inner - ${_desc_cws[$_i]}))
737
739
  (( desc_pad < 0 )) && desc_pad=0
738
740
 
739
741
  # Priority and type tags
740
- local priority_tag="[$priority]"
741
- local priority_color="${PRIORITY_COLOR[$priority]:-$A_DIM}"
742
- local type_tag="" type_color="" tags_w=0
742
+ priority_tag="[$priority]"
743
+ priority_color="${PRIORITY_COLOR[$priority]:-$A_DIM}"
744
+ type_tag="" type_color="" tags_w=0
743
745
  if [[ -n "$issue_type" ]]; then
744
746
  type_tag="[${TYPE_LABEL[$issue_type]:-$issue_type}]"
745
747
  type_color="${TYPE_COLOR[$issue_type]:-$A_DIM}"
@@ -747,11 +749,11 @@ build_column_lines() {
747
749
  else
748
750
  tags_w=${#priority_tag}
749
751
  fi
750
- local tags_pad=$((card_inner - tags_w - 2))
752
+ tags_pad=$((card_inner - tags_w - 2))
751
753
 
752
- local border_color="$A_DIM"
753
- local text_color="$A_FG"
754
- local desc_color="$A_DIM"
754
+ border_color="$A_DIM"
755
+ text_color="$A_FG"
756
+ desc_color="$A_DIM"
755
757
  if (( is_col_selected && shown == card_sel )); then
756
758
  border_color="${A_SELECTED}"
757
759
  text_color="${A_BOLD}${A_ACCENT}"
@@ -1044,7 +1046,17 @@ delete_issue() {
1044
1046
  local id=$1
1045
1047
  local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
1046
1048
  local wt_dir="$VIBAN_DATA_DIR/worktrees/$id"
1049
+
1050
+ # Determine branch name: prefer issue-{num} when external_id present
1047
1051
  local branch="viban-$id"
1052
+ local _ext_id=$(get_ext_id "$id")
1053
+ if [[ -n "$_ext_id" && "$_ext_id" != "null" ]]; then
1054
+ local _issue_num="${_ext_id##*:}"
1055
+ if git -C "$repo_root" rev-parse --verify "issue-${_issue_num}" &>/dev/null 2>&1; then
1056
+ branch="issue-${_issue_num}"
1057
+ fi
1058
+ fi
1059
+
1048
1060
  if [[ -d "$wt_dir" ]]; then
1049
1061
  git -C "$repo_root" worktree remove "$wt_dir" --force 2>/dev/null
1050
1062
  git -C "$repo_root" branch -D "$branch" 2>/dev/null
@@ -1058,6 +1070,11 @@ level1_columns() {
1058
1070
  _start_coproc
1059
1071
  local col=0 card=0
1060
1072
 
1073
+ # Auto-sync state (120 iterations × 0.5s timeout = ~60s interval)
1074
+ local _sync_counter=0
1075
+ local _SYNC_INTERVAL=120
1076
+ local _sync_pid=""
1077
+
1061
1078
  # Hide cursor and disable input echo
1062
1079
  stty -echo 2>/dev/null
1063
1080
  printf '\033[?25l\033[2J\033[H'
@@ -1066,6 +1083,27 @@ level1_columns() {
1066
1083
  update_term_cache
1067
1084
 
1068
1085
  while true; do
1086
+ # Auto-sync: reap finished background sync
1087
+ if [[ -n "$_sync_pid" ]]; then
1088
+ if ! kill -0 "$_sync_pid" 2>/dev/null; then
1089
+ wait "$_sync_pid" 2>/dev/null
1090
+ _sync_pid=""
1091
+ fi
1092
+ fi
1093
+
1094
+ # Auto-sync: trigger when interval reached and sync configured
1095
+ ((_sync_counter++)) || true
1096
+ if (( _sync_counter >= _SYNC_INTERVAL )) && [[ -z "$_sync_pid" && -f "$VIBAN_DATA_DIR/sync.json" ]]; then
1097
+ _sync_counter=0
1098
+ local _sync_provider
1099
+ _sync_provider=$(jq -r '.provider // ""' "$VIBAN_DATA_DIR/sync.json" 2>/dev/null)
1100
+ if [[ -n "$_sync_provider" && "$_sync_provider" != "null" ]]; then
1101
+ VIBAN_JSON="$VIBAN_JSON" VIBAN_DATA_DIR="$VIBAN_DATA_DIR" \
1102
+ VIBAN_PROVIDER="$_sync_provider" VIBAN_SCRIPT_DIR="$VIBAN_SCRIPT_DIR" \
1103
+ bash "$VIBAN_SCRIPT_DIR/scripts/sync.sh" --auto &
1104
+ _sync_pid=$!
1105
+ fi
1106
+ fi
1069
1107
  # Cache JSON data once per frame
1070
1108
  local json_data=$(cat "$VIBAN_JSON")
1071
1109
 
@@ -1417,6 +1455,20 @@ cmd_add() {
1417
1455
  updated_at:$now
1418
1456
  }]' "$VIBAN_JSON" > "$VIBAN_JSON.tmp" && mv "$VIBAN_JSON.tmp" "$VIBAN_JSON"
1419
1457
  rm -f "$tmpjson"
1458
+
1459
+ # Auto-create remote issue if sync is configured and no ext_id provided
1460
+ if [[ -z "$ext_id" && -f "$VIBAN_DATA_DIR/sync.json" ]]; then
1461
+ local provider
1462
+ provider=$(jq -r '.provider // ""' "$VIBAN_DATA_DIR/sync.json" 2>/dev/null)
1463
+ if [[ -n "$provider" && "$provider" != "null" ]]; then
1464
+ local created_ext_id
1465
+ created_ext_id=$(VIBAN_JSON="$VIBAN_JSON" VIBAN_DATA_DIR="$VIBAN_DATA_DIR" \
1466
+ VIBAN_PROVIDER="$provider" VIBAN_SCRIPT_DIR="$VIBAN_SCRIPT_DIR" \
1467
+ bash "$VIBAN_SCRIPT_DIR/scripts/sync_create.sh" "$id" 2>/dev/null) || true
1468
+ [[ -n "$created_ext_id" ]] && ext_id="$created_ext_id"
1469
+ fi
1470
+ fi
1471
+
1420
1472
  local type_info=""
1421
1473
  [[ -n "$issue_type" ]] && type_info=" [$issue_type]"
1422
1474
  local attach_info=""
@@ -1466,7 +1518,17 @@ cmd_done() {
1466
1518
  # Cleanup worktree if exists
1467
1519
  local repo_root=$(git rev-parse --show-toplevel 2>/dev/null)
1468
1520
  local wt_dir="$VIBAN_DATA_DIR/worktrees/$1"
1521
+
1522
+ # Determine branch name: prefer issue-{num} when external_id present
1469
1523
  local branch="viban-$1"
1524
+ local _ext_id=$(get_ext_id "$1")
1525
+ if [[ -n "$_ext_id" && "$_ext_id" != "null" ]]; then
1526
+ local _issue_num="${_ext_id##*:}"
1527
+ if git -C "$repo_root" rev-parse --verify "issue-${_issue_num}" &>/dev/null 2>&1; then
1528
+ branch="issue-${_issue_num}"
1529
+ fi
1530
+ fi
1531
+
1470
1532
  if [[ -d "$wt_dir" ]]; then
1471
1533
  git -C "$repo_root" worktree remove "$wt_dir" --force 2>/dev/null
1472
1534
  git -C "$repo_root" branch -D "$branch" 2>/dev/null
package/commands/add.md CHANGED
@@ -4,78 +4,54 @@ description: "Register a problem as a viban issue"
4
4
 
5
5
  # /add - Register Issue
6
6
 
7
- Register a problem as a viban issue. Keep it lightweight — no codebase exploration, no heavy analysis.
7
+ Register a problem as a viban issue. No codebase exploration, no solutions — symptoms only.
8
8
 
9
- > **Principle**: Clarify symptoms only if vague. Don't explore code or propose solutions.
9
+ **Input**: `$ARGUMENTS`
10
10
 
11
- ## Input
11
+ ## Step 1: Clarify (only if vague)
12
12
 
13
- **User Input**: `$ARGUMENTS`
13
+ If clear enough (who/what/where), skip to Step 2. Otherwise, one AskUserQuestion:
14
+ - header: "Problem", question: "Can you describe the symptom more specifically?"
15
+ - options: context-appropriate (e.g. "Error/crash", "Feature not working", "Performance issue", "Let me describe")
14
16
 
15
- ## Step 1: Clarify (only if needed)
17
+ ## Step 2: Priority & Type
16
18
 
17
- If the user's description is **clear enough** to register (who/what/where), skip to Step 2.
19
+ Infer from description. Don't ask unless truly ambiguous.
18
20
 
19
- If **vague or ambiguous**, concretize with one AskUserQuestion:
21
+ | Priority | Condition | Type | When |
22
+ |----------|-----------|------|------|
23
+ | P0 | System down, data loss | bug | Something broken |
24
+ | P1 | Feature broken, errors | feat | New functionality |
25
+ | P2 | Performance, warnings | chore | Maintenance, config |
26
+ | P3 | Improvements, refactoring | refactor | Code restructuring |
20
27
 
21
- - header: "Problem"
22
- - question: "Can you describe the symptom more specifically?"
23
- - options based on context, e.g.:
24
- - "Error/crash on specific action"
25
- - "Feature not working as expected"
26
- - "Performance issue"
27
- - "Let me describe"
28
- - multiSelect: false
29
-
30
- Goal: get a **one-sentence symptom** clear enough for an assignee to understand.
31
-
32
- ## Step 2: Determine Priority & Type
33
-
34
- Infer from the description. Do NOT ask unless truly ambiguous.
35
-
36
- | Condition | Priority |
37
- |-----------|----------|
38
- | System down, data loss | P0 |
39
- | Feature broken, errors | P1 |
40
- | Performance, warnings | P2 |
41
- | Improvements, refactoring | P3 |
42
-
43
- | Type | When |
44
- |------|------|
45
- | bug | Something is broken |
46
- | feat | New functionality |
47
- | chore | Maintenance, config |
48
- | refactor | Code restructuring |
49
-
50
- ## Step 3: Check Workflow for Issue Numbering
28
+ ## Step 3: Issue Numbering
51
29
 
52
30
  ```bash
53
31
  [ -f ".viban/workflow.md" ] && cat ".viban/workflow.md"
54
32
  ```
55
33
 
56
- - If workflow says **Manual** issue numbering → ask user for an external ID (e.g. `PROJ-42`)
57
- - If workflow says **Auto** or no workflow exists → let viban auto-assign
58
- - If user provided a specific ID in `$ARGUMENTS` → use it regardless of workflow
34
+ - Workflow says **Manual** → ask user for external ID (e.g. `PROJ-42`)
35
+ - **Auto** or no workflow → let viban auto-assign
36
+ - ID in `$ARGUMENTS` → use it regardless
59
37
 
60
38
  ## Step 4: Register
61
39
 
62
- Write the description to a temp file, then register:
63
-
64
40
  ```bash
65
41
  cat > /tmp/viban-desc.md <<'VIBAN_EOF'
66
42
  ## Symptoms
67
- {concretized one-sentence symptom from Step 1}
68
- {additional context from user input, if any}
43
+ {one-sentence symptom}
44
+ {additional context, if any}
69
45
  VIBAN_EOF
70
46
 
71
47
  # Auto numbering (default)
72
48
  viban add "{title}" --desc-file /tmp/viban-desc.md --priority {priority} --type {type}
73
49
 
74
- # Manual numbering (when workflow specifies manual)
50
+ # Manual numbering (when workflow specifies)
75
51
  viban add "{title}" --desc-file /tmp/viban-desc.md --priority {priority} --type {type} --ext-id "{external_id}"
76
52
  ```
77
53
 
78
- **Why heredoc?** `<<'VIBAN_EOF'` prevents shell interpretation of backticks, `$`, etc.
54
+ Use `<<'VIBAN_EOF'` (quoted) to prevent shell interpretation.
79
55
 
80
56
  ## Step 5: Report
81
57
 
@@ -86,9 +62,38 @@ Issue #{id} registered
86
62
  Status: backlog
87
63
  ```
88
64
 
89
- ## Notes
65
+ ## Step 6: Suggest Plan Mode
66
+
67
+ **Skip** for trivial issues (typo, one-liner config, simple copy edit).
68
+ **Recommend** for everything else. Use AskUserQuestion:
69
+
70
+ - header: "Next step", question: "Want to start planning the solution now?"
71
+ - options:
72
+ - "Plan now (Recommended)" — Enter plan mode to analyze and design a solution
73
+ - "Later" — Just register, work on it later
74
+
75
+ **"Plan now"**: `EnterPlanMode` → after approval, save to `.viban/plans/{issue-id}.md`:
76
+
77
+ ```bash
78
+ mkdir -p .viban/plans
79
+ ```
80
+
81
+ ```markdown
82
+ # Plan: {issue title}
83
+ > Issue #{id} | {priority} | {type} | Created: {timestamp}
84
+
85
+ {full plan content}
86
+ ```
87
+
88
+ Report: `Plan saved to .viban/plans/{issue-id}.md — /viban:assign will auto-load it.`
89
+
90
+ **"Later"**: end skill.
91
+
92
+ > **Bias towards planning.** When in doubt, suggest plan mode.
93
+
94
+ ## Rules
90
95
 
91
- - **No codebase exploration**the assignee does that during `/viban:assign`
92
- - **No solution proposals**focus on symptoms only
93
- - **Check duplicates** before registering: `viban list`
94
- - **Don't over-prioritize** — P0 is system-down only
96
+ - No codebase exploration — assignee does that in `/viban:assign`
97
+ - No solution proposals — symptoms only
98
+ - Check duplicates first: `viban list`
99
+ - P0 is system-down only