@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,51 @@
1
+ #!/bin/bash
2
+ # SessionStart hook — warns if TASK_ID missing (Hard Rule #17); probes task status;
3
+ # auto-enables time tracking; writes session start ISO for digest.
4
+ # Non-blocking: always exit 0 so Claude session continues.
5
+
6
+ source "$(dirname "$0")/bitrix-lib.sh"
7
+
8
+ _b24_preflight || exit 0
9
+
10
+ PAYLOAD=$(cat)
11
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // "unknown"')
12
+
13
+ # Write session start timestamp for Stop hook digest (Phase 4)
14
+ SESSION_ENV_FILE="$SESSION_DIR/session-env-${SESSION_ID}"
15
+ BTS_SESSION_START_ISO=$(date -u +%Y-%m-%dT%H:%M:%SZ)
16
+ echo "BTS_SESSION_START_ISO=${BTS_SESSION_START_ISO}" > "$SESSION_ENV_FILE"
17
+
18
+ TASK_ID=$(find_task_id) || {
19
+ cat >&2 <<'EOF'
20
+ ⚠️ Hard Rule #17 violation: TASK_ID chưa set trong CLAUDE.md
21
+
22
+ Bitrix Task Sync sẽ KHÔNG hoạt động trong session này.
23
+ Hooks (PreToolUse/PostToolUse/Stop) sẽ silent skip.
24
+
25
+ Cách fix:
26
+ 1. Mở CLAUDE.md root hoặc CLAUDE.md của feature đang làm
27
+ 2. Thêm block:
28
+ ## Bitrix Task
29
+ TASK_ID: <id-từ-Bitrix>
30
+ 3. Reload session
31
+
32
+ Setup chi tiết: docs/bitrix-task-sync.md
33
+ EOF
34
+ exit 0
35
+ }
36
+
37
+ # Wipe stale skill counter from previous session
38
+ rm -f "$SESSION_DIR/skill-counter-${SESSION_ID}" \
39
+ "$SESSION_DIR/skill-counter-${SESSION_ID}.lock" 2>/dev/null || true
40
+
41
+ # Probe task status — skip lifecycle if task is closed (STATUS=5)
42
+ TASK_STATUS=$(b24_task_status "$TASK_ID") || true
43
+ if [[ "$TASK_STATUS" == "5" ]]; then
44
+ echo "BTS_TASK_CLOSED=1" >> "$SESSION_ENV_FILE"
45
+ exit 0
46
+ fi
47
+
48
+ # Auto-enable time tracking if not already enabled (Phase 2)
49
+ b24_ensure_timetracking "$TASK_ID" || true
50
+
51
+ exit 0
@@ -0,0 +1,89 @@
1
+ #!/bin/bash
2
+ # Stop hook — fires when Claude finishes a turn.
3
+ # Posts full session digest: skills + time + tokens + files + commits + plan progress.
4
+
5
+ source "$(dirname "$0")/bitrix-lib.sh"
6
+ _b24_preflight || exit 0
7
+
8
+ PAYLOAD=$(cat)
9
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // "unknown"')
10
+ TRANSCRIPT_PATH=$(echo "$PAYLOAD" | jq -r '.transcript_path // ""')
11
+
12
+ STATE_FILE="$SESSION_DIR/${SESSION_ID}.json"
13
+ [[ ! -f "$STATE_FILE" ]] && exit 0
14
+
15
+ STATE=$(cat "$STATE_FILE")
16
+ TASK_ID=$(echo "$STATE" | jq -r '.task_meta.task_id // ""')
17
+ [[ -z "$TASK_ID" ]] && exit 0
18
+
19
+ SKILL_COUNT=$(echo "$STATE" | jq '.skills_run | length')
20
+ [[ "$SKILL_COUNT" -eq 0 ]] && exit 0
21
+
22
+ SESSION_ENV_FILE="$SESSION_DIR/session-env-${SESSION_ID}"
23
+ SESSION_START=""
24
+ if [[ -f "$SESSION_ENV_FILE" ]]; then
25
+ source "$SESSION_ENV_FILE" 2>/dev/null || true
26
+ SESSION_START="${BTS_SESSION_START_ISO:-}"
27
+ fi
28
+ [[ -z "$SESSION_START" ]] && SESSION_START=$(echo "$STATE" | jq -r '.session_start')
29
+
30
+ # Stop timer crash-safety: if skill crashed mid-run, timer may still be running
31
+ if [[ "${BTS_TASK_CLOSED:-}" != "1" ]]; then
32
+ b24_timer_stop "$TASK_ID" || true
33
+ fi
34
+ # Wipe counter
35
+ rm -f "$SESSION_DIR/skill-counter-${SESSION_ID}" \
36
+ "$SESSION_DIR/skill-counter-${SESSION_ID}.lock" 2>/dev/null || true
37
+
38
+ # Write skills log to tmp file for renderer
39
+ SKILLS_LOG_FILE="$SESSION_DIR/skills-log-${SESSION_ID}.json"
40
+ echo "$STATE" | jq '.skills_run' > "$SKILLS_LOG_FILE"
41
+
42
+ # Phase 1: accumulate session tokens
43
+ TOKEN_LINE=""
44
+ if [[ -n "$TRANSCRIPT_PATH" && -f "$TRANSCRIPT_PATH" ]]; then
45
+ TOKEN_LINE=$(b24_finalize_session_tokens "$TASK_ID" "$TRANSCRIPT_PATH") || true
46
+ fi
47
+ # Fallback: probe recent JSONL if transcript_path not in payload.
48
+ # Exclude subagents/ dirs; sort newest-first to pick the right session.
49
+ if [[ -z "$TOKEN_LINE" ]]; then
50
+ CANDIDATE=$(find "${HOME}/.claude/projects" -maxdepth 2 -name "*.jsonl" \
51
+ -not -path "*/subagents/*" -newer "$STATE_FILE" 2>/dev/null \
52
+ | xargs ls -t 2>/dev/null | head -1 || true)
53
+ if [[ -n "$CANDIDATE" ]]; then
54
+ TOKEN_LINE=$(b24_finalize_session_tokens "$TASK_ID" "$CANDIDATE") || true
55
+ fi
56
+ fi
57
+
58
+ # Resolve active plan dir from state or .claude/active-plan.txt
59
+ PLAN_DIR=""
60
+ ACTIVE_PLAN_FILE="${HOME}/.claude/active-plan.txt"
61
+ if [[ -f "$ACTIVE_PLAN_FILE" ]]; then
62
+ PLAN_DIR=$(cat "$ACTIVE_PLAN_FILE" | tr -d '[:space:]')
63
+ fi
64
+ [[ -n "$PLAN_DIR" && ! -d "$PLAN_DIR" ]] && PLAN_DIR=""
65
+
66
+ # Resolve repo root for commit log
67
+ REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
68
+
69
+ # Attached files registry (from Phase 3 hook)
70
+ ATTACHED_LOG="$SESSION_DIR/attached-${SESSION_ID}.json"
71
+
72
+ # Render digest via pure renderer (use bash explicitly — script may not have +x bit)
73
+ SCRIPT_DIR="$(dirname "$0")"
74
+ MSG=$(bash "${SCRIPT_DIR}/bitrix-render-digest.sh" \
75
+ --session-start "$SESSION_START" \
76
+ --skills-log "$SKILLS_LOG_FILE" \
77
+ ${PLAN_DIR:+--plan-dir "$PLAN_DIR"} \
78
+ ${TOKEN_LINE:+--token-line "$TOKEN_LINE"} \
79
+ ${ATTACHED_LOG:+--attached-log "$ATTACHED_LOG"} \
80
+ ${REPO_ROOT:+--repo-root "$REPO_ROOT"})
81
+
82
+ [[ -z "$MSG" ]] && { rm -f "$STATE_FILE" "$SKILLS_LOG_FILE" "$SESSION_ENV_FILE" "$ATTACHED_LOG" 2>/dev/null; exit 0; }
83
+ b24_comment "$TASK_ID" "$MSG" || true
84
+
85
+ # Clean up session tmp files
86
+ rm -f "$STATE_FILE" "$SKILLS_LOG_FILE" "$SESSION_ENV_FILE" \
87
+ "$ATTACHED_LOG" "$SESSION_DIR/disk-root-${SESSION_ID}" 2>/dev/null || true
88
+
89
+ exit 0
@@ -0,0 +1,165 @@
1
+ #!/bin/bash
2
+ # PostToolUse hook — fires after Skill tool returns result
3
+ # Posts result preview + logs elapsed time; decrements skill counter → pauses
4
+ # task timer on last skill (Phase 2); attaches plan/brainstorm artifacts (Phase 3).
5
+
6
+ source "$(dirname "$0")/bitrix-lib.sh"
7
+ _b24_preflight || exit 0
8
+
9
+ PAYLOAD=$(cat)
10
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // "unknown"')
11
+ SKILL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_input.skill // "unknown"')
12
+
13
+ STATE_FILE="$SESSION_DIR/${SESSION_ID}.json"
14
+ [[ ! -f "$STATE_FILE" ]] && exit 0
15
+
16
+ STATE=$(cat "$STATE_FILE")
17
+ TASK_ID=$(echo "$STATE" | jq -r '.task_meta.task_id // ""')
18
+ [[ -z "$TASK_ID" ]] && exit 0
19
+
20
+ START_TIME=$(echo "$STATE" | jq -r '.current_skill.start // 0')
21
+ END_TIME=$(date -u +%s)
22
+ ELAPSED_SECONDS=$((END_TIME - START_TIME))
23
+ ELAPSED_MINUTES=$(( (ELAPSED_SECONDS + 59) / 60 ))
24
+
25
+ # Take first 1500 chars of result as preview
26
+ RESULT=$(echo "$PAYLOAD" | jq -r '.tool_response // ""' | head -c 1500)
27
+ [[ ${#RESULT} -ge 1500 ]] && RESULT="${RESULT}
28
+ ..."
29
+
30
+ # Update session state
31
+ STATE=$(echo "$STATE" | jq \
32
+ --arg skill "$SKILL_NAME" \
33
+ --argjson elapsed "$ELAPSED_SECONDS" \
34
+ --arg result "$RESULT" \
35
+ '.skills_run += [{skill: $skill, elapsed_seconds: $elapsed, result_preview: $result}]
36
+ | del(.current_skill)')
37
+ echo "$STATE" > "$STATE_FILE"
38
+
39
+ # Decrement skill counter; stop native timer on every skill end (records duration automatically)
40
+ _skill_counter_decr "$SESSION_ID" >/dev/null
41
+ SESSION_ENV_FILE="$SESSION_DIR/session-env-${SESSION_ID}"
42
+ if [[ -f "$SESSION_ENV_FILE" ]]; then
43
+ source "$SESSION_ENV_FILE" 2>/dev/null || true
44
+ fi
45
+ if [[ "${BTS_TASK_CLOSED:-}" != "1" ]]; then
46
+ b24_timer_stop "$TASK_ID"
47
+ fi
48
+
49
+ # Phase 3a: sync bitrix-checklist fenced block → Bitrix task checklist (plan/planner only)
50
+ case "$SKILL_NAME" in
51
+ plan|planner)
52
+ TOOL_RESPONSE_FULL=$(echo "$PAYLOAD" | jq -r '.tool_response // ""')
53
+ CHECKLIST_JSON=$(printf '%s' "$TOOL_RESPONSE_FULL" | \
54
+ awk '/```bitrix-checklist/{flag=1;next}/```/{flag=0}flag' | \
55
+ jq -e '.' 2>/dev/null || true)
56
+ if [[ -n "$CHECKLIST_JSON" ]]; then
57
+ b24_sync_checklist "$TASK_ID" "$CHECKLIST_JSON" >/dev/null || true
58
+ fi
59
+ ;;
60
+ esac
61
+
62
+ # Phase 3b: attach artifact files first so disk links can be included in comment
63
+ DISK_IDS=""
64
+ case "$SKILL_NAME" in
65
+ plan|planner|brainstorm|cook|research|journal)
66
+ DISK_IDS=$(_attach_skill_artifacts "$TASK_ID" "$SESSION_ID" "$PAYLOAD" "$SKILL_NAME")
67
+ ;;
68
+ esac
69
+
70
+ # Only post "xong" when tool_response has human-readable content (not empty/pure JSON)
71
+ if _has_useful_content "$RESULT"; then
72
+ EMOJI=$(skill_emoji "$SKILL_NAME")
73
+ MSG="${EMOJI} [Claude] ${SKILL_NAME} xong (${ELAPSED_MINUTES} phút)
74
+
75
+ ${RESULT}"
76
+ # Append [disk=ID] links for uploaded artifacts
77
+ if [[ -n "$DISK_IDS" ]]; then
78
+ while IFS= read -r did; do
79
+ [[ -n "$did" ]] && MSG="${MSG}[br]📎 [disk=${did}]"
80
+ done <<< "$DISK_IDS"
81
+ fi
82
+ b24_comment "$TASK_ID" "$MSG" || true
83
+ fi
84
+
85
+ exit 0
86
+
87
+ # ── Artifact attach helper ───────────────────────────────────────────────────
88
+
89
+ _attach_skill_artifacts() {
90
+ local task_id="$1"
91
+ local session_id="$2"
92
+ local payload="$3"
93
+ local skill_name="$4"
94
+
95
+ local attached_files=()
96
+ local disk_ids=()
97
+
98
+ # Path 1: parse ```bitrix-artifact``` fenced block from tool_response
99
+ local tool_response
100
+ tool_response=$(echo "$payload" | jq -r '.tool_response // ""')
101
+ local artifact_path
102
+ artifact_path=$(printf '%s' "$tool_response" | \
103
+ awk '/```bitrix-artifact/{flag=1;next}/```/{flag=0}flag' | \
104
+ jq -r '.path // empty' 2>/dev/null || true)
105
+
106
+ if [[ -n "$artifact_path" && -f "$artifact_path" ]]; then
107
+ local rel_dir; rel_dir=$(dirname "$artifact_path")
108
+ local drive_id
109
+ drive_id=$(b24_attach_artifact "$task_id" "$artifact_path" "$session_id" "$rel_dir") || true
110
+ if [[ -n "$drive_id" ]]; then
111
+ attached_files+=("$artifact_path")
112
+ disk_ids+=("$drive_id")
113
+ fi
114
+ fi
115
+
116
+ # Path 2: fallback — glob .md files in plans/ modified in last 60s
117
+ if [[ ${#attached_files[@]} -eq 0 ]]; then
118
+ local glob_pattern=""
119
+ case "$skill_name" in
120
+ plan|planner) glob_pattern="plans/*/plan.md plans/*/phase-*.md" ;;
121
+ brainstorm) glob_pattern="plans/reports/brainstorm-*.md plans/*/brainstorm-*.md" ;;
122
+ cook) glob_pattern="plans/*/plan.md plans/*/phase-*.md plans/reports/*.md" ;;
123
+ research) glob_pattern="plans/reports/research-*.md plans/reports/researcher-*.md" ;;
124
+ journal) glob_pattern="docs/journal/*.md" ;;
125
+ esac
126
+
127
+ if [[ -n "$glob_pattern" ]]; then
128
+ local f
129
+ for f in $glob_pattern; do
130
+ [[ ! -f "$f" ]] && continue
131
+ # Only attach recently modified files (within last 60 seconds)
132
+ local age=$(( $(date -u +%s) - $(date -r "$f" +%s 2>/dev/null || echo 0) ))
133
+ [[ "$age" -gt 60 ]] && continue
134
+ local rel_dir; rel_dir=$(dirname "$f")
135
+ local drive_id
136
+ drive_id=$(b24_attach_artifact "$task_id" "$f" "$session_id" "$rel_dir") || true
137
+ if [[ -n "$drive_id" ]]; then
138
+ attached_files+=("$f")
139
+ disk_ids+=("$drive_id")
140
+ fi
141
+ done
142
+ fi
143
+ fi
144
+
145
+ # Record attached files to session registry for Stop hook digest (Phase 4)
146
+ if [[ ${#attached_files[@]} -gt 0 ]]; then
147
+ local attached_json
148
+ attached_json=$(printf '%s\n' "${attached_files[@]}" | jq -R . | jq -s .)
149
+ local attach_log="$SESSION_DIR/attached-${session_id}.json"
150
+ if [[ -f "$attach_log" ]]; then
151
+ # Merge with existing list
152
+ local existing
153
+ existing=$(cat "$attach_log")
154
+ echo "$existing $attached_json" | jq -s '.[0] + .[1] | unique' > "$attach_log"
155
+ else
156
+ echo "$attached_json" > "$attach_log"
157
+ fi
158
+ fi
159
+
160
+ # Echo drive IDs to stdout so caller can build [disk=ID] links
161
+ local did
162
+ for did in "${disk_ids[@]}"; do
163
+ echo "$did"
164
+ done
165
+ }
@@ -0,0 +1,58 @@
1
+ #!/bin/bash
2
+ # PreToolUse hook — fires before Skill tool executes
3
+ # Captures full task meta into session state; posts "started" comment;
4
+ # increments skill counter → starts task timer on first skill (Phase 2).
5
+
6
+ source "$(dirname "$0")/bitrix-lib.sh"
7
+ _b24_preflight || exit 0
8
+
9
+ PAYLOAD=$(cat)
10
+ SESSION_ID=$(echo "$PAYLOAD" | jq -r '.session_id // "unknown"')
11
+ SKILL_NAME=$(echo "$PAYLOAD" | jq -r '.tool_input.skill // "unknown"')
12
+ SKILL_ARGS=$(echo "$PAYLOAD" | jq -r '.tool_input.args // ""')
13
+
14
+ STATE_FILE="$SESSION_DIR/${SESSION_ID}.json"
15
+ START_TIME=$(date -u +%s)
16
+
17
+ # Reuse existing state if present (multiple skills in one session); else bootstrap.
18
+ if [[ -f "$STATE_FILE" ]]; then
19
+ STATE=$(cat "$STATE_FILE")
20
+ TASK_META=$(echo "$STATE" | jq -c '.task_meta // empty')
21
+ else
22
+ TASK_META=$(find_task_meta) || exit 0
23
+ STATE=$(jq -n \
24
+ --arg sid "$SESSION_ID" \
25
+ --argjson meta "$TASK_META" \
26
+ --arg st "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
27
+ '{session_id: $sid, task_meta: $meta, session_start: $st, skills_run: []}')
28
+ fi
29
+
30
+ [[ -z "$TASK_META" ]] && exit 0
31
+ TASK_ID=$(echo "$TASK_META" | jq -r '.task_id // ""')
32
+ [[ -z "$TASK_ID" ]] && exit 0
33
+
34
+ # Skip lifecycle if task was closed at SessionStart
35
+ SESSION_ENV_FILE="$SESSION_DIR/session-env-${SESSION_ID}"
36
+ if [[ -f "$SESSION_ENV_FILE" ]]; then
37
+ source "$SESSION_ENV_FILE" 2>/dev/null || true
38
+ [[ "${BTS_TASK_CLOSED:-}" == "1" ]] && exit 0
39
+ fi
40
+
41
+ STATE=$(echo "$STATE" | jq \
42
+ --arg skill "$SKILL_NAME" \
43
+ --arg args "$SKILL_ARGS" \
44
+ --argjson start "$START_TIME" \
45
+ '.current_skill = {skill: $skill, args: $args, start: $start}')
46
+
47
+ echo "$STATE" > "$STATE_FILE"
48
+
49
+ # Increment skill counter; activate native timer on every skill start
50
+ _skill_counter_incr "$SESSION_ID" >/dev/null
51
+ b24_timer_start "$TASK_ID"
52
+
53
+ # Only post "bắt đầu" when args carry meaningful context (≥15 chars)
54
+ if [[ ${#SKILL_ARGS} -ge 15 ]]; then
55
+ EMOJI=$(skill_emoji "$SKILL_NAME")
56
+ b24_comment "$TASK_ID" "${EMOJI} [Claude] ${SKILL_NAME} bắt đầu: ${SKILL_ARGS}" || true
57
+ fi
58
+ exit 0
@@ -0,0 +1,110 @@
1
+ #!/bin/bash
2
+ # BB code rendering helpers for Bitrix task chat output.
3
+ # Bitrix renders BB code, NOT markdown. Source this file to use the helpers.
4
+ #
5
+ # Extracted from bitrix-render-digest.sh logic.
6
+ # Depends on: _format_duration from bitrix-lib.sh (source that first).
7
+ #
8
+ # Usage:
9
+ # source "$(dirname "$0")/bitrix-lib.sh"
10
+ # source "$(dirname "$0")/bb-formatter.sh"
11
+
12
+ # Wrap text in [b]...[/b]
13
+ bb_bold() {
14
+ printf '[b]%s[/b]' "$1"
15
+ }
16
+
17
+ # Wrap text in [i]...[/i]
18
+ bb_italic() {
19
+ printf '[i]%s[/i]' "$1"
20
+ }
21
+
22
+ # Wrap text in [code]...[/code]
23
+ bb_code() {
24
+ printf '[code]%s[/code]' "$1"
25
+ }
26
+
27
+ # Wrap text in [quote]...[/quote]
28
+ bb_quote() {
29
+ printf '[quote]%s[/quote]' "$1"
30
+ }
31
+
32
+ # Render a URL link: bb_link <url> <label>
33
+ bb_link() {
34
+ printf '[url=%s]%s[/url]' "$1" "$2"
35
+ }
36
+
37
+ # Render a Bitrix Drive file attachment tag: bb_disk <drive_file_id>
38
+ bb_disk() {
39
+ printf '[disk=%s]' "$1"
40
+ }
41
+
42
+ # Render a bullet list item with Unicode bullet
43
+ bb_bullet() {
44
+ printf '• %s' "$1"
45
+ }
46
+
47
+ # Render the session summary header line
48
+ # bb_session_header <session_start_iso>
49
+ bb_session_header() {
50
+ local start="${1:-?}"
51
+ printf '[b][Claude] Session summary[/b] (%s)' "$start"
52
+ }
53
+
54
+ # Render skills section given a skills_log JSON file path.
55
+ # Output: per-skill bullet lines + total line.
56
+ # Requires _format_duration from bitrix-lib.sh.
57
+ # bb_render_skills_section <skills_log_file>
58
+ bb_render_skills_section() {
59
+ local log_file="$1"
60
+ [[ ! -f "$log_file" ]] && return 0
61
+
62
+ local total_seconds
63
+ total_seconds=$(jq '[.[].elapsed_seconds // 0] | add // 0' "$log_file")
64
+ local count
65
+ count=$(jq 'length' "$log_file")
66
+
67
+ local summary=""
68
+ for ((idx = 0; idx < count; idx++)); do
69
+ local s_name s_sec s_fmt
70
+ s_name=$(jq -r ".[$idx].skill // \"?\"" "$log_file")
71
+ s_sec=$(jq -r ".[$idx].elapsed_seconds // 0" "$log_file")
72
+ s_fmt=$(_format_duration "$s_sec")
73
+ summary="${summary}• ${s_name} (${s_fmt})
74
+ "
75
+ done
76
+ # Trim trailing newline
77
+ summary="${summary%$'\n'}"
78
+
79
+ local total_fmt
80
+ total_fmt=$(_format_duration "$total_seconds")
81
+
82
+ if [[ -n "$summary" ]]; then
83
+ printf '%s\n\n[b]Tổng thời gian:[/b] %s' "$summary" "$total_fmt"
84
+ fi
85
+ }
86
+
87
+ # Render plan progress section given a plan directory.
88
+ # bb_render_plan_section <plan_dir>
89
+ bb_render_plan_section() {
90
+ local plan_dir="$1"
91
+ [[ -z "$plan_dir" || ! -f "${plan_dir}/plan.md" ]] && return 0
92
+
93
+ local plan_title plan_status
94
+ plan_title=$(grep -m1 '^# Plan:' "${plan_dir}/plan.md" | sed 's/^# Plan: *//' || true)
95
+ plan_status=$(awk '/^---/{c++;next} c==1 && /^status:/{print $2;exit}' "${plan_dir}/plan.md" || true)
96
+
97
+ local checked=0 total_boxes=0
98
+ for f in "${plan_dir}"/phase-*.md; do
99
+ [[ ! -f "$f" ]] && continue
100
+ checked=$(( checked + $(grep -c '^\- \[x\]' "$f" 2>/dev/null || echo 0) ))
101
+ total_boxes=$(( total_boxes + $(grep -c '^\- \[.\]' "$f" 2>/dev/null || echo 0) ))
102
+ done
103
+
104
+ if [[ -n "$plan_title" ]]; then
105
+ local line="📋 [b]Plan:[/b] ${plan_title}"
106
+ [[ -n "$plan_status" ]] && line="${line} [${plan_status}]"
107
+ [[ "$total_boxes" -gt 0 ]] && line="${line} — ${checked}/${total_boxes} tasks done"
108
+ printf '%s' "$line"
109
+ fi
110
+ }