dw-kit 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,246 +0,0 @@
1
- #!/bin/bash
2
- # scripts/upgrade.sh
3
- # ⚠ DEPRECATED: Prefer `dw upgrade` from npm CLI.
4
- # Upgrade dw-kit: update generated/ files, preserve overrides/ và extensions/
5
- # Usage: bash scripts/upgrade.sh [--dry-run] [--layer core|platform|all]
6
-
7
- set -euo pipefail
8
-
9
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
- PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11
-
12
- # ── Options ──────────────────────────────────────────────────────────────────
13
- DRY_RUN=false
14
- LAYER="all"
15
-
16
- for arg in "$@"; do
17
- case "$arg" in
18
- --dry-run) DRY_RUN=true ;;
19
- --layer) shift; LAYER="${1:-all}" ;;
20
- --layer=*) LAYER="${arg#*=}" ;;
21
- esac
22
- done
23
-
24
- # ── Helpers ───────────────────────────────────────────────────────────────────
25
- log() { echo " $*"; }
26
- info() { echo ""; echo "▶ $*"; }
27
- warn() { echo " ⚠ $*"; }
28
- ok() { echo " ✓ $*"; }
29
- dry() { echo " [dry-run] $*"; }
30
-
31
- do_copy() {
32
- local src="$1" dst="$2"
33
- if [ "$DRY_RUN" = true ]; then
34
- dry "cp $src → $dst"
35
- else
36
- mkdir -p "$(dirname "$dst")"
37
- cp "$src" "$dst"
38
- fi
39
- }
40
-
41
- do_mkdir() {
42
- if [ "$DRY_RUN" = true ]; then
43
- dry "mkdir -p $1"
44
- else
45
- mkdir -p "$1"
46
- fi
47
- }
48
-
49
- # ── Read current versions ─────────────────────────────────────────────────────
50
- CONFIG_FILE="$PROJECT_ROOT/.dw/config/dw.config.yml"
51
- CURRENT_CORE="unknown"
52
- CURRENT_PLATFORM="unknown"
53
-
54
- if [ -f "$CONFIG_FILE" ]; then
55
- CURRENT_CORE=$(grep -m1 "core_version:" "$CONFIG_FILE" 2>/dev/null \
56
- | sed 's/.*:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]' || echo "unknown")
57
- CURRENT_PLATFORM=$(grep -m1 "platform_version:" "$CONFIG_FILE" 2>/dev/null \
58
- | sed 's/.*:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]' || echo "unknown")
59
- fi
60
-
61
- TOOLKIT_CORE=$(grep -m1 "core-version:" "$PROJECT_ROOT/core/WORKFLOW.md" 2>/dev/null \
62
- | sed 's/.*core-version:[[:space:]]*//' | tr -d ' -->' | head -1 || echo "1.0")
63
-
64
- echo ""
65
- echo "══════════════════════════════════════════"
66
- echo " dw-kit Upgrade"
67
- echo " Current: core=$CURRENT_CORE platform=$CURRENT_PLATFORM"
68
- echo " Toolkit: core=$TOOLKIT_CORE"
69
- if [ "$DRY_RUN" = true ]; then
70
- echo " Mode: DRY RUN (no changes)"
71
- fi
72
- echo "══════════════════════════════════════════"
73
-
74
- # ── Phase 1: Update generated/ ───────────────────────────────────────────────
75
- info "Phase 1: Update generated files"
76
-
77
- GENERATED="$PROJECT_ROOT/.dw/adapters/claude-cli/generated"
78
- CLAUDE_DIR="$PROJECT_ROOT/.claude"
79
-
80
- if [ "$LAYER" = "all" ] || [ "$LAYER" = "platform" ]; then
81
- # Copy generated skills → .claude/skills (skip if override exists)
82
- if [ -d "$GENERATED/skills" ]; then
83
- for skill_dir in "$GENERATED/skills"/*/; do
84
- skill_name=$(basename "$skill_dir")
85
- override="$PROJECT_ROOT/.dw/adapters/claude-cli/overrides/skills/$skill_name"
86
-
87
- if [ -d "$override" ]; then
88
- warn "Skill '$skill_name': override exists → keeping override"
89
- # Copy override instead of generated
90
- for f in "$override"/*; do
91
- [ -f "$f" ] && do_copy "$f" "$CLAUDE_DIR/skills/$skill_name/$(basename "$f")"
92
- done
93
- else
94
- for f in "$skill_dir"*; do
95
- [ -f "$f" ] && do_copy "$f" "$CLAUDE_DIR/skills/$skill_name/$(basename "$f")"
96
- done
97
- ok "Skill '$skill_name': updated"
98
- fi
99
- done
100
- fi
101
-
102
- # Copy generated agents → .claude/agents (skip if override exists)
103
- if [ -d "$GENERATED/agents" ]; then
104
- for agent_file in "$GENERATED/agents"/*.md; do
105
- [ -f "$agent_file" ] || continue
106
- agent_name=$(basename "$agent_file")
107
- override="$PROJECT_ROOT/.dw/adapters/claude-cli/overrides/agents/$agent_name"
108
-
109
- if [ -f "$override" ]; then
110
- warn "Agent '$agent_name': override exists → keeping override"
111
- do_copy "$override" "$CLAUDE_DIR/agents/$agent_name"
112
- else
113
- do_copy "$agent_file" "$CLAUDE_DIR/agents/$agent_name"
114
- ok "Agent '$agent_name': updated"
115
- fi
116
- done
117
- fi
118
- fi
119
-
120
- # ── Phase 2: Copy extensions/ ────────────────────────────────────────────────
121
- info "Phase 2: Copy extensions (team-specific skills)"
122
-
123
- EXTENSIONS="$PROJECT_ROOT/.dw/adapters/claude-cli/extensions"
124
- if [ -d "$EXTENSIONS" ]; then
125
- ext_count=0
126
- for ext_dir in "$EXTENSIONS"/*/; do
127
- [ -d "$ext_dir" ] || continue
128
- ext_name=$(basename "$ext_dir")
129
- [ "$ext_name" = ".gitkeep" ] && continue
130
-
131
- do_mkdir "$CLAUDE_DIR/skills/$ext_name"
132
- for f in "$ext_dir"*; do
133
- [ -f "$f" ] && do_copy "$f" "$CLAUDE_DIR/skills/$ext_name/$(basename "$f")"
134
- done
135
- ok "Extension '$ext_name': installed"
136
- ext_count=$((ext_count + 1))
137
- done
138
- [ $ext_count -eq 0 ] && log "No extensions found"
139
- fi
140
-
141
- # ── Phase 3: Merge settings.json ─────────────────────────────────────────────
142
- info "Phase 3: Merge settings.json"
143
-
144
- SETTINGS_TEMPLATE="$GENERATED/settings.json"
145
- SETTINGS_TARGET="$CLAUDE_DIR/settings.json"
146
-
147
- if [ -f "$SETTINGS_TEMPLATE" ] && [ -f "$SETTINGS_TARGET" ]; then
148
- if command -v python3 &>/dev/null; then
149
- if [ "$DRY_RUN" = true ]; then
150
- dry "Merge settings.json (python3)"
151
- else
152
- python3 - "$SETTINGS_TEMPLATE" "$SETTINGS_TARGET" <<'PYEOF'
153
- import json, sys
154
-
155
- template_path = sys.argv[1]
156
- target_path = sys.argv[2]
157
-
158
- with open(template_path) as f:
159
- template = json.load(f)
160
- with open(target_path) as f:
161
- target = json.load(f)
162
-
163
- def deep_merge(base, override):
164
- """Override values from base with override, but preserve keys not in template."""
165
- result = dict(base)
166
- for k, v in override.items():
167
- if k in result and isinstance(result[k], dict) and isinstance(v, dict):
168
- result[k] = deep_merge(result[k], v)
169
- else:
170
- result[k] = v
171
- return result
172
-
173
- merged = deep_merge(template, target)
174
-
175
- with open(target_path, 'w') as f:
176
- json.dump(merged, f, indent=2, ensure_ascii=False)
177
- f.write('\n')
178
- PYEOF
179
- ok "settings.json: merged"
180
- fi
181
- else
182
- warn "python3 not found — skipping settings.json merge. Manual merge may be needed."
183
- fi
184
- elif [ -f "$SETTINGS_TEMPLATE" ] && [ ! -f "$SETTINGS_TARGET" ]; then
185
- do_copy "$SETTINGS_TEMPLATE" "$SETTINGS_TARGET"
186
- ok "settings.json: created from template"
187
- fi
188
-
189
- # ── Phase 4: Update version in config ────────────────────────────────────────
190
- info "Phase 4: Update version tracking"
191
-
192
- if [ "$DRY_RUN" = false ] && [ -f "$CONFIG_FILE" ]; then
193
- TODAY=$(date +%Y-%m-%d)
194
- if command -v python3 &>/dev/null; then
195
- python3 - "$CONFIG_FILE" "$TOOLKIT_CORE" "$TODAY" <<'PYEOF'
196
- import sys, re
197
-
198
- config_path = sys.argv[1]
199
- new_core = sys.argv[2]
200
- today = sys.argv[3]
201
-
202
- with open(config_path) as f:
203
- content = f.read()
204
-
205
- content = re.sub(r'(core_version:\s*)["\']?[\d.]+["\']?', f'\\g<1>"{new_core}"', content)
206
- content = re.sub(r'(last_upgrade:\s*)["\']?[\d-]+["\']?', f'\\g<1>"{today}"', content)
207
-
208
- with open(config_path, 'w') as f:
209
- f.write(content)
210
- PYEOF
211
- ok "Config version updated: core=$TOOLKIT_CORE, last_upgrade=$TODAY"
212
- fi
213
- fi
214
-
215
- # ── Phase 5: Check for CI/CD references needing manual update ────────────────
216
- info "Phase 5: Check backward compatibility"
217
-
218
- OLD_CONFIG="$PROJECT_ROOT/.dw/config/dw.config.yml"
219
- if [ -f "$OLD_CONFIG" ]; then
220
- if [ -L "$OLD_CONFIG" ]; then
221
- ok "config/dw.config.yml: symlink intact (backward compat)"
222
- else
223
- warn "config/dw.config.yml exists as real file. Run scripts/migrate-v03-to-v1.sh first."
224
- fi
225
- fi
226
-
227
- # Check for CI references
228
- for ci_file in ".github/workflows/"*.yml ".gitlab-ci.yml" "Makefile" ".circleci/config.yml"; do
229
- full_path="$PROJECT_ROOT/$ci_file"
230
- if [ -f "$full_path" ] && grep -q ".dw/.dw/config/dw.config.yml" "$full_path" 2>/dev/null; then
231
- warn "CI file '$ci_file' references config/dw.config.yml — update manually"
232
- fi
233
- done
234
-
235
- # ── Summary ───────────────────────────────────────────────────────────────────
236
- echo ""
237
- echo "══════════════════════════════════════════"
238
- if [ "$DRY_RUN" = true ]; then
239
- echo " DRY RUN complete. No changes made."
240
- echo " Run without --dry-run to apply."
241
- else
242
- echo " Upgrade complete!"
243
- echo " Core version: $TOOLKIT_CORE"
244
- fi
245
- echo "══════════════════════════════════════════"
246
- echo ""
package/setup.sh DELETED
@@ -1,382 +0,0 @@
1
- #!/bin/bash
2
- # =============================================================================
3
- # dw-kit — Interactive Setup Wizard (LEGACY)
4
- # =============================================================================
5
- # ⚠ DEPRECATED: Use `dw init` instead (npm install -g dw-kit)
6
- #
7
- # npm install -g dw-kit
8
- # dw init
9
- #
10
- # This script is kept for environments without Node.js.
11
- # New features will only be added to the `dw` CLI.
12
- # =============================================================================
13
- # Chạy từ root của dự án của bạn. Hỏi 4 câu, tự cấu hình mọi thứ.
14
- # Thời gian: ~1-2 phút
15
- #
16
- # Usage:
17
- # bash .dw-module/setup.sh
18
- #
19
- # Silent mode (CI/scripted):
20
- # DW_NAME="my-app" DW_LEVEL=2 DW_ROLES="dev,techlead" DW_LANG="vi" \
21
- # bash .dw-module/setup.sh --silent
22
- # =============================================================================
23
-
24
- set -e
25
-
26
- TOOLKIT_DIR=".dw-module"
27
- SILENT="${1:-}"
28
-
29
-
30
- CYAN='\033[0;36m'
31
- GREEN='\033[0;32m'
32
- YELLOW='\033[1;33m'
33
- BOLD='\033[1m'
34
- NC='\033[0m'
35
-
36
- # Kiểm tra submodule
37
- if [ ! -d "$TOOLKIT_DIR/.claude" ]; then
38
- echo "Toolkit chưa được add. Chạy trước:"
39
- echo " git submodule add https://github.com/dv-workflow/dv-workflow.git .dw-module"
40
- exit 1
41
- fi
42
-
43
- clear
44
- echo -e "${CYAN}${BOLD}"
45
- echo " ██████╗ ██╗ ██╗ ██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗██╗ ██████╗ ██╗ ██╗"
46
- echo " ██╔══██╗██║ ██║ ██║ ██║██╔═══██╗██╔══██╗██║ ██╔╝██╔════╝██║ ██╔═══██╗██║ ██║"
47
- echo " ██║ ██║██║ ██║ ██║ █╗ ██║██║ ██║██████╔╝█████╔╝ █████╗ ██║ ██║ ██║██║ █╗ ██║"
48
- echo " ██║ ██║╚██╗ ██╔╝ ██║███╗██║██║ ██║██╔══██╗██╔═██╗ ██╔══╝ ██║ ██║ ██║██║███╗██║"
49
- echo " ██████╔╝ ╚████╔╝ ╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗██║ ███████╗╚██████╔╝╚███╔███╔╝"
50
- echo " ╚═════╝ ╚═══╝ ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝"
51
- echo -e "${NC}"
52
- echo -e "${CYAN} Setup Wizard v0.3 — ~1 phút${NC}"
53
- echo ""
54
-
55
- # =============================================================================
56
- # BƯỚC 1: Đọc từ env (silent mode) hoặc hỏi user (interactive mode)
57
- # =============================================================================
58
-
59
- if [ -n "$SILENT" ] && [ "$SILENT" = "--silent" ]; then
60
- # Silent mode: đọc từ environment variables
61
- PROJECT_NAME="${DW_NAME:-my-project}"
62
- LEVEL="${DW_LEVEL:-2}"
63
- ROLES_RAW="${DW_ROLES:-dev,techlead}"
64
- LANG="${DW_LANG:-vi}"
65
- else
66
- # ---- Câu 1: Project name ----
67
- echo -e "${BOLD}[Project] Tên project?${NC}"
68
- read -r -p " > " PROJECT_NAME
69
- PROJECT_NAME="${PROJECT_NAME:-my-project}"
70
- echo ""
71
-
72
- # ---- Câu 2: Level ----
73
- echo -e "${BOLD}[Level] Chọn level workflow:${NC}"
74
- echo " 1 = Lite — research → execute → commit (solo dev, hotfix nhanh)"
75
- echo " 2 = Standard — research → plan → execute → review (team, feature mới) [default]"
76
- echo " 3 = Enterprise — full workflow + living docs + metrics (team lớn, audit trail)"
77
- read -r -p " Level [1/2/3, Enter = 2]: " LEVEL
78
- LEVEL="${LEVEL:-2}"
79
- echo ""
80
-
81
- # ---- Câu 3: Roles ----
82
- echo -e "${BOLD}[Role] Team có những roles nào? (Dev luôn được bật)${NC}"
83
- echo " Nhập số cách nhau bởi dấu phẩy, hoặc Enter để chọn dev+techlead:"
84
- echo " 1 = Dev (luôn bật)"
85
- echo " 2 = Tech Lead — architecture review, approve plans"
86
- echo " 3 = BA — requirements, user stories"
87
- echo " 4 = QC — test plans, bug reports"
88
- echo " 5 = PM — dashboard, metrics"
89
- read -r -p " Roles [1,2,...]: " ROLES_INPUT
90
- ROLES_INPUT="${ROLES_INPUT:-1,2}"
91
- echo ""
92
-
93
- # ---- Câu 4: Language ----
94
- echo -e "${BOLD}[Lang] Ngôn ngữ docs output?${NC}"
95
- echo " vi = Tiếng Việt [default]"
96
- echo " en = English"
97
- read -r -p " Language [vi/en]: " LANG
98
- LANG="${LANG:-vi}"
99
- echo ""
100
- fi
101
-
102
- # =============================================================================
103
- # BUILD roles list từ input
104
- # =============================================================================
105
-
106
- build_roles() {
107
- local input="$1"
108
- local roles=" - dev"
109
- # Nếu input dạng "dev,techlead" (silent mode)
110
- if echo "$input" | grep -q "[a-z]"; then
111
- echo "$input" | tr ',' '\n' | while read -r r; do
112
- r=$(echo "$r" | tr -d ' ')
113
- [ "$r" != "dev" ] && echo " - $r"
114
- done
115
- return
116
- fi
117
- # Nếu input dạng "1,2,3" (interactive mode)
118
- echo "$input" | tr ',' '\n' | while read -r n; do
119
- n=$(echo "$n" | tr -d ' ')
120
- case "$n" in
121
- 2) echo " - techlead" ;;
122
- 3) echo " - ba" ;;
123
- 4) echo " - qc" ;;
124
- 5) echo " - pm" ;;
125
- esac
126
- done
127
- }
128
-
129
- HAS_TL=false; HAS_BA=false; HAS_QC=false; HAS_PM=false
130
-
131
- check_role() {
132
- local input="$1"
133
- if echo "$input" | grep -q "techlead\|,2\b\|^2\b"; then HAS_TL=true; fi
134
- if echo "$input" | grep -q "ba\b\|,3\b\|^3\b"; then HAS_BA=true; fi
135
- if echo "$input" | grep -q "qc\b\|,4\b\|^4\b"; then HAS_QC=true; fi
136
- if echo "$input" | grep -q "pm\b\|,5\b\|^5\b"; then HAS_PM=true; fi
137
- }
138
-
139
- ROLES_RAW="${ROLES_RAW:-$ROLES_INPUT}"
140
- check_role "$ROLES_RAW"
141
-
142
- # =============================================================================
143
- # APPLY: Copy files + Write config
144
- # =============================================================================
145
-
146
- echo -e "${CYAN}Đang setup...${NC}"
147
-
148
- # Copy .claude/
149
- if [ -d ".claude" ]; then
150
- cp -rn "$TOOLKIT_DIR/.claude/skills" ".claude/" 2>/dev/null || true
151
- cp -rn "$TOOLKIT_DIR/.claude/agents" ".claude/" 2>/dev/null || true
152
- cp -rn "$TOOLKIT_DIR/.claude/rules" ".claude/" 2>/dev/null || true
153
- cp -rn "$TOOLKIT_DIR/.claude/hooks" ".claude/" 2>/dev/null || true
154
- cp -rn "$TOOLKIT_DIR/.claude/templates" ".claude/" 2>/dev/null || true
155
- [ ! -f ".claude/settings.json" ] && cp "$TOOLKIT_DIR/.claude/settings.json" ".claude/"
156
- else
157
- cp -r "$TOOLKIT_DIR/.claude" "./"
158
- fi
159
-
160
- # Chọn base config theo level
161
- if [ "$LEVEL" = "3" ]; then
162
- BASE_CONFIG="$TOOLKIT_DIR/project-templates/enterprise/.dw/config/dw.config.yml"
163
- elif [ "$LEVEL" = "1" ]; then
164
- BASE_CONFIG="$TOOLKIT_DIR/project-templates/old-maintenance/.dw/config/dw.config.yml"
165
- else
166
- BASE_CONFIG="$TOOLKIT_DIR/project-templates/new-product/.dw/config/dw.config.yml"
167
- fi
168
-
169
- # Ghi config với giá trị user đã chọn
170
- if [ ! -f ".dw/.dw/config/dw.config.yml" ]; then
171
- ROLES_YAML=" roles:\n - dev"
172
- $HAS_TL && ROLES_YAML="$ROLES_YAML\n - techlead"
173
- $HAS_BA && ROLES_YAML="$ROLES_YAML\n - ba"
174
- $HAS_QC && ROLES_YAML="$ROLES_YAML\n - qc"
175
- $HAS_PM && ROLES_YAML="$ROLES_YAML\n - pm"
176
-
177
- sed \
178
- -e "s|name: \"your.*\"|name: \"$PROJECT_NAME\"|" \
179
- -e "s|name: \"your-enterprise-project\"|name: \"$PROJECT_NAME\"|" \
180
- -e "s|language: \"vi\"|language: \"$LANG\"|" \
181
- "$BASE_CONFIG" > ".dw/.dw/config/dw.config.yml"
182
-
183
- # Inject roles (replace toàn bộ roles section) — pure awk, no Python needed
184
- ROLES_LINES=" - dev"
185
- $HAS_TL && ROLES_LINES="${ROLES_LINES}| - techlead"
186
- $HAS_BA && ROLES_LINES="${ROLES_LINES}| - ba"
187
- $HAS_QC && ROLES_LINES="${ROLES_LINES}| - qc"
188
- $HAS_PM && ROLES_LINES="${ROLES_LINES}| - pm"
189
-
190
- awk -v roles="$ROLES_LINES" '
191
- /^ roles:/ {
192
- print " roles:"
193
- n = split(roles, a, "|")
194
- for (i = 1; i <= n; i++) print a[i]
195
- in_roles = 1; next
196
- }
197
- in_roles && /^ -/ { next }
198
- { in_roles = 0; print }
199
- ' config/dw.config.yml > config/dw.config.yml.tmp \
200
- && mv config/dw.config.yml.tmp config/dw.config.yml
201
- fi
202
-
203
- # Tạo CLAUDE.md
204
- if [ ! -f "CLAUDE.md" ]; then
205
- cp "$TOOLKIT_DIR/CLAUDE.md" .
206
- cat >> CLAUDE.md << 'SECTION'
207
-
208
- ---
209
-
210
- ## Tech Stack
211
-
212
- <!-- Cập nhật phần này với stack thực tế của project -->
213
- - Framework: [e.g. NestJS / Django / Laravel / Next.js]
214
- - Database: [e.g. PostgreSQL / MySQL / MongoDB]
215
- - Testing: [e.g. Jest / Pytest / PHPUnit]
216
-
217
- ## Project-Specific Rules
218
-
219
- <!-- Thêm rules đặc thù của project -->
220
- - [Rule 1]
221
- SECTION
222
- fi
223
-
224
- # Tạo runtime directories (gom vào .dw/ để không pollute root)
225
- mkdir -p .dw/tasks .dw/docs .dw/metrics .dw/reports
226
-
227
- # =============================================================================
228
- # V2: Generate config/dw.config.yml và settings.json từ MCP config
229
- # =============================================================================
230
-
231
- # Copy config mới nếu chưa có (v1 structure)
232
- if [ ! -f ".dw/.dw/config/dw.config.yml" ] && [ -f "$TOOLKIT_DIR/.dw/config/dw.config.yml" ]; then
233
- mkdir -p config/presets
234
- cp "$TOOLKIT_DIR/.dw/config/dw.config.yml" ".dw/.dw/config/dw.config.yml"
235
- cp "$TOOLKIT_DIR/config/config.schema.json" ".dw/config/config.schema.json" 2>/dev/null || true
236
- cp "$TOOLKIT_DIR/config/presets/"* "config/presets/" 2>/dev/null || true
237
- fi
238
-
239
- # Generate settings.json từ claude.mcp config (nếu có python3 + có mcp config)
240
- generate_mcp_settings() {
241
- local config_file="${1:-config/dw.config.yml}"
242
- [ ! -f "$config_file" ] && return 0
243
- [ ! -f ".claude/settings.json" ] && return 0
244
- command -v python3 &>/dev/null || return 0
245
-
246
- python3 - "$config_file" ".claude/settings.json" <<'PYEOF' 2>/dev/null || true
247
- import sys, json, re
248
-
249
- config_path = sys.argv[1]
250
- settings_path = sys.argv[2]
251
-
252
- with open(config_path) as f:
253
- content = f.read()
254
-
255
- # Extract MCP servers từ YAML (simple parser — không cần PyYAML)
256
- mcp_servers = {}
257
- in_mcp = False
258
- current_server = None
259
-
260
- for line in content.split('\n'):
261
- stripped = line.rstrip()
262
- # Detect mcp: block
263
- if re.match(r'\s*mcp:\s*$', stripped):
264
- in_mcp = True
265
- continue
266
- if in_mcp:
267
- # New top-level key = end of mcp block
268
- if re.match(r'^[a-z_]', stripped) or re.match(r'^_toolkit', stripped):
269
- in_mcp = False
270
- continue
271
- # Server entry: - name: "..."
272
- m = re.match(r'\s*-\s*name:\s*["\']?([^"\']+)["\']?', stripped)
273
- if m:
274
- current_server = m.group(1).strip()
275
- mcp_servers[current_server] = {}
276
- continue
277
- if current_server:
278
- # command:
279
- m = re.match(r'\s+command:\s*["\']?([^"\']+)["\']?', stripped)
280
- if m:
281
- mcp_servers[current_server]['command'] = m.group(1).strip()
282
- # args: (array — simplified)
283
- m = re.match(r'\s+args:\s*\[([^\]]*)\]', stripped)
284
- if m:
285
- args = [a.strip().strip('"').strip("'") for a in m.group(1).split(',') if a.strip()]
286
- mcp_servers[current_server]['args'] = args
287
-
288
- if not mcp_servers:
289
- sys.exit(0)
290
-
291
- # Update settings.json
292
- with open(settings_path) as f:
293
- settings = json.load(f)
294
-
295
- settings['mcpServers'] = {}
296
- for name, server in mcp_servers.items():
297
- settings['mcpServers'][name] = {
298
- 'command': server.get('command', ''),
299
- 'args': server.get('args', [])
300
- }
301
-
302
- with open(settings_path, 'w') as f:
303
- json.dump(settings, f, indent=2, ensure_ascii=False)
304
- f.write('\n')
305
-
306
- print(f" MCP servers configured: {', '.join(mcp_servers.keys())}")
307
- PYEOF
308
- }
309
-
310
- generate_mcp_settings ".dw/.dw/config/dw.config.yml"
311
-
312
- # Validate config nếu có python3 và jsonschema
313
- if command -v python3 &>/dev/null && [ -f ".dw/.dw/config/dw.config.yml" ] && [ -f ".dw/config/config.schema.json" ]; then
314
- python3 -c "import jsonschema" 2>/dev/null && \
315
- python3 - ".dw/.dw/config/dw.config.yml" ".dw/config/config.schema.json" <<'PYEOF' 2>/dev/null || true
316
- import sys, json
317
- try:
318
- import yaml
319
- with open(sys.argv[1]) as f:
320
- config = yaml.safe_load(f)
321
- import jsonschema
322
- with open(sys.argv[2]) as f:
323
- schema = json.load(f)
324
- jsonschema.validate(config, schema)
325
- print(" config/dw.config.yml: valid")
326
- except jsonschema.ValidationError as e:
327
- print(f" ⚠ Config validation warning: {e.message}")
328
- except Exception:
329
- pass
330
- PYEOF
331
- fi
332
-
333
- # Gitignore
334
- if [ -f ".gitignore" ]; then
335
- if ! grep -q ".dw/metrics" .gitignore; then
336
- printf "\n# dw-kit\n.dw/metrics/\n.dw/reports/\nCLAUDE.local.md\n" >> .gitignore
337
- fi
338
- else
339
- cp "$TOOLKIT_DIR/.gitignore" .
340
- fi
341
-
342
- # =============================================================================
343
- # SUMMARY
344
- # =============================================================================
345
-
346
- clear
347
- echo -e "${GREEN}${BOLD}"
348
- echo " ✅ Setup hoàn tất!"
349
- echo -e "${NC}"
350
- echo " Project : $PROJECT_NAME"
351
- echo " Level : $LEVEL $([ "$LEVEL" = "1" ] && echo "(Lite)" || ([ "$LEVEL" = "2" ] && echo "(Standard)" || echo "(Enterprise — Level 3 beta)"))"
352
- echo " Language: $LANG"
353
- printf " Roles : dev"
354
- $HAS_TL && printf ", techlead"
355
- $HAS_BA && printf ", ba"
356
- $HAS_QC && printf ", qc"
357
- $HAS_PM && printf ", pm"
358
- echo ""
359
- echo ""
360
- echo " Files tạo:"
361
- echo " .claude/ — 22 skills, agents, rules, hooks, templates"
362
- echo " config/dw.config.yml"
363
- echo " CLAUDE.md"
364
- echo " .dw/tasks/ .dw/docs/ .dw/metrics/ .dw/reports/"
365
- echo ""
366
- echo -e "${CYAN} Skills đã bật:${NC}"
367
- echo " /dw-task-init /dw-research /dw-execute /dw-commit /dw-debug /dw-handoff"
368
- [ "$LEVEL" -ge 2 ] && echo " /dw-plan /dw-review /dw-estimate /dw-log-work"
369
- [ "$LEVEL" -ge 3 ] && echo " /dw-docs-update /dw-dashboard /dw-sprint-review"
370
- $HAS_TL && echo " /dw-arch-review (Tech Lead)"
371
- $HAS_BA && echo " /dw-requirements (BA)"
372
- $HAS_QC && echo " /dw-test-plan (QC)"
373
- $HAS_PM && echo " /dw-dashboard (PM)"
374
- echo ""
375
- echo -e "${YELLOW} Bước tiếp theo:${NC}"
376
- echo " 1. Mở Claude Code trong thư mục này"
377
- echo " 2. Cập nhật Tech Stack trong CLAUDE.md (tuỳ chọn nhưng nên làm)"
378
- echo " 3. Chạy: /dw-task-init [tên-feature-đầu-tiên]"
379
- echo ""
380
- echo " Docs: .dw-module/docs/README.md"
381
- echo " Cheatsheet: .dw-module/docs/cheatsheet.md"
382
- echo ""