timsquad 3.4.0 → 3.6.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 (218) hide show
  1. package/README.ko.md +4 -0
  2. package/README.md +4 -0
  3. package/dist/commands/audit.d.ts +22 -0
  4. package/dist/commands/audit.d.ts.map +1 -0
  5. package/dist/commands/audit.js +233 -0
  6. package/dist/commands/audit.js.map +1 -0
  7. package/dist/commands/compile.d.ts.map +1 -1
  8. package/dist/commands/compile.js +84 -3
  9. package/dist/commands/compile.js.map +1 -1
  10. package/dist/commands/daemon.d.ts.map +1 -1
  11. package/dist/commands/daemon.js +50 -3
  12. package/dist/commands/daemon.js.map +1 -1
  13. package/dist/commands/full.js +1 -0
  14. package/dist/commands/full.js.map +1 -1
  15. package/dist/commands/git/pr.js +6 -5
  16. package/dist/commands/git/pr.js.map +1 -1
  17. package/dist/commands/git/release.js +2 -7
  18. package/dist/commands/git/release.js.map +1 -1
  19. package/dist/commands/improve.js +2 -2
  20. package/dist/commands/improve.js.map +1 -1
  21. package/dist/commands/init.js +42 -6
  22. package/dist/commands/init.js.map +1 -1
  23. package/dist/commands/log.d.ts.map +1 -1
  24. package/dist/commands/log.js +34 -2
  25. package/dist/commands/log.js.map +1 -1
  26. package/dist/commands/meta-index.d.ts.map +1 -1
  27. package/dist/commands/meta-index.js +30 -0
  28. package/dist/commands/meta-index.js.map +1 -1
  29. package/dist/commands/metrics.d.ts.map +1 -1
  30. package/dist/commands/metrics.js +6 -2
  31. package/dist/commands/metrics.js.map +1 -1
  32. package/dist/commands/retro.d.ts.map +1 -1
  33. package/dist/commands/retro.js +71 -14
  34. package/dist/commands/retro.js.map +1 -1
  35. package/dist/commands/session.js +3 -3
  36. package/dist/commands/session.js.map +1 -1
  37. package/dist/commands/skills.js +4 -7
  38. package/dist/commands/skills.js.map +1 -1
  39. package/dist/commands/status.js +1 -1
  40. package/dist/commands/status.js.map +1 -1
  41. package/dist/commands/upgrade.d.ts.map +1 -1
  42. package/dist/commands/upgrade.js +18 -1
  43. package/dist/commands/upgrade.js.map +1 -1
  44. package/dist/commands/workflow.d.ts +2 -0
  45. package/dist/commands/workflow.d.ts.map +1 -1
  46. package/dist/commands/workflow.js +180 -6
  47. package/dist/commands/workflow.js.map +1 -1
  48. package/dist/daemon/context-writer.d.ts +14 -0
  49. package/dist/daemon/context-writer.d.ts.map +1 -1
  50. package/dist/daemon/context-writer.js +29 -0
  51. package/dist/daemon/context-writer.js.map +1 -1
  52. package/dist/daemon/event-queue.d.ts +4 -0
  53. package/dist/daemon/event-queue.d.ts.map +1 -1
  54. package/dist/daemon/event-queue.js +107 -6
  55. package/dist/daemon/event-queue.js.map +1 -1
  56. package/dist/daemon/file-watcher.d.ts +14 -8
  57. package/dist/daemon/file-watcher.d.ts.map +1 -1
  58. package/dist/daemon/file-watcher.js +78 -41
  59. package/dist/daemon/file-watcher.js.map +1 -1
  60. package/dist/daemon/index.d.ts +1 -0
  61. package/dist/daemon/index.d.ts.map +1 -1
  62. package/dist/daemon/index.js +129 -53
  63. package/dist/daemon/index.js.map +1 -1
  64. package/dist/daemon/jsonl-watcher.d.ts +1 -0
  65. package/dist/daemon/jsonl-watcher.d.ts.map +1 -1
  66. package/dist/daemon/jsonl-watcher.js.map +1 -1
  67. package/dist/daemon/session-notes.d.ts +33 -0
  68. package/dist/daemon/session-notes.d.ts.map +1 -0
  69. package/dist/daemon/session-notes.js +74 -0
  70. package/dist/daemon/session-notes.js.map +1 -0
  71. package/dist/daemon/session-state.d.ts +8 -0
  72. package/dist/daemon/session-state.d.ts.map +1 -1
  73. package/dist/daemon/session-state.js +33 -0
  74. package/dist/daemon/session-state.js.map +1 -1
  75. package/dist/daemon/shutdown.d.ts.map +1 -1
  76. package/dist/daemon/shutdown.js +3 -1
  77. package/dist/daemon/shutdown.js.map +1 -1
  78. package/dist/index.js +2 -0
  79. package/dist/index.js.map +1 -1
  80. package/dist/lib/agent-generator.d.ts +4 -0
  81. package/dist/lib/agent-generator.d.ts.map +1 -1
  82. package/dist/lib/agent-generator.js +63 -3
  83. package/dist/lib/agent-generator.js.map +1 -1
  84. package/dist/lib/compile-rules.d.ts +2 -0
  85. package/dist/lib/compile-rules.d.ts.map +1 -1
  86. package/dist/lib/compile-rules.js +2 -0
  87. package/dist/lib/compile-rules.js.map +1 -1
  88. package/dist/lib/compiler.d.ts +21 -1
  89. package/dist/lib/compiler.d.ts.map +1 -1
  90. package/dist/lib/compiler.js +113 -3
  91. package/dist/lib/compiler.js.map +1 -1
  92. package/dist/lib/config.d.ts +3 -0
  93. package/dist/lib/config.d.ts.map +1 -1
  94. package/dist/lib/config.js +17 -2
  95. package/dist/lib/config.js.map +1 -1
  96. package/dist/lib/project.d.ts.map +1 -1
  97. package/dist/lib/project.js +8 -3
  98. package/dist/lib/project.js.map +1 -1
  99. package/dist/lib/skill-generator.d.ts +1 -1
  100. package/dist/lib/skill-generator.d.ts.map +1 -1
  101. package/dist/lib/skill-generator.js +39 -3
  102. package/dist/lib/skill-generator.js.map +1 -1
  103. package/dist/lib/ssot-map.d.ts +31 -0
  104. package/dist/lib/ssot-map.d.ts.map +1 -0
  105. package/dist/lib/ssot-map.js +76 -0
  106. package/dist/lib/ssot-map.js.map +1 -0
  107. package/dist/lib/template.js +1 -0
  108. package/dist/lib/template.js.map +1 -1
  109. package/dist/lib/workflow-state.d.ts +1 -1
  110. package/dist/lib/workflow-state.d.ts.map +1 -1
  111. package/dist/lib/workflow-state.js +1 -1
  112. package/dist/lib/workflow-state.js.map +1 -1
  113. package/dist/types/config.d.ts +10 -1
  114. package/dist/types/config.d.ts.map +1 -1
  115. package/dist/types/config.js +22 -17
  116. package/dist/types/config.js.map +1 -1
  117. package/dist/types/index.d.ts +1 -0
  118. package/dist/types/index.d.ts.map +1 -1
  119. package/dist/types/index.js +1 -0
  120. package/dist/types/index.js.map +1 -1
  121. package/dist/types/meta-index.d.ts +8 -0
  122. package/dist/types/meta-index.d.ts.map +1 -1
  123. package/dist/types/project.d.ts +1 -1
  124. package/dist/types/project.d.ts.map +1 -1
  125. package/dist/types/project.js.map +1 -1
  126. package/dist/types/ssot-map.d.ts +28 -0
  127. package/dist/types/ssot-map.d.ts.map +1 -0
  128. package/dist/types/ssot-map.js +6 -0
  129. package/dist/types/ssot-map.js.map +1 -0
  130. package/package.json +4 -4
  131. package/templates/base/agents/base/tsq-architect.md +2 -2
  132. package/templates/base/agents/base/tsq-librarian.md +45 -0
  133. package/templates/base/knowledge/checklists/plan-quality.md +31 -0
  134. package/templates/base/knowledge/checklists/stability-verification.md +14 -0
  135. package/templates/base/skills/_shared/naming-conventions.md +49 -0
  136. package/templates/base/skills/_template/SKILL.md +33 -17
  137. package/templates/base/skills/audit/SKILL.md +66 -0
  138. package/templates/base/skills/coding/SKILL.md +49 -29
  139. package/templates/base/skills/coding/rules/async-patterns.md +81 -0
  140. package/templates/base/skills/coding/rules/code-organization.md +80 -0
  141. package/templates/base/skills/coding/rules/error-handling.md +76 -0
  142. package/templates/base/skills/coding/rules/type-safety.md +85 -0
  143. package/templates/base/skills/controller/SKILL.md +50 -84
  144. package/templates/base/skills/controller/delegation/developer.md +25 -0
  145. package/templates/base/skills/controller/delegation/librarian.md +33 -0
  146. package/templates/base/skills/controller/delegation/reviewer.md +19 -0
  147. package/templates/base/skills/controller/memory/.gitkeep +0 -0
  148. package/templates/base/skills/controller/triggers/phase-complete.md +25 -0
  149. package/templates/base/skills/controller/triggers/sequence-complete.md +15 -0
  150. package/templates/base/skills/controller/triggers/ssot-changed.md +14 -0
  151. package/templates/base/skills/controller/triggers/task-complete.md +14 -0
  152. package/templates/base/skills/database/SKILL.md +8 -25
  153. package/templates/base/skills/database/rules/query-optimization.md +32 -0
  154. package/templates/base/skills/database/rules/supabase-patterns.md +94 -0
  155. package/templates/base/skills/librarian/SKILL.md +53 -0
  156. package/templates/base/skills/main-session-constraints/SKILL.md +62 -0
  157. package/templates/base/skills/methodology/tdd/SKILL.md +6 -0
  158. package/templates/base/skills/product-audit/SKILL.md +115 -0
  159. package/templates/base/skills/product-audit/checklists/01-security.md +86 -0
  160. package/templates/base/skills/product-audit/checklists/02-performance.md +67 -0
  161. package/templates/base/skills/product-audit/checklists/03-seo.md +46 -0
  162. package/templates/base/skills/product-audit/checklists/04-accessibility.md +66 -0
  163. package/templates/base/skills/product-audit/checklists/05-ui-ux.md +50 -0
  164. package/templates/base/skills/product-audit/checklists/06-architecture.md +53 -0
  165. package/templates/base/skills/product-audit/checklists/07-functional-requirements.md +55 -0
  166. package/templates/base/skills/product-audit/rules/audit-protocol.md +136 -0
  167. package/templates/base/skills/product-audit/rules/false-positive-guard.md +81 -0
  168. package/templates/base/skills/product-audit/rules/scoring-criteria.md +113 -0
  169. package/templates/base/skills/product-audit/templates/improvement-plan-template.md +60 -0
  170. package/templates/base/skills/product-audit/templates/report-template.md +88 -0
  171. package/templates/base/skills/prompt-engineering/SKILL.md +54 -73
  172. package/templates/base/skills/retrospective/SKILL.md +70 -95
  173. package/templates/base/skills/retrospective/references/improvement-template.md +26 -0
  174. package/templates/base/skills/review/SKILL.md +72 -0
  175. package/templates/base/skills/security/SKILL.md +50 -37
  176. package/templates/base/skills/security/rules/auth-patterns.md +62 -0
  177. package/templates/base/skills/security/rules/dependency-security.md +69 -0
  178. package/templates/base/skills/security/rules/input-validation.md +68 -0
  179. package/templates/base/skills/security/rules/secrets-management.md +65 -0
  180. package/templates/base/skills/spec/SKILL.md +60 -0
  181. package/templates/base/skills/stability-verification/SKILL.md +64 -0
  182. package/templates/base/skills/stability-verification/references/release-checklist.md +34 -0
  183. package/templates/base/skills/stability-verification/references/security-fix-patterns.md +112 -0
  184. package/templates/base/skills/stability-verification/rules/verification-layers.md +67 -0
  185. package/templates/base/skills/stability-verification/rules/verification-workflow.md +69 -0
  186. package/templates/base/skills/stability-verification/scripts/verify.sh +294 -0
  187. package/templates/base/skills/testing/SKILL.md +33 -25
  188. package/templates/base/skills/testing/references/e2e-stability.md +33 -0
  189. package/templates/base/skills/tsq-cli/SKILL.md +73 -0
  190. package/templates/base/skills/tsq-cli/references/cli-reference.md +92 -0
  191. package/templates/base/skills/tsq-protocol/SKILL.md +41 -25
  192. package/templates/base/skills/typescript/SKILL.md +3 -9
  193. package/templates/base/timsquad/ssot/test-spec.template.md +11 -1
  194. package/templates/base/timsquad/ssot-map.template.yaml +41 -0
  195. package/templates/platforms/claude-code/rules/api-conventions.md +12 -0
  196. package/templates/platforms/claude-code/rules/build-gate.md +28 -0
  197. package/templates/platforms/claude-code/rules/completion-verification.md +30 -0
  198. package/templates/platforms/claude-code/rules/context-monitor.md +23 -0
  199. package/templates/platforms/claude-code/rules/librarian-constraints.md +11 -0
  200. package/templates/platforms/claude-code/rules/plan-review.md +45 -0
  201. package/templates/platforms/claude-code/rules/quality-guards.md +43 -0
  202. package/templates/platforms/claude-code/rules/session-notes.md +18 -0
  203. package/templates/platforms/claude-code/rules/skill-suggest.md +27 -0
  204. package/templates/platforms/claude-code/rules/test-conventions.md +13 -0
  205. package/templates/platforms/claude-code/scripts/build-gate.sh +73 -0
  206. package/templates/platforms/claude-code/scripts/change-scope-guard.sh +113 -0
  207. package/templates/platforms/claude-code/scripts/completion-guard.sh +122 -24
  208. package/templates/platforms/claude-code/scripts/context-restore.sh +68 -0
  209. package/templates/platforms/claude-code/scripts/e2e-commit-gate.sh +70 -0
  210. package/templates/platforms/claude-code/scripts/e2e-marker.sh +51 -0
  211. package/templates/platforms/claude-code/scripts/phase-guard.sh +5 -5
  212. package/templates/platforms/claude-code/scripts/pre-compact.sh +70 -0
  213. package/templates/platforms/claude-code/scripts/safe-guard.sh +83 -0
  214. package/templates/platforms/claude-code/scripts/skill-inject.sh +216 -0
  215. package/templates/platforms/claude-code/scripts/skill-rules.json +95 -0
  216. package/templates/platforms/claude-code/scripts/skill-suggest.sh +105 -0
  217. package/templates/platforms/claude-code/scripts/subagent-inject.sh +53 -0
  218. package/templates/platforms/claude-code/settings.json +71 -9
@@ -1,13 +1,22 @@
1
1
  #!/bin/bash
2
2
  # Completion Guard — Stop Hook
3
- # Warns when stopping without test execution during implementation phase.
4
- # First version: warn only (not block), to avoid disrupting workflow.
3
+ # 1. Implementation phase에서 테스트 미실행 블로킹 (decision: block)
4
+ # 2. 세션 노트 컨텍스트를 systemMessage로 주입 (컨텍스트 생존 메모)
5
+ # 3. 컨텍스트 윈도우 85% 경고
5
6
  #
6
7
  # Input: JSON via stdin (Claude Code hook protocol)
7
- # Output: JSON with systemMessage (warning if applicable)
8
+ # Output: JSON with decision (block) or systemMessage
8
9
 
9
10
  set -e
10
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
+
11
20
  # Find project root
12
21
  PROJECT_ROOT="$(pwd)"
13
22
  while [ "$PROJECT_ROOT" != "/" ]; do
@@ -23,35 +32,124 @@ if [ ! -d "$PROJECT_ROOT/.timsquad" ]; then
23
32
  exit 0
24
33
  fi
25
34
 
26
- # Read current phase
35
+ BLOCK_REASON=""
36
+
37
+ # ── 1. Test execution gate ──
38
+ # 변경된 소스 파일에 대한 테스트 실행 여부 확인
39
+ # implementation phase에서만 block, 그 외에는 warning
27
40
  PHASE_FILE="$PROJECT_ROOT/.timsquad/state/current-phase.json"
28
- if [ ! -f "$PHASE_FILE" ]; then
29
- echo '{}'
30
- exit 0
41
+ PHASE="unknown"
42
+ if [ -f "$PHASE_FILE" ]; then
43
+ PHASE=$(jq -r '.current // .current_phase // .phase // "unknown"' "$PHASE_FILE" 2>/dev/null || echo "unknown")
31
44
  fi
32
45
 
33
- PHASE=$(jq -r '.phase // "unknown"' "$PHASE_FILE" 2>/dev/null)
46
+ TEST_WARNING=""
47
+ SESSION_STATE="$PROJECT_ROOT/.timsquad/.daemon/session-state.json"
48
+ if [ -f "$SESSION_STATE" ]; then
49
+ # Check if any bash commands were executed (tests run via bash)
50
+ BASH_COMMANDS=$(jq -r '.metrics.bashCommands // 0' "$SESSION_STATE" 2>/dev/null || echo "0")
34
51
 
35
- # Only check during implementation phase
36
- if [ "$PHASE" != "implementation" ]; then
37
- echo '{}'
38
- exit 0
52
+ if [ "$BASH_COMMANDS" -eq 0 ] 2>/dev/null; then
53
+ # No tests or bash at all — check for changed source files
54
+ CHANGED_SRC=$(cd "$PROJECT_ROOT" && git diff --name-only --diff-filter=ACMR HEAD 2>/dev/null | grep -E '\.(ts|tsx|js|jsx)$' | grep -vE '\.(test|spec)\.' | head -5)
55
+ if [ -n "$CHANGED_SRC" ]; then
56
+ # List related test files that should have been run
57
+ RELATED_TESTS=""
58
+ while IFS= read -r SRC_FILE; do
59
+ [ -z "$SRC_FILE" ] && continue
60
+ BASE_NAME=$(basename "$SRC_FILE" | sed 's/\.[^.]*$//')
61
+ POTENTIAL=$(cd "$PROJECT_ROOT" && find . -name "${BASE_NAME}.test.*" -o -name "${BASE_NAME}.spec.*" 2>/dev/null | head -1)
62
+ if [ -n "$POTENTIAL" ]; then
63
+ RELATED_TESTS="$RELATED_TESTS $POTENTIAL"
64
+ fi
65
+ done <<< "$CHANGED_SRC"
66
+
67
+ if [ -n "$RELATED_TESTS" ]; then
68
+ TEST_WARNING="[Test Gate] 변경된 파일에 대한 테스트가 실행되지 않았습니다. 관련 테스트:$RELATED_TESTS"
69
+ else
70
+ TEST_WARNING="[Test Gate] 변경된 소스 파일이 있으나 테스트가 실행되지 않았습니다."
71
+ fi
72
+
73
+ if [ "$PHASE" = "implementation" ]; then
74
+ BLOCK_REASON="$TEST_WARNING 테스트를 실행한 후 완료하세요."
75
+ fi
76
+ fi
77
+ fi
39
78
  fi
40
79
 
41
- # Check session metrics for test execution
42
- SESSION_STATE="$PROJECT_ROOT/.timsquad/.daemon/session-state.json"
43
- if [ ! -f "$SESSION_STATE" ]; then
44
- echo '{"systemMessage":"[Completion Guard] 세션 메트릭을 확인할 수 없습니다. 테스트 실행 여부를 확인해주세요."}'
45
- exit 0
80
+ # ── 2. Session notes context injection ──
81
+ SESSION_CTX=""
82
+ NOTES_FILE="$PROJECT_ROOT/.timsquad/.daemon/session-notes.jsonl"
83
+ if [ -f "$NOTES_FILE" ]; then
84
+ LAST_LINE=$(tail -1 "$NOTES_FILE" 2>/dev/null || echo "")
85
+ if [ -n "$LAST_LINE" ]; then
86
+ TURN=$(echo "$LAST_LINE" | jq -r '.turn // 0' 2>/dev/null)
87
+ TOOLS=$(echo "$LAST_LINE" | jq -r '.metrics.tools // 0' 2>/dev/null)
88
+ FAILS=$(echo "$LAST_LINE" | jq -r '.metrics.fails // 0' 2>/dev/null)
89
+ AGENTS=$(echo "$LAST_LINE" | jq -r '(.activeAgents // []) | join(", ")' 2>/dev/null)
90
+ FILES=$(echo "$LAST_LINE" | jq -r '(.recentFiles // [])[:5] | map(split("/") | last) | join(", ")' 2>/dev/null)
91
+
92
+ CTX_PCT=$(echo "$LAST_LINE" | jq -r '.contextPct // 0' 2>/dev/null)
93
+
94
+ SESSION_CTX="[Session] Turn $TURN | Tools: $TOOLS"
95
+ [ "$FAILS" != "0" ] && [ "$FAILS" != "null" ] && SESSION_CTX="$SESSION_CTX (Fail: $FAILS)"
96
+ [ -n "$AGENTS" ] && [ "$AGENTS" != "null" ] && SESSION_CTX="$SESSION_CTX | Active: $AGENTS"
97
+ [ -n "$FILES" ] && [ "$FILES" != "null" ] && SESSION_CTX="$SESSION_CTX | Files: $FILES"
98
+
99
+ # ── 3. Context window monitor (85% threshold) ──
100
+ if [ "$CTX_PCT" -ge 85 ] 2>/dev/null; then
101
+ SESSION_CTX="$SESSION_CTX\\n⚠ [Context ${CTX_PCT}%] 컨텍스트 윈도우 85% 이상 사용. 중요 정보를 session-notes에 기록하고, 현재 태스크를 정리한 뒤 완료하세요."
102
+ elif [ "$CTX_PCT" -ge 70 ] 2>/dev/null; then
103
+ SESSION_CTX="$SESSION_CTX | Ctx: ${CTX_PCT}%"
104
+ fi
105
+ fi
46
106
  fi
47
107
 
48
- # Check if any bash commands included test execution
49
- BASH_COMMANDS=$(jq -r '.metrics.bashCommands // 0' "$SESSION_STATE" 2>/dev/null)
108
+ # ── 4. Skill Verification reminder (max 3 skills) ──
109
+ VERIFICATION=""
110
+ SKILLS_DIR="$PROJECT_ROOT/.claude/skills"
111
+ if [ -d "$SKILLS_DIR" ]; then
112
+ SKILL_COUNT=0
113
+ for SKILL_DIR in "$SKILLS_DIR"/*/; do
114
+ [ "$SKILL_COUNT" -ge 3 ] && break
115
+ SKILL_FILE="$SKILL_DIR/SKILL.md"
116
+ [ ! -f "$SKILL_FILE" ] && continue
50
117
 
51
- if [ "$BASH_COMMANDS" -eq 0 ]; then
52
- echo '{"systemMessage":"[Completion Guard] 이번 세션에서 테스트가 실행되지 않았습니다. 완료 전 테스트 실행을 권장합니다."}'
53
- exit 0
118
+ SKILL_NAME=$(basename "$SKILL_DIR")
119
+ # Skip non-active/template skills
120
+ case "$SKILL_NAME" in _template|tsq-cli|main-session-constraints) continue ;; esac
121
+
122
+ # Extract Verification section (2 lines max)
123
+ CHECKS=$(awk '/^## Verification/{capture=1; next} /^## /{capture=0} capture && /\|.*\|.*\|/ && !/Check.*Command/' "$SKILL_FILE" 2>/dev/null | head -2)
124
+ if [ -n "$CHECKS" ]; then
125
+ VERIFICATION="$VERIFICATION
126
+ [Skill Verification] $SKILL_NAME: $CHECKS"
127
+ SKILL_COUNT=$((SKILL_COUNT + 1))
128
+ fi
129
+ done
54
130
  fi
55
131
 
56
- # Allow
57
- echo '{}'
132
+ # ── 5. Combine and output ──
133
+ # BLOCK_REASON이 있으면 decision:block으로 강제 속행 (세션 컨텍스트 포함)
134
+ if [ -n "$BLOCK_REASON" ]; then
135
+ FULL_REASON="$BLOCK_REASON"
136
+ [ -n "$SESSION_CTX" ] && FULL_REASON="$FULL_REASON
137
+ $SESSION_CTX"
138
+ [ -n "$VERIFICATION" ] && FULL_REASON="$FULL_REASON
139
+ $VERIFICATION"
140
+ jq -n --arg reason "$FULL_REASON" '{"decision": "block", "reason": $reason}'
141
+ elif [ -n "$SESSION_CTX" ] || [ -n "$VERIFICATION" ] || [ -n "$TEST_WARNING" ]; then
142
+ FULL_MSG=""
143
+ [ -n "$SESSION_CTX" ] && FULL_MSG="$SESSION_CTX"
144
+ if [ -n "$TEST_WARNING" ]; then
145
+ [ -n "$FULL_MSG" ] && FULL_MSG="$FULL_MSG
146
+ $TEST_WARNING" || FULL_MSG="$TEST_WARNING"
147
+ fi
148
+ if [ -n "$VERIFICATION" ]; then
149
+ [ -n "$FULL_MSG" ] && FULL_MSG="$FULL_MSG
150
+ $VERIFICATION" || FULL_MSG="$VERIFICATION"
151
+ fi
152
+ jq -n --arg msg "$FULL_MSG" '{"systemMessage": $msg}'
153
+ else
154
+ echo '{}'
155
+ fi
@@ -0,0 +1,68 @@
1
+ #!/bin/bash
2
+ # Context Restore — SessionStart (compact) Hook
3
+ # 컴팩션 후 핵심 컨텍스트를 재주입하여 에이전트 맥락 유실을 방지한다.
4
+ #
5
+ # Input: JSON via stdin (Claude Code hook protocol)
6
+ # Output: JSON with systemMessage (컨텍스트 재주입)
7
+
8
+ set -e
9
+
10
+ # Find project root
11
+ PROJECT_ROOT="$(pwd)"
12
+ while [ "$PROJECT_ROOT" != "/" ]; do
13
+ if [ -d "$PROJECT_ROOT/.timsquad" ]; then
14
+ break
15
+ fi
16
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
17
+ done
18
+
19
+ if [ ! -d "$PROJECT_ROOT/.timsquad" ]; then
20
+ exit 0
21
+ fi
22
+
23
+ # Build context summary
24
+ CONTEXT=""
25
+
26
+ # 1. Project name from config
27
+ PROJECT_NAME=""
28
+ CONFIG_FILE="$PROJECT_ROOT/.timsquad/config.yaml"
29
+ if [ -f "$CONFIG_FILE" ]; then
30
+ PROJECT_NAME=$(grep -m1 'name:' "$CONFIG_FILE" 2>/dev/null | awk '{print $2}' || echo "")
31
+ fi
32
+
33
+ if [ -n "$PROJECT_NAME" ]; then
34
+ CONTEXT="[Context Restore] Project: $PROJECT_NAME"
35
+ else
36
+ CONTEXT="[Context Restore] TimSquad project"
37
+ fi
38
+
39
+ # 2. Current phase from workflow
40
+ WORKFLOW_FILE="$PROJECT_ROOT/.timsquad/workflow.json"
41
+ if [ -f "$WORKFLOW_FILE" ]; then
42
+ CURRENT_PHASE=$(jq -r '.currentPhase // "unknown"' "$WORKFLOW_FILE" 2>/dev/null || echo "unknown")
43
+ CONTEXT="$CONTEXT | Phase: $CURRENT_PHASE"
44
+ fi
45
+
46
+ # 3. Read compact summary if exists (saved by pre-compact.sh)
47
+ COMPACT_SUMMARY="$PROJECT_ROOT/.timsquad/.daemon/compact-summary.md"
48
+ if [ -f "$COMPACT_SUMMARY" ]; then
49
+ SUMMARY_CONTENT=$(head -10 "$COMPACT_SUMMARY" 2>/dev/null || echo "")
50
+ if [ -n "$SUMMARY_CONTENT" ]; then
51
+ CONTEXT="$CONTEXT
52
+ $SUMMARY_CONTENT"
53
+ fi
54
+ fi
55
+
56
+ # 4. Key constraints (3 lines max)
57
+ CONTEXT="$CONTEXT
58
+ [Key Constraints] TSQ CLI 사용 필수 (tsq log/feedback/commit). 직접 파일 조작 금지. 구현 전 검증 기준 명시."
59
+
60
+ # Output as systemMessage
61
+ jq -n --arg msg "$CONTEXT" '{
62
+ hookSpecificOutput: {
63
+ hookEventName: "SessionStart",
64
+ systemMessage: $msg
65
+ }
66
+ }'
67
+
68
+ exit 0
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # e2e-commit-gate.sh — Pre-commit hook: validates E2E marker before allowing commit
3
+ # Checks: JSON marker exists, valid format, source_hash matches, not expired
4
+ #
5
+ # Exit 0 = allow commit, Exit 1 = block commit
6
+
7
+ set -euo pipefail
8
+
9
+ MARKER_FILE=".e2e-passed"
10
+
11
+ # No marker = no E2E tests configured, allow
12
+ if [ ! -f "$MARKER_FILE" ]; then
13
+ exit 0
14
+ fi
15
+
16
+ # Try to parse as JSON; if not JSON, treat as bare touch (legacy fallback)
17
+ if ! command -v jq >/dev/null 2>&1; then
18
+ echo "Warning: jq not installed, skipping E2E marker validation"
19
+ exit 0
20
+ fi
21
+
22
+ # Attempt JSON parse
23
+ if ! jq empty "$MARKER_FILE" 2>/dev/null; then
24
+ # Bare touch file (legacy) — allow with warning
25
+ echo "Warning: .e2e-passed is not JSON format. Consider running E2E tests with e2e-marker.sh"
26
+ exit 0
27
+ fi
28
+
29
+ # Validate required fields
30
+ EXIT_CODE=$(jq -r '.exit_code // empty' "$MARKER_FILE")
31
+ SOURCE_HASH=$(jq -r '.source_hash // empty' "$MARKER_FILE")
32
+ EXPIRES_AT=$(jq -r '.expires_at // empty' "$MARKER_FILE")
33
+
34
+ # exit_code must be 0
35
+ if [ -n "$EXIT_CODE" ] && [ "$EXIT_CODE" -ne 0 ]; then
36
+ echo "E2E commit gate: BLOCKED — marker shows exit_code=$EXIT_CODE (tests failed)"
37
+ exit 1
38
+ fi
39
+
40
+ # source_hash check
41
+ if [ -n "$SOURCE_HASH" ] && [ "$SOURCE_HASH" != "unknown" ]; then
42
+ CURRENT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
43
+ if [ "$SOURCE_HASH" != "$CURRENT_HASH" ]; then
44
+ echo "E2E commit gate: WARNING — marker source_hash ($SOURCE_HASH) != current HEAD ($CURRENT_HASH)"
45
+ echo " E2E tests may be stale. Consider re-running."
46
+ fi
47
+ fi
48
+
49
+ # Expiry check
50
+ if [ -n "$EXPIRES_AT" ]; then
51
+ EXPIRES_EPOCH=$(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$EXPIRES_AT" +%s 2>/dev/null || \
52
+ date -u -d "$EXPIRES_AT" +%s 2>/dev/null || echo "0")
53
+ NOW_EPOCH=$(date -u +%s)
54
+ if [ "$EXPIRES_EPOCH" -gt 0 ] && [ "$NOW_EPOCH" -gt "$EXPIRES_EPOCH" ]; then
55
+ echo "E2E commit gate: BLOCKED — marker expired at $EXPIRES_AT"
56
+ echo " Re-run E2E tests to refresh the marker."
57
+ exit 1
58
+ fi
59
+ fi
60
+
61
+ # All checks passed
62
+ PASSED=$(jq -r '.passed // 0' "$MARKER_FILE")
63
+ FLAKY=$(jq -r '.flaky // 0' "$MARKER_FILE")
64
+ if [ "$FLAKY" -gt 0 ]; then
65
+ echo "E2E commit gate: PASSED ($PASSED tests, $FLAKY flaky — investigate flaky tests)"
66
+ else
67
+ echo "E2E commit gate: PASSED ($PASSED tests)"
68
+ fi
69
+
70
+ exit 0
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # e2e-marker.sh — E2E 테스트 결과를 JSON 마커로 기록
3
+ # 용도: PostToolUse 훅에서 E2E 테스트 완료 후 호출
4
+ # 출력: .e2e-passed (JSON)
5
+
6
+ set -euo pipefail
7
+
8
+ MARKER_FILE=".e2e-passed"
9
+ EXPIRY_HOURS="${E2E_MARKER_EXPIRY_HOURS:-24}"
10
+
11
+ # 인자: exit_code passed failed flaky
12
+ EXIT_CODE="${1:-0}"
13
+ PASSED="${2:-0}"
14
+ FAILED="${3:-0}"
15
+ FLAKY="${4:-0}"
16
+
17
+ # exit_code != 0 이면 마커 미생성
18
+ if [ "$EXIT_CODE" -ne 0 ]; then
19
+ rm -f "$MARKER_FILE"
20
+ echo "E2E failed (exit_code=$EXIT_CODE) — marker removed"
21
+ exit 0
22
+ fi
23
+
24
+ # source_hash: 현재 소스의 git short hash
25
+ SOURCE_HASH=""
26
+ if command -v git >/dev/null 2>&1; then
27
+ SOURCE_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
28
+ fi
29
+
30
+ # timestamp & expiry
31
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
32
+ if [[ "$OSTYPE" == "darwin"* ]]; then
33
+ EXPIRES_AT=$(date -u -v+"${EXPIRY_HOURS}"H +"%Y-%m-%dT%H:%M:%SZ")
34
+ else
35
+ EXPIRES_AT=$(date -u -d "+${EXPIRY_HOURS} hours" +"%Y-%m-%dT%H:%M:%SZ")
36
+ fi
37
+
38
+ # JSON 마커 생성
39
+ cat > "$MARKER_FILE" <<MARKER
40
+ {
41
+ "timestamp": "$TIMESTAMP",
42
+ "exit_code": $EXIT_CODE,
43
+ "passed": $PASSED,
44
+ "failed": $FAILED,
45
+ "flaky": $FLAKY,
46
+ "source_hash": "$SOURCE_HASH",
47
+ "expires_at": "$EXPIRES_AT"
48
+ }
49
+ MARKER
50
+
51
+ echo "E2E marker created: $PASSED passed, $FAILED failed, $FLAKY flaky"
@@ -22,7 +22,7 @@ if [ -z "$INPUT" ]; then
22
22
  fi
23
23
 
24
24
  # Extract file path from tool input
25
- FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // ""' 2>/dev/null)
25
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // ""' 2>/dev/null || echo "")
26
26
 
27
27
  # If no file path, allow (not a file operation)
28
28
  if [ -z "$FILE_PATH" ] || [ "$FILE_PATH" = "null" ]; then
@@ -52,24 +52,24 @@ if [ ! -f "$PHASE_FILE" ]; then
52
52
  exit 0
53
53
  fi
54
54
 
55
- PHASE=$(jq -r '.phase // "unknown"' "$PHASE_FILE" 2>/dev/null)
55
+ PHASE=$(jq -r '.current // .current_phase // .phase // "unknown"' "$PHASE_FILE" 2>/dev/null)
56
56
 
57
57
  # Normalize file path (make relative to project root)
58
- REL_PATH="${FILE_PATH#$PROJECT_ROOT/}"
58
+ REL_PATH="${FILE_PATH#"$PROJECT_ROOT"/}"
59
59
 
60
60
  # Phase enforcement rules
61
61
  case "$PHASE" in
62
62
  planning|design)
63
63
  # Block source code modifications during planning/design
64
64
  if echo "$REL_PATH" | grep -qE '^src/|^lib/|^app/|^pages/|^components/'; then
65
- echo "{\"hookSpecificOutput\":{\"permissionDecision\":\"deny\"},\"systemMessage\":\"[Phase Guard] $PHASE phase에서 코드 수정이 금지됩니다. SSOT 문서 작성을 먼저 완료하세요.\"}"
65
+ jq -n --arg phase "$PHASE" '{hookSpecificOutput:{permissionDecision:"deny"},systemMessage:("[Phase Guard] " + $phase + " phase에서 코드 수정이 금지됩니다. SSOT 문서 작성을 먼저 완료하세요.")}'
66
66
  exit 0
67
67
  fi
68
68
  ;;
69
69
  implementation)
70
70
  # Block SSOT modifications during implementation (prevent drift)
71
71
  if echo "$REL_PATH" | grep -qE '^\.timsquad/ssot/'; then
72
- echo "{\"hookSpecificOutput\":{\"permissionDecision\":\"deny\"},\"systemMessage\":\"[Phase Guard] implementation phase에서 SSOT 직접 수정이 금지됩니다. 변경이 필요하면 PM에게 L2 피드백을 보고하세요.\"}"
72
+ echo '{"hookSpecificOutput":{"permissionDecision":"deny"},"systemMessage":"[Phase Guard] implementation phase에서 SSOT 직접 수정이 금지됩니다. 변경이 필요하면 PM에게 L2 피드백을 보고하세요."}'
73
73
  exit 0
74
74
  fi
75
75
  ;;
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+ # Pre-Compact — PreCompact Hook
3
+ # 컴팩션 직전 현재 상태 요약을 compact-summary.md에 저장한다.
4
+ # context-restore.sh가 이 파일을 읽어 compact 후 재주입.
5
+ #
6
+ # Input: JSON via stdin (Claude Code hook protocol)
7
+ # Output: none (파일 생성만)
8
+
9
+ set -e
10
+
11
+ # Find project root
12
+ PROJECT_ROOT="$(pwd)"
13
+ while [ "$PROJECT_ROOT" != "/" ]; do
14
+ if [ -d "$PROJECT_ROOT/.timsquad" ]; then
15
+ break
16
+ fi
17
+ PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
18
+ done
19
+
20
+ if [ ! -d "$PROJECT_ROOT/.timsquad" ]; then
21
+ exit 0
22
+ fi
23
+
24
+ DAEMON_DIR="$PROJECT_ROOT/.timsquad/.daemon"
25
+ mkdir -p "$DAEMON_DIR"
26
+
27
+ SUMMARY_FILE="$DAEMON_DIR/compact-summary.md"
28
+
29
+ # Collect summary data
30
+ SUMMARY="# Compact Summary (auto-generated)"
31
+ SUMMARY="$SUMMARY
32
+ Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
33
+
34
+ # Current phase
35
+ WORKFLOW_FILE="$PROJECT_ROOT/.timsquad/workflow.json"
36
+ if [ -f "$WORKFLOW_FILE" ]; then
37
+ PHASE=$(jq -r '.currentPhase // "unknown"' "$WORKFLOW_FILE" 2>/dev/null || echo "unknown")
38
+ SUMMARY="$SUMMARY
39
+ Phase: $PHASE"
40
+ fi
41
+
42
+ # Recent changed files (top 5)
43
+ RECENT_FILES=$(git -C "$PROJECT_ROOT" diff --name-only HEAD 2>/dev/null | head -5 || echo "")
44
+ if [ -n "$RECENT_FILES" ]; then
45
+ SUMMARY="$SUMMARY
46
+ Recent changes: $RECENT_FILES"
47
+ fi
48
+
49
+ # Active tasks from session state
50
+ SESSION_STATE="$DAEMON_DIR/session-state.json"
51
+ if [ -f "$SESSION_STATE" ]; then
52
+ TASK_INFO=$(jq -r '.currentTask // "none"' "$SESSION_STATE" 2>/dev/null || echo "none")
53
+ SUMMARY="$SUMMARY
54
+ Active task: $TASK_INFO"
55
+ fi
56
+
57
+ # Session notes (last entry)
58
+ NOTES_FILE="$DAEMON_DIR/session-notes.jsonl"
59
+ if [ -f "$NOTES_FILE" ]; then
60
+ LAST_NOTE=$(tail -1 "$NOTES_FILE" 2>/dev/null | jq -r '.summary // ""' 2>/dev/null || echo "")
61
+ if [ -n "$LAST_NOTE" ] && [ "$LAST_NOTE" != "null" ]; then
62
+ SUMMARY="$SUMMARY
63
+ Last note: $LAST_NOTE"
64
+ fi
65
+ fi
66
+
67
+ # Write summary (fail-open: if write fails, don't block compact)
68
+ echo "$SUMMARY" > "$SUMMARY_FILE" 2>/dev/null || true
69
+
70
+ exit 0
@@ -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"}}'