timsquad 3.3.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/README.ko.md +288 -0
  2. package/README.md +158 -151
  3. package/dist/commands/compile.d.ts +3 -0
  4. package/dist/commands/compile.d.ts.map +1 -0
  5. package/dist/commands/compile.js +170 -0
  6. package/dist/commands/compile.js.map +1 -0
  7. package/dist/commands/daemon.d.ts.map +1 -1
  8. package/dist/commands/daemon.js +95 -5
  9. package/dist/commands/daemon.js.map +1 -1
  10. package/dist/commands/full.js +1 -0
  11. package/dist/commands/full.js.map +1 -1
  12. package/dist/commands/git/pr.js +6 -5
  13. package/dist/commands/git/pr.js.map +1 -1
  14. package/dist/commands/git/release.js +2 -7
  15. package/dist/commands/git/release.js.map +1 -1
  16. package/dist/commands/improve.js +2 -2
  17. package/dist/commands/improve.js.map +1 -1
  18. package/dist/commands/init.d.ts.map +1 -1
  19. package/dist/commands/init.js +12 -3
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/log.d.ts.map +1 -1
  22. package/dist/commands/log.js +2 -2
  23. package/dist/commands/log.js.map +1 -1
  24. package/dist/commands/metrics.d.ts.map +1 -1
  25. package/dist/commands/metrics.js +6 -2
  26. package/dist/commands/metrics.js.map +1 -1
  27. package/dist/commands/retro.js +8 -8
  28. package/dist/commands/retro.js.map +1 -1
  29. package/dist/commands/session.js +3 -3
  30. package/dist/commands/session.js.map +1 -1
  31. package/dist/commands/skills.d.ts +12 -0
  32. package/dist/commands/skills.d.ts.map +1 -0
  33. package/dist/commands/skills.js +228 -0
  34. package/dist/commands/skills.js.map +1 -0
  35. package/dist/commands/status.js +1 -1
  36. package/dist/commands/status.js.map +1 -1
  37. package/dist/commands/upgrade.d.ts.map +1 -1
  38. package/dist/commands/upgrade.js +23 -1
  39. package/dist/commands/upgrade.js.map +1 -1
  40. package/dist/daemon/entry.js +3 -3
  41. package/dist/daemon/entry.js.map +1 -1
  42. package/dist/daemon/event-queue.d.ts.map +1 -1
  43. package/dist/daemon/event-queue.js +2 -2
  44. package/dist/daemon/event-queue.js.map +1 -1
  45. package/dist/daemon/index.d.ts +4 -2
  46. package/dist/daemon/index.d.ts.map +1 -1
  47. package/dist/daemon/index.js +214 -52
  48. package/dist/daemon/index.js.map +1 -1
  49. package/dist/daemon/jsonl-watcher.d.ts +1 -0
  50. package/dist/daemon/jsonl-watcher.d.ts.map +1 -1
  51. package/dist/daemon/jsonl-watcher.js.map +1 -1
  52. package/dist/daemon/meta-cache.d.ts +1 -0
  53. package/dist/daemon/meta-cache.d.ts.map +1 -1
  54. package/dist/daemon/meta-cache.js +9 -0
  55. package/dist/daemon/meta-cache.js.map +1 -1
  56. package/dist/daemon/session-notes.d.ts +33 -0
  57. package/dist/daemon/session-notes.d.ts.map +1 -0
  58. package/dist/daemon/session-notes.js +74 -0
  59. package/dist/daemon/session-notes.js.map +1 -0
  60. package/dist/daemon/session-state.d.ts +27 -0
  61. package/dist/daemon/session-state.d.ts.map +1 -0
  62. package/dist/daemon/session-state.js +165 -0
  63. package/dist/daemon/session-state.js.map +1 -0
  64. package/dist/daemon/shutdown.d.ts.map +1 -1
  65. package/dist/daemon/shutdown.js +9 -1
  66. package/dist/daemon/shutdown.js.map +1 -1
  67. package/dist/index.js +4 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/lib/agent-generator.d.ts +4 -0
  70. package/dist/lib/agent-generator.d.ts.map +1 -1
  71. package/dist/lib/agent-generator.js +52 -3
  72. package/dist/lib/agent-generator.js.map +1 -1
  73. package/dist/lib/compile-rules.d.ts +66 -0
  74. package/dist/lib/compile-rules.d.ts.map +1 -0
  75. package/dist/lib/compile-rules.js +114 -0
  76. package/dist/lib/compile-rules.js.map +1 -0
  77. package/dist/lib/compiler.d.ts +105 -0
  78. package/dist/lib/compiler.d.ts.map +1 -0
  79. package/dist/lib/compiler.js +368 -0
  80. package/dist/lib/compiler.js.map +1 -0
  81. package/dist/lib/config.d.ts +1 -0
  82. package/dist/lib/config.d.ts.map +1 -1
  83. package/dist/lib/config.js +8 -1
  84. package/dist/lib/config.js.map +1 -1
  85. package/dist/lib/project.d.ts.map +1 -1
  86. package/dist/lib/project.js +8 -3
  87. package/dist/lib/project.js.map +1 -1
  88. package/dist/lib/skill-generator.d.ts.map +1 -1
  89. package/dist/lib/skill-generator.js +22 -1
  90. package/dist/lib/skill-generator.js.map +1 -1
  91. package/dist/lib/template.d.ts.map +1 -1
  92. package/dist/lib/template.js +6 -0
  93. package/dist/lib/template.js.map +1 -1
  94. package/dist/types/config.d.ts +1 -0
  95. package/dist/types/config.d.ts.map +1 -1
  96. package/dist/types/config.js +12 -1
  97. package/dist/types/config.js.map +1 -1
  98. package/dist/types/project.d.ts +1 -1
  99. package/dist/types/project.d.ts.map +1 -1
  100. package/dist/types/project.js +2 -0
  101. package/dist/types/project.js.map +1 -1
  102. package/package.json +4 -4
  103. package/templates/base/agents/base/tsq-architect.md +2 -2
  104. package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
  105. package/templates/base/knowledge/checklists/plan-quality.md +31 -0
  106. package/templates/base/knowledge/checklists/stability-verification.md +14 -0
  107. package/templates/base/skills/controller/SKILL.md +111 -0
  108. package/templates/base/skills/controller/references/README.md +35 -0
  109. package/templates/base/skills/controller/rules/README.md +18 -0
  110. package/templates/base/skills/mobile/dart/SKILL.md +69 -0
  111. package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
  112. package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
  113. package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
  114. package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
  115. package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
  116. package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
  117. package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
  118. package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
  119. package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
  120. package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
  121. package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
  122. package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
  123. package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
  124. package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
  125. package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
  126. package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
  127. package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
  128. package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
  129. package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
  130. package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
  131. package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
  132. package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
  133. package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
  134. package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
  135. package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
  136. package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
  137. package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
  138. package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
  139. package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
  140. package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
  141. package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
  142. package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
  143. package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
  144. package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
  145. package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
  146. package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
  147. package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
  148. package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
  149. package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
  150. package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
  151. package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
  152. package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
  153. package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
  154. package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
  155. package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
  156. package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
  157. package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
  158. package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
  159. package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
  160. package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
  161. package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
  162. package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
  163. package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
  164. package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
  165. package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
  166. package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
  167. package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
  168. package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
  169. package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
  170. package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
  171. package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
  172. package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
  173. package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
  174. package/templates/base/skills/stability-verification/SKILL.md +64 -0
  175. package/templates/base/skills/stability-verification/references/release-checklist.md +34 -0
  176. package/templates/base/skills/stability-verification/references/security-fix-patterns.md +112 -0
  177. package/templates/base/skills/stability-verification/rules/verification-layers.md +67 -0
  178. package/templates/base/skills/stability-verification/rules/verification-workflow.md +69 -0
  179. package/templates/base/skills/stability-verification/scripts/verify.sh +294 -0
  180. package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
  181. package/templates/platforms/claude-code/rules/build-gate.md +28 -0
  182. package/templates/platforms/claude-code/rules/completion-verification.md +30 -0
  183. package/templates/platforms/claude-code/rules/context-monitor.md +23 -0
  184. package/templates/platforms/claude-code/rules/plan-review.md +45 -0
  185. package/templates/platforms/claude-code/rules/quality-guards.md +43 -0
  186. package/templates/platforms/claude-code/rules/session-notes.md +18 -0
  187. package/templates/platforms/claude-code/rules/skill-suggest.md +27 -0
  188. package/templates/platforms/claude-code/scripts/build-gate.sh +73 -0
  189. package/templates/platforms/claude-code/scripts/completion-guard.sh +93 -0
  190. package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
  191. package/templates/platforms/claude-code/scripts/safe-guard.sh +83 -0
  192. package/templates/platforms/claude-code/scripts/skill-rules.json +85 -0
  193. package/templates/platforms/claude-code/scripts/skill-suggest.sh +105 -0
  194. package/templates/platforms/claude-code/settings.json +111 -3
  195. package/templates/project-types/mobile-app/config.yaml +123 -0
  196. package/templates/project-types/mobile-app/process/workflow.xml +191 -0
@@ -0,0 +1,93 @@
1
+ #!/bin/bash
2
+ # Completion Guard — Stop Hook
3
+ # 1. Implementation phase에서 테스트 미실행 시 블로킹 (decision: block)
4
+ # 2. 세션 노트 컨텍스트를 systemMessage로 주입 (컨텍스트 생존 메모)
5
+ # 3. 컨텍스트 윈도우 85% 경고
6
+ #
7
+ # Input: JSON via stdin (Claude Code hook protocol)
8
+ # Output: JSON with decision (block) or systemMessage
9
+
10
+ set -e
11
+
12
+ # ── 0. Stop hook loop prevention ──
13
+ INPUT=$(cat 2>/dev/null || echo "")
14
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null || echo "false")
15
+ if [ "$STOP_HOOK_ACTIVE" = "true" ]; then
16
+ echo '{}'
17
+ exit 0
18
+ fi
19
+
20
+ # Find project root
21
+ PROJECT_ROOT="$(pwd)"
22
+ while [ "$PROJECT_ROOT" != "/" ]; do
23
+ if [ -d "$PROJECT_ROOT/.timsquad" ]; then
24
+ break
25
+ fi
26
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
27
+ done
28
+
29
+ # If not in a TimSquad project, allow
30
+ if [ ! -d "$PROJECT_ROOT/.timsquad" ]; then
31
+ echo '{}'
32
+ exit 0
33
+ fi
34
+
35
+ BLOCK_REASON=""
36
+
37
+ # ── 1. Test execution gate (implementation phase only) ──
38
+ # 자기 보고 금지: 테스트 exit code로 완료를 판정한다.
39
+ # stop_hook_active=false일 때만 블로킹 (1회 기회 부여, 루프 방지는 섹션 0에서 처리)
40
+ PHASE_FILE="$PROJECT_ROOT/.timsquad/state/current-phase.json"
41
+ if [ -f "$PHASE_FILE" ]; then
42
+ PHASE=$(jq -r '.current // .current_phase // .phase // "unknown"' "$PHASE_FILE" 2>/dev/null || echo "unknown")
43
+ if [ "$PHASE" = "implementation" ]; then
44
+ SESSION_STATE="$PROJECT_ROOT/.timsquad/.daemon/session-state.json"
45
+ if [ -f "$SESSION_STATE" ]; then
46
+ BASH_COMMANDS=$(jq -r '.metrics.bashCommands // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
47
+ if [ "$BASH_COMMANDS" -eq 0 ] 2>/dev/null; then
48
+ BLOCK_REASON="[Completion Guard] 이번 세션에서 테스트가 실행되지 않았습니다. 프로젝트의 테스트를 실행하여 변경사항을 검증한 후 완료하세요."
49
+ fi
50
+ fi
51
+ fi
52
+ fi
53
+
54
+ # ── 2. Session notes context injection ──
55
+ SESSION_CTX=""
56
+ NOTES_FILE="$PROJECT_ROOT/.timsquad/.daemon/session-notes.jsonl"
57
+ if [ -f "$NOTES_FILE" ]; then
58
+ LAST_LINE=$(tail -1 "$NOTES_FILE" 2>/dev/null || echo "")
59
+ if [ -n "$LAST_LINE" ]; then
60
+ TURN=$(echo "$LAST_LINE" | jq -r '.turn // 0' 2>/dev/null)
61
+ TOOLS=$(echo "$LAST_LINE" | jq -r '.metrics.tools // 0' 2>/dev/null)
62
+ FAILS=$(echo "$LAST_LINE" | jq -r '.metrics.fails // 0' 2>/dev/null)
63
+ AGENTS=$(echo "$LAST_LINE" | jq -r '(.activeAgents // []) | join(", ")' 2>/dev/null)
64
+ FILES=$(echo "$LAST_LINE" | jq -r '(.recentFiles // [])[:5] | map(split("/") | last) | join(", ")' 2>/dev/null)
65
+
66
+ CTX_PCT=$(echo "$LAST_LINE" | jq -r '.contextPct // 0' 2>/dev/null)
67
+
68
+ SESSION_CTX="[Session] Turn $TURN | Tools: $TOOLS"
69
+ [ "$FAILS" != "0" ] && [ "$FAILS" != "null" ] && SESSION_CTX="$SESSION_CTX (Fail: $FAILS)"
70
+ [ -n "$AGENTS" ] && [ "$AGENTS" != "null" ] && SESSION_CTX="$SESSION_CTX | Active: $AGENTS"
71
+ [ -n "$FILES" ] && [ "$FILES" != "null" ] && SESSION_CTX="$SESSION_CTX | Files: $FILES"
72
+
73
+ # ── 3. Context window monitor (85% threshold) ──
74
+ if [ "$CTX_PCT" -ge 85 ] 2>/dev/null; then
75
+ SESSION_CTX="$SESSION_CTX\\n⚠ [Context ${CTX_PCT}%] 컨텍스트 윈도우 85% 이상 사용. 중요 정보를 session-notes에 기록하고, 현재 태스크를 정리한 뒤 완료하세요."
76
+ elif [ "$CTX_PCT" -ge 70 ] 2>/dev/null; then
77
+ SESSION_CTX="$SESSION_CTX | Ctx: ${CTX_PCT}%"
78
+ fi
79
+ fi
80
+ fi
81
+
82
+ # ── 4. Combine and output ──
83
+ # BLOCK_REASON이 있으면 decision:block으로 강제 속행 (세션 컨텍스트 포함)
84
+ if [ -n "$BLOCK_REASON" ]; then
85
+ FULL_REASON="$BLOCK_REASON"
86
+ [ -n "$SESSION_CTX" ] && FULL_REASON="$BLOCK_REASON
87
+ $SESSION_CTX"
88
+ jq -n --arg reason "$FULL_REASON" '{"decision": "block", "reason": $reason}'
89
+ elif [ -n "$SESSION_CTX" ]; then
90
+ jq -n --arg msg "$SESSION_CTX" '{"systemMessage": $msg}'
91
+ else
92
+ echo '{}'
93
+ fi
@@ -0,0 +1,79 @@
1
+ #!/bin/bash
2
+ # Phase Guard — PreToolUse Hook
3
+ # Enforces phase restrictions on file operations.
4
+ # planning/design phase: blocks src/** writes
5
+ # implementation phase: blocks .timsquad/ssot/** writes
6
+ #
7
+ # Input: JSON via stdin (Claude Code hook protocol)
8
+ # Output: JSON with permissionDecision (allow/deny)
9
+
10
+ set -e
11
+
12
+ # Read hook input from stdin (non-blocking)
13
+ INPUT=""
14
+ if read -t 1 -r line; then
15
+ INPUT="$line"
16
+ fi
17
+
18
+ # If no input, allow (fail-open for safety)
19
+ if [ -z "$INPUT" ]; then
20
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
21
+ exit 0
22
+ fi
23
+
24
+ # Extract file path from tool input
25
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // ""' 2>/dev/null || echo "")
26
+
27
+ # If no file path, allow (not a file operation)
28
+ if [ -z "$FILE_PATH" ] || [ "$FILE_PATH" = "null" ]; then
29
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
30
+ exit 0
31
+ fi
32
+
33
+ # Find project root (look for .timsquad directory)
34
+ PROJECT_ROOT="$(pwd)"
35
+ while [ "$PROJECT_ROOT" != "/" ]; do
36
+ if [ -d "$PROJECT_ROOT/.timsquad" ]; then
37
+ break
38
+ fi
39
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
40
+ done
41
+
42
+ # If not in a TimSquad project, allow
43
+ if [ ! -d "$PROJECT_ROOT/.timsquad" ]; then
44
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
45
+ exit 0
46
+ fi
47
+
48
+ # Read current phase
49
+ PHASE_FILE="$PROJECT_ROOT/.timsquad/state/current-phase.json"
50
+ if [ ! -f "$PHASE_FILE" ]; then
51
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
52
+ exit 0
53
+ fi
54
+
55
+ PHASE=$(jq -r '.current // .current_phase // .phase // "unknown"' "$PHASE_FILE" 2>/dev/null)
56
+
57
+ # Normalize file path (make relative to project root)
58
+ REL_PATH="${FILE_PATH#$PROJECT_ROOT/}"
59
+
60
+ # Phase enforcement rules
61
+ case "$PHASE" in
62
+ planning|design)
63
+ # Block source code modifications during planning/design
64
+ if echo "$REL_PATH" | grep -qE '^src/|^lib/|^app/|^pages/|^components/'; then
65
+ jq -n --arg phase "$PHASE" '{hookSpecificOutput:{permissionDecision:"deny"},systemMessage:("[Phase Guard] " + $phase + " phase에서 코드 수정이 금지됩니다. SSOT 문서 작성을 먼저 완료하세요.")}'
66
+ exit 0
67
+ fi
68
+ ;;
69
+ implementation)
70
+ # Block SSOT modifications during implementation (prevent drift)
71
+ if echo "$REL_PATH" | grep -qE '^\.timsquad/ssot/'; then
72
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Phase Guard] implementation phase에서 SSOT 직접 수정이 금지됩니다. 변경이 필요하면 PM에게 L2 피드백을 보고하세요."}'
73
+ exit 0
74
+ fi
75
+ ;;
76
+ esac
77
+
78
+ # Default: allow
79
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
@@ -0,0 +1,83 @@
1
+ #!/bin/bash
2
+ # Safe Guard — PreToolUse Hook (Bash)
3
+ # Blocks or escalates destructive shell commands.
4
+ #
5
+ # Input: JSON via stdin (Claude Code hook protocol)
6
+ # Output: JSON with permissionDecision (allow/deny/ask)
7
+
8
+ set -e
9
+
10
+ # Read hook input from stdin (non-blocking)
11
+ INPUT=""
12
+ if read -t 1 -r line; then
13
+ INPUT="$line"
14
+ fi
15
+
16
+ # Fail-open: no input → allow
17
+ if [ -z "$INPUT" ]; then
18
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
19
+ exit 0
20
+ fi
21
+
22
+ # Extract command from tool input
23
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null || echo "")
24
+
25
+ if [ -z "$COMMAND" ] || [ "$COMMAND" = "null" ]; then
26
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
27
+ exit 0
28
+ fi
29
+
30
+ # ── DENY: Hard block (exit 0 + permissionDecision deny) ──
31
+
32
+ # Root deletion
33
+ if echo "$COMMAND" | grep -qE 'rm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*--force\s+)/*$|rm\s+-rf\s+/\s'; then
34
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Safe Guard] 루트 디렉토리 삭제 명령이 차단되었습니다."}'
35
+ exit 0
36
+ fi
37
+
38
+ # git push --force to main/master
39
+ if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\s+(main|master)|git\s+push\s+-f\s+.*\s+(main|master)'; then
40
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Safe Guard] main/master 브랜치에 force push가 차단되었습니다. 일반 push를 사용하세요."}'
41
+ exit 0
42
+ fi
43
+
44
+ # git reset --hard
45
+ if echo "$COMMAND" | grep -qE 'git\s+reset\s+--hard'; then
46
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Safe Guard] git reset --hard가 차단되었습니다. 커밋되지 않은 변경사항이 유실될 수 있습니다."}'
47
+ exit 0
48
+ fi
49
+
50
+ # SQL destructive operations
51
+ if echo "$COMMAND" | grep -qiE 'DROP\s+(TABLE|DATABASE|SCHEMA)|TRUNCATE\s+TABLE'; then
52
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Safe Guard] 파괴적 SQL 명령이 차단되었습니다. 데이터 유실 위험이 있습니다."}'
53
+ exit 0
54
+ fi
55
+
56
+ # chmod 777
57
+ if echo "$COMMAND" | grep -qE 'chmod\s+777'; then
58
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Safe Guard] chmod 777이 차단되었습니다. 최소 권한 원칙을 따르세요."}'
59
+ exit 0
60
+ fi
61
+
62
+ # ── ASK: Escalate to user confirmation ──
63
+
64
+ # npm publish
65
+ if echo "$COMMAND" | grep -qE 'npm\s+publish'; then
66
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"npm publish는 패키지를 공개 배포합니다. 계속하시겠습니까?"}}'
67
+ exit 0
68
+ fi
69
+
70
+ # git push to main/master (non-force)
71
+ if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*\s+(main|master)|git\s+push\s+(main|master)'; then
72
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"main/master 브랜치에 push합니다. 계속하시겠습니까?"}}'
73
+ exit 0
74
+ fi
75
+
76
+ # rm -rf (non-root, general)
77
+ if echo "$COMMAND" | grep -qE 'rm\s+(-[a-zA-Z]*r[a-zA-Z]*f|.*-rf)\s'; then
78
+ echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"재귀적 삭제 명령입니다. 대상을 확인해주세요."}}'
79
+ exit 0
80
+ fi
81
+
82
+ # ── Default: allow ──
83
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
@@ -0,0 +1,85 @@
1
+ {
2
+ "threshold": 4,
3
+ "rules": [
4
+ {
5
+ "skill": "mobile/flutter",
6
+ "keywords": ["flutter", "widget", "riverpod", "stateless", "stateful", "플러터"],
7
+ "patterns": ["(build|create|implement|add).*?(app|screen|page|widget)"]
8
+ },
9
+ {
10
+ "skill": "mobile/dart",
11
+ "keywords": ["dart", "freezed", "isolate"],
12
+ "patterns": []
13
+ },
14
+ {
15
+ "skill": "frontend/react",
16
+ "keywords": ["react", "component", "hook", "useState", "useEffect", "리액트", "컴포넌트"],
17
+ "patterns": ["(create|build|refactor).*?(component|page|layout)"]
18
+ },
19
+ {
20
+ "skill": "frontend/nextjs",
21
+ "keywords": ["nextjs", "next.js", "app router", "server component", "middleware"],
22
+ "patterns": ["(ssr|server.*render|static.*generat)"]
23
+ },
24
+ {
25
+ "skill": "backend/node",
26
+ "keywords": ["express", "fastify", "middleware", "api endpoint", "route"],
27
+ "patterns": ["(create|add|implement).*?(api|endpoint|route|server)"]
28
+ },
29
+ {
30
+ "skill": "database",
31
+ "keywords": ["database", "sql", "migration", "schema", "table", "index", "데이터베이스", "스키마"],
32
+ "patterns": ["(create|modify|add|alter).*?(table|column|index|migration)"]
33
+ },
34
+ {
35
+ "skill": "database/prisma",
36
+ "keywords": ["prisma", "prisma schema", "prisma migrate"],
37
+ "patterns": []
38
+ },
39
+ {
40
+ "skill": "methodology/debugging",
41
+ "keywords": ["debug", "bug", "error", "crash", "fix", "디버그", "버그", "에러"],
42
+ "patterns": ["(why|how).*?(fail|break|crash|error|not work)"]
43
+ },
44
+ {
45
+ "skill": "methodology/tdd",
46
+ "keywords": ["tdd", "test-driven", "vitest", "jest", "테스트 주도"],
47
+ "patterns": ["(write|add|create).*?test"]
48
+ },
49
+ {
50
+ "skill": "methodology/ddd",
51
+ "keywords": ["ddd", "domain-driven", "aggregate", "bounded context", "도메인 주도"],
52
+ "patterns": []
53
+ },
54
+ {
55
+ "skill": "security",
56
+ "keywords": ["security", "owasp", "vulnerability", "xss", "csrf", "injection", "보안", "취약점"],
57
+ "patterns": ["(security|auth|permission).*?(review|audit|check)"]
58
+ },
59
+ {
60
+ "skill": "testing",
61
+ "keywords": ["test", "coverage", "assertion", "mock", "stub", "테스트"],
62
+ "patterns": ["(increase|improve).*?coverage"]
63
+ },
64
+ {
65
+ "skill": "architecture",
66
+ "keywords": ["architecture", "system design", "api design", "adr", "아키텍처", "설계"],
67
+ "patterns": ["(design|architect|structure).*?(system|api|service)"]
68
+ },
69
+ {
70
+ "skill": "typescript",
71
+ "keywords": ["typescript", "type", "interface", "generic", "타입"],
72
+ "patterns": ["(type|typing).*?(error|issue|strict)"]
73
+ },
74
+ {
75
+ "skill": "ui-design",
76
+ "keywords": ["ui", "ux", "design system", "responsive", "accessibility", "a11y"],
77
+ "patterns": ["(design|redesign|improve).*?(ui|layout|interface)"]
78
+ },
79
+ {
80
+ "skill": "planning",
81
+ "keywords": ["prd", "requirements", "specification", "기획", "요구사항"],
82
+ "patterns": ["(write|create|draft).*?(prd|spec|requirement)"]
83
+ }
84
+ ]
85
+ }
@@ -0,0 +1,105 @@
1
+ #!/bin/bash
2
+ # Skill Suggest — UserPromptSubmit Hook
3
+ # 사용자 프롬프트를 분석하여 관련 스킬을 제안한다.
4
+ # 키워드/패턴 매칭 기반, advisory only (블로킹 안 함).
5
+ #
6
+ # Input: JSON via stdin (Claude Code hook protocol, .prompt 필드 포함)
7
+ # Output: JSON with additionalContext (스킬 제안)
8
+
9
+ set -e
10
+
11
+ # Read hook input
12
+ INPUT=$(cat 2>/dev/null || echo "")
13
+ if [ -z "$INPUT" ]; then
14
+ exit 0
15
+ fi
16
+
17
+ PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null || echo "")
18
+ if [ -z "$PROMPT" ] || [ "$PROMPT" = "null" ]; then
19
+ exit 0
20
+ fi
21
+
22
+ # Find project root
23
+ PROJECT_ROOT="$(pwd)"
24
+ while [ "$PROJECT_ROOT" != "/" ]; do
25
+ if [ -d "$PROJECT_ROOT/.timsquad" ]; then
26
+ break
27
+ fi
28
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
29
+ done
30
+
31
+ if [ ! -d "$PROJECT_ROOT/.timsquad" ]; then
32
+ exit 0
33
+ fi
34
+
35
+ # Load skill rules
36
+ RULES_FILE="$PROJECT_ROOT/.claude/scripts/skill-rules.json"
37
+ if [ ! -f "$RULES_FILE" ]; then
38
+ exit 0
39
+ fi
40
+
41
+ THRESHOLD=$(jq -r '.threshold // 4' "$RULES_FILE" 2>/dev/null || echo "4")
42
+ PROMPT_LOWER=$(echo "$PROMPT" | tr '[:upper:]' '[:lower:]')
43
+
44
+ # Match skills
45
+ MATCHES=""
46
+ RULE_COUNT=$(jq '.rules | length' "$RULES_FILE" 2>/dev/null || echo "0")
47
+
48
+ i=0
49
+ while [ "$i" -lt "$RULE_COUNT" ]; do
50
+ SKILL=$(jq -r ".rules[$i].skill" "$RULES_FILE" 2>/dev/null)
51
+ SCORE=0
52
+
53
+ # Check if skill is deployed
54
+ SKILL_DIR="$PROJECT_ROOT/.claude/skills/$SKILL"
55
+ if [ ! -d "$SKILL_DIR" ]; then
56
+ i=$((i + 1))
57
+ continue
58
+ fi
59
+
60
+ # Keyword matching (2 points each)
61
+ KW_COUNT=$(jq ".rules[$i].keywords | length" "$RULES_FILE" 2>/dev/null)
62
+ j=0
63
+ while [ "$j" -lt "$KW_COUNT" ]; do
64
+ KW=$(jq -r ".rules[$i].keywords[$j]" "$RULES_FILE" 2>/dev/null)
65
+ if echo "$PROMPT_LOWER" | grep -qi -- "$KW" 2>/dev/null; then
66
+ SCORE=$((SCORE + 2))
67
+ fi
68
+ j=$((j + 1))
69
+ done
70
+
71
+ # Pattern matching (4 points each)
72
+ PAT_COUNT=$(jq ".rules[$i].patterns | length" "$RULES_FILE" 2>/dev/null)
73
+ k=0
74
+ while [ "$k" -lt "$PAT_COUNT" ]; do
75
+ PAT=$(jq -r ".rules[$i].patterns[$k]" "$RULES_FILE" 2>/dev/null)
76
+ if [ -n "$PAT" ] && echo "$PROMPT_LOWER" | grep -qiE -- "$PAT" 2>/dev/null; then
77
+ SCORE=$((SCORE + 4))
78
+ fi
79
+ k=$((k + 1))
80
+ done
81
+
82
+ # Threshold check
83
+ if [ "$SCORE" -ge "$THRESHOLD" ]; then
84
+ if [ -n "$MATCHES" ]; then
85
+ MATCHES="$MATCHES, $SKILL"
86
+ else
87
+ MATCHES="$SKILL"
88
+ fi
89
+ fi
90
+
91
+ i=$((i + 1))
92
+ done
93
+
94
+ # Output suggestion if matches found
95
+ if [ -n "$MATCHES" ]; then
96
+ SUGGESTION="[Skill Suggest] 관련 스킬이 감지되었습니다: $MATCHES. 해당 스킬의 rules/를 참조하면 더 높은 품질의 결과를 얻을 수 있습니다."
97
+ jq -n --arg ctx "$SUGGESTION" '{
98
+ hookSpecificOutput: {
99
+ hookEventName: "UserPromptSubmit",
100
+ additionalContext: $ctx
101
+ }
102
+ }'
103
+ fi
104
+
105
+ exit 0
@@ -1,23 +1,131 @@
1
1
  {
2
2
  "hooks": {
3
+ "UserPromptSubmit": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "bash .claude/scripts/skill-suggest.sh",
9
+ "timeout": 5
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "PreToolUse": [
15
+ {
16
+ "matcher": "Write|Edit",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "bash .claude/scripts/phase-guard.sh",
21
+ "timeout": 3
22
+ }
23
+ ]
24
+ },
25
+ {
26
+ "matcher": "Bash",
27
+ "hooks": [
28
+ {
29
+ "type": "command",
30
+ "command": "bash .claude/scripts/safe-guard.sh",
31
+ "timeout": 3
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "matcher": "Task",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "tsq daemon notify subagent-start 2>/dev/null || true",
41
+ "timeout": 3
42
+ }
43
+ ]
44
+ }
45
+ ],
46
+ "SubagentStart": [
47
+ {
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "tsq daemon notify subagent-start 2>/dev/null || true",
52
+ "timeout": 3
53
+ }
54
+ ]
55
+ }
56
+ ],
57
+ "SubagentStop": [
58
+ {
59
+ "hooks": [
60
+ {
61
+ "type": "command",
62
+ "command": "tsq daemon notify subagent-stop 2>/dev/null || true",
63
+ "timeout": 3
64
+ }
65
+ ]
66
+ }
67
+ ],
3
68
  "SessionStart": [
4
69
  {
5
70
  "hooks": [
6
71
  {
7
72
  "type": "command",
8
- "command": "tsq daemon start --jsonl \"$CLAUDE_TRANSCRIPT_PATH\" 2>/dev/null || true",
73
+ "command": "tsq daemon start 2>/dev/null || true",
9
74
  "timeout": 5
10
75
  }
11
76
  ]
12
77
  }
13
78
  ],
79
+ "PostToolUse": [
80
+ {
81
+ "hooks": [
82
+ {
83
+ "type": "command",
84
+ "command": "tsq daemon notify tool-use --status success 2>/dev/null || true",
85
+ "timeout": 3
86
+ }
87
+ ]
88
+ }
89
+ ],
90
+ "PostToolUseFailure": [
91
+ {
92
+ "hooks": [
93
+ {
94
+ "type": "command",
95
+ "command": "tsq daemon notify tool-use --status failure 2>/dev/null || true",
96
+ "timeout": 3
97
+ }
98
+ ]
99
+ }
100
+ ],
101
+ "Stop": [
102
+ {
103
+ "hooks": [
104
+ {
105
+ "type": "command",
106
+ "command": "tsq daemon notify stop 2>/dev/null || true",
107
+ "timeout": 3
108
+ },
109
+ {
110
+ "type": "command",
111
+ "command": "bash .claude/scripts/completion-guard.sh",
112
+ "timeout": 5
113
+ },
114
+ {
115
+ "type": "command",
116
+ "command": "bash .claude/scripts/build-gate.sh",
117
+ "timeout": 30
118
+ }
119
+ ]
120
+ }
121
+ ],
14
122
  "SessionEnd": [
15
123
  {
16
124
  "hooks": [
17
125
  {
18
126
  "type": "command",
19
- "command": "tsq daemon stop 2>/dev/null || true",
20
- "timeout": 10
127
+ "command": "tsq daemon notify session-end 2>/dev/null || true",
128
+ "timeout": 5
21
129
  }
22
130
  ]
23
131
  }