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.
- package/README.ko.md +288 -0
- package/README.md +158 -151
- package/dist/commands/compile.d.ts +3 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +170 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +95 -5
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/full.js +1 -0
- package/dist/commands/full.js.map +1 -1
- package/dist/commands/git/pr.js +6 -5
- package/dist/commands/git/pr.js.map +1 -1
- package/dist/commands/git/release.js +2 -7
- package/dist/commands/git/release.js.map +1 -1
- package/dist/commands/improve.js +2 -2
- package/dist/commands/improve.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/log.d.ts.map +1 -1
- package/dist/commands/log.js +2 -2
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/metrics.d.ts.map +1 -1
- package/dist/commands/metrics.js +6 -2
- package/dist/commands/metrics.js.map +1 -1
- package/dist/commands/retro.js +8 -8
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/session.js +3 -3
- package/dist/commands/session.js.map +1 -1
- package/dist/commands/skills.d.ts +12 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +228 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/status.js +1 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +23 -1
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/daemon/entry.js +3 -3
- package/dist/daemon/entry.js.map +1 -1
- package/dist/daemon/event-queue.d.ts.map +1 -1
- package/dist/daemon/event-queue.js +2 -2
- package/dist/daemon/event-queue.js.map +1 -1
- package/dist/daemon/index.d.ts +4 -2
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +214 -52
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/jsonl-watcher.d.ts +1 -0
- package/dist/daemon/jsonl-watcher.d.ts.map +1 -1
- package/dist/daemon/jsonl-watcher.js.map +1 -1
- package/dist/daemon/meta-cache.d.ts +1 -0
- package/dist/daemon/meta-cache.d.ts.map +1 -1
- package/dist/daemon/meta-cache.js +9 -0
- package/dist/daemon/meta-cache.js.map +1 -1
- package/dist/daemon/session-notes.d.ts +33 -0
- package/dist/daemon/session-notes.d.ts.map +1 -0
- package/dist/daemon/session-notes.js +74 -0
- package/dist/daemon/session-notes.js.map +1 -0
- package/dist/daemon/session-state.d.ts +27 -0
- package/dist/daemon/session-state.d.ts.map +1 -0
- package/dist/daemon/session-state.js +165 -0
- package/dist/daemon/session-state.js.map +1 -0
- package/dist/daemon/shutdown.d.ts.map +1 -1
- package/dist/daemon/shutdown.js +9 -1
- package/dist/daemon/shutdown.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-generator.d.ts +4 -0
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +52 -3
- package/dist/lib/agent-generator.js.map +1 -1
- package/dist/lib/compile-rules.d.ts +66 -0
- package/dist/lib/compile-rules.d.ts.map +1 -0
- package/dist/lib/compile-rules.js +114 -0
- package/dist/lib/compile-rules.js.map +1 -0
- package/dist/lib/compiler.d.ts +105 -0
- package/dist/lib/compiler.d.ts.map +1 -0
- package/dist/lib/compiler.js +368 -0
- package/dist/lib/compiler.js.map +1 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +8 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/project.d.ts.map +1 -1
- package/dist/lib/project.js +8 -3
- package/dist/lib/project.js.map +1 -1
- package/dist/lib/skill-generator.d.ts.map +1 -1
- package/dist/lib/skill-generator.js +22 -1
- package/dist/lib/skill-generator.js.map +1 -1
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +6 -0
- package/dist/lib/template.js.map +1 -1
- package/dist/types/config.d.ts +1 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +12 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/project.d.ts +1 -1
- package/dist/types/project.d.ts.map +1 -1
- package/dist/types/project.js +2 -0
- package/dist/types/project.js.map +1 -1
- package/package.json +4 -4
- package/templates/base/agents/base/tsq-architect.md +2 -2
- package/templates/base/agents/overlays/domain/mobile/_common.md +13 -0
- package/templates/base/knowledge/checklists/plan-quality.md +31 -0
- package/templates/base/knowledge/checklists/stability-verification.md +14 -0
- package/templates/base/skills/controller/SKILL.md +111 -0
- package/templates/base/skills/controller/references/README.md +35 -0
- package/templates/base/skills/controller/rules/README.md +18 -0
- package/templates/base/skills/mobile/dart/SKILL.md +69 -0
- package/templates/base/skills/mobile/dart/rules/async-patterns.md +112 -0
- package/templates/base/skills/mobile/dart/rules/code-style.md +96 -0
- package/templates/base/skills/mobile/dart/rules/null-safety.md +84 -0
- package/templates/base/skills/mobile/dart/rules/type-system.md +111 -0
- package/templates/base/skills/mobile/flutter/SKILL.md +89 -0
- package/templates/base/skills/mobile/flutter/ci-cd/SKILL.md +82 -0
- package/templates/base/skills/mobile/flutter/ci-cd/references/ci-cd-pipeline.md +314 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/code-signing.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/codemagic-setup.md +116 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/fastlane-setup.md +105 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/github-actions.md +112 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/store-deployment.md +106 -0
- package/templates/base/skills/mobile/flutter/ci-cd/rules/versioning.md +107 -0
- package/templates/base/skills/mobile/flutter/i18n/SKILL.md +78 -0
- package/templates/base/skills/mobile/flutter/i18n/references/i18n-architecture.md +225 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/arb-files.md +182 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/locale-switching.md +226 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/localization-setup.md +137 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/plural-gender.md +159 -0
- package/templates/base/skills/mobile/flutter/i18n/rules/text-direction.md +199 -0
- package/templates/base/skills/mobile/flutter/monitoring/SKILL.md +81 -0
- package/templates/base/skills/mobile/flutter/monitoring/references/monitoring-architecture.md +269 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/analytics.md +227 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/crashlytics-setup.md +195 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/logging.md +258 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/performance-monitoring.md +248 -0
- package/templates/base/skills/mobile/flutter/monitoring/rules/sentry-integration.md +249 -0
- package/templates/base/skills/mobile/flutter/networking/SKILL.md +88 -0
- package/templates/base/skills/mobile/flutter/networking/references/api-client-architecture.md +305 -0
- package/templates/base/skills/mobile/flutter/networking/rules/caching.md +212 -0
- package/templates/base/skills/mobile/flutter/networking/rules/connectivity.md +213 -0
- package/templates/base/skills/mobile/flutter/networking/rules/dio-setup.md +159 -0
- package/templates/base/skills/mobile/flutter/networking/rules/error-handling.md +209 -0
- package/templates/base/skills/mobile/flutter/networking/rules/interceptors.md +205 -0
- package/templates/base/skills/mobile/flutter/networking/rules/retrofit-patterns.md +194 -0
- package/templates/base/skills/mobile/flutter/push-notifications/SKILL.md +87 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/notification-architecture.md +340 -0
- package/templates/base/skills/mobile/flutter/push-notifications/references/platform-setup.md +286 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/background-processing.md +308 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/deep-linking.md +217 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/fcm-setup.md +164 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/local-notifications.md +262 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-handling.md +210 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/notification-permissions.md +246 -0
- package/templates/base/skills/mobile/flutter/push-notifications/rules/rich-notifications.md +320 -0
- package/templates/base/skills/mobile/flutter/references/freezed-patterns.md +162 -0
- package/templates/base/skills/mobile/flutter/references/project-structure.md +170 -0
- package/templates/base/skills/mobile/flutter/rules/animations.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/architecture.md +121 -0
- package/templates/base/skills/mobile/flutter/rules/navigation-routing.md +117 -0
- package/templates/base/skills/mobile/flutter/rules/performance.md +112 -0
- package/templates/base/skills/mobile/flutter/rules/platform-adaptive.md +126 -0
- package/templates/base/skills/mobile/flutter/rules/state-management.md +110 -0
- package/templates/base/skills/mobile/flutter/rules/testing.md +131 -0
- package/templates/base/skills/mobile/flutter/rules/widget-conventions.md +122 -0
- package/templates/base/skills/mobile/flutter/security/SKILL.md +86 -0
- package/templates/base/skills/mobile/flutter/security/references/mobile-security-checklist.md +168 -0
- package/templates/base/skills/mobile/flutter/security/rules/api-key-protection.md +206 -0
- package/templates/base/skills/mobile/flutter/security/rules/authentication.md +248 -0
- package/templates/base/skills/mobile/flutter/security/rules/data-protection.md +271 -0
- package/templates/base/skills/mobile/flutter/security/rules/obfuscation.md +213 -0
- package/templates/base/skills/mobile/flutter/security/rules/secure-storage.md +171 -0
- package/templates/base/skills/mobile/flutter/security/rules/ssl-pinning.md +197 -0
- package/templates/base/skills/stability-verification/SKILL.md +64 -0
- package/templates/base/skills/stability-verification/references/release-checklist.md +34 -0
- package/templates/base/skills/stability-verification/references/security-fix-patterns.md +112 -0
- package/templates/base/skills/stability-verification/rules/verification-layers.md +67 -0
- package/templates/base/skills/stability-verification/rules/verification-workflow.md +69 -0
- package/templates/base/skills/stability-verification/scripts/verify.sh +294 -0
- package/templates/platforms/claude-code/CLAUDE.md.template +25 -0
- package/templates/platforms/claude-code/rules/build-gate.md +28 -0
- package/templates/platforms/claude-code/rules/completion-verification.md +30 -0
- package/templates/platforms/claude-code/rules/context-monitor.md +23 -0
- package/templates/platforms/claude-code/rules/plan-review.md +45 -0
- package/templates/platforms/claude-code/rules/quality-guards.md +43 -0
- package/templates/platforms/claude-code/rules/session-notes.md +18 -0
- package/templates/platforms/claude-code/rules/skill-suggest.md +27 -0
- package/templates/platforms/claude-code/scripts/build-gate.sh +73 -0
- package/templates/platforms/claude-code/scripts/completion-guard.sh +93 -0
- package/templates/platforms/claude-code/scripts/phase-guard.sh +79 -0
- package/templates/platforms/claude-code/scripts/safe-guard.sh +83 -0
- package/templates/platforms/claude-code/scripts/skill-rules.json +85 -0
- package/templates/platforms/claude-code/scripts/skill-suggest.sh +105 -0
- package/templates/platforms/claude-code/settings.json +111 -3
- package/templates/project-types/mobile-app/config.yaml +123 -0
- 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
|
|
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
|
|
20
|
-
"timeout":
|
|
127
|
+
"command": "tsq daemon notify session-end 2>/dev/null || true",
|
|
128
|
+
"timeout": 5
|
|
21
129
|
}
|
|
22
130
|
]
|
|
23
131
|
}
|