@synity/bitrix-skills 1.3.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 (77) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/LICENSE +21 -0
  3. package/README.md +83 -0
  4. package/bin/bitrix-skills.js +3 -0
  5. package/dist/cli.js +1510 -0
  6. package/dist/features/bx-task/install.js +111 -0
  7. package/dist/features/task-sync/index.js +1053 -0
  8. package/package.json +69 -0
  9. package/src/features/bx/assets/SKILL.md +34 -0
  10. package/src/features/bx/feature.json +8 -0
  11. package/src/features/bx-calendar/assets/SKILL.md +61 -0
  12. package/src/features/bx-calendar/assets/availability.md +65 -0
  13. package/src/features/bx-calendar/assets/meeting.md +87 -0
  14. package/src/features/bx-calendar/assets/reminder.md +71 -0
  15. package/src/features/bx-calendar/assets/sync.md +70 -0
  16. package/src/features/bx-calendar/feature.json +10 -0
  17. package/src/features/bx-crm/assets/SKILL.md +59 -0
  18. package/src/features/bx-crm/assets/commerce.md +96 -0
  19. package/src/features/bx-crm/assets/onboard.md +127 -0
  20. package/src/features/bx-crm/assets/report.md +98 -0
  21. package/src/features/bx-crm/assets/research.md +71 -0
  22. package/src/features/bx-crm/feature.json +10 -0
  23. package/src/features/bx-task/assets/SKILL.md +148 -0
  24. package/src/features/bx-task/assets/lib/bx-api.sh +39 -0
  25. package/src/features/bx-task/assets/lib/bx-checklist.sh +127 -0
  26. package/src/features/bx-task/assets/lib/bx-resolve-task.sh +41 -0
  27. package/src/features/bx-task/assets/lib/bx-state.sh +131 -0
  28. package/src/features/bx-task/assets/lib/bx-tasks.sh +109 -0
  29. package/src/features/bx-task/assets/references/bootstrap.md +184 -0
  30. package/src/features/bx-task/assets/references/feature.md +97 -0
  31. package/src/features/bx-task/assets/references/init-templates/cli-tool.md +47 -0
  32. package/src/features/bx-task/assets/references/init-templates/generic.md +31 -0
  33. package/src/features/bx-task/assets/references/init-templates/library.md +45 -0
  34. package/src/features/bx-task/assets/references/init-templates/monorepo.md +38 -0
  35. package/src/features/bx-task/assets/references/init-templates/npm-package.md +40 -0
  36. package/src/features/bx-task/assets/references/init-templates/web-app.md +46 -0
  37. package/src/features/bx-task/assets/references/init.md +107 -0
  38. package/src/features/bx-task/assets/references/roadmap.md +93 -0
  39. package/src/features/bx-task/assets/references/summary.md +269 -0
  40. package/src/features/bx-task/assets/references/sync.md +104 -0
  41. package/src/features/bx-task/assets/references/time-log.md +214 -0
  42. package/src/features/bx-task/feature.json +10 -0
  43. package/src/features/bx-task/install.ts +117 -0
  44. package/src/features/task-sync/assets/docs/bitrix-task-reference.md +318 -0
  45. package/src/features/task-sync/assets/docs/bitrix-task-sync.md +254 -0
  46. package/src/features/task-sync/assets/githooks/commit-msg +44 -0
  47. package/src/features/task-sync/assets/githooks/install.sh +15 -0
  48. package/src/features/task-sync/assets/manifest.json +108 -0
  49. package/src/features/task-sync/assets/rules/00-bitrix-task-sync.md +161 -0
  50. package/src/features/task-sync/assets/scripts/bitrix-attach-files.sh +55 -0
  51. package/src/features/task-sync/assets/scripts/bitrix-lib.sh +540 -0
  52. package/src/features/task-sync/assets/scripts/bitrix-render-digest.sh +116 -0
  53. package/src/features/task-sync/assets/scripts/bitrix-session-check.sh +51 -0
  54. package/src/features/task-sync/assets/scripts/bitrix-session-sync.sh +89 -0
  55. package/src/features/task-sync/assets/scripts/bitrix-skill-end.sh +165 -0
  56. package/src/features/task-sync/assets/scripts/bitrix-skill-start.sh +58 -0
  57. package/src/features/task-sync/assets/scripts/lib/bb-formatter.sh +110 -0
  58. package/src/features/task-sync/assets/scripts/lib/bitrix-lib.sh +540 -0
  59. package/src/features/task-sync/assets/scripts/lib/time-helpers.sh +57 -0
  60. package/src/features/task-sync/assets/workflows/bitrix-sync.yml +85 -0
  61. package/src/features/task-sync/commands/install.ts +296 -0
  62. package/src/features/task-sync/commands/uninstall.ts +189 -0
  63. package/src/features/task-sync/commands/update.ts +11 -0
  64. package/src/features/task-sync/commands/verify.ts +141 -0
  65. package/src/features/task-sync/feature.json +12 -0
  66. package/src/features/task-sync/index.ts +121 -0
  67. package/src/features/task-sync/lib/dest-map.ts +96 -0
  68. package/src/features/task-sync/lib/drift-check.ts +47 -0
  69. package/src/features/task-sync/lib/file-ops.ts +36 -0
  70. package/src/features/task-sync/lib/manifest.ts +66 -0
  71. package/src/features/task-sync/lib/project-root.ts +38 -0
  72. package/src/features/task-sync/lib/settings-merge.ts +112 -0
  73. package/src/features/task-sync/lib/skill-refs.ts +106 -0
  74. package/src/features/task-sync/lib/task-id-finder.ts +31 -0
  75. package/src/features/task-sync/lib/token-extractor.ts +52 -0
  76. package/src/features/task-sync/lib/version.ts +36 -0
  77. package/src/features/task-sync/types.ts +40 -0
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env bash
2
+ # bx-state.sh — Project-local state file helpers for /bx:task skill.
3
+ #
4
+ # State file: .claude/bitrix-task-state.json (gitignored, project-local)
5
+ #
6
+ # Schema:
7
+ # {
8
+ # "claudeUserId": 614,
9
+ # "responsibleId": 614,
10
+ # "userMapping": { "github-username": <bitrix-uid> },
11
+ # "mainTaskId": <number>,
12
+ # "features": { "<name>": { "taskId": N, "status": "active|done|planned" } },
13
+ # "checklists": {
14
+ # "done": { "parentId": N },
15
+ # "doing": { "parentId": N },
16
+ # "plan": { "parentId": N }
17
+ # }
18
+ # }
19
+ #
20
+ # IMPORTANT: This schema deliberately omits a `webhook` field. Webhook URL must
21
+ # come from `BITRIX_WEBHOOK_URL` env var. If an old state file with `webhook`
22
+ # is detected, `bx_state_migrate` strips it (one-shot, backed up to .bak).
23
+ #
24
+ # Public functions:
25
+ # bx_state_file — print path to state file (respects $BX_TASK_STATE_FILE override)
26
+ # bx_state_exists — return 0 if state file exists, 1 otherwise
27
+ # bx_state_read — print full JSON (or "{}" if missing)
28
+ # bx_state_get <key> — print value at dot-path (jq filter), or empty
29
+ # bx_state_write <key> <value> — set scalar value (auto numberify if numeric)
30
+ # bx_state_write_json <key> <json> — set value from raw JSON
31
+ # bx_state_init — create skeleton state file if missing
32
+ # bx_state_migrate — strip deprecated `webhook` field (one-shot, with .bak)
33
+
34
+ bx_state_file() {
35
+ echo "${BX_TASK_STATE_FILE:-.claude/bitrix-task-state.json}"
36
+ }
37
+
38
+ bx_state_exists() {
39
+ local f; f=$(bx_state_file)
40
+ [[ -f "$f" ]]
41
+ }
42
+
43
+ bx_state_read() {
44
+ local f; f=$(bx_state_file)
45
+ if [[ -f "$f" ]]; then cat "$f"; else echo '{}'; fi
46
+ }
47
+
48
+ bx_state_get() {
49
+ local key="$1"
50
+ bx_state_read | jq -r ".${key} // empty"
51
+ }
52
+
53
+ bx_state_write() {
54
+ local key="$1" value="$2"
55
+ local f; f=$(bx_state_file)
56
+ mkdir -p "$(dirname "$f")"
57
+ local tmp; tmp=$(mktemp)
58
+ # Strip outer quotes (prevents "\"x\"" double-wrap)
59
+ value="${value#\"}"; value="${value%\"}"
60
+ if bx_state_read | jq --arg v "$value" ".${key} = (\$v | try tonumber catch \$v)" > "$tmp" 2>/dev/null; then
61
+ mv "$tmp" "$f"
62
+ else
63
+ rm -f "$tmp"; return 1
64
+ fi
65
+ }
66
+
67
+ bx_state_write_json() {
68
+ local key="$1" json_value="$2"
69
+ local f; f=$(bx_state_file)
70
+ mkdir -p "$(dirname "$f")"
71
+ local tmp; tmp=$(mktemp)
72
+ if bx_state_read | jq --argjson v "$json_value" ".${key} = \$v" > "$tmp" 2>/dev/null; then
73
+ mv "$tmp" "$f"
74
+ else
75
+ rm -f "$tmp"; return 1
76
+ fi
77
+ }
78
+
79
+ bx_state_init() {
80
+ if bx_state_exists; then return 0; fi
81
+ local f; f=$(bx_state_file)
82
+ mkdir -p "$(dirname "$f")"
83
+ cat > "$f" <<'EOF'
84
+ {
85
+ "claudeUserId": 614,
86
+ "responsibleId": 614,
87
+ "userMapping": {},
88
+ "mainTaskId": null,
89
+ "features": {},
90
+ "checklists": {}
91
+ }
92
+ EOF
93
+ }
94
+
95
+ bx_state_migrate() {
96
+ if ! bx_state_exists; then return 0; fi
97
+ local f; f=$(bx_state_file)
98
+ local has_webhook; has_webhook=$(jq -r 'has("webhook")' "$f" 2>/dev/null)
99
+ if [[ "$has_webhook" != "true" ]]; then return 0; fi
100
+
101
+ echo "⚠️ Detected deprecated 'webhook' field in $f — migrating to BITRIX_WEBHOOK_URL env var." >&2
102
+ echo " Backup written to $f.bak" >&2
103
+ cp "$f" "$f.bak"
104
+ local tmp; tmp=$(mktemp)
105
+ jq 'del(.webhook)' "$f" > "$tmp" && mv "$tmp" "$f"
106
+ }
107
+
108
+ # --- User mapping (Bitrix uid lookup) ---
109
+
110
+ _BX_GITHUB_USER=""
111
+ bx_get_github_user() {
112
+ if [[ -z "$_BX_GITHUB_USER" ]]; then
113
+ _BX_GITHUB_USER=$(gh api user --jq '.login' 2>/dev/null || echo "")
114
+ fi
115
+ echo "$_BX_GITHUB_USER"
116
+ }
117
+
118
+ bx_get_claude_user_id() {
119
+ local id; id=$(bx_state_get "claudeUserId")
120
+ echo "${id:-614}"
121
+ }
122
+
123
+ # Returns Bitrix uid for current GitHub user, falls back to responsibleId in state
124
+ bx_get_bitrix_user_id() {
125
+ local gh_user="${1:-$(bx_get_github_user)}"
126
+ if [[ -n "$gh_user" ]]; then
127
+ local mapped; mapped=$(bx_state_get "userMapping.\"$gh_user\"")
128
+ if [[ -n "$mapped" ]]; then echo "$mapped"; return; fi
129
+ fi
130
+ bx_state_get "responsibleId"
131
+ }
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env bash
2
+ # bx-tasks.sh — Task CRUD + comment helpers for /bx:task skill.
3
+ #
4
+ # Depends on:
5
+ # bx-api.sh — bx_call_v1, bx_call_v3, bx_preflight
6
+ # bx-state.sh — bx_get_claude_user_id, bx_get_bitrix_user_id
7
+ #
8
+ # Public functions:
9
+ # bx_task_get <id> — GET /tasks.task.get → raw JSON
10
+ # bx_task_create <title> <desc> [parentId] [responsibleId]
11
+ # — create task or subtask; returns task ID
12
+ # bx_task_update <taskId> <fieldsJson> — patch DESCRIPTION/STATUS/etc.
13
+ # bx_task_complete <taskId> — mark complete
14
+ # bx_task_comment <taskId> <message> — task.commentitem.add as Claude
15
+ # bx_task_notify <taskId> <message> — comment with [USER=N] mention prepended
16
+ # bx_task_elapsed <taskId> <seconds> [comment]
17
+ # — manual time entry
18
+ # bx_task_find_by_title <title> — search; print first matching ID or empty
19
+
20
+ # --- Internal: build temp JSON file, POST, clean up ---
21
+ _bx_post() {
22
+ local method="$1"; local body="$2"
23
+ local tmp; tmp=$(mktemp)
24
+ echo "$body" > "$tmp"
25
+ local resp; resp=$(curl -sS -m 15 -X POST "${BITRIX_WEBHOOK_URL%/}/${method}" \
26
+ -H "Content-Type: application/json" -d @"$tmp" 2>/dev/null)
27
+ rm -f "$tmp"
28
+ echo "$resp"
29
+ }
30
+
31
+ bx_task_get() {
32
+ local id="$1"
33
+ local body; body=$(jq -n --arg id "$id" '{taskId: ($id | tonumber)}')
34
+ _bx_post "tasks.task.get" "$body"
35
+ }
36
+
37
+ bx_task_create() {
38
+ local title="$1"; local desc="${2:-}"; local parent="${3:-0}"
39
+ local rid="${4:-$(bx_get_claude_user_id)}"
40
+
41
+ local body
42
+ if [[ "$parent" != "0" && -n "$parent" ]]; then
43
+ body=$(jq -n --arg t "$title" --arg d "$desc" --arg r "$rid" --arg p "$parent" \
44
+ '{fields: {TITLE: $t, DESCRIPTION: $d, RESPONSIBLE_ID: ($r|tonumber),
45
+ PARENT_ID: ($p|tonumber), ALLOW_TIME_TRACKING: "Y"}}')
46
+ else
47
+ body=$(jq -n --arg t "$title" --arg d "$desc" --arg r "$rid" \
48
+ '{fields: {TITLE: $t, DESCRIPTION: $d, RESPONSIBLE_ID: ($r|tonumber),
49
+ ALLOW_TIME_TRACKING: "Y"}}')
50
+ fi
51
+
52
+ local resp; resp=$(_bx_post "tasks.task.add" "$body")
53
+ # Description fields contain literal newlines → invalid JSON for jq.
54
+ # Use Python regex extraction (proven pattern from old skill).
55
+ echo "$resp" | python3 -c "import sys,re; m=re.search(r'\"id\":\"(\d+)\"', sys.stdin.read()); print(m.group(1) if m else '')"
56
+ }
57
+
58
+ # Update task fields. fieldsJson = JSON object (e.g., '{"DESCRIPTION":"..."}')
59
+ # Uses Python json.dumps to safely encode multiline BB-code (avoids jq newline parse errors).
60
+ bx_task_update() {
61
+ local id="$1"; local fields_json="$2"
62
+ python3 - "$id" "$fields_json" <<'PYEOF'
63
+ import sys, json, os, urllib.request
64
+ task_id = int(sys.argv[1])
65
+ fields = json.loads(sys.argv[2])
66
+ webhook = os.environ["BITRIX_WEBHOOK_URL"].rstrip("/")
67
+ payload = json.dumps({"taskId": task_id, "fields": fields}).encode("utf-8")
68
+ req = urllib.request.Request(f"{webhook}/tasks.task.update",
69
+ data=payload, headers={"Content-Type": "application/json"}, method="POST")
70
+ with urllib.request.urlopen(req, timeout=15) as r:
71
+ print(r.read().decode("utf-8")[:200])
72
+ PYEOF
73
+ }
74
+
75
+ bx_task_complete() {
76
+ local id="$1"
77
+ local body; body=$(jq -n --arg id "$id" '{taskId: ($id | tonumber)}')
78
+ _bx_post "tasks.task.complete" "$body" > /dev/null
79
+ }
80
+
81
+ bx_task_comment() {
82
+ local id="$1"; local msg="$2"
83
+ local aid; aid=$(bx_get_claude_user_id)
84
+ local body; body=$(jq -n --arg id "$id" --arg m "$msg" --arg a "$aid" \
85
+ '{TASKID: ($id|tonumber), FIELDS: {POST_MESSAGE: $m, AUTHOR_ID: ($a|tonumber)}}')
86
+ _bx_post "task.commentitem.add" "$body" > /dev/null
87
+ }
88
+
89
+ bx_task_notify() {
90
+ local id="$1"; local msg="$2"
91
+ local owner; owner=$(bx_get_bitrix_user_id)
92
+ local mention; mention=$(printf '[USER=%s][/USER] ' "$owner")
93
+ bx_task_comment "$id" "${mention}${msg}"
94
+ }
95
+
96
+ bx_task_elapsed() {
97
+ local id="$1"; local seconds="$2"; local comment="${3:-}"
98
+ local body; body=$(jq -n --arg id "$id" --arg s "$seconds" --arg c "$comment" \
99
+ '{TASKID: ($id|tonumber), ARFIELDS: {SECONDS: ($s|tonumber), COMMENT_TEXT: $c}}')
100
+ _bx_post "task.elapseditem.add" "$body" > /dev/null
101
+ }
102
+
103
+ # Search for task by title (exact %TITLE filter). Print first matching ID, or empty.
104
+ bx_task_find_by_title() {
105
+ local title="$1"
106
+ local body; body=$(jq -n --arg t "$title" '{filter: {"%TITLE": $t}, select: ["ID"]}')
107
+ local resp; resp=$(_bx_post "tasks.task.list" "$body")
108
+ echo "$resp" | jq -r '.result.tasks[0].id // empty' 2>/dev/null
109
+ }
@@ -0,0 +1,184 @@
1
+ # /bx:task bootstrap — Rich project init in Bitrix24
2
+
3
+ Creates the **main task** + per-phase **subtasks** + 3 root-level **checklist groups**
4
+ (Done / Doing / Plan) on Bitrix24, then writes a project-local state file at
5
+ `.claude/bitrix-task-state.json`. Description is rendered from a per-project-type
6
+ BB-code template.
7
+
8
+ ## Args
9
+
10
+ ```
11
+ /bx:task bootstrap [--type <npm-package|web-app|library|monorepo|cli-tool|generic>] [--dry-run] [--main-task-id N]
12
+ ```
13
+
14
+ | Flag | Purpose |
15
+ |------|---------|
16
+ | `--type` | Skip auto-detection; force template choice |
17
+ | `--dry-run` | Render preview + show payload; NO Bitrix writes, NO state file |
18
+ | `--main-task-id` | Reuse an existing task (skip create); only adds subtasks + checklists + state |
19
+
20
+ ## Prerequisite check (MANDATORY)
21
+
22
+ ```bash
23
+ MISSING=()
24
+ [[ ! -f docs/project-overview-pdr.md ]] && MISSING+=("project-overview-pdr.md")
25
+ [[ ! -f docs/system-architecture.md ]] && MISSING+=("system-architecture.md")
26
+ [[ ! -f docs/codebase-summary.md ]] && MISSING+=("codebase-summary.md")
27
+ if [[ ${#MISSING[@]} -gt 0 ]]; then
28
+ echo "⛔ Missing docs: ${MISSING[*]}"
29
+ echo " Run first: /ck:docs init (parallel codebase scan → generates docs/)"
30
+ echo " Then re-run: /bx:task bootstrap"
31
+ exit 1
32
+ fi
33
+ ```
34
+
35
+ Hard block. Never fall back to thin description. Skill chaining: `/ck:docs init` → `/bx:task bootstrap`.
36
+
37
+ ## Project-type detection
38
+
39
+ If `--type` not provided, run this decision tree in `$PROJECT_ROOT`:
40
+
41
+ | Detector | Type |
42
+ |----------|------|
43
+ | `pnpm-workspace.yaml` OR `lerna.json` OR `.workspaces` in `package.json` | `monorepo` |
44
+ | `next.config.*` / `vite.config.*` / `astro.config.*` / `pages/` or `app/` (Next-style) | `web-app` |
45
+ | `package.json` has `bin` + no web framework deps + has CLI deps (`commander`/`yargs`/`citty`) | `cli-tool` |
46
+ | `package.json` has `bin` + `files` + `publishConfig` (or `bin` + `main`) | `npm-package` |
47
+ | `package.json` has `main`+`types` but no `bin` | `library` |
48
+ | _else_ | `generic` |
49
+
50
+ Print detected type and **confirm via `AskUserQuestion`** before render:
51
+
52
+ > "Detected project type: **<type>**. Use template `init-templates/<type>.md`?"
53
+ > Options: Yes / Choose other / Generic fallback
54
+
55
+ ## Data collection (fill template placeholders)
56
+
57
+ | Placeholder | Source |
58
+ |-------------|--------|
59
+ | `{{PACKAGE_NAME}}` | `package.json:name` or repo dir name |
60
+ | `{{ONE_LINE_VALUE_PROP}}` | `docs/project-overview-pdr.md` first H2/H3 paragraph, or `package.json:description` |
61
+ | `{{INSTALL_COMMANDS}}` | `README.md` "Install" section block, or `package.json:scripts` |
62
+ | `{{KEY_COMMANDS_OR_USAGE}}` | `README.md` "Commands" / "Usage" block |
63
+ | `{{ASCII_DIAGRAM}}` | `docs/system-architecture.md` first `[code]` or ```` ``` ```` block; if none → generic 3-layer placeholder |
64
+ | `{{FEATURE_MATRIX_ROWS}}` | `docs/codebase-summary.md` module table rows |
65
+ | `{{PHASE_TIMELINE}}` | iterate `plans/<active>/phase-*.md`: emoji + title + 2-3 wins from "Success criteria" / completed items |
66
+ | `{{OPEN_ISSUES}}` | `gh issue list --state open --limit 5 --json title,number` + grep `TODO\|FIXME` (top 5) |
67
+ | `{{SUCCESS_CRITERIA}}` | Active phase's "Success criteria" section |
68
+ | `{{REPO_URL}}` | `git config --get remote.origin.url` → normalize |
69
+ | `{{PLAN_DIR}}` | Latest `plans/<date>-*/` by mtime |
70
+ | `{{ACTIVE_PHASE_PATH}}` | Active phase from plan.md frontmatter `status: in_progress` |
71
+
72
+ Emoji bank for phase timeline: 🏷️ rename · 🛠 fix · 🧪 test · 🔒 security · 🧰 ux · ⭐ quality · 🚀 release · 📦 package · 🪙 cost · ⏱ time · 🎬 ui · 📚 docs · 🔍 research
73
+
74
+ ## Algorithm
75
+
76
+ ```bash
77
+ source ~/.claude/skills/bx-task/lib/bx-api.sh
78
+ source ~/.claude/skills/bx-task/lib/bx-state.sh
79
+ source ~/.claude/skills/bx-task/lib/bx-tasks.sh
80
+ source ~/.claude/skills/bx-task/lib/bx-checklist.sh
81
+
82
+ # 0. Preflight
83
+ bx_preflight || exit 1
84
+ bx_state_migrate # strip deprecated webhook field if present
85
+ bx_state_init # create skeleton if missing
86
+
87
+ # 1. Prerequisite docs check (above)
88
+
89
+ # 2. Project type detection (above) → $TYPE
90
+
91
+ # 3. Read template
92
+ TEMPLATE="$(dirname "$0")/../references/init-templates/${TYPE}.md"
93
+ [[ ! -f "$TEMPLATE" ]] && { echo "ERROR: template not found: $TEMPLATE"; exit 1; }
94
+
95
+ # 4. Data collection (above) → write rendered description to /tmp/bx-desc.bb
96
+
97
+ # 5. Confirm preview (AskUserQuestion with first 30 lines of /tmp/bx-desc.bb)
98
+
99
+ # 6. Branch on --dry-run
100
+ if [[ "$DRY_RUN" == "1" ]]; then
101
+ echo "=== DRY RUN — rendered description ==="
102
+ cat /tmp/bx-desc.bb
103
+ echo "=== END (no Bitrix writes, no state changes) ==="
104
+ exit 0
105
+ fi
106
+
107
+ # 7. Duplicate check
108
+ EXISTING=$(bx_state_get "mainTaskId")
109
+ if [[ -n "$EXISTING" && "$EXISTING" != "null" ]]; then
110
+ echo "Main task already exists: #$EXISTING — skipping create"
111
+ MAIN_ID="$EXISTING"
112
+ elif [[ -n "$MAIN_TASK_ID_FLAG" ]]; then
113
+ MAIN_ID="$MAIN_TASK_ID_FLAG"
114
+ bx_state_write "mainTaskId" "$MAIN_ID"
115
+ else
116
+ FOUND=$(bx_task_find_by_title "$PACKAGE_NAME")
117
+ if [[ -n "$FOUND" ]]; then
118
+ echo "Found existing task #$FOUND with same title — reusing"
119
+ MAIN_ID="$FOUND"
120
+ bx_state_write "mainTaskId" "$MAIN_ID"
121
+ else
122
+ DESC=$(cat /tmp/bx-desc.bb)
123
+ MAIN_ID=$(bx_task_create "$PACKAGE_NAME" "$DESC")
124
+ bx_state_write "mainTaskId" "$MAIN_ID"
125
+ echo "Created main task #$MAIN_ID"
126
+ fi
127
+ fi
128
+
129
+ # 8. Update description if main existed (always refresh to latest)
130
+ bx_task_update "$MAIN_ID" "{\"DESCRIPTION\": $(jq -Rs . < /tmp/bx-desc.bb)}"
131
+
132
+ # 9. Per-phase subtasks
133
+ for PHASE_FILE in plans/*/phase-*.md; do
134
+ PHASE_TITLE=$(grep -m1 '^# Phase' "$PHASE_FILE" | sed 's/^# //')
135
+ EXISTING_SUB=$(bx_state_get "features.\"$PHASE_TITLE\".taskId")
136
+ if [[ -z "$EXISTING_SUB" || "$EXISTING_SUB" == "null" ]]; then
137
+ SUB_ID=$(bx_task_create "$PHASE_TITLE" "See $PHASE_FILE" "$MAIN_ID")
138
+ STATUS=$(grep -m1 '^status:' "$PHASE_FILE" | awk '{print $2}')
139
+ bx_state_write "features.\"$PHASE_TITLE\".taskId" "$SUB_ID"
140
+ bx_state_write "features.\"$PHASE_TITLE\".status" "$STATUS"
141
+ [[ "$STATUS" == "complete" || "$STATUS" == "done" ]] && bx_task_complete "$SUB_ID"
142
+ echo "Subtask #$SUB_ID — $PHASE_TITLE ($STATUS)"
143
+ fi
144
+ done
145
+
146
+ # 10. Initialize 3 root checklists (Done/Doing/Plan) — idempotent: skip if state.checklists populated
147
+ if [[ -z "$(bx_state_get 'checklists.done.parentId')" ]]; then
148
+ bx_checklist_init "$MAIN_ID"
149
+ fi
150
+
151
+ # 11. Populate checklist items per phase status
152
+ for PHASE_FILE in plans/*/phase-*.md; do
153
+ PHASE_TITLE=$(grep -m1 '^# Phase' "$PHASE_FILE" | sed 's/^# //')
154
+ STATUS=$(grep -m1 '^status:' "$PHASE_FILE" | awk '{print $2}')
155
+ case "$STATUS" in
156
+ complete|done) GROUP=done ;;
157
+ in_progress|active) GROUP=doing ;;
158
+ *) GROUP=plan ;;
159
+ esac
160
+ EXISTING_ITEM=$(bx_checklist_find_item "$MAIN_ID" "$GROUP" "$PHASE_TITLE")
161
+ if [[ -z "$EXISTING_ITEM" ]]; then
162
+ ITEM_ID=$(bx_checklist_add "$MAIN_ID" "$GROUP" "$PHASE_TITLE")
163
+ [[ "$GROUP" == "done" ]] && bx_checklist_toggle "$MAIN_ID" "$ITEM_ID" "Y"
164
+ fi
165
+ done
166
+
167
+ # 12. Notify
168
+ bx_task_notify "$MAIN_ID" "[B]Bootstrap complete[/B] — Type: ${TYPE}, Phases: $(ls plans/*/phase-*.md | wc -l)"
169
+ ```
170
+
171
+ ## Errors / UX
172
+
173
+ - Missing docs → exit 1 with explicit `/ck:docs init` hint (see prerequisite section)
174
+ - Template file missing → exit 1 with path
175
+ - Webhook not set → bx_preflight prints clear error
176
+ - Existing mainTaskId → reuse, refresh description only
177
+ - `--dry-run` → render + show, no side effects
178
+
179
+ ## What NOT to do
180
+
181
+ - ❌ Auto-fill missing docs with placeholder text (defeats the prerequisite check)
182
+ - ❌ Skip `AskUserQuestion` confirmation before POST (KISS — show raw BB to user)
183
+ - ❌ Hardcode webhook URL (always env var)
184
+ - ❌ Use `[LIST]` / `[TABLE]` / `[IMG]` in chat comments (crashes mobile app — but OK in DESCRIPTION)
@@ -0,0 +1,97 @@
1
+ # /bx:task feature — Create a new feature subtask + checklist item + git branch
2
+
3
+ Spawns a child task under `state.mainTaskId`, adds a checklist item under the **Doing**
4
+ group, optionally creates a git branch `feature/{taskId}-{slug}`, and records the
5
+ feature in state.
6
+
7
+ ## Args
8
+
9
+ ```
10
+ /bx:task feature <name> [description] [--no-branch] [--description-file <path>]
11
+ ```
12
+
13
+ | Flag | Purpose |
14
+ |------|---------|
15
+ | `--no-branch` | Skip `git checkout -b`. Useful for non-git projects or doc-only work. |
16
+ | `--description-file` | Read description from file (multiline BB-code). Overrides `[description]` arg. |
17
+
18
+ `<name>` is the human-readable feature name (used as task title + checklist item). Slug
19
+ for branch is `name | tr ' /' '-' | tr '[:upper:]' '[:lower:]'` (kebab-case lowercase).
20
+
21
+ ## Prerequisite
22
+
23
+ State must contain `mainTaskId` and `checklists.doing.parentId`. If missing, run
24
+ `/bx:task bootstrap` first. Skill prints clear error and exits 1.
25
+
26
+ ## Algorithm
27
+
28
+ ```bash
29
+ source ~/.claude/skills/bx-task/lib/bx-api.sh
30
+ source ~/.claude/skills/bx-task/lib/bx-state.sh
31
+ source ~/.claude/skills/bx-task/lib/bx-tasks.sh
32
+ source ~/.claude/skills/bx-task/lib/bx-checklist.sh
33
+
34
+ bx_preflight || exit 1
35
+ bx_state_migrate
36
+
37
+ MAIN_ID=$(bx_state_get "mainTaskId")
38
+ DOING_PID=$(bx_state_get "checklists.doing.parentId")
39
+
40
+ if [[ -z "$MAIN_ID" || -z "$DOING_PID" ]]; then
41
+ echo "⛔ State not bootstrapped. Run /bx:task bootstrap first." >&2
42
+ exit 1
43
+ fi
44
+
45
+ # Resolve description
46
+ if [[ -n "$DESCRIPTION_FILE" ]]; then
47
+ DESC=$(cat "$DESCRIPTION_FILE")
48
+ elif [[ -n "$DESCRIPTION_ARG" ]]; then
49
+ DESC="$DESCRIPTION_ARG"
50
+ else
51
+ DESC="Feature: $NAME"
52
+ fi
53
+
54
+ # Confirm before POST (KISS — show what gets created)
55
+ # AskUserQuestion: "Create subtask under #$MAIN_ID with title '$NAME'?"
56
+ # Options: Yes / Cancel
57
+
58
+ # 1. Create subtask
59
+ TASK_ID=$(bx_task_create "$NAME" "$DESC" "$MAIN_ID")
60
+ [[ -z "$TASK_ID" ]] && { echo "ERROR: task create failed" >&2; exit 1; }
61
+
62
+ # 2. Add checklist item under Doing
63
+ ITEM_ID=$(bx_checklist_add "$MAIN_ID" "doing" "$NAME")
64
+
65
+ # 3. Record in state
66
+ bx_state_write "features.\"$NAME\".taskId" "$TASK_ID"
67
+ bx_state_write "features.\"$NAME\".status" "active"
68
+
69
+ # 4. Git branch (unless --no-branch)
70
+ if [[ "$NO_BRANCH" != "1" ]] && command -v git >/dev/null 2>&1 && [[ -d .git || -f .git ]]; then
71
+ SLUG=$(echo "$NAME" | tr ' /' '--' | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]//g')
72
+ BRANCH="feature/${TASK_ID}-${SLUG}"
73
+ git checkout -b "$BRANCH" 2>&1 || echo "(branch exists or not in git repo)"
74
+ fi
75
+
76
+ # 5. Notify main task
77
+ bx_task_notify "$MAIN_ID" "[B]New feature:[/B] ${NAME} → subtask #${TASK_ID}"
78
+
79
+ # 6. Notify subtask (start marker)
80
+ bx_task_comment "$TASK_ID" "[B]Started[/B] — feature workflow active"
81
+
82
+ echo "✓ Created feature '$NAME' → task #$TASK_ID, branch ${BRANCH:-<skipped>}"
83
+ ```
84
+
85
+ ## Errors / UX
86
+
87
+ - Missing mainTaskId / checklists.doing → exit 1, hint to run `bootstrap`
88
+ - Webhook not set → bx_preflight handles
89
+ - Feature already exists in state → warn + ask if user wants to overwrite or reuse taskId
90
+ - Git branch create fails (e.g., branch exists) → continue, print warning (task is still created)
91
+ - Name with special chars → slug strips to `[a-z0-9-]`; empty slug after strip → error
92
+
93
+ ## What NOT to do
94
+
95
+ - ❌ Skip the `mainTaskId` precondition (creates orphan tasks)
96
+ - ❌ Auto-overwrite existing feature in state (always confirm)
97
+ - ❌ Push the new branch (user controls when to push — `git push -u origin` is manual)
@@ -0,0 +1,47 @@
1
+ [B]{{PACKAGE_NAME}}[/B] — {{ONE_LINE_VALUE_PROP}}
2
+
3
+ [B]Install[/B]
4
+ [code]
5
+ {{INSTALL_COMMANDS}}
6
+ [/code]
7
+
8
+ [B]Commands & Flags[/B]
9
+ [code]
10
+ {{KEY_COMMANDS_OR_USAGE}}
11
+ [/code]
12
+
13
+ [B]Exit Codes[/B]
14
+ [table]
15
+ [tr][th]Code[/th][th]Meaning[/th][/tr]
16
+ [tr][td]0[/td][td]Success[/td][/tr]
17
+ [tr][td]1[/td][td]Generic error / failed precondition[/td][/tr]
18
+ [tr][td]2[/td][td]Usage error (bad flag / missing arg)[/td][/tr]
19
+ {{EXTRA_EXIT_CODES}}
20
+ [/table]
21
+
22
+ [B]Architecture[/B]
23
+ [code]
24
+ {{ASCII_DIAGRAM}}
25
+ [/code]
26
+
27
+ [B]Subcommands[/B]
28
+ [table]
29
+ [tr][th]Command[/th][th]Action[/th][th]Args[/th][/tr]
30
+ {{FEATURE_MATRIX_ROWS}}
31
+ [/table]
32
+
33
+ [B]Phase Timeline[/B]
34
+ {{PHASE_TIMELINE}}
35
+
36
+ [B]Open Issues[/B]
37
+ {{OPEN_ISSUES}}
38
+
39
+ [B]Success Criteria[/B]
40
+ {{SUCCESS_CRITERIA}}
41
+
42
+ [B]Liên kết[/B]
43
+ • Repo: [URL={{REPO_URL}}]{{REPO_SHORT}}[/URL]
44
+ • Plan dir: {{PLAN_DIR}}
45
+ • Active phase: {{ACTIVE_PHASE_PATH}}
46
+ • Docs: docs/project-overview-pdr.md, docs/system-architecture.md, docs/codebase-summary.md
47
+ • README: README.md
@@ -0,0 +1,31 @@
1
+ [B]{{PACKAGE_NAME}}[/B] — {{ONE_LINE_VALUE_PROP}}
2
+
3
+ [B]Purpose[/B]
4
+ {{PURPOSE_PARAGRAPH}}
5
+
6
+ [B]Architecture[/B]
7
+ [code]
8
+ {{ASCII_DIAGRAM}}
9
+ [/code]
10
+
11
+ [B]Components[/B]
12
+ [table]
13
+ [tr][th]Module[/th][th]Role[/th][/tr]
14
+ {{FEATURE_MATRIX_ROWS}}
15
+ [/table]
16
+
17
+ [B]Phase Timeline[/B]
18
+ {{PHASE_TIMELINE}}
19
+
20
+ [B]Open Issues[/B]
21
+ {{OPEN_ISSUES}}
22
+
23
+ [B]Success Criteria[/B]
24
+ {{SUCCESS_CRITERIA}}
25
+
26
+ [B]Liên kết[/B]
27
+ • Repo: [URL={{REPO_URL}}]{{REPO_SHORT}}[/URL]
28
+ • Plan dir: {{PLAN_DIR}}
29
+ • Active phase: {{ACTIVE_PHASE_PATH}}
30
+ • Docs: docs/project-overview-pdr.md, docs/system-architecture.md, docs/codebase-summary.md
31
+ • README: README.md
@@ -0,0 +1,45 @@
1
+ [B]{{PACKAGE_NAME}}[/B] — {{ONE_LINE_VALUE_PROP}}
2
+
3
+ [B]Install[/B]
4
+ [code]
5
+ {{INSTALL_COMMANDS}}
6
+ [/code]
7
+
8
+ [B]Usage[/B]
9
+ [code]
10
+ {{KEY_COMMANDS_OR_USAGE}}
11
+ [/code]
12
+
13
+ [B]Public API[/B]
14
+ [table]
15
+ [tr][th]Export[/th][th]Type[/th][th]Description[/th][/tr]
16
+ {{FEATURE_MATRIX_ROWS}}
17
+ [/table]
18
+
19
+ [B]Architecture[/B]
20
+ [code]
21
+ {{ASCII_DIAGRAM}}
22
+ [/code]
23
+
24
+ [B]Versioning & Compat[/B]
25
+ • Semver: {{SEMVER_POLICY}}
26
+ • Node: {{NODE_VERSION}}
27
+ • Breaking changes: see CHANGELOG.md
28
+
29
+ [B]Phase Timeline[/B]
30
+ {{PHASE_TIMELINE}}
31
+
32
+ [B]Open Issues[/B]
33
+ {{OPEN_ISSUES}}
34
+
35
+ [B]Success Criteria[/B]
36
+ {{SUCCESS_CRITERIA}}
37
+
38
+ [B]Liên kết[/B]
39
+ • Repo: [URL={{REPO_URL}}]{{REPO_SHORT}}[/URL]
40
+ • npm: [URL=https://www.npmjs.com/package/{{PACKAGE_NAME}}]{{PACKAGE_NAME}}[/URL]
41
+ • Plan dir: {{PLAN_DIR}}
42
+ • Active phase: {{ACTIVE_PHASE_PATH}}
43
+ • Docs: docs/project-overview-pdr.md, docs/system-architecture.md, docs/codebase-summary.md
44
+ • README: README.md
45
+ • CHANGELOG: CHANGELOG.md
@@ -0,0 +1,38 @@
1
+ [B]{{PACKAGE_NAME}}[/B] — {{ONE_LINE_VALUE_PROP}}
2
+
3
+ [B]Setup[/B]
4
+ [code]
5
+ {{INSTALL_COMMANDS}}
6
+ [/code]
7
+
8
+ [B]Workspaces[/B]
9
+ [table]
10
+ [tr][th]Package[/th][th]Path[/th][th]Role[/th][/tr]
11
+ {{FEATURE_MATRIX_ROWS}}
12
+ [/table]
13
+
14
+ [B]Architecture[/B]
15
+ [code]
16
+ {{ASCII_DIAGRAM}}
17
+ [/code]
18
+
19
+ [B]Build Matrix[/B]
20
+ [code]
21
+ {{BUILD_COMMANDS}}
22
+ [/code]
23
+
24
+ [B]Phase Timeline[/B]
25
+ {{PHASE_TIMELINE}}
26
+
27
+ [B]Open Issues[/B]
28
+ {{OPEN_ISSUES}}
29
+
30
+ [B]Success Criteria[/B]
31
+ {{SUCCESS_CRITERIA}}
32
+
33
+ [B]Liên kết[/B]
34
+ • Repo: [URL={{REPO_URL}}]{{REPO_SHORT}}[/URL]
35
+ • Plan dir: {{PLAN_DIR}}
36
+ • Active phase: {{ACTIVE_PHASE_PATH}}
37
+ • Docs: docs/project-overview-pdr.md, docs/system-architecture.md, docs/codebase-summary.md
38
+ • README: README.md