@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,214 @@
1
+ # /bx:task time-log — Manual Time Entry
2
+
3
+ Log time manually to a Bitrix task via `task.elapseditem.add`. Use when the auto time-tracker (`bitrix-task-sync` hook) didn't catch the work — offline session, different machine, forgot to start, or filling in past sessions.
4
+
5
+ Bitrix auto-emits a system message *"<user> activated time tracking. Duration: N seconds"* on every successful add — your time entry is visible in the task chat and time tab.
6
+
7
+ ## Args
8
+
9
+ ```
10
+ /bx:task time-log <duration> [comment] [--task-id N]
11
+ ```
12
+
13
+ | Arg | Required | Notes |
14
+ |-----|----------|-------|
15
+ | `<duration>` | yes | One of 4 formats. See "Duration formats" below. |
16
+ | `[comment]` | optional | Free text. Default: `"Manual log via /bx:task time-log"`. |
17
+ | `--task-id N` | optional | Override CLAUDE.md walk-up. |
18
+
19
+ ## Duration formats
20
+
21
+ | Pattern | Example | Meaning | Seconds |
22
+ |---------|---------|---------|--------:|
23
+ | `^\d+$` | `30` | minutes | 1800 |
24
+ | `^\d+h\d+m?$` | `1h30m` / `1h30` | hours + minutes | 5400 |
25
+ | `^\d+h$` | `2h` | whole hours | 7200 |
26
+ | `^\d+\.\d+h$` | `1.5h` | decimal hours (truncated to int seconds) | 5400 |
27
+ | `^\d+:\d+(:\d+)?$` | `0:30:00` / `1:00:00` | HH:MM or HH:MM:SS | 1800 / 3600 |
28
+
29
+ Anything else → error listing all 4 formats + exit 1.
30
+
31
+ ## Algorithm (Claude follows step by step)
32
+
33
+ ```
34
+ 1. Source helpers + pre-flight:
35
+ source ~/.claude/skills/bx-task/lib/bx-resolve-task.sh
36
+ source ~/.claude/skills/bx-task/lib/bx-api.sh
37
+ bx_preflight || abort
38
+
39
+ 2. Parse args:
40
+ - extract --task-id N if present, store in EXPLICIT_TASK_ID
41
+ - first positional = duration (REQUIRED, abort if empty)
42
+ - second positional = comment (optional)
43
+
44
+ 3. Resolve TASK_ID:
45
+ TASK_ID=$(bx_resolve_task_id "$EXPLICIT_TASK_ID") || abort
46
+
47
+ 4. Parse duration:
48
+ SECONDS_VAL=$(bx_parse_duration "$duration") || abort
49
+ # bx_parse_duration body — inline in this skill:
50
+
51
+ bx_parse_duration() {
52
+ # IMPORTANT: every arithmetic expansion uses 10# prefix to force decimal
53
+ # base. Without it, leading-zero minutes ('08', '1h08m', '0:09:30') trigger
54
+ # bash octal interpretation → 'value too great for base' error.
55
+ local input="$1"
56
+ if [[ "$input" =~ ^([0-9]+)$ ]]; then
57
+ echo $(( 10#${BASH_REMATCH[1]} * 60 ))
58
+ elif [[ "$input" =~ ^([0-9]+)h([0-9]+)m?$ ]]; then
59
+ echo $(( 10#${BASH_REMATCH[1]} * 3600 + 10#${BASH_REMATCH[2]} * 60 ))
60
+ elif [[ "$input" =~ ^([0-9]+)h$ ]]; then
61
+ echo $(( 10#${BASH_REMATCH[1]} * 3600 ))
62
+ elif [[ "$input" =~ ^([0-9]+)\.([0-9]+)h$ ]]; then
63
+ awk -v h="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}" 'BEGIN{printf "%d", h*3600}'
64
+ elif [[ "$input" =~ ^([0-9]+):([0-9]+)(:([0-9]+))?$ ]]; then
65
+ local h="${BASH_REMATCH[1]}" m="${BASH_REMATCH[2]}" s="${BASH_REMATCH[4]:-0}"
66
+ echo $(( 10#$h * 3600 + 10#$m * 60 + 10#$s ))
67
+ else
68
+ echo "ERROR: invalid duration. Use: '30' (min), '1h30m', '1.5h', '0:30:00'." >&2
69
+ return 1
70
+ fi
71
+ }
72
+
73
+ 5. If SECONDS_VAL <= 0 → abort with error "duration must be > 0".
74
+
75
+ 6. COMMENT="${comment:-Manual log via /bx:task time-log}"
76
+
77
+ 7. POST:
78
+ PAYLOAD=$(jq -n \
79
+ --argjson tid "$TASK_ID" \
80
+ --argjson sec "$SECONDS_VAL" \
81
+ --arg c "$COMMENT" \
82
+ '{TASKID:$tid, ARFIELDS:{SECONDS:$sec, COMMENT_TEXT:$c}}')
83
+ RESP=$(bx_call_v1 "task.elapseditem.add" "$PAYLOAD")
84
+
85
+ 8. Extract entry id (response is `{"result": NNN}` — usually clean JSON, but
86
+ we still use grep for parity with summary.md):
87
+ ENTRY_ID=$(echo "$RESP" | grep -oE '"result":[[:space:]]*[0-9]+' | grep -oE '[0-9]+')
88
+
89
+ 9. If ENTRY_ID non-empty:
90
+ echo "⏱️ Logged $(bx_format_duration $SECONDS_VAL) to task #${TASK_ID}"
91
+ Else:
92
+ echo "❌ POST failed: $RESP" >&2
93
+ exit 1
94
+ ```
95
+
96
+ ## Output formatter
97
+
98
+ ```bash
99
+ bx_format_duration() {
100
+ local s="$1"
101
+ if (( s < 60 )); then echo "${s}s"
102
+ elif (( s < 3600 )); then echo "$((s/60))m"
103
+ else
104
+ local h=$((s/3600)) m=$(( (s%3600)/60 ))
105
+ if (( m == 0 )); then echo "${h}h"
106
+ else echo "${h}h ${m}m"
107
+ fi
108
+ fi
109
+ }
110
+ ```
111
+
112
+ Examples:
113
+ - 30 sec → `30s`
114
+ - 1800 sec → `30m`
115
+ - 3600 sec → `1h`
116
+ - 5400 sec → `1h 30m`
117
+ - 7200 sec → `2h`
118
+
119
+ ## API payload shape
120
+
121
+ ```json
122
+ {
123
+ "TASKID": 2776,
124
+ "ARFIELDS": {
125
+ "SECONDS": 1800,
126
+ "COMMENT_TEXT": "Manual log via /bx:task time-log"
127
+ }
128
+ }
129
+ ```
130
+
131
+ Endpoint: `task.elapseditem.add` on the v1 path (`/rest/<user>/<token>/`). `bx_call_v1` does NOT rewrite the URL.
132
+
133
+ Response (success):
134
+
135
+ ```json
136
+ { "result": 12345, "time": {...} }
137
+ ```
138
+
139
+ `12345` = the new elapsed-item ID.
140
+
141
+ ## Error handling
142
+
143
+ | Failure | Behaviour |
144
+ |---------|-----------|
145
+ | `bx_preflight` fails | Exit 1, print env hint |
146
+ | `bx_resolve_task_id` fails | Exit 1, print "TASK_ID not found" hint |
147
+ | Duration arg missing | Exit 1, print usage |
148
+ | Duration parse fails | Exit 1, list 4 valid formats |
149
+ | Duration ≤ 0 | Exit 1, "duration must be > 0" |
150
+ | Network/HTTP error | Print raw response to stderr, exit 1 (NO retry — fail-fast) |
151
+
152
+ ## Test matrix (parser + formatter)
153
+
154
+ | Input | Seconds | Output |
155
+ |-------|--------:|--------|
156
+ | `30` | 1800 | `⏱️ Logged 30m to task #N` |
157
+ | `1` | 60 | `⏱️ Logged 1m to task #N` |
158
+ | `1h30m` | 5400 | `⏱️ Logged 1h 30m to task #N` |
159
+ | `1h30` | 5400 | `⏱️ Logged 1h 30m to task #N` |
160
+ | `1.5h` | 5400 | `⏱️ Logged 1h 30m to task #N` |
161
+ | `2h` | 7200 | `⏱️ Logged 2h to task #N` |
162
+ | `0:30:00` | 1800 | `⏱️ Logged 30m to task #N` |
163
+ | `1:00:00` | 3600 | `⏱️ Logged 1h to task #N` |
164
+ | `0:00:30` | 30 | `⏱️ Logged 30s to task #N` |
165
+ | `08` | 480 | `⏱️ Logged 8m to task #N` (leading-zero — `10#` prefix prevents octal trap) |
166
+ | `1h08m` | 4080 | `⏱️ Logged 1h 8m to task #N` (leading-zero minutes) |
167
+ | `0:09:30` | 570 | `⏱️ Logged 9m to task #N` (leading-zero in HH:MM:SS) |
168
+ | `abc` | — | error + exit 1 |
169
+ | `30s` | — | error + exit 1 (raw seconds not supported v1) |
170
+ | `-30` | — | error + exit 1 (regex rejects sign) |
171
+
172
+ ## Examples
173
+
174
+ ### Quick log
175
+
176
+ ```
177
+ /bx:task time-log 30
178
+ ```
179
+ → Logs 1800s with default comment `"Manual log via /bx:task time-log"`.
180
+
181
+ ### With comment
182
+
183
+ ```
184
+ /bx:task time-log 1h30m "Code review for crm-deal-sync"
185
+ ```
186
+ → Logs 5400s with custom comment.
187
+
188
+ ### Decimal hours
189
+
190
+ ```
191
+ /bx:task time-log 1.5h "Architecture brainstorm"
192
+ ```
193
+ → Logs 5400s.
194
+
195
+ ### HH:MM:SS
196
+
197
+ ```
198
+ /bx:task time-log 0:30:00
199
+ ```
200
+ → Logs 1800s.
201
+
202
+ ### Override task
203
+
204
+ ```
205
+ /bx:task time-log 30 --task-id 9999
206
+ ```
207
+ → Logs 30 minutes against task 9999.
208
+
209
+ ## Notes
210
+
211
+ - Bitrix auto-emits *"<user> activated time tracking. Duration: N seconds"* on success — that's why even short entries surface in the task chat.
212
+ - Time entries are removable via `task.elapseditem.delete {TASKID, ITEMID}` if you need to undo. Skill does NOT auto-delete (audit value).
213
+ - Decimal hour math truncates to int seconds via `awk` — `1.5h = 5400s exact`, `1.7h ≈ 6120s` (not `6120.0`).
214
+ - If the task has time-tracking disabled, the API returns an error — surface it via the fail-fast path.
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "bx-task",
3
+ "displayName": "Bitrix Task Skill (manual)",
4
+ "version": "1.1.0",
5
+ "target": "global",
6
+ "description": "Claude Code skill for Bitrix24 task management",
7
+ "requires": {
8
+ "mcp": ["bitrix-synity-mcp"]
9
+ }
10
+ }
@@ -0,0 +1,117 @@
1
+ import { homedir } from 'node:os';
2
+ import { resolve, join, dirname } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { mkdir, copyFile, readdir, stat, rm, access } from 'node:fs/promises';
5
+ import { assertNotSymlink, assertContainedIn } from '../../lib/fs-safety.js';
6
+
7
+ export interface InstallOpts {
8
+ projectRoot: string;
9
+ onConflict?: 'overwrite' | 'skip';
10
+ force?: boolean;
11
+ /** @internal Override destination base for testing only — bypasses path-containment guard. Not wired to any CLI flag. */
12
+ _destOverride?: string;
13
+ }
14
+
15
+ export interface InstallResult {
16
+ installedFiles: string[];
17
+ skippedFiles: string[];
18
+ warnings: string[];
19
+ }
20
+
21
+ // Resolve assets directory. Code is inlined into dist/cli.js (__dirname = dist/) AND
22
+ // compiled to dist/features/bx-task/install.js (__dirname = dist/features/bx-task/).
23
+ // assets/ lives in src/features/bx-task/assets/ (included in package.json "files").
24
+ async function getAssetsDir(): Promise<string> {
25
+ const here = dirname(fileURLToPath(import.meta.url));
26
+ const candidates = [
27
+ // dist/ context (inlined in cli.js): ../src/features/bx-task/assets
28
+ resolve(here, '../src/features/bx-task/assets'),
29
+ // dist/features/bx-task/ context: ../../../src/features/bx-task/assets
30
+ resolve(here, '../../../src/features/bx-task/assets'),
31
+ // src/features/bx-task/ (dev)
32
+ resolve(here, 'assets'),
33
+ ];
34
+ for (const c of candidates) {
35
+ try {
36
+ await access(c);
37
+ return c;
38
+ } catch { /* continue */ }
39
+ }
40
+ return candidates[0]!;
41
+ }
42
+
43
+ function getSkillBase(): string {
44
+ return resolve(homedir(), '.claude', 'skills');
45
+ }
46
+
47
+ export async function install(opts: InstallOpts): Promise<InstallResult> {
48
+ const skillBase = getSkillBase();
49
+ const dest = opts._destOverride ?? resolve(skillBase, 'bx-task');
50
+ // Path traversal guard: dest must be absolute and not escape via ../
51
+ const resolvedDest = resolve(dest);
52
+ if (!opts._destOverride && !resolvedDest.startsWith(skillBase)) {
53
+ throw new Error(`Path traversal detected: ${resolvedDest}`);
54
+ }
55
+
56
+ const result: InstallResult = {
57
+ installedFiles: [],
58
+ skippedFiles: [],
59
+ warnings: [],
60
+ };
61
+
62
+ await installDir(await getAssetsDir(), resolvedDest, result, opts);
63
+
64
+ result.warnings.push(
65
+ '[bx-task] Requires MCP server "bitrix-synity-mcp" — verify it is configured in Claude Code settings.',
66
+ );
67
+
68
+ return result;
69
+ }
70
+
71
+ async function installDir(
72
+ srcDir: string,
73
+ destDir: string,
74
+ result: InstallResult,
75
+ opts: InstallOpts,
76
+ ): Promise<void> {
77
+ await mkdir(destDir, { recursive: true });
78
+ const entries = await readdir(srcDir, { withFileTypes: true });
79
+
80
+ for (const entry of entries) {
81
+ const srcPath = join(srcDir, entry.name);
82
+ const destPath = join(destDir, entry.name);
83
+
84
+ if (entry.isDirectory()) {
85
+ await installDir(srcPath, destPath, result, opts);
86
+ continue;
87
+ }
88
+
89
+ await assertNotSymlink(destPath);
90
+ const exists = await fileExists(destPath);
91
+ if (exists && !opts.force && opts.onConflict === 'skip') {
92
+ result.skippedFiles.push(destPath);
93
+ continue;
94
+ }
95
+
96
+ await copyFile(srcPath, destPath);
97
+ result.installedFiles.push(destPath);
98
+ }
99
+ }
100
+
101
+ async function fileExists(filePath: string): Promise<boolean> {
102
+ try {
103
+ await stat(filePath);
104
+ return true;
105
+ } catch {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ export async function uninstall(opts?: { _destOverride?: string }): Promise<void> {
111
+ const dest = resolve(opts?._destOverride ?? resolve(homedir(), '.claude', 'skills', 'bx-task'));
112
+ // Guard only applies to production paths; _destOverride is an internal test escape hatch.
113
+ if (!opts?._destOverride) {
114
+ assertContainedIn(dest, getSkillBase(), dest);
115
+ }
116
+ await rm(dest, { recursive: true, force: true });
117
+ }
@@ -0,0 +1,318 @@
1
+ # Bitrix Task — Agent Reference
2
+
3
+ > Reference cho agent khi query task hoặc lập plan structure trong Bitrix24.
4
+ > Setup hooks: [bitrix-task-sync.md](bitrix-task-sync.md)
5
+ > Quy trình bắt buộc: [.claude/rules/00-bitrix-task-sync.md](../.claude/rules/00-bitrix-task-sync.md)
6
+
7
+ > **⚠️ Hard Rule #13:** Mọi query field/structure portal PHẢI qua `bitrix-synity-mcp` (KHÔNG curl/fetch trong agent runtime). Code derived từ MCP cần annotation: `// MCP-DERIVED: <source> @ <ISO-timestamp>`.
8
+
9
+ Các tra cứu trong file này dành cho agent runtime (đọc/query qua `bitrix-synity-mcp`, sync checklist qua `bitrix-lib.sh`). Không dùng cho dev setup — nội dung setup ở file `bitrix-task-sync.md`.
10
+
11
+ ## 1. Bitrix Object Hierarchy
12
+
13
+ ```
14
+ Portal (tamgiac.bitrix24.com)
15
+ └── Project (groupId) ← "App" trong ngữ cảnh Synity
16
+ ├── Epic (epicId) ← nhóm tính năng (Scrum only)
17
+ │ ├── Task (id)
18
+ │ │ ├── Subtask (parentId → Task.id)
19
+ │ │ └── Checklist items (checklist[])
20
+ │ └── Task ...
21
+ └── Task (không thuộc Epic)
22
+ ```
23
+
24
+ ### Workgroup = "App" trong Synity
25
+
26
+ Mỗi Bitrix24 app của Synity tương ứng với một **Project** trên portal:
27
+
28
+ | Bitrix Field | Giá trị ví dụ | Ý nghĩa |
29
+ |-------------|--------------|---------|
30
+ | `groupId` | `12` | ID Project chứa task này |
31
+
32
+ Agent đọc `groupId` → biết task thuộc app nào → không nhầm task giữa các dự án.
33
+
34
+ ### Mapping: App → Workgroup
35
+
36
+ Agent xác định Project tương ứng với app theo 1 trong 2 flow:
37
+
38
+ #### Greenfield — app mới, chưa có Project trên Bitrix
39
+
40
+ ```
41
+ 1. Agent hỏi user: "App này đã có Project trên portal chưa?"
42
+ 2. User: chưa
43
+ 3. Agent hỏi: "Tên Project là gì?" (gợi ý theo tên app, vd: "SynFinance")
44
+ 4. Agent → bitrix-synity-mcp: socintranet.workgroup.add
45
+ → input: NAME, DESCRIPTION (optional), VISIBLE=Y
46
+ → output: workgroup.id
47
+ 5. (Optional) Tạo Epic per nhóm tính năng — xem [Section 2](#2-epic--nhóm-tính-năng)
48
+ 6. Agent ghi vào CLAUDE.md root:
49
+ ## Bitrix Project
50
+ GROUP_ID: <workgroup.id>
51
+ GROUP_NAME: <name> # tham chiếu nhanh, optional
52
+ 7. Cho mỗi feature task tiếp theo → tạo task với groupId = GROUP_ID, lưu TASK_ID
53
+ ```
54
+
55
+ #### Brownfield — app đã có code + Project sẵn trên Bitrix
56
+
57
+ ```
58
+ 1. Agent đọc CLAUDE.md root → check GROUP_ID có chưa
59
+ 2. Nếu CHƯA có:
60
+ a. Agent hỏi: "Project ID trên Bitrix là gì?" hoặc
61
+ b. Agent → bitrix-synity-mcp: socintranet.workgroup.list (filter NAME)
62
+ → user xác nhận đúng project
63
+ c. Ghi GROUP_ID vào CLAUDE.md root
64
+ 3. Nếu ĐÃ có → dùng luôn, không hỏi
65
+ 4. Agent → bitrix-synity-mcp: tasks.api.scrum.epic.list (groupId)
66
+ → list Epic hiện có → user chọn hoặc tạo mới
67
+ 5. TASK_ID per feature đặt vào CLAUDE.md feature folder (override root)
68
+ ```
69
+
70
+ #### CLAUDE.md keys reference
71
+
72
+ | Key | Required | Owner | Mục đích |
73
+ |-----|:--------:|-------|---------|
74
+ | `TASK_ID` | ✅ | feature folder (preferred) hoặc root | Bắt buộc — hooks parse |
75
+ | `PORTAL` | optional | root | Build task link trong session summary |
76
+ | `GROUP_ID` | optional | root | Project ID — agent lookup epic/task list |
77
+ | `GROUP_NAME` | optional | root | Tham chiếu nhanh, không dùng cho API call |
78
+ | `EPIC_ID` | optional | feature folder | Pre-set epic cho feature task mới |
79
+ | `CREATOR_ID` | optional | root | Default user khi agent tạo task mới |
80
+ | `RESPONSIBLE_ID` | optional | feature folder | Assign default — agent fallback nếu user không chỉ định |
81
+
82
+ ## 2. Epic — Nhóm Tính Năng
83
+
84
+ Epic là **nhóm tính năng** trong một Workgroup Scrum. Mỗi task có thể thuộc một Epic (`epicId`).
85
+
86
+ ### Khi nào dùng Epic
87
+
88
+ - App có nhiều tính năng độc lập → tạo Epic per tính năng
89
+ - Giúp filter task theo nhóm chức năng trên Bitrix board
90
+ - `epicId = null` → task chưa được gắn vào Epic nào
91
+
92
+ ### Gợi ý đặt tên Epic theo app
93
+
94
+ Agent gợi ý user đặt Epic dựa trên các tính năng của app:
95
+
96
+ | App | Epic gợi ý |
97
+ |-----|-----------|
98
+ | SynFinance | Payment Integration / Expense Bot / Admin Dashboard |
99
+ | SynCRM | Deal Pipeline / Contact Sync / Activity Log |
100
+ | SynProject | Task Automation / Report Generator / Notification Engine |
101
+
102
+ ### Agent Flow với Epic
103
+
104
+ ```
105
+ Đọc task → epicId = 42
106
+ → bitrix-synity-mcp: tasks.api.scrum.epic.get (id: 42)
107
+ → đọc epic.name → bổ sung context: "Feature group: Payment Integration"
108
+ → nếu user tạo task mới trong cùng nhóm → set epicId = 42
109
+ ```
110
+
111
+ Nếu `epicId = null` và task có scope lớn → hỏi user: "Task này thuộc nhóm tính năng nào? Tôi sẽ tạo hoặc gắn Epic."
112
+
113
+ ## 3. Subtask vs Checklist — Decision Guide
114
+
115
+ Cả hai đều dùng để chia nhỏ công việc, nhưng phục vụ mục đích khác nhau.
116
+
117
+ ### So sánh
118
+
119
+ | Tiêu chí | Subtask | Checklist item |
120
+ |---------|---------|---------------|
121
+ | Có assignee riêng | ✅ | ❌ |
122
+ | Có deadline riêng | ✅ | ❌ |
123
+ | Có status riêng (pending/done) | ✅ | ✅ (checked/unchecked) |
124
+ | Tạo git branch riêng | ✅ | ❌ |
125
+ | Track trong Bitrix board | ✅ | ❌ |
126
+ | Phù hợp cho | Work độc lập, dev khác nhau | Todo list trong 1 task |
127
+
128
+ ### Convention cho AI coding workflow
129
+
130
+ **Dùng Subtask khi:**
131
+ - Phase cần branch + PR riêng (ví dụ: backend vs frontend)
132
+ - Developer khác nhau phụ trách
133
+ - Có deadline hoặc estimate riêng
134
+ - Cần track status độc lập trên Bitrix board
135
+
136
+ **Dùng Checklist khi:**
137
+ - Đây là plan steps của AI session (phases do AI thực hiện trong 1 branch)
138
+ - Không cần assignee/deadline riêng
139
+ - Muốn tick từng bước trong khi làm việc
140
+ - Ví dụ: `/ck:plan` output → sync thành checklists qua `b24_sync_checklist()` (Section 5)
141
+
142
+ ### Quy tắc thực tế
143
+
144
+ ```
145
+ 1 feature task
146
+ ├── 📋 Checklist: Phase 1 Backend ← AI làm trong 1 session
147
+ │ ├── [x] DB schema migration
148
+ │ └── [ ] Hono route handler
149
+ ├── 📋 Checklist: Phase 2 Frontend ← AI làm trong session tiếp
150
+ │ └── [ ] ListShell component
151
+ └── Subtask: "QA Testing" ← Tester khác review/test
152
+ ```
153
+
154
+ ## 4. Task Fields Reference
155
+
156
+ Các trường quan trọng agent cần biết khi query task qua `bitrix-synity-mcp`.
157
+
158
+ ### Định danh & Navigation
159
+
160
+ | Field | Type | Dùng khi |
161
+ |-------|------|----------|
162
+ | `id` | integer | Mọi API call |
163
+ | `title` | string | Hiểu context task đang làm |
164
+ | `description` | string | Đọc scope, yêu cầu đầy đủ |
165
+ | `parentId` | integer | Xác nhận đây là subtask của task nào |
166
+ | `link` | string | Mention trong commit/PR message |
167
+ | `groupId` | integer | Biết task thuộc app/workgroup nào — xem [Section 1](#1-bitrix-object-hierarchy) |
168
+ | `epicId` | integer | Thuộc nhóm tính năng nào (Scrum only) — xem [Section 2](#2-epic--nhóm-tính-năng) |
169
+
170
+ ### Trạng thái & Ưu tiên
171
+
172
+ | Field | Giá trị |
173
+ |-------|---------|
174
+ | `status` | `pending` / `in_progress` / `supposedly_completed` / `completed` / `deferred` / `declined` |
175
+ | `priority` | `high` / `average` / `low` |
176
+ | `deadline` | ISO-8601 — agent cảnh báo nếu sắp hết |
177
+
178
+ ### Con người
179
+
180
+ | Field | Dùng khi |
181
+ |-------|----------|
182
+ | `responsibleId` | Developer phụ trách — lưu trong CLAUDE.md `RESPONSIBLE_ID` |
183
+ | `creatorId` | Phải = Claude user ID (`CREATOR_ID`) để được phép ghi task |
184
+ | `accomplices` | Những người liên quan — agent thêm vào context |
185
+
186
+ ### Cấu trúc & Hierarchy
187
+
188
+ | Field | Dùng khi |
189
+ |-------|----------|
190
+ | `parentId` | Subtask của task nào — null nếu là task gốc |
191
+ | `containsSubTasks` | Check trước khi tạo subtask — tránh duplicate |
192
+ | `containsRelatedTasks` | Xem flow bên dưới |
193
+ | `checklist` | IDs checklist items — dùng để check/tick trạng thái |
194
+ | `chatId` | Chat của task — `tasks.task.chat.message.send` dùng field này nội bộ |
195
+ | `xmlId` | Agent ghi git branch name khi bắt đầu session → trace task↔branch |
196
+
197
+ #### containsRelatedTasks — Agent Flow
198
+
199
+ ```
200
+ Đọc task → containsRelatedTasks = true
201
+ → tasks.task.relation.get (taskId) → list [{taskId, relatedTaskId, relationType}]
202
+ → đọc title/status related tasks
203
+ → bổ sung vào context: "Task liên quan #456 (blocks) và #789 (related)"
204
+
205
+ User yêu cầu "thêm related task #999":
206
+ → tasks.task.relation.add (taskId, relatedTaskId, relationType)
207
+ ```
208
+
209
+ `relationType`: `"blocked_by"` / `"blocking"` / `"related"`
210
+
211
+ ### Time tracking
212
+
213
+ | Field | Dùng khi |
214
+ |-------|----------|
215
+ | `elapsedTime` | Tổng thời gian đã log — agent tránh log trùng |
216
+ | `actualDuration` | Thời gian thực tế tính toán bởi Bitrix |
217
+ | `UF_AI_TOKENS_TOTAL` | Custom field (v0.2+) — cumulative AI token usage (input + output). Auto-created on first session. Read to check token budget. |
218
+
219
+ ### CRM links
220
+
221
+ | Field | Dùng khi |
222
+ |-------|----------|
223
+ | `crmItemIds` | Task liên kết CRM object nào (`D_XX` deal, `C_XX` contact...) |
224
+
225
+ ## 5. Checklist Sync API — Plan Phases → Bitrix
226
+
227
+ Khi agent lập plan, có thể sync từng phase thành một **named checklist** trong Bitrix task qua batch API.
228
+
229
+ ### Cơ chế batch API
230
+
231
+ Batch API cho phép gọi tối đa 50 cmds trong 1 request và chain kết quả qua `$result[cmd_name]`:
232
+
233
+ ```bash
234
+ # Tạo 2 named checklists + items trong 1 batch call
235
+ POST /batch
236
+ {
237
+ "halt": 1,
238
+ "cmd": {
239
+ "cl_0": "task.checklistitem.add?TASKID=123&FIELDS[TITLE]=Phase+1+Backend&FIELDS[PARENT_ID]=0",
240
+ "item_0_0": "task.checklistitem.add?TASKID=123&FIELDS[TITLE]=DB+schema&FIELDS[PARENT_ID]=$result[cl_0]",
241
+ "item_0_1": "task.checklistitem.add?TASKID=123&FIELDS[TITLE]=Route+handler&FIELDS[PARENT_ID]=$result[cl_0]",
242
+ "cl_1": "task.checklistitem.add?TASKID=123&FIELDS[TITLE]=Phase+2+Frontend&FIELDS[PARENT_ID]=0",
243
+ "item_1_0": "task.checklistitem.add?TASKID=123&FIELDS[TITLE]=ListShell+component&FIELDS[PARENT_ID]=$result[cl_1]"
244
+ }
245
+ }
246
+ ```
247
+
248
+ - `PARENT_ID=0` → tạo named checklist mới (tên = `TITLE`)
249
+ - `PARENT_ID=$result[cl_0]` → thêm item vào checklist vừa tạo
250
+ - `halt=1` → dừng nếu 1 cmd lỗi
251
+
252
+ ### Kết quả trong Bitrix UI
253
+
254
+ ```
255
+ Task: "Implement CRM Sync"
256
+ ├── 📋 Phase 1 Backend
257
+ │ ├── [ ] DB schema
258
+ │ └── [ ] Route handler
259
+ └── 📋 Phase 2 Frontend
260
+ └── [ ] ListShell component
261
+ ```
262
+
263
+ ### Plan output format — `bitrix-checklist` fenced block
264
+
265
+ Planner agent emit JSON trong fenced code block với info-string `bitrix-checklist` để hook PostToolUse parse và sync tự động:
266
+
267
+ ````markdown
268
+ ```bitrix-checklist
269
+ [
270
+ {"name":"Phase 1: Backend","items":["DB schema","Hono route","Tests"]},
271
+ {"name":"Phase 2: Frontend","items":["ListShell","Composable"]}
272
+ ]
273
+ ```
274
+ ````
275
+
276
+ **Spec contract:**
277
+ - Info-string PHẢI = `bitrix-checklist` (không phải `json` hoặc khác)
278
+ - Top-level: array của objects, mỗi object có `name` (string) + `items` (array of string)
279
+ - Tổng `phase_count + sum(items.length)` ≤ 50 (batch API limit)
280
+ - Chỉ 1 block per plan output — block thứ 2 trong cùng output sẽ bị bỏ qua
281
+ - Không emit block → no-op (planner muốn skip sync)
282
+
283
+ **Hook behavior (`PostToolUse` cho skill `plan` / `planner`):**
284
+ 1. Regex extract block từ `tool_response`
285
+ 2. `jq` validate schema → silent skip nếu invalid (không tạo checklist rác)
286
+ 3. Đọc `TASK_ID` từ state file → call `b24_sync_checklist`
287
+ 4. Errors → stderr only, exit 0
288
+
289
+ ### Trigger qua shell script (manual)
290
+
291
+ ```bash
292
+ # Agent gọi khi plan đã được approve
293
+ PHASES='[
294
+ {"name":"Phase 1 Backend","items":["DB schema migration","Hono route handler","Unit tests"]},
295
+ {"name":"Phase 2 Frontend","items":["ListShell component","useAsyncData composable"]}
296
+ ]'
297
+ source .claude/scripts/bitrix-lib.sh
298
+ _b24_preflight || exit 0
299
+ TASK_ID=$(find_task_id)
300
+ b24_sync_checklist "$TASK_ID" "$PHASES"
301
+ ```
302
+
303
+ **Limit:** Tối đa 50 cmds/batch. Script tự warn nếu vượt (phases × items + phase count > 50).
304
+
305
+ ### `bitrix-lib.sh` exports
306
+
307
+ | Function | Mục đích |
308
+ |----------|---------|
309
+ | `_b24_preflight` | Check awk/jq/curl present; warn stderr nếu thiếu, return 1 (hooks dùng `\|\| exit 0`) |
310
+ | `_b24_md_field <file> <key>` | POSIX awk parse `KEY: value` từ markdown — works trên macOS BSD / Linux / Windows Git Bash |
311
+ | `find_task_id [dir]` | Walk up từ `dir` (default `pwd`), trả về `TASK_ID` đầu tiên trong CLAUDE.md |
312
+ | `find_task_meta [dir]` | Walk up, parse 4 field (`task_id`, `creator_id`, `responsible_id`, `portal`) → JSON |
313
+ | `b24_call <method> [body]` | POST JSON tới webhook (silent on error) |
314
+ | `b24_batch <cmd_obj> [halt=1]` | Gọi `batch` với JSON cmd object, chain `$result[]` |
315
+ | `b24_comment <task_id> <text>` | Post message vào task chat (`tasks.task.chat.message.send`) |
316
+ | `b24_log_time <task_id> <minutes> [comment]` | Ghi elapsed time (`task.elapseditem.add`, params `{TASKID, ARFIELDS:{SECONDS, COMMENT_TEXT}}`) |
317
+ | `b24_sync_checklist <task_id> <phases_json>` | Batch tạo named checklists + items |
318
+ | `skill_emoji <skill_name>` | Map skill → emoji cho comment prefix |