timsquad 3.5.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 (160) 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 +48 -2
  12. package/dist/commands/daemon.js.map +1 -1
  13. package/dist/commands/init.js +42 -6
  14. package/dist/commands/init.js.map +1 -1
  15. package/dist/commands/log.d.ts.map +1 -1
  16. package/dist/commands/log.js +32 -0
  17. package/dist/commands/log.js.map +1 -1
  18. package/dist/commands/meta-index.d.ts.map +1 -1
  19. package/dist/commands/meta-index.js +30 -0
  20. package/dist/commands/meta-index.js.map +1 -1
  21. package/dist/commands/retro.d.ts.map +1 -1
  22. package/dist/commands/retro.js +63 -6
  23. package/dist/commands/retro.js.map +1 -1
  24. package/dist/commands/workflow.d.ts +2 -0
  25. package/dist/commands/workflow.d.ts.map +1 -1
  26. package/dist/commands/workflow.js +180 -6
  27. package/dist/commands/workflow.js.map +1 -1
  28. package/dist/daemon/context-writer.d.ts +14 -0
  29. package/dist/daemon/context-writer.d.ts.map +1 -1
  30. package/dist/daemon/context-writer.js +29 -0
  31. package/dist/daemon/context-writer.js.map +1 -1
  32. package/dist/daemon/event-queue.d.ts +4 -0
  33. package/dist/daemon/event-queue.d.ts.map +1 -1
  34. package/dist/daemon/event-queue.js +107 -6
  35. package/dist/daemon/event-queue.js.map +1 -1
  36. package/dist/daemon/file-watcher.d.ts +14 -8
  37. package/dist/daemon/file-watcher.d.ts.map +1 -1
  38. package/dist/daemon/file-watcher.js +78 -41
  39. package/dist/daemon/file-watcher.js.map +1 -1
  40. package/dist/daemon/index.d.ts.map +1 -1
  41. package/dist/daemon/index.js +42 -36
  42. package/dist/daemon/index.js.map +1 -1
  43. package/dist/index.js +2 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/lib/agent-generator.d.ts.map +1 -1
  46. package/dist/lib/agent-generator.js +11 -0
  47. package/dist/lib/agent-generator.js.map +1 -1
  48. package/dist/lib/compile-rules.d.ts +2 -0
  49. package/dist/lib/compile-rules.d.ts.map +1 -1
  50. package/dist/lib/compile-rules.js +2 -0
  51. package/dist/lib/compile-rules.js.map +1 -1
  52. package/dist/lib/compiler.d.ts +21 -1
  53. package/dist/lib/compiler.d.ts.map +1 -1
  54. package/dist/lib/compiler.js +113 -3
  55. package/dist/lib/compiler.js.map +1 -1
  56. package/dist/lib/config.d.ts +3 -0
  57. package/dist/lib/config.d.ts.map +1 -1
  58. package/dist/lib/config.js +17 -2
  59. package/dist/lib/config.js.map +1 -1
  60. package/dist/lib/skill-generator.d.ts +1 -1
  61. package/dist/lib/skill-generator.d.ts.map +1 -1
  62. package/dist/lib/skill-generator.js +17 -2
  63. package/dist/lib/skill-generator.js.map +1 -1
  64. package/dist/lib/ssot-map.d.ts +31 -0
  65. package/dist/lib/ssot-map.d.ts.map +1 -0
  66. package/dist/lib/ssot-map.js +76 -0
  67. package/dist/lib/ssot-map.js.map +1 -0
  68. package/dist/lib/template.js +1 -0
  69. package/dist/lib/template.js.map +1 -1
  70. package/dist/lib/workflow-state.d.ts +1 -1
  71. package/dist/lib/workflow-state.d.ts.map +1 -1
  72. package/dist/lib/workflow-state.js +1 -1
  73. package/dist/lib/workflow-state.js.map +1 -1
  74. package/dist/types/config.d.ts +10 -1
  75. package/dist/types/config.d.ts.map +1 -1
  76. package/dist/types/config.js +22 -17
  77. package/dist/types/config.js.map +1 -1
  78. package/dist/types/index.d.ts +1 -0
  79. package/dist/types/index.d.ts.map +1 -1
  80. package/dist/types/index.js +1 -0
  81. package/dist/types/index.js.map +1 -1
  82. package/dist/types/meta-index.d.ts +8 -0
  83. package/dist/types/meta-index.d.ts.map +1 -1
  84. package/dist/types/project.d.ts +1 -1
  85. package/dist/types/project.d.ts.map +1 -1
  86. package/dist/types/project.js.map +1 -1
  87. package/dist/types/ssot-map.d.ts +28 -0
  88. package/dist/types/ssot-map.d.ts.map +1 -0
  89. package/dist/types/ssot-map.js +6 -0
  90. package/dist/types/ssot-map.js.map +1 -0
  91. package/package.json +1 -1
  92. package/templates/base/agents/base/tsq-librarian.md +45 -0
  93. package/templates/base/skills/_shared/naming-conventions.md +49 -0
  94. package/templates/base/skills/_template/SKILL.md +33 -17
  95. package/templates/base/skills/audit/SKILL.md +66 -0
  96. package/templates/base/skills/coding/SKILL.md +49 -29
  97. package/templates/base/skills/coding/rules/async-patterns.md +81 -0
  98. package/templates/base/skills/coding/rules/code-organization.md +80 -0
  99. package/templates/base/skills/coding/rules/error-handling.md +76 -0
  100. package/templates/base/skills/coding/rules/type-safety.md +85 -0
  101. package/templates/base/skills/controller/SKILL.md +50 -84
  102. package/templates/base/skills/controller/delegation/developer.md +25 -0
  103. package/templates/base/skills/controller/delegation/librarian.md +33 -0
  104. package/templates/base/skills/controller/delegation/reviewer.md +19 -0
  105. package/templates/base/skills/controller/memory/.gitkeep +0 -0
  106. package/templates/base/skills/controller/triggers/phase-complete.md +25 -0
  107. package/templates/base/skills/controller/triggers/sequence-complete.md +15 -0
  108. package/templates/base/skills/controller/triggers/ssot-changed.md +14 -0
  109. package/templates/base/skills/controller/triggers/task-complete.md +14 -0
  110. package/templates/base/skills/database/SKILL.md +8 -25
  111. package/templates/base/skills/database/rules/query-optimization.md +32 -0
  112. package/templates/base/skills/database/rules/supabase-patterns.md +94 -0
  113. package/templates/base/skills/librarian/SKILL.md +53 -0
  114. package/templates/base/skills/main-session-constraints/SKILL.md +62 -0
  115. package/templates/base/skills/methodology/tdd/SKILL.md +6 -0
  116. package/templates/base/skills/product-audit/SKILL.md +115 -0
  117. package/templates/base/skills/product-audit/checklists/01-security.md +86 -0
  118. package/templates/base/skills/product-audit/checklists/02-performance.md +67 -0
  119. package/templates/base/skills/product-audit/checklists/03-seo.md +46 -0
  120. package/templates/base/skills/product-audit/checklists/04-accessibility.md +66 -0
  121. package/templates/base/skills/product-audit/checklists/05-ui-ux.md +50 -0
  122. package/templates/base/skills/product-audit/checklists/06-architecture.md +53 -0
  123. package/templates/base/skills/product-audit/checklists/07-functional-requirements.md +55 -0
  124. package/templates/base/skills/product-audit/rules/audit-protocol.md +136 -0
  125. package/templates/base/skills/product-audit/rules/false-positive-guard.md +81 -0
  126. package/templates/base/skills/product-audit/rules/scoring-criteria.md +113 -0
  127. package/templates/base/skills/product-audit/templates/improvement-plan-template.md +60 -0
  128. package/templates/base/skills/product-audit/templates/report-template.md +88 -0
  129. package/templates/base/skills/prompt-engineering/SKILL.md +54 -73
  130. package/templates/base/skills/retrospective/SKILL.md +70 -95
  131. package/templates/base/skills/retrospective/references/improvement-template.md +26 -0
  132. package/templates/base/skills/review/SKILL.md +72 -0
  133. package/templates/base/skills/security/SKILL.md +50 -37
  134. package/templates/base/skills/security/rules/auth-patterns.md +62 -0
  135. package/templates/base/skills/security/rules/dependency-security.md +69 -0
  136. package/templates/base/skills/security/rules/input-validation.md +68 -0
  137. package/templates/base/skills/security/rules/secrets-management.md +65 -0
  138. package/templates/base/skills/spec/SKILL.md +60 -0
  139. package/templates/base/skills/testing/SKILL.md +33 -25
  140. package/templates/base/skills/testing/references/e2e-stability.md +33 -0
  141. package/templates/base/skills/tsq-cli/SKILL.md +73 -0
  142. package/templates/base/skills/tsq-cli/references/cli-reference.md +92 -0
  143. package/templates/base/skills/tsq-protocol/SKILL.md +41 -25
  144. package/templates/base/skills/typescript/SKILL.md +3 -9
  145. package/templates/base/timsquad/ssot/test-spec.template.md +11 -1
  146. package/templates/base/timsquad/ssot-map.template.yaml +41 -0
  147. package/templates/platforms/claude-code/rules/api-conventions.md +12 -0
  148. package/templates/platforms/claude-code/rules/librarian-constraints.md +11 -0
  149. package/templates/platforms/claude-code/rules/test-conventions.md +13 -0
  150. package/templates/platforms/claude-code/scripts/change-scope-guard.sh +113 -0
  151. package/templates/platforms/claude-code/scripts/completion-guard.sh +75 -13
  152. package/templates/platforms/claude-code/scripts/context-restore.sh +68 -0
  153. package/templates/platforms/claude-code/scripts/e2e-commit-gate.sh +70 -0
  154. package/templates/platforms/claude-code/scripts/e2e-marker.sh +51 -0
  155. package/templates/platforms/claude-code/scripts/phase-guard.sh +1 -1
  156. package/templates/platforms/claude-code/scripts/pre-compact.sh +70 -0
  157. package/templates/platforms/claude-code/scripts/skill-inject.sh +216 -0
  158. package/templates/platforms/claude-code/scripts/skill-rules.json +11 -1
  159. package/templates/platforms/claude-code/scripts/subagent-inject.sh +53 -0
  160. package/templates/platforms/claude-code/settings.json +27 -1
@@ -0,0 +1,41 @@
1
+ # SSOT Map — 티어별 문서 주입 기준
2
+ # Tier 0: 모든 작업에 항상 주입 (Hook → systemMessage)
3
+ # Tier 1: Phase 시작 시 Controller가 주입
4
+ # Tier 2: Sequence 범위 문서 (Controller가 선택 주입)
5
+ # Tier 3: 개별 Task에 필요한 구체 spec (Controller가 선택 주입)
6
+
7
+ tier-0-always:
8
+ description: "모든 작업에 항상 주입되는 제약 문서"
9
+ inject_via: hook
10
+ documents:
11
+ - compiled: rules/security-constraints.md
12
+ source: security-spec.md
13
+ - compiled: rules/completion-criteria.md
14
+ source: requirements.md
15
+
16
+ tier-1-phase:
17
+ description: "Phase 시작 시 controller가 주입하는 방향성 문서"
18
+ inject_via: controller
19
+ documents:
20
+ - compiled: references/prd-summary.spec.md
21
+ source: prd.md
22
+ - compiled: references/architecture.spec.md
23
+ source: planning.md
24
+
25
+ tier-2-sequence:
26
+ description: "Sequence 범위의 기능별 문서"
27
+ inject_via: controller
28
+ documents:
29
+ - compiled: references/service.spec.md
30
+ source: service-spec.md
31
+ - compiled: references/data-design.spec.md
32
+ source: data-design.md
33
+
34
+ tier-3-task:
35
+ description: "개별 Task에 필요한 구체 spec"
36
+ inject_via: controller
37
+ documents:
38
+ - compiled: references/error-codes.spec.md
39
+ source: error-codes.md
40
+ - compiled: references/functional.spec.md
41
+ source: functional-spec.md
@@ -0,0 +1,12 @@
1
+ ---
2
+ paths:
3
+ - "src/api/**/*.ts"
4
+ - "src/routes/**/*.ts"
5
+ - "src/controllers/**/*.ts"
6
+ ---
7
+
8
+ - RESTful 네이밍 사용 (복수형 리소스, kebab-case)
9
+ - 에러 응답 표준 형식: `{ code: string, message: string, details?: unknown }`
10
+ - HTTP 상태 코드 정확히 매핑 (200/201/400/401/403/404/500)
11
+ - 요청 validation은 엔드포인트 진입 시점에서 수행
12
+ - 비즈니스 로직은 서비스 레이어에 위치 (컨트롤러에 직접 작성 금지)
@@ -0,0 +1,11 @@
1
+ ---
2
+ paths:
3
+ - ".timsquad/**"
4
+ - "docs/**"
5
+ ---
6
+
7
+ Librarian 에이전트가 이 경로에서 작업할 때:
8
+ - 문서 작성/갱신 허용
9
+ - 소스 코드(src/, lib/, app/) 수정 절대 금지
10
+ - 기록, 분석, 리포트 작성만 수행
11
+ - 코드 변경이 필요한 사항은 피드백으로 라우팅
@@ -0,0 +1,13 @@
1
+ ---
2
+ paths:
3
+ - "tests/**/*.test.ts"
4
+ - "tests/**/*.spec.ts"
5
+ - "**/*.test.ts"
6
+ - "**/*.spec.ts"
7
+ ---
8
+
9
+ - describe/it 중첩 3단계 이하 유지
10
+ - 테스트 데이터는 인라인 또는 fixtures/ 디렉토리 사용
11
+ - 각 테스트는 독립적 (공유 상태 금지, beforeEach에서 초기화)
12
+ - 테스트 이름은 "should + 기대 동작" 형식
13
+ - mock은 최소 범위로 사용 (외부 의존성만 mock)
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env bash
2
+ # change-scope-guard.sh — PreToolUse Hook (Edit/Write)
3
+ # Tracks cumulative file changes and enforces SCR (Single Change Rule).
4
+ # 3 files: warning, 6 files: block, >100 lines total: warning
5
+ #
6
+ # Input: JSON via stdin (Claude Code hook protocol)
7
+ # Output: JSON with permissionDecision (allow/deny/ask)
8
+ #
9
+ # State file: /tmp/tsq-scope-guard-${SESSION_ID:-default}.json
10
+
11
+ set -euo pipefail
12
+
13
+ WARN_FILES=3
14
+ BLOCK_FILES=6
15
+ WARN_LINES=100
16
+
17
+ # Session-scoped state file
18
+ SESSION_ID="${CLAUDE_SESSION_ID:-default}"
19
+ STATE_FILE="/tmp/tsq-scope-guard-${SESSION_ID}.json"
20
+
21
+ # Read hook input from stdin
22
+ INPUT=""
23
+ if read -t 1 -r line; then
24
+ INPUT="$line"
25
+ fi
26
+
27
+ # Fail-open: no input → allow
28
+ if [ -z "$INPUT" ]; then
29
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
30
+ exit 0
31
+ fi
32
+
33
+ # Extract tool name
34
+ TOOL_NAME=""
35
+ if command -v jq >/dev/null 2>&1; then
36
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName // empty' 2>/dev/null || true)
37
+ fi
38
+
39
+ # Only track Edit and Write tools
40
+ if [ "$TOOL_NAME" != "Edit" ] && [ "$TOOL_NAME" != "Write" ]; then
41
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
42
+ exit 0
43
+ fi
44
+
45
+ # Extract file path from tool input
46
+ FILE_PATH=""
47
+ if command -v jq >/dev/null 2>&1; then
48
+ FILE_PATH=$(echo "$INPUT" | jq -r '.toolInput.file_path // empty' 2>/dev/null || true)
49
+ fi
50
+
51
+ if [ -z "$FILE_PATH" ]; then
52
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
53
+ exit 0
54
+ fi
55
+
56
+ # Initialize state file if needed
57
+ if [ ! -f "$STATE_FILE" ]; then
58
+ echo '{"files":[],"total_lines":0}' > "$STATE_FILE"
59
+ fi
60
+
61
+ # Read current state
62
+ if command -v jq >/dev/null 2>&1; then
63
+ CURRENT_FILES=$(jq -r '.files[]' "$STATE_FILE" 2>/dev/null || true)
64
+ TOTAL_LINES=$(jq -r '.total_lines // 0' "$STATE_FILE" 2>/dev/null || echo "0")
65
+ else
66
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
67
+ exit 0
68
+ fi
69
+
70
+ # Check if file is already tracked
71
+ ALREADY_TRACKED=false
72
+ while IFS= read -r f; do
73
+ if [ "$f" = "$FILE_PATH" ]; then
74
+ ALREADY_TRACKED=true
75
+ break
76
+ fi
77
+ done <<< "$CURRENT_FILES"
78
+
79
+ # Add new file if not tracked
80
+ if [ "$ALREADY_TRACKED" = false ]; then
81
+ jq --arg fp "$FILE_PATH" '.files += [$fp]' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE"
82
+ fi
83
+
84
+ # Estimate line changes (for Edit: old_string length approximation)
85
+ if [ "$TOOL_NAME" = "Edit" ]; then
86
+ NEW_LINES=$(echo "$INPUT" | jq -r '.toolInput.new_string // ""' 2>/dev/null | wc -l || echo "0")
87
+ TOTAL_LINES=$((TOTAL_LINES + NEW_LINES))
88
+ jq --argjson tl "$TOTAL_LINES" '.total_lines = $tl' "$STATE_FILE" > "${STATE_FILE}.tmp" && mv "${STATE_FILE}.tmp" "$STATE_FILE"
89
+ fi
90
+
91
+ # Count unique files
92
+ FILE_COUNT=$(jq '.files | length' "$STATE_FILE" 2>/dev/null || echo "0")
93
+
94
+ # Decision logic
95
+ if [ "$FILE_COUNT" -ge "$BLOCK_FILES" ]; then
96
+ MSG="SCR violation: ${FILE_COUNT} files modified (limit: ${BLOCK_FILES}). Split into smaller tasks."
97
+ echo "{\"hookSpecificOutput\":{\"permissionDecision\":\"deny\",\"message\":\"${MSG}\"}}"
98
+ exit 0
99
+ fi
100
+
101
+ if [ "$FILE_COUNT" -ge "$WARN_FILES" ]; then
102
+ MSG="SCR warning: ${FILE_COUNT} files modified. Consider splitting this task."
103
+ echo "{\"hookSpecificOutput\":{\"permissionDecision\":\"allow\",\"message\":\"${MSG}\"}}"
104
+ exit 0
105
+ fi
106
+
107
+ if [ "$TOTAL_LINES" -ge "$WARN_LINES" ]; then
108
+ MSG="Large change: ~${TOTAL_LINES} lines modified across ${FILE_COUNT} files."
109
+ echo "{\"hookSpecificOutput\":{\"permissionDecision\":\"allow\",\"message\":\"${MSG}\"}}"
110
+ exit 0
111
+ fi
112
+
113
+ echo '{"hookSpecificOutput":{"permissionDecision":"allow"}}'
@@ -34,18 +34,44 @@ fi
34
34
 
35
35
  BLOCK_REASON=""
36
36
 
37
- # ── 1. Test execution gate (implementation phase only) ──
38
- # 자기 보고 금지: 테스트 exit code로 완료를 판정한다.
39
- # stop_hook_active=false일 때만 블로킹 (1회 기회 부여, 루프 방지는 섹션 0에서 처리)
37
+ # ── 1. Test execution gate ──
38
+ # 변경된 소스 파일에 대한 테스트 실행 여부 확인
39
+ # implementation phase에서만 block, 외에는 warning
40
40
  PHASE_FILE="$PROJECT_ROOT/.timsquad/state/current-phase.json"
41
+ PHASE="unknown"
41
42
  if [ -f "$PHASE_FILE" ]; then
42
43
  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] 이번 세션에서 테스트가 실행되지 않았습니다. 프로젝트의 테스트를 실행하여 변경사항을 검증한 후 완료하세요."
44
+ fi
45
+
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")
51
+
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 테스트를 실행한 후 완료하세요."
49
75
  fi
50
76
  fi
51
77
  fi
@@ -79,15 +105,51 @@ if [ -f "$NOTES_FILE" ]; then
79
105
  fi
80
106
  fi
81
107
 
82
- # ── 4. Combine and output ──
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
117
+
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
130
+ fi
131
+
132
+ # ── 5. Combine and output ──
83
133
  # BLOCK_REASON이 있으면 decision:block으로 강제 속행 (세션 컨텍스트 포함)
84
134
  if [ -n "$BLOCK_REASON" ]; then
85
135
  FULL_REASON="$BLOCK_REASON"
86
- [ -n "$SESSION_CTX" ] && FULL_REASON="$BLOCK_REASON
136
+ [ -n "$SESSION_CTX" ] && FULL_REASON="$FULL_REASON
87
137
  $SESSION_CTX"
138
+ [ -n "$VERIFICATION" ] && FULL_REASON="$FULL_REASON
139
+ $VERIFICATION"
88
140
  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}'
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}'
91
153
  else
92
154
  echo '{}'
93
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"
@@ -55,7 +55,7 @@ fi
55
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
@@ -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