@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.
- package/CHANGELOG.md +169 -0
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/bin/bitrix-skills.js +3 -0
- package/dist/cli.js +1510 -0
- package/dist/features/bx-task/install.js +111 -0
- package/dist/features/task-sync/index.js +1053 -0
- package/package.json +69 -0
- package/src/features/bx/assets/SKILL.md +34 -0
- package/src/features/bx/feature.json +8 -0
- package/src/features/bx-calendar/assets/SKILL.md +61 -0
- package/src/features/bx-calendar/assets/availability.md +65 -0
- package/src/features/bx-calendar/assets/meeting.md +87 -0
- package/src/features/bx-calendar/assets/reminder.md +71 -0
- package/src/features/bx-calendar/assets/sync.md +70 -0
- package/src/features/bx-calendar/feature.json +10 -0
- package/src/features/bx-crm/assets/SKILL.md +59 -0
- package/src/features/bx-crm/assets/commerce.md +96 -0
- package/src/features/bx-crm/assets/onboard.md +127 -0
- package/src/features/bx-crm/assets/report.md +98 -0
- package/src/features/bx-crm/assets/research.md +71 -0
- package/src/features/bx-crm/feature.json +10 -0
- package/src/features/bx-task/assets/SKILL.md +148 -0
- package/src/features/bx-task/assets/lib/bx-api.sh +39 -0
- package/src/features/bx-task/assets/lib/bx-checklist.sh +127 -0
- package/src/features/bx-task/assets/lib/bx-resolve-task.sh +41 -0
- package/src/features/bx-task/assets/lib/bx-state.sh +131 -0
- package/src/features/bx-task/assets/lib/bx-tasks.sh +109 -0
- package/src/features/bx-task/assets/references/bootstrap.md +184 -0
- package/src/features/bx-task/assets/references/feature.md +97 -0
- package/src/features/bx-task/assets/references/init-templates/cli-tool.md +47 -0
- package/src/features/bx-task/assets/references/init-templates/generic.md +31 -0
- package/src/features/bx-task/assets/references/init-templates/library.md +45 -0
- package/src/features/bx-task/assets/references/init-templates/monorepo.md +38 -0
- package/src/features/bx-task/assets/references/init-templates/npm-package.md +40 -0
- package/src/features/bx-task/assets/references/init-templates/web-app.md +46 -0
- package/src/features/bx-task/assets/references/init.md +107 -0
- package/src/features/bx-task/assets/references/roadmap.md +93 -0
- package/src/features/bx-task/assets/references/summary.md +269 -0
- package/src/features/bx-task/assets/references/sync.md +104 -0
- package/src/features/bx-task/assets/references/time-log.md +214 -0
- package/src/features/bx-task/feature.json +10 -0
- package/src/features/bx-task/install.ts +117 -0
- package/src/features/task-sync/assets/docs/bitrix-task-reference.md +318 -0
- package/src/features/task-sync/assets/docs/bitrix-task-sync.md +254 -0
- package/src/features/task-sync/assets/githooks/commit-msg +44 -0
- package/src/features/task-sync/assets/githooks/install.sh +15 -0
- package/src/features/task-sync/assets/manifest.json +108 -0
- package/src/features/task-sync/assets/rules/00-bitrix-task-sync.md +161 -0
- package/src/features/task-sync/assets/scripts/bitrix-attach-files.sh +55 -0
- package/src/features/task-sync/assets/scripts/bitrix-lib.sh +540 -0
- package/src/features/task-sync/assets/scripts/bitrix-render-digest.sh +116 -0
- package/src/features/task-sync/assets/scripts/bitrix-session-check.sh +51 -0
- package/src/features/task-sync/assets/scripts/bitrix-session-sync.sh +89 -0
- package/src/features/task-sync/assets/scripts/bitrix-skill-end.sh +165 -0
- package/src/features/task-sync/assets/scripts/bitrix-skill-start.sh +58 -0
- package/src/features/task-sync/assets/scripts/lib/bb-formatter.sh +110 -0
- package/src/features/task-sync/assets/scripts/lib/bitrix-lib.sh +540 -0
- package/src/features/task-sync/assets/scripts/lib/time-helpers.sh +57 -0
- package/src/features/task-sync/assets/workflows/bitrix-sync.yml +85 -0
- package/src/features/task-sync/commands/install.ts +296 -0
- package/src/features/task-sync/commands/uninstall.ts +189 -0
- package/src/features/task-sync/commands/update.ts +11 -0
- package/src/features/task-sync/commands/verify.ts +141 -0
- package/src/features/task-sync/feature.json +12 -0
- package/src/features/task-sync/index.ts +121 -0
- package/src/features/task-sync/lib/dest-map.ts +96 -0
- package/src/features/task-sync/lib/drift-check.ts +47 -0
- package/src/features/task-sync/lib/file-ops.ts +36 -0
- package/src/features/task-sync/lib/manifest.ts +66 -0
- package/src/features/task-sync/lib/project-root.ts +38 -0
- package/src/features/task-sync/lib/settings-merge.ts +112 -0
- package/src/features/task-sync/lib/skill-refs.ts +106 -0
- package/src/features/task-sync/lib/task-id-finder.ts +31 -0
- package/src/features/task-sync/lib/token-extractor.ts +52 -0
- package/src/features/task-sync/lib/version.ts +36 -0
- 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
|