dw-kit 1.1.0 → 1.2.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/.claude/hooks/post-write.sh +9 -11
- package/.claude/hooks/privacy-block.sh +94 -0
- package/.claude/hooks/scout-block.sh +82 -0
- package/.claude/hooks/session-init.sh +74 -0
- package/.claude/hooks/stop-check.sh +36 -0
- package/.claude/rules/code-style.md +37 -37
- package/.claude/rules/commit-standards.md +37 -37
- package/.claude/rules/dw-core.md +100 -0
- package/.claude/rules/dw-skills.md +53 -0
- package/.claude/settings.json +30 -2
- package/.claude/skills/dw-kit-report/SKILL.md +152 -0
- package/.claude/skills/dw-research/SKILL.md +17 -1
- package/.claude/templates/agent-report.md +35 -0
- package/.dw/core/AGENTS.md +53 -0
- package/CLAUDE.md +27 -99
- package/README.md +119 -119
- package/package.json +31 -4
- package/src/cli.mjs +92 -92
- package/src/commands/init.mjs +17 -17
- package/src/lib/config.mjs +31 -2
- package/scripts/e2e-local-check.sh +0 -75
- package/src/__fixtures__/claude-cli-bug-snippet.js +0 -15
- package/src/smoke-test.mjs +0 -351
|
@@ -6,17 +6,15 @@
|
|
|
6
6
|
INPUT=$(cat)
|
|
7
7
|
|
|
8
8
|
# Extract file path từ tool result
|
|
9
|
-
FILE_PATH=$(echo "$INPUT" |
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
except:
|
|
19
|
-
pass
|
|
9
|
+
FILE_PATH=$(echo "$INPUT" | node -e "
|
|
10
|
+
let d='';
|
|
11
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
12
|
+
try{
|
|
13
|
+
const data=JSON.parse(d);
|
|
14
|
+
const p=data.file_path||data.path||data.filePath||(data.tool_input&&(data.tool_input.file_path||data.tool_input.path))||'';
|
|
15
|
+
process.stdout.write(p);
|
|
16
|
+
}catch(e){}
|
|
17
|
+
});
|
|
20
18
|
" 2>/dev/null || true)
|
|
21
19
|
|
|
22
20
|
[ -z "$FILE_PATH" ] && exit 0
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# .claude/hooks/privacy-block.sh — dw-kit v1.2
|
|
3
|
+
# Block agent reads vào sensitive files (credentials, secrets, private keys).
|
|
4
|
+
# Học từ claudekit privacy-block pattern.
|
|
5
|
+
#
|
|
6
|
+
# PreToolUse hook cho: Read
|
|
7
|
+
# exit 0 = allow, exit 2 = block
|
|
8
|
+
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
TOOL_NAME=$(echo "$INPUT" | node -e "
|
|
12
|
+
let d='';
|
|
13
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
14
|
+
try{ process.stdout.write(JSON.parse(d).tool_name||''); }catch(e){}
|
|
15
|
+
});
|
|
16
|
+
" 2>/dev/null || true)
|
|
17
|
+
|
|
18
|
+
[ "$TOOL_NAME" != "Read" ] && exit 0
|
|
19
|
+
|
|
20
|
+
FILE_PATH=$(echo "$INPUT" | node -e "
|
|
21
|
+
let d='';
|
|
22
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
23
|
+
try{ const p=JSON.parse(d); process.stdout.write((p.tool_input&&p.tool_input.file_path)||''); }catch(e){}
|
|
24
|
+
});
|
|
25
|
+
" 2>/dev/null || true)
|
|
26
|
+
|
|
27
|
+
[ -z "$FILE_PATH" ] && exit 0
|
|
28
|
+
|
|
29
|
+
BASENAME=$(basename "$FILE_PATH")
|
|
30
|
+
NORM=$(echo "$FILE_PATH" | tr '\\' '/')
|
|
31
|
+
|
|
32
|
+
# ── Allow-list: safe files dù tên giống sensitive ─────────────────────────────
|
|
33
|
+
ALLOWED_PATTERNS=(
|
|
34
|
+
".env.example"
|
|
35
|
+
".env.sample"
|
|
36
|
+
".env.template"
|
|
37
|
+
".env.test"
|
|
38
|
+
".env.local.example"
|
|
39
|
+
"*.example"
|
|
40
|
+
"*.sample"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
for allow in "${ALLOWED_PATTERNS[@]}"; do
|
|
44
|
+
if [[ "$BASENAME" == $allow ]]; then
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
|
|
49
|
+
# ── Block patterns ──────────────────────────────────────────────────────────────
|
|
50
|
+
BLOCKED=false
|
|
51
|
+
REASON=""
|
|
52
|
+
|
|
53
|
+
# .env files (nhưng không phải .env.example đã allow ở trên)
|
|
54
|
+
if [[ "$BASENAME" == ".env" ]] || [[ "$BASENAME" == .env.* ]]; then
|
|
55
|
+
BLOCKED=true
|
|
56
|
+
REASON="Environment file (có thể chứa secrets/API keys)"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Private key files
|
|
60
|
+
if [[ "$BASENAME" == *.pem ]] || [[ "$BASENAME" == *.key ]] || \
|
|
61
|
+
[[ "$BASENAME" == *.p12 ]] || [[ "$BASENAME" == *.pfx ]] || \
|
|
62
|
+
[[ "$BASENAME" == *.jks ]]; then
|
|
63
|
+
BLOCKED=true
|
|
64
|
+
REASON="Private key / certificate file"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Credentials & secrets files
|
|
68
|
+
if echo "$BASENAME" | grep -qiE '^(credentials|secrets?|secret[-_]key|api[-_]key|auth[-_]token|access[-_]token)(\.[a-z]+)?$'; then
|
|
69
|
+
BLOCKED=true
|
|
70
|
+
REASON="Credentials / secrets file"
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Known credential file patterns
|
|
74
|
+
if [[ "$BASENAME" == "*.credentials.json" ]] || \
|
|
75
|
+
echo "$NORM" | grep -qiE '/(credentials|secrets)/'; then
|
|
76
|
+
BLOCKED=true
|
|
77
|
+
REASON="Credentials directory hoặc file"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Service account / GCP keys
|
|
81
|
+
if echo "$BASENAME" | grep -qiE 'service[-_]account.*\.json$|gcp.*key.*\.json$|firebase.*key.*\.json$'; then
|
|
82
|
+
BLOCKED=true
|
|
83
|
+
REASON="Service account / cloud credentials"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
if [ "$BLOCKED" = true ]; then
|
|
87
|
+
echo "🔒 privacy-block: Blocked sensitive file read" >&2
|
|
88
|
+
echo " File: $FILE_PATH" >&2
|
|
89
|
+
echo " Lý do: $REASON" >&2
|
|
90
|
+
echo " Nếu thực sự cần đọc file này, hãy confirm rõ ràng trong prompt." >&2
|
|
91
|
+
exit 2
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
exit 0
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# .claude/hooks/scout-block.sh — dw-kit v1.2
|
|
3
|
+
# Block agent reads vào heavy/irrelevant directories để tăng performance.
|
|
4
|
+
# Học từ claudekit scout-block pattern.
|
|
5
|
+
#
|
|
6
|
+
# PreToolUse hook cho: Read, Glob
|
|
7
|
+
# exit 0 = allow, exit 2 = block
|
|
8
|
+
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract tool name và file path từ JSON input
|
|
12
|
+
TOOL_NAME=$(echo "$INPUT" | node -e "
|
|
13
|
+
let d='';
|
|
14
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
15
|
+
try{ process.stdout.write(JSON.parse(d).tool_name||''); }catch(e){}
|
|
16
|
+
});
|
|
17
|
+
" 2>/dev/null || true)
|
|
18
|
+
|
|
19
|
+
# Extract path tùy theo tool
|
|
20
|
+
if [ "$TOOL_NAME" = "Read" ]; then
|
|
21
|
+
TARGET=$(echo "$INPUT" | node -e "
|
|
22
|
+
let d='';
|
|
23
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
24
|
+
try{ const p=JSON.parse(d); process.stdout.write((p.tool_input&&p.tool_input.file_path)||''); }catch(e){}
|
|
25
|
+
});
|
|
26
|
+
" 2>/dev/null || true)
|
|
27
|
+
elif [ "$TOOL_NAME" = "Glob" ]; then
|
|
28
|
+
TARGET=$(echo "$INPUT" | node -e "
|
|
29
|
+
let d='';
|
|
30
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
31
|
+
try{ const p=JSON.parse(d); const ti=p.tool_input||{}; process.stdout.write(ti.path||ti.pattern||''); }catch(e){}
|
|
32
|
+
});
|
|
33
|
+
" 2>/dev/null || true)
|
|
34
|
+
else
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
[ -z "$TARGET" ] && exit 0
|
|
39
|
+
|
|
40
|
+
# Normalize: lowercase, forward slashes
|
|
41
|
+
NORM=$(echo "$TARGET" | tr '\\' '/')
|
|
42
|
+
|
|
43
|
+
# ── Danh sách heavy/irrelevant directories ─────────────────────────────────────
|
|
44
|
+
BLOCKED_PATTERNS=(
|
|
45
|
+
"node_modules/"
|
|
46
|
+
"/node_modules"
|
|
47
|
+
"dist/"
|
|
48
|
+
"/dist/"
|
|
49
|
+
"build/"
|
|
50
|
+
"/build/"
|
|
51
|
+
".git/"
|
|
52
|
+
"/__pycache__/"
|
|
53
|
+
"/.pytest_cache/"
|
|
54
|
+
"/.next/"
|
|
55
|
+
"/.nuxt/"
|
|
56
|
+
"/vendor/"
|
|
57
|
+
"/coverage/"
|
|
58
|
+
"/.tmp/"
|
|
59
|
+
"/tmp/"
|
|
60
|
+
"/.cache/"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
for pattern in "${BLOCKED_PATTERNS[@]}"; do
|
|
64
|
+
if echo "$NORM" | grep -q "$pattern"; then
|
|
65
|
+
echo "⚡ scout-block: Skipped heavy directory [${TOOL_NAME}] $(basename "$TARGET")/" >&2
|
|
66
|
+
echo " Path: $TARGET" >&2
|
|
67
|
+
echo " Dùng path cụ thể hơn nếu thực sự cần đọc file này." >&2
|
|
68
|
+
exit 2
|
|
69
|
+
fi
|
|
70
|
+
done
|
|
71
|
+
|
|
72
|
+
# Block pattern: kết thúc bằng thư mục blocked (không có trailing slash)
|
|
73
|
+
BLOCKED_EXACT=("node_modules" ".git" "dist" "build" "__pycache__" ".next" ".nuxt" "vendor" "coverage")
|
|
74
|
+
BASENAME=$(basename "$NORM")
|
|
75
|
+
for exact in "${BLOCKED_EXACT[@]}"; do
|
|
76
|
+
if [ "$BASENAME" = "$exact" ]; then
|
|
77
|
+
echo "⚡ scout-block: Skipped heavy directory [${TOOL_NAME}] $BASENAME/" >&2
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
exit 0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# .claude/hooks/session-init.sh — dw-kit v1.2
|
|
3
|
+
# Inject active task context vào đầu session, giải quyết "session amnesia".
|
|
4
|
+
# Chỉ chạy một lần mỗi session (track bằng session_id).
|
|
5
|
+
#
|
|
6
|
+
# UserPromptSubmit hook
|
|
7
|
+
# Stdout output → được inject vào context của user prompt
|
|
8
|
+
# exit 0 = allow
|
|
9
|
+
|
|
10
|
+
INPUT=$(cat)
|
|
11
|
+
|
|
12
|
+
SESSION_ID=$(echo "$INPUT" | node -e "
|
|
13
|
+
let d='';
|
|
14
|
+
process.stdin.on('data',c=>d+=c).on('end',()=>{
|
|
15
|
+
try{ process.stdout.write(JSON.parse(d).session_id||''); }catch(e){}
|
|
16
|
+
});
|
|
17
|
+
" 2>/dev/null || true)
|
|
18
|
+
|
|
19
|
+
[ -z "$SESSION_ID" ] && exit 0
|
|
20
|
+
|
|
21
|
+
# ── Track session: chỉ chạy một lần mỗi session ───────────────────────────────
|
|
22
|
+
SESSION_MARKER="/tmp/dw-session-${SESSION_ID}"
|
|
23
|
+
if [ -f "$SESSION_MARKER" ]; then
|
|
24
|
+
exit 0
|
|
25
|
+
fi
|
|
26
|
+
touch "$SESSION_MARKER" 2>/dev/null || true
|
|
27
|
+
|
|
28
|
+
# ── Scan .dw/tasks/ tìm tasks In Progress ─────────────────────────────────────
|
|
29
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
30
|
+
TASKS_DIR="$PROJECT_DIR/.dw/tasks"
|
|
31
|
+
|
|
32
|
+
[ ! -d "$TASKS_DIR" ] && exit 0
|
|
33
|
+
|
|
34
|
+
ACTIVE_TASKS=()
|
|
35
|
+
ACTIVE_SUMMARIES=()
|
|
36
|
+
|
|
37
|
+
while IFS= read -r progress_file; do
|
|
38
|
+
if grep -q "Trạng thái: In Progress" "$progress_file" 2>/dev/null; then
|
|
39
|
+
task_name=$(basename "$(dirname "$progress_file")")
|
|
40
|
+
|
|
41
|
+
# Extract current subtask (dòng "In Progress" trong table)
|
|
42
|
+
current_st=$(grep -m1 "In Progress" "$progress_file" 2>/dev/null \
|
|
43
|
+
| grep -oP '\| ST-\d+ \| [^|]+' | sed 's/|//g' | xargs 2>/dev/null || echo "")
|
|
44
|
+
|
|
45
|
+
# Extract last handoff note nếu có
|
|
46
|
+
last_handoff=$(awk '/## Handoff Notes/{found=1} found{print}' "$progress_file" 2>/dev/null \
|
|
47
|
+
| grep -m1 "Bước tiếp theo:" | sed 's/.*Bước tiếp theo://' | xargs 2>/dev/null || echo "")
|
|
48
|
+
|
|
49
|
+
ACTIVE_TASKS+=("$task_name")
|
|
50
|
+
summary="$task_name"
|
|
51
|
+
[ -n "$current_st" ] && summary="$summary — $current_st"
|
|
52
|
+
[ -n "$last_handoff" ] && summary="$summary | Next: $last_handoff"
|
|
53
|
+
ACTIVE_SUMMARIES+=("$summary")
|
|
54
|
+
fi
|
|
55
|
+
done < <(find "$TASKS_DIR" -name "*-progress.md" 2>/dev/null)
|
|
56
|
+
|
|
57
|
+
[ ${#ACTIVE_TASKS[@]} -eq 0 ] && exit 0
|
|
58
|
+
|
|
59
|
+
# ── Output context vào stdout (injected vào conversation) ─────────────────────
|
|
60
|
+
echo ""
|
|
61
|
+
echo "---"
|
|
62
|
+
echo "[dw-kit session-init] Task đang in-progress:"
|
|
63
|
+
for summary in "${ACTIVE_SUMMARIES[@]}"; do
|
|
64
|
+
echo " • $summary"
|
|
65
|
+
done
|
|
66
|
+
if [ ${#ACTIVE_TASKS[@]} -eq 1 ]; then
|
|
67
|
+
echo "Context: .dw/tasks/${ACTIVE_TASKS[0]}/"
|
|
68
|
+
else
|
|
69
|
+
echo "Nhiều tasks đang active — hỏi user task nào cần tiếp tục."
|
|
70
|
+
fi
|
|
71
|
+
echo "---"
|
|
72
|
+
echo ""
|
|
73
|
+
|
|
74
|
+
exit 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Stop hook: warn nếu có uncommitted changes hoặc task in-progress chưa update
|
|
3
|
+
# Output ra stderr để hiện thị cho user, exit 0 để không block
|
|
4
|
+
|
|
5
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
|
|
6
|
+
WARNINGS=()
|
|
7
|
+
|
|
8
|
+
# Check uncommitted changes
|
|
9
|
+
if git -C "$PROJECT_DIR" diff --quiet && git -C "$PROJECT_DIR" diff --cached --quiet; then
|
|
10
|
+
: # clean
|
|
11
|
+
else
|
|
12
|
+
CHANGED=$(git -C "$PROJECT_DIR" diff --stat --cached && git -C "$PROJECT_DIR" diff --stat)
|
|
13
|
+
WARNINGS+=("Uncommitted changes:"$'\n'"$CHANGED")
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Check in-progress tasks
|
|
17
|
+
TASKS_DIR="$PROJECT_DIR/.dw/tasks"
|
|
18
|
+
if [ -d "$TASKS_DIR" ]; then
|
|
19
|
+
while IFS= read -r progress_file; do
|
|
20
|
+
if grep -q "Trạng thái: In Progress" "$progress_file" 2>/dev/null; then
|
|
21
|
+
task_name=$(basename "$(dirname "$progress_file")")
|
|
22
|
+
WARNINGS+=("Task in-progress chưa được update: $task_name")
|
|
23
|
+
fi
|
|
24
|
+
done < <(find "$TASKS_DIR" -name "*-progress.md" 2>/dev/null)
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# Print warnings
|
|
28
|
+
if [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
29
|
+
echo "--- dw stop-check ---" >&2
|
|
30
|
+
for w in "${WARNINGS[@]}"; do
|
|
31
|
+
printf "⚠ %b\n" "$w" >&2
|
|
32
|
+
done
|
|
33
|
+
echo "---------------------" >&2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
exit 0
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
# Code Style & Conventions
|
|
2
|
-
|
|
3
|
-
## Nguyên tắc chung
|
|
4
|
-
- Đặt tên biến/hàm rõ ràng, tự giải thích (self-documenting)
|
|
5
|
-
- Ưu tiên đơn giản, dễ đọc hơn clever code
|
|
6
|
-
- Mỗi function làm MỘT việc
|
|
7
|
-
- Comments giải thích WHY, không phải WHAT
|
|
8
|
-
- Xử lý errors ở đầu function (guard clauses / early return)
|
|
9
|
-
|
|
10
|
-
## Naming Conventions
|
|
11
|
-
- Variables/Functions: camelCase
|
|
12
|
-
- Classes/Components: PascalCase
|
|
13
|
-
- Constants: UPPER_SNAKE_CASE
|
|
14
|
-
- Files: kebab-case hoặc theo convention của framework
|
|
15
|
-
- Directories: kebab-case
|
|
16
|
-
|
|
17
|
-
## File Organization
|
|
18
|
-
- 1 component/class per file (trừ khi strongly related)
|
|
19
|
-
- Group imports: external → internal → relative
|
|
20
|
-
- Export ở cuối file hoặc inline (nhất quán trong project)
|
|
21
|
-
|
|
22
|
-
## Error Handling
|
|
23
|
-
- KHÔNG swallow errors (catch rỗng)
|
|
24
|
-
- Log đủ context để debug (error message, input data, stack)
|
|
25
|
-
- Dùng custom error types cho domain errors
|
|
26
|
-
- Validate input ở boundary (API, form, external data)
|
|
27
|
-
|
|
28
|
-
## Testing
|
|
29
|
-
- Test file cùng tên với source: `foo.ts` → `foo.test.ts` hoặc `foo.spec.ts`
|
|
30
|
-
- Mỗi test case kiểm tra MỘT behavior
|
|
31
|
-
- Test name mô tả expected behavior: "should return error when input is empty"
|
|
32
|
-
- Arrange → Act → Assert pattern
|
|
33
|
-
- KHÔNG mock internal implementation details
|
|
34
|
-
|
|
35
|
-
## NOTE
|
|
36
|
-
Đây là quy tắc mặc định. Team tùy chỉnh theo stack cụ thể của dự án.
|
|
37
|
-
Thêm framework-specific rules vào file này hoặc tạo file riêng trong `.claude/rules/`.
|
|
1
|
+
# Code Style & Conventions
|
|
2
|
+
|
|
3
|
+
## Nguyên tắc chung
|
|
4
|
+
- Đặt tên biến/hàm rõ ràng, tự giải thích (self-documenting)
|
|
5
|
+
- Ưu tiên đơn giản, dễ đọc hơn clever code
|
|
6
|
+
- Mỗi function làm MỘT việc
|
|
7
|
+
- Comments giải thích WHY, không phải WHAT
|
|
8
|
+
- Xử lý errors ở đầu function (guard clauses / early return)
|
|
9
|
+
|
|
10
|
+
## Naming Conventions
|
|
11
|
+
- Variables/Functions: camelCase
|
|
12
|
+
- Classes/Components: PascalCase
|
|
13
|
+
- Constants: UPPER_SNAKE_CASE
|
|
14
|
+
- Files: kebab-case hoặc theo convention của framework
|
|
15
|
+
- Directories: kebab-case
|
|
16
|
+
|
|
17
|
+
## File Organization
|
|
18
|
+
- 1 component/class per file (trừ khi strongly related)
|
|
19
|
+
- Group imports: external → internal → relative
|
|
20
|
+
- Export ở cuối file hoặc inline (nhất quán trong project)
|
|
21
|
+
|
|
22
|
+
## Error Handling
|
|
23
|
+
- KHÔNG swallow errors (catch rỗng)
|
|
24
|
+
- Log đủ context để debug (error message, input data, stack)
|
|
25
|
+
- Dùng custom error types cho domain errors
|
|
26
|
+
- Validate input ở boundary (API, form, external data)
|
|
27
|
+
|
|
28
|
+
## Testing
|
|
29
|
+
- Test file cùng tên với source: `foo.ts` → `foo.test.ts` hoặc `foo.spec.ts`
|
|
30
|
+
- Mỗi test case kiểm tra MỘT behavior
|
|
31
|
+
- Test name mô tả expected behavior: "should return error when input is empty"
|
|
32
|
+
- Arrange → Act → Assert pattern
|
|
33
|
+
- KHÔNG mock internal implementation details
|
|
34
|
+
|
|
35
|
+
## NOTE
|
|
36
|
+
Đây là quy tắc mặc định. Team tùy chỉnh theo stack cụ thể của dự án.
|
|
37
|
+
Thêm framework-specific rules vào file này hoặc tạo file riêng trong `.claude/rules/`.
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
# Commit Standards
|
|
2
|
-
|
|
3
|
-
## Format
|
|
4
|
-
```
|
|
5
|
-
<type>(<scope>): <mô tả tiếng Việt hoặc tiếng Anh>
|
|
6
|
-
|
|
7
|
-
[Body - chi tiết thay đổi, lý do]
|
|
8
|
-
[Blank line]
|
|
9
|
-
[Footer - breaking changes, references]
|
|
10
|
-
|
|
11
|
-
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Types
|
|
15
|
-
| Type | Khi nào dùng |
|
|
16
|
-
|------|-------------|
|
|
17
|
-
| `feat` | Tính năng mới |
|
|
18
|
-
| `fix` | Sửa lỗi |
|
|
19
|
-
| `refactor` | Tái cấu trúc, không thay đổi behavior |
|
|
20
|
-
| `test` | Thêm/sửa tests |
|
|
21
|
-
| `docs` | Tài liệu, comments |
|
|
22
|
-
| `chore` | Build, config, dependencies |
|
|
23
|
-
| `style` | Format, whitespace (không thay đổi logic) |
|
|
24
|
-
| `perf` | Cải thiện performance |
|
|
25
|
-
|
|
26
|
-
## Quy tắc
|
|
27
|
-
- Mỗi commit = 1 subtask hoặc 1 đơn vị logic hoàn chỉnh
|
|
28
|
-
- Mô tả ngắn <= 72 ký tự
|
|
29
|
-
- Dùng thì hiện tại: "thêm", "sửa", "cập nhật" (không phải "đã thêm")
|
|
30
|
-
- KHÔNG commit files chứa secrets (.env, credentials, tokens)
|
|
31
|
-
- KHÔNG commit console.log/debugger còn sót
|
|
32
|
-
|
|
33
|
-
## Branch Naming
|
|
34
|
-
```
|
|
35
|
-
<type>/<task-name>
|
|
36
|
-
```
|
|
37
|
-
Ví dụ: `feat/user-auth`, `fix/login-redirect`, `refactor/api-structure`
|
|
1
|
+
# Commit Standards
|
|
2
|
+
|
|
3
|
+
## Format
|
|
4
|
+
```
|
|
5
|
+
<type>(<scope>): <mô tả tiếng Việt hoặc tiếng Anh>
|
|
6
|
+
|
|
7
|
+
[Body - chi tiết thay đổi, lý do]
|
|
8
|
+
[Blank line]
|
|
9
|
+
[Footer - breaking changes, references]
|
|
10
|
+
|
|
11
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Types
|
|
15
|
+
| Type | Khi nào dùng |
|
|
16
|
+
|------|-------------|
|
|
17
|
+
| `feat` | Tính năng mới |
|
|
18
|
+
| `fix` | Sửa lỗi |
|
|
19
|
+
| `refactor` | Tái cấu trúc, không thay đổi behavior |
|
|
20
|
+
| `test` | Thêm/sửa tests |
|
|
21
|
+
| `docs` | Tài liệu, comments |
|
|
22
|
+
| `chore` | Build, config, dependencies |
|
|
23
|
+
| `style` | Format, whitespace (không thay đổi logic) |
|
|
24
|
+
| `perf` | Cải thiện performance |
|
|
25
|
+
|
|
26
|
+
## Quy tắc
|
|
27
|
+
- Mỗi commit = 1 subtask hoặc 1 đơn vị logic hoàn chỉnh
|
|
28
|
+
- Mô tả ngắn <= 72 ký tự
|
|
29
|
+
- Dùng thì hiện tại: "thêm", "sửa", "cập nhật" (không phải "đã thêm")
|
|
30
|
+
- KHÔNG commit files chứa secrets (.env, credentials, tokens)
|
|
31
|
+
- KHÔNG commit console.log/debugger còn sót
|
|
32
|
+
|
|
33
|
+
## Branch Naming
|
|
34
|
+
```
|
|
35
|
+
<type>/<task-name>
|
|
36
|
+
```
|
|
37
|
+
Ví dụ: `feat/user-auth`, `fix/login-redirect`, `refactor/api-structure`
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<!-- dw-kit | core: 1.2 -->
|
|
2
|
+
# dw Workflow
|
|
3
|
+
|
|
4
|
+
Config: `.dw/config/dw.config.yml`
|
|
5
|
+
Full methodology: `.dw/core/WORKFLOW.md` · `.dw/core/THINKING.md` · `.dw/core/QUALITY.md` · `.dw/core/ROLES.md`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Override
|
|
10
|
+
|
|
11
|
+
If the prompt contains `--no-dw`: ignore all dw instructions, work as plain Claude Code. Applies only to that request.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Depth Routing
|
|
16
|
+
|
|
17
|
+
Read `workflow.default_depth` from config. Assess per task based on facts (file count, API changes, git blame) — not assumptions:
|
|
18
|
+
|
|
19
|
+
| Scope | Depth | Approach |
|
|
20
|
+
|-------|-------|----------|
|
|
21
|
+
| ≤2 files, hotfix, familiar module | quick | Understand → Execute → Close |
|
|
22
|
+
| 3–5 files, new module | standard | Full 6-phase |
|
|
23
|
+
| 6+ files, API/DB/security changes | thorough | Full + arch-review + test-plan |
|
|
24
|
+
|
|
25
|
+
When unsure → default to `standard`.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Session Start
|
|
30
|
+
|
|
31
|
+
1. Read `.dw/config/dw.config.yml` — depth, roles, quality commands
|
|
32
|
+
2. Check `.dw/tasks/` for active tasks; if one is in progress, resume from its `[task]-progress.md`
|
|
33
|
+
|
|
34
|
+
> `session-init` hook auto-injects active task context — no manual action needed.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Task Docs
|
|
39
|
+
|
|
40
|
+
For tasks spanning 3+ files, research → plan → approve → execute gives better outcomes.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
.dw/tasks/[task-name]/
|
|
44
|
+
├── [name]-context.md # Research findings
|
|
45
|
+
├── [name]-plan.md # Implementation plan
|
|
46
|
+
└── [name]-progress.md # Progress + handoff notes
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Commit Format
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
<type>(<scope>): <description ≤72 chars>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Types: `feat` `fix` `refactor` `test` `docs` `chore` `style` `perf`
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Hooks (v1.2)
|
|
62
|
+
|
|
63
|
+
Auto-run — no user action needed:
|
|
64
|
+
|
|
65
|
+
| Hook | Trigger | Effect |
|
|
66
|
+
|------|---------|--------|
|
|
67
|
+
| `session-init` | Session start | Inject active task context |
|
|
68
|
+
| `scout-block` | Read/Glob | Block node_modules/, dist/, .git/ etc. |
|
|
69
|
+
| `privacy-block` | Read | Block .env*, *.pem, credentials* |
|
|
70
|
+
| `pre-commit-gate` | Bash (git commit) | Quality check + sensitive data scan |
|
|
71
|
+
| `safety-guard` | Bash | Block destructive commands |
|
|
72
|
+
| `post-write` | Write/Edit | Lint reminder |
|
|
73
|
+
| `stop-check` | Stop | Warn on uncommitted changes + in-progress tasks |
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Agent Reports (v1.2)
|
|
78
|
+
|
|
79
|
+
For multi-phase tasks (standard/thorough), create reports for audit trail:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
.dw/tasks/[task-name]/reports/[YYMMDD-HHMM]-from-[role]-to-[role]-[desc].md
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Status: `DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT`
|
|
86
|
+
Template: `.claude/templates/agent-report.md` · Guide: `.dw/core/AGENTS.md`
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Config Local Override (v1.2)
|
|
91
|
+
|
|
92
|
+
`.dw/config/dw.config.local.yml` (gitignored) for machine-specific settings:
|
|
93
|
+
|
|
94
|
+
```yaml
|
|
95
|
+
claude:
|
|
96
|
+
models:
|
|
97
|
+
plan: "claude-opus-4-6"
|
|
98
|
+
quality:
|
|
99
|
+
test_command: "npm test"
|
|
100
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<!-- dw-kit | core: 1.2 -->
|
|
2
|
+
# dw Skills
|
|
3
|
+
|
|
4
|
+
Invoke via Claude Code slash commands. Availability governed by config flags and `workflow.default_depth`.
|
|
5
|
+
|
|
6
|
+
## Core Workflow
|
|
7
|
+
|
|
8
|
+
| Skill | Description | Depth |
|
|
9
|
+
|-------|-------------|-------|
|
|
10
|
+
| `/dw-flow [task]` | Full workflow shortcut | all |
|
|
11
|
+
| `/dw-task-init [name]` | Init task docs | all |
|
|
12
|
+
| `/dw-research [name]` | Survey codebase | all |
|
|
13
|
+
| `/dw-plan [name]` | Design plan (waits for approval) | standard+ |
|
|
14
|
+
| `/dw-execute [name]` | Implement per plan (TDD) | all |
|
|
15
|
+
| `/dw-commit [msg]` | Smart commit + quality gates | all |
|
|
16
|
+
| `/dw-handoff` | Session handoff | all |
|
|
17
|
+
|
|
18
|
+
## Dev
|
|
19
|
+
|
|
20
|
+
| Skill | Description |
|
|
21
|
+
|-------|-------------|
|
|
22
|
+
| `/dw-debug [issue]` | Investigate → diagnose → fix |
|
|
23
|
+
| `/dw-review` | Code review with checklist |
|
|
24
|
+
| `/dw-thinking [question]` | Apply thinking framework |
|
|
25
|
+
| `/dw-prompt [desc]` | Build structured prompt |
|
|
26
|
+
| `/dw-docs-update` | Update living docs |
|
|
27
|
+
|
|
28
|
+
## Role-Specific
|
|
29
|
+
|
|
30
|
+
| Skill | Role | Description |
|
|
31
|
+
|-------|------|-------------|
|
|
32
|
+
| `/dw-requirements` | BA | Requirements + user stories |
|
|
33
|
+
| `/dw-test-plan` | QC | Test plan + regression |
|
|
34
|
+
| `/dw-arch-review` | TechLead | Architecture review |
|
|
35
|
+
| `/dw-dashboard` | PM | Metrics report |
|
|
36
|
+
| `/dw-sprint-review` | All | Retrospective |
|
|
37
|
+
| `/dw-estimate [name]` | if enabled | Effort estimation |
|
|
38
|
+
| `/dw-log-work [name]` | if enabled | Log actual effort |
|
|
39
|
+
|
|
40
|
+
## Setup & Maintenance
|
|
41
|
+
|
|
42
|
+
| Skill | Description |
|
|
43
|
+
|-------|-------------|
|
|
44
|
+
| `/dw-onboard` | Onboard dw to existing project (breadth-first scan) |
|
|
45
|
+
| `/dw-retroactive [name]` | Document existing feature (depth-first) |
|
|
46
|
+
| `/dw-config-init` | Initialize new config |
|
|
47
|
+
| `/dw-config-validate` | Validate config file |
|
|
48
|
+
| `/dw-upgrade` | Upgrade toolkit |
|
|
49
|
+
| `/dw-rollback [name]` | Rollback task docs |
|
|
50
|
+
| `/dw-archive [name]` | Archive completed task |
|
|
51
|
+
| `/dw-kit-report [desc]` | Submit feedback/bug to GitHub |
|
|
52
|
+
|
|
53
|
+
> **Maintainer-only** (TechLead, dw-kit repo): `/dw-kit-evolve [issue#]` · `/dw-kit-audit [days]`
|
package/.claude/settings.json
CHANGED
|
@@ -16,7 +16,35 @@
|
|
|
16
16
|
},
|
|
17
17
|
"mcpServers": {},
|
|
18
18
|
"hooks": {
|
|
19
|
+
"UserPromptSubmit": [
|
|
20
|
+
{
|
|
21
|
+
"hooks": [
|
|
22
|
+
{
|
|
23
|
+
"type": "command",
|
|
24
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-init.sh\""
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
],
|
|
19
29
|
"PreToolUse": [
|
|
30
|
+
{
|
|
31
|
+
"matcher": "Read|Glob",
|
|
32
|
+
"hooks": [
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/scout-block.sh\""
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"matcher": "Read",
|
|
41
|
+
"hooks": [
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/privacy-block.sh\""
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
},
|
|
20
48
|
{
|
|
21
49
|
"matcher": "Bash",
|
|
22
50
|
"hooks": [
|
|
@@ -51,8 +79,8 @@
|
|
|
51
79
|
{
|
|
52
80
|
"hooks": [
|
|
53
81
|
{
|
|
54
|
-
"type": "
|
|
55
|
-
"
|
|
82
|
+
"type": "command",
|
|
83
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/stop-check.sh\""
|
|
56
84
|
}
|
|
57
85
|
]
|
|
58
86
|
}
|