dw-kit 1.2.1 → 1.3.4

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 (59) hide show
  1. package/.claude/hooks/post-write.sh +64 -58
  2. package/.claude/hooks/pre-commit-gate.sh +96 -90
  3. package/.claude/hooks/privacy-block.sh +99 -94
  4. package/.claude/hooks/progress-ping.sh +53 -47
  5. package/.claude/hooks/safety-guard.sh +60 -54
  6. package/.claude/hooks/scout-block.sh +88 -82
  7. package/.claude/hooks/session-init.sh +6 -0
  8. package/.claude/hooks/stop-check.sh +88 -36
  9. package/.claude/hooks/telemetry-log.sh +34 -0
  10. package/.claude/rules/dw.md +138 -0
  11. package/.claude/settings.json +28 -1
  12. package/.claude/skills/dw-arch-review/SKILL.md +119 -119
  13. package/.claude/skills/dw-archive/SKILL.md +81 -81
  14. package/.claude/skills/dw-commit/SKILL.md +81 -81
  15. package/.claude/skills/dw-config-init/SKILL.md +91 -91
  16. package/.claude/skills/dw-config-validate/SKILL.md +75 -75
  17. package/.claude/skills/dw-dashboard/SKILL.md +209 -209
  18. package/.claude/skills/dw-debug/SKILL.md +97 -97
  19. package/.claude/skills/dw-decision/SKILL.md +116 -0
  20. package/.claude/skills/dw-docs-update/SKILL.md +125 -125
  21. package/.claude/skills/dw-estimate/SKILL.md +90 -90
  22. package/.claude/skills/dw-execute/SKILL.md +121 -98
  23. package/.claude/skills/dw-flow/SKILL.md +274 -274
  24. package/.claude/skills/dw-handoff/SKILL.md +92 -81
  25. package/.claude/skills/dw-kit-report/SKILL.md +152 -152
  26. package/.claude/skills/dw-log-work/SKILL.md +69 -69
  27. package/.claude/skills/dw-onboard/SKILL.md +201 -201
  28. package/.claude/skills/dw-plan/SKILL.md +222 -125
  29. package/.claude/skills/dw-prompt/SKILL.md +62 -62
  30. package/.claude/skills/dw-requirements/SKILL.md +98 -98
  31. package/.claude/skills/dw-research/SKILL.md +128 -114
  32. package/.claude/skills/dw-retroactive/SKILL.md +195 -311
  33. package/.claude/skills/dw-review/SKILL.md +66 -66
  34. package/.claude/skills/dw-rollback/SKILL.md +90 -90
  35. package/.claude/skills/dw-sprint-review/SKILL.md +99 -99
  36. package/.claude/skills/dw-task-init/SKILL.md +71 -59
  37. package/.claude/skills/dw-test-plan/SKILL.md +113 -113
  38. package/.claude/skills/dw-thinking/SKILL.md +70 -70
  39. package/.claude/skills/dw-upgrade/SKILL.md +72 -72
  40. package/.dw/core/PILLARS.md +122 -0
  41. package/.dw/core/ROLES.md +257 -257
  42. package/.dw/core/templates/v2/spec.md +68 -0
  43. package/.dw/core/templates/v2/tracking.md +62 -0
  44. package/.dw/core/v14-evaluation-protocol.md +118 -0
  45. package/CLAUDE.md +42 -39
  46. package/MIGRATION-v1.3.md +202 -0
  47. package/README.md +35 -6
  48. package/package.json +4 -2
  49. package/src/cli.mjs +29 -1
  50. package/src/commands/dashboard.mjs +116 -0
  51. package/src/commands/doctor.mjs +165 -149
  52. package/src/commands/init.mjs +339 -332
  53. package/src/commands/metrics.mjs +185 -0
  54. package/src/lib/active-index.mjs +87 -0
  55. package/src/lib/cut-analysis.mjs +240 -0
  56. package/src/lib/telemetry.mjs +80 -0
  57. package/.claude/rules/dw-core.md +0 -100
  58. package/.claude/rules/dw-skills.md +0 -53
  59. package/.claude/rules/workflow-rules.md +0 -77
@@ -1,58 +1,64 @@
1
- #!/bin/bash
2
- # .claude/hooks/post-write.sh
3
- # Chạy lint trên file vừa được Write/Edit — non-blocking.
4
- # Được gọi bởi PostToolUse hook sau Write và Edit.
5
-
6
- INPUT=$(cat)
7
-
8
- # Extract file path từ tool result
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
- });
18
- " 2>/dev/null || true)
19
-
20
- [ -z "$FILE_PATH" ] && exit 0
21
-
22
- # ── Đọc lint command từ config ────────────────────────────────────────────────
23
- CONFIG_FILE="${CLAUDE_PROJECT_DIR:-$PWD}/.dw/config/dw.config.yml"
24
- [ ! -f "$CONFIG_FILE" ] && exit 0
25
-
26
- LINT_CMD=$(grep -m1 "lint_command:" "$CONFIG_FILE" 2>/dev/null \
27
- | sed 's/.*:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]' || true)
28
-
29
- [ -z "$LINT_CMD" ] || [ "$LINT_CMD" = "" ] && exit 0
30
-
31
- # ── Kiểm tra file có phải source code không ──────────────────────────────────
32
- is_source_file() {
33
- local f="$1"
34
- echo "$f" | grep -qE '\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|vue|svelte|css|scss)$'
35
- }
36
-
37
- is_source_file "$FILE_PATH" || exit 0
38
-
39
- # ── Chạy lint trên file (non-blocking) ───────────────────────────────────────
40
- # Thử chạy lint chỉ trên file vừa thay đổi nếu tool hỗ trợ
41
- if echo "$LINT_CMD" | grep -q "eslint"; then
42
- RESULT=$(eval "$LINT_CMD '$FILE_PATH'" 2>&1 || true)
43
- elif echo "$LINT_CMD" | grep -q "ruff"; then
44
- RESULT=$(eval "ruff check '$FILE_PATH'" 2>&1 || true)
45
- elif echo "$LINT_CMD" | grep -q "pylint"; then
46
- RESULT=$(eval "pylint '$FILE_PATH'" 2>&1 || true)
47
- else
48
- # Generic: chạy toàn bộ lint command
49
- RESULT=$(eval "$LINT_CMD" 2>&1 || true)
50
- fi
51
-
52
- if [ -n "$RESULT" ]; then
53
- echo "⚠ Lint warnings sau khi write $FILE_PATH:" >&2
54
- echo "$RESULT" | head -20 >&2
55
- echo " (non-blocking kiểm tra và fix nếu cần)" >&2
56
- fi
57
-
58
- exit 0
1
+ #!/bin/bash
2
+ # .claude/hooks/post-write.sh
3
+ # Chạy lint trên file vừa được Write/Edit — non-blocking.
4
+ # Được gọi bởi PostToolUse hook sau Write và Edit.
5
+
6
+ # Telemetry (local, fire-and-forget)
7
+ TELEMETRY_SCRIPT="${CLAUDE_PROJECT_DIR:-$(pwd)}/.claude/hooks/telemetry-log.sh"
8
+ if [ -x "$TELEMETRY_SCRIPT" ] && [ "${DW_NO_TELEMETRY:-}" != "1" ]; then
9
+ "$TELEMETRY_SCRIPT" hook post-write >/dev/null 2>&1 || true
10
+ fi
11
+
12
+ INPUT=$(cat)
13
+
14
+ # Extract file path từ tool result
15
+ FILE_PATH=$(echo "$INPUT" | node -e "
16
+ let d='';
17
+ process.stdin.on('data',c=>d+=c).on('end',()=>{
18
+ try{
19
+ const data=JSON.parse(d);
20
+ const p=data.file_path||data.path||data.filePath||(data.tool_input&&(data.tool_input.file_path||data.tool_input.path))||'';
21
+ process.stdout.write(p);
22
+ }catch(e){}
23
+ });
24
+ " 2>/dev/null || true)
25
+
26
+ [ -z "$FILE_PATH" ] && exit 0
27
+
28
+ # ── Đọc lint command từ config ────────────────────────────────────────────────
29
+ CONFIG_FILE="${CLAUDE_PROJECT_DIR:-$PWD}/.dw/config/dw.config.yml"
30
+ [ ! -f "$CONFIG_FILE" ] && exit 0
31
+
32
+ LINT_CMD=$(grep -m1 "lint_command:" "$CONFIG_FILE" 2>/dev/null \
33
+ | sed 's/.*:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]' || true)
34
+
35
+ [ -z "$LINT_CMD" ] || [ "$LINT_CMD" = "" ] && exit 0
36
+
37
+ # ── Kiểm tra file có phải source code không ──────────────────────────────────
38
+ is_source_file() {
39
+ local f="$1"
40
+ echo "$f" | grep -qE '\.(ts|tsx|js|jsx|py|go|rs|java|rb|php|vue|svelte|css|scss)$'
41
+ }
42
+
43
+ is_source_file "$FILE_PATH" || exit 0
44
+
45
+ # ── Chạy lint trên file (non-blocking) ───────────────────────────────────────
46
+ # Thử chạy lint chỉ trên file vừa thay đổi nếu tool hỗ trợ
47
+ if echo "$LINT_CMD" | grep -q "eslint"; then
48
+ RESULT=$(eval "$LINT_CMD '$FILE_PATH'" 2>&1 || true)
49
+ elif echo "$LINT_CMD" | grep -q "ruff"; then
50
+ RESULT=$(eval "ruff check '$FILE_PATH'" 2>&1 || true)
51
+ elif echo "$LINT_CMD" | grep -q "pylint"; then
52
+ RESULT=$(eval "pylint '$FILE_PATH'" 2>&1 || true)
53
+ else
54
+ # Generic: chạy toàn bộ lint command
55
+ RESULT=$(eval "$LINT_CMD" 2>&1 || true)
56
+ fi
57
+
58
+ if [ -n "$RESULT" ]; then
59
+ echo "⚠ Lint warnings sau khi write $FILE_PATH:" >&2
60
+ echo "$RESULT" | head -20 >&2
61
+ echo " (non-blocking — kiểm tra và fix nếu cần)" >&2
62
+ fi
63
+
64
+ exit 0
@@ -1,90 +1,96 @@
1
- #!/bin/bash
2
- # .claude/hooks/pre-commit-gate.sh
3
- # Quality gate: chạy trước mỗi Bash tool call.
4
- # Intercepts `git commit` để kiểm tra quality config (v1 schema).
5
- # exit 0 = allow (có thể warn), exit 2 = block
6
-
7
- INPUT=$(cat)
8
-
9
- # Extract command từ JSON input — pure grep/sed, no Python needed
10
- COMMAND=$(echo "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"command"[[:space:]]*:[[:space:]]*"//;s/"$//' | head -1)
11
-
12
- # Chỉ xử lý git commit commands
13
- if ! echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
14
- exit 0
15
- fi
16
-
17
- # Đọc config
18
- CONFIG_FILE="$CLAUDE_PROJECT_DIR/.dw/config/dw.config.yml"
19
- if [ ! -f "$CONFIG_FILE" ]; then
20
- exit 0
21
- fi
22
-
23
- # Parse values từ YAML — pure grep/sed, no Python needed
24
- get_value() {
25
- local key="$1"
26
- grep -m1 "^[[:space:]]*${key}:" "$CONFIG_FILE" 2>/dev/null \
27
- | sed 's/.*:[[:space:]]*//' \
28
- | sed 's/[[:space:]]*#.*//' \
29
- | tr -d '"'"'"' \
30
- | tr '[:upper:]' '[:lower:]' \
31
- | tr -d '[:space:]' \
32
- || echo ""
33
- }
34
-
35
- BLOCK_ON_FAIL=$(get_value "block_on_fail")
36
- TEST_COMMAND=$(get_value "test_command")
37
- LINT_COMMAND=$(get_value "lint_command")
38
- HAS_TESTS=false
39
- HAS_LINT=false
40
- [ -n "$TEST_COMMAND" ] && HAS_TESTS=true
41
- [ -n "$LINT_COMMAND" ] && HAS_LINT=true
42
-
43
- # Nếu không cấu hình test/lint command → allow (chỉ chạy safety checks)
44
- if [ "$HAS_TESTS" = false ] && [ "$HAS_LINT" = false ]; then
45
- exit 0
46
- fi
47
-
48
- # Thông báo quality gate đang check
49
- echo "⚙️ dw-kit quality gate đang kiểm tra..." >&2
50
-
51
- ISSUES=0
52
-
53
- # Check: có debug code không?
54
- STAGED_FILES=$(git diff --cached --name-only 2>/dev/null)
55
- if [ -n "$STAGED_FILES" ]; then
56
- DEBUG_FOUND=$(git diff --cached 2>/dev/null | grep "^+" | grep -E "console\.log\(|debugger|var_dump\(|dd\(|pdb\.set_trace" | grep -v "^+++" | head -5)
57
- if [ -n "$DEBUG_FOUND" ]; then
58
- echo "⚠️ Warning: Phát hiện debug code còn sót:" >&2
59
- echo "$DEBUG_FOUND" >&2
60
- ISSUES=$((ISSUES + 1))
61
- fi
62
- fi
63
-
64
- # Check: sensitive patterns?
65
- SENSITIVE=$(git diff --cached 2>/dev/null | grep "^+" | grep -iE "(password|secret|api_key|private_key)\s*=\s*['\"][^'\"]{8,}" | grep -v "^+++" | head -3)
66
- if [ -n "$SENSITIVE" ]; then
67
- echo "🚨 CẢNH BÁO: Có thể có sensitive data trong commit!" >&2
68
- echo "$SENSITIVE" >&2
69
- if [ "$BLOCK_ON_FAIL" = "true" ]; then
70
- echo "Commit bị block. Kiểm tra lại staged files." >&2
71
- exit 2
72
- fi
73
- fi
74
-
75
- # Reminder về tests/lint theo config v1
76
- if [ "$HAS_TESTS" = true ]; then
77
- echo "📋 Reminder: Hãy đảm bảo tests đã pass trước khi commit." >&2
78
- echo " Test command: $TEST_COMMAND" >&2
79
- fi
80
-
81
- if [ "$HAS_LINT" = true ]; then
82
- echo "📋 Reminder: Hãy đảm bảo lint đã pass trước khi commit." >&2
83
- echo " Lint command: $LINT_COMMAND" >&2
84
- fi
85
-
86
- if [ "$ISSUES" -eq 0 ]; then
87
- echo " Quality gate: OK" >&2
88
- fi
89
-
90
- exit 0
1
+ #!/bin/bash
2
+ # .claude/hooks/pre-commit-gate.sh
3
+ # Quality gate: chạy trước mỗi Bash tool call.
4
+ # Intercepts `git commit` để kiểm tra quality config (v1 schema).
5
+ # exit 0 = allow (có thể warn), exit 2 = block
6
+
7
+ INPUT=$(cat)
8
+
9
+ # Extract command từ JSON input — pure grep/sed, no Python needed
10
+ COMMAND=$(echo "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"command"[[:space:]]*:[[:space:]]*"//;s/"$//' | head -1)
11
+
12
+ # Chỉ xử lý git commit commands
13
+ if ! echo "$COMMAND" | grep -qE '^\s*git\s+commit'; then
14
+ exit 0
15
+ fi
16
+
17
+ # Telemetry (local, fire-and-forget)
18
+ TELEMETRY_SCRIPT="${CLAUDE_PROJECT_DIR:-$(pwd)}/.claude/hooks/telemetry-log.sh"
19
+ if [ -x "$TELEMETRY_SCRIPT" ] && [ "${DW_NO_TELEMETRY:-}" != "1" ]; then
20
+ "$TELEMETRY_SCRIPT" hook pre-commit-gate >/dev/null 2>&1 || true
21
+ fi
22
+
23
+ # Đọc config
24
+ CONFIG_FILE="$CLAUDE_PROJECT_DIR/.dw/config/dw.config.yml"
25
+ if [ ! -f "$CONFIG_FILE" ]; then
26
+ exit 0
27
+ fi
28
+
29
+ # Parse values từ YAML — pure grep/sed, no Python needed
30
+ get_value() {
31
+ local key="$1"
32
+ grep -m1 "^[[:space:]]*${key}:" "$CONFIG_FILE" 2>/dev/null \
33
+ | sed 's/.*:[[:space:]]*//' \
34
+ | sed 's/[[:space:]]*#.*//' \
35
+ | sed 's/["'\'']//g' \
36
+ | tr '[:upper:]' '[:lower:]' \
37
+ | tr -d '[:space:]' \
38
+ || echo ""
39
+ }
40
+
41
+ BLOCK_ON_FAIL=$(get_value "block_on_fail")
42
+ TEST_COMMAND=$(get_value "test_command")
43
+ LINT_COMMAND=$(get_value "lint_command")
44
+ HAS_TESTS=false
45
+ HAS_LINT=false
46
+ [ -n "$TEST_COMMAND" ] && HAS_TESTS=true
47
+ [ -n "$LINT_COMMAND" ] && HAS_LINT=true
48
+
49
+ # Nếu không cấu hình test/lint command → allow (chỉ chạy safety checks)
50
+ if [ "$HAS_TESTS" = false ] && [ "$HAS_LINT" = false ]; then
51
+ exit 0
52
+ fi
53
+
54
+ # Thông báo quality gate đang check
55
+ echo "⚙️ dw-kit quality gate đang kiểm tra..." >&2
56
+
57
+ ISSUES=0
58
+
59
+ # Check: có debug code không?
60
+ STAGED_FILES=$(git diff --cached --name-only 2>/dev/null)
61
+ if [ -n "$STAGED_FILES" ]; then
62
+ DEBUG_FOUND=$(git diff --cached 2>/dev/null | grep "^+" | grep -E "console\.log\(|debugger|var_dump\(|dd\(|pdb\.set_trace" | grep -v "^+++" | head -5)
63
+ if [ -n "$DEBUG_FOUND" ]; then
64
+ echo "⚠️ Warning: Phát hiện debug code còn sót:" >&2
65
+ echo "$DEBUG_FOUND" >&2
66
+ ISSUES=$((ISSUES + 1))
67
+ fi
68
+ fi
69
+
70
+ # Check: sensitive patterns?
71
+ SENSITIVE=$(git diff --cached 2>/dev/null | grep "^+" | grep -iE '(password|secret|api_key|private_key)[[:space:]]*=[[:space:]]*.{8,}' | grep -v "^+++" | head -3)
72
+ if [ -n "$SENSITIVE" ]; then
73
+ echo "🚨 CẢNH BÁO: Có thể có sensitive data trong commit!" >&2
74
+ echo "$SENSITIVE" >&2
75
+ if [ "$BLOCK_ON_FAIL" = "true" ]; then
76
+ echo "Commit bị block. Kiểm tra lại staged files." >&2
77
+ exit 2
78
+ fi
79
+ fi
80
+
81
+ # Reminder về tests/lint theo config v1
82
+ if [ "$HAS_TESTS" = true ]; then
83
+ echo "📋 Reminder: Hãy đảm bảo tests đã pass trước khi commit." >&2
84
+ echo " Test command: $TEST_COMMAND" >&2
85
+ fi
86
+
87
+ if [ "$HAS_LINT" = true ]; then
88
+ echo "📋 Reminder: Hãy đảm bảo lint đã pass trước khi commit." >&2
89
+ echo " Lint command: $LINT_COMMAND" >&2
90
+ fi
91
+
92
+ if [ "$ISSUES" -eq 0 ]; then
93
+ echo "✅ Quality gate: OK" >&2
94
+ fi
95
+
96
+ exit 0
@@ -1,94 +1,99 @@
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 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
1
+ #!/usr/bin/env bash
2
+ # .claude/hooks/privacy-block.sh — dw-kit v1.3 (Guards pillar)
3
+ # Block agent reads vào sensitive files (credentials, secrets, private keys).
4
+ #
5
+ # PreToolUse hook cho: Read
6
+ # exit 0 = allow, exit 2 = block
7
+
8
+ # Telemetry (local, fire-and-forget) — log every check
9
+ TELEMETRY_SCRIPT="${CLAUDE_PROJECT_DIR:-$(pwd)}/.claude/hooks/telemetry-log.sh"
10
+ if [ -x "$TELEMETRY_SCRIPT" ] && [ "${DW_NO_TELEMETRY:-}" != "1" ]; then
11
+ "$TELEMETRY_SCRIPT" hook privacy-block >/dev/null 2>&1 || true
12
+ fi
13
+
14
+ INPUT=$(cat)
15
+
16
+ TOOL_NAME=$(echo "$INPUT" | node -e "
17
+ let d='';
18
+ process.stdin.on('data',c=>d+=c).on('end',()=>{
19
+ try{ process.stdout.write(JSON.parse(d).tool_name||''); }catch(e){}
20
+ });
21
+ " 2>/dev/null || true)
22
+
23
+ [ "$TOOL_NAME" != "Read" ] && exit 0
24
+
25
+ FILE_PATH=$(echo "$INPUT" | node -e "
26
+ let d='';
27
+ process.stdin.on('data',c=>d+=c).on('end',()=>{
28
+ try{ const p=JSON.parse(d); process.stdout.write((p.tool_input&&p.tool_input.file_path)||''); }catch(e){}
29
+ });
30
+ " 2>/dev/null || true)
31
+
32
+ [ -z "$FILE_PATH" ] && exit 0
33
+
34
+ BASENAME=$(basename "$FILE_PATH")
35
+ NORM=$(echo "$FILE_PATH" | tr '\\' '/')
36
+
37
+ # ── Allow-list: safe files dù tên giống sensitive ─────────────────────────────
38
+ ALLOWED_PATTERNS=(
39
+ ".env.example"
40
+ ".env.sample"
41
+ ".env.template"
42
+ ".env.test"
43
+ ".env.local.example"
44
+ "*.example"
45
+ "*.sample"
46
+ )
47
+
48
+ for allow in "${ALLOWED_PATTERNS[@]}"; do
49
+ if [[ "$BASENAME" == $allow ]]; then
50
+ exit 0
51
+ fi
52
+ done
53
+
54
+ # ── Block patterns ──────────────────────────────────────────────────────────────
55
+ BLOCKED=false
56
+ REASON=""
57
+
58
+ # .env files (nhưng không phải .env.example đã allow ở trên)
59
+ if [[ "$BASENAME" == ".env" ]] || [[ "$BASENAME" == .env.* ]]; then
60
+ BLOCKED=true
61
+ REASON="Environment file (có thể chứa secrets/API keys)"
62
+ fi
63
+
64
+ # Private key files
65
+ if [[ "$BASENAME" == *.pem ]] || [[ "$BASENAME" == *.key ]] || \
66
+ [[ "$BASENAME" == *.p12 ]] || [[ "$BASENAME" == *.pfx ]] || \
67
+ [[ "$BASENAME" == *.jks ]]; then
68
+ BLOCKED=true
69
+ REASON="Private key / certificate file"
70
+ fi
71
+
72
+ # Credentials & secrets files
73
+ if echo "$BASENAME" | grep -qiE '^(credentials|secrets?|secret[-_]key|api[-_]key|auth[-_]token|access[-_]token)(\.[a-z]+)?$'; then
74
+ BLOCKED=true
75
+ REASON="Credentials / secrets file"
76
+ fi
77
+
78
+ # Known credential file patterns
79
+ if [[ "$BASENAME" == "*.credentials.json" ]] || \
80
+ echo "$NORM" | grep -qiE '/(credentials|secrets)/'; then
81
+ BLOCKED=true
82
+ REASON="Credentials directory hoặc file"
83
+ fi
84
+
85
+ # Service account / GCP keys
86
+ if echo "$BASENAME" | grep -qiE 'service[-_]account.*\.json$|gcp.*key.*\.json$|firebase.*key.*\.json$'; then
87
+ BLOCKED=true
88
+ REASON="Service account / cloud credentials"
89
+ fi
90
+
91
+ if [ "$BLOCKED" = true ]; then
92
+ echo "🔒 privacy-block: Blocked sensitive file read" >&2
93
+ echo " File: $FILE_PATH" >&2
94
+ echo " Lý do: $REASON" >&2
95
+ echo " Nếu thực sự cần đọc file này, hãy confirm rõ ràng trong prompt." >&2
96
+ exit 2
97
+ fi
98
+
99
+ exit 0
@@ -1,47 +1,53 @@
1
- #!/bin/bash
2
- # .claude/hooks/progress-ping.sh
3
- # Nhắc cập nhật progress file khi notification event xảy ra.
4
- # Non-blocking, informational only.
5
- # Được gọi bởi Notification hook.
6
-
7
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
8
-
9
- # ── Tìm active tasks ──────────────────────────────────────────────────────────
10
- CONFIG_FILE="$PROJECT_DIR/config/dw.config.yml"
11
- [ ! -f "$CONFIG_FILE" ] && CONFIG_FILE="$PROJECT_DIR/config/dw.config.yml"
12
-
13
- TASKS_DIR="$PROJECT_DIR/.dw/tasks"
14
-
15
- # Đọc paths.tasks từ config nếu có
16
- if [ -f "$CONFIG_FILE" ]; then
17
- CUSTOM_TASKS=$(grep -m1 "tasks:" "$CONFIG_FILE" 2>/dev/null \
18
- | sed 's/.*:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]' || true)
19
- [ -n "$CUSTOM_TASKS" ] && TASKS_DIR="$PROJECT_DIR/$CUSTOM_TASKS"
20
- fi
21
-
22
- [ ! -d "$TASKS_DIR" ] && exit 0
23
-
24
- # ── Kiểm tra in-progress tasks ───────────────────────────────────────────────
25
- in_progress_tasks=()
26
-
27
- for task_dir in "$TASKS_DIR"/*/; do
28
- [ -d "$task_dir" ] || continue
29
- task_name=$(basename "$task_dir")
30
- [ "$task_name" = "archive" ] && continue
31
-
32
- progress_file="$task_dir/${task_name}-progress.md"
33
- [ -f "$progress_file" ] || continue
34
-
35
- # Kiểm tra status
36
- if grep -q "Trạng thái: In Progress" "$progress_file" 2>/dev/null; then
37
- in_progress_tasks+=("$task_name")
38
- fi
39
- done
40
-
41
- # ── Chỉ remind nếu có active tasks ───────────────────────────────────────────
42
- if [ ${#in_progress_tasks[@]} -gt 0 ]; then
43
- echo "📋 Active task(s): ${in_progress_tasks[*]}" >&2
44
- echo " Nhớ cập nhật progress file sau khi hoàn thành subtask." >&2
45
- fi
46
-
47
- exit 0
1
+ #!/bin/bash
2
+ # .claude/hooks/progress-ping.sh
3
+ # Nhắc cập nhật progress file khi notification event xảy ra.
4
+ # Non-blocking, informational only.
5
+ # Được gọi bởi Notification hook.
6
+
7
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
8
+
9
+ # Telemetry (local, fire-and-forget)
10
+ TELEMETRY_SCRIPT="$PROJECT_DIR/.claude/hooks/telemetry-log.sh"
11
+ if [ -x "$TELEMETRY_SCRIPT" ] && [ "${DW_NO_TELEMETRY:-}" != "1" ]; then
12
+ "$TELEMETRY_SCRIPT" hook progress-ping >/dev/null 2>&1 || true
13
+ fi
14
+
15
+ # ── Tìm active tasks ──────────────────────────────────────────────────────────
16
+ CONFIG_FILE="$PROJECT_DIR/config/dw.config.yml"
17
+ [ ! -f "$CONFIG_FILE" ] && CONFIG_FILE="$PROJECT_DIR/config/dw.config.yml"
18
+
19
+ TASKS_DIR="$PROJECT_DIR/.dw/tasks"
20
+
21
+ # Đọc paths.tasks từ config nếu có
22
+ if [ -f "$CONFIG_FILE" ]; then
23
+ CUSTOM_TASKS=$(grep -m1 "tasks:" "$CONFIG_FILE" 2>/dev/null \
24
+ | sed 's/.*:[[:space:]]*//' | tr -d '"' | tr -d "'" | tr -d '[:space:]' || true)
25
+ [ -n "$CUSTOM_TASKS" ] && TASKS_DIR="$PROJECT_DIR/$CUSTOM_TASKS"
26
+ fi
27
+
28
+ [ ! -d "$TASKS_DIR" ] && exit 0
29
+
30
+ # ── Kiểm tra in-progress tasks ───────────────────────────────────────────────
31
+ in_progress_tasks=()
32
+
33
+ for task_dir in "$TASKS_DIR"/*/; do
34
+ [ -d "$task_dir" ] || continue
35
+ task_name=$(basename "$task_dir")
36
+ [ "$task_name" = "archive" ] && continue
37
+
38
+ progress_file="$task_dir/${task_name}-progress.md"
39
+ [ -f "$progress_file" ] || continue
40
+
41
+ # Kiểm tra status
42
+ if grep -q "Trạng thái: In Progress" "$progress_file" 2>/dev/null; then
43
+ in_progress_tasks+=("$task_name")
44
+ fi
45
+ done
46
+
47
+ # ── Chỉ remind nếu có active tasks ───────────────────────────────────────────
48
+ if [ ${#in_progress_tasks[@]} -gt 0 ]; then
49
+ echo "📋 Active task(s): ${in_progress_tasks[*]}" >&2
50
+ echo " Nhớ cập nhật progress file sau khi hoàn thành subtask." >&2
51
+ fi
52
+
53
+ exit 0