41 Commits

Author SHA1 Message Date
Affaan Mustafa
fdea3085a7 Merge pull request #428 from zdocapp/zh-CN-pr
docs(zh-CN): sync Chinese docs with latest upstream changes
2026-03-13 06:05:52 -07:00
neo
4c0107a322 docs(zh-CN): update 2026-03-13 17:45:44 +08:00
Affaan Mustafa
f548ca3e19 Merge pull request #427 from affaan-m/codex/orchestration-harness-skills
fix: harden observe loop prevention
2026-03-13 02:14:40 -07:00
Affaan Mustafa
5e481879ca fix: harden observe loop prevention 2026-03-13 01:16:45 -07:00
Affaan Mustafa
cc9b11d163 Merge pull request #392 from hahmee/docs/add-korean-translation
Docs/add korean translation
2026-03-13 00:23:44 -07:00
Affaan Mustafa
bfc802204e Merge pull request #403 from swarnika-cmd/main
fix: background observer fails closed on confirmation/permission prompts (#400)
2026-03-13 00:17:56 -07:00
Affaan Mustafa
fb7b73a962 docs: address Korean translation review feedback 2026-03-13 00:17:54 -07:00
Affaan Mustafa
4de5da2f8f Merge pull request #309 from cookiee339/feat/kotlin-ecosystem
feat(kotlin): add Kotlin/Ktor/Exposed ecosystem
2026-03-13 00:01:06 -07:00
Affaan Mustafa
1c1a9ef73e Merge branch 'main' into main 2026-03-13 00:00:34 -07:00
Affaan Mustafa
e043a2824a fix: harden observer prompt guard handling 2026-03-12 23:59:01 -07:00
Affaan Mustafa
3010f75297 Merge pull request #409 from pangerlkr/main
fix: refresh markdown docs and Windows hook test handling
2026-03-12 23:55:59 -07:00
Affaan Mustafa
99d443b16e fix: align kotlin diagnostics and heading hierarchy 2026-03-12 23:53:23 -07:00
avesh-h
bc21e7adba feat: add /aside command (#407)
* Introduces /aside — a mid-task side conversation command inspired by
  Claude Code's native /btw feature. Allows users to ask a question while
  Claude is actively working without losing task context or touching any files.

  Key behaviors:
  - Freezes current task state before answering (read-only during aside)
  - Delivers answers in a consistent ASIDE / Back to task format
  - Auto-resumes the active task after answering
  - Handles edge cases: no question given, answer reveals a blocker,
    question implies a task redirect, chained asides, ambiguous questions,
    and answers that suggest code changes without making them

* Two documentation inconsistencies fixed:

* Fixed 4 pre-existing lint errors in skills/videodb/ that were causing  CI to fail across all PR checks:  - api-reference.md: add blockquote continuation line to fix MD028  - capture-reference.md: wrap bare URL to fix MD034  - SKILL.md: wrap bare URL to fix MD034
2026-03-12 23:52:46 -07:00
Affaan Mustafa
240d553443 Merge branch 'main' into main 2026-03-12 23:52:10 -07:00
Affaan Mustafa
e692a2886c fix: address kotlin doc review feedback 2026-03-12 23:47:17 -07:00
ispaydeu
a6f380fde0 feat: active hours + idle detection gates for session-guardian (#413)
* feat: add project cooldown log to prevent rapid observer re-spawn

Adds session-guardian.sh, called by observer-loop.sh before each Haiku
spawn. It reads ~/.claude/observer-last-run.log and blocks the cycle if
the same project was observed within OBSERVER_INTERVAL_SECONDS (default
300s).

Prevents self-referential loops where a spawned session triggers
observe.sh, which signals the observer before the cooldown has elapsed.

Uses a mkdir-based lock for safe concurrent access across multiple
simultaneously-observed projects. Log entries use tab-delimited format
to handle paths containing spaces. Fails open on lock contention.

Config:
  OBSERVER_INTERVAL_SECONDS   default: 300
  OBSERVER_LAST_RUN_LOG       default: ~/.claude/observer-last-run.log

No external dependencies. Works on macOS, Linux, Windows (Git Bash/MSYS2).

* feat: extend session-guardian with time window and idle detection gates

Adds Gate 1 (active hours check) and Gate 3 (system idle detection) to
session-guardian.sh, building on the per-project cooldown log from PR 1.

Gate 1 — Time Window:
- OBSERVER_ACTIVE_HOURS_START/END (default 800–2300 local time)
- Uses date +%k%M with 10# prefix to avoid octal crash at midnight
- Toolless on all platforms; set both vars to 0 to disable

Gate 3 — Idle Detection:
- macOS: ioreg + awk (built-in, no deps)
- Linux: xprintidle if available, else fail open
- Windows (Git Bash/MSYS2): PowerShell GetLastInputInfo via Add-Type
- Unknown/headless: always returns 0 (fail open)
- OBSERVER_MAX_IDLE_SECONDS=0 disables gate

Fixes in this commit:
- 10# base-10 prefix prevents octal arithmetic crash on midnight minutes
  containing digits 8 or 9 (e.g. 00:08 = "008" is invalid octal)
- PowerShell output piped through tr -d '\r' to strip Windows CRLF;
  also uses [long] cast to avoid TickCount 32-bit overflow after 24 days
- mktemp now uses log file directory instead of TMPDIR to ensure
  same-filesystem mv on Linux (atomic rename instead of copy+unlink)
- mkdir -p failure exits 0 (fail open) rather than crashing under set -e
- Numeric validation on last_spawn prevents arithmetic error on corrupt log

Gate execution order: 1 (time, ~0ms) → 2 (cooldown, ~1ms) → 3 (idle, ~50ms)

* fix: harden session guardian gates

---------

Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
2026-03-12 23:44:34 -07:00
ispaydeu
c52a28ace9 fix(observe): 5-layer automated session guard to prevent self-loop observations (#399)
* fix(observe): add 5-layer automated session guard to prevent self-loop observations

observe.sh currently fires for ALL hook events including automated/programmatic
sessions: the ECC observer's own Haiku analysis runs, claude-mem observer
sessions, CI pipelines, and any other tool that spawns `claude --print`.

This causes an infinite feedback loop where automated sessions generate
observations that trigger more automated analysis, burning Haiku tokens with
no human activity.

Add a 5-layer guard block after the `disabled` check:

Layer 1: agent_id payload field — only present in subagent hooks; skip any
         subagent-scoped session (always automated by definition).

Layer 2: CLAUDE_CODE_ENTRYPOINT env var — Claude Code sets this to sdk-ts,
         sdk-py, sdk-cli, mcp, or remote for programmatic/SDK invocations.
         Skip if any non-cli entrypoint is detected. This is universal: catches
         any tool using the Anthropic SDK without requiring tool cooperation.

Layer 3: ECC_HOOK_PROFILE=minimal — existing ECC mechanism; respect it here
         to suppress non-essential hooks in observer contexts.

Layer 4: ECC_SKIP_OBSERVE=1 — cooperative env var any external tool can set
         before spawning automated sessions (explicit opt-out contract).

Layer 5: CWD path exclusions — skip sessions whose working directory matches
         known observer-session path patterns. Configurable via
         ECC_OBSERVE_SKIP_PATHS (comma-separated substrings, default:
         "observer-sessions,.claude-mem").

Also fix observer-loop.sh to set ECC_SKIP_OBSERVE=1 and ECC_HOOK_PROFILE=minimal
before spawning the Haiku analysis subprocess, making the observer loop
self-aware and closing the ECC→ECC self-observation loop without needing
external coordination.

Fixes: observe.sh fires unconditionally on automated sessions (#398)

* fix(observe): address review feedback — reorder guards cheapest-first, fix empty pattern bug

Two issues flagged by Copilot and CodeRabbit in PR #399:

1. Layer ordering: the agent_id check spawns a Python subprocess but ran
   before the cheap env-var checks (CLAUDE_CODE_ENTRYPOINT, ECC_HOOK_PROFILE,
   ECC_SKIP_OBSERVE). Reorder to put all env-var checks first (Layers 1-3),
   then the subprocess-requiring agent_id check (Layer 4). Automated sessions
   that set env vars — the common case — now exit without spawning Python.

2. Empty pattern bug in Layer 5: if ECC_OBSERVE_SKIP_PATHS contains a trailing
   comma or spaces after commas (e.g. "path1, path2" or "path1,"), _pattern
   becomes empty or whitespace-only, and the glob *""* matches every CWD,
   silently disabling all observations. Fix: trim leading/trailing whitespace
   from each pattern and skip empty patterns with `continue`.

* fix: fail closed for non-cli entrypoints

---------

Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
2026-03-12 23:40:03 -07:00
Jinyi_Yang
83f6d5679c feat(skills): add prompt-optimizer skill and /prompt-optimize command (#418)
* feat(skills): add prompt-optimizer skill and /prompt-optimize command

Adds a prompt-optimizer skill that analyzes draft prompts, matches them
to ECC components (skills/commands/agents), and outputs a ready-to-paste
optimized prompt. Advisory role only — never executes the task.

Features:
- 6-phase analysis pipeline (project detection, intent, scope, component
  matching, missing context, workflow + model recommendation)
- Auto-detects project tech stack from package.json, go.mod, etc.
- Maps intents to ECC commands, skills, and agents by type and tech stack
- Recommends correct model tier (Sonnet vs Opus) based on task complexity
- Outputs Full + Quick versions of the optimized prompt
- Hard gate: never executes the task, only produces advisory output
- AskUserQuestion trigger when 3+ critical context items are missing
- Multi-prompt splitting guidance for HIGH/EPIC scope tasks
- Feedback footer for iterative refinement

Also adds /prompt-optimize command as an explicit invocation entry point.

* fix: keep prompt optimizer advisory-only

* fix: refine prompt optimizer guidance

---------

Co-authored-by: Affaan Mustafa <affaan@dcube.ai>
2026-03-12 23:40:02 -07:00
Frank
c5acb5ac32 fix: accept shorter mixed-case session IDs (#408) 2026-03-12 23:29:50 -07:00
Pangerkumzuk Longkumer
c1bff00d1f Merge pull request #16 from pangerlkr/copilot/fix-failing-checks
Fix Windows CI: skip bash-path-incompatible test on win32
2026-03-12 14:39:13 +05:30
copilot-swe-agent[bot]
27b537d568 fix: skip detect-project bash test on Windows (path backslash incompatibility)
Co-authored-by: pangerlkr <73515951+pangerlkr@users.noreply.github.com>
2026-03-12 09:08:08 +00:00
copilot-swe-agent[bot]
2c726244ca Initial plan 2026-03-12 08:45:44 +00:00
Pangerkumzuk Longkumer
2856b79591 Merge pull request #15 from pangerlkr/copilot/fix-link-not-working
Fix markdownlint errors introduced by merge of affaan-m:main
2026-03-12 14:15:20 +05:30
copilot-swe-agent[bot]
b0bc3dc0c9 Fix markdownlint errors from merge of affaan-m:main into main
Co-authored-by: pangerlkr <73515951+pangerlkr@users.noreply.github.com>
2026-03-12 08:21:41 +00:00
copilot-swe-agent[bot]
db89e7bcd0 Initial plan 2026-03-12 08:14:36 +00:00
Pangerkumzuk Longkumer
8627cd07e7 Merge branch 'affaan-m:main' into main 2026-03-12 11:40:41 +05:30
swarnika-cmd
96708e5d45 fix: add confirmation-prompt guard to start-observer.sh (issue #400)
- Redirect observer output to temp log before appending to main log
- Check temp log for confirmation/permission language immediately after start
- Fail closed with exit 2 if detected, preventing retry loops
2026-03-12 06:52:54 +05:30
swarnika-cmd
8079d354d1 fix: observer fails closed on confirmation/permission prompts (issue #400) 2026-03-12 06:46:42 +05:30
Affaan Mustafa
135eb4c98d feat: add kotlin commands and skill pack 2026-03-10 21:25:52 -07:00
hahmee
526a9070e6 docs(ko-KR): add Korean translation for examples
Translate 6 CLAUDE.md examples (project, user, SaaS Next.js, Django API,
Go microservice, Rust API) and copy statusline.json config.
2026-03-10 17:09:23 +09:00
hahmee
3144b96faa docs(ko-KR): add Korean terminology glossary
Add TERMINOLOGY.md with translation conventions and term mappings
to ensure consistency across all 58 translated files.
2026-03-10 14:28:14 +09:00
hahmee
3e9c207c25 docs(ko-KR): complete all command translations with full examples
Add missing example sessions, code blocks, and detailed sections
to 14 command files that were previously summarized versions.
2026-03-10 13:59:43 +09:00
hahmee
cbe2e68c26 docs(ko-KR): complete missing sections in code-reviewer and planner translations
- code-reviewer: add code examples (deep nesting, useEffect deps, key props,
  N+1 queries), Project-Specific Guidelines section, cost-awareness check
- planner: add Worked Example (Stripe Subscriptions) and Red Flags sections
2026-03-10 13:39:16 +09:00
hahmee
b3f8206d47 docs(ko-KR): add Korean translation for skills
- 15 skill categories (17 files): coding-standards, tdd-workflow,
  frontend-patterns, backend-patterns, security-review (2 files),
  postgres-patterns, verification-loop, continuous-learning,
  continuous-learning-v2, eval-harness, iterative-retrieval,
  strategic-compact, golang-patterns, golang-testing, clickhouse-io,
  project-guidelines-example
2026-03-10 13:29:00 +09:00
hahmee
a693d2e023 docs(ko-KR): add Korean translation for commands and agents
- commands: 18 files (build-fix, checkpoint, code-review, e2e, eval,
  go-build, go-review, go-test, learn, orchestrate, plan, refactor-clean,
  setup-pm, tdd, test-coverage, update-codemaps, update-docs, verify)
- agents: 12 files (architect, build-error-resolver, code-reviewer,
  database-reviewer, doc-updater, e2e-runner, go-build-resolver,
  go-reviewer, planner, refactor-cleaner, security-reviewer, tdd-guide)
2026-03-10 12:56:11 +09:00
hahmee
b390fd141d docs(ko-KR): add Korean translation for rules 2026-03-08 18:00:43 +09:00
hahmee
cb56d1a22d docs: add Korean (ko-KR) README and CONTRIBUTING translation 2026-03-08 17:58:02 +09:00
Pangerkumzuk Longkumer
c1954aee72 Merge branch 'main' into main 2026-02-28 07:08:10 +05:30
Pangerkumzuk Longkumer
1cda15440a Merge pull request #13 from pangerlkr/claude/fix-all-workflows
fix: remove malformed workflow and fix hooks.json regex escaping
2026-02-25 20:01:13 +05:30
anthropic-code-agent[bot]
264b44f617 fix: remove malformed copilot-setup-steps.yml and fix hooks.json regex
Co-authored-by: pangerlkr <73515951+pangerlkr@users.noreply.github.com>
2026-02-25 14:27:33 +00:00
anthropic-code-agent[bot]
2652578aa4 Initial plan 2026-02-25 14:19:05 +00:00
188 changed files with 35416 additions and 416 deletions

View File

@@ -0,0 +1,39 @@
---
description: "Kotlin coding style extending common rules"
globs: ["**/*.kt", "**/*.kts", "**/build.gradle.kts"]
alwaysApply: false
---
# Kotlin Coding Style
> This file extends the common coding style rule with Kotlin-specific content.
## Formatting
- Auto-formatting via **ktfmt** or **ktlint** (configured in `kotlin-hooks.md`)
- Use trailing commas in multiline declarations
## Immutability
The global immutability requirement is enforced in the common coding style rule.
For Kotlin specifically:
- Prefer `val` over `var`
- Use immutable collection types (`List`, `Map`, `Set`)
- Use `data class` with `copy()` for immutable updates
## Null Safety
- Avoid `!!` -- use `?.`, `?:`, `require`, or `checkNotNull`
- Handle platform types explicitly at Java interop boundaries
## Expression Bodies
Prefer expression bodies for single-expression functions:
```kotlin
fun isAdult(age: Int): Boolean = age >= 18
```
## Reference
See skill: `kotlin-patterns` for comprehensive Kotlin idioms and patterns.

View File

@@ -0,0 +1,16 @@
---
description: "Kotlin hooks extending common rules"
globs: ["**/*.kt", "**/*.kts", "**/build.gradle.kts"]
alwaysApply: false
---
# Kotlin Hooks
> This file extends the common hooks rule with Kotlin-specific content.
## PostToolUse Hooks
Configure in `~/.claude/settings.json`:
- **ktfmt/ktlint**: Auto-format `.kt` and `.kts` files after edit
- **detekt**: Run static analysis after editing Kotlin files
- **./gradlew build**: Verify compilation after changes

View File

@@ -0,0 +1,50 @@
---
description: "Kotlin patterns extending common rules"
globs: ["**/*.kt", "**/*.kts", "**/build.gradle.kts"]
alwaysApply: false
---
# Kotlin Patterns
> This file extends the common patterns rule with Kotlin-specific content.
## Sealed Classes
Use sealed classes/interfaces for exhaustive type hierarchies:
```kotlin
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Failure(val error: AppError) : Result<Nothing>()
}
```
## Extension Functions
Add behavior without inheritance, scoped to where they're used:
```kotlin
fun String.toSlug(): String =
lowercase().replace(Regex("[^a-z0-9\\s-]"), "").replace(Regex("\\s+"), "-")
```
## Scope Functions
- `let`: Transform nullable or scoped result
- `apply`: Configure an object
- `also`: Side effects
- Avoid nesting scope functions
## Dependency Injection
Use Koin for DI in Ktor projects:
```kotlin
val appModule = module {
single<UserRepository> { ExposedUserRepository(get()) }
single { UserService(get()) }
}
```
## Reference
See skill: `kotlin-patterns` for comprehensive Kotlin patterns including coroutines, DSL builders, and delegation.

View File

@@ -0,0 +1,58 @@
---
description: "Kotlin security extending common rules"
globs: ["**/*.kt", "**/*.kts", "**/build.gradle.kts"]
alwaysApply: false
---
# Kotlin Security
> This file extends the common security rule with Kotlin-specific content.
## Secret Management
```kotlin
val apiKey = System.getenv("API_KEY")
?: throw IllegalStateException("API_KEY not configured")
```
## SQL Injection Prevention
Always use Exposed's parameterized queries:
```kotlin
// Good: Parameterized via Exposed DSL
UsersTable.selectAll().where { UsersTable.email eq email }
// Bad: String interpolation in raw SQL
exec("SELECT * FROM users WHERE email = '$email'")
```
## Authentication
Use Ktor's Auth plugin with JWT:
```kotlin
install(Authentication) {
jwt("jwt") {
verifier(
JWT.require(Algorithm.HMAC256(secret))
.withAudience(audience)
.withIssuer(issuer)
.build()
)
validate { credential ->
val payload = credential.payload
if (payload.audience.contains(audience) &&
payload.issuer == issuer &&
payload.subject != null) {
JWTPrincipal(payload)
} else {
null
}
}
}
}
```
## Null Safety as Security
Kotlin's type system prevents null-related vulnerabilities -- avoid `!!` to maintain this guarantee.

View File

@@ -0,0 +1,38 @@
---
description: "Kotlin testing extending common rules"
globs: ["**/*.kt", "**/*.kts", "**/build.gradle.kts"]
alwaysApply: false
---
# Kotlin Testing
> This file extends the common testing rule with Kotlin-specific content.
## Framework
Use **Kotest** with spec styles (StringSpec, FunSpec, BehaviorSpec) and **MockK** for mocking.
## Coroutine Testing
Use `runTest` from `kotlinx-coroutines-test`:
```kotlin
test("async operation completes") {
runTest {
val result = service.fetchData()
result.shouldNotBeEmpty()
}
}
```
## Coverage
Use **Kover** for coverage reporting:
```bash
./gradlew koverHtmlReport
./gradlew koverVerify
```
## Reference
See skill: `kotlin-testing` for detailed Kotest patterns, MockK usage, and property-based testing.

View File

@@ -116,7 +116,7 @@ the community.
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html>.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
@@ -124,5 +124,5 @@ enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
<https://www.contributor-covenant.org/faq>. Translations are available at
<https://www.contributor-covenant.org/translations>.

View File

@@ -1,4 +1,4 @@
**Language:** English | [繁體中文](docs/zh-TW/README.md)
**Language:** English | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
# Everything Claude Code
@@ -25,7 +25,7 @@
**🌐 Language / 语言 / 語言**
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md)
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
</div>

View File

@@ -14,7 +14,7 @@
**🌐 Language / 语言 / 語言**
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md)
[**English**](README.md) | [简体中文](README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md) | [한국어](docs/ko-KR/README.md)
</div>

View File

@@ -0,0 +1,118 @@
---
name: kotlin-build-resolver
description: Kotlin/Gradle build, compilation, and dependency error resolution specialist. Fixes build errors, Kotlin compiler errors, and Gradle issues with minimal changes. Use when Kotlin builds fail.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# Kotlin Build Error Resolver
You are an expert Kotlin/Gradle build error resolution specialist. Your mission is to fix Kotlin build errors, Gradle configuration issues, and dependency resolution failures with **minimal, surgical changes**.
## Core Responsibilities
1. Diagnose Kotlin compilation errors
2. Fix Gradle build configuration issues
3. Resolve dependency conflicts and version mismatches
4. Handle Kotlin compiler errors and warnings
5. Fix detekt and ktlint violations
## Diagnostic Commands
Run these in order:
```bash
./gradlew build 2>&1
./gradlew detekt 2>&1 || echo "detekt not configured"
./gradlew ktlintCheck 2>&1 || echo "ktlint not configured"
./gradlew dependencies --configuration runtimeClasspath 2>&1 | head -100
```
## Resolution Workflow
```text
1. ./gradlew build -> Parse error message
2. Read affected file -> Understand context
3. Apply minimal fix -> Only what's needed
4. ./gradlew build -> Verify fix
5. ./gradlew test -> Ensure nothing broke
```
## Common Fix Patterns
| Error | Cause | Fix |
|-------|-------|-----|
| `Unresolved reference: X` | Missing import, typo, missing dependency | Add import or dependency |
| `Type mismatch: Required X, Found Y` | Wrong type, missing conversion | Add conversion or fix type |
| `None of the following candidates is applicable` | Wrong overload, wrong argument types | Fix argument types or add explicit cast |
| `Smart cast impossible` | Mutable property or concurrent access | Use local `val` copy or `let` |
| `'when' expression must be exhaustive` | Missing branch in sealed class `when` | Add missing branches or `else` |
| `Suspend function can only be called from coroutine` | Missing `suspend` or coroutine scope | Add `suspend` modifier or launch coroutine |
| `Cannot access 'X': it is internal in 'Y'` | Visibility issue | Change visibility or use public API |
| `Conflicting declarations` | Duplicate definitions | Remove duplicate or rename |
| `Could not resolve: group:artifact:version` | Missing repository or wrong version | Add repository or fix version |
| `Execution failed for task ':detekt'` | Code style violations | Fix detekt findings |
## Gradle Troubleshooting
```bash
# Check dependency tree for conflicts
./gradlew dependencies --configuration runtimeClasspath
# Force refresh dependencies
./gradlew build --refresh-dependencies
# Clear project-local Gradle build cache
./gradlew clean && rm -rf .gradle/build-cache/
# Check Gradle version compatibility
./gradlew --version
# Run with debug output
./gradlew build --debug 2>&1 | tail -50
# Check for dependency conflicts
./gradlew dependencyInsight --dependency <name> --configuration runtimeClasspath
```
## Kotlin Compiler Flags
```kotlin
// build.gradle.kts - Common compiler options
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xjsr305=strict") // Strict Java null safety
allWarningsAsErrors = true
}
}
```
## Key Principles
- **Surgical fixes only** -- don't refactor, just fix the error
- **Never** suppress warnings without explicit approval
- **Never** change function signatures unless necessary
- **Always** run `./gradlew build` after each fix to verify
- Fix root cause over suppressing symptoms
- Prefer adding missing imports over wildcard imports
## Stop Conditions
Stop and report if:
- Same error persists after 3 fix attempts
- Fix introduces more errors than it resolves
- Error requires architectural changes beyond scope
- Missing external dependencies that need user decision
## Output Format
```text
[FIXED] src/main/kotlin/com/example/service/UserService.kt:42
Error: Unresolved reference: UserRepository
Fix: Added import com.example.repository.UserRepository
Remaining errors: 2
```
Final: `Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list`
For detailed Kotlin patterns and code examples, see `skill: kotlin-patterns`.

164
commands/aside.md Normal file
View File

@@ -0,0 +1,164 @@
---
description: Answer a quick side question without interrupting or losing context from the current task. Resume work automatically after answering.
---
# Aside Command
Ask a question mid-task and get an immediate, focused answer — then continue right where you left off. The current task, files, and context are never modified.
## When to Use
- You're curious about something while Claude is working and don't want to lose momentum
- You need a quick explanation of code Claude is currently editing
- You want a second opinion or clarification on a decision without derailing the task
- You need to understand an error, concept, or pattern before Claude proceeds
- You want to ask something unrelated to the current task without starting a new session
## Usage
```
/aside <your question>
/aside what does this function actually return?
/aside is this pattern thread-safe?
/aside why are we using X instead of Y here?
/aside what's the difference between foo() and bar()?
/aside should we be worried about the N+1 query we just added?
```
## Process
### Step 1: Freeze the current task state
Before answering anything, mentally note:
- What is the active task? (what file, feature, or problem was being worked on)
- What step was in progress at the moment `/aside` was invoked?
- What was about to happen next?
Do NOT touch, edit, create, or delete any files during the aside.
### Step 2: Answer the question directly
Answer the question in the most concise form that is still complete and useful.
- Lead with the answer, not the reasoning
- Keep it short — if a full explanation is needed, offer to go deeper after the task
- If the question is about the current file or code being worked on, reference it precisely (file path and line number if relevant)
- If answering requires reading a file, read it — but read only, never write
Format the response as:
```
ASIDE: [restate the question briefly]
[Your answer here]
— Back to task: [one-line description of what was being done]
```
### Step 3: Resume the main task
After delivering the answer, immediately continue the active task from the exact point it was paused. Do not ask for permission to resume unless the aside answer revealed a blocker or a reason to reconsider the current approach (see Edge Cases).
---
## Edge Cases
**No question provided (`/aside` with nothing after it):**
Respond:
```
ASIDE: no question provided
What would you like to know? (ask your question and I'll answer without losing the current task context)
— Back to task: [one-line description of what was being done]
```
**Question reveals a potential problem with the current task:**
Flag it clearly before resuming:
```
ASIDE: [answer]
⚠️ Note: This answer suggests [issue] with the current approach. Want to address this before continuing, or proceed as planned?
```
Wait for the user's decision before resuming.
**Question is actually a task redirect (not a side question):**
If the question implies changing what is being built (e.g., `/aside actually, let's use Redis instead`), clarify:
```
ASIDE: That sounds like a direction change, not just a side question.
Do you want to:
(a) Answer this as information only and keep the current plan
(b) Pause the current task and change approach
```
Wait for the user's answer — do not make assumptions.
**Question is about the currently open file or code:**
Answer from the live context. If the file was read earlier in the session, reference it directly. If not, read it now (read-only) and answer with a file:line reference.
**No active task (nothing in progress when `/aside` is invoked):**
Still use the standard wrapper so the response shape stays consistent:
```
ASIDE: [restate the question briefly]
[Your answer here]
— Back to task: no active task to resume
```
**Question requires a long answer:**
Give the essential answer concisely, then offer:
```
That's the short version. Want a deeper explanation after we finish [current task]?
```
**Multiple `/aside` questions in a row:**
Answer each one in sequence. After the last answer, resume the main task. Do not lose task state across a chain of asides.
**Aside answer implies a code change is needed:**
Note the change needed but do not make it during the aside:
```
ASIDE: [answer]
📝 Worth fixing: [what should be changed]. I'll flag this after the current task unless you want to address it now.
```
**Question is ambiguous or too vague:**
Ask one clarifying question — the shortest question that gets the information needed to answer. Do not ask multiple questions.
---
## Example Output
```
User: /aside what does fetchWithRetry() actually do?
ASIDE: what does fetchWithRetry() do?
fetchWithRetry() (src/api/retry.ts:12) attempts the request up to 3 times with
exponential backoff (250ms → 500ms → 1s). It only retries on 5xx responses and
network errors — 4xx errors are treated as final and not retried.
— Back to task: refactoring the auth middleware in src/middleware/auth.ts
```
```
User: /aside is the approach we're taking thread-safe?
ASIDE: is the current approach thread-safe?
No — the shared cache object in src/cache/store.ts:34 is mutated without locking.
Under concurrent requests this is a race condition. It's low risk in a single-process
Node.js server but would be a real problem with worker threads or clustering.
⚠️ Note: This could affect the feature we're building. Want to address this now or continue and fix it in a follow-up?
```
---
## Notes
- Never modify files during an aside — read-only access only
- The aside is a conversation pause, not a new task — the original task must always resume
- Keep answers focused: the goal is to unblock the user quickly, not to deliver a lecture
- If an aside sparks a larger discussion, finish the current task first unless the aside reveals a blocker
- Asides are not saved to session files unless explicitly relevant to the task outcome

174
commands/kotlin-build.md Normal file
View File

@@ -0,0 +1,174 @@
---
description: Fix Kotlin/Gradle build errors, compiler warnings, and dependency issues incrementally. Invokes the kotlin-build-resolver agent for minimal, surgical fixes.
---
# Kotlin Build and Fix
This command invokes the **kotlin-build-resolver** agent to incrementally fix Kotlin build errors with minimal changes.
## What This Command Does
1. **Run Diagnostics**: Execute `./gradlew build`, `detekt`, `ktlintCheck`
2. **Parse Errors**: Group by file and sort by severity
3. **Fix Incrementally**: One error at a time
4. **Verify Each Fix**: Re-run build after each change
5. **Report Summary**: Show what was fixed and what remains
## When to Use
Use `/kotlin-build` when:
- `./gradlew build` fails with errors
- Kotlin compiler reports errors
- `./gradlew detekt` reports violations
- Gradle dependency resolution fails
- After pulling changes that break the build
## Diagnostic Commands Run
```bash
# Primary build check
./gradlew build 2>&1
# Static analysis
./gradlew detekt 2>&1 || echo "detekt not configured"
./gradlew ktlintCheck 2>&1 || echo "ktlint not configured"
# Dependency issues
./gradlew dependencies --configuration runtimeClasspath 2>&1 | head -100
# Optional deep refresh when caches or dependency metadata are suspect
./gradlew build --refresh-dependencies
```
## Example Session
````text
User: /kotlin-build
Agent:
# Kotlin Build Resolution
## Initial Diagnostics
```bash
$ ./gradlew build
e: src/main/kotlin/com/example/service/UserService.kt:25:15 Unresolved reference: UserRepository
e: src/main/kotlin/com/example/routes/UserRoutes.kt:42:9 Type mismatch: inferred type is String but Int was expected
e: src/main/kotlin/com/example/routes/UserRoutes.kt:58:5 'when' expression must be exhaustive
```
Errors found: 3
## Fix 1: Unresolved Reference
File: src/main/kotlin/com/example/service/UserService.kt:25
Error: Unresolved reference: UserRepository
Cause: Missing import
```kotlin
// Added import
import com.example.repository.UserRepository
```
```bash
$ ./gradlew build
# 2 errors remaining
```
## Fix 2: Type Mismatch
File: src/main/kotlin/com/example/routes/UserRoutes.kt:42
Error: Type mismatch: inferred type is String but Int was expected
```kotlin
// Changed
val count = call.parameters["count"]
// To
val count = call.parameters["count"]?.toIntOrNull()
?: return@get call.respond(HttpStatusCode.BadRequest, "Invalid count")
```
```bash
$ ./gradlew build
# 1 error remaining
```
## Fix 3: Non-Exhaustive When
File: src/main/kotlin/com/example/routes/UserRoutes.kt:58
Error: 'when' expression must be exhaustive
```kotlin
// Added missing branch
when (user.role) {
Role.ADMIN -> handleAdmin(user)
Role.USER -> handleUser(user)
Role.MODERATOR -> handleModerator(user) // Added
}
```
```bash
$ ./gradlew build
# Build successful!
```
## Final Verification
```bash
$ ./gradlew detekt
# No issues
$ ./gradlew test
# All tests passed
```
## Summary
| Metric | Count |
|--------|-------|
| Build errors fixed | 3 |
| Detekt issues fixed | 0 |
| Files modified | 2 |
| Remaining issues | 0 |
Build Status: ✅ SUCCESS
````
## Common Errors Fixed
| Error | Typical Fix |
|-------|-------------|
| `Unresolved reference: X` | Add import or dependency |
| `Type mismatch` | Fix type conversion or assignment |
| `'when' must be exhaustive` | Add missing sealed class branches |
| `Suspend function can only be called from coroutine` | Add `suspend` modifier |
| `Smart cast impossible` | Use local `val` or `let` |
| `None of the following candidates is applicable` | Fix argument types |
| `Could not resolve dependency` | Fix version or add repository |
## Fix Strategy
1. **Build errors first** - Code must compile
2. **Detekt violations second** - Fix code quality issues
3. **ktlint warnings third** - Fix formatting
4. **One fix at a time** - Verify each change
5. **Minimal changes** - Don't refactor, just fix
## Stop Conditions
The agent will stop and report if:
- Same error persists after 3 attempts
- Fix introduces more errors
- Requires architectural changes
- Missing external dependencies
## Related Commands
- `/kotlin-test` - Run tests after build succeeds
- `/kotlin-review` - Review code quality
- `/verify` - Full verification loop
## Related
- Agent: `agents/kotlin-build-resolver.md`
- Skill: `skills/kotlin-patterns/`

140
commands/kotlin-review.md Normal file
View File

@@ -0,0 +1,140 @@
---
description: Comprehensive Kotlin code review for idiomatic patterns, null safety, coroutine safety, and security. Invokes the kotlin-reviewer agent.
---
# Kotlin Code Review
This command invokes the **kotlin-reviewer** agent for comprehensive Kotlin-specific code review.
## What This Command Does
1. **Identify Kotlin Changes**: Find modified `.kt` and `.kts` files via `git diff`
2. **Run Build & Static Analysis**: Execute `./gradlew build`, `detekt`, `ktlintCheck`
3. **Security Scan**: Check for SQL injection, command injection, hardcoded secrets
4. **Null Safety Review**: Analyze `!!` usage, platform type handling, unsafe casts
5. **Coroutine Review**: Check structured concurrency, dispatcher usage, cancellation
6. **Generate Report**: Categorize issues by severity
## When to Use
Use `/kotlin-review` when:
- After writing or modifying Kotlin code
- Before committing Kotlin changes
- Reviewing pull requests with Kotlin code
- Onboarding to a new Kotlin codebase
- Learning idiomatic Kotlin patterns
## Review Categories
### CRITICAL (Must Fix)
- SQL/Command injection vulnerabilities
- Force-unwrap `!!` without justification
- Platform type null safety violations
- GlobalScope usage (structured concurrency violation)
- Hardcoded credentials
- Unsafe deserialization
### HIGH (Should Fix)
- Mutable state where immutable suffices
- Blocking calls inside coroutine context
- Missing cancellation checks in long loops
- Non-exhaustive `when` on sealed types
- Large functions (>50 lines)
- Deep nesting (>4 levels)
### MEDIUM (Consider)
- Non-idiomatic Kotlin (Java-style patterns)
- Missing trailing commas
- Scope function misuse or nesting
- Missing sequence for large collection chains
- Redundant explicit types
## Automated Checks Run
```bash
# Build check
./gradlew build
# Static analysis
./gradlew detekt
# Formatting check
./gradlew ktlintCheck
# Tests
./gradlew test
```
## Example Usage
````text
User: /kotlin-review
Agent:
# Kotlin Code Review Report
## Files Reviewed
- src/main/kotlin/com/example/service/UserService.kt (modified)
- src/main/kotlin/com/example/routes/UserRoutes.kt (modified)
## Static Analysis Results
✓ Build: Successful
✓ detekt: No issues
⚠ ktlint: 2 formatting warnings
## Issues Found
[CRITICAL] Force-Unwrap Null Safety
File: src/main/kotlin/com/example/service/UserService.kt:28
Issue: Using !! on nullable repository result
```kotlin
val user = repository.findById(id)!! // NPE risk
```
Fix: Use safe call with error handling
```kotlin
val user = repository.findById(id)
?: throw UserNotFoundException("User $id not found")
```
[HIGH] GlobalScope Usage
File: src/main/kotlin/com/example/routes/UserRoutes.kt:45
Issue: Using GlobalScope breaks structured concurrency
```kotlin
GlobalScope.launch {
notificationService.sendWelcome(user)
}
```
Fix: Use the call's coroutine scope
```kotlin
launch {
notificationService.sendWelcome(user)
}
```
## Summary
- CRITICAL: 1
- HIGH: 1
- MEDIUM: 0
Recommendation: ❌ Block merge until CRITICAL issue is fixed
````
## Approval Criteria
| Status | Condition |
|--------|-----------|
| ✅ Approve | No CRITICAL or HIGH issues |
| ⚠️ Warning | Only MEDIUM issues (merge with caution) |
| ❌ Block | CRITICAL or HIGH issues found |
## Integration with Other Commands
- Use `/kotlin-test` first to ensure tests pass
- Use `/kotlin-build` if build errors occur
- Use `/kotlin-review` before committing
- Use `/code-review` for non-Kotlin-specific concerns
## Related
- Agent: `agents/kotlin-reviewer.md`
- Skills: `skills/kotlin-patterns/`, `skills/kotlin-testing/`

312
commands/kotlin-test.md Normal file
View File

@@ -0,0 +1,312 @@
---
description: Enforce TDD workflow for Kotlin. Write Kotest tests first, then implement. Verify 80%+ coverage with Kover.
---
# Kotlin TDD Command
This command enforces test-driven development methodology for Kotlin code using Kotest, MockK, and Kover.
## What This Command Does
1. **Define Types/Interfaces**: Scaffold function signatures first
2. **Write Kotest Tests**: Create comprehensive test specs (RED)
3. **Run Tests**: Verify tests fail for the right reason
4. **Implement Code**: Write minimal code to pass (GREEN)
5. **Refactor**: Improve while keeping tests green
6. **Check Coverage**: Ensure 80%+ coverage with Kover
## When to Use
Use `/kotlin-test` when:
- Implementing new Kotlin functions or classes
- Adding test coverage to existing Kotlin code
- Fixing bugs (write failing test first)
- Building critical business logic
- Learning TDD workflow in Kotlin
## TDD Cycle
```
RED -> Write failing Kotest test
GREEN -> Implement minimal code to pass
REFACTOR -> Improve code, tests stay green
REPEAT -> Next test case
```
## Example Session
````text
User: /kotlin-test I need a function to validate user registration requests
Agent:
# TDD Session: Registration Validator
## Step 1: Define Interface
```kotlin
// validator/RegistrationValidator.kt
package com.example.validator
data class RegistrationRequest(
val name: String,
val email: String,
val password: String,
)
sealed class ValidationResult {
data object Valid : ValidationResult()
data class Invalid(val errors: List<String>) : ValidationResult()
}
fun validateRegistration(request: RegistrationRequest): ValidationResult {
TODO("not implemented")
}
```
## Step 2: Write Kotest Tests (RED)
```kotlin
// validator/RegistrationValidatorTest.kt
package com.example.validator
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
class RegistrationValidatorTest : FunSpec({
test("valid registration returns Valid") {
val request = RegistrationRequest(
name = "Alice",
email = "alice@example.com",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
result.shouldBeInstanceOf<ValidationResult.Valid>()
}
test("blank name returns Invalid") {
val request = RegistrationRequest(
name = "",
email = "alice@example.com",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Name is required")
}
test("invalid email returns Invalid") {
val request = RegistrationRequest(
name = "Alice",
email = "not-an-email",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Invalid email format")
}
test("short password returns Invalid") {
val request = RegistrationRequest(
name = "Alice",
email = "alice@example.com",
password = "short",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Password must be at least 8 characters")
}
test("multiple errors returns all errors") {
val request = RegistrationRequest(
name = "",
email = "bad",
password = "short",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors.size shouldBe 3
}
})
```
## Step 3: Run Tests - Verify FAIL
```bash
$ ./gradlew test
RegistrationValidatorTest > valid registration returns Valid FAILED
kotlin.NotImplementedError: An operation is not implemented
FAILED (5 tests, 0 passed, 5 failed)
```
✓ Tests fail as expected (NotImplementedError).
## Step 4: Implement Minimal Code (GREEN)
```kotlin
// validator/RegistrationValidator.kt
package com.example.validator
private val EMAIL_REGEX = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
private const val MIN_PASSWORD_LENGTH = 8
fun validateRegistration(request: RegistrationRequest): ValidationResult {
val errors = buildList {
if (request.name.isBlank()) add("Name is required")
if (!EMAIL_REGEX.matches(request.email)) add("Invalid email format")
if (request.password.length < MIN_PASSWORD_LENGTH) add("Password must be at least $MIN_PASSWORD_LENGTH characters")
}
return if (errors.isEmpty()) ValidationResult.Valid
else ValidationResult.Invalid(errors)
}
```
## Step 5: Run Tests - Verify PASS
```bash
$ ./gradlew test
RegistrationValidatorTest > valid registration returns Valid PASSED
RegistrationValidatorTest > blank name returns Invalid PASSED
RegistrationValidatorTest > invalid email returns Invalid PASSED
RegistrationValidatorTest > short password returns Invalid PASSED
RegistrationValidatorTest > multiple errors returns all errors PASSED
PASSED (5 tests, 5 passed, 0 failed)
```
✓ All tests passing!
## Step 6: Check Coverage
```bash
$ ./gradlew koverHtmlReport
Coverage: 100.0% of statements
```
✓ Coverage: 100%
## TDD Complete!
````
## Test Patterns
### StringSpec (Simplest)
```kotlin
class CalculatorTest : StringSpec({
"add two positive numbers" {
Calculator.add(2, 3) shouldBe 5
}
})
```
### BehaviorSpec (BDD)
```kotlin
class OrderServiceTest : BehaviorSpec({
Given("a valid order") {
When("placed") {
Then("should be confirmed") { /* ... */ }
}
}
})
```
### Data-Driven Tests
```kotlin
class ParserTest : FunSpec({
context("valid inputs") {
withData("2026-01-15", "2026-12-31", "2000-01-01") { input ->
parseDate(input).shouldNotBeNull()
}
}
})
```
### Coroutine Testing
```kotlin
class AsyncServiceTest : FunSpec({
test("concurrent fetch completes") {
runTest {
val result = service.fetchAll()
result.shouldNotBeEmpty()
}
}
})
```
## Coverage Commands
```bash
# Run tests with coverage
./gradlew koverHtmlReport
# Verify coverage thresholds
./gradlew koverVerify
# XML report for CI
./gradlew koverXmlReport
# Open HTML report
open build/reports/kover/html/index.html
# Run specific test class
./gradlew test --tests "com.example.UserServiceTest"
# Run with verbose output
./gradlew test --info
```
## Coverage Targets
| Code Type | Target |
|-----------|--------|
| Critical business logic | 100% |
| Public APIs | 90%+ |
| General code | 80%+ |
| Generated code | Exclude |
## TDD Best Practices
**DO:**
- Write test FIRST, before any implementation
- Run tests after each change
- Use Kotest matchers for expressive assertions
- Use MockK's `coEvery`/`coVerify` for suspend functions
- Test behavior, not implementation details
- Include edge cases (empty, null, max values)
**DON'T:**
- Write implementation before tests
- Skip the RED phase
- Test private functions directly
- Use `Thread.sleep()` in coroutine tests
- Ignore flaky tests
## Related Commands
- `/kotlin-build` - Fix build errors
- `/kotlin-review` - Review code after implementation
- `/verify` - Run full verification loop
## Related
- Skill: `skills/kotlin-testing/`
- Skill: `skills/tdd-workflow/`

View File

@@ -0,0 +1,38 @@
---
description: Analyze a draft prompt and output an optimized, ECC-enriched version ready to paste and run. Does NOT execute the task — outputs advisory analysis only.
---
# /prompt-optimize
Analyze and optimize the following prompt for maximum ECC leverage.
## Your Task
Apply the **prompt-optimizer** skill to the user's input below. Follow the 6-phase analysis pipeline:
0. **Project Detection** — Read CLAUDE.md, detect tech stack from project files (package.json, go.mod, pyproject.toml, etc.)
1. **Intent Detection** — Classify the task type (new feature, bug fix, refactor, research, testing, review, documentation, infrastructure, design)
2. **Scope Assessment** — Evaluate complexity (TRIVIAL / LOW / MEDIUM / HIGH / EPIC), using codebase size as signal if detected
3. **ECC Component Matching** — Map to specific skills, commands, agents, and model tier
4. **Missing Context Detection** — Identify gaps. If 3+ critical items missing, ask the user to clarify before generating
5. **Workflow & Model** — Determine lifecycle position, recommend model tier, and split into multiple prompts if HIGH/EPIC
## Output Requirements
- Present diagnosis, recommended ECC components, and an optimized prompt using the Output Format from the prompt-optimizer skill
- Provide both **Full Version** (detailed) and **Quick Version** (compact, varied by intent type)
- Respond in the same language as the user's input
- The optimized prompt must be complete and ready to copy-paste into a new session
- End with a footer offering adjustment or a clear next step for starting a separate execution request
## CRITICAL
Do NOT execute the user's task. Output ONLY the analysis and optimized prompt.
If the user asks for direct execution, explain that `/prompt-optimize` only produces advisory output and tell them to start a normal task request instead.
Note: `blueprint` is a **skill**, not a slash command. Write "Use the blueprint skill"
instead of presenting it as a `/...` command.
## User Input
$ARGUMENTS

View File

@@ -1,4 +1,4 @@
**言語:** English | [简体中文](../../README.zh-CN.md) | [繁體中文](docs/zh-TW/README.md) | [日本語](docs/ja-JP/README.md)
**言語:** English | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](README.md) | [한국어](../ko-KR/README.md)
# Everything Claude Code

View File

@@ -581,7 +581,7 @@ LOGGING = {
| 強力なシークレット | SECRET_KEYに環境変数を使用 |
| パスワード検証 | すべてのパスワードバリデータを有効化 |
| CSRF保護 | デフォルトで有効、無効にしない |
| XSS防止 | Djangoは自動エスケープ、ユーザー入力で`|safe`を使用しない |
| XSS防止 | Djangoは自動エスケープ、ユーザー入力で<code>\|safe</code>を使用しない |
| SQLインジェクション | ORMを使用、クエリで文字列を連結しない |
| ファイルアップロード | ファイルタイプとサイズを検証 |
| レート制限 | APIエンドポイントをスロットル |

453
docs/ko-KR/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,453 @@
# Everything Claude Code에 기여하기
기여에 관심을 가져주셔서 감사합니다! 이 저장소는 Claude Code 사용자를 위한 커뮤니티 리소스입니다.
## 목차
- [우리가 찾는 것](#우리가-찾는-것)
- [빠른 시작](#빠른-시작)
- [스킬 기여하기](#스킬-기여하기)
- [에이전트 기여하기](#에이전트-기여하기)
- [훅 기여하기](#훅-기여하기)
- [커맨드 기여하기](#커맨드-기여하기)
- [Pull Request 프로세스](#pull-request-프로세스)
---
## 우리가 찾는 것
### 에이전트
특정 작업을 잘 처리하는 새로운 에이전트:
- 언어별 리뷰어 (Python, Go, Rust)
- 프레임워크 전문가 (Django, Rails, Laravel, Spring)
- DevOps 전문가 (Kubernetes, Terraform, CI/CD)
- 도메인 전문가 (ML 파이프라인, 데이터 엔지니어링, 모바일)
### 스킬
워크플로우 정의와 도메인 지식:
- 언어 모범 사례
- 프레임워크 패턴
- 테스팅 전략
- 아키텍처 가이드
### 훅
유용한 자동화:
- 린팅/포매팅 훅
- 보안 검사
- 유효성 검증 훅
- 알림 훅
### 커맨드
유용한 워크플로우를 호출하는 슬래시 커맨드:
- 배포 커맨드
- 테스팅 커맨드
- 코드 생성 커맨드
---
## 빠른 시작
```bash
# 1. 포크 및 클론
gh repo fork affaan-m/everything-claude-code --clone
cd everything-claude-code
# 2. 브랜치 생성
git checkout -b feat/my-contribution
# 3. 기여 항목 추가 (아래 섹션 참고)
# 4. 로컬 테스트
cp -r skills/my-skill ~/.claude/skills/ # 스킬의 경우
# 그런 다음 Claude Code로 테스트
# 5. PR 제출
git add . && git commit -m "feat: add my-skill" && git push -u origin feat/my-contribution
```
---
## 스킬 기여하기
스킬은 Claude Code가 컨텍스트에 따라 로드하는 지식 모듈입니다.
### 디렉토리 구조
```
skills/
└── your-skill-name/
└── SKILL.md
```
### SKILL.md 템플릿
```markdown
---
name: your-skill-name
description: 스킬 목록에 표시되는 간단한 설명
origin: ECC
---
# 스킬 제목
이 스킬이 다루는 내용에 대한 간단한 개요.
## 핵심 개념
주요 패턴과 가이드라인 설명.
## 코드 예제
\`\`\`typescript
// 실용적이고 테스트된 예제 포함
function example() {
// 잘 주석 처리된 코드
}
\`\`\`
## 모범 사례
- 실행 가능한 가이드라인
- 해야 할 것과 하지 말아야 할 것
- 흔한 실수 방지
## 사용 시점
이 스킬이 적용되는 시나리오 설명.
```
### 스킬 체크리스트
- [ ] 하나의 도메인/기술에 집중
- [ ] 실용적인 코드 예제 포함
- [ ] 500줄 미만
- [ ] 명확한 섹션 헤더 사용
- [ ] Claude Code에서 테스트 완료
### 스킬 예시
| 스킬 | 용도 |
|------|------|
| `coding-standards/` | TypeScript/JavaScript 패턴 |
| `frontend-patterns/` | React와 Next.js 모범 사례 |
| `backend-patterns/` | API와 데이터베이스 패턴 |
| `security-review/` | 보안 체크리스트 |
---
## 에이전트 기여하기
에이전트는 Task 도구를 통해 호출되는 전문 어시스턴트입니다.
### 파일 위치
```
agents/your-agent-name.md
```
### 에이전트 템플릿
```markdown
---
name: your-agent-name
description: 이 에이전트가 하는 일과 Claude가 언제 호출해야 하는지. 구체적으로 작성!
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
당신은 [역할] 전문가입니다.
## 역할
- 주요 책임
- 부차적 책임
- 하지 않는 것 (경계)
## 워크플로우
### 1단계: 이해
작업에 접근하는 방법.
### 2단계: 실행
작업을 수행하는 방법.
### 3단계: 검증
결과를 검증하는 방법.
## 출력 형식
사용자에게 반환하는 것.
## 예제
### 예제: [시나리오]
입력: [사용자가 제공하는 것]
행동: [수행하는 것]
출력: [반환하는 것]
```
### 에이전트 필드
| 필드 | 설명 | 옵션 |
|------|------|------|
| `name` | 소문자, 하이픈 연결 | `code-reviewer` |
| `description` | 호출 시점 결정에 사용 | 구체적으로 작성! |
| `tools` | 필요한 것만 포함 | `Read, Write, Edit, Bash, Grep, Glob, WebFetch, Task` |
| `model` | 복잡도 수준 | `haiku` (단순), `sonnet` (코딩), `opus` (복잡) |
### 예시 에이전트
| 에이전트 | 용도 |
|----------|------|
| `tdd-guide.md` | 테스트 주도 개발 |
| `code-reviewer.md` | 코드 리뷰 |
| `security-reviewer.md` | 보안 점검 |
| `build-error-resolver.md` | 빌드 오류 수정 |
---
## 훅 기여하기
훅은 Claude Code 이벤트에 의해 트리거되는 자동 동작입니다.
### 파일 위치
```
hooks/hooks.json
```
### 훅 유형
| 유형 | 트리거 시점 | 사용 사례 |
|------|-----------|----------|
| `PreToolUse` | 도구 실행 전 | 유효성 검증, 경고, 차단 |
| `PostToolUse` | 도구 실행 후 | 포매팅, 검사, 알림 |
| `SessionStart` | 세션 시작 시 | 컨텍스트 로딩 |
| `Stop` | 세션 종료 시 | 정리, 감사 |
### 훅 형식
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"rm -rf /\"",
"hooks": [
{
"type": "command",
"command": "echo '[Hook] BLOCKED: Dangerous command' && exit 1"
}
],
"description": "위험한 rm 명령 차단"
}
]
}
}
```
### Matcher 문법
```javascript
// 특정 도구 매칭
tool == "Bash"
tool == "Edit"
tool == "Write"
// 입력 패턴 매칭
tool_input.command matches "npm install"
tool_input.file_path matches "\\.tsx?$"
// 조건 결합
tool == "Bash" && tool_input.command matches "git push"
```
### 훅 예시
```json
// tmux 밖 dev 서버 차단
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"npm run dev\"",
"hooks": [{"type": "command", "command": "echo '개발 서버는 tmux에서 실행하세요' && exit 1"}],
"description": "dev 서버를 tmux에서 실행하도록 강제"
}
// TypeScript 편집 후 자동 포맷
{
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\.tsx?$\"",
"hooks": [{"type": "command", "command": "npx prettier --write \"$file_path\""}],
"description": "TypeScript 파일 편집 후 포맷"
}
// git push 전 경고
{
"matcher": "tool == \"Bash\" && tool_input.command matches \"git push\"",
"hooks": [{"type": "command", "command": "echo '[Hook] push 전에 변경사항을 다시 검토하세요'"}],
"description": "push 전 검토 리마인더"
}
```
### 훅 체크리스트
- [ ] Matcher가 구체적 (너무 광범위하지 않게)
- [ ] 명확한 오류/정보 메시지 포함
- [ ] 올바른 종료 코드 사용 (`exit 1`은 차단, `exit 0`은 허용)
- [ ] 충분한 테스트 완료
- [ ] 설명 포함
---
## 커맨드 기여하기
커맨드는 `/command-name`으로 사용자가 호출하는 액션입니다.
### 파일 위치
```
commands/your-command.md
```
### 커맨드 템플릿
```markdown
---
description: /help에 표시되는 간단한 설명
---
# 커맨드 이름
## 목적
이 커맨드가 수행하는 작업.
## 사용법
\`\`\`
/your-command [args]
\`\`\`
## 워크플로우
1. 첫 번째 단계
2. 두 번째 단계
3. 마지막 단계
## 출력
사용자가 받는 결과.
```
### 커맨드 예시
| 커맨드 | 용도 |
|--------|------|
| `commit.md` | Git 커밋 생성 |
| `code-review.md` | 코드 변경사항 리뷰 |
| `tdd.md` | TDD 워크플로우 |
| `e2e.md` | E2E 테스팅 |
---
## 크로스-하네스 및 번역
### 스킬 서브셋 (Codex 및 Cursor)
ECC는 다른 하네스를 위한 스킬 서브셋도 제공합니다:
- **Codex:** `.agents/skills/``agents/openai.yaml`에 나열된 스킬이 Codex에서 로드됩니다.
- **Cursor:** `.cursor/skills/` — Cursor용 스킬 서브셋이 별도로 포함됩니다.
Codex 또는 Cursor에서도 제공해야 하는 **새 스킬**을 추가한다면:
1. 먼저 `skills/your-skill-name/` 아래에 일반적인 ECC 스킬로 추가합니다.
2. **Codex**에서도 제공해야 하면 `.agents/skills/`에 반영하고, 필요하면 `agents/openai.yaml`에도 참조를 추가합니다.
3. **Cursor**에서도 제공해야 하면 Cursor 레이아웃에 맞게 `.cursor/skills/` 아래에 추가합니다.
기존 디렉터리의 구조를 확인한 뒤 같은 패턴을 따르세요. 이 서브셋 동기화는 수동이므로 PR 설명에 반영 여부를 적어 두는 것이 좋습니다.
### 번역
번역 문서는 `docs/` 아래에 있습니다. 예: `docs/zh-CN`, `docs/zh-TW`, `docs/ja-JP`.
번역된 에이전트, 커맨드, 스킬을 변경한다면:
- 대응하는 번역 파일도 함께 업데이트하거나
- 유지보수자/번역자가 후속 작업을 할 수 있도록 이슈를 열어 주세요.
---
## Pull Request 프로세스
### 1. PR 제목 형식
```
feat(skills): add rust-patterns skill
feat(agents): add api-designer agent
feat(hooks): add auto-format hook
fix(skills): update React patterns
docs: improve contributing guide
```
### 2. PR 설명
```markdown
## 요약
무엇을 추가했고 왜 필요한지.
## 유형
- [ ] 스킬
- [ ] 에이전트
- [ ]
- [ ] 커맨드
## 테스트
어떻게 테스트했는지.
## 체크리스트
- [ ] 형식 가이드라인 준수
- [ ] Claude Code에서 테스트 완료
- [ ] 민감한 정보 없음 (API 키, 경로)
- [ ] 명확한 설명 포함
```
### 3. 리뷰 프로세스
1. 메인테이너가 48시간 이내에 리뷰
2. 피드백이 있으면 수정 반영
3. 승인되면 main에 머지
---
## 가이드라인
### 해야 할 것
- 기여를 집중적이고 모듈화되게 유지
- 명확한 설명 포함
- 제출 전 테스트
- 기존 패턴 따르기
- 의존성 문서화
### 하지 말아야 할 것
- 민감한 데이터 포함 (API 키, 토큰, 경로)
- 지나치게 복잡하거나 특수한 설정 추가
- 테스트하지 않은 기여 제출
- 기존 기능과 중복되는 것 생성
---
## 파일 이름 규칙
- 소문자에 하이픈 사용: `python-reviewer.md`
- 설명적으로 작성: `workflow.md`가 아닌 `tdd-workflow.md`
- name과 파일명을 일치시키기
---
## 질문이 있으신가요?
- **이슈:** [github.com/affaan-m/everything-claude-code/issues](https://github.com/affaan-m/everything-claude-code/issues)
- **X/Twitter:** [@affaanmustafa](https://x.com/affaanmustafa)
---
기여해 주셔서 감사합니다! 함께 훌륭한 리소스를 만들어 갑시다.

731
docs/ko-KR/README.md Normal file
View File

@@ -0,0 +1,731 @@
**언어:** [English](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | 한국어
# Everything Claude Code
[![Stars](https://img.shields.io/github/stars/affaan-m/everything-claude-code?style=flat)](https://github.com/affaan-m/everything-claude-code/stargazers)
[![Forks](https://img.shields.io/github/forks/affaan-m/everything-claude-code?style=flat)](https://github.com/affaan-m/everything-claude-code/network/members)
[![Contributors](https://img.shields.io/github/contributors/affaan-m/everything-claude-code?style=flat)](https://github.com/affaan-m/everything-claude-code/graphs/contributors)
[![npm ecc-universal](https://img.shields.io/npm/dw/ecc-universal?label=ecc-universal%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-universal)
[![npm ecc-agentshield](https://img.shields.io/npm/dw/ecc-agentshield?label=ecc-agentshield%20weekly%20downloads&logo=npm)](https://www.npmjs.com/package/ecc-agentshield)
[![GitHub App Install](https://img.shields.io/badge/GitHub%20App-150%20installs-2ea44f?logo=github)](https://github.com/marketplace/ecc-tools)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE)
![Shell](https://img.shields.io/badge/-Shell-4EAA25?logo=gnu-bash&logoColor=white)
![TypeScript](https://img.shields.io/badge/-TypeScript-3178C6?logo=typescript&logoColor=white)
![Python](https://img.shields.io/badge/-Python-3776AB?logo=python&logoColor=white)
![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go&logoColor=white)
![Java](https://img.shields.io/badge/-Java-ED8B00?logo=openjdk&logoColor=white)
![Markdown](https://img.shields.io/badge/-Markdown-000000?logo=markdown&logoColor=white)
> **50K+ stars** | **6K+ forks** | **30 contributors** | **6개 언어 지원** | **Anthropic 해커톤 우승**
---
<div align="center">
**🌐 Language / 语言 / 語言 / 언어**
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](README.md)
</div>
---
**AI 에이전트 하네스를 위한 성능 최적화 시스템. Anthropic 해커톤 우승자가 만들었습니다.**
단순한 설정 파일 모음이 아닙니다. 스킬, 직관(Instinct), 메모리 최적화, 지속적 학습, 보안 스캐닝, 리서치 우선 개발을 아우르는 완전한 시스템입니다. 10개월 이상 실제 프로덕트를 만들며 매일 집중적으로 사용해 발전시킨 프로덕션 레벨의 에이전트, 훅, 커맨드, 룰, MCP 설정이 포함되어 있습니다.
**Claude Code**, **Codex**, **Cowork** 등 다양한 AI 에이전트 하네스에서 사용할 수 있습니다.
---
## 가이드
이 저장소는 코드만 포함하고 있습니다. 가이드에서 모든 것을 설명합니다.
<table>
<tr>
<td width="50%">
<a href="https://x.com/affaanmustafa/status/2012378465664745795">
<img src="https://github.com/user-attachments/assets/1a471488-59cc-425b-8345-5245c7efbcef" alt="The Shorthand Guide to Everything Claude Code" />
</a>
</td>
<td width="50%">
<a href="https://x.com/affaanmustafa/status/2014040193557471352">
<img src="https://github.com/user-attachments/assets/c9ca43bc-b149-427f-b551-af6840c368f0" alt="The Longform Guide to Everything Claude Code" />
</a>
</td>
</tr>
<tr>
<td align="center"><b>요약 가이드</b><br/>설정, 기초, 철학. <b>이것부터 읽으세요.</b></td>
<td align="center"><b>상세 가이드</b><br/>토큰 최적화, 메모리 영속성, 평가, 병렬 처리.</td>
</tr>
</table>
| 주제 | 배울 수 있는 것 |
|------|----------------|
| 토큰 최적화 | 모델 선택, 시스템 프롬프트 최적화, 백그라운드 프로세스 |
| 메모리 영속성 | 세션 간 컨텍스트를 자동으로 저장/불러오는 훅 |
| 지속적 학습 | 세션에서 패턴을 자동 추출하여 재사용 가능한 스킬로 변환 |
| 검증 루프 | 체크포인트 vs 연속 평가, 채점 유형, pass@k 메트릭 |
| 병렬 처리 | Git worktree, 캐스케이드 방식, 인스턴스 확장 시점 |
| 서브에이전트 오케스트레이션 | 컨텍스트 문제, 반복 검색 패턴 |
---
## 새로운 소식
### v1.8.0 — 하네스 성능 시스템 (2026년 3월)
- **하네스 중심 릴리스** — ECC는 이제 단순 설정 모음이 아닌, 에이전트 하네스 성능 시스템으로 명시됩니다.
- **훅 안정성 개선** — SessionStart 루트 폴백, Stop 단계 세션 요약, 취약한 인라인 원라이너를 스크립트 기반 훅으로 교체.
- **훅 런타임 제어** — `ECC_HOOK_PROFILE=minimal|standard|strict``ECC_DISABLED_HOOKS=...`로 훅 파일 수정 없이 런타임 제어.
- **새 하네스 커맨드** — `/harness-audit`, `/loop-start`, `/loop-status`, `/quality-gate`, `/model-route`.
- **NanoClaw v2** — 모델 라우팅, 스킬 핫로드, 세션 분기/검색/내보내기/압축/메트릭.
- **크로스 하네스 호환성** — Claude Code, Cursor, OpenCode, Codex 간 동작 일관성 강화.
- **997개 내부 테스트 통과** — 훅/런타임 리팩토링 및 호환성 업데이트 후 전체 테스트 통과.
### v1.7.0 — 크로스 플랫폼 확장 & 프레젠테이션 빌더 (2026년 2월)
- **Codex 앱 + CLI 지원** — AGENTS.md 기반의 직접적인 Codex 지원
- **`frontend-slides` 스킬** — 의존성 없는 HTML 프레젠테이션 빌더
- **5개 신규 비즈니스/콘텐츠 스킬** — `article-writing`, `content-engine`, `market-research`, `investor-materials`, `investor-outreach`
- **992개 내부 테스트** — 확장된 검증 및 회귀 테스트 범위
### v1.6.0 — Codex CLI, AgentShield & 마켓플레이스 (2026년 2월)
- **Codex CLI 지원** — OpenAI Codex CLI 호환성을 위한 `/codex-setup` 커맨드
- **7개 신규 스킬** — `search-first`, `swift-actor-persistence`, `swift-protocol-di-testing`
- **AgentShield 통합** — `/security-scan`으로 Claude Code에서 직접 AgentShield 실행; 1282개 테스트, 102개 규칙
- **GitHub 마켓플레이스** — [github.com/marketplace/ecc-tools](https://github.com/marketplace/ecc-tools)에서 무료/프로/엔터프라이즈 티어 제공
- **30명 이상의 커뮤니티 기여** — 6개 언어에 걸친 30명의 기여자
- **978개 내부 테스트** — 에이전트, 스킬, 커맨드, 훅, 룰 전반에 걸친 검증
전체 변경 내역은 [Releases](https://github.com/affaan-m/everything-claude-code/releases)에서 확인하세요.
---
## 🚀 빠른 시작
2분 안에 설정 완료:
### 1단계: 플러그인 설치
```bash
# 마켓플레이스 추가
/plugin marketplace add affaan-m/everything-claude-code
# 플러그인 설치
/plugin install everything-claude-code@everything-claude-code
```
### 2단계: 룰 설치 (필수)
> ⚠️ **중요:** Claude Code 플러그인은 `rules`를 자동으로 배포할 수 없습니다. 수동으로 설치해야 합니다:
```bash
# 먼저 저장소 클론
git clone https://github.com/affaan-m/everything-claude-code.git
cd everything-claude-code
# 권장: 설치 스크립트 사용 (common + 언어별 룰을 안전하게 처리)
./install.sh typescript # 또는 python, golang
# 여러 언어를 한번에 설치할 수 있습니다:
# ./install.sh typescript python golang
# Cursor를 대상으로 설치:
# ./install.sh --target cursor typescript
```
수동 설치 방법은 `rules/` 폴더의 README를 참고하세요.
### 3단계: 사용 시작
```bash
# 커맨드 실행 (플러그인 설치 시 네임스페이스 형태 사용)
/everything-claude-code:plan "사용자 인증 추가"
# 수동 설치(옵션 2) 시에는 짧은 형태를 사용:
# /plan "사용자 인증 추가"
# 사용 가능한 커맨드 확인
/plugin list everything-claude-code@everything-claude-code
```
**끝!** 이제 16개 에이전트, 65개 스킬, 40개 커맨드를 사용할 수 있습니다.
---
## 🌐 크로스 플랫폼 지원
이 플러그인은 **Windows, macOS, Linux**를 완벽하게 지원하며, 주요 IDE(Cursor, OpenCode, Antigravity) 및 CLI 하네스와 긴밀하게 통합됩니다. 모든 훅과 스크립트는 최대 호환성을 위해 Node.js로 작성되었습니다.
### 패키지 매니저 감지
플러그인이 선호하는 패키지 매니저(npm, pnpm, yarn, bun)를 자동으로 감지합니다:
1. **환경 변수**: `CLAUDE_PACKAGE_MANAGER`
2. **프로젝트 설정**: `.claude/package-manager.json`
3. **package.json**: `packageManager` 필드
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb에서 감지
5. **글로벌 설정**: `~/.claude/package-manager.json`
6. **폴백**: `npm`
패키지 매니저 설정 방법:
```bash
# 환경 변수로 설정
export CLAUDE_PACKAGE_MANAGER=pnpm
# 글로벌 설정
node scripts/setup-package-manager.js --global pnpm
# 프로젝트 설정
node scripts/setup-package-manager.js --project bun
# 현재 설정 확인
node scripts/setup-package-manager.js --detect
```
또는 Claude Code에서 `/setup-pm` 커맨드를 사용하세요.
### 훅 런타임 제어
런타임 플래그로 엄격도를 조절하거나 특정 훅을 임시로 비활성화할 수 있습니다:
```bash
# 훅 엄격도 프로필 (기본값: standard)
export ECC_HOOK_PROFILE=standard
# 비활성화할 훅 ID (쉼표로 구분)
export ECC_DISABLED_HOOKS="pre:bash:tmux-reminder,post:edit:typecheck"
```
---
## 📦 구성 요소
이 저장소는 **Claude Code 플러그인**입니다 - 직접 설치하거나 컴포넌트를 수동으로 복사할 수 있습니다.
```
everything-claude-code/
|-- .claude-plugin/ # 플러그인 및 마켓플레이스 매니페스트
| |-- plugin.json # 플러그인 메타데이터와 컴포넌트 경로
| |-- marketplace.json # /plugin marketplace add용 마켓플레이스 카탈로그
|
|-- agents/ # 위임을 위한 전문 서브에이전트
| |-- planner.md # 기능 구현 계획
| |-- architect.md # 시스템 설계 의사결정
| |-- tdd-guide.md # 테스트 주도 개발
| |-- code-reviewer.md # 품질 및 보안 리뷰
| |-- security-reviewer.md # 취약점 분석
| |-- build-error-resolver.md
| |-- e2e-runner.md # Playwright E2E 테스팅
| |-- refactor-cleaner.md # 사용하지 않는 코드 정리
| |-- doc-updater.md # 문서 동기화
| |-- go-reviewer.md # Go 코드 리뷰
| |-- go-build-resolver.md # Go 빌드 에러 해결
| |-- python-reviewer.md # Python 코드 리뷰
| |-- database-reviewer.md # 데이터베이스/Supabase 리뷰
|
|-- skills/ # 워크플로우 정의와 도메인 지식
| |-- coding-standards/ # 언어 모범 사례
| |-- backend-patterns/ # API, 데이터베이스, 캐싱 패턴
| |-- frontend-patterns/ # React, Next.js 패턴
| |-- continuous-learning/ # 세션에서 패턴 자동 추출
| |-- continuous-learning-v2/ # 신뢰도 점수가 있는 직관 기반 학습
| |-- tdd-workflow/ # TDD 방법론
| |-- security-review/ # 보안 체크리스트
| |-- 그 외 다수...
|
|-- commands/ # 빠른 실행을 위한 슬래시 커맨드
| |-- tdd.md # /tdd - 테스트 주도 개발
| |-- plan.md # /plan - 구현 계획
| |-- e2e.md # /e2e - E2E 테스트 생성
| |-- code-review.md # /code-review - 품질 리뷰
| |-- build-fix.md # /build-fix - 빌드 에러 수정
| |-- 그 외 다수...
|
|-- rules/ # 항상 따르는 가이드라인 (~/.claude/rules/에 복사)
| |-- common/ # 언어 무관 원칙
| |-- typescript/ # TypeScript/JavaScript 전용
| |-- python/ # Python 전용
| |-- golang/ # Go 전용
|
|-- hooks/ # 트리거 기반 자동화
| |-- hooks.json # 모든 훅 설정
| |-- memory-persistence/ # 세션 라이프사이클 훅
|
|-- scripts/ # 크로스 플랫폼 Node.js 스크립트
|-- tests/ # 테스트 모음
|-- contexts/ # 동적 시스템 프롬프트 주입 컨텍스트
|-- examples/ # 예제 설정 및 세션
|-- mcp-configs/ # MCP 서버 설정
```
---
## 🛠️ 에코시스템 도구
### Skill Creator
저장소에서 Claude Code 스킬을 생성하는 두 가지 방법:
#### 옵션 A: 로컬 분석 (내장)
외부 서비스 없이 로컬에서 분석하려면 `/skill-create` 커맨드를 사용하세요:
```bash
/skill-create # 현재 저장소 분석
/skill-create --instincts # 직관(instincts)도 함께 생성
```
git 히스토리를 로컬에서 분석하여 SKILL.md 파일을 생성합니다.
#### 옵션 B: GitHub 앱 (고급)
고급 기능(10k+ 커밋, 자동 PR, 팀 공유)이 필요한 경우:
[GitHub 앱 설치](https://github.com/apps/skill-creator) | [ecc.tools](https://ecc.tools)
### AgentShield — 보안 감사 도구
> Claude Code 해커톤(Cerebral Valley x Anthropic, 2026년 2월)에서 개발. 1282개 테스트, 98% 커버리지, 102개 정적 분석 규칙.
Claude Code 설정에서 취약점, 잘못된 구성, 인젝션 위험을 스캔합니다.
```bash
# 빠른 스캔 (설치 불필요)
npx ecc-agentshield scan
# 안전한 문제 자동 수정
npx ecc-agentshield scan --fix
# 3개의 Opus 4.6 에이전트로 정밀 분석
npx ecc-agentshield scan --opus --stream
# 안전한 설정을 처음부터 생성
npx ecc-agentshield init
```
**스캔 대상:** CLAUDE.md, settings.json, MCP 설정, 훅, 에이전트 정의, 스킬 — 시크릿 감지(14개 패턴), 권한 감사, 훅 인젝션 분석, MCP 서버 위험 프로파일링, 에이전트 설정 검토의 5가지 카테고리.
**`--opus` 플래그**는 레드팀/블루팀/감사관 파이프라인으로 3개의 Claude Opus 4.6 에이전트를 실행합니다. 공격자가 익스플로잇 체인을 찾고, 방어자가 보호 조치를 평가하며, 감사관이 양쪽의 결과를 종합하여 우선순위가 매겨진 위험 평가를 작성합니다.
Claude Code에서 `/security-scan`을 사용하거나, [GitHub Action](https://github.com/affaan-m/agentshield)으로 CI에 추가하세요.
[GitHub](https://github.com/affaan-m/agentshield) | [npm](https://www.npmjs.com/package/ecc-agentshield)
### 🧠 지속적 학습 v2
직관(Instinct) 기반 학습 시스템이 여러분의 패턴을 자동으로 학습합니다:
```bash
/instinct-status # 학습된 직관과 신뢰도 확인
/instinct-import <file> # 다른 사람의 직관 가져오기
/instinct-export # 내 직관 내보내기
/evolve # 관련 직관을 스킬로 클러스터링
```
자세한 내용은 `skills/continuous-learning-v2/`를 참고하세요.
---
## 📋 요구 사항
### Claude Code CLI 버전
**최소 버전: v2.1.0 이상**
이 플러그인은 훅 시스템 변경으로 인해 Claude Code CLI v2.1.0 이상이 필요합니다.
버전 확인:
```bash
claude --version
```
### 중요: 훅 자동 로딩 동작
> ⚠️ **기여자 참고:** `.claude-plugin/plugin.json`에 `"hooks"` 필드를 추가하지 **마세요**. 회귀 테스트로 이를 강제합니다.
Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 **자동으로 로드**합니다. 명시적으로 선언하면 중복 감지 오류가 발생합니다.
---
## 📥 설치
### 옵션 1: 플러그인으로 설치 (권장)
```bash
# 마켓플레이스 추가
/plugin marketplace add affaan-m/everything-claude-code
# 플러그인 설치
/plugin install everything-claude-code@everything-claude-code
```
또는 `~/.claude/settings.json`에 직접 추가:
```json
{
"extraKnownMarketplaces": {
"everything-claude-code": {
"source": {
"source": "github",
"repo": "affaan-m/everything-claude-code"
}
}
},
"enabledPlugins": {
"everything-claude-code@everything-claude-code": true
}
}
```
> **참고:** Claude Code 플러그인 시스템은 `rules`를 플러그인으로 배포하는 것을 지원하지 않습니다. 룰은 수동으로 설치해야 합니다:
>
> ```bash
> git clone https://github.com/affaan-m/everything-claude-code.git
>
> # 옵션 A: 사용자 레벨 룰 (모든 프로젝트에 적용)
> mkdir -p ~/.claude/rules
> cp -r everything-claude-code/rules/common/* ~/.claude/rules/
> cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 사용하는 스택 선택
>
> # 옵션 B: 프로젝트 레벨 룰 (현재 프로젝트에만 적용)
> mkdir -p .claude/rules
> cp -r everything-claude-code/rules/common/* .claude/rules/
> ```
---
### 🔧 옵션 2: 수동 설치
설치할 항목을 직접 선택하고 싶다면:
```bash
# 저장소 클론
git clone https://github.com/affaan-m/everything-claude-code.git
# 에이전트 복사
cp everything-claude-code/agents/*.md ~/.claude/agents/
# 룰 복사 (common + 언어별)
cp -r everything-claude-code/rules/common/* ~/.claude/rules/
cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 사용하는 스택 선택
# 커맨드 복사
cp everything-claude-code/commands/*.md ~/.claude/commands/
# 스킬 복사
cp -r everything-claude-code/skills/* ~/.claude/skills/
cp -r everything-claude-code/skills/search-first ~/.claude/skills/
```
---
## 🎯 핵심 개념
### 에이전트
서브에이전트가 제한된 범위 내에서 위임된 작업을 처리합니다. 예시:
```markdown
---
name: code-reviewer
description: 코드의 품질, 보안, 유지보수성을 리뷰합니다
tools: ["Read", "Grep", "Glob", "Bash"]
model: opus
---
당신은 시니어 코드 리뷰어입니다...
```
### 스킬
스킬은 커맨드나 에이전트에 의해 호출되는 워크플로우 정의입니다:
```markdown
# TDD 워크플로우
1. 인터페이스를 먼저 정의
2. 실패하는 테스트 작성 (RED)
3. 최소한의 코드 구현 (GREEN)
4. 리팩토링 (IMPROVE)
5. 80% 이상 커버리지 확인
```
### 훅
훅은 도구 이벤트에 반응하여 실행됩니다. 예시 - console.log 경고:
```json
{
"matcher": "tool == \"Edit\" && tool_input.file_path matches \"\\\\.(ts|tsx|js|jsx)$\"",
"hooks": [{
"type": "command",
"command": "#!/bin/bash\ngrep -n 'console\\.log' \"$file_path\" && echo '[Hook] console.log를 제거하세요' >&2"
}]
}
```
### 룰
룰은 항상 따라야 하는 가이드라인으로, `common/`(언어 무관) + 언어별 디렉토리로 구성됩니다:
```
rules/
common/ # 보편적 원칙 (항상 설치)
typescript/ # TS/JS 전용 패턴과 도구
python/ # Python 전용 패턴과 도구
golang/ # Go 전용 패턴과 도구
```
자세한 내용은 [`rules/README.md`](../../rules/README.md)를 참고하세요.
---
## 🗺️ 어떤 에이전트를 사용해야 할까?
어디서 시작해야 할지 모르겠다면 이 참고표를 보세요:
| 하고 싶은 것 | 사용할 커맨드 | 사용되는 에이전트 |
|-------------|-------------|-----------------|
| 새 기능 계획하기 | `/everything-claude-code:plan "인증 추가"` | planner |
| 시스템 아키텍처 설계 | `/everything-claude-code:plan` + architect 에이전트 | architect |
| 테스트를 먼저 작성하며 코딩 | `/tdd` | tdd-guide |
| 방금 작성한 코드 리뷰 | `/code-review` | code-reviewer |
| 빌드 실패 수정 | `/build-fix` | build-error-resolver |
| E2E 테스트 실행 | `/e2e` | e2e-runner |
| 보안 취약점 찾기 | `/security-scan` | security-reviewer |
| 사용하지 않는 코드 제거 | `/refactor-clean` | refactor-cleaner |
| 문서 업데이트 | `/update-docs` | doc-updater |
| Go 빌드 실패 수정 | `/go-build` | go-build-resolver |
| Go 코드 리뷰 | `/go-review` | go-reviewer |
| 데이터베이스 스키마/쿼리 리뷰 | `/code-review` + database-reviewer 에이전트 | database-reviewer |
| Python 코드 리뷰 | `/python-review` | python-reviewer |
### 일반적인 워크플로우
**새로운 기능 시작:**
```
/everything-claude-code:plan "OAuth를 사용한 사용자 인증 추가"
→ planner가 구현 청사진 작성
/tdd → tdd-guide가 테스트 먼저 작성 강제
/code-review → code-reviewer가 코드 검토
```
**버그 수정:**
```
/tdd → tdd-guide: 버그를 재현하는 실패 테스트 작성
→ 수정 구현, 테스트 통과 확인
/code-review → code-reviewer: 회귀 검사
```
**프로덕션 준비:**
```
/security-scan → security-reviewer: OWASP Top 10 감사
/e2e → e2e-runner: 핵심 사용자 흐름 테스트
/test-coverage → 80% 이상 커버리지 확인
```
---
## ❓ FAQ
<details>
<summary><b>설치된 에이전트/커맨드 확인은 어떻게 하나요?</b></summary>
```bash
/plugin list everything-claude-code@everything-claude-code
```
플러그인에서 사용할 수 있는 모든 에이전트, 커맨드, 스킬을 보여줍니다.
</details>
<details>
<summary><b>훅이 작동하지 않거나 "Duplicate hooks file" 오류가 보여요</b></summary>
가장 흔한 문제입니다. `.claude-plugin/plugin.json``"hooks"` 필드를 **추가하지 마세요.** Claude Code v2.1+는 설치된 플러그인의 `hooks/hooks.json`을 자동으로 로드합니다.
</details>
<details>
<summary><b>컨텍스트 윈도우가 줄어들어요 / Claude가 컨텍스트가 부족해요</b></summary>
MCP 서버가 너무 많으면 컨텍스트를 잡아먹습니다. 각 MCP 도구 설명이 200k 윈도우에서 토큰을 소비하여 ~70k까지 줄어들 수 있습니다.
**해결:** 프로젝트별로 사용하지 않는 MCP를 비활성화하세요:
```json
// 프로젝트의 .claude/settings.json에서
{
"disabledMcpServers": ["supabase", "railway", "vercel"]
}
```
10개 미만의 MCP와 80개 미만의 도구를 활성화 상태로 유지하세요.
</details>
<details>
<summary><b>일부 컴포넌트만 사용할 수 있나요? (예: 에이전트만)</b></summary>
네. 옵션 2(수동 설치)를 사용하여 필요한 것만 복사하세요:
```bash
# 에이전트만
cp everything-claude-code/agents/*.md ~/.claude/agents/
# 룰만
cp -r everything-claude-code/rules/common/* ~/.claude/rules/
```
각 컴포넌트는 완전히 독립적입니다.
</details>
<details>
<summary><b>Cursor / OpenCode / Codex / Antigravity에서도 작동하나요?</b></summary>
네. ECC는 크로스 플랫폼입니다:
- **Cursor**: `.cursor/`에 변환된 설정 제공
- **OpenCode**: `.opencode/`에 전체 플러그인 지원
- **Codex**: macOS 앱과 CLI 모두 퍼스트클래스 지원
- **Antigravity**: `.agent/`에 워크플로우, 스킬, 평탄화된 룰 통합
- **Claude Code**: 네이티브 — 이것이 주 타겟입니다
</details>
<details>
<summary><b>새 스킬이나 에이전트를 기여하고 싶어요</b></summary>
[CONTRIBUTING.md](../../CONTRIBUTING.md)를 참고하세요. 간단히 말하면:
1. 저장소를 포크
2. `skills/your-skill-name/SKILL.md`에 스킬 생성 (YAML frontmatter 포함)
3. 또는 `agents/your-agent.md`에 에이전트 생성
4. 명확한 설명과 함께 PR 제출
</details>
---
## 🧪 테스트 실행
```bash
# 모든 테스트 실행
node tests/run-all.js
# 개별 테스트 파일 실행
node tests/lib/utils.test.js
node tests/lib/package-manager.test.js
node tests/hooks/hooks.test.js
```
---
## 🤝 기여하기
**기여를 환영합니다.**
이 저장소는 커뮤니티 리소스로 만들어졌습니다. 가지고 계신 것이 있다면:
- 유용한 에이전트나 스킬
- 멋진 훅
- 더 나은 MCP 설정
- 개선된 룰
기여해 주세요! 가이드라인은 [CONTRIBUTING.md](../../CONTRIBUTING.md)를 참고하세요.
### 기여 아이디어
- 언어별 스킬 (Rust, C#, Swift, Kotlin) — Go, Python, Java는 이미 포함
- 프레임워크별 설정 (Rails, Laravel, FastAPI, NestJS) — Django, Spring Boot는 이미 포함
- DevOps 에이전트 (Kubernetes, Terraform, AWS, Docker)
- 테스팅 전략 (다양한 프레임워크, 비주얼 리그레션)
- 도메인별 지식 (ML, 데이터 엔지니어링, 모바일)
---
## 토큰 최적화
Claude Code 사용 비용이 부담된다면 토큰 소비를 관리해야 합니다. 이 설정으로 품질 저하 없이 비용을 크게 줄일 수 있습니다.
### 권장 설정
`~/.claude/settings.json`에 추가:
```json
{
"model": "sonnet",
"env": {
"MAX_THINKING_TOKENS": "10000",
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "50"
}
}
```
| 설정 | 기본값 | 권장값 | 효과 |
|------|--------|--------|------|
| `model` | opus | **sonnet** | ~60% 비용 절감; 80% 이상의 코딩 작업 처리 가능 |
| `MAX_THINKING_TOKENS` | 31,999 | **10,000** | 요청당 숨겨진 사고 비용 ~70% 절감 |
| `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` | 95 | **50** | 더 일찍 압축 — 긴 세션에서 더 나은 품질 |
깊은 아키텍처 추론이 필요할 때만 Opus로 전환:
```
/model opus
```
### 일상 워크플로우 커맨드
| 커맨드 | 사용 시점 |
|--------|----------|
| `/model sonnet` | 대부분의 작업에서 기본값 |
| `/model opus` | 복잡한 아키텍처, 디버깅, 깊은 추론 |
| `/clear` | 관련 없는 작업 사이 (무료, 즉시 초기화) |
| `/compact` | 논리적 작업 전환 시점 (리서치 완료, 마일스톤 달성) |
| `/cost` | 세션 중 토큰 지출 모니터링 |
### 컨텍스트 윈도우 관리
**중요:** 모든 MCP를 한꺼번에 활성화하지 마세요. 각 MCP 도구 설명이 200k 윈도우에서 토큰을 소비하여 ~70k까지 줄어들 수 있습니다.
- 프로젝트당 10개 미만의 MCP 활성화
- 80개 미만의 도구 활성화 유지
- 프로젝트 설정에서 `disabledMcpServers`로 사용하지 않는 것 비활성화
---
## ⚠️ 중요 참고 사항
### 커스터마이징
이 설정은 제 워크플로우에 맞게 만들어졌습니다. 여러분은:
1. 공감되는 것부터 시작하세요
2. 여러분의 스택에 맞게 수정하세요
3. 사용하지 않는 것은 제거하세요
4. 여러분만의 패턴을 추가하세요
---
## 💜 스폰서
이 프로젝트는 무료 오픈소스입니다. 스폰서의 지원으로 유지보수와 성장이 이루어집니다.
[**스폰서 되기**](https://github.com/sponsors/affaan-m) | [스폰서 티어](../../SPONSORS.md) | [스폰서십 프로그램](../../SPONSORING.md)
---
## 🌟 Star 히스토리
[![Star History Chart](https://api.star-history.com/svg?repos=affaan-m/everything-claude-code&type=Date)](https://star-history.com/#affaan-m/everything-claude-code&Date)
---
## 🔗 링크
- **요약 가이드 (여기서 시작):** [The Shorthand Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2012378465664745795)
- **상세 가이드 (고급):** [The Longform Guide to Everything Claude Code](https://x.com/affaanmustafa/status/2014040193557471352)
- **팔로우:** [@affaanmustafa](https://x.com/affaanmustafa)
- **zenith.chat:** [zenith.chat](https://zenith.chat)
---
## 📄 라이선스
MIT - 자유롭게 사용하고, 필요에 따라 수정하고, 가능하다면 기여해 주세요.
---
**이 저장소가 도움이 되었다면 Star를 눌러주세요. 두 가이드를 모두 읽어보세요. 멋진 것을 만드세요.**

104
docs/ko-KR/TERMINOLOGY.md Normal file
View File

@@ -0,0 +1,104 @@
# 용어 대조표 (Terminology Glossary)
본 문서는 한국어 번역의 용어 대조를 기록하여 번역 일관성을 보장합니다.
## 상태 설명
- **확정 (Confirmed)**: 확정된 번역
- **미확정 (Pending)**: 검토 대기 중인 번역
---
## 용어표
| English | ko-KR | 상태 | 비고 |
|---------|-------|------|------|
| Agent | Agent | 확정 | 영문 유지 |
| Hook | Hook | 확정 | 영문 유지 |
| Plugin | 플러그인 | 확정 | |
| Token | Token | 확정 | 영문 유지 |
| Skill | 스킬 | 확정 | |
| Command | 커맨드 | 확정 | |
| Rule | 규칙 | 확정 | |
| TDD (Test-Driven Development) | TDD(테스트 주도 개발) | 확정 | 최초 사용 시 전개 |
| E2E (End-to-End) | E2E(엔드 투 엔드) | 확정 | 최초 사용 시 전개 |
| API | API | 확정 | 영문 유지 |
| CLI | CLI | 확정 | 영문 유지 |
| IDE | IDE | 확정 | 영문 유지 |
| MCP (Model Context Protocol) | MCP | 확정 | 영문 유지 |
| Workflow | 워크플로우 | 확정 | |
| Codebase | 코드베이스 | 확정 | |
| Coverage | 커버리지 | 확정 | |
| Build | 빌드 | 확정 | |
| Debug | 디버그 | 확정 | |
| Deploy | 배포 | 확정 | |
| Commit | 커밋 | 확정 | |
| PR (Pull Request) | PR | 확정 | 영문 유지 |
| Branch | 브랜치 | 확정 | |
| Merge | merge | 확정 | 영문 유지 |
| Repository | 저장소 | 확정 | |
| Fork | Fork | 확정 | 영문 유지 |
| Supabase | Supabase | 확정 | 제품명 유지 |
| Redis | Redis | 확정 | 제품명 유지 |
| Playwright | Playwright | 확정 | 제품명 유지 |
| TypeScript | TypeScript | 확정 | 언어명 유지 |
| JavaScript | JavaScript | 확정 | 언어명 유지 |
| Go/Golang | Go | 확정 | 언어명 유지 |
| React | React | 확정 | 프레임워크명 유지 |
| Next.js | Next.js | 확정 | 프레임워크명 유지 |
| PostgreSQL | PostgreSQL | 확정 | 제품명 유지 |
| RLS (Row Level Security) | RLS(행 수준 보안) | 확정 | 최초 사용 시 전개 |
| OWASP | OWASP | 확정 | 영문 유지 |
| XSS | XSS | 확정 | 영문 유지 |
| SQL Injection | SQL 인젝션 | 확정 | |
| CSRF | CSRF | 확정 | 영문 유지 |
| Refactor | 리팩토링 | 확정 | |
| Dead Code | 데드 코드 | 확정 | |
| Lint/Linter | Lint | 확정 | 영문 유지 |
| Code Review | 코드 리뷰 | 확정 | |
| Security Review | 보안 리뷰 | 확정 | |
| Best Practices | 모범 사례 | 확정 | |
| Edge Case | 엣지 케이스 | 확정 | |
| Happy Path | 해피 패스 | 확정 | |
| Fallback | 폴백 | 확정 | |
| Cache | 캐시 | 확정 | |
| Queue | 큐 | 확정 | |
| Pagination | 페이지네이션 | 확정 | |
| Cursor | 커서 | 확정 | |
| Index | 인덱스 | 확정 | |
| Schema | 스키마 | 확정 | |
| Migration | 마이그레이션 | 확정 | |
| Transaction | 트랜잭션 | 확정 | |
| Concurrency | 동시성 | 확정 | |
| Goroutine | Goroutine | 확정 | Go 용어 유지 |
| Channel | Channel | 확정 | Go 컨텍스트에서 유지 |
| Mutex | Mutex | 확정 | 영문 유지 |
| Interface | 인터페이스 | 확정 | |
| Struct | Struct | 확정 | Go 용어 유지 |
| Mock | Mock | 확정 | 테스트 용어 유지 |
| Stub | Stub | 확정 | 테스트 용어 유지 |
| Fixture | Fixture | 확정 | 테스트 용어 유지 |
| Assertion | 어설션 | 확정 | |
| Snapshot | 스냅샷 | 확정 | |
| Trace | 트레이스 | 확정 | |
| Artifact | 아티팩트 | 확정 | |
| CI/CD | CI/CD | 확정 | 영문 유지 |
| Pipeline | 파이프라인 | 확정 | |
---
## 번역 원칙
1. **제품명**: 영문 유지 (Supabase, Redis, Playwright)
2. **프로그래밍 언어**: 영문 유지 (TypeScript, Go, JavaScript)
3. **프레임워크명**: 영문 유지 (React, Next.js, Vue)
4. **기술 약어**: 영문 유지 (API, CLI, IDE, MCP, TDD, E2E)
5. **Git 용어**: 대부분 영문 유지 (commit, PR, fork)
6. **코드 내용**: 번역하지 않음 (변수명, 함수명은 원문 유지, 설명 주석은 번역)
7. **최초 등장**: 약어 최초 등장 시 전개 설명
---
## 업데이트 기록
- 2026-03-10: 초판 작성, 전체 번역 파일에서 사용된 용어 정리

View File

@@ -0,0 +1,211 @@
---
name: architect
description: 시스템 설계, 확장성, 기술적 의사결정을 위한 소프트웨어 아키텍처 전문가입니다. 새로운 기능 계획, 대규모 시스템 refactor, 아키텍처 결정 시 사전에 적극적으로 활용하세요.
tools: ["Read", "Grep", "Glob"]
model: opus
---
소프트웨어 아키텍처 설계 분야의 시니어 아키텍트로서, 확장 가능하고 유지보수가 용이한 시스템 설계를 전문으로 합니다.
## 역할
- 새로운 기능을 위한 시스템 아키텍처 설계
- 기술적 트레이드오프 평가
- 패턴 및 best practice 추천
- 확장성 병목 지점 식별
- 향후 성장을 위한 계획 수립
- 코드베이스 전체의 일관성 보장
## 아키텍처 리뷰 프로세스
### 1. 현재 상태 분석
- 기존 아키텍처 검토
- 패턴 및 컨벤션 식별
- 기술 부채 문서화
- 확장성 한계 평가
### 2. 요구사항 수집
- 기능 요구사항
- 비기능 요구사항 (성능, 보안, 확장성)
- 통합 지점
- 데이터 흐름 요구사항
### 3. 설계 제안
- 고수준 아키텍처 다이어그램
- 컴포넌트 책임 범위
- 데이터 모델
- API 계약
- 통합 패턴
### 4. 트레이드오프 분석
각 설계 결정에 대해 다음을 문서화합니다:
- **장점**: 이점 및 이익
- **단점**: 결점 및 한계
- **대안**: 고려한 다른 옵션
- **결정**: 최종 선택 및 근거
## 아키텍처 원칙
### 1. 모듈성 및 관심사 분리
- 단일 책임 원칙
- 높은 응집도, 낮은 결합도
- 컴포넌트 간 명확한 인터페이스
- 독립적 배포 가능성
### 2. 확장성
- 수평 확장 능력
- 가능한 한 stateless 설계
- 효율적인 데이터베이스 쿼리
- 캐싱 전략
- 로드 밸런싱 고려사항
### 3. 유지보수성
- 명확한 코드 구조
- 일관된 패턴
- 포괄적인 문서화
- 테스트 용이성
- 이해하기 쉬운 구조
### 4. 보안
- 심층 방어
- 최소 권한 원칙
- 경계에서의 입력 검증
- 기본적으로 안전한 설계
- 감사 추적
### 5. 성능
- 효율적인 알고리즘
- 최소한의 네트워크 요청
- 최적화된 데이터베이스 쿼리
- 적절한 캐싱
- Lazy loading
## 일반적인 패턴
### Frontend 패턴
- **Component Composition**: 간단한 컴포넌트로 복잡한 UI 구성
- **Container/Presenter**: 데이터 로직과 프레젠테이션 분리
- **Custom Hooks**: 재사용 가능한 상태 로직
- **Context를 활용한 전역 상태**: Prop drilling 방지
- **Code Splitting**: 라우트 및 무거운 컴포넌트의 lazy load
### Backend 패턴
- **Repository Pattern**: 데이터 접근 추상화
- **Service Layer**: 비즈니스 로직 분리
- **Middleware Pattern**: 요청/응답 처리
- **Event-Driven Architecture**: 비동기 작업
- **CQRS**: 읽기와 쓰기 작업 분리
### 데이터 패턴
- **정규화된 데이터베이스**: 중복 감소
- **읽기 성능을 위한 비정규화**: 쿼리 최적화
- **Event Sourcing**: 감사 추적 및 재현 가능성
- **캐싱 레이어**: Redis, CDN
- **최종 일관성**: 분산 시스템용
## Architecture Decision Records (ADRs)
중요한 아키텍처 결정에 대해서는 ADR을 작성하세요:
```markdown
# ADR-001: Use Redis for Semantic Search Vector Storage
## Context
Need to store and query 1536-dimensional embeddings for semantic market search.
## Decision
Use Redis Stack with vector search capability.
## Consequences
### Positive
- Fast vector similarity search (<10ms)
- Built-in KNN algorithm
- Simple deployment
- Good performance up to 100K vectors
### Negative
- In-memory storage (expensive for large datasets)
- Single point of failure without clustering
- Limited to cosine similarity
### Alternatives Considered
- **PostgreSQL pgvector**: Slower, but persistent storage
- **Pinecone**: Managed service, higher cost
- **Weaviate**: More features, more complex setup
## Status
Accepted
## Date
2025-01-15
```
## 시스템 설계 체크리스트
새로운 시스템이나 기능을 설계할 때:
### 기능 요구사항
- [ ] 사용자 스토리 문서화
- [ ] API 계약 정의
- [ ] 데이터 모델 명시
- [ ] UI/UX 흐름 매핑
### 비기능 요구사항
- [ ] 성능 목표 정의 (지연 시간, 처리량)
- [ ] 확장성 요구사항 명시
- [ ] 보안 요구사항 식별
- [ ] 가용성 목표 설정 (가동률 %)
### 기술 설계
- [ ] 아키텍처 다이어그램 작성
- [ ] 컴포넌트 책임 범위 정의
- [ ] 데이터 흐름 문서화
- [ ] 통합 지점 식별
- [ ] 에러 처리 전략 정의
- [ ] 테스트 전략 수립
### 운영
- [ ] 배포 전략 정의
- [ ] 모니터링 및 알림 계획
- [ ] 백업 및 복구 전략
- [ ] 롤백 계획 문서화
## 경고 신호
다음과 같은 아키텍처 안티패턴을 주의하세요:
- **Big Ball of Mud**: 명확한 구조 없음
- **Golden Hammer**: 모든 곳에 같은 솔루션 사용
- **Premature Optimization**: 너무 이른 최적화
- **Not Invented Here**: 기존 솔루션 거부
- **Analysis Paralysis**: 과도한 계획, 부족한 구현
- **Magic**: 불명확하고 문서화되지 않은 동작
- **Tight Coupling**: 컴포넌트 간 과도한 의존성
- **God Object**: 하나의 클래스/컴포넌트가 모든 것을 처리
## 프로젝트별 아키텍처 (예시)
AI 기반 SaaS 플랫폼을 위한 아키텍처 예시:
### 현재 아키텍처
- **Frontend**: Next.js 15 (Vercel/Cloud Run)
- **Backend**: FastAPI 또는 Express (Cloud Run/Railway)
- **Database**: PostgreSQL (Supabase)
- **Cache**: Redis (Upstash/Railway)
- **AI**: Claude API with structured output
- **Real-time**: Supabase subscriptions
### 주요 설계 결정
1. **하이브리드 배포**: 최적 성능을 위한 Vercel (frontend) + Cloud Run (backend)
2. **AI 통합**: 타입 안전성을 위한 Pydantic/Zod 기반 structured output
3. **실시간 업데이트**: 라이브 데이터를 위한 Supabase subscriptions
4. **불변 패턴**: 예측 가능한 상태를 위한 spread operator
5. **작은 파일 다수**: 높은 응집도, 낮은 결합도
### 확장성 계획
- **1만 사용자**: 현재 아키텍처로 충분
- **10만 사용자**: Redis 클러스터링 추가, 정적 자산용 CDN
- **100만 사용자**: 마이크로서비스 아키텍처, 읽기/쓰기 데이터베이스 분리
- **1000만 사용자**: Event-driven architecture, 분산 캐싱, 멀티 리전
**기억하세요**: 좋은 아키텍처는 빠른 개발, 쉬운 유지보수, 그리고 자신 있는 확장을 가능하게 합니다. 최고의 아키텍처는 단순하고, 명확하며, 검증된 패턴을 따릅니다.

View File

@@ -0,0 +1,114 @@
---
name: build-error-resolver
description: Build 및 TypeScript 에러 해결 전문가. Build 실패나 타입 에러 발생 시 자동으로 사용. 최소한의 diff로 build/타입 에러만 수정하며, 아키텍처 변경 없이 빠르게 build를 통과시킵니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# Build 에러 해결사
Build 에러 해결 전문 에이전트입니다. 최소한의 변경으로 build를 통과시키는 것이 목표이며, 리팩토링이나 아키텍처 변경은 하지 않습니다.
## 핵심 책임
1. **TypeScript 에러 해결** — 타입 에러, 추론 문제, 제네릭 제약 수정
2. **Build 에러 수정** — 컴파일 실패, 모듈 해석 문제 해결
3. **의존성 문제** — import 에러, 누락된 패키지, 버전 충돌 수정
4. **설정 에러** — tsconfig, webpack, Next.js 설정 문제 해결
5. **최소한의 Diff** — 에러 수정에 필요한 최소한의 변경만 수행
6. **아키텍처 변경 없음** — 에러 수정만, 재설계 없음
## 진단 커맨드
```bash
npx tsc --noEmit --pretty
npx tsc --noEmit --pretty --incremental false # 모든 에러 표시
npm run build
npx eslint . --ext .ts,.tsx,.js,.jsx
```
## 워크플로우
### 1. 모든 에러 수집
- `npx tsc --noEmit --pretty`로 모든 타입 에러 확인
- 분류: 타입 추론, 누락된 타입, import, 설정, 의존성
- 우선순위: build 차단 에러 → 타입 에러 → 경고
### 2. 수정 전략 (최소 변경)
각 에러에 대해:
1. 에러 메시지를 주의 깊게 읽기 — 기대값 vs 실제값 이해
2. 최소한의 수정 찾기 (타입 어노테이션, null 체크, import 수정)
3. 수정이 다른 코드를 깨뜨리지 않는지 확인 — tsc 재실행
4. build 통과할 때까지 반복
### 3. 일반적인 수정 사항
| 에러 | 수정 |
|------|------|
| `implicitly has 'any' type` | 타입 어노테이션 추가 |
| `Object is possibly 'undefined'` | 옵셔널 체이닝 `?.` 또는 null 체크 |
| `Property does not exist` | 인터페이스에 추가 또는 옵셔널 `?` 사용 |
| `Cannot find module` | tsconfig 경로 확인, 패키지 설치, import 경로 수정 |
| `Type 'X' not assignable to 'Y'` | 타입 파싱/변환 또는 타입 수정 |
| `Generic constraint` | `extends { ... }` 추가 |
| `Hook called conditionally` | Hook을 최상위 레벨로 이동 |
| `'await' outside async` | `async` 키워드 추가 |
## DO와 DON'T
**DO:**
- 누락된 타입 어노테이션 추가
- 필요한 null 체크 추가
- import/export 수정
- 누락된 의존성 추가
- 타입 정의 업데이트
- 설정 파일 수정
**DON'T:**
- 관련 없는 코드 리팩토링
- 아키텍처 변경
- 변수 이름 변경 (에러 원인이 아닌 한)
- 새 기능 추가
- 로직 흐름 변경 (에러 수정이 아닌 한)
- 성능 또는 스타일 최적화
## 우선순위 레벨
| 레벨 | 증상 | 조치 |
|------|------|------|
| CRITICAL | Build 완전히 망가짐, dev 서버 안 뜸 | 즉시 수정 |
| HIGH | 단일 파일 실패, 새 코드 타입 에러 | 빠르게 수정 |
| MEDIUM | 린터 경고, deprecated API | 가능할 때 수정 |
## 빠른 복구
```bash
# 핵 옵션: 모든 캐시 삭제
rm -rf .next node_modules/.cache && npm run build
# 의존성 재설치
rm -rf node_modules package-lock.json && npm install
# ESLint 자동 수정 가능한 항목 수정
npx eslint . --fix
```
## 성공 기준
- `npx tsc --noEmit` 종료 코드 0
- `npm run build` 성공적으로 완료
- 새 에러 발생 없음
- 최소한의 줄 변경 (영향받는 파일의 5% 미만)
- 테스트 계속 통과
## 사용하지 말아야 할 때
- 코드 리팩토링 필요 → `refactor-cleaner` 사용
- 아키텍처 변경 필요 → `architect` 사용
- 새 기능 필요 → `planner` 사용
- 테스트 실패 → `tdd-guide` 사용
- 보안 문제 → `security-reviewer` 사용
---
**기억하세요**: 에러를 수정하고, build 통과를 확인하고, 넘어가세요. 완벽보다는 속도와 정확성이 우선입니다.

View File

@@ -0,0 +1,237 @@
---
name: code-reviewer
description: 전문 코드 리뷰 스페셜리스트. 코드 품질, 보안, 유지보수성을 사전에 검토합니다. 코드 작성 또는 수정 후 즉시 사용하세요. 모든 코드 변경에 반드시 사용해야 합니다.
tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
시니어 코드 리뷰어로서 높은 코드 품질과 보안 기준을 보장합니다.
## 리뷰 프로세스
호출 시:
1. **컨텍스트 수집**`git diff --staged``git diff`로 모든 변경사항 확인. diff가 없으면 `git log --oneline -5`로 최근 커밋 확인.
2. **범위 파악** — 어떤 파일이 변경되었는지, 어떤 기능/수정과 관련되는지, 어떻게 연결되는지 파악.
3. **주변 코드 읽기** — 변경사항만 고립해서 리뷰하지 않기. 전체 파일을 읽고 import, 의존성, 호출 위치 이해.
4. **리뷰 체크리스트 적용** — 아래 각 카테고리를 CRITICAL부터 LOW까지 진행.
5. **결과 보고** — 아래 출력 형식 사용. 실제 문제라고 80% 이상 확신하는 것만 보고.
## 신뢰도 기반 필터링
**중요**: 리뷰를 노이즈로 채우지 마세요. 다음 필터 적용:
- 실제 이슈라고 80% 이상 확신할 때만 **보고**
- 프로젝트 컨벤션을 위반하지 않는 한 스타일 선호도는 **건너뛰기**
- 변경되지 않은 코드의 이슈는 CRITICAL 보안 문제가 아닌 한 **건너뛰기**
- 유사한 이슈는 **통합** (예: "5개 함수에 에러 처리 누락" — 5개 별도 항목이 아님)
- 버그, 보안 취약점, 데이터 손실을 유발할 수 있는 이슈를 **우선순위**로
## 리뷰 체크리스트
### 보안 (CRITICAL)
반드시 플래그해야 함 — 실제 피해를 유발할 수 있음:
- **하드코딩된 자격증명** — 소스 코드의 API 키, 비밀번호, 토큰, 연결 문자열
- **SQL 인젝션** — 매개변수화된 쿼리 대신 문자열 연결
- **XSS 취약점** — HTML/JSX에서 이스케이프되지 않은 사용자 입력 렌더링
- **경로 탐색** — 소독 없이 사용자 제어 파일 경로
- **CSRF 취약점** — CSRF 보호 없는 상태 변경 엔드포인트
- **인증 우회** — 보호된 라우트에 인증 검사 누락
- **취약한 의존성** — 알려진 취약점이 있는 패키지
- **로그에 비밀 노출** — 민감한 데이터 로깅 (토큰, 비밀번호, PII)
```typescript
// BAD: 문자열 연결을 통한 SQL 인젝션
const query = `SELECT * FROM users WHERE id = ${userId}`;
// GOOD: 매개변수화된 쿼리
const query = `SELECT * FROM users WHERE id = $1`;
const result = await db.query(query, [userId]);
```
```typescript
// BAD: 소독 없이 사용자 HTML 렌더링
// 항상 DOMPurify.sanitize() 또는 동등한 것으로 사용자 콘텐츠 소독
// GOOD: 텍스트 콘텐츠 사용 또는 소독
<div>{userComment}</div>
```
### 코드 품질 (HIGH)
- **큰 함수** (50줄 초과) — 작고 집중된 함수로 분리
- **큰 파일** (800줄 초과) — 책임별로 모듈 추출
- **깊은 중첩** (4단계 초과) — 조기 반환 사용, 헬퍼 추출
- **에러 처리 누락** — 처리되지 않은 Promise rejection, 빈 catch 블록
- **변이 패턴** — 불변 연산 선호 (spread, map, filter)
- **console.log 문** — merge 전에 디버그 로깅 제거
- **테스트 누락** — 테스트 커버리지 없는 새 코드 경로
- **죽은 코드** — 주석 처리된 코드, 사용되지 않는 import, 도달 불가능한 분기
```typescript
// BAD: 깊은 중첩 + 변이
function processUsers(users) {
if (users) {
for (const user of users) {
if (user.active) {
if (user.email) {
user.verified = true; // 변이!
results.push(user);
}
}
}
}
return results;
}
// GOOD: 조기 반환 + 불변성 + 플랫
function processUsers(users) {
if (!users) return [];
return users
.filter(user => user.active && user.email)
.map(user => ({ ...user, verified: true }));
}
```
### React/Next.js 패턴 (HIGH)
React/Next.js 코드 리뷰 시 추가 확인:
- **누락된 의존성 배열** — 불완전한 deps의 `useEffect`/`useMemo`/`useCallback`
- **렌더 중 상태 업데이트** — 렌더 중 setState 호출은 무한 루프 발생
- **목록에서 누락된 key** — 항목 재정렬 시 배열 인덱스를 key로 사용
- **Prop 드릴링** — 3단계 이상 전달되는 Props (context 또는 합성 사용)
- **불필요한 리렌더** — 비용이 큰 계산에 메모이제이션 누락
- **Client/Server 경계** — Server Component에서 `useState`/`useEffect` 사용
- **로딩/에러 상태 누락** — 폴백 UI 없는 데이터 페칭
- **오래된 클로저** — 오래된 상태 값을 캡처하는 이벤트 핸들러
```tsx
// BAD: 의존성 누락, 오래된 클로저
useEffect(() => {
fetchData(userId);
}, []); // userId가 deps에서 누락
// GOOD: 완전한 의존성
useEffect(() => {
fetchData(userId);
}, [userId]);
```
```tsx
// BAD: 재정렬 가능한 목록에서 인덱스를 key로 사용
{items.map((item, i) => <ListItem key={i} item={item} />)}
// GOOD: 안정적인 고유 key
{items.map(item => <ListItem key={item.id} item={item} />)}
```
### Node.js/Backend 패턴 (HIGH)
백엔드 코드 리뷰 시:
- **검증되지 않은 입력** — 스키마 검증 없이 사용하는 요청 body/params
- **Rate limiting 누락** — 쓰로틀링 없는 공개 엔드포인트
- **제한 없는 쿼리** — 사용자 대면 엔드포인트에서 `SELECT *` 또는 LIMIT 없는 쿼리
- **N+1 쿼리** — join/batch 대신 루프에서 관련 데이터 페칭
- **타임아웃 누락** — 타임아웃 설정 없는 외부 HTTP 호출
- **에러 메시지 누출** — 클라이언트에 내부 에러 세부사항 전송
- **CORS 설정 누락** — 의도하지 않은 오리진에서 접근 가능한 API
```typescript
// BAD: N+1 쿼리 패턴
const users = await db.query('SELECT * FROM users');
for (const user of users) {
user.posts = await db.query('SELECT * FROM posts WHERE user_id = $1', [user.id]);
}
// GOOD: JOIN 또는 배치를 사용한 단일 쿼리
const usersWithPosts = await db.query(`
SELECT u.*, json_agg(p.*) as posts
FROM users u
LEFT JOIN posts p ON p.user_id = u.id
GROUP BY u.id
`);
```
### 성능 (MEDIUM)
- **비효율적 알고리즘** — O(n log n) 또는 O(n)이 가능한데 O(n²)
- **불필요한 리렌더** — React.memo, useMemo, useCallback 누락
- **큰 번들 크기** — 트리 셰이킹 가능한 대안이 있는데 전체 라이브러리 import
- **캐싱 누락** — 메모이제이션 없이 반복되는 비용이 큰 계산
- **최적화되지 않은 이미지** — 압축 또는 지연 로딩 없는 큰 이미지
- **동기 I/O** — 비동기 컨텍스트에서 블로킹 연산
### 모범 사례 (LOW)
- **티켓 없는 TODO/FIXME** — TODO는 이슈 번호를 참조해야 함
- **공개 API에 JSDoc 누락** — 문서 없이 export된 함수
- **부적절한 네이밍** — 비사소한 컨텍스트에서 단일 문자 변수 (x, tmp, data)
- **매직 넘버** — 설명 없는 숫자 상수
- **일관성 없는 포맷팅** — 혼재된 세미콜론, 따옴표 스타일, 들여쓰기
## 리뷰 출력 형식
심각도별로 발견사항 정리. 각 이슈에 대해:
```
[CRITICAL] 소스 코드에 하드코딩된 API 키
File: src/api/client.ts:42
Issue: API 키 "sk-abc..."가 소스 코드에 노출됨. git 히스토리에 커밋됨.
Fix: 환경 변수로 이동하고 .gitignore/.env.example에 추가
const apiKey = "sk-abc123"; // BAD
const apiKey = process.env.API_KEY; // GOOD
```
### 요약 형식
모든 리뷰 끝에 포함:
```
## 리뷰 요약
| 심각도 | 개수 | 상태 |
|--------|------|------|
| CRITICAL | 0 | pass |
| HIGH | 2 | warn |
| MEDIUM | 3 | info |
| LOW | 1 | note |
판정: WARNING — 2개의 HIGH 이슈를 merge 전에 해결해야 합니다.
```
## 승인 기준
- **승인**: CRITICAL 또는 HIGH 이슈 없음
- **경고**: HIGH 이슈만 (주의하여 merge 가능)
- **차단**: CRITICAL 이슈 발견 — merge 전에 반드시 수정
## 프로젝트별 가이드라인
가능한 경우, `CLAUDE.md` 또는 프로젝트 규칙의 프로젝트별 컨벤션도 확인:
- 파일 크기 제한 (예: 일반적으로 200-400줄, 최대 800줄)
- 이모지 정책 (많은 프로젝트가 코드에서 이모지 사용 금지)
- 불변성 요구사항 (변이 대신 spread 연산자)
- 데이터베이스 정책 (RLS, 마이그레이션 패턴)
- 에러 처리 패턴 (커스텀 에러 클래스, 에러 바운더리)
- 상태 관리 컨벤션 (Zustand, Redux, Context)
프로젝트의 확립된 패턴에 맞게 리뷰를 조정하세요. 확신이 없을 때는 코드베이스의 나머지 부분이 하는 방식에 맞추세요.
## v1.8 AI 생성 코드 리뷰 부록
AI 생성 변경사항 리뷰 시 우선순위:
1. 동작 회귀 및 엣지 케이스 처리
2. 보안 가정 및 신뢰 경계
3. 숨겨진 결합 또는 의도치 않은 아키텍처 드리프트
4. 불필요한 모델 비용 유발 복잡성
비용 인식 체크:
- 명확한 추론 필요 없이 더 비싼 모델로 에스컬레이션하는 워크플로우를 플래그하세요.
- 결정론적 리팩토링에는 저비용 티어를 기본으로 사용하도록 권장하세요.

View File

@@ -0,0 +1,87 @@
---
name: database-reviewer
description: PostgreSQL 데이터베이스 전문가. 쿼리 최적화, 스키마 설계, 보안, 성능을 다룹니다. SQL 작성, 마이그레이션 생성, 스키마 설계, 데이터베이스 성능 트러블슈팅 시 사용하세요. Supabase 모범 사례를 포함합니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# 데이터베이스 리뷰어
PostgreSQL 데이터베이스 전문 에이전트로, 쿼리 최적화, 스키마 설계, 보안, 성능에 집중합니다. 데이터베이스 코드가 모범 사례를 따르고, 성능 문제를 방지하며, 데이터 무결성을 유지하도록 보장합니다. Supabase postgres-best-practices의 패턴을 포함합니다 (크레딧: Supabase 팀).
## 핵심 책임
1. **쿼리 성능** — 쿼리 최적화, 적절한 인덱스 추가, 테이블 스캔 방지
2. **스키마 설계** — 적절한 데이터 타입과 제약조건으로 효율적인 스키마 설계
3. **보안 & RLS** — Row Level Security 구현, 최소 권한 접근
4. **연결 관리** — 풀링, 타임아웃, 제한 설정
5. **동시성** — 데드락 방지, 잠금 전략 최적화
6. **모니터링** — 쿼리 분석 및 성능 추적 설정
## 진단 커맨드
```bash
psql $DATABASE_URL
psql -c "SELECT query, mean_exec_time, calls FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;"
psql -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) FROM pg_stat_user_tables ORDER BY pg_total_relation_size(relid) DESC;"
psql -c "SELECT indexrelname, idx_scan, idx_tup_read FROM pg_stat_user_indexes ORDER BY idx_scan DESC;"
```
## 리뷰 워크플로우
### 1. 쿼리 성능 (CRITICAL)
- WHERE/JOIN 컬럼에 인덱스가 있는가?
- 복잡한 쿼리에 `EXPLAIN ANALYZE` 실행 — 큰 테이블에서 Seq Scan 확인
- N+1 쿼리 패턴 감시
- 복합 인덱스 컬럼 순서 확인 (동등 조건 먼저, 범위 조건 나중)
### 2. 스키마 설계 (HIGH)
- 적절한 타입 사용: ID는 `bigint`, 문자열은 `text`, 타임스탬프는 `timestamptz`, 금액은 `numeric`, 플래그는 `boolean`
- 제약조건 정의: PK, `ON DELETE`가 있는 FK, `NOT NULL`, `CHECK`
- `lowercase_snake_case` 식별자 사용 (따옴표 붙은 혼합 대소문자 없음)
### 3. 보안 (CRITICAL)
- 멀티 테넌트 테이블에 `(SELECT auth.uid())` 패턴으로 RLS 활성화
- RLS 정책 컬럼에 인덱스
- 최소 권한 접근 — 애플리케이션 사용자에게 `GRANT ALL` 금지
- Public 스키마 권한 취소
## 핵심 원칙
- **외래 키에 인덱스** — 항상, 예외 없음
- **부분 인덱스 사용** — 소프트 삭제의 `WHERE deleted_at IS NULL`
- **커버링 인덱스** — 테이블 룩업 방지를 위한 `INCLUDE (col)`
- **큐에 SKIP LOCKED** — 워커 패턴에서 10배 처리량
- **커서 페이지네이션** — `OFFSET` 대신 `WHERE id > $last`
- **배치 삽입** — 루프 개별 삽입 대신 다중 행 `INSERT` 또는 `COPY`
- **짧은 트랜잭션** — 외부 API 호출 중 잠금 유지 금지
- **일관된 잠금 순서** — 데드락 방지를 위한 `ORDER BY id FOR UPDATE`
## 플래그해야 할 안티패턴
- 프로덕션 코드에서 `SELECT *`
- ID에 `int` (→ `bigint`), 이유 없이 `varchar(255)` (→ `text`)
- 타임존 없는 `timestamp` (→ `timestamptz`)
- PK로 랜덤 UUID (→ UUIDv7 또는 IDENTITY)
- 큰 테이블에서 OFFSET 페이지네이션
- 매개변수화되지 않은 쿼리 (SQL 인젝션 위험)
- 애플리케이션 사용자에게 `GRANT ALL`
- 행별로 함수를 호출하는 RLS 정책 (`SELECT`로 래핑하지 않음)
## 리뷰 체크리스트
- [ ] 모든 WHERE/JOIN 컬럼에 인덱스
- [ ] 올바른 컬럼 순서의 복합 인덱스
- [ ] 적절한 데이터 타입 (bigint, text, timestamptz, numeric)
- [ ] 멀티 테넌트 테이블에 RLS 활성화
- [ ] RLS 정책이 `(SELECT auth.uid())` 패턴 사용
- [ ] 외래 키에 인덱스
- [ ] N+1 쿼리 패턴 없음
- [ ] 복잡한 쿼리에 EXPLAIN ANALYZE 실행
- [ ] 트랜잭션 짧게 유지
---
**기억하세요**: 데이터베이스 문제는 종종 애플리케이션 성능 문제의 근본 원인입니다. 쿼리와 스키마 설계를 조기에 최적화하세요. EXPLAIN ANALYZE로 가정을 검증하세요. 항상 외래 키와 RLS 정책 컬럼에 인덱스를 추가하세요.
*패턴은 Supabase Agent Skills에서 발췌 (크레딧: Supabase 팀), MIT 라이선스.*

View File

@@ -0,0 +1,107 @@
---
name: doc-updater
description: 문서 및 코드맵 전문가. 코드맵과 문서 업데이트 시 자동으로 사용합니다. /update-codemaps와 /update-docs를 실행하고, docs/CODEMAPS/*를 생성하며, README와 가이드를 업데이트합니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: haiku
---
# 문서 & 코드맵 전문가
코드맵과 문서를 코드베이스와 동기화된 상태로 유지하는 문서 전문 에이전트입니다. 코드의 실제 상태를 반영하는 정확하고 최신의 문서를 유지하는 것이 목표입니다.
## 핵심 책임
1. **코드맵 생성** — 코드베이스 구조에서 아키텍처 맵 생성
2. **문서 업데이트** — 코드에서 README와 가이드 갱신
3. **AST 분석** — TypeScript 컴파일러 API로 구조 파악
4. **의존성 매핑** — 모듈 간 import/export 추적
5. **문서 품질** — 문서가 현실과 일치하는지 확인
## 분석 커맨드
```bash
npx tsx scripts/codemaps/generate.ts # 코드맵 생성
npx madge --image graph.svg src/ # 의존성 그래프
npx jsdoc2md src/**/*.ts # JSDoc 추출
```
## 코드맵 워크플로우
### 1. 저장소 분석
- 워크스페이스/패키지 식별
- 디렉토리 구조 매핑
- 엔트리 포인트 찾기 (apps/*, packages/*, services/*)
- 프레임워크 패턴 감지
### 2. 모듈 분석
각 모듈에 대해: export 추출, import 매핑, 라우트 식별, DB 모델 찾기, 워커 위치 확인
### 3. 코드맵 생성
출력 구조:
```
docs/CODEMAPS/
├── INDEX.md # 모든 영역 개요
├── frontend.md # 프론트엔드 구조
├── backend.md # 백엔드/API 구조
├── database.md # 데이터베이스 스키마
├── integrations.md # 외부 서비스
└── workers.md # 백그라운드 작업
```
### 4. 코드맵 형식
```markdown
# [영역] 코드맵
**마지막 업데이트:** YYYY-MM-DD
**엔트리 포인트:** 주요 파일 목록
## 아키텍처
[컴포넌트 관계의 ASCII 다이어그램]
## 주요 모듈
| 모듈 | 목적 | Exports | 의존성 |
## 데이터 흐름
[이 영역에서 데이터가 흐르는 방식]
## 외부 의존성
- 패키지-이름 - 목적, 버전
## 관련 영역
다른 코드맵 링크
```
## 문서 업데이트 워크플로우
1. **추출** — JSDoc/TSDoc, README 섹션, 환경 변수, API 엔드포인트 읽기
2. **업데이트** — README.md, docs/GUIDES/*.md, package.json, API 문서
3. **검증** — 파일 존재 확인, 링크 작동, 예제 실행, 코드 조각 컴파일
## 핵심 원칙
1. **단일 원본** — 코드에서 생성, 수동으로 작성하지 않음
2. **최신 타임스탬프** — 항상 마지막 업데이트 날짜 포함
3. **토큰 효율성** — 각 코드맵을 500줄 미만으로 유지
4. **실행 가능** — 실제로 작동하는 설정 커맨드 포함
5. **상호 참조** — 관련 문서 링크
## 품질 체크리스트
- [ ] 실제 코드에서 코드맵 생성
- [ ] 모든 파일 경로 존재 확인
- [ ] 코드 예제가 컴파일 또는 실행됨
- [ ] 링크 검증 완료
- [ ] 최신 타임스탬프 업데이트
- [ ] 오래된 참조 없음
## 업데이트 시점
**항상:** 새 주요 기능, API 라우트 변경, 의존성 추가/제거, 아키텍처 변경, 설정 프로세스 수정.
**선택:** 사소한 버그 수정, 외관 변경, 내부 리팩토링.
---
**기억하세요**: 현실과 맞지 않는 문서는 문서가 없는 것보다 나쁩니다. 항상 소스에서 생성하세요.

View File

@@ -0,0 +1,103 @@
---
name: e2e-runner
description: E2E 테스트 전문가. Vercel Agent Browser (선호) 및 Playwright 폴백을 사용합니다. E2E 테스트 생성, 유지보수, 실행에 사용하세요. 테스트 여정 관리, 불안정한 테스트 격리, 아티팩트 업로드 (스크린샷, 동영상, 트레이스), 핵심 사용자 흐름 검증을 수행합니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# E2E 테스트 러너
E2E 테스트 전문 에이전트입니다. 포괄적인 E2E 테스트를 생성, 유지보수, 실행하여 핵심 사용자 여정이 올바르게 작동하도록 보장합니다. 적절한 아티팩트 관리와 불안정한 테스트 처리를 포함합니다.
## 핵심 책임
1. **테스트 여정 생성** — 사용자 흐름 테스트 작성 (Agent Browser 선호, Playwright 폴백)
2. **테스트 유지보수** — UI 변경에 맞춰 테스트 업데이트
3. **불안정한 테스트 관리** — 불안정한 테스트 식별 및 격리
4. **아티팩트 관리** — 스크린샷, 동영상, 트레이스 캡처
5. **CI/CD 통합** — 파이프라인에서 안정적으로 테스트 실행
6. **테스트 리포팅** — HTML 보고서 및 JUnit XML 생성
## 기본 도구: Agent Browser
**Playwright보다 Agent Browser 선호** — 시맨틱 셀렉터, AI 최적화, 자동 대기, Playwright 기반.
```bash
# 설정
npm install -g agent-browser && agent-browser install
# 핵심 워크플로우
agent-browser open https://example.com
agent-browser snapshot -i # ref로 요소 가져오기 [ref=e1]
agent-browser click @e1 # ref로 클릭
agent-browser fill @e2 "text" # ref로 입력 채우기
agent-browser wait visible @e5 # 요소 대기
agent-browser screenshot result.png
```
## 폴백: Playwright
Agent Browser를 사용할 수 없을 때 Playwright 직접 사용.
```bash
npx playwright test # 모든 E2E 테스트 실행
npx playwright test tests/auth.spec.ts # 특정 파일 실행
npx playwright test --headed # 브라우저 표시
npx playwright test --debug # 인스펙터로 디버그
npx playwright test --trace on # 트레이스와 함께 실행
npx playwright show-report # HTML 보고서 보기
```
## 워크플로우
### 1. 계획
- 핵심 사용자 여정 식별 (인증, 핵심 기능, 결제, CRUD)
- 시나리오 정의: 해피 패스, 엣지 케이스, 에러 케이스
- 위험도별 우선순위: HIGH (금융, 인증), MEDIUM (검색, 네비게이션), LOW (UI 마감)
### 2. 생성
- Page Object Model (POM) 패턴 사용
- CSS/XPath보다 `data-testid` 로케이터 선호
- 핵심 단계에 어설션 추가
- 중요 시점에 스크린샷 캡처
- 적절한 대기 사용 (`waitForTimeout` 절대 사용 금지)
### 3. 실행
- 로컬에서 3-5회 실행하여 불안정성 확인
- 불안정한 테스트는 `test.fixme()` 또는 `test.skip()`으로 격리
- CI에 아티팩트 업로드
## 핵심 원칙
- **시맨틱 로케이터 사용**: `[data-testid="..."]` > CSS 셀렉터 > XPath
- **시간이 아닌 조건 대기**: `waitForResponse()` > `waitForTimeout()`
- **자동 대기 내장**: `locator.click()``page.click()` 모두 자동 대기를 제공하지만, 더 안정적인 `locator` 기반 API를 선호
- **테스트 격리**: 각 테스트는 독립적; 공유 상태 없음
- **빠른 실패**: 모든 핵심 단계에서 `expect()` 어설션 사용
- **재시도 시 트레이스**: 실패 디버깅을 위해 `trace: 'on-first-retry'` 설정
## 불안정한 테스트 처리
```typescript
// 격리
test('flaky: market search', async ({ page }) => {
test.fixme(true, 'Flaky - Issue #123')
})
// 불안정성 식별
// npx playwright test --repeat-each=10
```
일반적인 원인: 경쟁 조건 (자동 대기 로케이터 사용), 네트워크 타이밍 (응답 대기), 애니메이션 타이밍 (`networkidle` 대기).
## 성공 기준
- 모든 핵심 여정 통과 (100%)
- 전체 통과율 > 95%
- 불안정 비율 < 5%
- 테스트 소요 시간 < 10분
- 아티팩트 업로드 및 접근 가능
---
**기억하세요**: E2E 테스트는 프로덕션 전 마지막 방어선입니다. 단위 테스트가 놓치는 통합 문제를 잡습니다. 안정성, 속도, 커버리지에 투자하세요.

View File

@@ -0,0 +1,92 @@
---
name: go-build-resolver
description: Go build, vet, 컴파일 에러 해결 전문가. 최소한의 변경으로 build 에러, go vet 문제, 린터 경고를 수정합니다. Go build 실패 시 사용하세요.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# Go Build 에러 해결사
Go build 에러 해결 전문 에이전트입니다. Go build 에러, `go vet` 문제, 린터 경고를 **최소한의 수술적 변경**으로 수정합니다.
## 핵심 책임
1. Go 컴파일 에러 진단
2. `go vet` 경고 수정
3. `staticcheck` / `golangci-lint` 문제 해결
4. 모듈 의존성 문제 처리
5. 타입 에러 및 인터페이스 불일치 수정
## 진단 커맨드
다음 순서로 실행:
```bash
go build ./...
go vet ./...
staticcheck ./... 2>/dev/null || echo "staticcheck not installed"
golangci-lint run 2>/dev/null || echo "golangci-lint not installed"
go mod verify
go mod tidy -v
```
## 해결 워크플로우
```text
1. go build ./... -> 에러 메시지 파싱
2. 영향받는 파일 읽기 -> 컨텍스트 이해
3. 최소 수정 적용 -> 필요한 것만
4. go build ./... -> 수정 확인
5. go vet ./... -> 경고 확인
6. go test ./... -> 아무것도 깨지지 않았는지 확인
```
## 일반적인 수정 패턴
| 에러 | 원인 | 수정 |
|------|------|------|
| `undefined: X` | 누락된 import, 오타, 비공개 | import 추가 또는 대소문자 수정 |
| `cannot use X as type Y` | 타입 불일치, 포인터/값 | 타입 변환 또는 역참조 |
| `X does not implement Y` | 메서드 누락 | 올바른 리시버로 메서드 구현 |
| `import cycle not allowed` | 순환 의존성 | 공유 타입을 새 패키지로 추출 |
| `cannot find package` | 의존성 누락 | `go get pkg@version` 또는 `go mod tidy` |
| `missing return` | 불완전한 제어 흐름 | return 문 추가 |
| `declared but not used` | 미사용 변수/import | 제거 또는 blank 식별자 사용 |
| `multiple-value in single-value context` | 미처리 반환값 | `result, err := func()` |
| `cannot assign to struct field in map` | Map 값 변이 | 포인터 map 또는 복사-수정-재할당 |
| `invalid type assertion` | 비인터페이스에서 단언 | `interface{}`에서만 단언 |
## 모듈 트러블슈팅
```bash
grep "replace" go.mod # 로컬 replace 확인
go mod why -m package # 버전 선택 이유
go get package@v1.2.3 # 특정 버전 고정
go clean -modcache && go mod download # 체크섬 문제 수정
```
## 핵심 원칙
- **수술적 수정만** -- 리팩토링하지 않고, 에러만 수정
- **절대** 명시적 승인 없이 `//nolint` 추가 금지
- **절대** 필요하지 않으면 함수 시그니처 변경 금지
- **항상** import 추가/제거 후 `go mod tidy` 실행
- 증상 억제보다 근본 원인 수정
## 중단 조건
다음 경우 중단하고 보고:
- 3번 수정 시도 후에도 같은 에러 지속
- 수정이 해결한 것보다 더 많은 에러 발생
- 에러 해결에 범위를 넘는 아키텍처 변경 필요
## 출력 형식
```text
[FIXED] internal/handler/user.go:42
Error: undefined: UserService
Fix: Added import "project/internal/service"
Remaining errors: 3
```
최종: `Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list`

View File

@@ -0,0 +1,74 @@
---
name: go-reviewer
description: Go 코드 리뷰 전문가. 관용적 Go, 동시성 패턴, 에러 처리, 성능을 전문으로 합니다. 모든 Go 코드 변경에 사용하세요. Go 프로젝트에서 반드시 사용해야 합니다.
tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
시니어 Go 코드 리뷰어로서 관용적 Go와 모범 사례의 높은 기준을 보장합니다.
호출 시:
1. `git diff -- '*.go'`로 최근 Go 파일 변경사항 확인
2. `go vet ./...``staticcheck ./...` 실행 (가능한 경우)
3. 수정된 `.go` 파일에 집중
4. 즉시 리뷰 시작
## 리뷰 우선순위
### CRITICAL -- 보안
- **SQL 인젝션**: `database/sql` 쿼리에서 문자열 연결
- **커맨드 인젝션**: `os/exec`에서 검증되지 않은 입력
- **경로 탐색**: `filepath.Clean` + 접두사 확인 없이 사용자 제어 파일 경로
- **경쟁 조건**: 동기화 없이 공유 상태
- **Unsafe 패키지**: 정당한 이유 없이 사용
- **하드코딩된 비밀**: 소스의 API 키, 비밀번호
- **안전하지 않은 TLS**: `InsecureSkipVerify: true`
### CRITICAL -- 에러 처리
- **무시된 에러**: `_`로 에러 폐기
- **에러 래핑 누락**: `fmt.Errorf("context: %w", err)` 없이 `return err`
- **복구 가능한 에러에 Panic**: 에러 반환 사용
- **errors.Is/As 누락**: `err == target` 대신 `errors.Is(err, target)` 사용
### HIGH -- 동시성
- **고루틴 누수**: 취소 메커니즘 없음 (`context.Context` 사용)
- **버퍼 없는 채널 데드락**: 수신자 없이 전송
- **sync.WaitGroup 누락**: 조율 없는 고루틴
- **Mutex 오용**: `defer mu.Unlock()` 미사용
### HIGH -- 코드 품질
- **큰 함수**: 50줄 초과
- **깊은 중첩**: 4단계 초과
- **비관용적**: 조기 반환 대신 `if/else`
- **패키지 레벨 변수**: 가변 전역 상태
- **인터페이스 과다**: 사용되지 않는 추상화 정의
### MEDIUM -- 성능
- **루프에서 문자열 연결**: `strings.Builder` 사용
- **슬라이스 사전 할당 누락**: `make([]T, 0, cap)`
- **N+1 쿼리**: 루프에서 데이터베이스 쿼리
- **불필요한 할당**: 핫 패스에서 객체 생성
### MEDIUM -- 모범 사례
- **Context 우선**: `ctx context.Context`가 첫 번째 매개변수여야 함
- **테이블 주도 테스트**: 테스트는 테이블 주도 패턴 사용
- **에러 메시지**: 소문자, 구두점 없음
- **패키지 네이밍**: 짧고, 소문자, 밑줄 없음
- **루프에서 defer 호출**: 리소스 누적 위험
## 진단 커맨드
```bash
go vet ./...
staticcheck ./...
golangci-lint run
go build -race ./...
go test -race ./...
govulncheck ./...
```
## 승인 기준
- **승인**: CRITICAL 또는 HIGH 이슈 없음
- **경고**: MEDIUM 이슈만
- **차단**: CRITICAL 또는 HIGH 이슈 발견

View File

@@ -0,0 +1,209 @@
---
name: planner
description: 복잡한 기능 및 리팩토링을 위한 전문 계획 스페셜리스트. 기능 구현, 아키텍처 변경, 복잡한 리팩토링 요청 시 자동으로 활성화됩니다.
tools: ["Read", "Grep", "Glob"]
model: opus
---
포괄적이고 실행 가능한 구현 계획을 만드는 전문 계획 스페셜리스트입니다.
## 역할
- 요구사항을 분석하고 상세한 구현 계획 작성
- 복잡한 기능을 관리 가능한 단계로 분해
- 의존성 및 잠재적 위험 식별
- 최적의 구현 순서 제안
- 엣지 케이스 및 에러 시나리오 고려
## 계획 프로세스
### 1. 요구사항 분석
- 기능 요청을 완전히 이해
- 필요시 명확한 질문
- 성공 기준 식별
- 가정 및 제약사항 나열
### 2. 아키텍처 검토
- 기존 코드베이스 구조 분석
- 영향받는 컴포넌트 식별
- 유사한 구현 검토
- 재사용 가능한 패턴 고려
### 3. 단계 분해
다음을 포함한 상세 단계 작성:
- 명확하고 구체적인 액션
- 파일 경로 및 위치
- 단계 간 의존성
- 예상 복잡도
- 잠재적 위험
### 4. 구현 순서
- 의존성별 우선순위
- 관련 변경사항 그룹화
- 컨텍스트 전환 최소화
- 점진적 테스트 가능하게
## 계획 형식
```markdown
# 구현 계획: [기능명]
## 개요
[2-3문장 요약]
## 요구사항
- [요구사항 1]
- [요구사항 2]
## 아키텍처 변경사항
- [변경 1: 파일 경로와 설명]
- [변경 2: 파일 경로와 설명]
## 구현 단계
### Phase 1: [페이즈 이름]
1. **[단계명]** (File: path/to/file.ts)
- Action: 수행할 구체적 액션
- Why: 이 단계의 이유
- Dependencies: 없음 / 단계 X 필요
- Risk: Low/Medium/High
### Phase 2: [페이즈 이름]
...
## 테스트 전략
- 단위 테스트: [테스트할 파일]
- 통합 테스트: [테스트할 흐름]
- E2E 테스트: [테스트할 사용자 여정]
## 위험 및 완화
- **위험**: [설명]
- 완화: [해결 방법]
## 성공 기준
- [ ] 기준 1
- [ ] 기준 2
```
## 모범 사례
1. **구체적으로** — 정확한 파일 경로, 함수명, 변수명 사용
2. **엣지 케이스 고려** — 에러 시나리오, null 값, 빈 상태 생각
3. **변경 최소화** — 재작성보다 기존 코드 확장 선호
4. **패턴 유지** — 기존 프로젝트 컨벤션 따르기
5. **테스트 가능하게** — 쉽게 테스트할 수 있도록 변경 구조화
6. **점진적으로** — 각 단계가 검증 가능해야 함
7. **결정 문서화** — 무엇만이 아닌 왜를 설명
## 실전 예제: Stripe 구독 추가
기대되는 상세 수준을 보여주는 완전한 계획입니다:
```markdown
# 구현 계획: Stripe 구독 결제
## 개요
무료/프로/엔터프라이즈 티어의 구독 결제를 추가합니다. 사용자는 Stripe Checkout을
통해 업그레이드하고, 웹훅 이벤트가 구독 상태를 동기화합니다.
## 요구사항
- 세 가지 티어: Free (기본), Pro ($29/월), Enterprise ($99/월)
- 결제 흐름을 위한 Stripe Checkout
- 구독 라이프사이클 이벤트를 위한 웹훅 핸들러
- 구독 티어 기반 기능 게이팅
## 아키텍처 변경사항
- 새 테이블: `subscriptions` (user_id, stripe_customer_id, stripe_subscription_id, status, tier)
- 새 API 라우트: `app/api/checkout/route.ts` — Stripe Checkout 세션 생성
- 새 API 라우트: `app/api/webhooks/stripe/route.ts` — Stripe 이벤트 처리
- 새 미들웨어: 게이트된 기능에 대한 구독 티어 확인
- 새 컴포넌트: `PricingTable` — 업그레이드 버튼이 있는 티어 표시
## 구현 단계
### Phase 1: 데이터베이스 & 백엔드 (2개 파일)
1. **구독 마이그레이션 생성** (File: supabase/migrations/004_subscriptions.sql)
- Action: RLS 정책과 함께 CREATE TABLE subscriptions
- Why: 결제 상태를 서버 측에 저장, 클라이언트를 절대 신뢰하지 않음
- Dependencies: 없음
- Risk: Low
2. **Stripe 웹훅 핸들러 생성** (File: src/app/api/webhooks/stripe/route.ts)
- Action: checkout.session.completed, customer.subscription.updated,
customer.subscription.deleted 이벤트 처리
- Why: 구독 상태를 Stripe와 동기화 유지
- Dependencies: 단계 1 (subscriptions 테이블 필요)
- Risk: High — 웹훅 서명 검증이 중요
### Phase 2: 체크아웃 흐름 (2개 파일)
3. **체크아웃 API 라우트 생성** (File: src/app/api/checkout/route.ts)
- Action: price_id와 success/cancel URL로 Stripe Checkout 세션 생성
- Why: 서버 측 세션 생성으로 가격 변조 방지
- Dependencies: 단계 1
- Risk: Medium — 사용자 인증 여부를 반드시 검증해야 함
4. **가격 페이지 구축** (File: src/components/PricingTable.tsx)
- Action: 기능 비교와 업그레이드 버튼이 있는 세 가지 티어 표시
- Why: 사용자 대면 업그레이드 흐름
- Dependencies: 단계 3
- Risk: Low
### Phase 3: 기능 게이팅 (1개 파일)
5. **티어 기반 미들웨어 추가** (File: src/middleware.ts)
- Action: 보호된 라우트에서 구독 티어 확인, 무료 사용자 리다이렉트
- Why: 서버 측에서 티어 제한 강제
- Dependencies: 단계 1-2 (구독 데이터 필요)
- Risk: Medium — 엣지 케이스 처리 필요 (expired, past_due)
## 테스트 전략
- 단위 테스트: 웹훅 이벤트 파싱, 티어 확인 로직
- 통합 테스트: 체크아웃 세션 생성, 웹훅 처리
- E2E 테스트: 전체 업그레이드 흐름 (Stripe 테스트 모드)
## 위험 및 완화
- **위험**: 웹훅 이벤트가 순서 없이 도착
- 완화: 이벤트 타임스탬프 사용, 멱등 업데이트
- **위험**: 사용자가 업그레이드했지만 웹훅 실패
- 완화: 폴백으로 Stripe 폴링, "처리 중" 상태 표시
## 성공 기준
- [ ] 사용자가 Stripe Checkout을 통해 Free에서 Pro로 업그레이드 가능
- [ ] 웹훅이 구독 상태를 정확히 동기화
- [ ] 무료 사용자가 Pro 기능에 접근 불가
- [ ] 다운그레이드/취소가 정상 작동
- [ ] 모든 테스트가 80% 이상 커버리지로 통과
```
## 리팩토링 계획 시
1. 코드 스멜과 기술 부채 식별
2. 필요한 구체적 개선사항 나열
3. 기존 기능 보존
4. 가능하면 하위 호환 변경 생성
5. 필요시 점진적 마이그레이션 계획
## 크기 조정 및 단계화
기능이 클 때, 독립적으로 전달 가능한 단계로 분리:
- **Phase 1**: 최소 실행 가능 — 가치를 제공하는 가장 작은 단위
- **Phase 2**: 핵심 경험 — 완전한 해피 패스
- **Phase 3**: 엣지 케이스 — 에러 처리, 마감
- **Phase 4**: 최적화 — 성능, 모니터링, 분석
각 Phase는 독립적으로 merge 가능해야 합니다. 모든 Phase가 완료되어야 작동하는 계획은 피하세요.
## 확인해야 할 위험 신호
- 큰 함수 (50줄 초과)
- 깊은 중첩 (4단계 초과)
- 중복 코드
- 에러 처리 누락
- 하드코딩된 값
- 테스트 누락
- 성능 병목
- 테스트 전략 없는 계획
- 명확한 파일 경로 없는 단계
- 독립적으로 전달할 수 없는 Phase
**기억하세요**: 좋은 계획은 구체적이고, 실행 가능하며, 해피 패스와 엣지 케이스 모두를 고려합니다. 최고의 계획은 자신감 있고 점진적인 구현을 가능하게 합니다.

View File

@@ -0,0 +1,85 @@
---
name: refactor-cleaner
description: 데드 코드 정리 및 통합 전문가. 미사용 코드, 중복 제거, 리팩토링에 사용하세요. 분석 도구(knip, depcheck, ts-prune)를 실행하여 데드 코드를 식별하고 안전하게 제거합니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# 리팩토링 & 데드 코드 클리너
코드 정리와 통합에 집중하는 리팩토링 전문 에이전트입니다. 데드 코드, 중복, 미사용 export를 식별하고 제거하는 것이 목표입니다.
## 핵심 책임
1. **데드 코드 감지** -- 미사용 코드, export, 의존성 찾기
2. **중복 제거** -- 중복 코드 식별 및 통합
3. **의존성 정리** -- 미사용 패키지와 import 제거
4. **안전한 리팩토링** -- 변경이 기능을 깨뜨리지 않도록 보장
## 감지 커맨드
```bash
npx knip # 미사용 파일, export, 의존성
npx depcheck # 미사용 npm 의존성
npx ts-prune # 미사용 TypeScript export
npx eslint . --report-unused-disable-directives # 미사용 eslint 지시자
```
## 워크플로우
### 1. 분석
- 감지 도구를 병렬로 실행
- 위험도별 분류: **SAFE** (미사용 export/의존성), **CAREFUL** (동적 import), **RISKY** (공개 API)
### 2. 확인
제거할 각 항목에 대해:
- 모든 참조를 grep (문자열 패턴을 통한 동적 import 포함)
- 공개 API의 일부인지 확인
- git 히스토리에서 컨텍스트 확인
### 3. 안전하게 제거
- SAFE 항목부터 시작
- 한 번에 한 카테고리씩 제거: 의존성 → export → 파일 → 중복
- 각 배치 후 테스트 실행
- 각 배치 후 커밋
### 4. 중복 통합
- 중복 컴포넌트/유틸리티 찾기
- 최선의 구현 선택 (가장 완전하고, 가장 잘 테스트된)
- 모든 import 업데이트, 중복 삭제
- 테스트 통과 확인
## 안전 체크리스트
제거 전:
- [ ] 감지 도구가 미사용 확인
- [ ] Grep이 참조 없음 확인 (동적 포함)
- [ ] 공개 API의 일부가 아님
- [ ] 제거 후 테스트 통과
각 배치 후:
- [ ] Build 성공
- [ ] 테스트 통과
- [ ] 설명적 메시지로 커밋
## 핵심 원칙
1. **작게 시작** -- 한 번에 한 카테고리
2. **자주 테스트** -- 모든 배치 후
3. **보수적으로** -- 확신이 없으면 제거하지 않기
4. **문서화** -- 배치별 설명적 커밋 메시지
5. **절대 제거 금지** -- 활발한 기능 개발 중 또는 배포 전
## 사용하지 말아야 할 때
- 활발한 기능 개발 중
- 프로덕션 배포 직전
- 적절한 테스트 커버리지 없이
- 이해하지 못하는 코드에
## 성공 기준
- 모든 테스트 통과
- Build 성공
- 회귀 없음
- 번들 크기 감소

View File

@@ -0,0 +1,104 @@
---
name: security-reviewer
description: 보안 취약점 감지 및 수정 전문가. 사용자 입력 처리, 인증, API 엔드포인트, 민감한 데이터를 다루는 코드 작성 후 사용하세요. 시크릿, SSRF, 인젝션, 안전하지 않은 암호화, OWASP Top 10 취약점을 플래그합니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# 보안 리뷰어
웹 애플리케이션의 취약점을 식별하고 수정하는 보안 전문 에이전트입니다. 보안 문제가 프로덕션에 도달하기 전에 방지하는 것이 목표입니다.
## 핵심 책임
1. **취약점 감지** — OWASP Top 10 및 일반적인 보안 문제 식별
2. **시크릿 감지** — 하드코딩된 API 키, 비밀번호, 토큰 찾기
3. **입력 유효성 검사** — 모든 사용자 입력이 적절히 소독되는지 확인
4. **인증/인가** — 적절한 접근 제어 확인
5. **의존성 보안** — 취약한 npm 패키지 확인
6. **보안 모범 사례** — 안전한 코딩 패턴 강제
## 분석 커맨드
```bash
npm audit --audit-level=high
npx eslint . --plugin security
```
## 리뷰 워크플로우
### 1. 초기 스캔
- `npm audit`, `eslint-plugin-security` 실행, 하드코딩된 시크릿 검색
- 고위험 영역 검토: 인증, API 엔드포인트, DB 쿼리, 파일 업로드, 결제, 웹훅
### 2. OWASP Top 10 점검
1. **인젝션** — 쿼리 매개변수화? 사용자 입력 소독? ORM 안전 사용?
2. **인증 취약** — 비밀번호 해시(bcrypt/argon2)? JWT 검증? 세션 안전?
3. **민감 데이터** — HTTPS 강제? 시크릿이 환경 변수? PII 암호화? 로그 소독?
4. **XXE** — XML 파서 안전 설정? 외부 엔터티 비활성화?
5. **접근 제어 취약** — 모든 라우트에 인증 확인? CORS 적절히 설정?
6. **잘못된 설정** — 기본 자격증명 변경? 프로덕션에서 디버그 모드 끔? 보안 헤더 설정?
7. **XSS** — 출력 이스케이프? CSP 설정? 프레임워크 자동 이스케이프?
8. **안전하지 않은 역직렬화** — 사용자 입력 안전하게 역직렬화?
9. **알려진 취약점** — 의존성 최신? npm audit 깨끗?
10. **불충분한 로깅** — 보안 이벤트 로깅? 알림 설정?
### 3. 코드 패턴 리뷰
다음 패턴 즉시 플래그:
| 패턴 | 심각도 | 수정 |
|------|--------|------|
| 하드코딩된 시크릿 | CRITICAL | `process.env` 사용 |
| 사용자 입력으로 셸 커맨드 | CRITICAL | 안전한 API 또는 execFile 사용 |
| 문자열 연결 SQL | CRITICAL | 매개변수화된 쿼리 |
| `innerHTML = userInput` | HIGH | `textContent` 또는 DOMPurify 사용 |
| `fetch(userProvidedUrl)` | HIGH | 허용 도메인 화이트리스트 |
| 평문 비밀번호 비교 | CRITICAL | `bcrypt.compare()` 사용 |
| 라우트에 인증 검사 없음 | CRITICAL | 인증 미들웨어 추가 |
| 잠금 없는 잔액 확인 | CRITICAL | 트랜잭션에서 `FOR UPDATE` 사용 |
| Rate limiting 없음 | HIGH | `express-rate-limit` 추가 |
| 비밀번호/시크릿 로깅 | MEDIUM | 로그 출력 소독 |
## 핵심 원칙
1. **심층 방어** — 여러 보안 계층
2. **최소 권한** — 필요한 최소 권한
3. **안전한 실패** — 에러가 데이터를 노출하지 않아야 함
4. **입력 불신** — 모든 것을 검증하고 소독
5. **정기 업데이트** — 의존성을 최신으로 유지
## 일반적인 오탐지
- `.env.example`의 환경 변수 (실제 시크릿이 아님)
- 테스트 파일의 테스트 자격증명 (명확히 표시된 경우)
- 공개 API 키 (실제로 공개 의도인 경우)
- 체크섬용 SHA256/MD5 (비밀번호용이 아님)
**플래그 전에 항상 컨텍스트를 확인하세요.**
## 긴급 대응
CRITICAL 취약점 발견 시:
1. 상세 보고서로 문서화
2. 프로젝트 소유자에게 즉시 알림
3. 안전한 코드 예제 제공
4. 수정이 작동하는지 확인
5. 자격증명 노출 시 시크릿 교체
## 실행 시점
**항상:** 새 API 엔드포인트, 인증 코드 변경, 사용자 입력 처리, DB 쿼리 변경, 파일 업로드, 결제 코드, 외부 API 연동, 의존성 업데이트.
**즉시:** 프로덕션 인시던트, 의존성 CVE, 사용자 보안 보고, 주요 릴리스 전.
## 성공 기준
- CRITICAL 이슈 없음
- 모든 HIGH 이슈 해결
- 코드에 시크릿 없음
- 의존성 최신
- 보안 체크리스트 완료
---
**기억하세요**: 보안은 선택 사항이 아닙니다. 하나의 취약점이 사용자에게 실제 금전적 손실을 줄 수 있습니다. 철저하게, 편집증적으로, 사전에 대응하세요.

View File

@@ -0,0 +1,101 @@
---
name: tdd-guide
description: 테스트 주도 개발 전문가. 테스트 먼저 작성 방법론을 강제합니다. 새 기능 작성, 버그 수정, 코드 리팩토링 시 사용하세요. 80% 이상 테스트 커버리지를 보장합니다.
tools: ["Read", "Write", "Edit", "Bash", "Grep"]
model: sonnet
---
테스트 주도 개발(TDD) 전문가로서 모든 코드가 테스트 우선으로 개발되고 포괄적인 커버리지를 갖추도록 보장합니다.
## 역할
- 테스트 먼저 작성 방법론 강제
- Red-Green-Refactor 사이클 가이드
- 80% 이상 테스트 커버리지 보장
- 포괄적인 테스트 스위트 작성 (단위, 통합, E2E)
- 구현 전에 엣지 케이스 포착
## TDD 워크플로우
### 1. 테스트 먼저 작성 (RED)
기대 동작을 설명하는 실패하는 테스트 작성.
### 2. 테스트 실행 -- 실패 확인
Node.js (npm):
```bash
npm test
```
언어 중립:
- 프로젝트의 기본 테스트 명령을 실행하세요.
- Python: `pytest`
- Go: `go test ./...`
### 3. 최소한의 구현 작성 (GREEN)
테스트를 통과하기에 충분한 코드만.
### 4. 테스트 실행 -- 통과 확인
### 5. 리팩토링 (IMPROVE)
중복 제거, 이름 개선, 최적화 -- 테스트는 그린 유지.
### 6. 커버리지 확인
Node.js (npm):
```bash
npm run test:coverage
# 필수: branches, functions, lines, statements 80% 이상
```
언어 중립:
- 프로젝트의 기본 커버리지 명령을 실행하세요.
- Python: `pytest --cov`
- Go: `go test ./... -cover`
## 필수 테스트 유형
| 유형 | 테스트 대상 | 시점 |
|------|------------|------|
| **단위** | 개별 함수를 격리하여 | 항상 |
| **통합** | API 엔드포인트, 데이터베이스 연산 | 항상 |
| **E2E** | 핵심 사용자 흐름 (Playwright) | 핵심 경로 |
## 반드시 테스트해야 할 엣지 케이스
1. **Null/Undefined** 입력
2. **빈** 배열/문자열
3. **잘못된 타입** 전달
4. **경계값** (최소/최대)
5. **에러 경로** (네트워크 실패, DB 에러)
6. **경쟁 조건** (동시 작업)
7. **대량 데이터** (10k+ 항목으로 성능)
8. **특수 문자** (유니코드, 이모지, SQL 문자)
## 테스트 안티패턴
- 동작 대신 구현 세부사항(내부 상태) 테스트
- 서로 의존하는 테스트 (공유 상태)
- 너무 적은 어설션 (아무것도 검증하지 않는 통과 테스트)
- 외부 의존성 목킹 안 함 (Supabase, Redis, OpenAI 등)
## 품질 체크리스트
- [ ] 모든 공개 함수에 단위 테스트
- [ ] 모든 API 엔드포인트에 통합 테스트
- [ ] 핵심 사용자 흐름에 E2E 테스트
- [ ] 엣지 케이스 커버 (null, empty, invalid)
- [ ] 에러 경로 테스트 (해피 패스만 아닌)
- [ ] 외부 의존성에 mock 사용
- [ ] 테스트가 독립적 (공유 상태 없음)
- [ ] 어설션이 구체적이고 의미 있음
- [ ] 커버리지 80% 이상
## Eval 주도 TDD 부록
TDD 흐름에 eval 주도 개발 통합:
1. 구현 전에 capability + regression eval 정의.
2. 베이스라인 실행 및 실패 시그니처 캡처.
3. 최소한의 통과 변경 구현.
4. 테스트와 eval 재실행; pass@1과 pass@3 보고.
릴리스 핵심 경로는 merge 전에 pass^3 안정성을 목표로 해야 합니다.

View File

@@ -0,0 +1,68 @@
---
name: build-fix
description: 최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
---
# Build 오류 수정
최소한의 안전한 변경으로 build 및 타입 오류를 점진적으로 수정합니다.
## 1단계: Build 시스템 감지
프로젝트의 build 도구를 식별하고 build를 실행합니다:
| 식별 기준 | Build 명령어 |
|-----------|---------------|
| `package.json``build` 스크립트 포함 | `npm run build` 또는 `pnpm build` |
| `tsconfig.json` (TypeScript 전용) | `npx tsc --noEmit` |
| `Cargo.toml` | `cargo build 2>&1` |
| `pom.xml` | `mvn compile` |
| `build.gradle` | `./gradlew compileJava` |
| `go.mod` | `go build ./...` |
| `pyproject.toml` | `python -m compileall .` 또는 `mypy .` |
## 2단계: 오류 파싱 및 그룹화
1. Build 명령어를 실행하고 stderr를 캡처합니다
2. 파일 경로별로 오류를 그룹화합니다
3. 의존성 순서에 따라 정렬합니다 (import/타입 오류를 로직 오류보다 먼저 수정)
4. 진행 상황 추적을 위해 전체 오류 수를 셉니다
## 3단계: 수정 루프 (한 번에 하나의 오류씩)
각 오류에 대해:
1. **파일 읽기** — Read 도구를 사용하여 오류 전후 10줄의 컨텍스트를 확인합니다
2. **진단** — 근본 원인을 식별합니다 (누락된 import, 잘못된 타입, 구문 오류)
3. **최소한으로 수정** — Edit 도구를 사용하여 오류를 해결하는 최소한의 변경을 적용합니다
4. **Build 재실행** — 오류가 해결되었고 새로운 오류가 발생하지 않았는지 확인합니다
5. **다음으로 이동** — 남은 오류를 계속 처리합니다
## 4단계: 안전장치
다음 경우 사용자에게 확인을 요청합니다:
- 수정이 **해결하는 것보다 더 많은 오류를 발생**시키는 경우
- **동일한 오류가 3번 시도 후에도 지속**되는 경우 (더 깊은 문제일 가능성)
- 수정에 **아키텍처 변경이 필요**한 경우 (단순 build 수정이 아님)
- Build 오류가 **누락된 의존성**에서 비롯된 경우 (`npm install`, `cargo add` 등이 필요)
## 5단계: 요약
결과를 표시합니다:
- 수정된 오류 (파일 경로 포함)
- 남아있는 오류 (있는 경우)
- 새로 발생한 오류 (0이어야 함)
- 미해결 문제에 대한 다음 단계 제안
## 복구 전략
| 상황 | 조치 |
|-----------|--------|
| 모듈/import 누락 | 패키지가 설치되어 있는지 확인하고 설치 명령어를 제안합니다 |
| 타입 불일치 | 양쪽 타입 정의를 확인하고 더 좁은 타입을 수정합니다 |
| 순환 의존성 | import 그래프로 순환을 식별하고 분리를 제안합니다 |
| 버전 충돌 | `package.json` / `Cargo.toml`의 버전 제약 조건을 확인합니다 |
| Build 도구 설정 오류 | 설정 파일을 확인하고 정상 동작하는 기본값과 비교합니다 |
안전을 위해 한 번에 하나의 오류씩 수정하세요. 리팩토링보다 최소한의 diff를 선호합니다.

View File

@@ -0,0 +1,79 @@
---
name: checkpoint
description: 워크플로우에서 checkpoint를 생성, 검증, 조회 또는 정리합니다.
---
# Checkpoint 명령어
워크플로우에서 checkpoint를 생성하거나 검증합니다.
## 사용법
`/checkpoint [create|verify|list|clear] [name]`
## Checkpoint 생성
Checkpoint를 생성할 때:
1. `/verify quick`를 실행하여 현재 상태가 깨끗한지 확인합니다
2. Checkpoint 이름으로 git stash 또는 commit을 생성합니다
3. `.claude/checkpoints.log`에 checkpoint를 기록합니다:
```bash
echo "$(date +%Y-%m-%d-%H:%M) | $CHECKPOINT_NAME | $(git rev-parse --short HEAD)" >> .claude/checkpoints.log
```
4. Checkpoint 생성 완료를 보고합니다
## Checkpoint 검증
Checkpoint와 대조하여 검증할 때:
1. 로그에서 checkpoint를 읽습니다
2. 현재 상태를 checkpoint와 비교합니다:
- Checkpoint 이후 추가된 파일
- Checkpoint 이후 수정된 파일
- 현재와 당시의 테스트 통과율
- 현재와 당시의 커버리지
3. 보고:
```
CHECKPOINT COMPARISON: $NAME
============================
Files changed: X
Tests: +Y passed / -Z failed
Coverage: +X% / -Y%
Build: [PASS/FAIL]
```
## Checkpoint 목록
모든 checkpoint를 다음 정보와 함께 표시합니다:
- 이름
- 타임스탬프
- Git SHA
- 상태 (current, behind, ahead)
## 워크플로우
일반적인 checkpoint 흐름:
```
[시작] --> /checkpoint create "feature-start"
|
[구현] --> /checkpoint create "core-done"
|
[테스트] --> /checkpoint verify "core-done"
|
[리팩토링] --> /checkpoint create "refactor-done"
|
[PR] --> /checkpoint verify "feature-start"
```
## 인자
$ARGUMENTS:
- `create <name>` - 이름이 지정된 checkpoint를 생성합니다
- `verify <name>` - 이름이 지정된 checkpoint와 검증합니다
- `list` - 모든 checkpoint를 표시합니다
- `clear` - 이전 checkpoint를 제거합니다 (최근 5개만 유지)

View File

@@ -0,0 +1,40 @@
# 코드 리뷰
커밋되지 않은 변경사항에 대한 포괄적인 보안 및 품질 리뷰를 수행합니다:
1. 변경된 파일 목록 조회: git diff --name-only HEAD
2. 각 변경된 파일에 대해 다음을 검사합니다:
**보안 이슈 (CRITICAL):**
- 하드코딩된 인증 정보, API 키, 토큰
- SQL 인젝션 취약점
- XSS 취약점
- 누락된 입력 유효성 검사
- 안전하지 않은 의존성
- 경로 탐색(Path Traversal) 위험
**코드 품질 (HIGH):**
- 50줄 초과 함수
- 800줄 초과 파일
- 4단계 초과 중첩 깊이
- 누락된 에러 처리
- 디버그 로깅 문구(예: 개발용 로그/print 등)
- TODO/FIXME 주석
- 활성 언어에 대한 공개 API 문서 누락(예: JSDoc/Go doc/Docstring 등)
**모범 사례 (MEDIUM):**
- 변이(Mutation) 패턴 (불변 패턴을 사용하세요)
- 코드/주석의 이모지 사용
- 새 코드에 대한 테스트 누락
- 접근성(a11y) 문제
3. 다음을 포함한 보고서를 생성합니다:
- 심각도: CRITICAL, HIGH, MEDIUM, LOW
- 파일 위치 및 줄 번호
- 이슈 설명
- 수정 제안
4. CRITICAL 또는 HIGH 이슈가 발견되면 commit을 차단합니다
보안 취약점이 있는 코드는 절대 승인하지 마세요!

334
docs/ko-KR/commands/e2e.md Normal file
View File

@@ -0,0 +1,334 @@
---
description: Playwright로 E2E 테스트를 생성하고 실행합니다. 테스트 여정을 만들고, 테스트를 실행하며, 스크린샷/비디오/트레이스를 캡처하고, 아티팩트를 업로드합니다.
---
# E2E 커맨드
이 커맨드는 **e2e-runner** 에이전트를 호출하여 Playwright를 사용한 E2E 테스트를 생성, 유지, 실행합니다.
## 이 커맨드가 하는 것
1. **테스트 여정 생성** - 사용자 흐름에 대한 Playwright 테스트 생성
2. **E2E 테스트 실행** - 여러 브라우저에서 테스트 실행
3. **아티팩트 캡처** - 실패 시 스크린샷, 비디오, 트레이스
4. **결과 업로드** - HTML 보고서 및 JUnit XML
5. **불안정한 테스트 식별** - 불안정한 테스트를 격리
## 사용 시점
`/e2e`를 사용해야 할 때:
- 핵심 사용자 여정 테스트 (로그인, 거래, 결제)
- 다단계 흐름이 E2E로 작동하는지 검증
- UI 인터랙션 및 네비게이션 테스트
- 프론트엔드와 백엔드 간 통합 검증
- 프로덕션 배포 준비
## 작동 방식
e2e-runner 에이전트가 수행하는 작업:
1. **사용자 흐름 분석** 및 테스트 시나리오 식별
2. Page Object Model 패턴을 사용한 **Playwright 테스트 생성**
3. 여러 브라우저(Chrome, Firefox, Safari)에서 **테스트 실행**
4. 스크린샷, 비디오, 트레이스로 **실패 캡처**
5. 결과와 아티팩트로 **보고서 생성**
6. **불안정한 테스트 식별** 및 수정 권장
## 사용 예시
````
User: /e2e 마켓 검색 및 조회 흐름 테스트
Agent (e2e-runner):
# E2E 테스트 생성: 마켓 검색 및 조회 흐름
## 식별된 테스트 시나리오
**사용자 여정:** 마켓 검색 → 결과 확인 → 마켓 클릭 → 상세 정보 확인
**테스트 범위:**
1. 마켓 페이지로 이동
2. 시맨틱 검색 수행
3. 검색 결과 확인
4. 첫 번째 결과 클릭
5. 마켓 상세 페이지 로드 확인
6. 차트 렌더링 확인
## 생성된 테스트 코드
```typescript
// tests/e2e/markets/search-and-view.spec.ts
import { test, expect } from '@playwright/test'
import { MarketsPage } from '../../pages/MarketsPage'
import { MarketDetailsPage } from '../../pages/MarketDetailsPage'
test.describe('Market Search and View Flow', () => {
test('user can search markets and view details', async ({ page }) => {
// 1. 마켓 페이지로 이동
const marketsPage = new MarketsPage(page)
await marketsPage.goto()
// 페이지 로드 확인
await expect(page).toHaveTitle(/Markets/)
await expect(page.locator('h1')).toContainText('Markets')
// 2. 시맨틱 검색 수행
await marketsPage.searchMarkets('election')
// API 응답 대기
await page.waitForResponse(resp =>
resp.url().includes('/api/markets/search') && resp.status() === 200
)
// 3. 검색 결과 확인
const marketCards = marketsPage.marketCards
await expect(marketCards.first()).toBeVisible()
const resultCount = await marketCards.count()
expect(resultCount).toBeGreaterThan(0)
// 검색 결과 스크린샷 촬영
await page.screenshot({ path: 'artifacts/search-results.png' })
// 4. 첫 번째 결과 클릭
const firstMarketTitle = await marketCards.first().textContent()
await marketCards.first().click()
// 5. 마켓 상세 페이지 로드 확인
await expect(page).toHaveURL(/\/markets\/[a-z0-9-]+/)
const detailsPage = new MarketDetailsPage(page)
await expect(detailsPage.marketName).toBeVisible()
await expect(detailsPage.marketDescription).toBeVisible()
// 6. 차트 렌더링 확인
await expect(detailsPage.priceChart).toBeVisible()
// 마켓 이름 일치 확인
const detailsTitle = await detailsPage.marketName.textContent()
expect(detailsTitle?.toLowerCase()).toContain(
firstMarketTitle?.toLowerCase().substring(0, 20) || ''
)
// 마켓 상세 스크린샷 촬영
await page.screenshot({ path: 'artifacts/market-details.png' })
})
test('search with no results shows empty state', async ({ page }) => {
const marketsPage = new MarketsPage(page)
await marketsPage.goto()
// 존재하지 않는 마켓 검색
await marketsPage.searchMarkets('xyznonexistentmarket123456')
// 빈 상태 확인
await expect(page.locator('[data-testid="no-results"]')).toBeVisible()
await expect(page.locator('[data-testid="no-results"]')).toContainText(
/no.*results|no.*markets/i
)
const marketCount = await marketsPage.marketCards.count()
expect(marketCount).toBe(0)
})
test('can clear search and see all markets again', async ({ page }) => {
const marketsPage = new MarketsPage(page)
await marketsPage.goto()
// 초기 마켓 수
const initialCount = await marketsPage.marketCards.count()
// 검색 수행
await marketsPage.searchMarkets('trump')
await page.waitForLoadState('networkidle')
// 필터링된 결과 확인
const filteredCount = await marketsPage.marketCards.count()
expect(filteredCount).toBeLessThan(initialCount)
// 검색 초기화
await marketsPage.searchInput.clear()
await page.waitForLoadState('networkidle')
// 모든 마켓이 다시 표시되는지 확인
const finalCount = await marketsPage.marketCards.count()
expect(finalCount).toBe(initialCount)
})
})
```
## 테스트 실행
```bash
# 생성된 테스트 실행
npx playwright test tests/e2e/markets/search-and-view.spec.ts
Running 3 tests using 3 workers
✓ [chromium] search-and-view.spec.ts:5:3 user can search markets and view details (4.2s)
✓ [chromium] search-and-view.spec.ts:52:3 search with no results shows empty state (1.8s)
✓ [chromium] search-and-view.spec.ts:67:3 can clear search and see all markets again (2.9s)
3 passed (9.1s)
생성된 아티팩트:
- artifacts/search-results.png
- artifacts/market-details.png
- playwright-report/index.html
```
## 테스트 보고서
```
╔══════════════════════════════════════════════════════════════╗
║ E2E 테스트 결과 ║
╠══════════════════════════════════════════════════════════════╣
║ 상태: ✅ 모든 테스트 통과 ║
║ 전체: 3개 테스트 ║
║ 통과: 3 (100%) ║
║ 실패: 0 ║
║ 불안정: 0 ║
║ 소요시간: 9.1s ║
╚══════════════════════════════════════════════════════════════╝
아티팩트:
📸 스크린샷: 2개 파일
📹 비디오: 0개 파일 (실패 시에만)
🔍 트레이스: 0개 파일 (실패 시에만)
📊 HTML 보고서: playwright-report/index.html
보고서 확인: npx playwright show-report
```
✅ CI/CD 통합 준비가 완료된 E2E 테스트 모음!
````
## 테스트 아티팩트
테스트 실행 시 다음 아티팩트가 캡처됩니다:
**모든 테스트:**
- 타임라인과 결과가 포함된 HTML 보고서
- CI 통합을 위한 JUnit XML
**실패 시에만:**
- 실패 상태의 스크린샷
- 테스트의 비디오 녹화
- 디버깅을 위한 트레이스 파일 (단계별 재생)
- 네트워크 로그
- 콘솔 로그
## 아티팩트 확인
```bash
# 브라우저에서 HTML 보고서 확인
npx playwright show-report
# 특정 트레이스 파일 확인
npx playwright show-trace artifacts/trace-abc123.zip
# 스크린샷은 artifacts/ 디렉토리에 저장됨
open artifacts/search-results.png
```
## 불안정한 테스트 감지
테스트가 간헐적으로 실패하는 경우:
```
⚠️ 불안정한 테스트 감지됨: tests/e2e/markets/trade.spec.ts
테스트가 10회 중 7회 통과 (70% 통과율)
일반적인 실패 원인:
"요소 '[data-testid="confirm-btn"]'을 대기하는 중 타임아웃"
권장 수정 사항:
1. 명시적 대기 추가: await page.waitForSelector('[data-testid="confirm-btn"]')
2. 타임아웃 증가: { timeout: 10000 }
3. 컴포넌트의 레이스 컨디션 확인
4. 애니메이션에 의해 요소가 숨겨져 있지 않은지 확인
격리 권장: 수정될 때까지 test.fixme()로 표시
```
## 브라우저 구성
기본적으로 여러 브라우저에서 테스트가 실행됩니다:
- Chromium (데스크톱 Chrome)
- Firefox (데스크톱)
- WebKit (데스크톱 Safari)
- Mobile Chrome (선택 사항)
`playwright.config.ts`에서 브라우저를 조정할 수 있습니다.
## CI/CD 통합
CI 파이프라인에 추가:
```yaml
# .github/workflows/e2e.yml
- name: Install Playwright
run: npx playwright install --with-deps
- name: Run E2E tests
run: npx playwright test
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report/
```
## 모범 사례
**해야 할 것:**
- Page Object Model을 사용하여 유지보수성 향상
- data-testid 속성을 셀렉터로 사용
- 임의의 타임아웃 대신 API 응답을 대기
- 핵심 사용자 여정을 E2E로 테스트
- main에 merge하기 전에 테스트 실행
- 테스트 실패 시 아티팩트 검토
**하지 말아야 할 것:**
- 취약한 셀렉터 사용 (CSS 클래스는 변경될 수 있음)
- 구현 세부사항 테스트
- 프로덕션에 대해 테스트 실행
- 불안정한 테스트 무시
- 실패 시 아티팩트 검토 생략
- E2E로 모든 엣지 케이스 테스트 (단위 테스트 사용)
## 다른 커맨드와의 연동
- `/plan`을 사용하여 테스트할 핵심 여정 식별
- `/tdd`를 사용하여 단위 테스트 (더 빠르고 세밀함)
- `/e2e`를 사용하여 통합 및 사용자 여정 테스트
- `/code-review`를 사용하여 테스트 품질 검증
## 관련 에이전트
이 커맨드는 `e2e-runner` 에이전트를 호출합니다:
`~/.claude/agents/e2e-runner.md`
## 빠른 커맨드
```bash
# 모든 E2E 테스트 실행
npx playwright test
# 특정 테스트 파일 실행
npx playwright test tests/e2e/markets/search.spec.ts
# headed 모드로 실행 (브라우저 표시)
npx playwright test --headed
# 테스트 디버그
npx playwright test --debug
# 테스트 코드 생성
npx playwright codegen http://localhost:3000
# 보고서 확인
npx playwright show-report
```

120
docs/ko-KR/commands/eval.md Normal file
View File

@@ -0,0 +1,120 @@
# Eval 커맨드
평가 기반 개발 워크플로우를 관리합니다.
## 사용법
`/eval [define|check|report|list|clean] [feature-name]`
## 평가 정의
`/eval define feature-name`
새로운 평가 정의를 생성합니다:
1. `.claude/evals/feature-name.md`에 템플릿을 생성합니다:
```markdown
## EVAL: feature-name
Created: $(date)
### Capability Evals
- [ ] [기능 1에 대한 설명]
- [ ] [기능 2에 대한 설명]
### Regression Evals
- [ ] [기존 동작 1이 여전히 작동함]
- [ ] [기존 동작 2이 여전히 작동함]
### Success Criteria
- capability eval에 대해 pass@3 > 90%
- regression eval에 대해 pass^3 = 100%
```
2. 사용자에게 구체적인 기준을 입력하도록 안내합니다
## 평가 확인
`/eval check feature-name`
기능에 대한 평가를 실행합니다:
1. `.claude/evals/feature-name.md`에서 평가 정의를 읽습니다
2. 각 capability eval에 대해:
- 기준 검증을 시도합니다
- PASS/FAIL을 기록합니다
- `.claude/evals/feature-name.log`에 시도를 기록합니다
3. 각 regression eval에 대해:
- 관련 테스트를 실행합니다
- 기준선과 비교합니다
- PASS/FAIL을 기록합니다
4. 현재 상태를 보고합니다:
```
EVAL CHECK: feature-name
========================
Capability: X/Y passing
Regression: X/Y passing
Status: IN PROGRESS / READY
```
## 평가 보고
`/eval report feature-name`
포괄적인 평가 보고서를 생성합니다:
```
EVAL REPORT: feature-name
=========================
Generated: $(date)
CAPABILITY EVALS
----------------
[eval-1]: PASS (pass@1)
[eval-2]: PASS (pass@2) - 재시도 필요했음
[eval-3]: FAIL - 비고 참조
REGRESSION EVALS
----------------
[test-1]: PASS
[test-2]: PASS
[test-3]: PASS
METRICS
-------
Capability pass@1: 67%
Capability pass@3: 100%
Regression pass^3: 100%
NOTES
-----
[이슈, 엣지 케이스 또는 관찰 사항]
RECOMMENDATION
--------------
[SHIP / NEEDS WORK / BLOCKED]
```
## 평가 목록
`/eval list`
모든 평가 정의를 표시합니다:
```
EVAL DEFINITIONS
================
feature-auth [3/5 passing] IN PROGRESS
feature-search [5/5 passing] READY
feature-export [0/4 passing] NOT STARTED
```
## 인자
$ARGUMENTS:
- `define <name>` - 새 평가 정의 생성
- `check <name>` - 평가 실행 및 확인
- `report <name>` - 전체 보고서 생성
- `list` - 모든 평가 표시
- `clean` - 오래된 평가 로그 제거 (최근 10회 실행 유지)

View File

@@ -0,0 +1,183 @@
---
description: Go build 에러, go vet 경고, 린터 이슈를 점진적으로 수정합니다. 최소한의 정밀한 수정을 위해 go-build-resolver 에이전트를 호출합니다.
---
# Go Build and Fix
이 커맨드는 **go-build-resolver** 에이전트를 호출하여 최소한의 변경으로 Go build 에러를 점진적으로 수정합니다.
## 이 커맨드가 하는 것
1. **진단 실행**: `go build`, `go vet`, `staticcheck` 실행
2. **에러 분석**: 파일별로 그룹화하고 심각도순 정렬
3. **점진적 수정**: 한 번에 하나의 에러씩
4. **각 수정 검증**: 각 변경 후 build 재실행
5. **요약 보고**: 수정된 것과 남은 것 표시
## 사용 시점
`/go-build`를 사용해야 할 때:
- `go build ./...`가 에러로 실패할 때
- `go vet ./...`가 이슈를 보고할 때
- `golangci-lint run`이 경고를 보여줄 때
- 모듈 의존성이 깨졌을 때
- 변경사항을 pull한 후 build가 깨졌을 때
## 실행되는 진단 커맨드
```bash
# 기본 build 확인
go build ./...
# 정적 분석
go vet ./...
# 확장 린팅 (사용 가능한 경우)
staticcheck ./...
golangci-lint run
# 모듈 이슈
go mod verify
go mod tidy -v
```
## 예시 세션
````text
User: /go-build
Agent:
# Go Build 해결
## 초기 진단
```bash
$ go build ./...
internal/service/user.go:25:15: undefined: UserRepository
internal/handler/api.go:42:9: cannot use x (type string) as type int
internal/handler/api.go:58:2: missing return at end of function
```
발견된 에러: 3개
## 수정 1: 정의되지 않은 식별자
파일: internal/service/user.go:25
에러: undefined: UserRepository
원인: import 누락
```go
// import 추가
import "project/internal/repository"
// 변경 전
var repo UserRepository
// 변경 후
var repo repository.UserRepository
```
```bash
$ go build ./...
# 2개 에러 남음
```
## 수정 2: 타입 불일치
파일: internal/handler/api.go:42
에러: cannot use x (type string) as type int
```go
// 변경 전
count := params.Get("count")
// 변경 후
countStr := params.Get("count")
count, _ := strconv.Atoi(countStr)
```
```bash
$ go build ./...
# 1개 에러 남음
```
## 수정 3: 누락된 return
파일: internal/handler/api.go:58
에러: missing return at end of function
```go
func GetUser(id string) (*User, error) {
if id == "" {
return nil, ErrInvalidID
}
user := findUser(id)
// 누락된 return 추가
return user, nil
}
```
```bash
$ go build ./...
# Build 성공!
```
## 최종 검증
```bash
$ go vet ./...
# 이슈 없음
$ go test ./...
ok project/internal/service 0.015s
ok project/internal/handler 0.023s
```
## 요약
| 항목 | 수량 |
|------|------|
| 수정된 Build 에러 | 3 |
| 수정된 Vet 경고 | 0 |
| 수정된 파일 | 2 |
| 남은 이슈 | 0 |
Build 상태: ✅ 성공
````
## 자주 발생하는 에러
| 에러 | 일반적인 수정 방법 |
|------|-------------------|
| `undefined: X` | import 추가 또는 오타 수정 |
| `cannot use X as Y` | 타입 변환 또는 할당 수정 |
| `missing return` | return 문 추가 |
| `X does not implement Y` | 누락된 메서드 추가 |
| `import cycle` | 패키지 구조 재구성 |
| `declared but not used` | 변수 제거 또는 사용 |
| `cannot find package` | `go get` 또는 `go mod tidy` |
## 수정 전략
1. **Build 에러 먼저** - 코드가 컴파일되어야 함
2. **Vet 경고 두 번째** - 의심스러운 구조 수정
3. **Lint 경고 세 번째** - 스타일과 모범 사례
4. **한 번에 하나씩** - 각 변경 검증
5. **최소한의 변경** - 리팩토링이 아닌 수정만
## 중단 조건
에이전트가 중단하고 보고하는 경우:
- 3번 시도 후에도 같은 에러가 지속
- 수정이 더 많은 에러를 발생시킴
- 아키텍처 변경이 필요한 경우
- 외부 의존성이 누락된 경우
## 관련 커맨드
- `/go-test` - build 성공 후 테스트 실행
- `/go-review` - 코드 품질 리뷰
- `/verify` - 전체 검증 루프
## 관련 항목
- 에이전트: `agents/go-build-resolver.md`
- 스킬: `skills/golang-patterns/`

View File

@@ -0,0 +1,148 @@
---
description: 관용적 패턴, 동시성 안전성, 에러 처리, 보안에 대한 포괄적인 Go 코드 리뷰. go-reviewer 에이전트를 호출합니다.
---
# Go 코드 리뷰
이 커맨드는 **go-reviewer** 에이전트를 호출하여 Go 전용 포괄적 코드 리뷰를 수행합니다.
## 이 커맨드가 하는 것
1. **Go 변경사항 식별**: `git diff`로 수정된 `.go` 파일 찾기
2. **정적 분석 실행**: `go vet`, `staticcheck`, `golangci-lint` 실행
3. **보안 스캔**: SQL 인젝션, 커맨드 인젝션, 레이스 컨디션 검사
4. **동시성 리뷰**: 고루틴 안전성, 채널 사용, 뮤텍스 패턴 분석
5. **관용적 Go 검사**: Go 컨벤션과 모범 사례 준수 여부 확인
6. **보고서 생성**: 심각도별 이슈 분류
## 사용 시점
`/go-review`를 사용해야 할 때:
- Go 코드를 작성하거나 수정한 후
- Go 변경사항을 커밋하기 전
- Go 코드가 포함된 PR 리뷰 시
- 새 Go 코드베이스에 온보딩할 때
- 관용적 Go 패턴 학습 시
## 리뷰 카테고리
### CRITICAL (반드시 수정)
- SQL/커맨드 인젝션 취약점
- 동기화 없는 레이스 컨디션
- 고루틴 누수
- 하드코딩된 인증 정보
- unsafe 포인터 사용
- 핵심 경로에서 에러 무시
### HIGH (수정 권장)
- 컨텍스트 없는 에러 래핑 누락
- 에러 반환 대신 panic 사용
- 컨텍스트 전파 누락
- 데드락을 유발하는 버퍼 없는 채널
- 인터페이스 미충족 에러
- 뮤텍스 보호 누락
### MEDIUM (고려)
- 비관용적 코드 패턴
- 공개 항목에 godoc 주석 누락
- 비효율적인 문자열 연결
- 슬라이스 사전 할당 누락
- 테이블 기반 테스트 미사용
## 실행되는 자동 검사
```bash
# 정적 분석
go vet ./...
# 고급 검사 (설치된 경우)
staticcheck ./...
golangci-lint run
# 레이스 감지
go build -race ./...
# 보안 취약점
govulncheck ./...
```
## 사용 예시
````text
User: /go-review
Agent:
# Go 코드 리뷰 보고서
## 리뷰된 파일
- internal/handler/user.go (수정됨)
- internal/service/auth.go (수정됨)
## 정적 분석 결과
✓ go vet: 이슈 없음
✓ staticcheck: 이슈 없음
## 발견된 이슈
[CRITICAL] 레이스 컨디션
파일: internal/service/auth.go:45
이슈: 동기화 없이 공유 맵에 접근
```go
var cache = map[string]*Session{} // 동시 접근!
func GetSession(id string) *Session {
return cache[id] // 레이스 컨디션
}
```
수정: sync.RWMutex 또는 sync.Map 사용
```go
var (
cache = map[string]*Session{}
cacheMu sync.RWMutex
)
func GetSession(id string) *Session {
cacheMu.RLock()
defer cacheMu.RUnlock()
return cache[id]
}
```
[HIGH] 에러 컨텍스트 누락
파일: internal/handler/user.go:28
이슈: 컨텍스트 없이 에러 반환
```go
return err // 컨텍스트 없음
```
수정: 컨텍스트와 함께 래핑
```go
return fmt.Errorf("get user %s: %w", userID, err)
```
## 요약
- CRITICAL: 1
- HIGH: 1
- MEDIUM: 0
권장: ❌ CRITICAL 이슈가 수정될 때까지 merge 차단
````
## 승인 기준
| 상태 | 조건 |
|------|------|
| ✅ 승인 | CRITICAL 또는 HIGH 이슈 없음 |
| ⚠️ 경고 | MEDIUM 이슈만 있음 (주의하여 merge) |
| ❌ 차단 | CRITICAL 또는 HIGH 이슈 발견 |
## 다른 커맨드와의 연동
- `/go-test`를 먼저 사용하여 테스트 통과 확인
- `/go-build`를 사용하여 build 에러 발생 시 수정
- `/go-review`를 커밋 전에 사용
- `/code-review`를 사용하여 Go 외 일반적인 관심사항 리뷰
## 관련 항목
- 에이전트: `agents/go-reviewer.md`
- 스킬: `skills/golang-patterns/`, `skills/golang-testing/`

View File

@@ -0,0 +1,268 @@
---
description: Go용 TDD 워크플로우 강제. 테이블 기반 테스트를 먼저 작성한 후 구현. go test -cover로 80% 이상 커버리지 검증.
---
# Go TDD 커맨드
이 커맨드는 관용적 Go 테스팅 패턴을 사용하여 Go 코드에 테스트 주도 개발 방법론을 강제합니다.
## 이 커맨드가 하는 것
1. **타입/인터페이스 정의**: 함수 시그니처를 먼저 스캐폴딩
2. **테이블 기반 테스트 작성**: 포괄적인 테스트 케이스 생성 (RED)
3. **테스트 실행**: 올바른 이유로 테스트가 실패하는지 확인
4. **코드 구현**: 통과하기 위한 최소한의 코드 작성 (GREEN)
5. **리팩토링**: 테스트를 통과시키면서 개선
6. **커버리지 확인**: 80% 이상 커버리지 확보
## 사용 시점
`/go-test`를 사용해야 할 때:
- 새로운 Go 함수 구현
- 기존 코드에 테스트 커버리지 추가
- 버그 수정 (실패하는 테스트를 먼저 작성)
- 핵심 비즈니스 로직 구현
- Go에서 TDD 워크플로우 학습
## TDD 사이클
```
RED → 실패하는 테이블 기반 테스트 작성
GREEN → 통과하기 위한 최소한의 코드 구현
REFACTOR → 코드 개선, 테스트는 통과 유지
REPEAT → 다음 테스트 케이스
```
## 예시 세션
````
User: /go-test 이메일 주소를 검증하는 함수가 필요합니다
Agent:
# TDD 세션: 이메일 검증기
## 1단계: 인터페이스 정의
```go
// validator/email.go
package validator
// ValidateEmail은 주어진 문자열이 유효한 이메일 주소인지 확인합니다.
// 유효하면 nil을 반환하고, 그렇지 않으면 이슈를 설명하는 error를 반환합니다.
func ValidateEmail(email string) error {
panic("not implemented")
}
```
## 2단계: 테이블 기반 테스트 작성 (RED)
```go
// validator/email_test.go
package validator
import (
"testing"
)
func TestValidateEmail(t *testing.T) {
tests := []struct {
name string
email string
wantErr bool
}{
// 유효한 이메일
{"단순 이메일", "user@example.com", false},
{"서브도메인 포함", "user@mail.example.com", false},
{"플러스 포함", "user+tag@example.com", false},
{"점 포함", "first.last@example.com", false},
// 유효하지 않은 이메일
{"빈 문자열", "", true},
{"@ 기호 없음", "userexample.com", true},
{"도메인 없음", "user@", true},
{"로컬 파트 없음", "@example.com", true},
{"이중 @", "user@@example.com", true},
{"공백 포함", "user @example.com", true},
{"TLD 없음", "user@example", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := ValidateEmail(tt.email)
if tt.wantErr && err == nil {
t.Errorf("ValidateEmail(%q) = nil; want error", tt.email)
}
if !tt.wantErr && err != nil {
t.Errorf("ValidateEmail(%q) = %v; want nil", tt.email, err)
}
})
}
}
```
## 3단계: 테스트 실행 - 실패 확인
```bash
$ go test ./validator/...
--- FAIL: TestValidateEmail (0.00s)
--- FAIL: TestValidateEmail/단순_이메일 (0.00s)
panic: not implemented
FAIL
```
✓ 예상대로 테스트 실패 (panic).
## 4단계: 최소한의 코드 구현 (GREEN)
```go
// validator/email.go
package validator
import (
"errors"
"regexp"
)
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
var (
ErrEmailEmpty = errors.New("email cannot be empty")
ErrEmailInvalid = errors.New("email format is invalid")
)
func ValidateEmail(email string) error {
if email == "" {
return ErrEmailEmpty
}
if !emailRegex.MatchString(email) {
return ErrEmailInvalid
}
return nil
}
```
## 5단계: 테스트 실행 - 통과 확인
```bash
$ go test ./validator/...
PASS
ok project/validator 0.003s
```
✓ 모든 테스트 통과!
## 6단계: 커버리지 확인
```bash
$ go test -cover ./validator/...
PASS
coverage: 100.0% of statements
ok project/validator 0.003s
```
✓ 커버리지: 100%
## TDD 완료!
````
## 테스트 패턴
### 테이블 기반 테스트
```go
tests := []struct {
name string
input InputType
want OutputType
wantErr bool
}{
{"케이스 1", input1, want1, false},
{"케이스 2", input2, want2, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := Function(tt.input)
// 단언문
})
}
```
### 병렬 테스트
```go
for _, tt := range tests {
tt := tt // 캡처
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
// 테스트 본문
})
}
```
### 테스트 헬퍼
```go
func setupTestDB(t *testing.T) *sql.DB {
t.Helper()
db := createDB()
t.Cleanup(func() { db.Close() })
return db
}
```
## 커버리지 커맨드
```bash
# 기본 커버리지
go test -cover ./...
# 커버리지 프로파일
go test -coverprofile=coverage.out ./...
# 브라우저에서 확인
go tool cover -html=coverage.out
# 함수별 커버리지
go tool cover -func=coverage.out
# 레이스 감지와 함께
go test -race -cover ./...
```
## 커버리지 목표
| 코드 유형 | 목표 |
|-----------|------|
| 핵심 비즈니스 로직 | 100% |
| 공개 API | 90%+ |
| 일반 코드 | 80%+ |
| 생성된 코드 | 제외 |
## TDD 모범 사례
**해야 할 것:**
- 구현 전에 테스트를 먼저 작성
- 각 변경 후 테스트 실행
- 포괄적인 커버리지를 위해 테이블 기반 테스트 사용
- 구현 세부사항이 아닌 동작 테스트
- 엣지 케이스 포함 (빈 값, nil, 최대값)
**하지 말아야 할 것:**
- 테스트 전에 구현 작성
- RED 단계 건너뛰기
- private 함수를 직접 테스트
- 테스트에서 `time.Sleep` 사용
- 불안정한 테스트 무시
## 관련 커맨드
- `/go-build` - build 에러 수정
- `/go-review` - 구현 후 코드 리뷰
- `/verify` - 전체 검증 루프
## 관련 항목
- 스킬: `skills/golang-testing/`
- 스킬: `skills/tdd-workflow/`

View File

@@ -0,0 +1,70 @@
# /learn - 재사용 가능한 패턴 추출
현재 세션을 분석하고 스킬로 저장할 가치가 있는 패턴을 추출합니다.
## 트리거
세션 중 중요한 문제를 해결했을 때 `/learn`을 실행합니다.
## 추출 대상
다음을 찾습니다:
1. **에러 해결 패턴**
- 어떤 에러가 발생했는가?
- 근본 원인은 무엇이었는가?
- 무엇이 해결했는가?
- 유사한 에러에 재사용 가능한가?
2. **디버깅 기법**
- 직관적이지 않은 디버깅 단계
- 효과적인 도구 조합
- 진단 패턴
3. **우회 방법**
- 라이브러리 특이 사항
- API 제한 사항
- 버전별 수정 사항
4. **프로젝트 특화 패턴**
- 발견된 코드베이스 컨벤션
- 내려진 아키텍처 결정
- 통합 패턴
## 출력 형식
`~/.claude/skills/learned/[pattern-name].md`에 스킬 파일을 생성합니다:
```markdown
# [설명적인 패턴 이름]
**추출일:** [날짜]
**컨텍스트:** [이 패턴이 적용되는 상황에 대한 간략한 설명]
## 문제
[이 패턴이 해결하는 문제 - 구체적으로 작성]
## 해결 방법
[패턴/기법/우회 방법]
## 예시
[해당하는 경우 코드 예시]
## 사용 시점
[트리거 조건 - 이 스킬이 활성화되어야 하는 상황]
```
## 프로세스
1. 세션에서 추출 가능한 패턴 검토
2. 가장 가치 있고 재사용 가능한 인사이트 식별
3. 스킬 파일 초안 작성
4. 저장 전 사용자 확인 요청
5. `~/.claude/skills/learned/`에 저장
## 참고 사항
- 사소한 수정은 추출하지 않기 (오타, 단순 구문 에러)
- 일회성 이슈는 추출하지 않기 (특정 API 장애 등)
- 향후 세션에서 시간을 절약할 수 있는 패턴에 집중
- 스킬은 집중적으로 - 스킬당 하나의 패턴

View File

@@ -0,0 +1,172 @@
# Orchestrate 커맨드
복잡한 작업을 위한 순차적 에이전트 워크플로우입니다.
## 사용법
`/orchestrate [workflow-type] [task-description]`
## 워크플로우 유형
### feature
전체 기능 구현 워크플로우:
```
planner -> tdd-guide -> code-reviewer -> security-reviewer
```
### bugfix
버그 조사 및 수정 워크플로우:
```
planner -> tdd-guide -> code-reviewer
```
### refactor
안전한 리팩토링 워크플로우:
```
architect -> code-reviewer -> tdd-guide
```
### security
보안 중심 리뷰:
```
security-reviewer -> code-reviewer -> architect
```
## 실행 패턴
워크플로우의 각 에이전트에 대해:
1. 이전 에이전트의 컨텍스트로 **에이전트 호출**
2. 구조화된 핸드오프 문서로 **출력 수집**
3. 체인의 **다음 에이전트에 전달**
4. **결과를 종합**하여 최종 보고서 작성
## 핸드오프 문서 형식
에이전트 간에 핸드오프 문서를 생성합니다:
```markdown
## HANDOFF: [이전-에이전트] -> [다음-에이전트]
### Context
[수행된 작업 요약]
### Findings
[주요 발견 사항 또는 결정 사항]
### Files Modified
[수정된 파일 목록]
### Open Questions
[다음 에이전트를 위한 미해결 항목]
### Recommendations
[제안하는 다음 단계]
```
## 예시: Feature 워크플로우
```
/orchestrate feature "Add user authentication"
```
실행 순서:
1. **Planner 에이전트**
- 요구사항 분석
- 구현 계획 작성
- 의존성 식별
- 출력: `HANDOFF: planner -> tdd-guide`
2. **TDD Guide 에이전트**
- planner 핸드오프 읽기
- 테스트 먼저 작성
- 테스트를 통과하도록 구현
- 출력: `HANDOFF: tdd-guide -> code-reviewer`
3. **Code Reviewer 에이전트**
- 구현 리뷰
- 이슈 확인
- 개선사항 제안
- 출력: `HANDOFF: code-reviewer -> security-reviewer`
4. **Security Reviewer 에이전트**
- 보안 감사
- 취약점 점검
- 최종 승인
- 출력: 최종 보고서
## 최종 보고서 형식
```
ORCHESTRATION REPORT
====================
Workflow: feature
Task: Add user authentication
Agents: planner -> tdd-guide -> code-reviewer -> security-reviewer
SUMMARY
-------
[한 단락 요약]
AGENT OUTPUTS
-------------
Planner: [요약]
TDD Guide: [요약]
Code Reviewer: [요약]
Security Reviewer: [요약]
FILES CHANGED
-------------
[수정된 모든 파일 목록]
TEST RESULTS
------------
[테스트 통과/실패 요약]
SECURITY STATUS
---------------
[보안 발견 사항]
RECOMMENDATION
--------------
[SHIP / NEEDS WORK / BLOCKED]
```
## 병렬 실행
독립적인 검사에 대해서는 에이전트를 병렬로 실행합니다:
```markdown
### Parallel Phase
동시에 실행:
- code-reviewer (품질)
- security-reviewer (보안)
- architect (설계)
### Merge Results
출력을 단일 보고서로 통합
```
## 인자
$ARGUMENTS:
- `feature <description>` - 전체 기능 워크플로우
- `bugfix <description>` - 버그 수정 워크플로우
- `refactor <description>` - 리팩토링 워크플로우
- `security <description>` - 보안 리뷰 워크플로우
- `custom <agents> <description>` - 사용자 정의 에이전트 순서
## 사용자 정의 워크플로우 예시
```
/orchestrate custom "architect,tdd-guide,code-reviewer" "Redesign caching layer"
```
## 팁
1. 복잡한 기능에는 **planner부터 시작**하세요
2. merge 전에는 **항상 code-reviewer를 포함**하세요
3. 인증/결제/개인정보 처리에는 **security-reviewer를 사용**하세요
4. **핸드오프는 간결하게** 유지하세요 - 다음 에이전트에 필요한 것에 집중
5. 필요한 경우 에이전트 사이에 **검증을 실행**하세요

113
docs/ko-KR/commands/plan.md Normal file
View File

@@ -0,0 +1,113 @@
---
description: 요구사항을 재확인하고, 위험을 평가하며, 단계별 구현 계획을 작성합니다. 코드를 건드리기 전에 사용자 확인을 기다립니다.
---
# Plan 커맨드
이 커맨드는 **planner** 에이전트를 호출하여 코드를 작성하기 전에 포괄적인 구현 계획을 만듭니다.
## 이 커맨드가 하는 일
1. **요구사항 재확인** - 무엇을 만들어야 하는지 명확히 합니다
2. **위험 식별** - 잠재적 이슈와 차단 요소를 도출합니다
3. **단계별 계획 작성** - 구현을 단계별로 분해합니다
4. **확인 대기** - 진행하기 전에 반드시 사용자 승인을 받아야 합니다
## 사용 시점
`/plan`을 사용해야 할 때:
- 새 기능을 시작할 때
- 중요한 아키텍처 변경을 할 때
- 복잡한 리팩토링 작업을 할 때
- 여러 파일/컴포넌트에 영향을 미칠 때
- 요구사항이 불명확하거나 모호할 때
## 작동 방식
planner 에이전트가 수행하는 작업:
1. 요청을 **분석**하고 요구사항을 명확한 용어로 재확인합니다
2. 구체적이고 실행 가능한 단계로 **분해**합니다
3. 컴포넌트 간 **의존성을 식별**합니다
4. **위험을 평가**하고 잠재적 차단 요소를 파악합니다
5. **복잡도를 추정**합니다 (High/Medium/Low)
6. 계획을 **제시**하고 명시적 확인을 **대기**합니다
## 사용 예시
```
User: /plan 마켓이 해결될 때 실시간 알림을 추가해야 합니다
Agent (planner):
# 구현 계획: 실시간 마켓 해결 알림
## 요구사항 재확인
- 사용자가 주시 중인 마켓이 해결될 때 알림 전송
- 다중 알림 채널 지원 (인앱, 이메일, webhook)
- 알림이 안정적으로 전달되도록 보장
- 마켓 결과와 사용자의 포지션 결과 포함
## 구현 단계
### 1단계: 데이터베이스 스키마
- id, user_id, market_id, type, status, created_at 컬럼을 가진 notifications 테이블 추가
- 채널 설정을 위한 user_notification_preferences 테이블 추가
- 성능을 위해 user_id와 market_id에 인덱스 생성
### 2단계: 알림 서비스
- lib/notifications.ts에 알림 서비스 생성
- BullMQ/Redis를 사용한 알림 큐 구현
- 전송 실패 시 재시도 로직 추가
- 알림 템플릿 생성
### 3단계: 연동 포인트
- 마켓 해결 로직에 연결 (status가 "resolved"로 변경될 때)
- 해당 마켓에 포지션을 가진 모든 사용자 조회
- 각 사용자에 대한 알림 큐에 추가
### 4단계: 프론트엔드 컴포넌트
- 헤더에 NotificationBell 컴포넌트 생성
- NotificationList 모달 추가
- Supabase subscriptions를 통한 실시간 업데이트 구현
- 알림 설정 페이지 추가
## 의존성
- Redis (큐용)
- 이메일 서비스 (SendGrid/Resend)
- Supabase 실시간 subscriptions
## 위험
- HIGH: 이메일 전달률 (SPF/DKIM 필요)
- MEDIUM: 마켓당 1000명 이상의 사용자 시 성능
- MEDIUM: 마켓이 자주 해결될 경우 알림 스팸
- LOW: 실시간 subscription 오버헤드
## 예상 복잡도: MEDIUM
- 백엔드: 4-6시간
- 프론트엔드: 3-4시간
- 테스트: 2-3시간
- 합계: 9-13시간
**확인 대기 중**: 이 계획으로 진행할까요? (yes/no/modify)
```
## 중요 참고 사항
**핵심**: planner 에이전트는 "yes"나 "proceed" 같은 긍정적 응답으로 명시적으로 계획을 확인하기 전까지 코드를 **절대 작성하지 않습니다.**
변경을 원하면 다음과 같이 응답하세요:
- "modify: [변경 사항]"
- "different approach: [대안]"
- "skip phase 2 and do phase 3 first"
## 다른 커맨드와의 연계
계획 수립 후:
- `/tdd`를 사용하여 테스트 주도 개발로 구현
- 빌드 에러 발생 시 `/build-fix` 사용
- 완성된 구현을 `/code-review`로 리뷰
## 관련 에이전트
이 커맨드는 다음 위치의 `planner` 에이전트를 호출합니다:
`~/.claude/agents/planner.md`

View File

@@ -0,0 +1,80 @@
# Refactor Clean
사용하지 않는 코드를 안전하게 식별하고 매 단계마다 테스트 검증을 수행하여 제거합니다.
## 1단계: 사용하지 않는 코드 감지
프로젝트 유형에 따라 분석 도구를 실행합니다:
| 도구 | 감지 대상 | 커맨드 |
|------|----------|--------|
| knip | 미사용 exports, 파일, 의존성 | `npx knip` |
| depcheck | 미사용 npm 의존성 | `npx depcheck` |
| ts-prune | 미사용 TypeScript exports | `npx ts-prune` |
| vulture | 미사용 Python 코드 | `vulture src/` |
| deadcode | 미사용 Go 코드 | `deadcode ./...` |
| cargo-udeps | 미사용 Rust 의존성 | `cargo +nightly udeps` |
사용 가능한 도구가 없는 경우, Grep을 사용하여 import가 없는 export를 찾습니다:
```
# export를 찾은 후, 다른 곳에서 import되는지 확인
```
## 2단계: 결과 분류
안전 등급별로 결과를 분류합니다:
| 등급 | 예시 | 조치 |
|------|------|------|
| **안전** | 미사용 유틸리티, 테스트 헬퍼, 내부 함수 | 확신을 가지고 삭제 |
| **주의** | 컴포넌트, API 라우트, 미들웨어 | 동적 import나 외부 소비자가 없는지 확인 |
| **위험** | 설정 파일, 엔트리 포인트, 타입 정의 | 건드리기 전에 조사 필요 |
## 3단계: 안전한 삭제 루프
각 안전 항목에 대해:
1. **전체 테스트 스위트 실행** --- 기준선 확립 (모두 통과)
2. **사용하지 않는 코드 삭제** --- Edit 도구로 정밀하게 제거
3. **테스트 스위트 재실행** --- 깨진 것이 없는지 확인
4. **테스트 실패 시** --- 즉시 `git checkout -- <file>`로 되돌리고 해당 항목을 건너뜀
5. **테스트 통과 시** --- 다음 항목으로 이동
## 4단계: 주의 항목 처리
주의 항목을 삭제하기 전에:
- 동적 import 검색: `import()`, `require()`, `__import__`
- 문자열 참조 검색: 라우트 이름, 설정 파일의 컴포넌트 이름
- 공개 패키지 API에서 export되는지 확인
- 외부 소비자가 없는지 확인 (게시된 경우 의존 패키지 확인)
## 5단계: 중복 통합
사용하지 않는 코드를 제거한 후 다음을 찾습니다:
- 거의 중복된 함수 (80% 이상 유사) --- 하나로 병합
- 중복된 타입 정의 --- 통합
- 가치를 추가하지 않는 래퍼 함수 --- 인라인 처리
- 목적이 없는 re-export --- 간접 참조 제거
## 6단계: 요약
결과를 보고합니다:
```
Dead Code Cleanup
──────────────────────────────
삭제: 미사용 함수 12개
미사용 파일 3개
미사용 의존성 5개
건너뜀: 항목 2개 (테스트 실패)
절감: 약 450줄 제거
──────────────────────────────
모든 테스트 통과 ✅
```
## 규칙
- **테스트를 먼저 실행하지 않고 절대 삭제하지 않기**
- **한 번에 하나씩 삭제** --- 원자적 변경으로 롤백이 쉬움
- **확실하지 않으면 건너뛰기** --- 프로덕션을 깨뜨리는 것보다 사용하지 않는 코드를 유지하는 것이 나음
- **정리하면서 리팩토링하지 않기** --- 관심사 분리 (먼저 정리, 나중에 리팩토링)

View File

@@ -0,0 +1,80 @@
---
description: 선호하는 패키지 매니저(npm/pnpm/yarn/bun) 설정
disable-model-invocation: true
---
# 패키지 매니저 설정
프로젝트 또는 전역으로 선호하는 패키지 매니저를 설정합니다.
## 사용법
```bash
# 현재 패키지 매니저 감지
node scripts/setup-package-manager.js --detect
# 전역 설정
node scripts/setup-package-manager.js --global pnpm
# 프로젝트 설정
node scripts/setup-package-manager.js --project bun
# 사용 가능한 패키지 매니저 목록
node scripts/setup-package-manager.js --list
```
## 감지 우선순위
패키지 매니저를 결정할 때 다음 순서로 확인합니다:
1. **환경 변수**: `CLAUDE_PACKAGE_MANAGER`
2. **프로젝트 설정**: `.claude/package-manager.json`
3. **package.json**: `packageManager` 필드
4. **락 파일**: package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb의 존재 여부
5. **전역 설정**: `~/.claude/package-manager.json`
6. **폴백**: `npm`
## 설정 파일
### 전역 설정
```json
// ~/.claude/package-manager.json
{
"packageManager": "pnpm"
}
```
### 프로젝트 설정
```json
// .claude/package-manager.json
{
"packageManager": "bun"
}
```
### package.json
```json
{
"packageManager": "pnpm@8.6.0"
}
```
## 환경 변수
`CLAUDE_PACKAGE_MANAGER`를 설정하면 다른 모든 감지 방법을 무시합니다:
```bash
# Windows (PowerShell)
$env:CLAUDE_PACKAGE_MANAGER = "pnpm"
# macOS/Linux
export CLAUDE_PACKAGE_MANAGER=pnpm
```
## 감지 실행
현재 패키지 매니저 감지 결과를 확인하려면 다음을 실행하세요:
```bash
node scripts/setup-package-manager.js --detect
```

326
docs/ko-KR/commands/tdd.md Normal file
View File

@@ -0,0 +1,326 @@
---
description: 테스트 주도 개발 워크플로우 강제. 인터페이스를 스캐폴딩하고, 테스트를 먼저 생성한 후 통과할 최소한의 코드를 구현합니다. 80% 이상 커버리지를 보장합니다.
---
# TDD 커맨드
이 커맨드는 **tdd-guide** 에이전트를 호출하여 테스트 주도 개발 방법론을 강제합니다.
## 이 커맨드가 하는 것
1. **인터페이스 스캐폴딩** - 타입/인터페이스를 먼저 정의
2. **테스트 먼저 생성** - 실패하는 테스트 작성 (RED)
3. **최소한의 코드 구현** - 통과하기에 충분한 코드만 작성 (GREEN)
4. **리팩토링** - 테스트를 통과시키면서 코드 개선 (REFACTOR)
5. **커버리지 확인** - 80% 이상 테스트 커버리지 보장
## 사용 시점
`/tdd`를 사용해야 할 때:
- 새 기능 구현
- 새 함수/컴포넌트 추가
- 버그 수정 (버그를 재현하는 테스트를 먼저 작성)
- 기존 코드 리팩토링
- 핵심 비즈니스 로직 구현
## 작동 방식
tdd-guide 에이전트가 수행하는 작업:
1. 입출력에 대한 **인터페이스 정의**
2. (코드가 아직 존재하지 않으므로) **실패하는 테스트 작성**
3. 올바른 이유로 실패하는지 **테스트 실행** 및 확인
4. 테스트를 통과하도록 **최소한의 구현 작성**
5. 통과하는지 **테스트 실행** 및 확인
6. 테스트를 통과시키면서 코드 **리팩토링**
7. **커버리지 확인** 및 80% 미만이면 테스트 추가
## TDD 사이클
```
RED → GREEN → REFACTOR → REPEAT
RED: 실패하는 테스트 작성
GREEN: 통과할 최소한의 코드 작성
REFACTOR: 코드 개선, 테스트 계속 통과 유지
REPEAT: 다음 기능/시나리오
```
## 사용 예시
````
User: /tdd 마켓 유동성 점수를 계산하는 함수가 필요합니다
Agent (tdd-guide):
# TDD 세션: 마켓 유동성 점수 계산기
## 1단계: 인터페이스 정의 (SCAFFOLD)
```typescript
// lib/liquidity.ts
export interface MarketData {
totalVolume: number
bidAskSpread: number
activeTraders: number
lastTradeTime: Date
}
export function calculateLiquidityScore(market: MarketData): number {
// TODO: 구현
throw new Error('Not implemented')
}
```
## 2단계: 실패하는 테스트 작성 (RED)
```typescript
// lib/liquidity.test.ts
import { calculateLiquidityScore } from './liquidity'
describe('calculateLiquidityScore', () => {
it('should return high score for liquid market', () => {
const market = {
totalVolume: 100000,
bidAskSpread: 0.01,
activeTraders: 500,
lastTradeTime: new Date()
}
const score = calculateLiquidityScore(market)
expect(score).toBeGreaterThan(80)
expect(score).toBeLessThanOrEqual(100)
})
it('should return low score for illiquid market', () => {
const market = {
totalVolume: 100,
bidAskSpread: 0.5,
activeTraders: 2,
lastTradeTime: new Date(Date.now() - 86400000) // 1일 전
}
const score = calculateLiquidityScore(market)
expect(score).toBeLessThan(30)
expect(score).toBeGreaterThanOrEqual(0)
})
it('should handle edge case: zero volume', () => {
const market = {
totalVolume: 0,
bidAskSpread: 0,
activeTraders: 0,
lastTradeTime: new Date()
}
const score = calculateLiquidityScore(market)
expect(score).toBe(0)
})
})
```
## 3단계: 테스트 실행 - 실패 확인
```bash
npm test lib/liquidity.test.ts
FAIL lib/liquidity.test.ts
✕ should return high score for liquid market (2 ms)
Error: Not implemented
1 test failed, 0 passed
```
✅ 예상대로 테스트 실패. 구현 준비 완료.
## 4단계: 최소한의 코드 구현 (GREEN)
```typescript
// lib/liquidity.ts
export function calculateLiquidityScore(market: MarketData): number {
// 거래량 0인 엣지 케이스 처리
if (market.totalVolume === 0) {
return 0
}
// 구성 요소별 점수 계산 (0-100 스케일)
const volumeScore = Math.min(market.totalVolume / 1000, 100)
const spreadScore = Math.max(100 - (market.bidAskSpread * 1000), 0)
const traderScore = Math.min(market.activeTraders / 10, 100)
// 최근 활동 보너스
const hoursSinceLastTrade = (Date.now() - market.lastTradeTime.getTime()) / (1000 * 60 * 60)
const recencyScore = Math.max(100 - (hoursSinceLastTrade * 10), 0)
// 가중 평균
const score = (
volumeScore * 0.4 +
spreadScore * 0.3 +
traderScore * 0.2 +
recencyScore * 0.1
)
return Math.min(Math.max(score, 0), 100) // 0-100으로 클램핑
}
```
## 5단계: 테스트 실행 - 통과 확인
```bash
npm test lib/liquidity.test.ts
PASS lib/liquidity.test.ts
✓ should return high score for liquid market (3 ms)
✓ should return low score for illiquid market (2 ms)
✓ should handle edge case: zero volume (1 ms)
3 tests passed
```
✅ 모든 테스트 통과!
## 6단계: 리팩토링 (IMPROVE)
```typescript
// lib/liquidity.ts - 상수와 가독성 향상을 위한 리팩토링
const WEIGHTS = {
VOLUME: 0.4,
SPREAD: 0.3,
TRADERS: 0.2,
RECENCY: 0.1,
} as const
const SCALE_FACTORS = {
VOLUME: 1000,
SPREAD: 1000,
TRADERS: 10,
RECENCY_PENALTY: 10,
} as const
function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
}
export function calculateLiquidityScore(market: MarketData): number {
if (market.totalVolume === 0) return 0
const volumeScore = Math.min(market.totalVolume / SCALE_FACTORS.VOLUME, 100)
const spreadScore = clamp(100 - (market.bidAskSpread * SCALE_FACTORS.SPREAD), 0, 100)
const traderScore = Math.min(market.activeTraders / SCALE_FACTORS.TRADERS, 100)
const hoursSinceLastTrade = (Date.now() - market.lastTradeTime.getTime()) / (1000 * 60 * 60)
const recencyScore = clamp(100 - (hoursSinceLastTrade * SCALE_FACTORS.RECENCY_PENALTY), 0, 100)
const weightedScore =
volumeScore * WEIGHTS.VOLUME +
spreadScore * WEIGHTS.SPREAD +
traderScore * WEIGHTS.TRADERS +
recencyScore * WEIGHTS.RECENCY
return clamp(weightedScore, 0, 100)
}
```
## 7단계: 테스트가 여전히 통과하는지 확인
```bash
npm test lib/liquidity.test.ts
PASS lib/liquidity.test.ts
✓ should return high score for liquid market (3 ms)
✓ should return low score for illiquid market (2 ms)
✓ should handle edge case: zero volume (1 ms)
3 tests passed
```
✅ 리팩토링 완료, 테스트 여전히 통과!
## 8단계: 커버리지 확인
```bash
npm test -- --coverage lib/liquidity.test.ts
File | % Stmts | % Branch | % Funcs | % Lines
---------------|---------|----------|---------|--------
liquidity.ts | 100 | 100 | 100 | 100
Coverage: 100% ✅ (목표: 80%)
```
✅ TDD 세션 완료!
````
## TDD 모범 사례
**해야 할 것:**
- 구현 전에 테스트를 먼저 작성
- 구현 전에 테스트를 실행하여 실패하는지 확인
- 테스트를 통과하기 위한 최소한의 코드 작성
- 테스트가 통과한 후에만 리팩토링
- 엣지 케이스와 에러 시나리오 추가
- 80% 이상 커버리지 목표 (핵심 코드는 100%)
**하지 말아야 할 것:**
- 테스트 전에 구현 작성
- 각 변경 후 테스트 실행 건너뛰기
- 한 번에 너무 많은 코드 작성
- 실패하는 테스트 무시
- 구현 세부사항 테스트 (동작을 테스트)
- 모든 것을 mock (통합 테스트 선호)
## 포함할 테스트 유형
**단위 테스트** (함수 수준):
- 정상 경로 시나리오
- 엣지 케이스 (빈 값, null, 최대값)
- 에러 조건
- 경계값
**통합 테스트** (컴포넌트 수준):
- API 엔드포인트
- 데이터베이스 작업
- 외부 서비스 호출
- hooks가 포함된 React 컴포넌트
**E2E 테스트** (`/e2e` 커맨드 사용):
- 핵심 사용자 흐름
- 다단계 프로세스
- 풀 스택 통합
## 커버리지 요구사항
- **80% 최소** - 모든 코드에 대해
- **100% 필수** - 다음 항목에 대해:
- 금융 계산
- 인증 로직
- 보안에 중요한 코드
- 핵심 비즈니스 로직
## 중요 사항
**필수**: 테스트는 반드시 구현 전에 작성해야 합니다. TDD 사이클은 다음과 같습니다:
1. **RED** - 실패하는 테스트 작성
2. **GREEN** - 통과하도록 구현
3. **REFACTOR** - 코드 개선
절대 RED 단계를 건너뛰지 마세요. 절대 테스트 전에 코드를 작성하지 마세요.
## 다른 커맨드와의 연동
- `/plan`을 먼저 사용하여 무엇을 만들지 이해
- `/tdd`를 사용하여 테스트와 함께 구현
- `/build-fix`를 사용하여 빌드 에러 발생 시 수정
- `/code-review`를 사용하여 구현 리뷰
- `/test-coverage`를 사용하여 커버리지 검증
## 관련 에이전트
이 커맨드는 `tdd-guide` 에이전트를 호출합니다:
`~/.claude/agents/tdd-guide.md`
그리고 `tdd-workflow` 스킬을 참조할 수 있습니다:
`~/.claude/skills/tdd-workflow/`

View File

@@ -0,0 +1,74 @@
---
name: test-coverage
description: 테스트 커버리지를 분석하고, 80% 이상을 목표로 누락된 테스트를 식별하고 생성합니다.
---
# 테스트 커버리지
테스트 커버리지를 분석하고, 갭을 식별하며, 80% 이상 커버리지 달성을 위해 누락된 테스트를 생성합니다.
## 1단계: 테스트 프레임워크 감지
| 지표 | 커버리지 커맨드 |
|------|----------------|
| `jest.config.*` 또는 `package.json` jest | `npx jest --coverage --coverageReporters=json-summary` |
| `vitest.config.*` | `npx vitest run --coverage` |
| `pytest.ini` / `pyproject.toml` pytest | `pytest --cov=src --cov-report=json` |
| `Cargo.toml` | `cargo llvm-cov --json` |
| `pom.xml` with JaCoCo | `mvn test jacoco:report` |
| `go.mod` | `go test -coverprofile=coverage.out ./...` |
## 2단계: 커버리지 보고서 분석
1. 커버리지 커맨드 실행
2. 출력 파싱 (JSON 요약 또는 터미널 출력)
3. **80% 미만인 파일**을 최저순으로 정렬하여 목록화
4. 각 커버리지 미달 파일에 대해 다음을 식별:
- 테스트되지 않은 함수 또는 메서드
- 누락된 분기 커버리지 (if/else, switch, 에러 경로)
- 분모를 부풀리는 데드 코드
## 3단계: 누락된 테스트 생성
각 커버리지 미달 파일에 대해 다음 우선순위에 따라 테스트를 생성합니다:
1. **Happy path** — 유효한 입력의 핵심 기능
2. **에러 처리** — 잘못된 입력, 누락된 데이터, 네트워크 실패
3. **엣지 케이스** — 빈 배열, null/undefined, 경계값 (0, -1, MAX_INT)
4. **분기 커버리지** — 각 if/else, switch case, 삼항 연산자
### 테스트 생성 규칙
- 소스 파일 옆에 테스트 배치: `foo.ts``foo.test.ts` (또는 프로젝트 컨벤션에 따름)
- 프로젝트의 기존 테스트 패턴 사용 (import 스타일, assertion 라이브러리, mocking 방식)
- 외부 의존성 mock 처리 (데이터베이스, API, 파일 시스템)
- 각 테스트는 독립적이어야 함 — 테스트 간 공유 가변 상태 없음
- 테스트 이름은 설명적으로: `test_create_user_with_duplicate_email_returns_409`
## 4단계: 검증
1. 전체 테스트 스위트 실행 — 모든 테스트가 통과해야 함
2. 커버리지 재실행 — 개선 확인
3. 여전히 80% 미만이면 나머지 갭에 대해 3단계 반복
## 5단계: 보고서
이전/이후 비교를 표시합니다:
```
커버리지 보고서
──────────────────────────────
파일 이전 이후
src/services/auth.ts 45% 88%
src/utils/validation.ts 32% 82%
──────────────────────────────
전체: 67% 84% ✅
```
## 집중 영역
- 복잡한 분기가 있는 함수 (높은 순환 복잡도)
- 에러 핸들러와 catch 블록
- 코드베이스 전반에서 사용되는 유틸리티 함수
- API 엔드포인트 핸들러 (요청 → 응답 흐름)
- 엣지 케이스: null, undefined, 빈 문자열, 빈 배열, 0, 음수

View File

@@ -0,0 +1,79 @@
# 코드맵 업데이트
코드베이스 구조를 분석하고 토큰 효율적인 아키텍처 문서를 생성합니다.
## 1단계: 프로젝트 구조 스캔
1. 프로젝트 유형 식별 (모노레포, 단일 앱, 라이브러리, 마이크로서비스)
2. 모든 소스 디렉토리 찾기 (src/, lib/, app/, packages/)
3. 엔트리 포인트 매핑 (main.ts, index.ts, app.py, main.go 등)
## 2단계: 코드맵 생성
`docs/CODEMAPS/`에 코드맵 생성 또는 업데이트:
| 파일 | 내용 |
|------|------|
| `INDEX.md` | 전체 코드베이스 개요와 영역별 링크 |
| `backend.md` | API 라우트, 미들웨어 체인, 서비스 → 리포지토리 매핑 |
| `frontend.md` | 페이지 트리, 컴포넌트 계층, 상태 관리 흐름 |
| `database.md` | 데이터베이스 스키마, 마이그레이션, 저장소 계층 |
| `integrations.md` | 외부 서비스, 서드파티 통합, 어댑터 |
| `workers.md` | 백그라운드 작업, 큐, 스케줄러 |
### 코드맵 형식
각 코드맵은 토큰 효율적이어야 합니다 — AI 컨텍스트 소비에 최적화:
```markdown
# Backend 아키텍처
## 라우트
POST /api/users → UserController.create → UserService.create → UserRepo.insert
GET /api/users/:id → UserController.get → UserService.findById → UserRepo.findById
## 주요 파일
src/services/user.ts (비즈니스 로직, 120줄)
src/repos/user.ts (데이터베이스 접근, 80줄)
## 의존성
- PostgreSQL (주 데이터 저장소)
- Redis (세션 캐시, 속도 제한)
- Stripe (결제 처리)
```
## 3단계: 영역 분류
생성기는 파일 경로 패턴을 기반으로 영역을 자동 분류합니다:
1. 프론트엔드: `app/`, `pages/`, `components/`, `hooks/`, `.tsx`, `.jsx`
2. 백엔드: `api/`, `routes/`, `controllers/`, `services/`, `.route.ts`
3. 데이터베이스: `db/`, `migrations/`, `prisma/`, `repositories/`
4. 통합: `integrations/`, `adapters/`, `connectors/`, `plugins/`
5. 워커: `workers/`, `jobs/`, `queues/`, `tasks/`, `cron/`
## 4단계: 메타데이터 추가
각 코드맵에 최신 정보 헤더를 추가합니다:
```markdown
**Last Updated:** 2026-03-12
**Total Files:** 42
**Total Lines:** 1875
```
## 5단계: 인덱스와 영역 문서 동기화
`INDEX.md`는 생성된 영역 문서를 링크하고 요약해야 합니다:
- 각 영역의 파일 수와 총 라인 수
- 감지된 엔트리 포인트
- 저장소 트리의 간단한 ASCII 개요
- 영역별 세부 문서 링크
## 팁
- **구현 세부사항이 아닌 상위 구조**에 집중
- 전체 코드 블록 대신 **파일 경로와 함수 시그니처** 사용
- 효율적인 컨텍스트 로딩을 위해 각 코드맵을 **1000 토큰 미만**으로 유지
- 장황한 설명 대신 데이터 흐름에 ASCII 다이어그램 사용
- 주요 기능 추가 또는 리팩토링 세션 후 `npx tsx scripts/codemaps/generate.ts` 실행

View File

@@ -0,0 +1,89 @@
---
name: update-docs
description: 코드베이스를 기준으로 문서를 동기화하고 생성된 섹션을 갱신합니다.
---
# 문서 업데이트
문서를 코드베이스와 동기화하고, 원본 소스 파일에서 생성합니다.
## 1단계: 원본 소스 식별
| 소스 | 생성 대상 |
|------|----------|
| `package.json` scripts | 사용 가능한 커맨드 참조 |
| `.env.example` | 환경 변수 문서 |
| `openapi.yaml` / 라우트 파일 | API 엔드포인트 참조 |
| 소스 코드 exports | 공개 API 문서 |
| `Dockerfile` / `docker-compose.yml` | 인프라 설정 문서 |
## 2단계: 스크립트 참조 생성
1. `package.json` (또는 `Makefile`, `Cargo.toml`, `pyproject.toml`) 읽기
2. 모든 스크립트/커맨드와 설명 추출
3. 참조 테이블 생성:
```markdown
| 커맨드 | 설명 |
|--------|------|
| `npm run dev` | hot reload로 개발 서버 시작 |
| `npm run build` | 타입 체크 포함 프로덕션 빌드 |
| `npm test` | 커버리지 포함 테스트 스위트 실행 |
```
## 3단계: 환경 변수 문서 생성
1. `.env.example` (또는 `.env.template`, `.env.sample`) 읽기
2. 모든 변수와 용도 추출
3. 필수 vs 선택으로 분류
4. 예상 형식과 유효 값 문서화
```markdown
| 변수 | 필수 | 설명 | 예시 |
|------|------|------|------|
| `DATABASE_URL` | 예 | PostgreSQL 연결 문자열 | `postgres://user:pass@host:5432/db` |
| `LOG_LEVEL` | 아니오 | 로깅 상세도 (기본값: info) | `debug`, `info`, `warn`, `error` |
```
## 4단계: 기여 가이드 업데이트
`docs/CONTRIBUTING.md`를 생성 또는 업데이트합니다:
- 개발 환경 설정 (사전 요구 사항, 설치 단계)
- 사용 가능한 스크립트와 용도
- 테스트 절차 (실행 방법, 새 테스트 작성 방법)
- 코드 스타일 적용 (linter, formatter, pre-commit hook)
- PR 제출 체크리스트
## 5단계: 운영 매뉴얼 업데이트
`docs/RUNBOOK.md`를 생성 또는 업데이트합니다:
- 배포 절차 (단계별)
- 헬스 체크 엔드포인트 및 모니터링
- 일반적인 이슈와 해결 방법
- 롤백 절차
- 알림 및 에스컬레이션 경로
## 6단계: 오래된 항목 점검
1. 90일 이상 수정되지 않은 문서 파일 찾기
2. 최근 소스 코드 변경 사항과 교차 참조
3. 잠재적으로 오래된 문서를 수동 검토 대상으로 표시
## 7단계: 요약 표시
```
문서 업데이트
──────────────────────────────
업데이트: docs/CONTRIBUTING.md (스크립트 테이블)
업데이트: docs/ENV.md (새 변수 3개)
플래그: docs/DEPLOY.md (142일 경과)
건너뜀: docs/API.md (변경 사항 없음)
──────────────────────────────
```
## 규칙
- **단일 원본**: 항상 코드에서 생성하고, 생성된 섹션을 수동으로 편집하지 않기
- **수동 섹션 보존**: 생성된 섹션만 업데이트; 수기 작성 내용은 그대로 유지
- **생성된 콘텐츠 표시**: 생성된 섹션 주변에 `<!-- AUTO-GENERATED -->` 마커 사용
- **요청 없이 문서 생성하지 않기**: 커맨드가 명시적으로 요청한 경우에만 새 문서 파일 생성

View File

@@ -0,0 +1,63 @@
# 검증 커맨드
현재 코드베이스 상태에 대한 포괄적인 검증을 실행합니다.
## 지시사항
정확히 이 순서로 검증을 실행하세요:
1. **Build 검사**
- 이 프로젝트의 build 커맨드 실행
- 실패 시 에러를 보고하고 중단
2. **타입 검사**
- TypeScript/타입 체커 실행
- 모든 에러를 파일:줄번호로 보고
3. **Lint 검사**
- 린터 실행
- 경고와 에러 보고
4. **테스트 실행**
- 모든 테스트 실행
- 통과/실패 수 보고
- 커버리지 비율 보고
5. **시크릿 스캔**
- 소스 파일에서 API 키, 토큰, 비밀값 패턴 검색
- 발견 위치 보고
6. **Console.log 감사**
- 소스 파일에서 console.log 검색
- 위치 보고
7. **Git 상태**
- 커밋되지 않은 변경사항 표시
- 마지막 커밋 이후 수정된 파일 표시
## 출력
간결한 검증 보고서를 생성합니다:
```
VERIFICATION: [PASS/FAIL]
Build: [OK/FAIL]
Types: [OK/X errors]
Lint: [OK/X issues]
Tests: [X/Y passed, Z% coverage]
Secrets: [OK/X found]
Logs: [OK/X console.logs]
Ready for PR: [YES/NO]
```
치명적 이슈가 있으면 수정 제안과 함께 목록화합니다.
## 인자
$ARGUMENTS:
- `quick` - build + 타입만
- `full` - 모든 검사 (기본값)
- `pre-commit` - 커밋에 관련된 검사
- `pre-pr` - 전체 검사 + 보안 스캔

View File

@@ -0,0 +1,100 @@
# 프로젝트 CLAUDE.md 예제
프로젝트 수준의 CLAUDE.md 파일 예제입니다. 프로젝트 루트에 배치하세요.
## 프로젝트 개요
[프로젝트에 대한 간단한 설명 - 기능, 기술 스택]
## 핵심 규칙
### 1. 코드 구성
- 큰 파일 소수보다 작은 파일 다수를 선호
- 높은 응집도, 낮은 결합도
- 일반적으로 200-400줄, 파일당 최대 800줄
- 타입별이 아닌 기능/도메인별로 구성
### 2. 코드 스타일
- 코드, 주석, 문서에 이모지 사용 금지
- 항상 불변성 유지 - 객체나 배열을 직접 변경하지 않음
- 프로덕션 코드에 console.log 사용 금지
- try/catch를 사용한 적절한 에러 처리
- Zod 또는 유사 라이브러리를 사용한 입력 유효성 검사
### 3. 테스트
- TDD: 테스트를 먼저 작성
- 최소 80% 커버리지
- 유틸리티에 대한 단위 테스트
- API에 대한 통합 테스트
- 핵심 흐름에 대한 E2E 테스트
### 4. 보안
- 하드코딩된 시크릿 금지
- 민감한 데이터는 환경 변수 사용
- 모든 사용자 입력 유효성 검사
- 매개변수화된 쿼리만 사용
- CSRF 보호 활성화
## 파일 구조
```
src/
|-- app/ # Next.js app router
|-- components/ # 재사용 가능한 UI 컴포넌트
|-- hooks/ # 커스텀 React hooks
|-- lib/ # 유틸리티 라이브러리
|-- types/ # TypeScript 타입 정의
```
## 주요 패턴
### API 응답 형식
```typescript
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
}
```
### 에러 처리
```typescript
try {
const result = await operation()
return { success: true, data: result }
} catch (error) {
console.error('Operation failed:', error)
return { success: false, error: 'User-friendly message' }
}
```
## 환경 변수
```bash
# 필수
DATABASE_URL=
API_KEY=
# 선택
DEBUG=false
```
## 사용 가능한 명령어
- `/tdd` - 테스트 주도 개발 워크플로우
- `/plan` - 구현 계획 생성
- `/code-review` - 코드 품질 리뷰
- `/build-fix` - 빌드 에러 수정
## Git 워크플로우
- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
- main 브랜치에 직접 커밋 금지
- PR은 리뷰 필수
- 병합 전 모든 테스트 통과 필수

View File

@@ -0,0 +1,308 @@
# Django REST API — 프로젝트 CLAUDE.md
> PostgreSQL과 Celery를 사용하는 Django REST Framework API의 실전 예시입니다.
> 프로젝트 루트에 복사하여 서비스에 맞게 커스터마이즈하세요.
## 프로젝트 개요
**기술 스택:** Python 3.12+, Django 5.x, Django REST Framework, PostgreSQL, Celery + Redis, pytest, Docker Compose
**아키텍처:** 비즈니스 도메인별 앱으로 구성된 도메인 주도 설계. API 레이어에 DRF, 비동기 작업에 Celery, 테스트에 pytest 사용. 모든 엔드포인트는 JSON을 반환하며 템플릿 렌더링은 없음.
## 필수 규칙
### Python 규칙
- 모든 함수 시그니처에 type hints 사용 — `from __future__ import annotations` 사용
- `print()` 문 사용 금지 — `logging.getLogger(__name__)` 사용
- 문자열 포매팅은 f-strings 사용, `%``.format()`은 사용 금지
- 파일 작업에 `os.path` 대신 `pathlib.Path` 사용
- isort로 import 정렬: stdlib, third-party, local 순서 (ruff에 의해 강제)
### 데이터베이스
- 모든 쿼리는 Django ORM 사용 — raw SQL은 `.raw()`와 parameterized 쿼리로만 사용
- 마이그레이션은 git에 커밋 — 프로덕션에서 `--fake` 사용 금지
- N+1 쿼리 방지를 위해 `select_related()``prefetch_related()` 사용
- 모든 모델에 `created_at``updated_at` 자동 필드 필수
- `filter()`, `order_by()`, 또는 `WHERE` 절에 사용되는 모든 필드에 인덱스 추가
```python
# 나쁜 예: N+1 쿼리
orders = Order.objects.all()
for order in orders:
print(order.customer.name) # 각 주문마다 DB를 조회함
# 좋은 예: join을 사용한 단일 쿼리
orders = Order.objects.select_related("customer").all()
```
### 인증
- `djangorestframework-simplejwt`를 통한 JWT — access token (15분) + refresh token (7일)
- 모든 뷰에 permission 클래스 지정 — 기본값에 의존하지 않기
- `IsAuthenticated`를 기본으로, 객체 수준 접근에는 커스텀 permission 추가
- 로그아웃을 위한 token blacklisting 활성화
### Serializers
- 간단한 CRUD에는 `ModelSerializer`, 복잡한 유효성 검증에는 `Serializer` 사용
- 입력/출력 형태가 다를 때는 읽기와 쓰기 serializer를 분리
- 유효성 검증은 serializer 레벨에서 — 뷰는 얇게 유지
```python
class CreateOrderSerializer(serializers.Serializer):
product_id = serializers.UUIDField()
quantity = serializers.IntegerField(min_value=1, max_value=100)
def validate_product_id(self, value):
if not Product.objects.filter(id=value, active=True).exists():
raise serializers.ValidationError("Product not found or inactive")
return value
class OrderDetailSerializer(serializers.ModelSerializer):
customer = CustomerSerializer(read_only=True)
product = ProductSerializer(read_only=True)
class Meta:
model = Order
fields = ["id", "customer", "product", "quantity", "total", "status", "created_at"]
```
### 오류 처리
- 일관된 오류 응답을 위해 DRF exception handler 사용
- 비즈니스 로직용 커스텀 예외는 `core/exceptions.py`에 정의
- 클라이언트에 내부 오류 세부 정보를 노출하지 않기
```python
# core/exceptions.py
from rest_framework.exceptions import APIException
class InsufficientStockError(APIException):
status_code = 409
default_detail = "Insufficient stock for this order"
default_code = "insufficient_stock"
```
### 코드 스타일
- 코드나 주석에 이모지 사용 금지
- 최대 줄 길이: 120자 (ruff에 의해 강제)
- 클래스: PascalCase, 함수/변수: snake_case, 상수: UPPER_SNAKE_CASE
- 뷰는 얇게 유지 — 비즈니스 로직은 서비스 함수나 모델 메서드에 배치
## 파일 구조
```
config/
settings/
base.py # 공유 설정
local.py # 개발 환경 오버라이드 (DEBUG=True)
production.py # 프로덕션 설정
urls.py # 루트 URL 설정
celery.py # Celery 앱 설정
apps/
accounts/ # 사용자 인증, 회원가입, 프로필
models.py
serializers.py
views.py
services.py # 비즈니스 로직
tests/
test_views.py
test_services.py
factories.py # Factory Boy 팩토리
orders/ # 주문 관리
models.py
serializers.py
views.py
services.py
tasks.py # Celery 작업
tests/
products/ # 상품 카탈로그
models.py
serializers.py
views.py
tests/
core/
exceptions.py # 커스텀 API 예외
permissions.py # 공유 permission 클래스
pagination.py # 커스텀 페이지네이션
middleware.py # 요청 로깅, 타이밍
tests/
```
## 주요 패턴
### Service 레이어
```python
# apps/orders/services.py
from django.db import transaction
def create_order(*, customer, product_id: uuid.UUID, quantity: int) -> Order:
"""재고 검증과 결제 보류를 포함한 주문 생성."""
with transaction.atomic():
product = Product.objects.select_for_update().get(id=product_id)
if product.stock < quantity:
raise InsufficientStockError()
order = Order.objects.create(
customer=customer,
product=product,
quantity=quantity,
total=product.price * quantity,
)
product.stock -= quantity
product.save(update_fields=["stock", "updated_at"])
# 비동기: 주문 확인 이메일 발송
send_order_confirmation.delay(order.id)
return order
```
### View 패턴
```python
# apps/orders/views.py
class OrderViewSet(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
pagination_class = StandardPagination
def get_serializer_class(self):
if self.action == "create":
return CreateOrderSerializer
return OrderDetailSerializer
def get_queryset(self):
return (
Order.objects
.filter(customer=self.request.user)
.select_related("product", "customer")
.order_by("-created_at")
)
def perform_create(self, serializer):
order = create_order(
customer=self.request.user,
product_id=serializer.validated_data["product_id"],
quantity=serializer.validated_data["quantity"],
)
serializer.instance = order
```
### 테스트 패턴 (pytest + Factory Boy)
```python
# apps/orders/tests/factories.py
import factory
from apps.accounts.tests.factories import UserFactory
from apps.products.tests.factories import ProductFactory
class OrderFactory(factory.django.DjangoModelFactory):
class Meta:
model = "orders.Order"
customer = factory.SubFactory(UserFactory)
product = factory.SubFactory(ProductFactory, stock=100)
quantity = 1
total = factory.LazyAttribute(lambda o: o.product.price * o.quantity)
# apps/orders/tests/test_views.py
import pytest
from rest_framework.test import APIClient
@pytest.mark.django_db
class TestCreateOrder:
def setup_method(self):
self.client = APIClient()
self.user = UserFactory()
self.client.force_authenticate(self.user)
def test_create_order_success(self):
product = ProductFactory(price=29_99, stock=10)
response = self.client.post("/api/orders/", {
"product_id": str(product.id),
"quantity": 2,
})
assert response.status_code == 201
assert response.data["total"] == 59_98
def test_create_order_insufficient_stock(self):
product = ProductFactory(stock=0)
response = self.client.post("/api/orders/", {
"product_id": str(product.id),
"quantity": 1,
})
assert response.status_code == 409
def test_create_order_unauthenticated(self):
self.client.force_authenticate(None)
response = self.client.post("/api/orders/", {})
assert response.status_code == 401
```
## 환경 변수
```bash
# Django
SECRET_KEY=
DEBUG=False
ALLOWED_HOSTS=api.example.com
# 데이터베이스
DATABASE_URL=postgres://user:pass@localhost:5432/myapp
# Redis (Celery broker + 캐시)
REDIS_URL=redis://localhost:6379/0
# JWT
JWT_ACCESS_TOKEN_LIFETIME=15 # 분
JWT_REFRESH_TOKEN_LIFETIME=10080 # 분 (7일)
# 이메일
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=smtp.example.com
```
## 테스트 전략
```bash
# 전체 테스트 실행
pytest --cov=apps --cov-report=term-missing
# 특정 앱 테스트 실행
pytest apps/orders/tests/ -v
# 병렬 실행
pytest -n auto
# 마지막 실행에서 실패한 테스트만 실행
pytest --lf
```
## ECC 워크플로우
```bash
# 계획 수립
/plan "Add order refund system with Stripe integration"
# TDD로 개발
/tdd # pytest 기반 TDD 워크플로우
# 리뷰
/python-review # Python 전용 코드 리뷰
/security-scan # Django 보안 감사
/code-review # 일반 품질 검사
# 검증
/verify # 빌드, 린트, 테스트, 보안 스캔
```
## Git 워크플로우
- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경
- `main`에서 feature 브랜치 생성, PR 필수
- CI: ruff (린트 + 포맷), mypy (타입), pytest (테스트), safety (의존성 검사)
- 배포: Docker 이미지, Kubernetes 또는 Railway로 관리

View File

@@ -0,0 +1,267 @@
# Go Microservice — 프로젝트 CLAUDE.md
> PostgreSQL, gRPC, Docker를 사용하는 Go 마이크로서비스의 실전 예시입니다.
> 프로젝트 루트에 복사하여 서비스에 맞게 커스터마이즈하세요.
## 프로젝트 개요
**기술 스택:** Go 1.22+, PostgreSQL, gRPC + REST (grpc-gateway), Docker, sqlc (타입 안전 SQL), Wire (의존성 주입)
**아키텍처:** domain, repository, service, handler 레이어로 구성된 클린 아키텍처. gRPC를 기본 전송 프로토콜로 사용하고, 외부 클라이언트를 위한 REST gateway 제공.
## 필수 규칙
### Go 규칙
- Effective Go와 Go Code Review Comments 가이드를 따를 것
- 오류 래핑에 `errors.New` / `fmt.Errorf``%w` 사용 — 오류를 문자열 매칭하지 않기
- `init()` 함수 사용 금지 — `main()`이나 생성자에서 명시적으로 초기화
- 전역 가변 상태 금지 — 생성자를 통해 의존성 전달
- Context는 반드시 첫 번째 매개변수이며 모든 레이어를 통해 전파
### 데이터베이스
- 모든 쿼리는 `queries/`에 순수 SQL로 작성 — sqlc가 타입 안전한 Go 코드를 생성
- 마이그레이션은 `migrations/`에 golang-migrate 사용 — 데이터베이스를 직접 변경하지 않기
- 다중 단계 작업에는 `pgx.Tx`를 통한 트랜잭션 사용
- 모든 쿼리에 parameterized placeholder (`$1`, `$2`) 사용 — 문자열 포매팅 사용 금지
### 오류 처리
- 오류를 반환하고, panic하지 않기 — panic은 진정으로 복구 불가능한 상황에만 사용
- 컨텍스트와 함께 오류 래핑: `fmt.Errorf("creating user: %w", err)`
- 비즈니스 로직을 위한 sentinel 오류는 `domain/errors.go`에 정의
- handler 레이어에서 도메인 오류를 gRPC status 코드로 매핑
```go
// 도메인 레이어 — sentinel 오류
var (
ErrUserNotFound = errors.New("user not found")
ErrEmailTaken = errors.New("email already registered")
)
// Handler 레이어 — gRPC status로 매핑
func toGRPCError(err error) error {
switch {
case errors.Is(err, domain.ErrUserNotFound):
return status.Error(codes.NotFound, err.Error())
case errors.Is(err, domain.ErrEmailTaken):
return status.Error(codes.AlreadyExists, err.Error())
default:
return status.Error(codes.Internal, "internal error")
}
}
```
### 코드 스타일
- 코드나 주석에 이모지 사용 금지
- 외부로 공개되는 타입과 함수에는 반드시 doc 주석 작성
- 함수는 50줄 이하로 유지 — 헬퍼 함수로 분리
- 여러 케이스가 있는 모든 로직에 table-driven 테스트 사용
- signal 채널에는 `bool`이 아닌 `struct{}` 사용
## 파일 구조
```
cmd/
server/
main.go # 진입점, Wire 주입, 우아한 종료
internal/
domain/ # 비즈니스 타입과 인터페이스
user.go # User 엔티티와 repository 인터페이스
errors.go # Sentinel 오류
service/ # 비즈니스 로직
user_service.go
user_service_test.go
repository/ # 데이터 접근 (sqlc 생성 + 커스텀)
postgres/
user_repo.go
user_repo_test.go # testcontainers를 사용한 통합 테스트
handler/ # gRPC + REST 핸들러
grpc/
user_handler.go
rest/
user_handler.go
config/ # 설정 로딩
config.go
proto/ # Protobuf 정의
user/v1/
user.proto
queries/ # sqlc용 SQL 쿼리
user.sql
migrations/ # 데이터베이스 마이그레이션
001_create_users.up.sql
001_create_users.down.sql
```
## 주요 패턴
### Repository 인터페이스
```go
type UserRepository interface {
Create(ctx context.Context, user *User) error
FindByID(ctx context.Context, id uuid.UUID) (*User, error)
FindByEmail(ctx context.Context, email string) (*User, error)
Update(ctx context.Context, user *User) error
Delete(ctx context.Context, id uuid.UUID) error
}
```
### 의존성 주입을 사용한 Service
```go
type UserService struct {
repo domain.UserRepository
hasher PasswordHasher
logger *slog.Logger
}
func NewUserService(repo domain.UserRepository, hasher PasswordHasher, logger *slog.Logger) *UserService {
return &UserService{repo: repo, hasher: hasher, logger: logger}
}
func (s *UserService) Create(ctx context.Context, req CreateUserRequest) (*domain.User, error) {
existing, err := s.repo.FindByEmail(ctx, req.Email)
if err != nil && !errors.Is(err, domain.ErrUserNotFound) {
return nil, fmt.Errorf("checking email: %w", err)
}
if existing != nil {
return nil, domain.ErrEmailTaken
}
hashed, err := s.hasher.Hash(req.Password)
if err != nil {
return nil, fmt.Errorf("hashing password: %w", err)
}
user := &domain.User{
ID: uuid.New(),
Name: req.Name,
Email: req.Email,
Password: hashed,
}
if err := s.repo.Create(ctx, user); err != nil {
return nil, fmt.Errorf("creating user: %w", err)
}
return user, nil
}
```
### Table-Driven 테스트
```go
func TestUserService_Create(t *testing.T) {
tests := []struct {
name string
req CreateUserRequest
setup func(*MockUserRepo)
wantErr error
}{
{
name: "valid user",
req: CreateUserRequest{Name: "Alice", Email: "alice@example.com", Password: "secure123"},
setup: func(m *MockUserRepo) {
m.On("FindByEmail", mock.Anything, "alice@example.com").Return(nil, domain.ErrUserNotFound)
m.On("Create", mock.Anything, mock.Anything).Return(nil)
},
wantErr: nil,
},
{
name: "duplicate email",
req: CreateUserRequest{Name: "Alice", Email: "taken@example.com", Password: "secure123"},
setup: func(m *MockUserRepo) {
m.On("FindByEmail", mock.Anything, "taken@example.com").Return(&domain.User{}, nil)
},
wantErr: domain.ErrEmailTaken,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repo := new(MockUserRepo)
tt.setup(repo)
svc := NewUserService(repo, &bcryptHasher{}, slog.Default())
_, err := svc.Create(context.Background(), tt.req)
if tt.wantErr != nil {
assert.ErrorIs(t, err, tt.wantErr)
} else {
assert.NoError(t, err)
}
})
}
}
```
## 환경 변수
```bash
# 데이터베이스
DATABASE_URL=postgres://user:pass@localhost:5432/myservice?sslmode=disable
# gRPC
GRPC_PORT=50051
REST_PORT=8080
# 인증
JWT_SECRET= # 프로덕션에서는 vault에서 로드
TOKEN_EXPIRY=24h
# 관측 가능성
LOG_LEVEL=info # debug, info, warn, error
OTEL_ENDPOINT= # OpenTelemetry 콜렉터
```
## 테스트 전략
```bash
/go-test # Go용 TDD 워크플로우
/go-review # Go 전용 코드 리뷰
/go-build # 빌드 오류 수정
```
### 테스트 명령어
```bash
# 단위 테스트 (빠름, 외부 의존성 없음)
go test ./internal/... -short -count=1
# 통합 테스트 (testcontainers를 위해 Docker 필요)
go test ./internal/repository/... -count=1 -timeout 120s
# 전체 테스트와 커버리지
go test ./... -coverprofile=coverage.out -count=1
go tool cover -func=coverage.out # 요약
go tool cover -html=coverage.out # 브라우저
# Race detector
go test ./... -race -count=1
```
## ECC 워크플로우
```bash
# 계획 수립
/plan "Add rate limiting to user endpoints"
# 개발
/go-test # Go 전용 패턴으로 TDD
# 리뷰
/go-review # Go 관용구, 오류 처리, 동시성
/security-scan # 시크릿 및 취약점 점검
# 머지 전 확인
go vet ./...
staticcheck ./...
```
## Git 워크플로우
- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경
- `main`에서 feature 브랜치 생성, PR 필수
- CI: `go vet`, `staticcheck`, `go test -race`, `golangci-lint`
- 배포: CI에서 Docker 이미지 빌드, Kubernetes에 배포

View File

@@ -0,0 +1,291 @@
# Rust API Service — 프로젝트 CLAUDE.md
> Axum, PostgreSQL, Docker를 사용하는 Rust API 서비스의 실전 예시입니다.
> 프로젝트 루트에 복사하여 서비스에 맞게 커스터마이즈하세요.
## 프로젝트 개요
**기술 스택:** Rust 1.78+, Axum (웹 프레임워크), SQLx (비동기 데이터베이스), PostgreSQL, Tokio (비동기 런타임), Docker
**아키텍처:** handler -> service -> repository로 분리된 레이어드 아키텍처. HTTP에 Axum, 컴파일 타임에 타입이 검증되는 SQL에 SQLx, 횡단 관심사에 Tower 미들웨어 사용.
## 필수 규칙
### Rust 규칙
- 라이브러리 오류에 `thiserror`, 바이너리 크레이트나 테스트에서만 `anyhow` 사용
- 프로덕션 코드에서 `.unwrap()`이나 `.expect()` 사용 금지 — `?`로 오류 전파
- 함수 매개변수에 `String`보다 `&str` 선호; 소유권 이전 시 `String` 반환
- `#![deny(clippy::all, clippy::pedantic)]`과 함께 `clippy` 사용 — 모든 경고 수정
- 모든 공개 타입에 `Debug` derive; `Clone`, `PartialEq`는 필요할 때만 derive
- `// SAFETY:` 주석으로 정당화하지 않는 한 `unsafe` 블록 사용 금지
### 데이터베이스
- 모든 쿼리에 SQLx `query!` 또는 `query_as!` 매크로 사용 — 스키마에 대해 컴파일 타임에 검증
- 마이그레이션은 `migrations/``sqlx migrate` 사용 — 데이터베이스를 직접 변경하지 않기
- 공유 상태로 `sqlx::Pool<Postgres>` 사용 — 요청마다 커넥션을 생성하지 않기
- 모든 쿼리에 parameterized placeholder (`$1`, `$2`) 사용 — 문자열 포매팅 사용 금지
```rust
// 나쁜 예: 문자열 보간 (SQL injection 위험)
let q = format!("SELECT * FROM users WHERE id = '{}'", id);
// 좋은 예: parameterized 쿼리, 컴파일 타임에 검증
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_optional(&pool)
.await?;
```
### 오류 처리
- 모듈별로 `thiserror`를 사용한 도메인 오류 enum 정의
- `IntoResponse`를 통해 오류를 HTTP 응답으로 매핑 — 내부 세부 정보를 노출하지 않기
- 구조화된 로깅에 `tracing` 사용 — `println!`이나 `eprintln!` 사용 금지
```rust
use thiserror::Error;
#[derive(Debug, Error)]
pub enum AppError {
#[error("Resource not found")]
NotFound,
#[error("Validation failed: {0}")]
Validation(String),
#[error("Unauthorized")]
Unauthorized,
#[error(transparent)]
Database(#[from] sqlx::Error),
#[error(transparent)]
Io(#[from] std::io::Error),
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match &self {
Self::NotFound => (StatusCode::NOT_FOUND, self.to_string()),
Self::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
Self::Unauthorized => (StatusCode::UNAUTHORIZED, self.to_string()),
Self::Database(err) => {
tracing::error!(?err, "database error");
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
}
Self::Io(err) => {
tracing::error!(?err, "internal error");
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".into())
}
};
(status, Json(json!({ "error": message }))).into_response()
}
}
```
### 테스트
- 각 소스 파일 내의 `#[cfg(test)]` 모듈에서 단위 테스트
- `tests/` 디렉토리에서 실제 PostgreSQL을 사용한 통합 테스트 (Testcontainers 또는 Docker)
- 자동 마이그레이션과 롤백이 포함된 데이터베이스 테스트에 `#[sqlx::test]` 사용
- 외부 서비스 모킹에 `mockall` 또는 `wiremock` 사용
### 코드 스타일
- 최대 줄 길이: 100자 (rustfmt에 의해 강제)
- import 그룹화: `std`, 외부 크레이트, `crate`/`super` — 빈 줄로 구분
- 모듈: 모듈당 파일 하나, `mod.rs`는 re-export용으로만 사용
- 타입: PascalCase, 함수/변수: snake_case, 상수: UPPER_SNAKE_CASE
## 파일 구조
```
src/
main.rs # 진입점, 서버 설정, 우아한 종료
lib.rs # 통합 테스트를 위한 re-export
config.rs # envy 또는 figment을 사용한 환경 설정
router.rs # 모든 라우트가 포함된 Axum 라우터
middleware/
auth.rs # JWT 추출 및 검증
logging.rs # 요청/응답 트레이싱
handlers/
mod.rs # 라우트 핸들러 (얇게 — 서비스에 위임)
users.rs
orders.rs
services/
mod.rs # 비즈니스 로직
users.rs
orders.rs
repositories/
mod.rs # 데이터베이스 접근 (SQLx 쿼리)
users.rs
orders.rs
domain/
mod.rs # 도메인 타입, 오류 enum
user.rs
order.rs
migrations/
001_create_users.sql
002_create_orders.sql
tests/
common/mod.rs # 공유 테스트 헬퍼, 테스트 서버 설정
api_users.rs # 사용자 엔드포인트 통합 테스트
api_orders.rs # 주문 엔드포인트 통합 테스트
```
## 주요 패턴
### Handler (얇은 레이어)
```rust
async fn create_user(
State(ctx): State<AppState>,
Json(payload): Json<CreateUserRequest>,
) -> Result<(StatusCode, Json<UserResponse>), AppError> {
let user = ctx.user_service.create(payload).await?;
Ok((StatusCode::CREATED, Json(UserResponse::from(user))))
}
```
### Service (비즈니스 로직)
```rust
impl UserService {
pub async fn create(&self, req: CreateUserRequest) -> Result<User, AppError> {
if self.repo.find_by_email(&req.email).await?.is_some() {
return Err(AppError::Validation("Email already registered".into()));
}
let password_hash = hash_password(&req.password)?;
let user = self.repo.insert(&req.email, &req.name, &password_hash).await?;
Ok(user)
}
}
```
### Repository (데이터 접근)
```rust
impl UserRepository {
pub async fn find_by_email(&self, email: &str) -> Result<Option<User>, sqlx::Error> {
sqlx::query_as!(User, "SELECT * FROM users WHERE email = $1", email)
.fetch_optional(&self.pool)
.await
}
pub async fn insert(
&self,
email: &str,
name: &str,
password_hash: &str,
) -> Result<User, sqlx::Error> {
sqlx::query_as!(
User,
r#"INSERT INTO users (email, name, password_hash)
VALUES ($1, $2, $3) RETURNING *"#,
email, name, password_hash,
)
.fetch_one(&self.pool)
.await
}
}
```
### 통합 테스트
```rust
#[tokio::test]
async fn test_create_user() {
let app = spawn_test_app().await;
let response = app
.client
.post(&format!("{}/api/v1/users", app.address))
.json(&json!({
"email": "alice@example.com",
"name": "Alice",
"password": "securepassword123"
}))
.send()
.await
.expect("Failed to send request");
assert_eq!(response.status(), StatusCode::CREATED);
let body: serde_json::Value = response.json().await.unwrap();
assert_eq!(body["email"], "alice@example.com");
}
#[tokio::test]
async fn test_create_user_duplicate_email() {
let app = spawn_test_app().await;
// 첫 번째 사용자 생성
create_test_user(&app, "alice@example.com").await;
// 중복 시도
let response = create_user_request(&app, "alice@example.com").await;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
}
```
## 환경 변수
```bash
# 서버
HOST=0.0.0.0
PORT=8080
RUST_LOG=info,tower_http=debug
# 데이터베이스
DATABASE_URL=postgres://user:pass@localhost:5432/myapp
# 인증
JWT_SECRET=your-secret-key-min-32-chars
JWT_EXPIRY_HOURS=24
# 선택 사항
CORS_ALLOWED_ORIGINS=http://localhost:3000
```
## 테스트 전략
```bash
# 전체 테스트 실행
cargo test
# 출력과 함께 실행
cargo test -- --nocapture
# 특정 테스트 모듈 실행
cargo test api_users
# 커버리지 확인 (cargo-llvm-cov 필요)
cargo llvm-cov --html
open target/llvm-cov/html/index.html
# 린트
cargo clippy -- -D warnings
# 포맷 검사
cargo fmt -- --check
```
## ECC 워크플로우
```bash
# 계획 수립
/plan "Add order fulfillment with Stripe payment"
# TDD로 개발
/tdd # cargo test 기반 TDD 워크플로우
# 리뷰
/code-review # Rust 전용 코드 리뷰
/security-scan # 의존성 감사 + unsafe 스캔
# 검증
/verify # 빌드, clippy, 테스트, 보안 스캔
```
## Git 워크플로우
- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경
- `main`에서 feature 브랜치 생성, PR 필수
- CI: `cargo fmt --check`, `cargo clippy`, `cargo test`, `cargo audit`
- 배포: `scratch` 또는 `distroless` 베이스를 사용한 Docker 멀티스테이지 빌드

View File

@@ -0,0 +1,166 @@
# SaaS 애플리케이션 — 프로젝트 CLAUDE.md
> Next.js + Supabase + Stripe SaaS 애플리케이션을 위한 실제 사용 예제입니다.
> 프로젝트 루트에 복사한 후 기술 스택에 맞게 커스터마이즈하세요.
## 프로젝트 개요
**기술 스택:** Next.js 15 (App Router), TypeScript, Supabase (인증 + DB), Stripe (결제), Tailwind CSS, Playwright (E2E)
**아키텍처:** 기본적으로 Server Components 사용. Client Components는 상호작용이 필요한 경우에만 사용. API route는 webhook용, Server Action은 mutation용.
## 핵심 규칙
### 데이터베이스
- 모든 쿼리는 RLS가 활성화된 Supabase client 사용 — RLS를 절대 우회하지 않음
- 마이그레이션은 `supabase/migrations/`에 저장 — 데이터베이스를 직접 수정하지 않음
- `select('*')` 대신 명시적 컬럼 목록이 포함된 `select()` 사용
- 모든 사용자 대상 쿼리에는 무제한 결과를 방지하기 위해 `.limit()` 포함 필수
### 인증
- Server Components에서는 `@supabase/ssr``createServerClient()` 사용
- Client Components에서는 `@supabase/ssr``createBrowserClient()` 사용
- 보호된 라우트는 `getUser()`로 확인 — 인증에 `getSession()`만 단독으로 신뢰하지 않음
- `middleware.ts`의 Middleware가 매 요청마다 인증 토큰 갱신
### 결제
- Stripe webhook 핸들러는 `app/api/webhooks/stripe/route.ts`에 위치
- 클라이언트 측 가격 데이터를 절대 신뢰하지 않음 — 항상 서버 측에서 Stripe로부터 조회
- 구독 상태는 webhook에 의해 동기화되는 `subscription_status` 컬럼으로 확인
- 무료 플랜 사용자: 프로젝트 3개, 일일 API 호출 100회
### 코드 스타일
- 코드나 주석에 이모지 사용 금지
- 불변 패턴만 사용 — spread 연산자 사용, 직접 변경 금지
- Server Components: `'use client'` 디렉티브 없음, `useState`/`useEffect` 없음
- Client Components: 파일 상단에 `'use client'` 작성, 최소한으로 유지 — 로직은 hooks로 분리
- 모든 입력 유효성 검사에 Zod 스키마 사용 선호 (API route, 폼, 환경 변수)
## 파일 구조
```
src/
app/
(auth)/ # 인증 페이지 (로그인, 회원가입, 비밀번호 찾기)
(dashboard)/ # 보호된 대시보드 페이지
api/
webhooks/ # Stripe, Supabase webhooks
layout.tsx # Provider가 포함된 루트 레이아웃
components/
ui/ # Shadcn/ui 컴포넌트
forms/ # 유효성 검사가 포함된 폼 컴포넌트
dashboard/ # 대시보드 전용 컴포넌트
hooks/ # 커스텀 React hooks
lib/
supabase/ # Supabase client 팩토리
stripe/ # Stripe client 및 헬퍼
utils.ts # 범용 유틸리티
types/ # 공유 TypeScript 타입
supabase/
migrations/ # 데이터베이스 마이그레이션
seed.sql # 개발용 시드 데이터
```
## 주요 패턴
### API 응답 형식
```typescript
type ApiResponse<T> =
| { success: true; data: T }
| { success: false; error: string; code?: string }
```
### Server Action 패턴
```typescript
'use server'
import { z } from 'zod'
import { createServerClient } from '@/lib/supabase/server'
const schema = z.object({
name: z.string().min(1).max(100),
})
export async function createProject(formData: FormData) {
const parsed = schema.safeParse({ name: formData.get('name') })
if (!parsed.success) {
return { success: false, error: parsed.error.flatten() }
}
const supabase = await createServerClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { success: false, error: 'Unauthorized' }
const { data, error } = await supabase
.from('projects')
.insert({ name: parsed.data.name, user_id: user.id })
.select('id, name, created_at')
.single()
if (error) return { success: false, error: 'Failed to create project' }
return { success: true, data }
}
```
## 환경 변수
```bash
# Supabase
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY= # 서버 전용, 클라이언트에 절대 노출 금지
# Stripe
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
# 앱
NEXT_PUBLIC_APP_URL=http://localhost:3000
```
## 테스트 전략
```bash
/tdd # 새 기능에 대한 단위 + 통합 테스트
/e2e # 인증 흐름, 결제, 대시보드에 대한 Playwright 테스트
/test-coverage # 80% 이상 커버리지 확인
```
### 핵심 E2E 흐름
1. 회원가입 → 이메일 인증 → 첫 프로젝트 생성
2. 로그인 → 대시보드 → CRUD 작업
3. 플랜 업그레이드 → Stripe checkout → 구독 활성화
4. Webhook: 구독 취소 → 무료 플랜으로 다운그레이드
## ECC 워크플로우
```bash
# 기능 계획 수립
/plan "Add team invitations with email notifications"
# TDD로 개발
/tdd
# 커밋 전
/code-review
/security-scan
# 릴리스 전
/e2e
/test-coverage
```
## Git 워크플로우
- `feat:` 새 기능, `fix:` 버그 수정, `refactor:` 코드 변경
- `main`에서 기능 브랜치 생성, PR 필수
- CI 실행 항목: lint, 타입 체크, 단위 테스트, E2E 테스트
- 배포: PR 시 Vercel 미리보기, `main` 병합 시 프로덕션 배포

View File

@@ -0,0 +1,19 @@
{
"statusLine": {
"type": "command",
"command": "input=$(cat); user=$(whoami); cwd=$(echo \"$input\" | jq -r '.workspace.current_dir' | sed \"s|$HOME|~|g\"); model=$(echo \"$input\" | jq -r '.model.display_name'); time=$(date +%H:%M); remaining=$(echo \"$input\" | jq -r '.context_window.remaining_percentage // empty'); transcript=$(echo \"$input\" | jq -r '.transcript_path'); todo_count=$([ -f \"$transcript\" ] && { grep -c '\"type\":\"todo\"' \"$transcript\" 2>/dev/null || true; } || echo 0); cd \"$(echo \"$input\" | jq -r '.workspace.current_dir')\" 2>/dev/null; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo ''); status=''; [ -n \"$branch\" ] && { [ -n \"$(git status --porcelain 2>/dev/null)\" ] && status='*'; }; B='\\033[38;2;30;102;245m'; G='\\033[38;2;64;160;43m'; Y='\\033[38;2;223;142;29m'; M='\\033[38;2;136;57;239m'; C='\\033[38;2;23;146;153m'; R='\\033[0m'; T='\\033[38;2;76;79;105m'; printf \"${C}${user}${R}:${B}${cwd}${R}\"; [ -n \"$branch\" ] && printf \" ${G}${branch}${Y}${status}${R}\"; [ -n \"$remaining\" ] && printf \" ${M}ctx:${remaining}%%${R}\"; printf \" ${T}${model}${R} ${Y}${time}${R}\"; [ \"$todo_count\" -gt 0 ] && printf \" ${C}todos:${todo_count}${R}\"; echo",
"description": "Custom status line showing: user:path branch* ctx:% model time todos:N"
},
"_comments": {
"colors": {
"B": "Blue - directory path",
"G": "Green - git branch",
"Y": "Yellow - dirty status, time",
"M": "Magenta - context remaining",
"C": "Cyan - username, todos",
"T": "Gray - model name"
},
"output_example": "affoon:~/projects/myapp main* ctx:73% sonnet-4.6 14:30 todos:3",
"usage": "Copy the statusLine object to your ~/.claude/settings.json"
}
}

View File

@@ -0,0 +1,109 @@
# 사용자 수준 CLAUDE.md 예제
사용자 수준 CLAUDE.md 파일 예제입니다. `~/.claude/CLAUDE.md`에 배치하세요.
사용자 수준 설정은 모든 프로젝트에 전역으로 적용됩니다. 다음 용도로 사용하세요:
- 개인 코딩 선호 설정
- 항상 적용하고 싶은 범용 규칙
- 모듈식 규칙 파일 링크
---
## 핵심 철학
당신은 Claude Code입니다. 저는 복잡한 작업에 특화된 agent와 skill을 사용합니다.
**핵심 원칙:**
1. **Agent 우선**: 복잡한 작업은 특화된 agent에 위임
2. **병렬 실행**: 가능할 때 Task tool을 사용하여 여러 agent를 동시에 실행
3. **실행 전 계획**: 복잡한 작업에는 Plan Mode 사용
4. **테스트 주도**: 구현 전에 테스트 작성
5. **보안 우선**: 보안에 대해 절대 타협하지 않음
---
## 모듈식 규칙
상세 가이드라인은 `~/.claude/rules/`에 있습니다:
| 규칙 파일 | 내용 |
|-----------|------|
| security.md | 보안 점검, 시크릿 관리 |
| coding-style.md | 불변성, 파일 구성, 에러 처리 |
| testing.md | TDD 워크플로우, 80% 커버리지 요구사항 |
| git-workflow.md | 커밋 형식, PR 워크플로우 |
| agents.md | Agent 오케스트레이션, 상황별 agent 선택 |
| patterns.md | API 응답, repository 패턴 |
| performance.md | 모델 선택, 컨텍스트 관리 |
| hooks.md | Hooks 시스템 |
---
## 사용 가능한 Agent
`~/.claude/agents/`에 위치합니다:
| Agent | 용도 |
|-------|------|
| planner | 기능 구현 계획 수립 |
| architect | 시스템 설계 및 아키텍처 |
| tdd-guide | 테스트 주도 개발 |
| code-reviewer | 품질/보안 코드 리뷰 |
| security-reviewer | 보안 취약점 분석 |
| build-error-resolver | 빌드 에러 해결 |
| e2e-runner | Playwright E2E 테스트 |
| refactor-cleaner | 불필요한 코드 정리 |
| doc-updater | 문서 업데이트 |
---
## 개인 선호 설정
### 개인정보 보호
- 항상 로그를 삭제하고, 시크릿(API 키/토큰/비밀번호/JWT)을 절대 붙여넣지 않음
- 공유 전 출력 내용을 검토하여 민감한 데이터 제거
### 코드 스타일
- 코드, 주석, 문서에 이모지 사용 금지
- 불변성 선호 - 객체나 배열을 직접 변경하지 않음
- 큰 파일 소수보다 작은 파일 다수를 선호
- 일반적으로 200-400줄, 파일당 최대 800줄
### Git
- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `test:`
- 커밋 전 항상 로컬에서 테스트
- 작고 집중된 커밋
### 테스트
- TDD: 테스트를 먼저 작성
- 최소 80% 커버리지
- 핵심 흐름에 대해 단위 + 통합 + E2E 테스트
### 지식 축적
- 개인 디버깅 메모, 선호 설정, 임시 컨텍스트 → auto memory
- 팀/프로젝트 지식(아키텍처 결정, API 변경, 구현 런북) → 프로젝트의 기존 문서 구조를 따름
- 현재 작업에서 이미 관련 문서, 주석, 예제를 생성하는 경우 동일한 지식을 다른 곳에 중복하지 않음
- 적절한 프로젝트 문서 위치가 없는 경우 새로운 최상위 문서를 만들기 전에 먼저 질문
---
## 에디터 연동
저는 Zed을 기본 에디터로 사용합니다:
- 파일 추적을 위한 Agent Panel
- CMD+Shift+R로 명령 팔레트 사용
- Vim 모드 활성화
---
## 성공 기준
다음 조건을 충족하면 성공입니다:
- 모든 테스트 통과 (80% 이상 커버리지)
- 보안 취약점 없음
- 코드가 읽기 쉽고 유지보수 가능
- 사용자 요구사항 충족
---
**철학**: Agent 우선 설계, 병렬 실행, 실행 전 계획, 코드 전 테스트, 항상 보안 우선.

View File

@@ -0,0 +1,52 @@
# 에이전트 오케스트레이션
## 사용 가능한 에이전트
`~/.claude/agents/`에 위치:
| 에이전트 | 용도 | 사용 시점 |
|---------|------|----------|
| planner | 구현 계획 | 복잡한 기능, 리팩토링 |
| architect | 시스템 설계 | 아키텍처 의사결정 |
| tdd-guide | 테스트 주도 개발 | 새 기능, 버그 수정 |
| code-reviewer | 코드 리뷰 | 코드 작성 후 |
| security-reviewer | 보안 분석 | 커밋 전 |
| build-error-resolver | 빌드 에러 수정 | 빌드 실패 시 |
| e2e-runner | E2E 테스팅 | 핵심 사용자 흐름 |
| database-reviewer | 데이터베이스 스키마/쿼리 리뷰 | 스키마 설계, 쿼리 최적화 |
| go-reviewer | Go 코드 리뷰 | Go 코드 작성 또는 수정 후 |
| go-build-resolver | Go 빌드 에러 수정 | `go build` 또는 `go vet` 실패 시 |
| refactor-cleaner | 사용하지 않는 코드 정리 | 코드 유지보수 |
| doc-updater | 문서 관리 | 문서 업데이트 |
## 즉시 에이전트 사용
사용자 프롬프트 불필요:
1. 복잡한 기능 요청 - **planner** 에이전트 사용
2. 코드 작성/수정 직후 - **code-reviewer** 에이전트 사용
3. 버그 수정 또는 새 기능 - **tdd-guide** 에이전트 사용
4. 아키텍처 의사결정 - **architect** 에이전트 사용
## 병렬 Task 실행
독립적인 작업에는 항상 병렬 Task 실행 사용:
```markdown
# 좋음: 병렬 실행
3개 에이전트를 병렬로 실행:
1. 에이전트 1: 인증 모듈 보안 분석
2. 에이전트 2: 캐시 시스템 성능 리뷰
3. 에이전트 3: 유틸리티 타입 검사
# 나쁨: 불필요하게 순차 실행
먼저 에이전트 1, 그다음 에이전트 2, 그다음 에이전트 3
```
## 다중 관점 분석
복잡한 문제에는 역할 분리 서브에이전트 사용:
- 사실 검증 리뷰어
- 시니어 엔지니어
- 보안 전문가
- 일관성 검토자
- 중복 검사자

View File

@@ -0,0 +1,48 @@
# 코딩 스타일
## 불변성 (중요)
항상 새 객체를 생성하고, 기존 객체를 절대 변경하지 마세요:
```
// 의사 코드
잘못된 예: modify(original, field, value) → 원본을 직접 변경
올바른 예: update(original, field, value) → 변경 사항이 반영된 새 복사본 반환
```
근거: 불변 데이터는 숨겨진 사이드 이펙트를 방지하고, 디버깅을 쉽게 하며, 안전한 동시성을 가능하게 합니다.
## 파일 구성
많은 작은 파일 > 적은 큰 파일:
- 높은 응집도, 낮은 결합도
- 200-400줄이 일반적, 최대 800줄
- 큰 모듈에서 유틸리티를 분리
- 타입이 아닌 기능/도메인별로 구성
## 에러 처리
항상 에러를 포괄적으로 처리:
- 모든 레벨에서 에러를 명시적으로 처리
- UI 코드에서는 사용자 친화적인 에러 메시지 제공
- 서버 측에서는 상세한 에러 컨텍스트 로깅
- 에러를 절대 조용히 무시하지 않기
## 입력 유효성 검증
항상 시스템 경계에서 유효성 검증:
- 처리 전에 모든 사용자 입력을 검증
- 가능한 경우 스키마 기반 유효성 검증 사용
- 명확한 에러 메시지와 함께 빠르게 실패
- 외부 데이터를 절대 신뢰하지 않기 (API 응답, 사용자 입력, 파일 내용)
## 코드 품질 체크리스트
작업 완료 전 확인:
- [ ] 코드가 읽기 쉽고 이름이 적절한가
- [ ] 함수가 작은가 (<50줄)
- [ ] 파일이 집중적인가 (<800줄)
- [ ] 깊은 중첩이 없는가 (>4단계)
- [ ] 적절한 에러 처리가 되어 있는가
- [ ] 하드코딩된 값이 없는가 (상수나 설정 사용)
- [ ] 변이가 없는가 (불변 패턴 사용)

View File

@@ -0,0 +1,24 @@
# Git 워크플로우
## 커밋 메시지 형식
```
<type>: <description>
<선택적 본문>
```
타입: feat, fix, refactor, docs, test, chore, perf, ci
참고: 어트리뷰션 비활성화 여부는 각자의 `~/.claude/settings.json` 로컬 설정에 따라 달라질 수 있습니다.
## Pull Request 워크플로우
PR을 만들 때:
1. 전체 커밋 히스토리를 분석 (최신 커밋만이 아닌)
2. `git diff [base-branch]...HEAD`로 모든 변경사항 확인
3. 포괄적인 PR 요약 작성
4. TODO가 포함된 테스트 계획 포함
5. 새 브랜치인 경우 `-u` 플래그와 함께 push
> git 작업 전 전체 개발 프로세스(계획, TDD, 코드 리뷰)는
> [development-workflow.md](./development-workflow.md)를 참고하세요.

30
docs/ko-KR/rules/hooks.md Normal file
View File

@@ -0,0 +1,30 @@
# 훅 시스템
## 훅 유형
- **PreToolUse**: 도구 실행 전 (유효성 검증, 매개변수 수정)
- **PostToolUse**: 도구 실행 후 (자동 포맷, 검사)
- **Stop**: 세션 종료 시 (최종 검증)
## 자동 수락 권한
주의하여 사용:
- 신뢰할 수 있는, 잘 정의된 계획에서만 활성화
- 탐색적 작업에서는 비활성화
- dangerously-skip-permissions 플래그를 절대 사용하지 않기
- 대신 `~/.claude.json`에서 `allowedTools`를 설정
## TodoWrite 모범 사례
TodoWrite 도구 활용:
- 다단계 작업의 진행 상황 추적
- 지시사항 이해도 검증
- 실시간 방향 조정 가능
- 세부 구현 단계 표시
Todo 목록으로 확인 가능한 것:
- 순서가 맞지 않는 단계
- 누락된 항목
- 불필요한 추가 항목
- 잘못된 세분화 수준
- 잘못 해석된 요구사항

View File

@@ -0,0 +1,31 @@
# 공통 패턴
## 스켈레톤 프로젝트
새 기능을 구현할 때:
1. 검증된 스켈레톤 프로젝트를 검색
2. 병렬 에이전트로 옵션 평가:
- 보안 평가
- 확장성 분석
- 관련성 점수
- 구현 계획
3. 가장 적합한 것을 기반으로 클론
4. 검증된 구조 내에서 반복 개선
## 디자인 패턴
### 리포지토리 패턴
일관된 인터페이스 뒤에 데이터 접근을 캡슐화:
- 표준 작업 정의: findAll, findById, create, update, delete
- 구체적 구현이 저장소 세부사항 처리 (데이터베이스, API, 파일 등)
- 비즈니스 로직은 저장소 메커니즘이 아닌 추상 인터페이스에 의존
- 데이터 소스의 쉬운 교체 및 모킹을 통한 테스트 단순화 가능
### API 응답 형식
모든 API 응답에 일관된 엔벨로프 사용:
- 성공/상태 표시자 포함
- 데이터 페이로드 포함 (에러 시 null)
- 에러 메시지 필드 포함 (성공 시 null)
- 페이지네이션 응답에 메타데이터 포함 (total, page, limit)

View File

@@ -0,0 +1,55 @@
# 성능 최적화
## 모델 선택 전략
**Haiku 4.5** (Sonnet 능력의 90%, 3배 비용 절감):
- 자주 호출되는 경량 에이전트
- 페어 프로그래밍과 코드 생성
- 멀티 에이전트 시스템의 워커 에이전트
**Sonnet 4.6** (최고의 코딩 모델):
- 주요 개발 작업
- 멀티 에이전트 워크플로우 오케스트레이션
- 복잡한 코딩 작업
**Opus 4.5** (가장 깊은 추론):
- 복잡한 아키텍처 의사결정
- 최대 추론 요구사항
- 리서치 및 분석 작업
## 컨텍스트 윈도우 관리
컨텍스트 윈도우의 마지막 20%에서는 다음을 피하세요:
- 대규모 리팩토링
- 여러 파일에 걸친 기능 구현
- 복잡한 상호작용 디버깅
컨텍스트 민감도가 낮은 작업:
- 단일 파일 수정
- 독립적인 유틸리티 생성
- 문서 업데이트
- 단순한 버그 수정
## 확장 사고 + 계획 모드
확장 사고는 기본적으로 활성화되어 있으며, 내부 추론을 위해 최대 31,999 토큰을 예약합니다.
확장 사고 제어 방법:
- **전환**: Option+T (macOS) / Alt+T (Windows/Linux)
- **설정**: `~/.claude/settings.json`에서 `alwaysThinkingEnabled` 설정
- **예산 제한**: `export MAX_THINKING_TOKENS=10000`
- **상세 모드**: Ctrl+O로 사고 출력 확인
깊은 추론이 필요한 복잡한 작업:
1. 확장 사고가 활성화되어 있는지 확인 (기본 활성)
2. 구조적 접근을 위해 **계획 모드** 활성화
3. 철저한 분석을 위해 여러 라운드의 비판 수행
4. 다양한 관점을 위해 역할 분리 서브에이전트 사용
## 빌드 문제 해결
빌드 실패 시:
1. **build-error-resolver** 에이전트 사용
2. 에러 메시지 분석
3. 점진적으로 수정
4. 각 수정 후 검증

View File

@@ -0,0 +1,29 @@
# 보안 가이드라인
## 필수 보안 점검
모든 커밋 전:
- [ ] 하드코딩된 시크릿이 없는가 (API 키, 비밀번호, 토큰)
- [ ] 모든 사용자 입력이 검증되었는가
- [ ] SQL 인젝션 방지가 되었는가 (매개변수화된 쿼리)
- [ ] XSS 방지가 되었는가 (HTML 새니타이징)
- [ ] CSRF 보호가 활성화되었는가
- [ ] 인증/인가가 검증되었는가
- [ ] 모든 엔드포인트에 속도 제한이 있는가
- [ ] 에러 메시지가 민감한 데이터를 노출하지 않는가
## 시크릿 관리
- 소스 코드에 시크릿을 절대 하드코딩하지 않기
- 항상 환경 변수나 시크릿 매니저 사용
- 시작 시 필요한 시크릿이 존재하는지 검증
- 노출되었을 수 있는 시크릿은 교체
## 보안 대응 프로토콜
보안 이슈 발견 시:
1. 즉시 중단
2. **security-reviewer** 에이전트 사용
3. 계속 진행하기 전에 치명적 이슈 수정
4. 노출된 시크릿 교체
5. 유사한 이슈가 있는지 전체 코드베이스 검토

View File

@@ -0,0 +1,29 @@
# 테스팅 요구사항
## 최소 테스트 커버리지: 80%
테스트 유형 (모두 필수):
1. **단위 테스트** - 개별 함수, 유틸리티, 컴포넌트
2. **통합 테스트** - API 엔드포인트, 데이터베이스 작업
3. **E2E 테스트** - 핵심 사용자 흐름 (언어별 프레임워크 선택)
## 테스트 주도 개발
필수 워크플로우:
1. 테스트를 먼저 작성 (RED)
2. 테스트 실행 - 실패해야 함
3. 최소한의 구현 작성 (GREEN)
4. 테스트 실행 - 통과해야 함
5. 리팩토링 (IMPROVE)
6. 커버리지 확인 (80% 이상)
## 테스트 실패 문제 해결
1. **tdd-guide** 에이전트 사용
2. 테스트 격리 확인
3. 모킹이 올바른지 검증
4. 테스트가 아닌 구현을 수정 (테스트가 잘못된 경우 제외)
## 에이전트 지원
- **tdd-guide** - 새 기능에 적극적으로 사용, 테스트 먼저 작성을 강제

View File

@@ -0,0 +1,599 @@
---
name: backend-patterns
description: Node.js, Express, Next.js API 라우트를 위한 백엔드 아키텍처 패턴, API 설계, 데이터베이스 최적화 및 서버 사이드 모범 사례.
origin: ECC
---
# 백엔드 개발 패턴
확장 가능한 서버 사이드 애플리케이션을 위한 백엔드 아키텍처 패턴과 모범 사례.
## 활성화 시점
- REST 또는 GraphQL API 엔드포인트를 설계할 때
- Repository, Service 또는 Controller 레이어를 구현할 때
- 데이터베이스 쿼리를 최적화할 때 (N+1, 인덱싱, 커넥션 풀링)
- 캐싱을 추가할 때 (Redis, 인메모리, HTTP 캐시 헤더)
- 백그라운드 작업이나 비동기 처리를 설정할 때
- API를 위한 에러 처리 및 유효성 검사를 구조화할 때
- 미들웨어를 구축할 때 (인증, 로깅, 요청 제한)
## API 설계 패턴
### RESTful API 구조
```typescript
// ✅ Resource-based URLs
GET /api/markets # List resources
GET /api/markets/:id # Get single resource
POST /api/markets # Create resource
PUT /api/markets/:id # Replace resource
PATCH /api/markets/:id # Update resource
DELETE /api/markets/:id # Delete resource
// ✅ Query parameters for filtering, sorting, pagination
GET /api/markets?status=active&sort=volume&limit=20&offset=0
```
### Repository 패턴
```typescript
// Abstract data access logic
interface MarketRepository {
findAll(filters?: MarketFilters): Promise<Market[]>
findById(id: string): Promise<Market | null>
findByIds(ids: string[]): Promise<Market[]>
create(data: CreateMarketDto): Promise<Market>
update(id: string, data: UpdateMarketDto): Promise<Market>
delete(id: string): Promise<void>
}
class SupabaseMarketRepository implements MarketRepository {
async findAll(filters?: MarketFilters): Promise<Market[]> {
let query = supabase.from('markets').select('*')
if (filters?.status) {
query = query.eq('status', filters.status)
}
if (filters?.limit) {
query = query.limit(filters.limit)
}
const { data, error } = await query
if (error) throw new Error(error.message)
return data
}
// Other methods...
}
```
### Service 레이어 패턴
```typescript
// Business logic separated from data access
class MarketService {
constructor(private marketRepo: MarketRepository) {}
async searchMarkets(query: string, limit: number = 10): Promise<Market[]> {
// Business logic
const embedding = await generateEmbedding(query)
const results = await this.vectorSearch(embedding, limit)
// Fetch full data
const markets = await this.marketRepo.findByIds(results.map(r => r.id))
// Sort by similarity
return [...markets].sort((a, b) => {
const scoreA = results.find(r => r.id === a.id)?.score || 0
const scoreB = results.find(r => r.id === b.id)?.score || 0
return scoreA - scoreB
})
}
private async vectorSearch(embedding: number[], limit: number) {
// Vector search implementation
}
}
```
### 미들웨어 패턴
```typescript
// Request/response processing pipeline
export function withAuth(handler: NextApiHandler): NextApiHandler {
return async (req, res) => {
const token = req.headers.authorization?.replace('Bearer ', '')
if (!token) {
return res.status(401).json({ error: 'Unauthorized' })
}
try {
const user = await verifyToken(token)
req.user = user
return handler(req, res)
} catch (error) {
return res.status(401).json({ error: 'Invalid token' })
}
}
}
// Usage
export default withAuth(async (req, res) => {
// Handler has access to req.user
})
```
## 데이터베이스 패턴
### 쿼리 최적화
```typescript
// ✅ GOOD: Select only needed columns
const { data } = await supabase
.from('markets')
.select('id, name, status, volume')
.eq('status', 'active')
.order('volume', { ascending: false })
.limit(10)
// ❌ BAD: Select everything
const { data } = await supabase
.from('markets')
.select('*')
```
### N+1 쿼리 방지
```typescript
// ❌ BAD: N+1 query problem
const markets = await getMarkets()
for (const market of markets) {
market.creator = await getUser(market.creator_id) // N queries
}
// ✅ GOOD: Batch fetch
const markets = await getMarkets()
const creatorIds = markets.map(m => m.creator_id)
const creators = await getUsers(creatorIds) // 1 query
const creatorMap = new Map(creators.map(c => [c.id, c]))
markets.forEach(market => {
market.creator = creatorMap.get(market.creator_id)
})
```
### 트랜잭션 패턴
```typescript
async function createMarketWithPosition(
marketData: CreateMarketDto,
positionData: CreatePositionDto
) {
// Use Supabase transaction
const { data, error } = await supabase.rpc('create_market_with_position', {
market_data: marketData,
position_data: positionData
})
if (error) throw new Error('Transaction failed')
return data
}
// SQL function in Supabase
CREATE OR REPLACE FUNCTION create_market_with_position(
market_data jsonb,
position_data jsonb
)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
BEGIN
-- Start transaction automatically
INSERT INTO markets VALUES (market_data);
INSERT INTO positions VALUES (position_data);
RETURN jsonb_build_object('success', true);
EXCEPTION
WHEN OTHERS THEN
-- Rollback happens automatically
RETURN jsonb_build_object('success', false, 'error', SQLERRM);
END;
$$;
```
## 캐싱 전략
### Redis 캐싱 레이어
```typescript
class CachedMarketRepository implements MarketRepository {
constructor(
private baseRepo: MarketRepository,
private redis: RedisClient
) {}
async findById(id: string): Promise<Market | null> {
// Check cache first
const cached = await this.redis.get(`market:${id}`)
if (cached) {
return JSON.parse(cached)
}
// Cache miss - fetch from database
const market = await this.baseRepo.findById(id)
if (market) {
// Cache for 5 minutes
await this.redis.setex(`market:${id}`, 300, JSON.stringify(market))
}
return market
}
async invalidateCache(id: string): Promise<void> {
await this.redis.del(`market:${id}`)
}
}
```
### Cache-Aside 패턴
```typescript
async function getMarketWithCache(id: string): Promise<Market> {
const cacheKey = `market:${id}`
// Try cache
const cached = await redis.get(cacheKey)
if (cached) return JSON.parse(cached)
// Cache miss - fetch from DB
const market = await db.markets.findUnique({ where: { id } })
if (!market) throw new Error('Market not found')
// Update cache
await redis.setex(cacheKey, 300, JSON.stringify(market))
return market
}
```
## 에러 처리 패턴
### 중앙화된 에러 핸들러
```typescript
class ApiError extends Error {
constructor(
public statusCode: number,
public message: string,
public isOperational = true
) {
super(message)
Object.setPrototypeOf(this, ApiError.prototype)
}
}
export function errorHandler(error: unknown, req: Request): Response {
if (error instanceof ApiError) {
return NextResponse.json({
success: false,
error: error.message
}, { status: error.statusCode })
}
if (error instanceof z.ZodError) {
return NextResponse.json({
success: false,
error: 'Validation failed',
details: error.errors
}, { status: 400 })
}
// Log unexpected errors
console.error('Unexpected error:', error)
return NextResponse.json({
success: false,
error: 'Internal server error'
}, { status: 500 })
}
// Usage
export async function GET(request: Request) {
try {
const data = await fetchData()
return NextResponse.json({ success: true, data })
} catch (error) {
return errorHandler(error, request)
}
}
```
### 지수 백오프를 이용한 재시도
```typescript
async function fetchWithRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
let lastError: Error = new Error('Retry attempts exhausted')
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
lastError = error as Error
if (i < maxRetries - 1) {
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, i) * 1000
await new Promise(resolve => setTimeout(resolve, delay))
}
}
}
throw lastError!
}
// Usage
const data = await fetchWithRetry(() => fetchFromAPI())
```
## 인증 및 인가
### JWT 토큰 검증
```typescript
import jwt from 'jsonwebtoken'
interface JWTPayload {
userId: string
email: string
role: 'admin' | 'user'
}
export function verifyToken(token: string): JWTPayload {
try {
const payload = jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload
return payload
} catch (error) {
throw new ApiError(401, 'Invalid token')
}
}
export async function requireAuth(request: Request) {
const token = request.headers.get('authorization')?.replace('Bearer ', '')
if (!token) {
throw new ApiError(401, 'Missing authorization token')
}
return verifyToken(token)
}
// Usage in API route
export async function GET(request: Request) {
const user = await requireAuth(request)
const data = await getDataForUser(user.userId)
return NextResponse.json({ success: true, data })
}
```
### 역할 기반 접근 제어
```typescript
type Permission = 'read' | 'write' | 'delete' | 'admin'
interface User {
id: string
role: 'admin' | 'moderator' | 'user'
}
const rolePermissions: Record<User['role'], Permission[]> = {
admin: ['read', 'write', 'delete', 'admin'],
moderator: ['read', 'write', 'delete'],
user: ['read', 'write']
}
export function hasPermission(user: User, permission: Permission): boolean {
return rolePermissions[user.role].includes(permission)
}
export function requirePermission(permission: Permission) {
return (handler: (request: Request, user: User) => Promise<Response>) => {
return async (request: Request) => {
const user = await requireAuth(request)
if (!hasPermission(user, permission)) {
throw new ApiError(403, 'Insufficient permissions')
}
return handler(request, user)
}
}
}
// Usage - HOF wraps the handler
export const DELETE = requirePermission('delete')(
async (request: Request, user: User) => {
// Handler receives authenticated user with verified permission
return new Response('Deleted', { status: 200 })
}
)
```
## 요청 제한
### 간단한 인메모리 요청 제한기
```typescript
class RateLimiter {
private requests = new Map<string, number[]>()
async checkLimit(
identifier: string,
maxRequests: number,
windowMs: number
): Promise<boolean> {
const now = Date.now()
const requests = this.requests.get(identifier) || []
// Remove old requests outside window
const recentRequests = requests.filter(time => now - time < windowMs)
if (recentRequests.length >= maxRequests) {
return false // Rate limit exceeded
}
// Add current request
recentRequests.push(now)
this.requests.set(identifier, recentRequests)
return true
}
}
const limiter = new RateLimiter()
export async function GET(request: Request) {
const ip = request.headers.get('x-forwarded-for') || 'unknown'
const allowed = await limiter.checkLimit(ip, 100, 60000) // 100 req/min
if (!allowed) {
return NextResponse.json({
error: 'Rate limit exceeded'
}, { status: 429 })
}
// Continue with request
}
```
## 백그라운드 작업 및 큐
### 간단한 큐 패턴
```typescript
class JobQueue<T> {
private queue: T[] = []
private processing = false
async add(job: T): Promise<void> {
this.queue.push(job)
if (!this.processing) {
this.process()
}
}
private async process(): Promise<void> {
this.processing = true
while (this.queue.length > 0) {
const job = this.queue.shift()!
try {
await this.execute(job)
} catch (error) {
console.error('Job failed:', error)
}
}
this.processing = false
}
private async execute(job: T): Promise<void> {
// Job execution logic
}
}
// Usage for indexing markets
interface IndexJob {
marketId: string
}
const indexQueue = new JobQueue<IndexJob>()
export async function POST(request: Request) {
const { marketId } = await request.json()
// Add to queue instead of blocking
await indexQueue.add({ marketId })
return NextResponse.json({ success: true, message: 'Job queued' })
}
```
## 로깅 및 모니터링
### 구조화된 로깅
```typescript
interface LogContext {
userId?: string
requestId?: string
method?: string
path?: string
[key: string]: unknown
}
class Logger {
log(level: 'info' | 'warn' | 'error', message: string, context?: LogContext) {
const entry = {
timestamp: new Date().toISOString(),
level,
message,
...context
}
console.log(JSON.stringify(entry))
}
info(message: string, context?: LogContext) {
this.log('info', message, context)
}
warn(message: string, context?: LogContext) {
this.log('warn', message, context)
}
error(message: string, error: Error, context?: LogContext) {
this.log('error', message, {
...context,
error: error.message,
stack: error.stack
})
}
}
const logger = new Logger()
// Usage
export async function GET(request: Request) {
const requestId = crypto.randomUUID()
logger.info('Fetching markets', {
requestId,
method: 'GET',
path: '/api/markets'
})
try {
const markets = await fetchMarkets()
return NextResponse.json({ success: true, data: markets })
} catch (error) {
logger.error('Failed to fetch markets', error as Error, { requestId })
return NextResponse.json({ error: 'Internal error' }, { status: 500 })
}
}
```
**기억하세요**: 백엔드 패턴은 확장 가능하고 유지보수 가능한 서버 사이드 애플리케이션을 가능하게 합니다. 복잡도 수준에 맞는 패턴을 선택하세요.

View File

@@ -0,0 +1,447 @@
---
name: clickhouse-io
description: 고성능 분석 워크로드를 위한 ClickHouse 데이터베이스 패턴, 쿼리 최적화, 분석 및 데이터 엔지니어링 모범 사례.
origin: ECC
---
# ClickHouse 분석 패턴
고성능 분석 및 데이터 엔지니어링을 위한 ClickHouse 전용 패턴.
## 활성화 시점
- ClickHouse 테이블 스키마 설계 시 (MergeTree 엔진 선택)
- 분석 쿼리 작성 시 (집계, 윈도우 함수, 조인)
- 쿼리 성능 최적화 시 (파티션 프루닝, 프로젝션, 구체화된 뷰)
- 대량 데이터 수집 시 (배치 삽입, Kafka 통합)
- PostgreSQL/MySQL에서 ClickHouse로 분석 마이그레이션 시
- 실시간 대시보드 또는 시계열 분석 구현 시
## 개요
ClickHouse는 온라인 분석 처리(OLAP)를 위한 컬럼 지향 데이터베이스 관리 시스템(DBMS)입니다. 대규모 데이터셋에 대한 빠른 분석 쿼리에 최적화되어 있습니다.
**주요 특징:**
- 컬럼 지향 저장소
- 데이터 압축
- 병렬 쿼리 실행
- 분산 쿼리
- 실시간 분석
## 테이블 설계 패턴
### MergeTree 엔진 (가장 일반적)
```sql
CREATE TABLE markets_analytics (
date Date,
market_id String,
market_name String,
volume UInt64,
trades UInt32,
unique_traders UInt32,
avg_trade_size Float64,
created_at DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(date)
ORDER BY (date, market_id)
SETTINGS index_granularity = 8192;
```
### ReplacingMergeTree (중복 제거)
```sql
-- 중복이 있을 수 있는 데이터용 (예: 여러 소스에서 수집된 경우)
CREATE TABLE user_events (
event_id String,
user_id String,
event_type String,
timestamp DateTime,
properties String
) ENGINE = ReplacingMergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (user_id, event_id, timestamp)
PRIMARY KEY (user_id, event_id);
```
### AggregatingMergeTree (사전 집계)
```sql
-- 집계 메트릭을 유지하기 위한 용도
CREATE TABLE market_stats_hourly (
hour DateTime,
market_id String,
total_volume AggregateFunction(sum, UInt64),
total_trades AggregateFunction(count, UInt32),
unique_users AggregateFunction(uniq, String)
) ENGINE = AggregatingMergeTree()
PARTITION BY toYYYYMM(hour)
ORDER BY (hour, market_id);
-- 집계된 데이터 조회
SELECT
hour,
market_id,
sumMerge(total_volume) AS volume,
countMerge(total_trades) AS trades,
uniqMerge(unique_users) AS users
FROM market_stats_hourly
WHERE hour >= toStartOfHour(now() - INTERVAL 24 HOUR)
GROUP BY hour, market_id
ORDER BY hour DESC;
```
## 쿼리 최적화 패턴
### 효율적인 필터링
```sql
-- ✅ 좋음: 인덱스된 컬럼을 먼저 사용
SELECT *
FROM markets_analytics
WHERE date >= '2025-01-01'
AND market_id = 'market-123'
AND volume > 1000
ORDER BY date DESC
LIMIT 100;
-- ❌ 나쁨: 비인덱스 컬럼을 먼저 필터링
SELECT *
FROM markets_analytics
WHERE volume > 1000
AND market_name LIKE '%election%'
AND date >= '2025-01-01';
```
### 집계
```sql
-- ✅ 좋음: ClickHouse 전용 집계 함수를 사용
SELECT
toStartOfDay(created_at) AS day,
market_id,
sum(volume) AS total_volume,
count() AS total_trades,
uniq(trader_id) AS unique_traders,
avg(trade_size) AS avg_size
FROM trades
WHERE created_at >= today() - INTERVAL 7 DAY
GROUP BY day, market_id
ORDER BY day DESC, total_volume DESC;
-- ✅ 백분위수에는 quantile 사용 (percentile보다 효율적)
SELECT
quantile(0.50)(trade_size) AS median,
quantile(0.95)(trade_size) AS p95,
quantile(0.99)(trade_size) AS p99
FROM trades
WHERE created_at >= now() - INTERVAL 1 HOUR;
```
### 윈도우 함수
```sql
-- 누적 합계 계산
SELECT
date,
market_id,
volume,
sum(volume) OVER (
PARTITION BY market_id
ORDER BY date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_volume
FROM markets_analytics
WHERE date >= today() - INTERVAL 30 DAY
ORDER BY market_id, date;
```
## 데이터 삽입 패턴
### 배치 삽입 (권장)
```typescript
import { ClickHouse } from 'clickhouse'
const clickhouse = new ClickHouse({
url: process.env.CLICKHOUSE_URL,
port: 8123,
basicAuth: {
username: process.env.CLICKHOUSE_USER,
password: process.env.CLICKHOUSE_PASSWORD
}
})
// ✅ 배치 삽입 (효율적)
async function bulkInsertTrades(trades: Trade[]) {
const rows = trades.map(trade => ({
id: trade.id,
market_id: trade.market_id,
user_id: trade.user_id,
amount: trade.amount,
timestamp: trade.timestamp.toISOString()
}))
await clickhouse.insert('trades', rows)
}
// ❌ 개별 삽입 (느림)
async function insertTrade(trade: Trade) {
// 루프 안에서 이렇게 하지 마세요!
await clickhouse.query(`
INSERT INTO trades VALUES ('${trade.id}', ...)
`).toPromise()
}
```
### 스트리밍 삽입
```typescript
// 연속적인 데이터 수집용
import { createWriteStream } from 'fs'
import { pipeline } from 'stream/promises'
async function streamInserts() {
const stream = clickhouse.insert('trades').stream()
for await (const batch of dataSource) {
stream.write(batch)
}
await stream.end()
}
```
## 구체화된 뷰
### 실시간 집계
```sql
-- 시간별 통계를 위한 materialized view 생성
CREATE MATERIALIZED VIEW market_stats_hourly_mv
TO market_stats_hourly
AS SELECT
toStartOfHour(timestamp) AS hour,
market_id,
sumState(amount) AS total_volume,
countState() AS total_trades,
uniqState(user_id) AS unique_users
FROM trades
GROUP BY hour, market_id;
-- materialized view 조회
SELECT
hour,
market_id,
sumMerge(total_volume) AS volume,
countMerge(total_trades) AS trades,
uniqMerge(unique_users) AS users
FROM market_stats_hourly
WHERE hour >= now() - INTERVAL 24 HOUR
GROUP BY hour, market_id;
```
## 성능 모니터링
### 쿼리 성능
```sql
-- 느린 쿼리 확인
SELECT
query_id,
user,
query,
query_duration_ms,
read_rows,
read_bytes,
memory_usage
FROM system.query_log
WHERE type = 'QueryFinish'
AND query_duration_ms > 1000
AND event_time >= now() - INTERVAL 1 HOUR
ORDER BY query_duration_ms DESC
LIMIT 10;
```
### 테이블 통계
```sql
-- 테이블 크기 확인
SELECT
database,
table,
formatReadableSize(sum(bytes)) AS size,
sum(rows) AS rows,
max(modification_time) AS latest_modification
FROM system.parts
WHERE active
GROUP BY database, table
ORDER BY sum(bytes) DESC;
```
## 일반적인 분석 쿼리
### 시계열 분석
```sql
-- 일간 활성 사용자
SELECT
toDate(timestamp) AS date,
uniq(user_id) AS daily_active_users
FROM events
WHERE timestamp >= today() - INTERVAL 30 DAY
GROUP BY date
ORDER BY date;
-- 리텐션 분석
SELECT
signup_date,
countIf(days_since_signup = 0) AS day_0,
countIf(days_since_signup = 1) AS day_1,
countIf(days_since_signup = 7) AS day_7,
countIf(days_since_signup = 30) AS day_30
FROM (
SELECT
user_id,
min(toDate(timestamp)) AS signup_date,
toDate(timestamp) AS activity_date,
dateDiff('day', signup_date, activity_date) AS days_since_signup
FROM events
GROUP BY user_id, activity_date
)
GROUP BY signup_date
ORDER BY signup_date DESC;
```
### 퍼널 분석
```sql
-- 전환 퍼널
SELECT
countIf(step = 'viewed_market') AS viewed,
countIf(step = 'clicked_trade') AS clicked,
countIf(step = 'completed_trade') AS completed,
round(clicked / viewed * 100, 2) AS view_to_click_rate,
round(completed / clicked * 100, 2) AS click_to_completion_rate
FROM (
SELECT
user_id,
session_id,
event_type AS step
FROM events
WHERE event_date = today()
)
GROUP BY session_id;
```
### 코호트 분석
```sql
-- 가입 월별 사용자 코호트
SELECT
toStartOfMonth(signup_date) AS cohort,
toStartOfMonth(activity_date) AS month,
dateDiff('month', cohort, month) AS months_since_signup,
count(DISTINCT user_id) AS active_users
FROM (
SELECT
user_id,
min(toDate(timestamp)) OVER (PARTITION BY user_id) AS signup_date,
toDate(timestamp) AS activity_date
FROM events
)
GROUP BY cohort, month, months_since_signup
ORDER BY cohort, months_since_signup;
```
## 데이터 파이프라인 패턴
### ETL 패턴
```typescript
// 추출, 변환, 적재(ETL)
async function etlPipeline() {
// 1. 소스에서 추출
const rawData = await extractFromPostgres()
// 2. 변환
const transformed = rawData.map(row => ({
date: new Date(row.created_at).toISOString().split('T')[0],
market_id: row.market_slug,
volume: parseFloat(row.total_volume),
trades: parseInt(row.trade_count)
}))
// 3. ClickHouse에 적재
await bulkInsertToClickHouse(transformed)
}
// 주기적으로 실행
let etlRunning = false
setInterval(async () => {
if (etlRunning) return
etlRunning = true
try {
await etlPipeline()
} finally {
etlRunning = false
}
}, 60 * 60 * 1000) // Every hour
```
### 변경 데이터 캡처 (CDC)
```typescript
// PostgreSQL 변경을 수신하고 ClickHouse와 동기화
import { Client } from 'pg'
const pgClient = new Client({ connectionString: process.env.DATABASE_URL })
pgClient.query('LISTEN market_updates')
pgClient.on('notification', async (msg) => {
const update = JSON.parse(msg.payload)
await clickhouse.insert('market_updates', [
{
market_id: update.id,
event_type: update.operation, // INSERT, UPDATE, DELETE
timestamp: new Date(),
data: JSON.stringify(update.new_data)
}
])
})
```
## 모범 사례
### 1. 파티셔닝 전략
- 시간별 파티셔닝 (보통 월 또는 일)
- 파티션이 너무 많은 것 방지 (성능 영향)
- 파티션 키에 DATE 타입 사용
### 2. 정렬 키
- 가장 자주 필터링되는 컬럼을 먼저 배치
- 카디널리티 고려 (높은 카디널리티 먼저)
- 정렬이 압축에 영향을 미침
### 3. 데이터 타입
- 가장 작은 적절한 타입 사용 (UInt32 vs UInt64)
- 반복되는 문자열에 LowCardinality 사용
- 범주형 데이터에 Enum 사용
### 4. 피해야 할 것
- SELECT * (컬럼을 명시)
- FINAL (쿼리 전에 데이터를 병합)
- 너무 많은 JOIN (분석을 위해 비정규화)
- 작은 빈번한 삽입 (배치 처리)
### 5. 모니터링
- 쿼리 성능 추적
- 디스크 사용량 모니터링
- 병합 작업 확인
- 슬로우 쿼리 로그 검토
**기억하세요**: ClickHouse는 분석 워크로드에 탁월합니다. 쿼리 패턴에 맞게 테이블을 설계하고, 배치 삽입을 사용하며, 실시간 집계를 위해 구체화된 뷰를 활용하세요.

View File

@@ -0,0 +1,530 @@
---
name: coding-standards
description: TypeScript, JavaScript, React, Node.js 개발을 위한 범용 코딩 표준, 모범 사례 및 패턴.
origin: ECC
---
# 코딩 표준 및 모범 사례
모든 프로젝트에 적용 가능한 범용 코딩 표준.
## 활성화 시점
- 새 프로젝트 또는 모듈을 시작할 때
- 코드 품질 및 유지보수성을 검토할 때
- 기존 코드를 컨벤션에 맞게 리팩터링할 때
- 네이밍, 포맷팅 또는 구조적 일관성을 적용할 때
- 린팅, 포맷팅 또는 타입 검사 규칙을 설정할 때
- 새 기여자에게 코딩 컨벤션을 안내할 때
## 코드 품질 원칙
### 1. 가독성 우선
- 코드는 작성보다 읽히는 횟수가 더 많다
- 명확한 변수 및 함수 이름 사용
- 주석보다 자기 문서화 코드를 선호
- 일관된 포맷팅 유지
### 2. KISS (Keep It Simple, Stupid)
- 동작하는 가장 단순한 해결책
- 과도한 엔지니어링 지양
- 조기 최적화 금지
- 이해하기 쉬운 코드 > 영리한 코드
### 3. DRY (Don't Repeat Yourself)
- 공통 로직을 함수로 추출
- 재사용 가능한 컴포넌트 생성
- 모듈 간 유틸리티 공유
- 복사-붙여넣기 프로그래밍 지양
### 4. YAGNI (You Aren't Gonna Need It)
- 필요하기 전에 기능을 만들지 않기
- 추측에 의한 일반화 지양
- 필요할 때만 복잡성 추가
- 단순하게 시작하고 필요할 때 리팩터링
## TypeScript/JavaScript 표준
### 변수 네이밍
```typescript
// ✅ GOOD: Descriptive names
const marketSearchQuery = 'election'
const isUserAuthenticated = true
const totalRevenue = 1000
// ❌ BAD: Unclear names
const q = 'election'
const flag = true
const x = 1000
```
### 함수 네이밍
```typescript
// ✅ GOOD: Verb-noun pattern
async function fetchMarketData(marketId: string) { }
function calculateSimilarity(a: number[], b: number[]) { }
function isValidEmail(email: string): boolean { }
// ❌ BAD: Unclear or noun-only
async function market(id: string) { }
function similarity(a, b) { }
function email(e) { }
```
### 불변성 패턴 (필수)
```typescript
// ✅ ALWAYS use spread operator
const updatedUser = {
...user,
name: 'New Name'
}
const updatedArray = [...items, newItem]
// ❌ NEVER mutate directly
user.name = 'New Name' // BAD
items.push(newItem) // BAD
```
### 에러 처리
```typescript
// ✅ GOOD: Comprehensive error handling
async function fetchData(url: string) {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
return await response.json()
} catch (error) {
console.error('Fetch failed:', error)
throw new Error('Failed to fetch data')
}
}
// ❌ BAD: No error handling
async function fetchData(url) {
const response = await fetch(url)
return response.json()
}
```
### Async/Await 모범 사례
```typescript
// ✅ GOOD: Parallel execution when possible
const [users, markets, stats] = await Promise.all([
fetchUsers(),
fetchMarkets(),
fetchStats()
])
// ❌ BAD: Sequential when unnecessary
const users = await fetchUsers()
const markets = await fetchMarkets()
const stats = await fetchStats()
```
### 타입 안전성
```typescript
// ✅ GOOD: Proper types
interface Market {
id: string
name: string
status: 'active' | 'resolved' | 'closed'
created_at: Date
}
function getMarket(id: string): Promise<Market> {
// Implementation
}
// ❌ BAD: Using 'any'
function getMarket(id: any): Promise<any> {
// Implementation
}
```
## React 모범 사례
### 컴포넌트 구조
```typescript
// ✅ GOOD: Functional component with types
interface ButtonProps {
children: React.ReactNode
onClick: () => void
disabled?: boolean
variant?: 'primary' | 'secondary'
}
export function Button({
children,
onClick,
disabled = false,
variant = 'primary'
}: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
className={`btn btn-${variant}`}
>
{children}
</button>
)
}
// ❌ BAD: No types, unclear structure
export function Button(props) {
return <button onClick={props.onClick}>{props.children}</button>
}
```
### 커스텀 Hook
```typescript
// ✅ GOOD: Reusable custom hook
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}
// Usage
const debouncedQuery = useDebounce(searchQuery, 500)
```
### 상태 관리
```typescript
// ✅ GOOD: Proper state updates
const [count, setCount] = useState(0)
// Functional update for state based on previous state
setCount(prev => prev + 1)
// ❌ BAD: Direct state reference
setCount(count + 1) // Can be stale in async scenarios
```
### 조건부 렌더링
```typescript
// ✅ GOOD: Clear conditional rendering
{isLoading && <Spinner />}
{error && <ErrorMessage error={error} />}
{data && <DataDisplay data={data} />}
// ❌ BAD: Ternary hell
{isLoading ? <Spinner /> : error ? <ErrorMessage error={error} /> : data ? <DataDisplay data={data} /> : null}
```
## API 설계 표준
### REST API 컨벤션
```
GET /api/markets # List all markets
GET /api/markets/:id # Get specific market
POST /api/markets # Create new market
PUT /api/markets/:id # Update market (full)
PATCH /api/markets/:id # Update market (partial)
DELETE /api/markets/:id # Delete market
# Query parameters for filtering
GET /api/markets?status=active&limit=10&offset=0
```
### 응답 형식
```typescript
// ✅ GOOD: Consistent response structure
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
meta?: {
total: number
page: number
limit: number
}
}
// Success response
return NextResponse.json({
success: true,
data: markets,
meta: { total: 100, page: 1, limit: 10 }
})
// Error response
return NextResponse.json({
success: false,
error: 'Invalid request'
}, { status: 400 })
```
### 입력 유효성 검사
```typescript
import { z } from 'zod'
// ✅ GOOD: Schema validation
const CreateMarketSchema = z.object({
name: z.string().min(1).max(200),
description: z.string().min(1).max(2000),
endDate: z.string().datetime(),
categories: z.array(z.string()).min(1)
})
export async function POST(request: Request) {
const body = await request.json()
try {
const validated = CreateMarketSchema.parse(body)
// Proceed with validated data
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({
success: false,
error: 'Validation failed',
details: error.errors
}, { status: 400 })
}
}
}
```
## 파일 구성
### 프로젝트 구조
```
src/
├── app/ # Next.js App Router
│ ├── api/ # API routes
│ ├── markets/ # Market pages
│ └── (auth)/ # Auth pages (route groups)
├── components/ # React components
│ ├── ui/ # Generic UI components
│ ├── forms/ # Form components
│ └── layouts/ # Layout components
├── hooks/ # Custom React hooks
├── lib/ # Utilities and configs
│ ├── api/ # API clients
│ ├── utils/ # Helper functions
│ └── constants/ # Constants
├── types/ # TypeScript types
└── styles/ # Global styles
```
### 파일 네이밍
```
components/Button.tsx # PascalCase for components
hooks/useAuth.ts # camelCase with 'use' prefix
lib/formatDate.ts # camelCase for utilities
types/market.types.ts # camelCase with .types suffix
```
## 주석 및 문서화
### 주석을 작성해야 하는 경우
```typescript
// ✅ GOOD: Explain WHY, not WHAT
// Use exponential backoff to avoid overwhelming the API during outages
const delay = Math.min(1000 * Math.pow(2, retryCount), 30000)
// Deliberately using mutation here for performance with large arrays
items.push(newItem)
// ❌ BAD: Stating the obvious
// Increment counter by 1
count++
// Set name to user's name
name = user.name
```
### 공개 API를 위한 JSDoc
```typescript
/**
* Searches markets using semantic similarity.
*
* @param query - Natural language search query
* @param limit - Maximum number of results (default: 10)
* @returns Array of markets sorted by similarity score
* @throws {Error} If OpenAI API fails or Redis unavailable
*
* @example
* ```typescript
* const results = await searchMarkets('election', 5)
* console.log(results[0].name) // "Trump vs Biden"
* ```
*/
export async function searchMarkets(
query: string,
limit: number = 10
): Promise<Market[]> {
// Implementation
}
```
## 성능 모범 사례
### 메모이제이션
```typescript
import { useMemo, useCallback } from 'react'
// ✅ GOOD: Memoize expensive computations
const sortedMarkets = useMemo(() => {
return [...markets].sort((a, b) => b.volume - a.volume)
}, [markets])
// ✅ GOOD: Memoize callbacks
const handleSearch = useCallback((query: string) => {
setSearchQuery(query)
}, [])
```
### 지연 로딩
```typescript
import { lazy, Suspense } from 'react'
// ✅ GOOD: Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'))
export function Dashboard() {
return (
<Suspense fallback={<Spinner />}>
<HeavyChart />
</Suspense>
)
}
```
### 데이터베이스 쿼리
```typescript
// ✅ GOOD: Select only needed columns
const { data } = await supabase
.from('markets')
.select('id, name, status')
.limit(10)
// ❌ BAD: Select everything
const { data } = await supabase
.from('markets')
.select('*')
```
## 테스트 표준
### 테스트 구조 (AAA 패턴)
```typescript
test('calculates similarity correctly', () => {
// Arrange
const vector1 = [1, 0, 0]
const vector2 = [0, 1, 0]
// Act
const similarity = calculateCosineSimilarity(vector1, vector2)
// Assert
expect(similarity).toBe(0)
})
```
### 테스트 네이밍
```typescript
// ✅ GOOD: Descriptive test names
test('returns empty array when no markets match query', () => { })
test('throws error when OpenAI API key is missing', () => { })
test('falls back to substring search when Redis unavailable', () => { })
// ❌ BAD: Vague test names
test('works', () => { })
test('test search', () => { })
```
## 코드 스멜 감지
다음 안티패턴을 주의하세요:
### 1. 긴 함수
```typescript
// ❌ BAD: Function > 50 lines
function processMarketData() {
// 100 lines of code
}
// ✅ GOOD: Split into smaller functions
function processMarketData() {
const validated = validateData()
const transformed = transformData(validated)
return saveData(transformed)
}
```
### 2. 깊은 중첩
```typescript
// ❌ BAD: 5+ levels of nesting
if (user) {
if (user.isAdmin) {
if (market) {
if (market.isActive) {
if (hasPermission) {
// Do something
}
}
}
}
}
// ✅ GOOD: Early returns
if (!user) return
if (!user.isAdmin) return
if (!market) return
if (!market.isActive) return
if (!hasPermission) return
// Do something
```
### 3. 매직 넘버
```typescript
// ❌ BAD: Unexplained numbers
if (retryCount > 3) { }
setTimeout(callback, 500)
// ✅ GOOD: Named constants
const MAX_RETRIES = 3
const DEBOUNCE_DELAY_MS = 500
if (retryCount > MAX_RETRIES) { }
setTimeout(callback, DEBOUNCE_DELAY_MS)
```
**기억하세요**: 코드 품질은 타협할 수 없습니다. 명확하고 유지보수 가능한 코드가 빠른 개발과 자신감 있는 리팩터링을 가능하게 합니다.

View File

@@ -0,0 +1,363 @@
---
name: continuous-learning-v2
description: 훅을 통해 세션을 관찰하고, 신뢰도 점수가 있는 원자적 본능을 생성하며, 이를 스킬/명령어/에이전트로 진화시키는 본능 기반 학습 시스템. v2.1에서는 프로젝트 간 오염을 방지하기 위한 프로젝트 범위 본능이 추가되었습니다.
origin: ECC
version: 2.1.0
---
# 지속적 학습 v2.1 - 본능 기반 아키텍처
Claude Code 세션을 원자적 "본능(instinct)" -- 신뢰도 점수가 있는 작은 학습된 행동 -- 을 통해 재사용 가능한 지식으로 변환하는 고급 학습 시스템입니다.
**v2.1**에서는 **프로젝트 범위 본능**이 추가되었습니다 -- React 패턴은 React 프로젝트에, Python 규칙은 Python 프로젝트에 유지되며, 범용 패턴(예: "항상 입력 유효성 검사")은 전역으로 공유됩니다.
## 활성화 시점
- Claude Code 세션에서 자동 학습 설정 시
- 훅을 통한 본능 기반 행동 추출 구성 시
- 학습된 행동의 신뢰도 임계값 조정 시
- 본능 라이브러리 검토, 내보내기, 가져오기 시
- 본능을 완전한 스킬, 명령어 또는 에이전트로 진화 시
- 프로젝트 범위 vs 전역 본능 관리 시
- 프로젝트에서 전역 범위로 본능 승격 시
## v2.1의 새로운 기능
| 기능 | v2.0 | v2.1 |
|---------|------|------|
| 저장소 | 전역 (~/.claude/homunculus/) | 프로젝트 범위 (projects/<hash>/) |
| 범위 | 모든 본능이 어디서나 적용 | 프로젝트 범위 + 전역 |
| 감지 | 없음 | git remote URL / 저장소 경로 |
| 승격 | 해당 없음 | 2개 이상 프로젝트에서 확인 시 프로젝트 -> 전역 |
| 명령어 | 4개 (status/evolve/export/import) | 6개 (+promote/projects) |
| 프로젝트 간 | 오염 위험 | 기본적으로 격리 |
## v2의 새로운 기능 (v1 대비)
| 기능 | v1 | v2 |
|---------|----|----|
| 관찰 | Stop 훅 (세션 종료) | PreToolUse/PostToolUse (100% 신뢰성) |
| 분석 | 메인 컨텍스트 | 백그라운드 에이전트 (Haiku) |
| 세분성 | 전체 스킬 | 원자적 "본능" |
| 신뢰도 | 없음 | 0.3-0.9 가중치 |
| 진화 | 직접 스킬로 | 본능 -> 클러스터 -> 스킬/명령어/에이전트 |
| 공유 | 없음 | 본능 내보내기/가져오기 |
## 본능 모델
본능은 작은 학습된 행동입니다:
```yaml
---
id: prefer-functional-style
trigger: "when writing new functions"
confidence: 0.7
domain: "code-style"
source: "session-observation"
scope: project
project_id: "a1b2c3d4e5f6"
project_name: "my-react-app"
---
# Prefer Functional Style
## Action
Use functional patterns over classes when appropriate.
## Evidence
- Observed 5 instances of functional pattern preference
- User corrected class-based approach to functional on 2025-01-15
```
**속성:**
- **원자적** -- 하나의 트리거, 하나의 액션
- **신뢰도 가중치** -- 0.3 = 잠정적, 0.9 = 거의 확실
- **도메인 태그** -- code-style, testing, git, debugging, workflow 등
- **증거 기반** -- 어떤 관찰이 이를 생성했는지 추적
- **범위 인식** -- `project` (기본값) 또는 `global`
## 작동 방식
```
세션 활동 (git 저장소 내)
|
| 훅이 프롬프트 + 도구 사용을 캡처 (100% 신뢰성)
| + 프로젝트 컨텍스트 감지 (git remote / 저장소 경로)
v
+---------------------------------------------+
| projects/<project-hash>/observations.jsonl |
| (프롬프트, 도구 호출, 결과, 프로젝트) |
+---------------------------------------------+
|
| 관찰자 에이전트가 읽기 (백그라운드, Haiku)
v
+---------------------------------------------+
| 패턴 감지 |
| * 사용자 수정 -> 본능 |
| * 에러 해결 -> 본능 |
| * 반복 워크플로우 -> 본능 |
| * 범위 결정: 프로젝트 또는 전역? |
+---------------------------------------------+
|
| 생성/업데이트
v
+---------------------------------------------+
| projects/<project-hash>/instincts/personal/ |
| * prefer-functional.yaml (0.7) [project] |
| * use-react-hooks.yaml (0.9) [project] |
+---------------------------------------------+
| instincts/personal/ (전역) |
| * always-validate-input.yaml (0.85) [global]|
| * grep-before-edit.yaml (0.6) [global] |
+---------------------------------------------+
|
| /evolve 클러스터링 + /promote
v
+---------------------------------------------+
| projects/<hash>/evolved/ (프로젝트 범위) |
| evolved/ (전역) |
| * commands/new-feature.md |
| * skills/testing-workflow.md |
| * agents/refactor-specialist.md |
+---------------------------------------------+
```
## 프로젝트 감지
시스템이 현재 프로젝트를 자동으로 감지합니다:
1. **`CLAUDE_PROJECT_DIR` 환경 변수** (최우선 순위)
2. **`git remote get-url origin`** -- 이식 가능한 프로젝트 ID를 생성하기 위해 해시됨 (서로 다른 머신에서 같은 저장소는 같은 ID를 가짐)
3. **`git rev-parse --show-toplevel`** -- 저장소 경로를 사용한 폴백 (머신별)
4. **전역 폴백** -- 프로젝트가 감지되지 않으면 본능은 전역 범위로 이동
각 프로젝트는 12자 해시 ID를 받습니다 (예: `a1b2c3d4e5f6`). `~/.claude/homunculus/projects.json`의 레지스트리 파일이 ID를 사람이 읽을 수 있는 이름에 매핑합니다.
## 빠른 시작
### 1. 관찰 훅 활성화
`~/.claude/settings.json`에 추가하세요.
**플러그인으로 설치한 경우** (권장):
```json
{
"hooks": {
"PreToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/hooks/observe.sh"
}]
}],
"PostToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/skills/continuous-learning-v2/hooks/observe.sh"
}]
}]
}
}
```
**수동으로 `~/.claude/skills`에 설치한 경우**:
```json
{
"hooks": {
"PreToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "~/.claude/skills/continuous-learning-v2/hooks/observe.sh"
}]
}],
"PostToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "~/.claude/skills/continuous-learning-v2/hooks/observe.sh"
}]
}]
}
}
```
### 2. 디렉터리 구조 초기화
시스템은 첫 사용 시 자동으로 디렉터리를 생성하지만, 수동으로도 생성할 수 있습니다:
```bash
# Global directories
mkdir -p ~/.claude/homunculus/{instincts/{personal,inherited},evolved/{agents,skills,commands},projects}
# Project directories are auto-created when the hook first runs in a git repo
```
### 3. 본능 명령어 사용
```bash
/instinct-status # 학습된 본능 표시 (프로젝트 + 전역)
/evolve # 관련 본능을 스킬/명령어로 클러스터링
/instinct-export # 본능을 파일로 내보내기
/instinct-import # 다른 사람의 본능 가져오기
/promote # 프로젝트 본능을 전역 범위로 승격
/projects # 모든 알려진 프로젝트와 본능 개수 목록
```
## 명령어
| 명령어 | 설명 |
|---------|-------------|
| `/instinct-status` | 모든 본능 (프로젝트 범위 + 전역) 을 신뢰도와 함께 표시 |
| `/evolve` | 관련 본능을 스킬/명령어로 클러스터링, 승격 제안 |
| `/instinct-export` | 본능 내보내기 (범위/도메인으로 필터링 가능) |
| `/instinct-import <file>` | 범위 제어와 함께 본능 가져오기 |
| `/promote [id]` | 프로젝트 본능을 전역 범위로 승격 |
| `/projects` | 모든 알려진 프로젝트와 본능 개수 목록 |
## 구성
백그라운드 관찰자를 제어하려면 `config.json`을 편집하세요:
```json
{
"version": "2.1",
"observer": {
"enabled": false,
"run_interval_minutes": 5,
"min_observations_to_analyze": 20
}
}
```
| 키 | 기본값 | 설명 |
|-----|---------|-------------|
| `observer.enabled` | `false` | 백그라운드 관찰자 에이전트 활성화 |
| `observer.run_interval_minutes` | `5` | 관찰자가 관찰 결과를 분석하는 빈도 |
| `observer.min_observations_to_analyze` | `20` | 분석 실행 전 최소 관찰 횟수 |
기타 동작 (관찰 캡처, 본능 임계값, 프로젝트 범위, 승격 기준)은 `instinct-cli.py``observe.sh`의 코드 기본값으로 구성됩니다.
## 파일 구조
```
~/.claude/homunculus/
+-- identity.json # 프로필, 기술 수준
+-- projects.json # 레지스트리: 프로젝트 해시 -> 이름/경로/리모트
+-- observations.jsonl # 전역 관찰 결과 (폴백)
+-- instincts/
| +-- personal/ # 전역 자동 학습된 본능
| +-- inherited/ # 전역 가져온 본능
+-- evolved/
| +-- agents/ # 전역 생성된 에이전트
| +-- skills/ # 전역 생성된 스킬
| +-- commands/ # 전역 생성된 명령어
+-- projects/
+-- a1b2c3d4e5f6/ # 프로젝트 해시 (git remote URL에서)
| +-- observations.jsonl
| +-- observations.archive/
| +-- instincts/
| | +-- personal/ # 프로젝트별 자동 학습
| | +-- inherited/ # 프로젝트별 가져온 것
| +-- evolved/
| +-- skills/
| +-- commands/
| +-- agents/
+-- f6e5d4c3b2a1/ # 다른 프로젝트
+-- ...
```
## 범위 결정 가이드
| 패턴 유형 | 범위 | 예시 |
|-------------|-------|---------|
| 언어/프레임워크 규칙 | **project** | "React hooks 사용", "Django REST 패턴 따르기" |
| 파일 구조 선호도 | **project** | "`__tests__`/에 테스트", "src/components/에 컴포넌트" |
| 코드 스타일 | **project** | "함수형 스타일 사용", "dataclasses 선호" |
| 에러 처리 전략 | **project** | "에러에 Result 타입 사용" |
| 보안 관행 | **global** | "사용자 입력 유효성 검사", "SQL 새니타이징" |
| 일반 모범 사례 | **global** | "테스트 먼저 작성", "항상 에러 처리" |
| 도구 워크플로우 선호도 | **global** | "편집 전 Grep", "쓰기 전 Read" |
| Git 관행 | **global** | "Conventional commits", "작고 집중된 커밋" |
## 본능 승격 (프로젝트 -> 전역)
같은 본능이 높은 신뢰도로 여러 프로젝트에 나타나면, 전역 범위로 승격할 후보가 됩니다.
**자동 승격 기준:**
- 2개 이상 프로젝트에서 같은 본능 ID
- 평균 신뢰도 >= 0.8
**승격 방법:**
```bash
# Promote a specific instinct
python3 instinct-cli.py promote prefer-explicit-errors
# Auto-promote all qualifying instincts
python3 instinct-cli.py promote
# Preview without changes
python3 instinct-cli.py promote --dry-run
```
`/evolve` 명령어도 승격 후보를 제안합니다.
## 신뢰도 점수
신뢰도는 시간이 지남에 따라 진화합니다:
| 점수 | 의미 | 동작 |
|-------|---------|----------|
| 0.3 | 잠정적 | 제안되지만 강제되지 않음 |
| 0.5 | 보통 | 관련 시 적용 |
| 0.7 | 강함 | 적용이 자동 승인됨 |
| 0.9 | 거의 확실 | 핵심 행동 |
**신뢰도가 증가하는 경우:**
- 패턴이 반복적으로 관찰됨
- 사용자가 제안된 행동을 수정하지 않음
- 다른 소스의 유사한 본능이 동의함
**신뢰도가 감소하는 경우:**
- 사용자가 행동을 명시적으로 수정함
- 패턴이 오랜 기간 관찰되지 않음
- 모순되는 증거가 나타남
## 왜 관찰에 스킬이 아닌 훅을 사용하나요?
> "v1은 관찰에 스킬을 의존했습니다. 스킬은 확률적입니다 -- Claude의 판단에 따라 약 50-80%의 확률로 실행됩니다."
훅은 **100% 확률로** 결정적으로 실행됩니다. 이는 다음을 의미합니다:
- 모든 도구 호출이 관찰됨
- 패턴이 누락되지 않음
- 학습이 포괄적임
## 하위 호환성
v2.1은 v2.0 및 v1과 완전히 호환됩니다:
- `~/.claude/homunculus/instincts/`의 기존 전역 본능이 전역 본능으로 계속 작동
- v1의 기존 `~/.claude/skills/learned/` 스킬이 계속 작동
- Stop 훅이 여전히 실행됨 (하지만 이제 v2에도 데이터를 공급)
- 점진적 마이그레이션: 둘 다 병렬로 실행 가능
## 개인정보 보호
- 관찰 결과는 사용자의 머신에 **로컬**로 유지
- 프로젝트 범위 본능은 프로젝트별로 격리됨
- **본능**(패턴)만 내보낼 수 있음 -- 원시 관찰 결과는 아님
- 실제 코드나 대화 내용은 공유되지 않음
- 내보내기와 승격 대상을 사용자가 제어
## 관련 자료
- [Skill Creator](https://skill-creator.app) - 저장소 히스토리에서 본능 생성
- Homunculus - v2 본능 기반 아키텍처에 영감을 준 커뮤니티 프로젝트 (원자적 관찰, 신뢰도 점수, 본능 진화 파이프라인)
- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 지속적 학습 섹션
---
*본능 기반 학습: Claude에게 당신의 패턴을 가르치기, 한 번에 하나의 프로젝트씩.*

View File

@@ -0,0 +1,148 @@
---
name: continuous-learning
description: Claude Code 세션에서 재사용 가능한 패턴을 자동으로 추출하여 향후 사용을 위한 학습된 스킬로 저장합니다.
origin: ECC
---
# 지속적 학습 스킬
Claude Code 세션 종료 시 자동으로 평가하여 학습된 스킬로 저장할 수 있는 재사용 가능한 패턴을 추출합니다.
## 활성화 시점
- Claude Code 세션에서 자동 패턴 추출을 설정할 때
- 세션 평가를 위한 Stop Hook을 구성할 때
- `~/.claude/skills/learned/`에서 학습된 스킬을 검토하거나 큐레이션할 때
- 추출 임계값이나 패턴 카테고리를 조정할 때
- v1 (이 방식)과 v2 (본능 기반) 접근법을 비교할 때
## 작동 방식
이 스킬은 각 세션 종료 시 **Stop Hook**으로 실행됩니다:
1. **세션 평가**: 세션에 충분한 메시지가 있는지 확인 (기본값: 10개 이상)
2. **패턴 감지**: 세션에서 추출 가능한 패턴을 식별
3. **스킬 추출**: 유용한 패턴을 `~/.claude/skills/learned/`에 저장
## 구성
`config.json`을 편집하여 사용자 지정합니다:
```json
{
"min_session_length": 10,
"extraction_threshold": "medium",
"auto_approve": false,
"learned_skills_path": "~/.claude/skills/learned/",
"patterns_to_detect": [
"error_resolution",
"user_corrections",
"workarounds",
"debugging_techniques",
"project_specific"
],
"ignore_patterns": [
"simple_typos",
"one_time_fixes",
"external_api_issues"
]
}
```
## 패턴 유형
| 패턴 | 설명 |
|---------|-------------|
| `error_resolution` | 특정 에러가 어떻게 해결되었는지 |
| `user_corrections` | 사용자 수정으로부터의 패턴 |
| `workarounds` | 프레임워크/라이브러리 특이점에 대한 해결책 |
| `debugging_techniques` | 효과적인 디버깅 접근법 |
| `project_specific` | 프로젝트 고유 컨벤션 |
## Hook 설정
`~/.claude/settings.json`에 추가합니다:
```json
{
"hooks": {
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "~/.claude/skills/continuous-learning/evaluate-session.sh"
}]
}]
}
}
```
## 예시
### 자동 패턴 추출 설정 예시
```json
{
"min_session_length": 10,
"extraction_threshold": "medium",
"auto_approve": false,
"learned_skills_path": "~/.claude/skills/learned/"
}
```
### Stop Hook 연결 예시
```json
{
"hooks": {
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "~/.claude/skills/continuous-learning/evaluate-session.sh"
}]
}]
}
}
```
## Stop Hook을 사용하는 이유
- **경량**: 세션 종료 시 한 번만 실행
- **비차단**: 모든 메시지에 지연을 추가하지 않음
- **완전한 컨텍스트**: 전체 세션 트랜스크립트에 접근 가능
## 관련 항목
- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 지속적 학습 섹션
- `/learn` 명령어 - 세션 중 수동 패턴 추출
---
## 비교 노트 (연구: 2025년 1월)
### vs Homunculus
Homunculus v2는 더 정교한 접근법을 취합니다:
| 기능 | 우리의 접근법 | Homunculus v2 |
|---------|--------------|---------------|
| 관찰 | Stop Hook (세션 종료 시) | PreToolUse/PostToolUse Hook (100% 신뢰) |
| 분석 | 메인 컨텍스트 | 백그라운드 에이전트 (Haiku) |
| 세분성 | 완전한 스킬 | 원자적 "본능" |
| 신뢰도 | 없음 | 0.3-0.9 가중치 |
| 진화 | 스킬로 직접 | 본능 -> 클러스터 -> 스킬/명령어/에이전트 |
| 공유 | 없음 | 본능 내보내기/가져오기 |
**Homunculus의 핵심 통찰:**
> "v1은 관찰을 스킬에 의존했습니다. 스킬은 확률적이어서 약 50-80%의 확률로 실행됩니다. v2는 관찰에 Hook(100% 신뢰)을 사용하고 본능을 학습된 행동의 원자 단위로 사용합니다."
### 잠재적 v2 개선 사항
1. **본능 기반 학습** - 신뢰도 점수가 있는 더 작고 원자적인 행동
2. **백그라운드 관찰자** - 병렬로 분석하는 Haiku 에이전트
3. **신뢰도 감쇠** - 반박 시 본능의 신뢰도 감소
4. **도메인 태깅** - code-style, testing, git, debugging 등
5. **진화 경로** - 관련 본능을 스킬/명령어로 클러스터링
자세한 사양은 [`continuous-learning-v2-spec.md`](../../../continuous-learning-v2-spec.md)를 참조하세요.

View File

@@ -0,0 +1,270 @@
---
name: eval-harness
description: 평가 주도 개발(EDD) 원칙을 구현하는 Claude Code 세션용 공식 평가 프레임워크
origin: ECC
tools: Read, Write, Edit, Bash, Grep, Glob
---
# 평가 하네스 스킬
Claude Code 세션을 위한 공식 평가 프레임워크로, 평가 주도 개발(EDD) 원칙을 구현합니다.
## 활성화 시점
- AI 지원 워크플로우에 평가 주도 개발(EDD) 설정 시
- Claude Code 작업 완료에 대한 합격/불합격 기준 정의 시
- pass@k 메트릭으로 에이전트 신뢰성 측정 시
- 프롬프트 또는 에이전트 변경에 대한 회귀 테스트 스위트 생성 시
- 모델 버전 간 에이전트 성능 벤치마킹 시
## 철학
평가 주도 개발은 평가를 "AI 개발의 단위 테스트"로 취급합니다:
- 구현 전에 예상 동작 정의
- 개발 중 지속적으로 평가 실행
- 각 변경 시 회귀 추적
- 신뢰성 측정을 위해 pass@k 메트릭 사용
## 평가 유형
### 기능 평가
Claude가 이전에 할 수 없었던 것을 할 수 있는지 테스트:
```markdown
[CAPABILITY EVAL: feature-name]
Task: Description of what Claude should accomplish
Success Criteria:
- [ ] Criterion 1
- [ ] Criterion 2
- [ ] Criterion 3
Expected Output: Description of expected result
```
### 회귀 평가
변경 사항이 기존 기능을 손상시키지 않는지 확인:
```markdown
[REGRESSION EVAL: feature-name]
Baseline: SHA or checkpoint name
Tests:
- existing-test-1: PASS/FAIL
- existing-test-2: PASS/FAIL
- existing-test-3: PASS/FAIL
Result: X/Y passed (previously Y/Y)
```
## 채점자 유형
### 1. 코드 기반 채점자
코드를 사용한 결정론적 검사:
```bash
# Check if file contains expected pattern
grep -q "export function handleAuth" src/auth.ts && echo "PASS" || echo "FAIL"
# Check if tests pass
npm test -- --testPathPattern="auth" && echo "PASS" || echo "FAIL"
# Check if build succeeds
npm run build && echo "PASS" || echo "FAIL"
```
### 2. 모델 기반 채점자
Claude를 사용하여 개방형 출력 평가:
```markdown
[MODEL GRADER PROMPT]
Evaluate the following code change:
1. Does it solve the stated problem?
2. Is it well-structured?
3. Are edge cases handled?
4. Is error handling appropriate?
Score: 1-5 (1=poor, 5=excellent)
Reasoning: [explanation]
```
### 3. 사람 채점자
수동 검토 플래그:
```markdown
[HUMAN REVIEW REQUIRED]
Change: Description of what changed
Reason: Why human review is needed
Risk Level: LOW/MEDIUM/HIGH
```
## 메트릭
### pass@k
"k번 시도 중 최소 한 번 성공"
- pass@1: 첫 번째 시도 성공률
- pass@3: 3번 시도 내 성공
- 일반적인 목표: pass@3 > 90%
### pass^k
"k번 시행 모두 성공"
- 신뢰성에 대한 더 높은 기준
- pass^3: 3회 연속 성공
- 핵심 경로에 사용
## 평가 워크플로우
### 1. 정의 (코딩 전)
```markdown
## EVAL DEFINITION: feature-xyz
### Capability Evals
1. Can create new user account
2. Can validate email format
3. Can hash password securely
### Regression Evals
1. Existing login still works
2. Session management unchanged
3. Logout flow intact
### Success Metrics
- pass@3 > 90% for capability evals
- pass^3 = 100% for regression evals
```
### 2. 구현
정의된 평가를 통과하기 위한 코드 작성.
### 3. 평가
```bash
# Run capability evals
[Run each capability eval, record PASS/FAIL]
# Run regression evals
npm test -- --testPathPattern="existing"
# Generate report
```
### 4. 보고서
```markdown
EVAL REPORT: feature-xyz
========================
Capability Evals:
create-user: PASS (pass@1)
validate-email: PASS (pass@2)
hash-password: PASS (pass@1)
Overall: 3/3 passed
Regression Evals:
login-flow: PASS
session-mgmt: PASS
logout-flow: PASS
Overall: 3/3 passed
Metrics:
pass@1: 67% (2/3)
pass@3: 100% (3/3)
Status: READY FOR REVIEW
```
## 통합 패턴
### 구현 전
```
/eval define feature-name
```
`.claude/evals/feature-name.md`에 평가 정의 파일 생성
### 구현 중
```
/eval check feature-name
```
현재 평가를 실행하고 상태 보고
### 구현 후
```
/eval report feature-name
```
전체 평가 보고서 생성
## 평가 저장소
프로젝트에 평가 저장:
```
.claude/
evals/
feature-xyz.md # 평가 정의
feature-xyz.log # 평가 실행 이력
baseline.json # 회귀 베이스라인
```
## 모범 사례
1. **코딩 전에 평가 정의** - 성공 기준에 대한 명확한 사고를 강제
2. **자주 평가 실행** - 회귀를 조기에 포착
3. **시간에 따른 pass@k 추적** - 신뢰성 추세 모니터링
4. **가능하면 코드 채점자 사용** - 결정론적 > 확률적
5. **보안에는 사람 검토** - 보안 검사를 완전히 자동화하지 말 것
6. **평가를 빠르게 유지** - 느린 평가는 실행되지 않음
7. **코드와 함께 평가 버전 관리** - 평가는 일급 산출물
## 예시: 인증 추가
```markdown
## EVAL: add-authentication
### Phase 1: 정의 (10분)
Capability Evals:
- [ ] User can register with email/password
- [ ] User can login with valid credentials
- [ ] Invalid credentials rejected with proper error
- [ ] Sessions persist across page reloads
- [ ] Logout clears session
Regression Evals:
- [ ] Public routes still accessible
- [ ] API responses unchanged
- [ ] Database schema compatible
### Phase 2: 구현 (가변)
[Write code]
### Phase 3: 평가
Run: /eval check add-authentication
### Phase 4: 보고서
EVAL REPORT: add-authentication
==============================
Capability: 5/5 passed (pass@3: 100%)
Regression: 3/3 passed (pass^3: 100%)
Status: SHIP IT
```
## 제품 평가 (v1.8)
행동 품질을 단위 테스트만으로 포착할 수 없을 때 제품 평가를 사용하세요.
### 채점자 유형
1. 코드 채점자 (결정론적 어서션)
2. 규칙 채점자 (정규식/스키마 제약 조건)
3. 모델 채점자 (LLM 심사위원 루브릭)
4. 사람 채점자 (모호한 출력에 대한 수동 판정)
### pass@k 가이드
- `pass@1`: 직접 신뢰성
- `pass@3`: 제어된 재시도 하에서의 실용적 신뢰성
- `pass^3`: 안정성 테스트 (3회 모두 통과해야 함)
권장 임계값:
- 기능 평가: pass@3 >= 0.90
- 회귀 평가: 릴리스 핵심 경로에 pass^3 = 1.00
### 평가 안티패턴
- 알려진 평가 예시에 프롬프트 과적합
- 정상 경로 출력만 측정
- 합격률을 쫓으면서 비용과 지연 시간 변동 무시
- 릴리스 게이트에 불안정한 채점자 허용
### 최소 평가 산출물 레이아웃
- `.claude/evals/<feature>.md` 정의
- `.claude/evals/<feature>.log` 실행 이력
- `docs/releases/<version>/eval-summary.md` 릴리스 스냅샷

View File

@@ -0,0 +1,652 @@
---
name: frontend-patterns
description: React, Next.js, 상태 관리, 성능 최적화 및 UI 모범 사례를 위한 프론트엔드 개발 패턴.
origin: ECC
---
# 프론트엔드 개발 패턴
React, Next.js 및 고성능 사용자 인터페이스를 위한 모던 프론트엔드 패턴.
## 활성화 시점
- React 컴포넌트를 구축할 때 (합성, props, 렌더링)
- 상태를 관리할 때 (useState, useReducer, Zustand, Context)
- 데이터 페칭을 구현할 때 (SWR, React Query, server components)
- 성능을 최적화할 때 (메모이제이션, 가상화, 코드 분할)
- 폼을 다룰 때 (유효성 검사, 제어 입력, Zod 스키마)
- 클라이언트 사이드 라우팅과 네비게이션을 처리할 때
- 접근성 있고 반응형인 UI 패턴을 구축할 때
## 컴포넌트 패턴
### 상속보다 합성
```typescript
// ✅ GOOD: Component composition
interface CardProps {
children: React.ReactNode
variant?: 'default' | 'outlined'
}
export function Card({ children, variant = 'default' }: CardProps) {
return <div className={`card card-${variant}`}>{children}</div>
}
export function CardHeader({ children }: { children: React.ReactNode }) {
return <div className="card-header">{children}</div>
}
export function CardBody({ children }: { children: React.ReactNode }) {
return <div className="card-body">{children}</div>
}
// Usage
<Card>
<CardHeader>Title</CardHeader>
<CardBody>Content</CardBody>
</Card>
```
### Compound Components
```typescript
interface TabsContextValue {
activeTab: string
setActiveTab: (tab: string) => void
}
const TabsContext = createContext<TabsContextValue | undefined>(undefined)
export function Tabs({ children, defaultTab }: {
children: React.ReactNode
defaultTab: string
}) {
const [activeTab, setActiveTab] = useState(defaultTab)
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
)
}
export function TabList({ children }: { children: React.ReactNode }) {
return <div className="tab-list">{children}</div>
}
export function Tab({ id, children }: { id: string, children: React.ReactNode }) {
const context = useContext(TabsContext)
if (!context) throw new Error('Tab must be used within Tabs')
return (
<button
className={context.activeTab === id ? 'active' : ''}
onClick={() => context.setActiveTab(id)}
>
{children}
</button>
)
}
// Usage
<Tabs defaultTab="overview">
<TabList>
<Tab id="overview">Overview</Tab>
<Tab id="details">Details</Tab>
</TabList>
</Tabs>
```
### Render Props 패턴
```typescript
interface DataLoaderProps<T> {
url: string
children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode
}
export function DataLoader<T>({ url, children }: DataLoaderProps<T>) {
const [data, setData] = useState<T | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false))
}, [url])
return <>{children(data, loading, error)}</>
}
// Usage
<DataLoader<Market[]> url="/api/markets">
{(markets, loading, error) => {
if (loading) return <Spinner />
if (error) return <Error error={error} />
return <MarketList markets={markets!} />
}}
</DataLoader>
```
## 커스텀 Hook 패턴
### 상태 관리 Hook
```typescript
export function useToggle(initialValue = false): [boolean, () => void] {
const [value, setValue] = useState(initialValue)
const toggle = useCallback(() => {
setValue(v => !v)
}, [])
return [value, toggle]
}
// Usage
const [isOpen, toggleOpen] = useToggle()
```
### 비동기 데이터 페칭 Hook
```typescript
import { useCallback, useEffect, useRef, useState } from 'react'
interface UseQueryOptions<T> {
onSuccess?: (data: T) => void
onError?: (error: Error) => void
enabled?: boolean
}
export function useQuery<T>(
key: string,
fetcher: () => Promise<T>,
options?: UseQueryOptions<T>
) {
const [data, setData] = useState<T | null>(null)
const [error, setError] = useState<Error | null>(null)
const [loading, setLoading] = useState(false)
const successRef = useRef(options?.onSuccess)
const errorRef = useRef(options?.onError)
const enabled = options?.enabled !== false
useEffect(() => {
successRef.current = options?.onSuccess
errorRef.current = options?.onError
}, [options?.onSuccess, options?.onError])
const refetch = useCallback(async () => {
setLoading(true)
setError(null)
try {
const result = await fetcher()
setData(result)
successRef.current?.(result)
} catch (err) {
const error = err as Error
setError(error)
errorRef.current?.(error)
} finally {
setLoading(false)
}
}, [fetcher])
useEffect(() => {
if (enabled) {
refetch()
}
}, [key, enabled, refetch])
return { data, error, loading, refetch }
}
// Usage
const { data: markets, loading, error, refetch } = useQuery(
'markets',
() => fetch('/api/markets').then(r => r.json()),
{
onSuccess: data => console.log('Fetched', data.length, 'markets'),
onError: err => console.error('Failed:', err)
}
)
```
### Debounce Hook
```typescript
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => clearTimeout(handler)
}, [value, delay])
return debouncedValue
}
// Usage
const [searchQuery, setSearchQuery] = useState('')
const debouncedQuery = useDebounce(searchQuery, 500)
useEffect(() => {
if (debouncedQuery) {
performSearch(debouncedQuery)
}
}, [debouncedQuery])
```
## 상태 관리 패턴
### Context + Reducer 패턴
```typescript
interface State {
markets: Market[]
selectedMarket: Market | null
loading: boolean
}
type Action =
| { type: 'SET_MARKETS'; payload: Market[] }
| { type: 'SELECT_MARKET'; payload: Market }
| { type: 'SET_LOADING'; payload: boolean }
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'SET_MARKETS':
return { ...state, markets: action.payload }
case 'SELECT_MARKET':
return { ...state, selectedMarket: action.payload }
case 'SET_LOADING':
return { ...state, loading: action.payload }
default:
return state
}
}
const MarketContext = createContext<{
state: State
dispatch: Dispatch<Action>
} | undefined>(undefined)
export function MarketProvider({ children }: { children: React.ReactNode }) {
const [state, dispatch] = useReducer(reducer, {
markets: [],
selectedMarket: null,
loading: false
})
return (
<MarketContext.Provider value={{ state, dispatch }}>
{children}
</MarketContext.Provider>
)
}
export function useMarkets() {
const context = useContext(MarketContext)
if (!context) throw new Error('useMarkets must be used within MarketProvider')
return context
}
```
## 성능 최적화
### 메모이제이션
```typescript
// ✅ useMemo for expensive computations
const sortedMarkets = useMemo(() => {
return [...markets].sort((a, b) => b.volume - a.volume)
}, [markets])
// ✅ useCallback for functions passed to children
const handleSearch = useCallback((query: string) => {
setSearchQuery(query)
}, [])
// ✅ React.memo for pure components
export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
return (
<div className="market-card">
<h3>{market.name}</h3>
<p>{market.description}</p>
</div>
)
})
```
### 코드 분할 및 지연 로딩
```typescript
import { lazy, Suspense } from 'react'
// ✅ Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'))
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
export function Dashboard() {
return (
<div>
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={data} />
</Suspense>
<Suspense fallback={null}>
<ThreeJsBackground />
</Suspense>
</div>
)
}
```
### 긴 리스트를 위한 가상화
```typescript
import { useVirtualizer } from '@tanstack/react-virtual'
export function VirtualMarketList({ markets }: { markets: Market[] }) {
const parentRef = useRef<HTMLDivElement>(null)
const virtualizer = useVirtualizer({
count: markets.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 100, // Estimated row height
overscan: 5 // Extra items to render
})
return (
<div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
<div
style={{
height: `${virtualizer.getTotalSize()}px`,
position: 'relative'
}}
>
{virtualizer.getVirtualItems().map(virtualRow => (
<div
key={virtualRow.index}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start}px)`
}}
>
<MarketCard market={markets[virtualRow.index]} />
</div>
))}
</div>
</div>
)
}
```
## 폼 처리 패턴
### 유효성 검사가 포함된 제어 폼
```typescript
interface FormData {
name: string
description: string
endDate: string
}
interface FormErrors {
name?: string
description?: string
endDate?: string
}
export function CreateMarketForm() {
const [formData, setFormData] = useState<FormData>({
name: '',
description: '',
endDate: ''
})
const [errors, setErrors] = useState<FormErrors>({})
const validate = (): boolean => {
const newErrors: FormErrors = {}
if (!formData.name.trim()) {
newErrors.name = 'Name is required'
} else if (formData.name.length > 200) {
newErrors.name = 'Name must be under 200 characters'
}
if (!formData.description.trim()) {
newErrors.description = 'Description is required'
}
if (!formData.endDate) {
newErrors.endDate = 'End date is required'
}
setErrors(newErrors)
return Object.keys(newErrors).length === 0
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!validate()) return
try {
await createMarket(formData)
// Success handling
} catch (error) {
// Error handling
}
}
return (
<form onSubmit={handleSubmit}>
<input
value={formData.name}
onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
placeholder="Market name"
/>
{errors.name && <span className="error">{errors.name}</span>}
{/* Other fields */}
<button type="submit">Create Market</button>
</form>
)
}
```
## Error Boundary 패턴
```typescript
interface ErrorBoundaryState {
hasError: boolean
error: Error | null
}
export class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
ErrorBoundaryState
> {
state: ErrorBoundaryState = {
hasError: false,
error: null
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error('Error boundary caught:', error, errorInfo)
}
render() {
if (this.state.hasError) {
return (
<div className="error-fallback">
<h2>Something went wrong</h2>
<p>{this.state.error?.message}</p>
<button onClick={() => this.setState({ hasError: false })}>
Try again
</button>
</div>
)
}
return this.props.children
}
}
// Usage
<ErrorBoundary>
<App />
</ErrorBoundary>
```
## 애니메이션 패턴
### Framer Motion 애니메이션
```typescript
import { motion, AnimatePresence } from 'framer-motion'
// ✅ List animations
export function AnimatedMarketList({ markets }: { markets: Market[] }) {
return (
<AnimatePresence>
{markets.map(market => (
<motion.div
key={market.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
<MarketCard market={market} />
</motion.div>
))}
</AnimatePresence>
)
}
// ✅ Modal animations
export function Modal({ isOpen, onClose, children }: ModalProps) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
className="modal-overlay"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
/>
<motion.div
className="modal-content"
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
>
{children}
</motion.div>
</>
)}
</AnimatePresence>
)
}
```
## 접근성 패턴
### 키보드 네비게이션
```typescript
export function Dropdown({ options, onSelect }: DropdownProps) {
const [isOpen, setIsOpen] = useState(false)
const [activeIndex, setActiveIndex] = useState(0)
const handleKeyDown = (e: React.KeyboardEvent) => {
switch (e.key) {
case 'ArrowDown':
e.preventDefault()
setActiveIndex(i => Math.min(i + 1, options.length - 1))
break
case 'ArrowUp':
e.preventDefault()
setActiveIndex(i => Math.max(i - 1, 0))
break
case 'Enter':
e.preventDefault()
onSelect(options[activeIndex])
setIsOpen(false)
break
case 'Escape':
setIsOpen(false)
break
}
}
return (
<div
role="combobox"
aria-expanded={isOpen}
aria-haspopup="listbox"
onKeyDown={handleKeyDown}
>
{/* Dropdown implementation */}
</div>
)
}
```
### 포커스 관리
```typescript
export function Modal({ isOpen, onClose, children }: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null)
const previousFocusRef = useRef<HTMLElement | null>(null)
useEffect(() => {
if (isOpen) {
// Save currently focused element
previousFocusRef.current = document.activeElement as HTMLElement
// Focus modal
modalRef.current?.focus()
} else {
// Restore focus when closing
previousFocusRef.current?.focus()
}
}, [isOpen])
return isOpen ? (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
tabIndex={-1}
onKeyDown={e => e.key === 'Escape' && onClose()}
>
{children}
</div>
) : null
}
```
**기억하세요**: 모던 프론트엔드 패턴은 유지보수 가능하고 고성능인 사용자 인터페이스를 가능하게 합니다. 프로젝트 복잡도에 맞는 패턴을 선택하세요.

View File

@@ -0,0 +1,675 @@
---
name: golang-patterns
description: 견고하고 효율적이며 유지보수 가능한 Go 애플리케이션 구축을 위한 관용적 Go 패턴, 모범 사례 및 규칙.
origin: ECC
---
# Go 개발 패턴
견고하고 효율적이며 유지보수 가능한 애플리케이션 구축을 위한 관용적 Go 패턴과 모범 사례.
## 활성화 시점
- 새로운 Go 코드 작성 시
- Go 코드 리뷰 시
- 기존 Go 코드 리팩토링 시
- Go 패키지/모듈 설계 시
## 핵심 원칙
### 1. 단순성과 명확성
Go는 영리함보다 단순성을 선호합니다. 코드는 명확하고 읽기 쉬워야 합니다.
```go
// Good: Clear and direct
func GetUser(id string) (*User, error) {
user, err := db.FindUser(id)
if err != nil {
return nil, fmt.Errorf("get user %s: %w", id, err)
}
return user, nil
}
// Bad: Overly clever
func GetUser(id string) (*User, error) {
return func() (*User, error) {
if u, e := db.FindUser(id); e == nil {
return u, nil
} else {
return nil, e
}
}()
}
```
### 2. 제로 값을 유용하게 만들기
제로 값이 초기화 없이 즉시 사용 가능하도록 타입을 설계하세요.
```go
// Good: Zero value is useful
type Counter struct {
mu sync.Mutex
count int // zero value is 0, ready to use
}
func (c *Counter) Inc() {
c.mu.Lock()
c.count++
c.mu.Unlock()
}
// Good: bytes.Buffer works with zero value
var buf bytes.Buffer
buf.WriteString("hello")
// Bad: Requires initialization
type BadCounter struct {
counts map[string]int // nil map will panic
}
```
### 3. 인터페이스를 받고 구조체를 반환하기
함수는 인터페이스 매개변수를 받고 구체적 타입을 반환해야 합니다.
```go
// Good: Accepts interface, returns concrete type
func ProcessData(r io.Reader) (*Result, error) {
data, err := io.ReadAll(r)
if err != nil {
return nil, err
}
return &Result{Data: data}, nil
}
// Bad: Returns interface (hides implementation details unnecessarily)
func ProcessData(r io.Reader) (io.Reader, error) {
// ...
}
```
## 에러 처리 패턴
### 컨텍스트가 있는 에러 래핑
```go
// Good: Wrap errors with context
func LoadConfig(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("load config %s: %w", path, err)
}
var cfg Config
if err := json.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse config %s: %w", path, err)
}
return &cfg, nil
}
```
### 커스텀 에러 타입
```go
// Define domain-specific errors
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
// Sentinel errors for common cases
var (
ErrNotFound = errors.New("resource not found")
ErrUnauthorized = errors.New("unauthorized")
ErrInvalidInput = errors.New("invalid input")
)
```
### errors.Is와 errors.As를 사용한 에러 확인
```go
func HandleError(err error) {
// Check for specific error
if errors.Is(err, sql.ErrNoRows) {
log.Println("No records found")
return
}
// Check for error type
var validationErr *ValidationError
if errors.As(err, &validationErr) {
log.Printf("Validation error on field %s: %s",
validationErr.Field, validationErr.Message)
return
}
// Unknown error
log.Printf("Unexpected error: %v", err)
}
```
### 에러를 절대 무시하지 말 것
```go
// Bad: Ignoring error with blank identifier
result, _ := doSomething()
// Good: Handle or explicitly document why it's safe to ignore
result, err := doSomething()
if err != nil {
return err
}
// Acceptable: When error truly doesn't matter (rare)
_ = writer.Close() // Best-effort cleanup, error logged elsewhere
```
## 동시성 패턴
### 워커 풀
```go
func WorkerPool(jobs <-chan Job, results chan<- Result, numWorkers int) {
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
results <- process(job)
}
}()
}
wg.Wait()
close(results)
}
```
### 취소 및 타임아웃을 위한 Context
```go
func FetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("fetch %s: %w", url, err)
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
```
### 우아한 종료
```go
func GracefulShutdown(server *http.Server) {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("Server exited")
}
```
### 조율된 고루틴을 위한 errgroup
```go
import "golang.org/x/sync/errgroup"
func FetchAll(ctx context.Context, urls []string) ([][]byte, error) {
g, ctx := errgroup.WithContext(ctx)
results := make([][]byte, len(urls))
for i, url := range urls {
i, url := i, url // Capture loop variables
g.Go(func() error {
data, err := FetchWithTimeout(ctx, url)
if err != nil {
return err
}
results[i] = data
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return results, nil
}
```
### 고루틴 누수 방지
```go
// Bad: Goroutine leak if context is cancelled
func leakyFetch(ctx context.Context, url string) <-chan []byte {
ch := make(chan []byte)
go func() {
data, _ := fetch(url)
ch <- data // Blocks forever if no receiver
}()
return ch
}
// Good: Properly handles cancellation
func safeFetch(ctx context.Context, url string) <-chan []byte {
ch := make(chan []byte, 1) // Buffered channel
go func() {
data, err := fetch(url)
if err != nil {
return
}
select {
case ch <- data:
case <-ctx.Done():
}
}()
return ch
}
```
## 인터페이스 설계
### 작고 집중된 인터페이스
```go
// Good: Single-method interfaces
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
// Compose interfaces as needed
type ReadWriteCloser interface {
Reader
Writer
Closer
}
```
### 사용되는 곳에서 인터페이스 정의
```go
// In the consumer package, not the provider
package service
// UserStore defines what this service needs
type UserStore interface {
GetUser(id string) (*User, error)
SaveUser(user *User) error
}
type Service struct {
store UserStore
}
// Concrete implementation can be in another package
// It doesn't need to know about this interface
```
### 타입 어서션을 통한 선택적 동작
```go
type Flusher interface {
Flush() error
}
func WriteAndFlush(w io.Writer, data []byte) error {
if _, err := w.Write(data); err != nil {
return err
}
// Flush if supported
if f, ok := w.(Flusher); ok {
return f.Flush()
}
return nil
}
```
## 패키지 구성
### 표준 프로젝트 레이아웃
```text
myproject/
├── cmd/
│ └── myapp/
│ └── main.go # Entry point
├── internal/
│ ├── handler/ # HTTP handlers
│ ├── service/ # Business logic
│ ├── repository/ # Data access
│ └── config/ # Configuration
├── pkg/
│ └── client/ # Public API client
├── api/
│ └── v1/ # API definitions (proto, OpenAPI)
├── testdata/ # Test fixtures
├── go.mod
├── go.sum
└── Makefile
```
### 패키지 명명
```go
// Good: Short, lowercase, no underscores
package http
package json
package user
// Bad: Verbose, mixed case, or redundant
package httpHandler
package json_parser
package userService // Redundant 'Service' suffix
```
### 패키지 수준 상태 피하기
```go
// Bad: Global mutable state
var db *sql.DB
func init() {
db, _ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
}
// Good: Dependency injection
type Server struct {
db *sql.DB
}
func NewServer(db *sql.DB) *Server {
return &Server{db: db}
}
```
## 구조체 설계
### 함수형 옵션 패턴
```go
type Server struct {
addr string
timeout time.Duration
logger *log.Logger
}
type Option func(*Server)
func WithTimeout(d time.Duration) Option {
return func(s *Server) {
s.timeout = d
}
}
func WithLogger(l *log.Logger) Option {
return func(s *Server) {
s.logger = l
}
}
func NewServer(addr string, opts ...Option) *Server {
s := &Server{
addr: addr,
timeout: 30 * time.Second, // default
logger: log.Default(), // default
}
for _, opt := range opts {
opt(s)
}
return s
}
// Usage
server := NewServer(":8080",
WithTimeout(60*time.Second),
WithLogger(customLogger),
)
```
### 합성을 위한 임베딩
```go
type Logger struct {
prefix string
}
func (l *Logger) Log(msg string) {
fmt.Printf("[%s] %s\n", l.prefix, msg)
}
type Server struct {
*Logger // Embedding - Server gets Log method
addr string
}
func NewServer(addr string) *Server {
return &Server{
Logger: &Logger{prefix: "SERVER"},
addr: addr,
}
}
// Usage
s := NewServer(":8080")
s.Log("Starting...") // Calls embedded Logger.Log
```
## 메모리 및 성능
### 크기를 알 때 슬라이스 미리 할당
```go
// Bad: Grows slice multiple times
func processItems(items []Item) []Result {
var results []Result
for _, item := range items {
results = append(results, process(item))
}
return results
}
// Good: Single allocation
func processItems(items []Item) []Result {
results := make([]Result, 0, len(items))
for _, item := range items {
results = append(results, process(item))
}
return results
}
```
### 빈번한 할당에 sync.Pool 사용
```go
var bufferPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func ProcessRequest(data []byte) []byte {
buf := bufferPool.Get().(*bytes.Buffer)
defer func() {
buf.Reset()
bufferPool.Put(buf)
}()
buf.Write(data)
// Process...
out := append([]byte(nil), buf.Bytes()...)
return out
}
```
### 루프에서 문자열 연결 피하기
```go
// Bad: Creates many string allocations
func join(parts []string) string {
var result string
for _, p := range parts {
result += p + ","
}
return result
}
// Good: Single allocation with strings.Builder
func join(parts []string) string {
var sb strings.Builder
for i, p := range parts {
if i > 0 {
sb.WriteString(",")
}
sb.WriteString(p)
}
return sb.String()
}
// Best: Use standard library
func join(parts []string) string {
return strings.Join(parts, ",")
}
```
## Go 도구 통합
### 필수 명령어
```bash
# Build and run
go build ./...
go run ./cmd/myapp
# Testing
go test ./...
go test -race ./...
go test -cover ./...
# Static analysis
go vet ./...
staticcheck ./...
golangci-lint run
# Module management
go mod tidy
go mod verify
# Formatting
gofmt -w .
goimports -w .
```
### 권장 린터 구성 (.golangci.yml)
```yaml
linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
- misspell
- unconvert
- unparam
linters-settings:
errcheck:
check-type-assertions: true
govet:
check-shadowing: true
issues:
exclude-use-default: false
```
## 빠른 참조: Go 관용구
| 관용구 | 설명 |
|-------|-------------|
| Accept interfaces, return structs | 함수는 인터페이스 매개변수를 받고 구체적 타입을 반환 |
| Errors are values | 에러를 예외가 아닌 일급 값으로 취급 |
| Don't communicate by sharing memory | 고루틴 간 조율에 채널 사용 |
| Make the zero value useful | 타입이 명시적 초기화 없이 작동해야 함 |
| A little copying is better than a little dependency | 불필요한 외부 의존성 피하기 |
| Clear is better than clever | 영리함보다 가독성 우선 |
| gofmt is no one's favorite but everyone's friend | 항상 gofmt/goimports로 포맷팅 |
| Return early | 에러를 먼저 처리하고 정상 경로는 들여쓰기 없이 유지 |
## 피해야 할 안티패턴
```go
// Bad: Naked returns in long functions
func process() (result int, err error) {
// ... 50 lines ...
return // What is being returned?
}
// Bad: Using panic for control flow
func GetUser(id string) *User {
user, err := db.Find(id)
if err != nil {
panic(err) // Don't do this
}
return user
}
// Bad: Passing context in struct
type Request struct {
ctx context.Context // Context should be first param
ID string
}
// Good: Context as first parameter
func ProcessRequest(ctx context.Context, id string) error {
// ...
}
// Bad: Mixing value and pointer receivers
type Counter struct{ n int }
func (c Counter) Value() int { return c.n } // Value receiver
func (c *Counter) Increment() { c.n++ } // Pointer receiver
// Pick one style and be consistent
```
**기억하세요**: Go 코드는 최고의 의미에서 지루해야 합니다 - 예측 가능하고, 일관적이며, 이해하기 쉽게. 의심스러울 때는 단순하게 유지하세요.

View File

@@ -0,0 +1,720 @@
---
name: golang-testing
description: 테이블 주도 테스트, 서브테스트, 벤치마크, 퍼징, 테스트 커버리지를 포함한 Go 테스팅 패턴. 관용적 Go 관행과 함께 TDD 방법론을 따릅니다.
origin: ECC
---
# Go 테스팅 패턴
TDD 방법론을 따르는 신뢰할 수 있고 유지보수 가능한 테스트 작성을 위한 포괄적인 Go 테스팅 패턴.
## 활성화 시점
- 새로운 Go 함수나 메서드 작성 시
- 기존 코드에 테스트 커버리지 추가 시
- 성능이 중요한 코드에 벤치마크 생성 시
- 입력 유효성 검사를 위한 퍼즈 테스트 구현 시
- Go 프로젝트에서 TDD 워크플로우 따를 시
## Go에서의 TDD 워크플로우
### RED-GREEN-REFACTOR 사이클
```
RED → Write a failing test first
GREEN → Write minimal code to pass the test
REFACTOR → Improve code while keeping tests green
REPEAT → Continue with next requirement
```
### Go에서의 단계별 TDD
```go
// Step 1: Define the interface/signature
// calculator.go
package calculator
func Add(a, b int) int {
panic("not implemented") // Placeholder
}
// Step 2: Write failing test (RED)
// calculator_test.go
package calculator
import "testing"
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d; want %d", got, want)
}
}
// Step 3: Run test - verify FAIL
// $ go test
// --- FAIL: TestAdd (0.00s)
// panic: not implemented
// Step 4: Implement minimal code (GREEN)
func Add(a, b int) int {
return a + b
}
// Step 5: Run test - verify PASS
// $ go test
// PASS
// Step 6: Refactor if needed, verify tests still pass
```
## 테이블 주도 테스트
Go 테스트의 표준 패턴. 최소한의 코드로 포괄적인 커버리지를 가능하게 합니다.
```go
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive numbers", 2, 3, 5},
{"negative numbers", -1, -2, -3},
{"zero values", 0, 0, 0},
{"mixed signs", -1, 1, 0},
{"large numbers", 1000000, 2000000, 3000000},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Add(tt.a, tt.b)
if got != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d",
tt.a, tt.b, got, tt.expected)
}
})
}
}
```
### 에러 케이스가 있는 테이블 주도 테스트
```go
func TestParseConfig(t *testing.T) {
tests := []struct {
name string
input string
want *Config
wantErr bool
}{
{
name: "valid config",
input: `{"host": "localhost", "port": 8080}`,
want: &Config{Host: "localhost", Port: 8080},
},
{
name: "invalid JSON",
input: `{invalid}`,
wantErr: true,
},
{
name: "empty input",
input: "",
wantErr: true,
},
{
name: "minimal config",
input: `{}`,
want: &Config{}, // Zero value config
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseConfig(tt.input)
if tt.wantErr {
if err == nil {
t.Error("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("got %+v; want %+v", got, tt.want)
}
})
}
}
```
## 서브테스트 및 서브벤치마크
### 관련 테스트 구성
```go
func TestUser(t *testing.T) {
// Setup shared by all subtests
db := setupTestDB(t)
t.Run("Create", func(t *testing.T) {
user := &User{Name: "Alice"}
err := db.CreateUser(user)
if err != nil {
t.Fatalf("CreateUser failed: %v", err)
}
if user.ID == "" {
t.Error("expected user ID to be set")
}
})
t.Run("Get", func(t *testing.T) {
user, err := db.GetUser("alice-id")
if err != nil {
t.Fatalf("GetUser failed: %v", err)
}
if user.Name != "Alice" {
t.Errorf("got name %q; want %q", user.Name, "Alice")
}
})
t.Run("Update", func(t *testing.T) {
// ...
})
t.Run("Delete", func(t *testing.T) {
// ...
})
}
```
### 병렬 서브테스트
```go
func TestParallel(t *testing.T) {
tests := []struct {
name string
input string
}{
{"case1", "input1"},
{"case2", "input2"},
{"case3", "input3"},
}
for _, tt := range tests {
tt := tt // Capture range variable
t.Run(tt.name, func(t *testing.T) {
t.Parallel() // Run subtests in parallel
result := Process(tt.input)
// assertions...
_ = result
})
}
}
```
## 테스트 헬퍼
### 헬퍼 함수
```go
func setupTestDB(t *testing.T) *sql.DB {
t.Helper() // Marks this as a helper function
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
// Cleanup when test finishes
t.Cleanup(func() {
db.Close()
})
// Run migrations
if _, err := db.Exec(schema); err != nil {
t.Fatalf("failed to create schema: %v", err)
}
return db
}
func assertNoError(t *testing.T, err error) {
t.Helper()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
func assertEqual[T comparable](t *testing.T, got, want T) {
t.Helper()
if got != want {
t.Errorf("got %v; want %v", got, want)
}
}
```
### 임시 파일 및 디렉터리
```go
func TestFileProcessing(t *testing.T) {
// Create temp directory - automatically cleaned up
tmpDir := t.TempDir()
// Create test file
testFile := filepath.Join(tmpDir, "test.txt")
err := os.WriteFile(testFile, []byte("test content"), 0644)
if err != nil {
t.Fatalf("failed to create test file: %v", err)
}
// Run test
result, err := ProcessFile(testFile)
if err != nil {
t.Fatalf("ProcessFile failed: %v", err)
}
// Assert...
_ = result
}
```
## 골든 파일
`testdata/`에 저장된 예상 출력 파일에 대한 테스트.
```go
var update = flag.Bool("update", false, "update golden files")
func TestRender(t *testing.T) {
tests := []struct {
name string
input Template
}{
{"simple", Template{Name: "test"}},
{"complex", Template{Name: "test", Items: []string{"a", "b"}}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Render(tt.input)
golden := filepath.Join("testdata", tt.name+".golden")
if *update {
// Update golden file: go test -update
err := os.WriteFile(golden, got, 0644)
if err != nil {
t.Fatalf("failed to update golden file: %v", err)
}
}
want, err := os.ReadFile(golden)
if err != nil {
t.Fatalf("failed to read golden file: %v", err)
}
if !bytes.Equal(got, want) {
t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want)
}
})
}
}
```
## 인터페이스를 사용한 모킹
### 인터페이스 기반 모킹
```go
// Define interface for dependencies
type UserRepository interface {
GetUser(id string) (*User, error)
SaveUser(user *User) error
}
// Production implementation
type PostgresUserRepository struct {
db *sql.DB
}
func (r *PostgresUserRepository) GetUser(id string) (*User, error) {
// Real database query
}
// Mock implementation for tests
type MockUserRepository struct {
GetUserFunc func(id string) (*User, error)
SaveUserFunc func(user *User) error
}
func (m *MockUserRepository) GetUser(id string) (*User, error) {
return m.GetUserFunc(id)
}
func (m *MockUserRepository) SaveUser(user *User) error {
return m.SaveUserFunc(user)
}
// Test using mock
func TestUserService(t *testing.T) {
mock := &MockUserRepository{
GetUserFunc: func(id string) (*User, error) {
if id == "123" {
return &User{ID: "123", Name: "Alice"}, nil
}
return nil, ErrNotFound
},
}
service := NewUserService(mock)
user, err := service.GetUserProfile("123")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if user.Name != "Alice" {
t.Errorf("got name %q; want %q", user.Name, "Alice")
}
}
```
## 벤치마크
### 기본 벤치마크
```go
func BenchmarkProcess(b *testing.B) {
data := generateTestData(1000)
b.ResetTimer() // Don't count setup time
for i := 0; i < b.N; i++ {
Process(data)
}
}
// Run: go test -bench=BenchmarkProcess -benchmem
// Output: BenchmarkProcess-8 10000 105234 ns/op 4096 B/op 10 allocs/op
```
### 다양한 크기의 벤치마크
```go
func BenchmarkSort(b *testing.B) {
sizes := []int{100, 1000, 10000, 100000}
for _, size := range sizes {
b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
data := generateRandomSlice(size)
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Make a copy to avoid sorting already sorted data
tmp := make([]int, len(data))
copy(tmp, data)
sort.Ints(tmp)
}
})
}
}
```
### 메모리 할당 벤치마크
```go
func BenchmarkStringConcat(b *testing.B) {
parts := []string{"hello", "world", "foo", "bar", "baz"}
b.Run("plus", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var s string
for _, p := range parts {
s += p
}
_ = s
}
})
b.Run("builder", func(b *testing.B) {
for i := 0; i < b.N; i++ {
var sb strings.Builder
for _, p := range parts {
sb.WriteString(p)
}
_ = sb.String()
}
})
b.Run("join", func(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = strings.Join(parts, "")
}
})
}
```
## 퍼징 (Go 1.18+)
### 기본 퍼즈 테스트
```go
func FuzzParseJSON(f *testing.F) {
// Add seed corpus
f.Add(`{"name": "test"}`)
f.Add(`{"count": 123}`)
f.Add(`[]`)
f.Add(`""`)
f.Fuzz(func(t *testing.T, input string) {
var result map[string]interface{}
err := json.Unmarshal([]byte(input), &result)
if err != nil {
// Invalid JSON is expected for random input
return
}
// If parsing succeeded, re-encoding should work
_, err = json.Marshal(result)
if err != nil {
t.Errorf("Marshal failed after successful Unmarshal: %v", err)
}
})
}
// Run: go test -fuzz=FuzzParseJSON -fuzztime=30s
```
### 다중 입력 퍼즈 테스트
```go
func FuzzCompare(f *testing.F) {
f.Add("hello", "world")
f.Add("", "")
f.Add("abc", "abc")
f.Fuzz(func(t *testing.T, a, b string) {
result := Compare(a, b)
// Property: Compare(a, a) should always equal 0
if a == b && result != 0 {
t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result)
}
// Property: Compare(a, b) and Compare(b, a) should have opposite signs
reverse := Compare(b, a)
if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) {
if result != 0 || reverse != 0 {
t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inconsistent",
a, b, result, b, a, reverse)
}
}
})
}
```
## 테스트 커버리지
### 커버리지 실행
```bash
# Basic coverage
go test -cover ./...
# Generate coverage profile
go test -coverprofile=coverage.out ./...
# View coverage in browser
go tool cover -html=coverage.out
# View coverage by function
go tool cover -func=coverage.out
# Coverage with race detection
go test -race -coverprofile=coverage.out ./...
```
### 커버리지 목표
| 코드 유형 | 목표 |
|-----------|--------|
| 핵심 비즈니스 로직 | 100% |
| 공개 API | 90%+ |
| 일반 코드 | 80%+ |
| 생성된 코드 | 제외 |
### 생성된 코드를 커버리지에서 제외
```go
//go:generate mockgen -source=interface.go -destination=mock_interface.go
// In coverage profile, exclude with build tags:
// go test -cover -tags=!generate ./...
```
## HTTP 핸들러 테스팅
```go
func TestHealthHandler(t *testing.T) {
// Create request
req := httptest.NewRequest(http.MethodGet, "/health", nil)
w := httptest.NewRecorder()
// Call handler
HealthHandler(w, req)
// Check response
resp := w.Result()
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK)
}
body, _ := io.ReadAll(resp.Body)
if string(body) != "OK" {
t.Errorf("got body %q; want %q", body, "OK")
}
}
func TestAPIHandler(t *testing.T) {
tests := []struct {
name string
method string
path string
body string
wantStatus int
wantBody string
}{
{
name: "get user",
method: http.MethodGet,
path: "/users/123",
wantStatus: http.StatusOK,
wantBody: `{"id":"123","name":"Alice"}`,
},
{
name: "not found",
method: http.MethodGet,
path: "/users/999",
wantStatus: http.StatusNotFound,
},
{
name: "create user",
method: http.MethodPost,
path: "/users",
body: `{"name":"Bob"}`,
wantStatus: http.StatusCreated,
},
}
handler := NewAPIHandler()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var body io.Reader
if tt.body != "" {
body = strings.NewReader(tt.body)
}
req := httptest.NewRequest(tt.method, tt.path, body)
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler.ServeHTTP(w, req)
if w.Code != tt.wantStatus {
t.Errorf("got status %d; want %d", w.Code, tt.wantStatus)
}
if tt.wantBody != "" && w.Body.String() != tt.wantBody {
t.Errorf("got body %q; want %q", w.Body.String(), tt.wantBody)
}
})
}
}
```
## 테스팅 명령어
```bash
# Run all tests
go test ./...
# Run tests with verbose output
go test -v ./...
# Run specific test
go test -run TestAdd ./...
# Run tests matching pattern
go test -run "TestUser/Create" ./...
# Run tests with race detector
go test -race ./...
# Run tests with coverage
go test -cover -coverprofile=coverage.out ./...
# Run short tests only
go test -short ./...
# Run tests with timeout
go test -timeout 30s ./...
# Run benchmarks
go test -bench=. -benchmem ./...
# Run fuzzing
go test -fuzz=FuzzParse -fuzztime=30s ./...
# Count test runs (for flaky test detection)
go test -count=10 ./...
```
## 모범 사례
**해야 할 것:**
- 테스트를 먼저 작성 (TDD)
- 포괄적인 커버리지를 위해 테이블 주도 테스트 사용
- 구현이 아닌 동작을 테스트
- 헬퍼 함수에서 `t.Helper()` 사용
- 독립적인 테스트에 `t.Parallel()` 사용
- `t.Cleanup()`으로 리소스 정리
- 시나리오를 설명하는 의미 있는 테스트 이름 사용
**하지 말아야 할 것:**
- 비공개 함수를 직접 테스트 (공개 API를 통해 테스트)
- 테스트에서 `time.Sleep()` 사용 (채널이나 조건 사용)
- 불안정한 테스트 무시 (수정하거나 제거)
- 모든 것을 모킹 (가능하면 통합 테스트 선호)
- 에러 경로 테스트 생략
## CI/CD 통합
```yaml
# GitHub Actions example
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run tests
run: go test -race -coverprofile=coverage.out ./...
- name: Check coverage
run: |
go tool cover -func=coverage.out | grep total | awk '{print $3}' | \
awk -F'%' '{if ($1 < 80) exit 1}'
```
**기억하세요**: 테스트는 문서입니다. 코드가 어떻게 사용되어야 하는지를 보여줍니다. 명확하게 작성하고 최신 상태로 유지하세요.

View File

@@ -0,0 +1,211 @@
---
name: iterative-retrieval
description: 서브에이전트 컨텍스트 문제를 해결하기 위한 점진적 컨텍스트 검색 개선 패턴
origin: ECC
---
# 반복적 검색 패턴
서브에이전트가 작업을 시작하기 전까지 필요한 컨텍스트를 알 수 없는 멀티 에이전트 워크플로우의 "컨텍스트 문제"를 해결합니다.
## 활성화 시점
- 사전에 예측할 수 없는 코드베이스 컨텍스트가 필요한 서브에이전트를 생성할 때
- 컨텍스트가 점진적으로 개선되는 멀티 에이전트 워크플로우를 구축할 때
- 에이전트 작업에서 "컨텍스트 초과" 또는 "컨텍스트 누락" 실패를 겪을 때
- 코드 탐색을 위한 RAG 유사 검색 파이프라인을 설계할 때
- 에이전트 오케스트레이션에서 토큰 사용량을 최적화할 때
## 문제
서브에이전트는 제한된 컨텍스트로 생성됩니다. 다음을 알 수 없습니다:
- 관련 코드가 포함된 파일
- 코드베이스에 존재하는 패턴
- 프로젝트에서 사용하는 용어
표준 접근법의 실패:
- **모든 것을 전송**: 컨텍스트 제한 초과
- **아무것도 전송하지 않음**: 에이전트가 중요한 정보를 갖지 못함
- **필요한 것을 추측**: 종종 잘못됨
## 해결책: 반복적 검색
컨텍스트를 점진적으로 개선하는 4단계 루프:
```
┌─────────────────────────────────────────────┐
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ DISPATCH │─────▶│ EVALUATE │ │
│ └──────────┘ └──────────┘ │
│ ▲ │ │
│ │ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ LOOP │◀─────│ REFINE │ │
│ └──────────┘ └──────────┘ │
│ │
│ Max 3 cycles, then proceed │
└─────────────────────────────────────────────┘
```
### 1단계: DISPATCH
후보 파일을 수집하기 위한 초기 광범위 쿼리:
```javascript
// Start with high-level intent
const initialQuery = {
patterns: ['src/**/*.ts', 'lib/**/*.ts'],
keywords: ['authentication', 'user', 'session'],
excludes: ['*.test.ts', '*.spec.ts']
};
// Dispatch to retrieval agent
const candidates = await retrieveFiles(initialQuery);
```
### 2단계: EVALUATE
검색된 콘텐츠의 관련성 평가:
```javascript
function evaluateRelevance(files, task) {
return files.map(file => ({
path: file.path,
relevance: scoreRelevance(file.content, task),
reason: explainRelevance(file.content, task),
missingContext: identifyGaps(file.content, task)
}));
}
```
점수 기준:
- **높음 (0.8-1.0)**: 대상 기능을 직접 구현
- **중간 (0.5-0.7)**: 관련 패턴이나 타입을 포함
- **낮음 (0.2-0.4)**: 간접적으로 관련
- **없음 (0-0.2)**: 관련 없음, 제외
### 3단계: REFINE
평가를 기반으로 검색 기준 업데이트:
```javascript
function refineQuery(evaluation, previousQuery) {
return {
// Add new patterns discovered in high-relevance files
patterns: [...previousQuery.patterns, ...extractPatterns(evaluation)],
// Add terminology found in codebase
keywords: [...previousQuery.keywords, ...extractKeywords(evaluation)],
// Exclude confirmed irrelevant paths
excludes: [...previousQuery.excludes, ...evaluation
.filter(e => e.relevance < 0.2)
.map(e => e.path)
],
// Target specific gaps
focusAreas: evaluation
.flatMap(e => e.missingContext)
.filter(unique)
};
}
```
### 4단계: LOOP
개선된 기준으로 반복 (최대 3회):
```javascript
async function iterativeRetrieve(task, maxCycles = 3) {
let query = createInitialQuery(task);
let bestContext = [];
for (let cycle = 0; cycle < maxCycles; cycle++) {
const candidates = await retrieveFiles(query);
const evaluation = evaluateRelevance(candidates, task);
// Check if we have sufficient context
const highRelevance = evaluation.filter(e => e.relevance >= 0.7);
if (highRelevance.length >= 3 && !hasCriticalGaps(evaluation)) {
return highRelevance;
}
// Refine and continue
query = refineQuery(evaluation, query);
bestContext = mergeContext(bestContext, highRelevance);
}
return bestContext;
}
```
## 실용적인 예시
### 예시 1: 버그 수정 컨텍스트
```
Task: "Fix the authentication token expiry bug"
Cycle 1:
DISPATCH: Search for "token", "auth", "expiry" in src/**
EVALUATE: Found auth.ts (0.9), tokens.ts (0.8), user.ts (0.3)
REFINE: Add "refresh", "jwt" keywords; exclude user.ts
Cycle 2:
DISPATCH: Search refined terms
EVALUATE: Found session-manager.ts (0.95), jwt-utils.ts (0.85)
REFINE: Sufficient context (2 high-relevance files)
Result: auth.ts, tokens.ts, session-manager.ts, jwt-utils.ts
```
### 예시 2: 기능 구현
```
Task: "Add rate limiting to API endpoints"
Cycle 1:
DISPATCH: Search "rate", "limit", "api" in routes/**
EVALUATE: No matches - codebase uses "throttle" terminology
REFINE: Add "throttle", "middleware" keywords
Cycle 2:
DISPATCH: Search refined terms
EVALUATE: Found throttle.ts (0.9), middleware/index.ts (0.7)
REFINE: Need router patterns
Cycle 3:
DISPATCH: Search "router", "express" patterns
EVALUATE: Found router-setup.ts (0.8)
REFINE: Sufficient context
Result: throttle.ts, middleware/index.ts, router-setup.ts
```
## 에이전트와의 통합
에이전트 프롬프트에서 사용:
```markdown
When retrieving context for this task:
1. Start with broad keyword search
2. Evaluate each file's relevance (0-1 scale)
3. Identify what context is still missing
4. Refine search criteria and repeat (max 3 cycles)
5. Return files with relevance >= 0.7
```
## 모범 사례
1. **광범위하게 시작하여 점진적으로 좁히기** - 초기 쿼리를 과도하게 지정하지 않기
2. **코드베이스 용어 학습** - 첫 번째 사이클에서 주로 네이밍 컨벤션이 드러남
3. **누락된 것 추적** - 명시적 격차 식별이 개선을 주도
4. **"충분히 좋은" 수준에서 중단** - 관련성 높은 파일 3개가 보통 수준의 파일 10개보다 나음
5. **자신 있게 제외** - 관련성 낮은 파일은 관련성이 높아지지 않음
## 관련 항목
- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) - 서브에이전트 오케스트레이션 섹션
- `continuous-learning` 스킬 - 시간이 지남에 따라 개선되는 패턴
- `~/.claude/agents/`의 에이전트 정의

View File

@@ -0,0 +1,147 @@
---
name: postgres-patterns
description: 쿼리 최적화, 스키마 설계, 인덱싱, 보안을 위한 PostgreSQL 데이터베이스 패턴. Supabase 모범 사례 기반.
origin: ECC
---
# PostgreSQL 패턴
PostgreSQL 모범 사례 빠른 참조. 자세한 가이드는 `database-reviewer` 에이전트를 사용하세요.
## 활성화 시점
- SQL 쿼리 또는 마이그레이션을 작성할 때
- 데이터베이스 스키마를 설계할 때
- 느린 쿼리를 문제 해결할 때
- Row Level Security를 구현할 때
- 커넥션 풀링을 설정할 때
## 빠른 참조
### 인덱스 치트 시트
| 쿼리 패턴 | 인덱스 유형 | 예시 |
|--------------|------------|---------|
| `WHERE col = value` | B-tree (기본값) | `CREATE INDEX idx ON t (col)` |
| `WHERE col > value` | B-tree | `CREATE INDEX idx ON t (col)` |
| `WHERE a = x AND b > y` | Composite | `CREATE INDEX idx ON t (a, b)` |
| `WHERE jsonb @> '{}'` | GIN | `CREATE INDEX idx ON t USING gin (col)` |
| `WHERE tsv @@ query` | GIN | `CREATE INDEX idx ON t USING gin (col)` |
| 시계열 범위 | BRIN | `CREATE INDEX idx ON t USING brin (col)` |
### 데이터 타입 빠른 참조
| 사용 사례 | 올바른 타입 | 지양 |
|----------|-------------|-------|
| ID | `bigint` | `int`, random UUID |
| 문자열 | `text` | `varchar(255)` |
| 타임스탬프 | `timestamptz` | `timestamp` |
| 금액 | `numeric(10,2)` | `float` |
| 플래그 | `boolean` | `varchar`, `int` |
### 일반 패턴
**복합 인덱스 순서:**
```sql
-- Equality columns first, then range columns
CREATE INDEX idx ON orders (status, created_at);
-- Works for: WHERE status = 'pending' AND created_at > '2024-01-01'
```
**커버링 인덱스:**
```sql
CREATE INDEX idx ON users (email) INCLUDE (name, created_at);
-- Avoids table lookup for SELECT email, name, created_at
```
**부분 인덱스:**
```sql
CREATE INDEX idx ON users (email) WHERE deleted_at IS NULL;
-- Smaller index, only includes active users
```
**RLS 정책 (최적화):**
```sql
CREATE POLICY policy ON orders
USING ((SELECT auth.uid()) = user_id); -- Wrap in SELECT!
```
**UPSERT:**
```sql
INSERT INTO settings (user_id, key, value)
VALUES (123, 'theme', 'dark')
ON CONFLICT (user_id, key)
DO UPDATE SET value = EXCLUDED.value;
```
**커서 페이지네이션:**
```sql
SELECT * FROM products WHERE id > $last_id ORDER BY id LIMIT 20;
-- O(1) vs OFFSET which is O(n)
```
**큐 처리:**
```sql
UPDATE jobs SET status = 'processing'
WHERE id = (
SELECT id FROM jobs WHERE status = 'pending'
ORDER BY created_at LIMIT 1
FOR UPDATE SKIP LOCKED
) RETURNING *;
```
### 안티패턴 감지
```sql
-- Find unindexed foreign keys
SELECT conrelid::regclass, a.attname
FROM pg_constraint c
JOIN pg_attribute a ON a.attrelid = c.conrelid AND a.attnum = ANY(c.conkey)
WHERE c.contype = 'f'
AND NOT EXISTS (
SELECT 1 FROM pg_index i
WHERE i.indrelid = c.conrelid AND a.attnum = ANY(i.indkey)
);
-- Find slow queries
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
WHERE mean_exec_time > 100
ORDER BY mean_exec_time DESC;
-- Check table bloat
SELECT relname, n_dead_tup, last_vacuum
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC;
```
### 구성 템플릿
```sql
-- Connection limits (adjust for RAM)
ALTER SYSTEM SET max_connections = 100;
ALTER SYSTEM SET work_mem = '8MB';
-- Timeouts
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';
ALTER SYSTEM SET statement_timeout = '30s';
-- Monitoring
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Security defaults
REVOKE ALL ON SCHEMA public FROM public;
SELECT pg_reload_conf();
```
## 관련 항목
- 에이전트: `database-reviewer` - 전체 데이터베이스 리뷰 워크플로우
- 스킬: `clickhouse-io` - ClickHouse 분석 패턴
- 스킬: `backend-patterns` - API 및 백엔드 패턴
---
*Supabase Agent Skills 기반 (크레딧: Supabase 팀) (MIT License)*

View File

@@ -0,0 +1,349 @@
---
name: project-guidelines-example
description: "실제 프로덕션 애플리케이션을 기반으로 한 프로젝트별 스킬 템플릿 예시."
origin: ECC
---
# 프로젝트 가이드라인 스킬 (예시)
이것은 프로젝트별 스킬의 예시입니다. 자신의 프로젝트에 맞는 템플릿으로 사용하세요.
실제 프로덕션 애플리케이션을 기반으로 합니다: [Zenith](https://zenith.chat) - AI 기반 고객 발견 플랫폼.
## 사용 시점
이 스킬이 설계된 특정 프로젝트에서 작업할 때 참조하세요. 프로젝트 스킬에는 다음이 포함됩니다:
- 아키텍처 개요
- 파일 구조
- 코드 패턴
- 테스팅 요구사항
- 배포 워크플로우
---
## 아키텍처 개요
**기술 스택:**
- **Frontend**: Next.js 15 (App Router), TypeScript, React
- **Backend**: FastAPI (Python), Pydantic 모델
- **Database**: Supabase (PostgreSQL)
- **AI**: Claude API (도구 호출 및 구조화된 출력)
- **Deployment**: Google Cloud Run
- **Testing**: Playwright (E2E), pytest (백엔드), React Testing Library
**서비스:**
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend │
│ Next.js 15 + TypeScript + TailwindCSS │
│ Deployed: Vercel / Cloud Run │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Backend │
│ FastAPI + Python 3.11 + Pydantic │
│ Deployed: Cloud Run │
└─────────────────────────────────────────────────────────────┘
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Supabase │ │ Claude │ │ Redis │
│ Database │ │ API │ │ Cache │
└──────────┘ └──────────┘ └──────────┘
```
---
## 파일 구조
```
project/
├── frontend/
│ └── src/
│ ├── app/ # Next.js app router 페이지
│ │ ├── api/ # API 라우트
│ │ ├── (auth)/ # 인증 보호 라우트
│ │ └── workspace/ # 메인 앱 워크스페이스
│ ├── components/ # React 컴포넌트
│ │ ├── ui/ # 기본 UI 컴포넌트
│ │ ├── forms/ # 폼 컴포넌트
│ │ └── layouts/ # 레이아웃 컴포넌트
│ ├── hooks/ # 커스텀 React hooks
│ ├── lib/ # 유틸리티
│ ├── types/ # TypeScript 정의
│ └── config/ # 설정
├── backend/
│ ├── routers/ # FastAPI 라우트 핸들러
│ ├── models.py # Pydantic 모델
│ ├── main.py # FastAPI 앱 엔트리
│ ├── auth_system.py # 인증
│ ├── database.py # 데이터베이스 작업
│ ├── services/ # 비즈니스 로직
│ └── tests/ # pytest 테스트
├── deploy/ # 배포 설정
├── docs/ # 문서
└── scripts/ # 유틸리티 스크립트
```
---
## 코드 패턴
### API 응답 형식 (FastAPI)
```python
from pydantic import BaseModel
from typing import Generic, TypeVar, Optional
T = TypeVar('T')
class ApiResponse(BaseModel, Generic[T]):
success: bool
data: Optional[T] = None
error: Optional[str] = None
@classmethod
def ok(cls, data: T) -> "ApiResponse[T]":
return cls(success=True, data=data)
@classmethod
def fail(cls, error: str) -> "ApiResponse[T]":
return cls(success=False, error=error)
```
### Frontend API 호출 (TypeScript)
```typescript
interface ApiResponse<T> {
success: boolean
data?: T
error?: string
}
async function fetchApi<T>(
endpoint: string,
options?: RequestInit
): Promise<ApiResponse<T>> {
try {
const response = await fetch(`/api${endpoint}`, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
})
if (!response.ok) {
return { success: false, error: `HTTP ${response.status}` }
}
return await response.json()
} catch (error) {
return { success: false, error: String(error) }
}
}
```
### Claude AI 통합 (구조화된 출력)
```python
from anthropic import Anthropic
from pydantic import BaseModel
class AnalysisResult(BaseModel):
summary: str
key_points: list[str]
confidence: float
async def analyze_with_claude(content: str) -> AnalysisResult:
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": content}],
tools=[{
"name": "provide_analysis",
"description": "Provide structured analysis",
"input_schema": AnalysisResult.model_json_schema()
}],
tool_choice={"type": "tool", "name": "provide_analysis"}
)
# Extract tool use result
tool_use = next(
block for block in response.content
if block.type == "tool_use"
)
return AnalysisResult(**tool_use.input)
```
### 커스텀 Hooks (React)
```typescript
import { useState, useCallback } from 'react'
interface UseApiState<T> {
data: T | null
loading: boolean
error: string | null
}
export function useApi<T>(
fetchFn: () => Promise<ApiResponse<T>>
) {
const [state, setState] = useState<UseApiState<T>>({
data: null,
loading: false,
error: null,
})
const execute = useCallback(async () => {
setState(prev => ({ ...prev, loading: true, error: null }))
const result = await fetchFn()
if (result.success) {
setState({ data: result.data!, loading: false, error: null })
} else {
setState({ data: null, loading: false, error: result.error! })
}
}, [fetchFn])
return { ...state, execute }
}
```
---
## 테스팅 요구사항
### Backend (pytest)
```bash
# Run all tests
poetry run pytest tests/
# Run with coverage
poetry run pytest tests/ --cov=. --cov-report=html
# Run specific test file
poetry run pytest tests/test_auth.py -v
```
**테스트 구조:**
```python
import pytest
from httpx import AsyncClient
from main import app
@pytest.fixture
async def client():
async with AsyncClient(app=app, base_url="http://test") as ac:
yield ac
@pytest.mark.asyncio
async def test_health_check(client: AsyncClient):
response = await client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
```
### Frontend (React Testing Library)
```bash
# Run tests
npm run test
# Run with coverage
npm run test -- --coverage
# Run E2E tests
npm run test:e2e
```
**테스트 구조:**
```typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { WorkspacePanel } from './WorkspacePanel'
describe('WorkspacePanel', () => {
it('renders workspace correctly', () => {
render(<WorkspacePanel />)
expect(screen.getByRole('main')).toBeInTheDocument()
})
it('handles session creation', async () => {
render(<WorkspacePanel />)
fireEvent.click(screen.getByText('New Session'))
expect(await screen.findByText('Session created')).toBeInTheDocument()
})
})
```
---
## 배포 워크플로우
### 배포 전 체크리스트
- [ ] 모든 테스트가 로컬에서 통과
- [ ] `npm run build` 성공 (frontend)
- [ ] `poetry run pytest` 통과 (backend)
- [ ] 하드코딩된 시크릿 없음
- [ ] 환경 변수 문서화됨
- [ ] 데이터베이스 마이그레이션 준비됨
### 배포 명령어
```bash
# Build and deploy frontend
cd frontend && npm run build
gcloud run deploy frontend --source .
# Build and deploy backend
cd backend
gcloud run deploy backend --source .
```
### 환경 변수
```bash
# Frontend (.env.local)
NEXT_PUBLIC_API_URL=https://api.example.com
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
# Backend (.env)
DATABASE_URL=postgresql://...
ANTHROPIC_API_KEY=sk-ant-...
SUPABASE_URL=https://xxx.supabase.co
SUPABASE_KEY=eyJ...
```
---
## 핵심 규칙
1. **코드, 주석, 문서에 이모지 없음**
2. **불변성** - 객체나 배열을 절대 변형하지 않음
3. **TDD** - 구현 전에 테스트 작성
4. **80% 커버리지** 최소
5. **작은 파일 여러 개** - 200-400줄이 일반적, 800줄 최대
6. **프로덕션 코드에 console.log 없음**
7. **적절한 에러 처리** (try/catch 사용)
8. **입력 유효성 검사** (Pydantic/Zod 사용)
---
## 관련 스킬
- `coding-standards.md` - 일반 코딩 모범 사례
- `backend-patterns.md` - API 및 데이터베이스 패턴
- `frontend-patterns.md` - React 및 Next.js 패턴
- `tdd-workflow/` - 테스트 주도 개발 방법론

View File

@@ -0,0 +1,504 @@
---
name: security-review
description: 인증 추가, 사용자 입력 처리, 시크릿 관리, API 엔드포인트 생성, 결제/민감한 기능 구현 시 이 스킬을 사용하세요. 포괄적인 보안 체크리스트와 패턴을 제공합니다.
origin: ECC
---
# 보안 리뷰 스킬
이 스킬은 모든 코드가 보안 모범 사례를 따르고 잠재적 취약점을 식별하도록 보장합니다.
## 활성화 시점
- 인증 또는 권한 부여 구현 시
- 사용자 입력 또는 파일 업로드 처리 시
- 새로운 API 엔드포인트 생성 시
- 시크릿 또는 자격 증명 작업 시
- 결제 기능 구현 시
- 민감한 데이터 저장 또는 전송 시
- 서드파티 API 통합 시
## 보안 체크리스트
### 1. 시크릿 관리
#### 절대 하지 말아야 할 것
```typescript
const apiKey = "sk-proj-xxxxx" // Hardcoded secret
const dbPassword = "password123" // In source code
```
#### 반드시 해야 할 것
```typescript
const apiKey = process.env.OPENAI_API_KEY
const dbUrl = process.env.DATABASE_URL
// Verify secrets exist
if (!apiKey) {
throw new Error('OPENAI_API_KEY not configured')
}
```
#### 확인 단계
- [ ] 하드코딩된 API 키, 토큰, 비밀번호 없음
- [ ] 모든 시크릿이 환경 변수에 저장됨
- [ ] `.env.local`이 .gitignore에 포함됨
- [ ] git 히스토리에 시크릿 없음
- [ ] 프로덕션 시크릿이 호스팅 플랫폼(Vercel, Railway)에 저장됨
### 2. 입력 유효성 검사
#### 항상 사용자 입력을 검증할 것
```typescript
import { z } from 'zod'
// Define validation schema
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
age: z.number().int().min(0).max(150)
})
// Validate before processing
export async function createUser(input: unknown) {
try {
const validated = CreateUserSchema.parse(input)
return await db.users.create(validated)
} catch (error) {
if (error instanceof z.ZodError) {
return { success: false, errors: error.errors }
}
throw error
}
}
```
#### 파일 업로드 유효성 검사
```typescript
function validateFileUpload(file: File) {
// Size check (5MB max)
const maxSize = 5 * 1024 * 1024
if (file.size > maxSize) {
throw new Error('File too large (max 5MB)')
}
// Type check
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']
if (!allowedTypes.includes(file.type)) {
throw new Error('Invalid file type')
}
// Extension check
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.gif']
const extension = file.name.toLowerCase().match(/\.[^.]+$/)?.[0]
if (!extension || !allowedExtensions.includes(extension)) {
throw new Error('Invalid file extension')
}
return true
}
```
#### 확인 단계
- [ ] 모든 사용자 입력이 스키마로 검증됨
- [ ] 파일 업로드가 제한됨 (크기, 타입, 확장자)
- [ ] 사용자 입력이 쿼리에 직접 사용되지 않음
- [ ] 화이트리스트 검증 사용 (블랙리스트가 아닌)
- [ ] 에러 메시지가 민감한 정보를 노출하지 않음
### 3. SQL Injection 방지
#### 절대 SQL을 연결하지 말 것
```typescript
// DANGEROUS - SQL Injection vulnerability
const query = `SELECT * FROM users WHERE email = '${userEmail}'`
await db.query(query)
```
#### 반드시 파라미터화된 쿼리를 사용할 것
```typescript
// Safe - parameterized query
const { data } = await supabase
.from('users')
.select('*')
.eq('email', userEmail)
// Or with raw SQL
await db.query(
'SELECT * FROM users WHERE email = $1',
[userEmail]
)
```
#### 확인 단계
- [ ] 모든 데이터베이스 쿼리가 파라미터화된 쿼리 사용
- [ ] SQL에서 문자열 연결 없음
- [ ] ORM/쿼리 빌더가 올바르게 사용됨
- [ ] Supabase 쿼리가 적절히 새니타이징됨
### 4. 인증 및 권한 부여
#### JWT 토큰 처리
```typescript
// ❌ WRONG: localStorage (vulnerable to XSS)
localStorage.setItem('token', token)
// ✅ CORRECT: httpOnly cookies
res.setHeader('Set-Cookie',
`token=${token}; HttpOnly; Secure; SameSite=Strict; Max-Age=3600`)
```
#### 권한 부여 확인
```typescript
export async function deleteUser(userId: string, requesterId: string) {
// ALWAYS verify authorization first
const requester = await db.users.findUnique({
where: { id: requesterId }
})
if (requester.role !== 'admin') {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 403 }
)
}
// Proceed with deletion
await db.users.delete({ where: { id: userId } })
}
```
#### Row Level Security (Supabase)
```sql
-- Enable RLS on all tables
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
-- Users can only view their own data
CREATE POLICY "Users view own data"
ON users FOR SELECT
USING (auth.uid() = id);
-- Users can only update their own data
CREATE POLICY "Users update own data"
ON users FOR UPDATE
USING (auth.uid() = id);
```
#### 확인 단계
- [ ] 토큰이 httpOnly 쿠키에 저장됨 (localStorage가 아닌)
- [ ] 민감한 작업 전에 권한 부여 확인
- [ ] Supabase에서 Row Level Security 활성화됨
- [ ] 역할 기반 접근 제어 구현됨
- [ ] 세션 관리가 안전함
### 5. XSS 방지
#### HTML 새니타이징
```typescript
import DOMPurify from 'isomorphic-dompurify'
// ALWAYS sanitize user-provided HTML
function renderUserContent(html: string) {
const clean = DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p'],
ALLOWED_ATTR: []
})
return <div dangerouslySetInnerHTML={{ __html: clean }} />
}
```
#### Content Security Policy
```typescript
// next.config.js
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: `
default-src 'self';
script-src 'self' 'nonce-{nonce}';
style-src 'self' 'nonce-{nonce}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
`.replace(/\s{2,}/g, ' ').trim()
}
]
```
`{nonce}`는 요청마다 새로 생성하고, 헤더와 인라인 `<script>`/`<style>` 태그에 동일하게 주입해야 합니다.
#### 확인 단계
- [ ] 사용자 제공 HTML이 새니타이징됨
- [ ] CSP 헤더가 구성됨
- [ ] 검증되지 않은 동적 콘텐츠 렌더링 없음
- [ ] React의 내장 XSS 보호가 사용됨
### 6. CSRF 보호
#### CSRF 토큰
```typescript
import { csrf } from '@/lib/csrf'
export async function POST(request: Request) {
const token = request.headers.get('X-CSRF-Token')
if (!csrf.verify(token)) {
return NextResponse.json(
{ error: 'Invalid CSRF token' },
{ status: 403 }
)
}
// Process request
}
```
#### SameSite 쿠키
```typescript
res.setHeader('Set-Cookie',
`session=${sessionId}; HttpOnly; Secure; SameSite=Strict`)
```
#### 확인 단계
- [ ] 상태 변경 작업에 CSRF 토큰 적용
- [ ] 모든 쿠키에 SameSite=Strict 설정
- [ ] Double-submit 쿠키 패턴 구현
### 7. 속도 제한
#### API 속도 제한
```typescript
import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: 'Too many requests'
})
// Apply to routes
app.use('/api/', limiter)
```
#### 비용이 높은 작업
```typescript
// Aggressive rate limiting for searches
const searchLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 10, // 10 requests per minute
message: 'Too many search requests'
})
app.use('/api/search', searchLimiter)
```
#### 확인 단계
- [ ] 모든 API 엔드포인트에 속도 제한 적용
- [ ] 비용이 높은 작업에 더 엄격한 제한
- [ ] IP 기반 속도 제한
- [ ] 사용자 기반 속도 제한 (인증된 사용자)
### 8. 민감한 데이터 노출
#### 로깅
```typescript
// ❌ WRONG: Logging sensitive data
console.log('User login:', { email, password })
console.log('Payment:', { cardNumber, cvv })
// ✅ CORRECT: Redact sensitive data
console.log('User login:', { email, userId })
console.log('Payment:', { last4: card.last4, userId })
```
#### 에러 메시지
```typescript
// ❌ WRONG: Exposing internal details
catch (error) {
return NextResponse.json(
{ error: error.message, stack: error.stack },
{ status: 500 }
)
}
// ✅ CORRECT: Generic error messages
catch (error) {
console.error('Internal error:', error)
return NextResponse.json(
{ error: 'An error occurred. Please try again.' },
{ status: 500 }
)
}
```
#### 확인 단계
- [ ] 로그에 비밀번호, 토큰, 시크릿 없음
- [ ] 사용자에게 표시되는 에러 메시지가 일반적임
- [ ] 상세 에러는 서버 로그에만 기록
- [ ] 사용자에게 스택 트레이스가 노출되지 않음
### 9. 블록체인 보안 (Solana)
#### 지갑 검증
```typescript
import nacl from 'tweetnacl'
import bs58 from 'bs58'
import { PublicKey } from '@solana/web3.js'
async function verifyWalletOwnership(
publicKey: string,
signature: string,
message: string
) {
try {
const publicKeyBytes = new PublicKey(publicKey).toBytes()
const signatureBytes = bs58.decode(signature)
const messageBytes = new TextEncoder().encode(message)
return nacl.sign.detached.verify(
messageBytes,
signatureBytes,
publicKeyBytes
)
} catch (error) {
return false
}
}
```
참고: Solana 공개 키와 서명은 일반적으로 base64가 아니라 base58로 인코딩됩니다.
#### 트랜잭션 검증
```typescript
async function verifyTransaction(transaction: Transaction) {
// Verify recipient
if (transaction.to !== expectedRecipient) {
throw new Error('Invalid recipient')
}
// Verify amount
if (transaction.amount > maxAmount) {
throw new Error('Amount exceeds limit')
}
// Verify user has sufficient balance
const balance = await getBalance(transaction.from)
if (balance < transaction.amount) {
throw new Error('Insufficient balance')
}
return true
}
```
#### 확인 단계
- [ ] 지갑 서명 검증됨
- [ ] 트랜잭션 세부 정보 유효성 검사됨
- [ ] 트랜잭션 전 잔액 확인
- [ ] 블라인드 트랜잭션 서명 없음
### 10. 의존성 보안
#### 정기 업데이트
```bash
# Check for vulnerabilities
npm audit
# Fix automatically fixable issues
npm audit fix
# Update dependencies
npm update
# Check for outdated packages
npm outdated
```
#### 잠금 파일
```bash
# ALWAYS commit lock files
git add package-lock.json
# Use in CI/CD for reproducible builds
npm ci # Instead of npm install
```
#### 확인 단계
- [ ] 의존성이 최신 상태
- [ ] 알려진 취약점 없음 (npm audit 클린)
- [ ] 잠금 파일 커밋됨
- [ ] GitHub에서 Dependabot 활성화됨
- [ ] 정기적인 보안 업데이트
## 보안 테스트
### 자동화된 보안 테스트
```typescript
// Test authentication
test('requires authentication', async () => {
const response = await fetch('/api/protected')
expect(response.status).toBe(401)
})
// Test authorization
test('requires admin role', async () => {
const response = await fetch('/api/admin', {
headers: { Authorization: `Bearer ${userToken}` }
})
expect(response.status).toBe(403)
})
// Test input validation
test('rejects invalid input', async () => {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ email: 'not-an-email' })
})
expect(response.status).toBe(400)
})
// Test rate limiting
test('enforces rate limits', async () => {
const requests = Array(101).fill(null).map(() =>
fetch('/api/endpoint')
)
const responses = await Promise.all(requests)
const tooManyRequests = responses.filter(r => r.status === 429)
expect(tooManyRequests.length).toBeGreaterThan(0)
})
```
## 배포 전 보안 체크리스트
모든 프로덕션 배포 전:
- [ ] **시크릿**: 하드코딩된 시크릿 없음, 모두 환경 변수에 저장
- [ ] **입력 유효성 검사**: 모든 사용자 입력 검증됨
- [ ] **SQL Injection**: 모든 쿼리 파라미터화됨
- [ ] **XSS**: 사용자 콘텐츠 새니타이징됨
- [ ] **CSRF**: 보호 활성화됨
- [ ] **인증**: 적절한 토큰 처리
- [ ] **권한 부여**: 역할 확인 적용됨
- [ ] **속도 제한**: 모든 엔드포인트에서 활성화됨
- [ ] **HTTPS**: 프로덕션에서 강제 적용
- [ ] **보안 헤더**: CSP, X-Frame-Options 구성됨
- [ ] **에러 처리**: 에러에 민감한 데이터 없음
- [ ] **로깅**: 민감한 데이터가 로그에 없음
- [ ] **의존성**: 최신 상태, 취약점 없음
- [ ] **Row Level Security**: Supabase에서 활성화됨
- [ ] **CORS**: 적절히 구성됨
- [ ] **파일 업로드**: 유효성 검사됨 (크기, 타입)
- [ ] **지갑 서명**: 검증됨 (블록체인인 경우)
## 참고 자료
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [Next.js Security](https://nextjs.org/docs/security)
- [Supabase Security](https://supabase.com/docs/guides/auth)
- [Web Security Academy](https://portswigger.net/web-security)
---
**기억하세요**: 보안은 선택 사항이 아닙니다. 하나의 취약점이 전체 플랫폼을 침해할 수 있습니다. 의심스러울 때는 보수적으로 대응하세요.

View File

@@ -0,0 +1,361 @@
| name | description |
|------|-------------|
| cloud-infrastructure-security | 클라우드 플랫폼 배포, 인프라 구성, IAM 정책 관리, 로깅/모니터링 설정, CI/CD 파이프라인 구현 시 이 스킬을 사용하세요. 모범 사례에 맞춘 클라우드 보안 체크리스트를 제공합니다. |
# 클라우드 및 인프라 보안 스킬
이 스킬은 클라우드 인프라, CI/CD 파이프라인, 배포 구성이 보안 모범 사례를 따르고 업계 표준을 준수하도록 보장합니다.
## 활성화 시점
- 클라우드 플랫폼(AWS, Vercel, Railway, Cloudflare)에 애플리케이션 배포 시
- IAM 역할 및 권한 구성 시
- CI/CD 파이프라인 설정 시
- Infrastructure as Code(Terraform, CloudFormation) 구현 시
- 로깅 및 모니터링 구성 시
- 클라우드 환경에서 시크릿 관리 시
- CDN 및 엣지 보안 설정 시
- 재해 복구 및 백업 전략 구현 시
## 클라우드 보안 체크리스트
### 1. IAM 및 접근 제어
#### 최소 권한 원칙
```yaml
# ✅ CORRECT: Minimal permissions
iam_role:
permissions:
- s3:GetObject # Only read access
- s3:ListBucket
resources:
- arn:aws:s3:::my-bucket/* # Specific bucket only
# ❌ WRONG: Overly broad permissions
iam_role:
permissions:
- s3:* # All S3 actions
resources:
- "*" # All resources
```
#### 다중 인증 (MFA)
```bash
# ALWAYS enable MFA for root/admin accounts
aws iam enable-mfa-device \
--user-name admin \
--serial-number arn:aws:iam::123456789:mfa/admin \
--authentication-code1 123456 \
--authentication-code2 789012
```
#### 확인 단계
- [ ] 프로덕션에서 루트 계정 사용 없음
- [ ] 모든 권한 있는 계정에 MFA 활성화됨
- [ ] 서비스 계정이 장기 자격 증명이 아닌 역할을 사용
- [ ] IAM 정책이 최소 권한을 따름
- [ ] 정기적인 접근 검토 수행
- [ ] 사용하지 않는 자격 증명 교체 또는 제거
### 2. 시크릿 관리
#### 클라우드 시크릿 매니저
```typescript
// ✅ CORRECT: Use cloud secrets manager
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
const client = new SecretsManager({ region: 'us-east-1' });
const secret = await client.getSecretValue({ SecretId: 'prod/api-key' });
const apiKey = JSON.parse(secret.SecretString).key;
// ❌ WRONG: Hardcoded or in environment variables only
const apiKey = process.env.API_KEY; // Not rotated, not audited
```
#### 시크릿 교체
```bash
# Set up automatic rotation for database credentials
aws secretsmanager rotate-secret \
--secret-id prod/db-password \
--rotation-lambda-arn arn:aws:lambda:region:account:function:rotate \
--rotation-rules AutomaticallyAfterDays=30
```
#### 확인 단계
- [ ] 모든 시크릿이 클라우드 시크릿 매니저에 저장됨 (AWS Secrets Manager, Vercel Secrets)
- [ ] 데이터베이스 자격 증명에 대한 자동 교체 활성화됨
- [ ] API 키가 최소 분기별로 교체됨
- [ ] 코드, 로그, 에러 메시지에 시크릿 없음
- [ ] 시크릿 접근에 대한 감사 로깅 활성화됨
### 3. 네트워크 보안
#### VPC 및 방화벽 구성
```terraform
# ✅ CORRECT: Restricted security group
resource "aws_security_group" "app" {
name = "app-sg"
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["10.0.0.0/16"] # Internal VPC only
}
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Only HTTPS outbound
}
}
# ❌ WRONG: Open to the internet
resource "aws_security_group" "bad" {
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # All ports, all IPs!
}
}
```
#### 확인 단계
- [ ] 데이터베이스가 공개적으로 접근 불가
- [ ] SSH/RDP 포트가 VPN/배스천에만 제한됨
- [ ] 보안 그룹이 최소 권한을 따름
- [ ] 네트워크 ACL이 구성됨
- [ ] VPC 플로우 로그가 활성화됨
### 4. 로깅 및 모니터링
#### CloudWatch/로깅 구성
```typescript
// ✅ CORRECT: Comprehensive logging
import { CloudWatchLogsClient, CreateLogStreamCommand } from '@aws-sdk/client-cloudwatch-logs';
const logSecurityEvent = async (event: SecurityEvent) => {
await cloudwatch.putLogEvents({
logGroupName: '/aws/security/events',
logStreamName: 'authentication',
logEvents: [{
timestamp: Date.now(),
message: JSON.stringify({
type: event.type,
userId: event.userId,
ip: event.ip,
result: event.result,
// Never log sensitive data
})
}]
});
};
```
#### 확인 단계
- [ ] 모든 서비스에 CloudWatch/로깅 활성화됨
- [ ] 실패한 인증 시도가 로깅됨
- [ ] 관리자 작업이 감사됨
- [ ] 로그 보존 기간이 구성됨 (규정 준수를 위해 90일 이상)
- [ ] 의심스러운 활동에 대한 알림 구성됨
- [ ] 로그가 중앙 집중화되고 변조 방지됨
### 5. CI/CD 파이프라인 보안
#### 보안 파이프라인 구성
```yaml
# ✅ CORRECT: Secure GitHub Actions workflow
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read # Minimal permissions
steps:
- uses: actions/checkout@v4
# Scan for secrets
- name: Secret scanning
uses: trufflesecurity/trufflehog@6c05c4a00b91aa542267d8e32a8254774799d68d
# Dependency audit
- name: Audit dependencies
run: npm audit --audit-level=high
# Use OIDC, not long-lived tokens
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: us-east-1
```
#### 공급망 보안
```json
// package.json - Use lock files and integrity checks
{
"scripts": {
"deps:install": "npm ci", // Use ci for reproducible builds
"audit": "npm audit --audit-level=moderate",
"check": "npm outdated"
}
}
```
#### 확인 단계
- [ ] 장기 자격 증명 대신 OIDC 사용
- [ ] 파이프라인에서 시크릿 스캐닝
- [ ] 의존성 취약점 스캐닝
- [ ] 컨테이너 이미지 스캐닝 (해당하는 경우)
- [ ] 브랜치 보호 규칙 적용됨
- [ ] 병합 전 코드 리뷰 필수
- [ ] 서명된 커밋 적용
### 6. Cloudflare 및 CDN 보안
#### Cloudflare 보안 구성
```typescript
// ✅ CORRECT: Cloudflare Workers with security headers
export default {
async fetch(request: Request): Promise<Response> {
const response = await fetch(request);
// Add security headers
const headers = new Headers(response.headers);
headers.set('X-Frame-Options', 'DENY');
headers.set('X-Content-Type-Options', 'nosniff');
headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
headers.set('Permissions-Policy', 'geolocation=(), microphone=()');
return new Response(response.body, {
status: response.status,
headers
});
}
};
```
#### WAF 규칙
```bash
# Enable Cloudflare WAF managed rules
# - OWASP Core Ruleset
# - Cloudflare Managed Ruleset
# - Rate limiting rules
# - Bot protection
```
#### 확인 단계
- [ ] OWASP 규칙으로 WAF 활성화됨
- [ ] 속도 제한 구성됨
- [ ] 봇 보호 활성화됨
- [ ] DDoS 보호 활성화됨
- [ ] 보안 헤더 구성됨
- [ ] SSL/TLS 엄격 모드 활성화됨
### 7. 백업 및 재해 복구
#### 자동 백업
```terraform
# ✅ CORRECT: Automated RDS backups
resource "aws_db_instance" "main" {
allocated_storage = 20
engine = "postgres"
backup_retention_period = 30 # 30 days retention
backup_window = "03:00-04:00"
maintenance_window = "mon:04:00-mon:05:00"
enabled_cloudwatch_logs_exports = ["postgresql"]
deletion_protection = true # Prevent accidental deletion
}
```
#### 확인 단계
- [ ] 자동 일일 백업 구성됨
- [ ] 백업 보존 기간이 규정 준수 요구사항을 충족
- [ ] 특정 시점 복구 활성화됨
- [ ] 분기별 백업 테스트 수행
- [ ] 재해 복구 계획 문서화됨
- [ ] RPO 및 RTO가 정의되고 테스트됨
## 배포 전 클라우드 보안 체크리스트
모든 프로덕션 클라우드 배포 전:
- [ ] **IAM**: 루트 계정 미사용, MFA 활성화, 최소 권한 정책
- [ ] **시크릿**: 모든 시크릿이 클라우드 시크릿 매니저에 교체와 함께 저장됨
- [ ] **네트워크**: 보안 그룹 제한됨, 공개 데이터베이스 없음
- [ ] **로깅**: CloudWatch/로깅이 보존 기간과 함께 활성화됨
- [ ] **모니터링**: 이상 징후에 대한 알림 구성됨
- [ ] **CI/CD**: OIDC 인증, 시크릿 스캐닝, 의존성 감사
- [ ] **CDN/WAF**: OWASP 규칙으로 Cloudflare WAF 활성화됨
- [ ] **암호화**: 저장 및 전송 중 데이터 암호화
- [ ] **백업**: 테스트된 복구와 함께 자동 백업
- [ ] **규정 준수**: GDPR/HIPAA 요구사항 충족 (해당하는 경우)
- [ ] **문서화**: 인프라 문서화, 런북 작성됨
- [ ] **인시던트 대응**: 보안 인시던트 계획 마련
## 일반적인 클라우드 보안 잘못된 구성
### S3 버킷 노출
```bash
# ❌ WRONG: Public bucket
aws s3api put-bucket-acl --bucket my-bucket --acl public-read
# ✅ CORRECT: Private bucket with specific access
aws s3api put-bucket-acl --bucket my-bucket --acl private
aws s3api put-bucket-policy --bucket my-bucket --policy file://policy.json
```
### RDS 공개 접근
```terraform
# ❌ WRONG
resource "aws_db_instance" "bad" {
publicly_accessible = true # NEVER do this!
}
# ✅ CORRECT
resource "aws_db_instance" "good" {
publicly_accessible = false
vpc_security_group_ids = [aws_security_group.db.id]
}
```
## 참고 자료
- [AWS Security Best Practices](https://aws.amazon.com/security/best-practices/)
- [CIS AWS Foundations Benchmark](https://www.cisecurity.org/benchmark/amazon_web_services)
- [Cloudflare Security Documentation](https://developers.cloudflare.com/security/)
- [OWASP Cloud Security](https://owasp.org/www-project-cloud-security/)
- [Terraform Security Best Practices](https://www.terraform.io/docs/cloud/guides/recommended-practices/)
**기억하세요**: 클라우드 잘못된 구성은 데이터 유출의 주요 원인입니다. 하나의 노출된 S3 버킷이나 과도하게 허용적인 IAM 정책이 전체 인프라를 침해할 수 있습니다. 항상 최소 권한 원칙과 심층 방어를 따르세요.

View File

@@ -0,0 +1,105 @@
---
name: strategic-compact
description: 임의의 자동 컴팩션 대신 논리적 간격에서 수동 컨텍스트 압축을 제안하여 작업 단계를 통해 컨텍스트를 보존합니다.
origin: ECC
---
# 전략적 컴팩트 스킬
임의의 자동 컴팩션에 의존하지 않고 워크플로우의 전략적 지점에서 수동 `/compact`를 제안합니다.
## 활성화 시점
- 컨텍스트 제한에 근접하는 긴 세션을 실행할 때 (200K+ 토큰)
- 다단계 작업을 수행할 때 (조사 -> 계획 -> 구현 -> 테스트)
- 같은 세션 내에서 관련 없는 작업 간 전환할 때
- 주요 마일스톤을 완료하고 새 작업을 시작할 때
- 응답이 느려지거나 일관성이 떨어질 때 (컨텍스트 압박)
## 전략적 컴팩션이 필요한 이유
자동 컴팩션은 임의의 지점에서 실행됩니다:
- 종종 작업 중간에 실행되어 중요한 컨텍스트를 잃음
- 논리적 작업 경계를 인식하지 못함
- 복잡한 다단계 작업을 중단할 수 있음
논리적 경계에서의 전략적 컴팩션:
- **탐색 후, 실행 전** -- 조사 컨텍스트를 압축하고 구현 계획은 유지
- **마일스톤 완료 후** -- 다음 단계를 위한 새로운 시작
- **주요 컨텍스트 전환 전** -- 다른 작업 시작 전에 탐색 컨텍스트 정리
## 작동 방식
`suggest-compact.js` 스크립트는 PreToolUse (Edit/Write)에서 실행되며 다음을 수행합니다:
1. **도구 호출 추적** -- 세션 내 도구 호출 횟수를 카운트
2. **임계값 감지** -- 설정 가능한 임계값에서 제안 (기본값: 50회)
3. **주기적 알림** -- 임계값 이후 25회마다 알림
## Hook 설정
`~/.claude/settings.json`에 추가합니다:
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/hooks/run-with-flags.js\" \"pre:edit-write:suggest-compact\" \"scripts/hooks/suggest-compact.js\" \"standard,strict\""
}
],
"description": "Suggest manual compaction at logical intervals"
}
]
}
}
```
## 구성
환경 변수:
- `COMPACT_THRESHOLD` -- 첫 번째 제안까지의 도구 호출 횟수 (기본값: 50)
## 컴팩션 결정 가이드
컴팩션 시기를 결정하기 위해 이 표를 사용하세요:
| 단계 전환 | 컴팩션? | 이유 |
|-----------------|----------|-----|
| 조사 -> 계획 | 예 | 조사 컨텍스트는 부피가 크고, 계획이 증류된 결과물 |
| 계획 -> 구현 | 예 | 계획은 TodoWrite 또는 파일에 있으므로 코드를 위한 컨텍스트 확보 |
| 구현 -> 테스트 | 경우에 따라 | 테스트가 최근 코드를 참조하면 유지; 포커스 전환 시 컴팩션 |
| 디버깅 -> 다음 기능 | 예 | 디버그 추적이 관련 없는 작업의 컨텍스트를 오염시킴 |
| 구현 중간 | 아니오 | 변수명, 파일 경로, 부분 상태를 잃는 비용이 큼 |
| 실패한 접근 후 | 예 | 새 접근을 시도하기 전에 막다른 길의 추론을 정리 |
## 컴팩션에서 유지되는 것
무엇이 유지되는지 이해하면 자신 있게 컴팩션할 수 있습니다:
| 유지됨 | 손실됨 |
|----------|------|
| CLAUDE.md 지침 | 중간 추론 및 분석 |
| TodoWrite 작업 목록 | 이전에 읽은 파일 내용 |
| 메모리 파일 (`~/.claude/memory/`) | 다단계 대화 컨텍스트 |
| Git 상태 (커밋, 브랜치) | 도구 호출 기록 및 횟수 |
| 디스크의 파일 | 구두로 언급된 세밀한 사용자 선호도 |
## 모범 사례
1. **계획 후 컴팩션** -- TodoWrite에서 계획이 확정되면 새로 시작하기 위해 컴팩션
2. **디버깅 후 컴팩션** -- 계속하기 전에 에러 해결 컨텍스트 정리
3. **구현 중간에는 컴팩션하지 않기** -- 관련 변경 사항의 컨텍스트 보존
4. **제안을 읽기** -- Hook이 *언제*를 알려주고, *할지* 여부는 당신이 결정
5. **컴팩션 전에 기록** -- 컴팩션 전에 중요한 컨텍스트를 파일이나 메모리에 저장
6. **요약과 함께 `/compact` 사용** -- 커스텀 메시지 추가: `/compact Focus on implementing auth middleware next`
## 관련 항목
- [The Longform Guide](https://x.com/affaanmustafa/status/2014040193557471352) -- 토큰 최적화 섹션
- 메모리 영속성 Hook -- 컴팩션에서 살아남는 상태를 위해
- `continuous-learning` 스킬 -- 세션 종료 전 패턴 추출

View File

@@ -0,0 +1,408 @@
---
name: tdd-workflow
description: 새 기능 작성, 버그 수정 또는 코드 리팩터링 시 이 스킬을 사용하세요. 단위, 통합, E2E 테스트를 포함한 80% 이상의 커버리지로 테스트 주도 개발을 시행합니다.
origin: ECC
---
# 테스트 주도 개발 워크플로우
이 스킬은 모든 코드 개발이 포괄적인 테스트 커버리지와 함께 TDD 원칙을 따르도록 보장합니다.
## 활성화 시점
- 새 기능이나 기능성을 작성할 때
- 버그나 이슈를 수정할 때
- 기존 코드를 리팩터링할 때
- API 엔드포인트를 추가할 때
- 새 컴포넌트를 생성할 때
## 핵심 원칙
### 1. 코드보다 테스트가 먼저
항상 테스트를 먼저 작성한 후, 테스트를 통과시키는 코드를 구현합니다.
### 2. 커버리지 요구 사항
- 최소 80% 커버리지 (단위 + 통합 + E2E)
- 모든 엣지 케이스 커버
- 에러 시나리오 테스트
- 경계 조건 검증
### 3. 테스트 유형
#### 단위 테스트
- 개별 함수 및 유틸리티
- 컴포넌트 로직
- 순수 함수
- 헬퍼 및 유틸리티
#### 통합 테스트
- API 엔드포인트
- 데이터베이스 작업
- 서비스 상호작용
- 외부 API 호출
#### E2E 테스트 (Playwright)
- 핵심 사용자 플로우
- 완전한 워크플로우
- 브라우저 자동화
- UI 상호작용
## TDD 워크플로우 단계
### 단계 1: 사용자 여정 작성
```
As a [role], I want to [action], so that [benefit]
Example:
As a user, I want to search for markets semantically,
so that I can find relevant markets even without exact keywords.
```
### 단계 2: 테스트 케이스 생성
각 사용자 여정에 대해 포괄적인 테스트 케이스를 작성합니다:
```typescript
describe('Semantic Search', () => {
it('returns relevant markets for query', async () => {
// Test implementation
})
it('handles empty query gracefully', async () => {
// Test edge case
})
it('falls back to substring search when Redis unavailable', async () => {
// Test fallback behavior
})
it('sorts results by similarity score', async () => {
// Test sorting logic
})
})
```
### 단계 3: 테스트 실행 (실패해야 함)
```bash
npm test
# Tests should fail - we haven't implemented yet
```
### 단계 4: 코드 구현
테스트를 통과시키기 위한 최소한의 코드를 작성합니다:
```typescript
// Implementation guided by tests
export async function searchMarkets(query: string) {
// Implementation here
}
```
### 단계 5: 테스트 재실행
```bash
npm test
# Tests should now pass
```
### 단계 6: 리팩터링
테스트가 통과하는 상태를 유지하면서 코드 품질을 개선합니다:
- 중복 제거
- 네이밍 개선
- 성능 최적화
- 가독성 향상
### 단계 7: 커버리지 확인
```bash
npm run test:coverage
# Verify 80%+ coverage achieved
```
## 테스트 패턴
### 단위 테스트 패턴 (Jest/Vitest)
```typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe('Button Component', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>)
expect(screen.getByText('Click me')).toBeInTheDocument()
})
it('calls onClick when clicked', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>Click</Button>)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click</Button>)
expect(screen.getByRole('button')).toBeDisabled()
})
})
```
### API 통합 테스트 패턴
```typescript
import { NextRequest } from 'next/server'
import { GET } from './route'
describe('GET /api/markets', () => {
it('returns markets successfully', async () => {
const request = new NextRequest('http://localhost/api/markets')
const response = await GET(request)
const data = await response.json()
expect(response.status).toBe(200)
expect(data.success).toBe(true)
expect(Array.isArray(data.data)).toBe(true)
})
it('validates query parameters', async () => {
const request = new NextRequest('http://localhost/api/markets?limit=invalid')
const response = await GET(request)
expect(response.status).toBe(400)
})
it('handles database errors gracefully', async () => {
// Mock database failure
const request = new NextRequest('http://localhost/api/markets')
// Test error handling
})
})
```
### E2E 테스트 패턴 (Playwright)
```typescript
import { test, expect } from '@playwright/test'
test('user can search and filter markets', async ({ page }) => {
// Navigate to markets page
await page.goto('/')
await page.click('a[href="/markets"]')
// Verify page loaded
await expect(page.locator('h1')).toContainText('Markets')
// Search for markets
await page.fill('input[placeholder="Search markets"]', 'election')
// Wait for stable search results instead of sleeping
const results = page.locator('[data-testid="market-card"]')
await expect(results.first()).toBeVisible({ timeout: 5000 })
await expect(results).toHaveCount(5, { timeout: 5000 })
// Verify results contain search term
const firstResult = results.first()
await expect(firstResult).toContainText('election', { ignoreCase: true })
// Filter by status
await page.click('button:has-text("Active")')
// Verify filtered results
await expect(results).toHaveCount(3)
})
test('user can create a new market', async ({ page }) => {
// Login first
await page.goto('/creator-dashboard')
// Fill market creation form
await page.fill('input[name="name"]', 'Test Market')
await page.fill('textarea[name="description"]', 'Test description')
await page.fill('input[name="endDate"]', '2025-12-31')
// Submit form
await page.click('button[type="submit"]')
// Verify success message
await expect(page.locator('text=Market created successfully')).toBeVisible()
// Verify redirect to market page
await expect(page).toHaveURL(/\/markets\/test-market/)
})
```
## 테스트 파일 구성
```
src/
├── components/
│ ├── Button/
│ │ ├── Button.tsx
│ │ ├── Button.test.tsx # Unit tests
│ │ └── Button.stories.tsx # Storybook
│ └── MarketCard/
│ ├── MarketCard.tsx
│ └── MarketCard.test.tsx
├── app/
│ └── api/
│ └── markets/
│ ├── route.ts
│ └── route.test.ts # Integration tests
└── e2e/
├── markets.spec.ts # E2E tests
├── trading.spec.ts
└── auth.spec.ts
```
## 외부 서비스 모킹
### Supabase Mock
```typescript
jest.mock('@/lib/supabase', () => ({
supabase: {
from: jest.fn(() => ({
select: jest.fn(() => ({
eq: jest.fn(() => Promise.resolve({
data: [{ id: 1, name: 'Test Market' }],
error: null
}))
}))
}))
}
}))
```
### Redis Mock
```typescript
jest.mock('@/lib/redis', () => ({
searchMarketsByVector: jest.fn(() => Promise.resolve([
{ slug: 'test-market', similarity_score: 0.95 }
])),
checkRedisHealth: jest.fn(() => Promise.resolve({ connected: true }))
}))
```
### OpenAI Mock
```typescript
jest.mock('@/lib/openai', () => ({
generateEmbedding: jest.fn(() => Promise.resolve(
new Array(1536).fill(0.1) // Mock 1536-dim embedding
))
}))
```
## 테스트 커버리지 검증
### 커버리지 리포트 실행
```bash
npm run test:coverage
```
### 커버리지 임계값
```json
{
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}
```
## 흔한 테스트 실수
### 잘못된 예: 구현 세부사항 테스트
```typescript
// Don't test internal state
expect(component.state.count).toBe(5)
```
### 올바른 예: 사용자에게 보이는 동작 테스트
```typescript
// Test what users see
expect(screen.getByText('Count: 5')).toBeInTheDocument()
```
### 잘못된 예: 취약한 셀렉터
```typescript
// Breaks easily
await page.click('.css-class-xyz')
```
### 올바른 예: 시맨틱 셀렉터
```typescript
// Resilient to changes
await page.click('button:has-text("Submit")')
await page.click('[data-testid="submit-button"]')
```
### 잘못된 예: 테스트 격리 없음
```typescript
// Tests depend on each other
test('creates user', () => { /* ... */ })
test('updates same user', () => { /* depends on previous test */ })
```
### 올바른 예: 독립적인 테스트
```typescript
// Each test sets up its own data
test('creates user', () => {
const user = createTestUser()
// Test logic
})
test('updates user', () => {
const user = createTestUser()
// Update logic
})
```
## 지속적 테스트
### 개발 중 Watch 모드
```bash
npm test -- --watch
# Tests run automatically on file changes
```
### Pre-Commit Hook
```bash
# Runs before every commit
npm test && npm run lint
```
### CI/CD 통합
```yaml
# GitHub Actions
- name: Run Tests
run: npm test -- --coverage
- name: Upload Coverage
uses: codecov/codecov-action@v3
```
## 모범 사례
1. **테스트 먼저 작성** - 항상 TDD
2. **테스트당 하나의 Assert** - 단일 동작에 집중
3. **설명적인 테스트 이름** - 무엇을 테스트하는지 설명
4. **Arrange-Act-Assert** - 명확한 테스트 구조
5. **외부 의존성 모킹** - 단위 테스트 격리
6. **엣지 케이스 테스트** - null, undefined, 빈 값, 큰 값
7. **에러 경로 테스트** - 정상 경로만이 아닌
8. **테스트 속도 유지** - 단위 테스트 각 50ms 미만
9. **테스트 후 정리** - 부작용 없음
10. **커버리지 리포트 검토** - 누락 부분 식별
## 성공 지표
- 80% 이상의 코드 커버리지 달성
- 모든 테스트 통과 (그린)
- 건너뛴 테스트나 비활성화된 테스트 없음
- 빠른 테스트 실행 (단위 테스트 30초 미만)
- E2E 테스트가 핵심 사용자 플로우를 커버
- 테스트가 프로덕션 이전에 버그를 포착
---
**기억하세요**: 테스트는 선택 사항이 아닙니다. 테스트는 자신감 있는 리팩터링, 빠른 개발, 그리고 프로덕션 안정성을 가능하게 하는 안전망입니다.

View File

@@ -0,0 +1,127 @@
---
name: verification-loop
description: "Claude Code 세션을 위한 포괄적인 검증 시스템."
origin: ECC
---
# 검증 루프 스킬
Claude Code 세션을 위한 포괄적인 검증 시스템.
## 사용 시점
다음 상황에서 이 스킬을 호출하세요:
- 기능 또는 주요 코드 변경을 완료한 후
- PR을 생성하기 전
- 품질 게이트가 통과하는지 확인하고 싶을 때
- 리팩터링 후
## 검증 단계
### 단계 1: 빌드 검증
```bash
# Check if project builds
npm run build 2>&1 | tail -20
# OR
pnpm build 2>&1 | tail -20
```
빌드가 실패하면 계속하기 전에 중단하고 수정합니다.
### 단계 2: 타입 검사
```bash
# TypeScript projects
npx tsc --noEmit 2>&1 | head -30
# Python projects
pyright . 2>&1 | head -30
```
모든 타입 에러를 보고합니다. 중요한 것은 계속하기 전에 수정합니다.
### 단계 3: 린트 검사
```bash
# JavaScript/TypeScript
npm run lint 2>&1 | head -30
# Python
ruff check . 2>&1 | head -30
```
### 단계 4: 테스트 스위트
```bash
# Run tests with coverage
npm run test -- --coverage 2>&1 | tail -50
# Check coverage threshold
# Target: 80% minimum
```
보고 항목:
- 전체 테스트: X
- 통과: X
- 실패: X
- 커버리지: X%
### 단계 5: 보안 스캔
```bash
# Check for secrets
grep -rn "sk-" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
grep -rn "api_key" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
# Check for console.log
grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | head -10
```
### 단계 6: Diff 리뷰
```bash
# Show what changed
git diff --stat
git diff --name-only
git diff --cached --name-only
```
각 변경된 파일에서 다음을 검토합니다:
- 의도하지 않은 변경
- 누락된 에러 처리
- 잠재적 엣지 케이스
## 출력 형식
모든 단계를 실행한 후 검증 보고서를 생성합니다:
```
VERIFICATION REPORT
==================
Build: [PASS/FAIL]
Types: [PASS/FAIL] (X errors)
Lint: [PASS/FAIL] (X warnings)
Tests: [PASS/FAIL] (X/Y passed, Z% coverage)
Security: [PASS/FAIL] (X issues)
Diff: [X files changed]
Overall: [READY/NOT READY] for PR
Issues to Fix:
1. ...
2. ...
```
## 연속 모드
긴 세션에서는 15분마다 또는 주요 변경 후에 검증을 실행합니다:
```markdown
Set a mental checkpoint:
- After completing each function
- After finishing a component
- Before moving to next task
Run: /verify
```
## Hook과의 통합
이 스킬은 PostToolUse Hook을 보완하지만 더 깊은 검증을 제공합니다.
Hook은 즉시 문제를 포착하고, 이 스킬은 포괄적인 검토를 제공합니다.

View File

@@ -1,6 +1,6 @@
# Everything Claude Code (ECC) — 智能体指令
这是一个**生产就绪的 AI 编码插件**,提供 13 个专业智能体、50+ 项技能、33 条命令以及用于软件开发的自动化钩子工作流。
这是一个**生产就绪的 AI 编码插件**,提供 16 个专业代理、65+ 项技能、40 条命令以及自动化钩子工作流,用于软件开发
## 核心原则
@@ -12,7 +12,7 @@
## 可用智能体
| 智能体 | 目的 | 何时使用 |
| 代理 | 用途 | 使用时机 |
|-------|---------|-------------|
| planner | 实施规划 | 复杂功能、重构 |
| architect | 系统设计与可扩展性 | 架构决策 |
@@ -21,12 +21,15 @@
| security-reviewer | 漏洞检测 | 提交前、敏感代码 |
| build-error-resolver | 修复构建/类型错误 | 构建失败时 |
| e2e-runner | 端到端 Playwright 测试 | 关键用户流程 |
| refactor-cleaner | 清理无用代码 | 代码维护 |
| doc-updater | 文档代码地图更新 | 更新文档 |
| refactor-cleaner | 死代码清理 | 代码维护 |
| doc-updater | 文档代码映射更新 | 更新文档 |
| go-reviewer | Go 代码审查 | Go 项目 |
| go-build-resolver | Go 构建错误 | Go 构建失败 |
| go-build-resolver | Go 构建错误 | Go 构建失败 |
| database-reviewer | PostgreSQL/Supabase 专家 | 模式设计、查询优化 |
| python-reviewer | Python 代码审查 | Python 项目 |
| chief-of-staff | 沟通分流与草稿 | 多渠道电子邮件、Slack、LINE、Messenger |
| loop-operator | 自主循环执行 | 安全运行循环、监控停滞、干预 |
| harness-optimizer | 线束配置调优 | 可靠性、成本、吞吐量 |
## 智能体编排
@@ -37,6 +40,9 @@
* 错误修复或新功能 → **tdd-guide**
* 架构决策 → **architect**
* 安全敏感代码 → **security-reviewer**
* 多渠道沟通分流 → **chief-of-staff**
* 自主循环 / 循环监控 → **loop-operator**
* 线束配置可靠性及成本 → **harness-optimizer**
对于独立操作使用并行执行 — 同时启动多个智能体。
@@ -94,10 +100,15 @@
## 开发工作流
1. **规划** — 使用 planner 智能体,识别依赖和风险,分解为阶段
2. **TDD** — 使用 tdd-guide 智能体,先写测试,实现重构
3. **审** — 立即使用 code-reviewer 智能体,解决 CRITICAL/HIGH 问题
4. **提交** — 约定式提交格式,全面的 PR 摘要
1. **规划** — 使用规划代理,识别依赖关系和风险,分阶段推进
2. **测试驱动开发** — 使用 tdd-guide 代理,先写测试,实现重构
3. **** — 立即使用代码评审代理,解决 CRITICAL/HIGH 级别的问题
4. **在适当位置记录知识**
* 个人调试笔记、偏好和临时上下文 → 自动记忆
* 团队/项目知识架构决策、API 变更、操作手册)→ 项目现有文档结构
* 如果当前任务已生成相关文档或代码注释,请勿在其他地方重复相同信息
* 如果没有明显的项目文档位置,在创建新的顶层文件前先询问
5. **提交** — 采用约定式提交格式,提供全面的 PR 摘要
## Git 工作流
@@ -123,8 +134,8 @@
```
agents/ — 13 specialized subagents
skills/ — 50+ workflow skills and domain knowledge
commands/ — 33 slash commands
skills/ — 65+ workflow skills and domain knowledge
commands/ — 40 slash commands
hooks/ — Trigger-based automations
rules/ — Always-follow guidelines (common + per-language)
scripts/ — Cross-platform Node.js utilities

View File

@@ -71,14 +71,13 @@
## 归属
行为准则改编自 \[Contributor Covenant]\[homepage]
版本 2.0,可在
https://www.contributor-covenant.org/version/2/0/code\_of\_conduct.html 获取。
本行为准则改编自 \[贡献者公约]\[homepage] 2.0 版本,可访问
<https://www.contributor-covenant.org/version/2/0/code_of_conduct.html> 获取。
社区影响指南的灵感来源于 [Mozilla 的行为准则执行阶梯](https://github.com/mozilla/diversity)。
[homepage]: https://www.contributor-covenant.org
关本行为准则常见问题解答,请参阅常见问题解答
https://www.contributor-covenant.org/faq翻译版本可在
https://www.contributor-covenant.org/translations 获取
本行为准则常见问题解答,请参阅 FAQ 页面
<https://www.contributor-covenant.org/faq>。其他语言翻译版本可在
<https://www.contributor-covenant.org/translations> 查阅

View File

@@ -10,6 +10,7 @@
* [贡献智能体](#贡献智能体)
* [贡献钩子](#贡献钩子)
* [贡献命令](#贡献命令)
* [跨平台与翻译](#跨平台与翻译)
* [拉取请求流程](#拉取请求流程)
***
@@ -349,6 +350,29 @@ description: 在 /help 中显示的简要描述
***
## 跨平台与翻译
### 技能子集 (Codex 和 Cursor)
ECC 为其他平台提供了技能子集:
* **Codex:** `.agents/skills/` — `agents/openai.yaml` 中列出的技能会被 Codex 加载。
* **Cursor:** `.cursor/skills/` — 为 Cursor 打包了一个技能子集。
当您**添加一个新技能**,并且希望它在 Codex 或 Cursor 上可用时:
1. 像往常一样,在 `skills/your-skill-name/` 下添加该技能。
2. 如果它应该在 **Codex** 上可用,请将其添加到 `.agents/skills/`(复制技能目录或添加引用),并在需要时确保它在 `agents/openai.yaml` 中被引用。
3. 如果它应该在 **Cursor** 上可用,请根据 Cursor 的布局,将其添加到 `.cursor/skills/` 下。
请参考这些目录中现有技能的结构。保持这些子集同步是手动操作;如果您更新了它们,请在您的 PR 中说明。
### 翻译
翻译文件位于 `docs/` 下(例如 `docs/zh-CN`、`docs/zh-TW`、`docs/ja-JP`)。如果您更改了已被翻译的智能体、命令或技能,请考虑更新相应的翻译文件,或创建一个问题,以便维护者或翻译人员可以更新它们。
***
## 拉取请求流程
### 1. PR 标题格式

View File

@@ -1,4 +1,4 @@
**语言:** [English](../../README.md) | [繁體中文](../zh-TW/README.md) | [简体中文](README.md)
**语言:** English | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md)
# Everything Claude Code
@@ -14,9 +14,10 @@
![Python](https://img.shields.io/badge/-Python-3776AB?logo=python\&logoColor=white)
![Go](https://img.shields.io/badge/-Go-00ADD8?logo=go\&logoColor=white)
![Java](https://img.shields.io/badge/-Java-ED8B00?logo=openjdk\&logoColor=white)
![Perl](https://img.shields.io/badge/-Perl-39457E?logo=perl\&logoColor=white)
![Markdown](https://img.shields.io/badge/-Markdown-000000?logo=markdown\&logoColor=white)
> **5+ stars** | **6+ forks** | **30位贡献者** | **支持6种语言** | **Anthropic黑客马拉松获胜者**
> **50K+ stars** | **6K+ forks** | **30 contributors** | **5 languages supported** | **Anthropic Hackathon Winner**
***
@@ -24,7 +25,7 @@
**🌐 语言 / 语言 / 語言**
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md)
[**English**](../../README.md) | [简体中文](../../README.zh-CN.md) | [繁體中文](../zh-TW/README.md) | [日本語](../ja-JP/README.md) | [한국어](../ko-KR/README.md)
</div>
@@ -38,24 +39,6 @@
***
## 采用与分发
向赞助商、平台或生态系统合作伙伴展示 ECC 时,请使用这些实时信号:
* **主包安装量:** npm 上的 [`ecc-universal`](https://www.npmjs.com/package/ecc-universal)
* **安全伴侣安装量:** npm 上的 [`ecc-agentshield`](https://www.npmjs.com/package/ecc-agentshield)
* **GitHub 应用分发:** [ECC 工具市场列表](https://github.com/marketplace/ecc-tools)
* **自动化月度指标问题:** 由 `.github/workflows/monthly-metrics.yml` 驱动
* **仓库采用信号:** 本 README 顶部的 stars/forks/contributors 徽章
Claude Code 插件安装的下载计数目前尚未作为公共 API 公开。对于合作伙伴报告,请将 npm 指标与 GitHub 应用安装量以及仓库流量/分支增长相结合。
有关赞助商通话的指标清单和命令片段,请参阅 [`docs/business/metrics-and-sponsorship.md`](../business/metrics-and-sponsorship.md)。
[**赞助 ECC**](https://github.com/sponsors/affaan-m) | [赞助层级](SPONSORS.md) | [赞助计划](SPONSORING.md)
***
## 指南
此仓库仅包含原始代码。指南解释了一切。
@@ -64,18 +47,18 @@ Claude Code 插件安装的下载计数目前尚未作为公共 API 公开。对
<tr>
<td width="50%">
<a href="https://x.com/affaanmustafa/status/2012378465664745795">
<img src="https://github.com/user-attachments/assets/1a471488-59cc-425b-8345-5245c7efbcef" alt="The Shorthand Guide to Everything Claude Code" />
<img src="https://github.com/user-attachments/assets/1a471488-59cc-425b-8345-5245c7efbcef" alt="Claude Code 的速记指南/>
</a>
</td>
<td width="50%">
<a href="https://x.com/affaanmustafa/status/2014040193557471352">
<img src="https://github.com/user-attachments/assets/c9ca43bc-b149-427f-b551-af6840c368f0" alt="The Longform Guide to Everything Claude Code" />
<img src="https://github.com/user-attachments/assets/c9ca43bc-b149-427f-b551-af6840c368f0" alt="Claude Code 的详细指南" />
</a>
</td>
</tr>
<tr>
<td align="center"><b>Shorthand Guide</b><br/>Setup, foundations, philosophy. <b>Read this first.</b></td>
<td align="center"><b>Longform Guide</b><br/>Token optimization, memory persistence, evals, parallelization.</td>
<td align="center"><b>Shorthand Guide</b><br/>设置、基础、理念。 <b>先阅读此部分。</b></td>
<td align="center"><b>详细指南</b><br/>令牌优化、记忆持久化、评估、并行化。</td>
</tr>
</table>
@@ -173,9 +156,9 @@ git clone https://github.com/affaan-m/everything-claude-code.git
cd everything-claude-code
# Recommended: use the installer (handles common + language rules safely)
./install.sh typescript # or python or golang
./install.sh typescript # or python or golang or swift or php
# You can pass multiple languages:
# ./install.sh typescript python golang
# ./install.sh typescript python golang swift php
# or target cursor:
# ./install.sh --target cursor typescript
# or target antigravity:
@@ -258,137 +241,143 @@ everything-claude-code/
| |-- plugin.json # 插件元数据和组件路径
| |-- marketplace.json # 用于 /plugin marketplace add 的市场目录
|
|-- agents/ # 用于委任务的专用子代理
|-- agents/ # 用于委任务的专用子代理
| |-- planner.md # 功能实现规划
| |-- architect.md # 系统设计决策
| |-- architect.md # 系统架构设计决策
| |-- tdd-guide.md # 测试驱动开发
| |-- code-reviewer.md # 质量安全审查
| |-- code-reviewer.md # 质量安全代码审查
| |-- security-reviewer.md # 漏洞分析
| |-- build-error-resolver.md
| |-- e2e-runner.md # Playwright E2E 测试
| |-- e2e-runner.md # Playwright 端到端测试
| |-- refactor-cleaner.md # 无用代码清理
| |-- doc-updater.md # 文档同步
| |-- go-reviewer.md # Go 代码审查
| |-- go-build-resolver.md # Go 构建错误修复
| |-- python-reviewer.md # Python 代码审查 (新增)
| |-- database-reviewer.md # 数据库 / Supabase 审查 (新增)
| |-- python-reviewer.md # Python 代码审查新增
| |-- database-reviewer.md # 数据库/Supabase 审查新增
|
|-- skills/ # 工作流定义领域知识
| |-- coding-standards/ # 语言最佳实践
| |-- clickhouse-io/ # ClickHouse 分析、查询数据工程
| |-- backend-patterns/ # API、数据库缓存模式
|-- skills/ # 工作流定义领域知识
| |-- coding-standards/ # 语言最佳实践
| |-- clickhouse-io/ # ClickHouse 分析、查询数据工程
| |-- backend-patterns/ # API、数据库缓存模式
| |-- frontend-patterns/ # React、Next.js 模式
| |-- frontend-slides/ # HTML 幻灯片和 PPTX 转 Web 演示流程 (新增)
| |-- article-writing/ # 使用指定风格进行长文写作,避免通用 AI 语气 (新增)
| |-- content-engine/ # 多平台内容生成与复用工作流 (新增)
| |-- market-research/ # 带来源引用的市场、竞品投资研究 (新增)
| |-- investor-materials/ # 融资演示文稿、单页、备忘录财务模型 (新增)
| |-- investor-outreach/ # 个性化融资外联与跟进 (新增)
| |-- continuous-learning/ # 从会话中自动提取模式 (Longform Guide)
| |-- frontend-slides/ # HTML 幻灯片和 PPTX 转 Web 演示工作流(新增
| |-- article-writing/ # 按指定写作风格撰写长文而不使用通用 AI 语气新增
| |-- content-engine/ # 多平台内容生成与内容复用工作流新增
| |-- market-research/ # 带来源引用的市场、竞品投资研究新增
| |-- investor-materials/ # 融资演示文稿、单页材料、备忘录财务模型新增
| |-- investor-outreach/ # 个性化融资沟通与跟进新增
| |-- continuous-learning/ # 从会话中自动提取模式(长文指南)
| |-- continuous-learning-v2/ # 基于直觉的学习与置信度评分
| |-- iterative-retrieval/ # 子代理渐进式上下文优化
| |-- strategic-compact/ # 手动压缩建议 (Longform Guide)
| |-- iterative-retrieval/ # 子代理渐进式上下文优化
| |-- strategic-compact/ # 手动压缩建议(长文指南)
| |-- tdd-workflow/ # TDD 方法论
| |-- security-review/ # 安全检查清单
| |-- eval-harness/ # 验证循环评估 (Longform Guide)
| |-- verification-loop/ # 持续验证 (Longform Guide)
| |-- golang-patterns/ # Go 语言惯用法和最佳实践
| |-- golang-testing/ # Go 测试模式、TDD、基准测试
| |-- cpp-coding-standards/ # 来自 C++ Core Guidelines 的 C++ 编码规范 (新增)
| |-- cpp-testing/ # 使用 GoogleTest、CMake/CTest 的 C++ 测试 (新增)
| |-- django-patterns/ # Django 模式、模型和视图 (新增)
| |-- django-security/ # Django 安全最佳实践 (新增)
| |-- django-tdd/ # Django TDD 工作流 (新增)
| |-- django-verification/ # Django 验证循环 (新增)
| |-- python-patterns/ # Python 惯用法和最佳实践 (新增)
| |-- python-testing/ # 使用 pytest 的 Python 测试 (新增)
| |-- springboot-patterns/ # Java Spring Boot 模式 (新增)
| |-- springboot-security/ # Spring Boot 安全 (新增)
| |-- springboot-tdd/ # Spring Boot TDD (新增)
| |-- springboot-verification/ # Spring Boot 验证流程 (新增)
| |-- configure-ecc/ # 交互式安装向导 (新增)
| |-- security-scan/ # AgentShield 安全审计集成 (新增)
| |-- java-coding-standards/ # Java 编码规范 (新增)
| |-- jpa-patterns/ # JPA/Hibernate 模式 (新增)
| |-- postgres-patterns/ # PostgreSQL 优化模式 (新增)
| |-- nutrient-document-processing/ # 使用 Nutrient API 进行文档处理 (新增)
| |-- eval-harness/ # 验证循环评估(长文指南)
| |-- verification-loop/ # 持续验证(长文指南)
| |-- videodb/ # 视频和音频:导入、搜索、编辑、生成与流式处理(新增)
| |-- golang-patterns/ # Go 习惯用法与最佳实践
| |-- golang-testing/ # Go 测试模式、TDD 与基准测试
| |-- cpp-coding-standards/ # 来自 C++ Core Guidelines 的 C++ 编码规范(新增
| |-- cpp-testing/ # 使用 GoogleTest 与 CMake/CTest 的 C++ 测试(新增
| |-- django-patterns/ # Django 模式、模型与视图(新增
| |-- django-security/ # Django 安全最佳实践(新增
| |-- django-tdd/ # Django TDD 工作流(新增
| |-- django-verification/ # Django 验证循环(新增
| |-- python-patterns/ # Python 习惯用法与最佳实践(新增
| |-- python-testing/ # 使用 pytest 的 Python 测试(新增
| |-- springboot-patterns/ # Java Spring Boot 模式(新增
| |-- springboot-security/ # Spring Boot 安全(新增
| |-- springboot-tdd/ # Spring Boot TDD新增
| |-- springboot-verification/ # Spring Boot 验证(新增
| |-- configure-ecc/ # 交互式安装向导(新增
| |-- security-scan/ # AgentShield 安全审计集成(新增
| |-- java-coding-standards/ # Java 编码规范(新增
| |-- jpa-patterns/ # JPA/Hibernate 模式(新增
| |-- postgres-patterns/ # PostgreSQL 优化模式(新增
| |-- nutrient-document-processing/ # 使用 Nutrient API 的文档处理(新增)
| |-- project-guidelines-example/ # 项目专用技能模板
| |-- database-migrations/ # 数据库迁移模式 (Prisma、Drizzle、Django、Go) (新增)
| |-- api-design/ # REST API 设计、分页错误响应 (新增)
| |-- deployment-patterns/ # CI/CD、Docker、健康检查回滚 (新增)
| |-- docker-patterns/ # Docker Compose、网络、卷容器安全 (新增)
| |-- e2e-testing/ # Playwright E2E 模式和 Page Object Model (新增)
| |-- content-hash-cache-pattern/ # 使用 SHA-256 内容哈希进行文件处理缓存 (新增)
| |-- cost-aware-llm-pipeline/ # LLM 成本优化、模型路由预算跟踪 (新增)
| |-- regex-vs-llm-structured-text/ # 文本解析决策框架:正则 vs LLM (新增)
| |-- swift-actor-persistence/ # 使用 Actor 的线程安全 Swift 数据持久化 (新增)
| |-- swift-protocol-di-testing/ # 基于 Protocol 的依赖注入用于可测试 Swift 代码 (新增)
| |-- search-first/ # 先研究再编码的工作流 (新增)
| |-- skill-stocktake/ # 审计技能和命令质量 (新增)
| |-- liquid-glass-design/ # iOS 26 Liquid Glass 设计系统 (新增)
| |-- foundation-models-on-device/ # Apple 设备端 LLM FoundationModels (新增)
| |-- swift-concurrency-6-2/ # Swift 6.2 易用并发模型 (新增)
| |-- autonomous-loops/ # 自动化循环模式顺序流水线、PR 循环、DAG 编排 (新增)
| |-- plankton-code-quality/ # 使用 Plankton hooks 在编写阶段执行代码质量检查 (新增)
| |-- database-migrations/ # 迁移模式Prisma、Drizzle、Django、Go新增
| |-- api-design/ # REST API 设计、分页错误响应新增
| |-- deployment-patterns/ # CI/CD、Docker、健康检查回滚新增
| |-- docker-patterns/ # Docker Compose、网络、卷容器安全新增
| |-- e2e-testing/ # Playwright 端到端模式与页面对象模型(新增
| |-- content-hash-cache-pattern/ # 文件处理中的 SHA-256 内容哈希缓存模式(新增
| |-- cost-aware-llm-pipeline/ # LLM 成本优化、模型路由预算追踪(新增
| |-- regex-vs-llm-structured-text/ # 文本解析决策框架:regex vs LLM新增
| |-- swift-actor-persistence/ # 使用 Actor 的线程安全 Swift 数据持久化新增
| |-- swift-protocol-di-testing/ # 基于 Protocol 的依赖注入用于可测试 Swift 代码新增
| |-- search-first/ # 先研究再编码的工作流新增
| |-- skill-stocktake/ # 审计技能和命令质量新增
| |-- liquid-glass-design/ # iOS 26 Liquid Glass 设计系统新增
| |-- foundation-models-on-device/ # Apple 设备端 LLMFoundationModels新增
| |-- swift-concurrency-6-2/ # Swift 6.2 易用并发新增
| |-- perl-patterns/ # 现代 Perl 5.36+ 习惯用法与最佳实践(新增
| |-- perl-security/ # Perl 安全模式、taint 模式与安全 I/O新增
| |-- perl-testing/ # 使用 Test2::V0、prove、Devel::Cover 的 Perl TDD新增
| |-- autonomous-loops/ # 自主循环模式顺序流水线、PR 循环与 DAG 编排(新增)
| |-- plankton-code-quality/ # 使用 Plankton hooks 的编写阶段代码质量控制(新增)
|
|-- commands/ # 用于快速执行的 Slash 命令
|-- commands/ # 快速执行的斜杠命令
| |-- tdd.md # /tdd - 测试驱动开发
| |-- plan.md # /plan - 实现规划
| |-- e2e.md # /e2e - E2E 测试生成
| |-- code-review.md # /code-review - 代码质量审查
| |-- e2e.md # /e2e - 端到端测试生成
| |-- code-review.md # /code-review - 质量审查
| |-- build-fix.md # /build-fix - 修复构建错误
| |-- refactor-clean.md # /refactor-clean - 删除无用代码
| |-- learn.md # /learn - 会话中提取模式 (Longform Guide)
| |-- learn-eval.md # /learn-eval - 提取、评估并保存模式 (新增)
| |-- checkpoint.md # /checkpoint - 保存验证状态 (Longform Guide)
| |-- verify.md # /verify - 运行验证循环 (Longform Guide)
| |-- refactor-clean.md # /refactor-clean - 无用代码清理
| |-- learn.md # /learn - 会话中提取模式(长文指南)
| |-- learn-eval.md # /learn-eval - 提取、评估并保存模式新增
| |-- checkpoint.md # /checkpoint - 保存验证状态(长文指南)
| |-- verify.md # /verify - 运行验证循环(长文指南)
| |-- setup-pm.md # /setup-pm - 配置包管理器
| |-- go-review.md # /go-review - Go 代码审查 (新增)
| |-- go-test.md # /go-test - Go TDD 工作流 (新增)
| |-- go-build.md # /go-build - 修复 Go 构建错误 (新增)
| |-- skill-create.md # /skill-create - 从 git 历史生成技能 (新增)
| |-- instinct-status.md # /instinct-status - 查看学习到的直觉规则 (新增)
| |-- instinct-import.md # /instinct-import - 导入直觉规则 (新增)
| |-- instinct-export.md # /instinct-export - 导出直觉规则 (新增)
| |-- go-review.md # /go-review - Go 代码审查新增
| |-- go-test.md # /go-test - Go TDD 工作流新增
| |-- go-build.md # /go-build - 修复 Go 构建错误新增
| |-- skill-create.md # /skill-create - 从 git 历史生成技能新增
| |-- instinct-status.md # /instinct-status - 查看学习到的直觉新增
| |-- instinct-import.md # /instinct-import - 导入直觉新增
| |-- instinct-export.md # /instinct-export - 导出直觉新增
| |-- evolve.md # /evolve - 将直觉聚类为技能
| |-- pm2.md # /pm2 - PM2 服务生命周期管理 (新增)
| |-- multi-plan.md # /multi-plan - 多代理任务拆解 (新增)
| |-- multi-execute.md # /multi-execute - 编排多代理工作流 (新增)
| |-- multi-backend.md # /multi-backend - 后端多服务编排 (新增)
| |-- multi-frontend.md # /multi-frontend - 前端多服务编排 (新增)
| |-- multi-workflow.md # /multi-workflow - 通用多服务工作流 (新增)
| |-- pm2.md # /pm2 - PM2 服务生命周期管理新增
| |-- multi-plan.md # /multi-plan - 多代理任务拆解新增
| |-- multi-execute.md # /multi-execute - 编排多代理工作流新增
| |-- multi-backend.md # /multi-backend - 后端多服务编排新增
| |-- multi-frontend.md # /multi-frontend - 前端多服务编排新增
| |-- multi-workflow.md # /multi-workflow - 通用多服务工作流新增
| |-- orchestrate.md # /orchestrate - 多代理协调
| |-- sessions.md # /sessions - 会话历史管理
| |-- eval.md # /eval - 按标准进行评估
| |-- eval.md # /eval - 按标准评估
| |-- test-coverage.md # /test-coverage - 测试覆盖率分析
| |-- update-docs.md # /update-docs - 更新文档
| |-- update-codemaps.md # /update-codemaps - 更新代码地图
| |-- python-review.md # /python-review - Python 代码审查 (新增)
| |-- update-codemaps.md # /update-codemaps - 更新代码映射
| |-- python-review.md # /python-review - Python 代码审查新增
|
|-- rules/ # 必须遵循的规则 (复制到 ~/.claude/rules/)
| |-- README.md # 结构概览和安装指南
|-- rules/ # 必须遵循的规则复制到 ~/.claude/rules/
| |-- README.md # 结构说明与安装指南
| |-- common/ # 与语言无关的原则
| | |-- coding-style.md # 不可变性文件组织
| | |-- git-workflow.md # 提交格式PR 流程
| | |-- testing.md # TDD80% 覆盖率要求
| | |-- performance.md # 模型选择上下文管理
| | |-- patterns.md # 设计模式骨架项目
| | |-- hooks.md # Hook 架构TodoWrite
| | |-- agents.md # 何时委给子代理
| | |-- security.md # 必须执行的安全检查
| |-- typescript/ # TypeScript / JavaScript 专用
| | |-- coding-style.md # 不可变性文件组织
| | |-- git-workflow.md # 提交格式PR 流程
| | |-- testing.md # TDD80% 覆盖率要求
| | |-- performance.md # 模型选择上下文管理
| | |-- patterns.md # 设计模式骨架项目
| | |-- hooks.md # Hook 架构TodoWrite
| | |-- agents.md # 何时委给子代理
| | |-- security.md # 强制安全检查
| |-- typescript/ # TypeScript/JavaScript 专用
| |-- python/ # Python 专用
| |-- golang/ # Go 专用
| |-- swift/ # Swift 专用
| |-- php/ # PHP 专用(新增)
|
|-- hooks/ # 基于触发器的自动化
| |-- README.md # Hook 文档、示例自定义指南
| |-- hooks.json # 所有 Hook 配置 (PreToolUse、PostToolUse、Stop 等)
| |-- memory-persistence/ # 会话生命周期 Hook (Longform Guide)
| |-- strategic-compact/ # 压缩建议 (Longform Guide)
| |-- README.md # Hook 文档、示例自定义指南
| |-- hooks.json # 所有 Hook 配置PreToolUse、PostToolUse、Stop 等
| |-- memory-persistence/ # 会话生命周期 Hook(长文指南)
| |-- strategic-compact/ # 压缩建议(长文指南)
|
|-- scripts/ # 跨平台 Node.js 脚本 (新增)
| |-- lib/ # 共工具
| | |-- utils.js # 跨平台文件 / 路径 / 系统工具
|-- scripts/ # 跨平台 Node.js 脚本新增
| |-- lib/ # 共工具
| | |-- utils.js # 跨平台文件/路径/系统工具
| | |-- package-manager.js # 包管理器检测与选择
| |-- hooks/ # Hook 实现
| | |-- session-start.js # 会话开始时加载上下文
@@ -398,28 +387,28 @@ everything-claude-code/
| | |-- evaluate-session.js # 从会话中提取模式
| |-- setup-package-manager.js # 交互式包管理器设置
|
|-- tests/ # 测试套件 (新增)
|-- tests/ # 测试套件新增
| |-- lib/ # 库测试
| |-- hooks/ # Hook 测试
| |-- run-all.js # 运行所有测试
|
|-- contexts/ # 动态系统提示上下文注入 (Longform Guide)
|-- contexts/ # 动态系统提示上下文(长文指南)
| |-- dev.md # 开发模式上下文
| |-- review.md # 代码审查模式上下文
| |-- research.md # 研究 / 探索模式上下文
| |-- research.md # 研究/探索模式上下文
|
|-- examples/ # 示例配置会话
|-- examples/ # 示例配置会话
| |-- CLAUDE.md # 项目级配置示例
| |-- user-CLAUDE.md # 用户级配置示例
| |-- saas-nextjs-CLAUDE.md # 实 SaaS 示例 (Next.js + Supabase + Stripe)
| |-- go-microservice-CLAUDE.md # 实 Go 微服务示例 (gRPC + PostgreSQL)
| |-- django-api-CLAUDE.md # 实 Django REST API 示例 (DRF + Celery)
| |-- rust-api-CLAUDE.md # 实 Rust API 示例 (Axum + SQLx + PostgreSQL) (新增)
| |-- saas-nextjs-CLAUDE.md # 实 SaaS 示例Next.js + Supabase + Stripe
| |-- go-microservice-CLAUDE.md # 实 Go 微服务示例gRPC + PostgreSQL
| |-- django-api-CLAUDE.md # 实 Django REST API 示例DRF + Celery
| |-- rust-api-CLAUDE.md # 实 Rust API 示例Axum + SQLx + PostgreSQL新增
|
|-- mcp-configs/ # MCP 服务器配置
| |-- mcp-servers.json # GitHub、Supabase、Vercel、Railway 等
|
|-- marketplace.json # 自托管市场配置 (用于 /plugin marketplace add)
|-- marketplace.json # 自托管市场配置用于 /plugin marketplace add
```
***
@@ -571,23 +560,24 @@ Duplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded fil
这将使您能够立即访问所有命令、代理、技能和钩子。
> **注意:** Claude Code 插件系统不支持通过插件分发 `rules`[上游限制](https://code.claude.com/docs/en/plugins-reference))。你需要手动安装规则:
> **注意:** Claude Code 插件系统不支持通过插件分发 `rules` ([上游限制](https://code.claude.com/docs/en/plugins-reference))。您需要手动安装规则:
>
> ```bash
> # 首先克隆仓库
> git clone https://github.com/affaan-m/everything-claude-code.git
>
> # 选项 A用户级规则用于所有项目)
> # 选项 A用户级规则用于所有项目)
> mkdir -p ~/.claude/rules
> cp -r everything-claude-code/rules/common/* ~/.claude/rules/
> cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 选择的技术栈
> cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # 选择的技术栈
> cp -r everything-claude-code/rules/python/* ~/.claude/rules/
> cp -r everything-claude-code/rules/golang/* ~/.claude/rules/
> cp -r everything-claude-code/rules/php/* ~/.claude/rules/
>
> # 选项 B项目级规则用于当前项目)
> # 选项 B项目级规则用于当前项目)
> mkdir -p .claude/rules
> cp -r everything-claude-code/rules/common/* .claude/rules/
> cp -r everything-claude-code/rules/typescript/* .claude/rules/ # 选择的技术栈
> cp -r everything-claude-code/rules/typescript/* .claude/rules/ # 选择的技术栈
> ```
***
@@ -608,6 +598,7 @@ cp -r everything-claude-code/rules/common/* ~/.claude/rules/
cp -r everything-claude-code/rules/typescript/* ~/.claude/rules/ # pick your stack
cp -r everything-claude-code/rules/python/* ~/.claude/rules/
cp -r everything-claude-code/rules/golang/* ~/.claude/rules/
cp -r everything-claude-code/rules/php/* ~/.claude/rules/
# Copy commands
cp everything-claude-code/commands/*.md ~/.claude/commands/
@@ -691,6 +682,8 @@ rules/
typescript/ # TS/JS specific patterns and tools
python/ # Python specific patterns and tools
golang/ # Go specific patterns and tools
swift/ # Swift specific patterns and tools
php/ # PHP specific patterns and tools
```
有关安装和结构详情,请参阅 [`rules/README.md`](rules/README.md)。
@@ -748,7 +741,7 @@ rules/
## ❓ 常见问题
<details>
<summary><b>How do I check which agents/commands are installed?</b></summary>
<summary><b>如何检查已安装的代理/命令?</b></summary>
```bash
/plugin list everything-claude-code@everything-claude-code
@@ -759,14 +752,40 @@ rules/
</details>
<details>
<summary><b>My hooks aren't working / I see "Duplicate hooks file" errors</b></summary>
<summary><b>我的钩子不工作 / 我看到“重复钩子文件”错误</b></summary>
这是最常见的问题。**不要在 `.claude-plugin/plugin.json` 中添加 `"hooks"` 字段。** Claude Code v2.1+ 会自动从已安装的插件加载 `hooks/hooks.json`。显式声明它会导致重复检测错误。参见 [#29](https://github.com/affaan-m/everything-claude-code/issues/29), [#52](https://github.com/affaan-m/everything-claude-code/issues/52), [#103](https://github.com/affaan-m/everything-claude-code/issues/103)。
</details>
<details>
<summary><b>My context window is shrinking / Claude is running out of context</b></summary>
<summary><b>我能否在自定义API端点或模型网关上使用ECC与Claude Code</b></summary>
是的。ECC 不会硬编码 Anthropic 托管的传输设置。它通过 Claude Code 正常的 CLI/插件接口在本地运行,因此可以与以下系统配合工作:
* Anthropic 托管的 Claude Code
* 使用 `ANTHROPIC_BASE_URL``ANTHROPIC_AUTH_TOKEN` 的官方 Claude Code 网关设置
* 兼容的自定义端点,这些端点能理解 Anthropic API 并符合 Claude Code 的预期
最小示例:
```bash
export ANTHROPIC_BASE_URL=https://your-gateway.example.com
export ANTHROPIC_AUTH_TOKEN=your-token
claude
```
如果您的网关重新映射模型名称,请在 Claude Code 中配置,而不是在 ECC 中。一旦 `claude` CLI 已经正常工作ECC 的钩子、技能、命令和规则就与模型提供商无关。
官方参考资料:
* [Claude Code LLM 网关文档](https://docs.anthropic.com/en/docs/claude-code/llm-gateway)
* [Claude Code 模型配置文档](https://docs.anthropic.com/en/docs/claude-code/model-config)
</details>
<details>
<summary><b>我的上下文窗口正在缩小 / Claude 即将耗尽上下文</b></summary>
太多的 MCP 服务器会消耗你的上下文。每个 MCP 工具描述都会消耗你 200k 窗口的令牌,可能将其减少到约 70k。
@@ -784,7 +803,7 @@ rules/
</details>
<details>
<summary><b>Can I use only some components (e.g., just agents)?</b></summary>
<summary><b>我可以只使用某些组件(例如,仅代理)吗?</b></summary>
是的。使用选项 2手动安装并仅复制你需要的部分
@@ -801,7 +820,7 @@ cp -r everything-claude-code/rules/common/* ~/.claude/rules/
</details>
<details>
<summary><b>Does this work with Cursor / OpenCode / Codex / Antigravity?</b></summary>
<summary><b>这能与 Cursor / OpenCode / Codex / Antigravity 一起使用吗?</b></summary>
是的。ECC 是跨平台的:
@@ -814,7 +833,7 @@ cp -r everything-claude-code/rules/common/* ~/.claude/rules/
</details>
<details>
<summary><b>How do I contribute a new skill or agent?</b></summary>
<summary><b>我如何贡献新技能或代理?</b></summary>
参见 [CONTRIBUTING.md](CONTRIBUTING.md)。简短版本:
@@ -858,11 +877,11 @@ node tests/hooks/hooks.test.js
### 贡献想法
* 特定语言技能 (Rust, C#, Swift, Kotlin) — Go, Python, Java 已包含
* 特定框架配置 (Rails, Laravel, FastAPI, NestJS) — Django, Spring Boot 已包含
* DevOps 智能体 (Kubernetes, Terraform, AWS, Docker)
* 测试策略 (不同框架,视觉回归)
* 领域特定知识 (ML, 数据工程, 移动端)
* 特定语言技能Rust, C#, Kotlin, Java—— Go, Python, Perl, Swift 和 TypeScript 已包含在内
* 特定框架配置Rails, Laravel, FastAPI, NestJS)—— Django, Spring Boot 已包含在内
* DevOps 代理(Kubernetes, Terraform, AWS, Docker
* 测试策略不同框架,视觉回归
* 特定领域知识ML数据工程移动端
***
@@ -875,18 +894,18 @@ ECC 提供**完整的 Cursor IDE 支持**,包括为 Cursor 原生格式适配
```bash
# Install for your language(s)
./install.sh --target cursor typescript
./install.sh --target cursor python golang swift
./install.sh --target cursor python golang swift php
```
### 包含内容
| 组件 | 数量 | 详情 |
|-----------|-------|---------|
| 钩子事件 | 15 | sessionStart, beforeShellExecution, afterFileEdit, beforeMCPExecution, beforeSubmitPrompt, 以及另外 10 个 |
| 钩子脚本 | 16 | 通过共享适配器委托给 `scripts/hooks/`轻量 Node.js 脚本 |
| 规则 | 29 | 9 通用规则 (alwaysApply) + 20 条语言特定规则 (TypeScript, Python, Go, Swift) |
| 代理 | 共享 | 通过根目录下的 AGENTS.md Cursor 原生读取) |
| 技能 | 共享 + 捆绑 | 通过根目录下的 AGENTS.md 和用于翻译补充的 `.cursor/skills/` |
| 钩子事件 | 15 | sessionStart, beforeShellExecution, afterFileEdit, beforeMCPExecution, beforeSubmitPrompt 10 个 |
| 钩子脚本 | 16 | 通过共享适配器委托给 `scripts/hooks/`精简 Node.js 脚本 |
| 规则 | 34 | 9 通用规则alwaysApply+ 25 个语言特定规则TypeScript, Python, Go, Swift, PHP |
| 代理 | 共享 | 通过根目录下的 AGENTS.md Cursor 原生读取) |
| 技能 | 共享 + 捆绑 | 通过根目录下的 AGENTS.md 和 `.cursor/skills/` 用于翻译后的补充内容 |
| 命令 | 共享 | `.cursor/commands/`(如果已安装) |
| MCP 配置 | 共享 | `.cursor/mcp.json`(如果已安装) |
@@ -928,29 +947,31 @@ ECC 为 macOS 应用和 CLI 提供 **一流的 Codex 支持**,包括参考配
### 快速开始Codex 应用 + CLI
```bash
# Copy the reference config to your home directory
cp .codex/config.toml ~/.codex/config.toml
# Run Codex CLI in the repo — AGENTS.md is auto-detected
# Run Codex CLI in the repo — AGENTS.md and .codex/ are auto-detected
codex
# Optional: copy the global-safe defaults to your home directory
cp .codex/config.toml ~/.codex/config.toml
```
Codex macOS 应用:
* 将此仓库作为您的工作打开。
* 根目录 `AGENTS.md`自动检测。
* 参考 `.codex/config.toml` 故意不固定 `model``model_provider`,因此 Codex 会使用它自己的当前默认值,除非您显式覆盖
* 可选:将 `.codex/config.toml` 复制到 `~/.codex/config.toml` 以实现 CLI/应用行为一致性
* 将此仓库作为您的工作空间打开。
* 根目录 `AGENTS.md` 会自动检测。
* `.codex/config.toml` `.codex/agents/*.toml` 在保持项目本地时效果最佳
* 参考文件 `.codex/config.toml` 有意未固定 `model``model_provider`因此除非您手动覆盖Codex 将使用其自身的当前默认版本
* 可选:将 `.codex/config.toml` 复制到 `~/.codex/config.toml` 以设置全局默认值;除非您也复制 `.codex/agents/`,否则请将多智能体角色文件保留在项目本地。
### 包含内容
| 组件 | 数量 | 详情 |
|-----------|-------|---------|
| 配置 | 1 | `.codex/config.toml` 权限、MCP 服务器通知配置文件 |
| 配置 | 1 | `.codex/config.toml`— 顶级 approvals/sandbox/web\_search, MCP 服务器通知配置文件 |
| AGENTS.md | 2 | 根目录(通用)+ `.codex/AGENTS.md`Codex 特定补充) |
| 技能 | 16 | `.agents/skills/` 每个技能包含 SKILL.md + agents/openai.yaml |
| MCP 服务器 | 4 | GitHubContext7MemorySequential Thinking基于命令 |
| 技能 | 16 | `.agents/skills/` SKILL.md + agents/openai.yaml 每个技能 |
| MCP 服务器 | 4 | GitHub, Context7, Memory, Sequential Thinking基于命令 |
| 配置文件 | 2 | `strict`(只读沙箱)和 `yolo`(完全自动批准) |
| 代理角色 | 3 | `.codex/agents/` —— explorer, reviewer, docs-researcher |
### 技能
@@ -977,7 +998,24 @@ Codex macOS 应用:
### 关键限制
Codex **尚未提供 Claude 风格的钩子执行对等性**。ECC 在该平台上的强制执行是通过 `AGENTS.md``persistent_instructions` 基于指令实现的,外加沙箱权限
Codex **尚未提供 Claude 风格同等的钩子执行功能**。ECC 在该平台上的强制执行是通过 `AGENTS.md`、可选的 `model_instructions_file` 覆盖以及沙箱/批准设置以指令方式实现的
### 多代理支持
当前的 Codex 版本支持实验性的多代理工作流。
*`.codex/config.toml` 中启用 `features.multi_agent = true`
*`[agents.<name>]` 下定义角色
* 将每个角色指向 `.codex/agents/` 下的一个文件
* 在 CLI 中使用 `/agent` 来检查或引导子代理
ECC 附带了三个示例角色配置:
| 角色 | 目的 |
|------|---------|
| `explorer` | 在进行编辑前进行只读的代码库证据收集 |
| `reviewer` | 正确性、安全性和缺失测试的审查 |
| `docs_researcher` | 在发布/文档更改前进行文档和 API 验证 |
***
@@ -1090,6 +1128,14 @@ npm install ecc-universal
}
```
该 npm 插件条目启用了 ECC 发布的 OpenCode 插件模块(钩子/事件和插件工具)。
它**不会**自动将 ECC 的完整命令/代理/指令目录添加到您的项目配置中。
要获得完整的 ECC OpenCode 设置,您可以:
* 在此仓库内运行 OpenCode或者
* 将捆绑的 `.opencode/` 配置资源复制到您的项目中,并在 `opencode.json` 中连接 `instructions``agent``command` 条目
### 文档
* **迁移指南**`.opencode/MIGRATION.md`
@@ -1105,26 +1151,26 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以
| 功能 | Claude Code | Cursor IDE | Codex CLI | OpenCode |
|---------|------------|------------|-----------|----------|
| **智能体** | 16 | 共享AGENTS.md | 共享AGENTS.md | 12 |
| **代理** | 16 | 共享AGENTS.md | 共享AGENTS.md | 12 |
| **命令** | 40 | 共享 | 基于指令 | 31 |
| **技能** | 65 | 共享 | 10原生格式 | 37 |
| **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 |
| **钩子脚本** | 20+ 脚本 | 16 个脚本DRY 适配器) | 不适用 | 插件钩子 |
| **规则** | 29(通用 + 语言) | 29YAML 前言) | 基于指令 | 13 条指令 |
| **自定义工具** | 通过钩子 | 通过钩子 | 不适用 | 6 个原生工具 |
| **MCP 服务器** | 14 | 共享mcp.json | 4基于命令 | 完整 |
| **钩子脚本** | 20+ 脚本 | 16 个脚本DRY 适配器) | N/A | 插件钩子 |
| **规则** | 34(通用 + 语言) | 34YAML 前言) | 基于指令 | 13 条指令 |
| **自定义工具** | 通过钩子 | 通过钩子 | N/A | 6 个原生工具 |
| **MCP 服务器** | 14 | 共享mcp.json | 4基于命令 | 完整支持 |
| **配置格式** | settings.json | hooks.json + rules/ | config.toml | opencode.json |
| **上下文文件** | CLAUDE.md + AGENTS.md | AGENTS.md | AGENTS.md | AGENTS.md |
| **秘密检测** | 基于钩子 | beforeSubmitPrompt 钩子 | 基于沙箱 | 基于钩子 |
| **自动格式化** | PostToolUse 钩子 | afterFileEdit 钩子 | 不适用 | file.edited 钩子 |
| **自动格式化** | PostToolUse 钩子 | afterFileEdit 钩子 | N/A | file.edited 钩子 |
| **版本** | 插件 | 插件 | 参考配置 | 1.8.0 |
**关键架构决策:**
* 根目录下的 **AGENTS.md** 是通用的跨工具文件(所有 4 个工具读取)
* **AGENTS.md** 在根目录是通用的跨工具文件(所有 4 个工具都能读取)
* **DRY 适配器模式** 让 Cursor 可以重用 Claude Code 的钩子脚本而无需重复
* **技能格式**(带有 YAML 前言的 SKILL.md在 Claude Code、Codex 和 OpenCode 都能工作
* Codex 缺钩子的问题通过 `persistent_instructions`沙箱权限来弥补
* **技能格式**(带有 YAML 前言的 SKILL.md在 Claude Code、Codex 和 OpenCode 都能工作
* Codex 缺钩子功能,通过 `AGENTS.md`、可选的 `model_instructions_file` 覆盖以及沙箱权限来弥补
***

View File

@@ -0,0 +1,446 @@
# 故障排除指南
Everything Claude Code (ECC) 插件的常见问题与解决方案。
## 目录
* [内存与上下文问题](#内存与上下文问题)
* [代理工具故障](#代理工具故障)
* [钩子与工作流错误](#钩子与工作流错误)
* [安装与设置](#安装与设置)
* [性能问题](#性能问题)
* [常见错误信息](#常见错误信息)
* [获取帮助](#获取帮助)
***
## 内存与上下文问题
### 上下文窗口溢出
**症状:** 出现"上下文过长"错误或响应不完整
**原因:**
* 上传的大文件超出令牌限制
* 累积的对话历史记录
* 单次会话中包含多个大型工具输出
**解决方案:**
```bash
# 1. Clear conversation history and start fresh
# Use Claude Code: "New Chat" or Cmd/Ctrl+Shift+N
# 2. Reduce file size before analysis
head -n 100 large-file.log > sample.log
# 3. Use streaming for large outputs
head -n 50 large-file.txt
# 4. Split tasks into smaller chunks
# Instead of: "Analyze all 50 files"
# Use: "Analyze files in src/components/ directory"
```
### 内存持久化失败
**症状:** 代理不记得先前的上下文或观察结果
**原因:**
* 连续学习钩子被禁用
* 观察文件损坏
* 项目检测失败
**解决方案:**
```bash
# Check if observations are being recorded
ls ~/.claude/homunculus/projects/*/observations.jsonl
# Find the current project's hash id
python3 - <<'PY'
import json, os
registry_path = os.path.expanduser("~/.claude/homunculus/projects.json")
with open(registry_path) as f:
registry = json.load(f)
for project_id, meta in registry.items():
if meta.get("root") == os.getcwd():
print(project_id)
break
else:
raise SystemExit("Project hash not found in ~/.claude/homunculus/projects.json")
PY
# View recent observations for that project
tail -20 ~/.claude/homunculus/projects/<project-hash>/observations.jsonl
# Back up a corrupted observations file before recreating it
mv ~/.claude/homunculus/projects/<project-hash>/observations.jsonl \
~/.claude/homunculus/projects/<project-hash>/observations.jsonl.bak.$(date +%Y%m%d-%H%M%S)
# Verify hooks are enabled
grep -r "observe" ~/.claude/settings.json
```
***
## 代理工具故障
### 未找到代理
**症状:** 出现"代理未加载"或"未知代理"错误
**原因:**
* 插件未正确安装
* 代理路径配置错误
* 市场安装与手动安装不匹配
**解决方案:**
```bash
# Check plugin installation
ls ~/.claude/plugins/cache/
# Verify agent exists (marketplace install)
ls ~/.claude/plugins/cache/*/agents/
# For manual install, agents should be in:
ls ~/.claude/agents/ # Custom agents only
# Reload plugin
# Claude Code → Settings → Extensions → Reload
```
### 工作流执行挂起
**症状:** 代理启动但从未完成
**原因:**
* 代理逻辑中存在无限循环
* 等待用户输入时被阻塞
* 等待 API 响应时网络超时
**解决方案:**
```bash
# 1. Check for stuck processes
ps aux | grep claude
# 2. Enable debug mode
export CLAUDE_DEBUG=1
# 3. Set shorter timeouts
export CLAUDE_TIMEOUT=30
# 4. Check network connectivity
curl -I https://api.anthropic.com
```
### 工具使用错误
**症状:** 出现"工具执行失败"或权限被拒绝
**原因:**
* 缺少依赖项npm、python 等)
* 文件权限不足
* 路径未找到
**解决方案:**
```bash
# Verify required tools are installed
which node python3 npm git
# Fix permissions on hook scripts
chmod +x ~/.claude/plugins/cache/*/hooks/*.sh
chmod +x ~/.claude/plugins/cache/*/skills/*/hooks/*.sh
# Check PATH includes necessary binaries
echo $PATH
```
***
## 钩子与工作流错误
### 钩子未触发
**症状:** 前置/后置钩子未执行
**原因:**
* 钩子未在 settings.json 中注册
* 钩子语法无效
* 钩子脚本不可执行
**解决方案:**
```bash
# Check hooks are registered
grep -A 10 '"hooks"' ~/.claude/settings.json
# Verify hook files exist and are executable
ls -la ~/.claude/plugins/cache/*/hooks/
# Test hook manually
bash ~/.claude/plugins/cache/*/hooks/pre-bash.sh <<< '{"command":"echo test"}'
# Re-register hooks (if using plugin)
# Disable and re-enable plugin in Claude Code settings
```
### Python/Node 版本不匹配
**症状:** 出现"未找到 python3"或"node: 命令未找到"
**原因:**
* 缺少 Python/Node 安装
* PATH 未配置
* Python 版本错误Windows
**解决方案:**
```bash
# Install Python 3 (if missing)
# macOS: brew install python3
# Ubuntu: sudo apt install python3
# Windows: Download from python.org
# Install Node.js (if missing)
# macOS: brew install node
# Ubuntu: sudo apt install nodejs npm
# Windows: Download from nodejs.org
# Verify installations
python3 --version
node --version
npm --version
# Windows: Ensure python (not python3) works
python --version
```
### 开发服务器拦截器误报
**症状:** 钩子拦截了提及"dev"的合法命令
**原因:**
* Heredoc 内容触发模式匹配
* 参数中包含"dev"的非开发命令
**解决方案:**
```bash
# This is fixed in v1.8.0+ (PR #371)
# Upgrade plugin to latest version
# Workaround: Wrap dev servers in tmux
tmux new-session -d -s dev "npm run dev"
tmux attach -t dev
# Disable hook temporarily if needed
# Edit ~/.claude/settings.json and remove pre-bash hook
```
***
## 安装与设置
### 插件未加载
**症状:** 安装后插件功能不可用
**原因:**
* 市场缓存未更新
* Claude Code 版本不兼容
* 插件文件损坏
**解决方案:**
```bash
# Inspect the plugin cache before changing it
ls -la ~/.claude/plugins/cache/
# Back up the plugin cache instead of deleting it in place
mv ~/.claude/plugins/cache ~/.claude/plugins/cache.backup.$(date +%Y%m%d-%H%M%S)
mkdir -p ~/.claude/plugins/cache
# Reinstall from marketplace
# Claude Code → Extensions → Everything Claude Code → Uninstall
# Then reinstall from marketplace
# Check Claude Code version
claude --version
# Requires Claude Code 2.0+
# Manual install (if marketplace fails)
git clone https://github.com/affaan-m/everything-claude-code.git
cp -r everything-claude-code ~/.claude/plugins/ecc
```
### 包管理器检测失败
**症状:** 使用了错误的包管理器(用 npm 而不是 pnpm
**原因:**
* 没有 lock 文件
* 未设置 CLAUDE\_PACKAGE\_MANAGER
* 多个 lock 文件导致检测混乱
**解决方案:**
```bash
# Set preferred package manager globally
export CLAUDE_PACKAGE_MANAGER=pnpm
# Add to ~/.bashrc or ~/.zshrc
# Or set per-project
echo '{"packageManager": "pnpm"}' > .claude/package-manager.json
# Or use package.json field
npm pkg set packageManager="pnpm@8.15.0"
# Warning: removing lock files can change installed dependency versions.
# Commit or back up the lock file first, then run a fresh install and re-run CI.
# Only do this when intentionally switching package managers.
rm package-lock.json # If using pnpm/yarn/bun
```
***
## 性能问题
### 响应时间缓慢
**症状:** 代理需要 30 秒以上才能响应
**原因:**
* 大型观察文件
* 活动钩子过多
* 到 API 的网络延迟
**解决方案:**
```bash
# Archive large observations instead of deleting them
archive_dir="$HOME/.claude/homunculus/archive/$(date +%Y%m%d)"
mkdir -p "$archive_dir"
find ~/.claude/homunculus/projects -name "observations.jsonl" -size +10M -exec sh -c '
for file do
base=$(basename "$(dirname "$file")")
gzip -c "$file" > "'"$archive_dir"'/${base}-observations.jsonl.gz"
: > "$file"
done
' sh {} +
# Disable unused hooks temporarily
# Edit ~/.claude/settings.json
# Keep active observation files small
# Large archives should live under ~/.claude/homunculus/archive/
```
### CPU 使用率高
**症状:** Claude Code 占用 100% CPU
**原因:**
* 无限观察循环
* 对大型目录的文件监视
* 钩子中的内存泄漏
**解决方案:**
```bash
# Check for runaway processes
top -o cpu | grep claude
# Disable continuous learning temporarily
touch ~/.claude/homunculus/disabled
# Restart Claude Code
# Cmd/Ctrl+Q then reopen
# Check observation file size
du -sh ~/.claude/homunculus/*/
```
***
## 常见错误信息
### "EACCES: permission denied"
```bash
# Fix hook permissions
find ~/.claude/plugins -name "*.sh" -exec chmod +x {} \;
# Fix observation directory permissions
chmod -R u+rwX,go+rX ~/.claude/homunculus
```
### "MODULE\_NOT\_FOUND"
```bash
# Install plugin dependencies
cd ~/.claude/plugins/cache/everything-claude-code
npm install
# Or for manual install
cd ~/.claude/plugins/ecc
npm install
```
### "spawn UNKNOWN"
```bash
# Windows-specific: Ensure scripts use correct line endings
# Convert CRLF to LF
find ~/.claude/plugins -name "*.sh" -exec dos2unix {} \;
# Or install dos2unix
# macOS: brew install dos2unix
# Ubuntu: sudo apt install dos2unix
```
***
## 获取帮助
如果您仍然遇到问题:
1. **检查 GitHub Issues**[github.com/affaan-m/everything-claude-code/issues](https://github.com/affaan-m/everything-claude-code/issues)
2. **启用调试日志记录**
```bash
export CLAUDE_DEBUG=1
export CLAUDE_LOG_LEVEL=debug
```
3. **收集诊断信息**
```bash
claude --version
node --version
python3 --version
echo $CLAUDE_PACKAGE_MANAGER
ls -la ~/.claude/plugins/cache/
```
4. **提交 Issue**:包括调试日志、错误信息和诊断信息
***
## 相关文档
* [README.md](README.md) - 安装与功能
* [CONTRIBUTING.md](CONTRIBUTING.md) - 开发指南
* [docs/](..) - 详细文档
* [examples/](../../examples) - 使用示例

View File

@@ -0,0 +1,119 @@
---
name: kotlin-build-resolver
description: Kotlin/Gradle 构建、编译和依赖错误解决专家。以最小改动修复构建错误、Kotlin 编译器错误和 Gradle 问题。适用于 Kotlin 构建失败时。
tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
model: sonnet
---
# Kotlin 构建错误解决器
你是一位 Kotlin/Gradle 构建错误解决专家。你的任务是以 **最小、精准的改动** 修复 Kotlin 构建错误、Gradle 配置问题和依赖解析失败。
## 核心职责
1. 诊断 Kotlin 编译错误
2. 修复 Gradle 构建配置问题
3. 解决依赖冲突和版本不匹配
4. 处理 Kotlin 编译器错误和警告
5. 修复 detekt 和 ktlint 违规
## 诊断命令
按顺序运行这些命令:
```bash
./gradlew build 2>&1
./gradlew detekt 2>&1 || echo "detekt not configured"
./gradlew ktlintCheck 2>&1 || echo "ktlint not configured"
./gradlew dependencies --configuration runtimeClasspath 2>&1 | head -100
```
## 解决工作流
```text
1. ./gradlew build -> Parse error message
2. Read affected file -> Understand context
3. Apply minimal fix -> Only what's needed
4. ./gradlew build -> Verify fix
5. ./gradlew test -> Ensure nothing broke
```
## 常见修复模式
| 错误 | 原因 | 修复方法 |
|-------|-------|-----|
| `Unresolved reference: X` | 缺少导入、拼写错误、缺少依赖 | 添加导入或依赖 |
| `Type mismatch: Required X, Found Y` | 类型错误、缺少转换 | 添加转换或修正类型 |
| `None of the following candidates is applicable` | 重载错误、参数类型错误 | 修正参数类型或添加显式转换 |
| `Smart cast impossible` | 可变属性或并发访问 | 使用局部 `val` 副本或 `let` |
| `'when' expression must be exhaustive` | 密封类 `when` 中缺少分支 | 添加缺失分支或 `else` |
| `Suspend function can only be called from coroutine` | 缺少 `suspend` 或协程作用域 | 添加 `suspend` 修饰符或启动协程 |
| `Cannot access 'X': it is internal in 'Y'` | 可见性问题 | 更改可见性或使用公共 API |
| `Conflicting declarations` | 重复定义 | 移除重复项或重命名 |
| `Could not resolve: group:artifact:version` | 缺少仓库或版本错误 | 添加仓库或修正版本 |
| `Execution failed for task ':detekt'` | 代码风格违规 | 修复 detekt 发现的问题 |
## Gradle 故障排除
```bash
# Check dependency tree for conflicts
./gradlew dependencies --configuration runtimeClasspath
# Force refresh dependencies
./gradlew build --refresh-dependencies
# Clear project-local Gradle build cache
./gradlew clean && rm -rf .gradle/build-cache/
# Check Gradle version compatibility
./gradlew --version
# Run with debug output
./gradlew build --debug 2>&1 | tail -50
# Check for dependency conflicts
./gradlew dependencyInsight --dependency <name> --configuration runtimeClasspath
```
## Kotlin 编译器标志
```kotlin
// build.gradle.kts - Common compiler options
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xjsr305=strict") // Strict Java null safety
allWarningsAsErrors = true
}
}
```
## 关键原则
* **仅进行精准修复** -- 不要重构,只修复错误
* **绝不** 在没有明确批准的情况下抑制警告
* **绝不** 更改函数签名,除非必要
* **始终** 在每次修复后运行 `./gradlew build` 以验证
* 修复根本原因而非抑制症状
* 优先添加缺失的导入而非使用通配符导入
## 停止条件
如果出现以下情况,请停止并报告:
* 尝试修复 3 次后相同错误仍然存在
* 修复引入的错误比它解决的更多
* 错误需要超出范围的架构更改
* 缺少需要用户决策的外部依赖
## 输出格式
```text
[FIXED] src/main/kotlin/com/example/service/UserService.kt:42
Error: Unresolved reference: UserRepository
Fix: Added import com.example.repository.UserRepository
Remaining errors: 2
```
最终:`Build Status: SUCCESS/FAILED | Errors Fixed: N | Files Modified: list`
有关详细的 Kotlin 模式和代码示例,请参阅 `skill: kotlin-patterns`

View File

@@ -0,0 +1,161 @@
---
name: kotlin-reviewer
description: Kotlin 和 Android/KMP 代码审查员。审查 Kotlin 代码以检查惯用模式、协程安全性、Compose 最佳实践、违反清洁架构原则以及常见的 Android 陷阱。
tools: ["Read", "Grep", "Glob", "Bash"]
model: sonnet
---
您是一位资深的 Kotlin 和 Android/KMP 代码审查员,确保代码符合语言习惯、安全且易于维护。
## 您的角色
* 审查 Kotlin 代码是否符合语言习惯模式以及 Android/KMP 最佳实践
* 检测协程误用、Flow 反模式和生命周期错误
* 强制执行清晰的架构模块边界
* 识别 Compose 性能问题和重组陷阱
* 您**不**重构或重写代码 —— 仅报告发现的问题
## 工作流程
### 步骤 1收集上下文
运行 `git diff --staged``git diff` 以查看更改。如果没有差异,请检查 `git log --oneline -5`。识别已更改的 Kotlin/KTS 文件。
### 步骤 2理解项目结构
检查:
* `build.gradle.kts``settings.gradle.kts` 以理解模块布局
* `CLAUDE.md` 了解项目特定的约定
* 项目是仅限 Android、KMP 还是 Compose Multiplatform
### 步骤 2b安全审查
在继续之前,应用 Kotlin/Android 安全指南:
* 已导出的 Android 组件、深度链接和意图过滤器
* 不安全的加密、WebView 和网络配置使用
* 密钥库、令牌和凭据处理
* 平台特定的存储和权限风险
如果发现**严重**安全问题,请停止审查,并在进行任何进一步分析之前,将问题移交给 `security-reviewer`
### 步骤 3阅读和审查
完整阅读已更改的文件。应用下面的审查清单,并检查周围代码以获取上下文。
### 步骤 4报告发现
使用下面的输出格式。仅报告置信度 >80% 的问题。
## 审查清单
### 架构(严重)
* **领域层导入框架** — `domain` 模块不得导入 Android、Ktor、Room 或任何框架
* **数据层泄漏到 UI 层** — 实体或 DTO 暴露给表示层(必须映射到领域模型)
* **ViewModel 中的业务逻辑** — 复杂逻辑应属于 UseCases而不是 ViewModels
* **循环依赖** — 模块 A 依赖于 B而模块 B 又依赖于 A
### 协程与 Flow
* **GlobalScope 使用** — 必须使用结构化作用域(`viewModelScope``coroutineScope`
* **捕获 CancellationException** — 必须重新抛出或不捕获;吞没该异常会破坏取消机制
* **IO 操作缺少 `withContext`** — 在 `Dispatchers.Main` 上进行数据库/网络调用
* **包含可变状态的 StateFlow** — 在 StateFlow 内部使用可变集合(必须复制)
* **在 `init {}` 中收集 Flow** — 应使用 `stateIn()` 或在作用域内启动
* **缺少 `WhileSubscribed`** — 当 `WhileSubscribed` 更合适时使用了 `stateIn(scope, SharingStarted.Eagerly)`
```kotlin
// BAD — swallows cancellation
try { fetchData() } catch (e: Exception) { log(e) }
// GOOD — preserves cancellation
try { fetchData() } catch (e: CancellationException) { throw e } catch (e: Exception) { log(e) }
// or use runCatching and check
```
### Compose
* **不稳定参数** — 可组合函数接收可变类型会导致不必要的重组
* **LaunchedEffect 之外的作用效应** — 网络/数据库调用必须在 `LaunchedEffect` 或 ViewModel 中
* **NavController 被深层传递** — 应传递 lambda 而非 `NavController` 引用
* **LazyColumn 中缺少 `key()`** — 没有稳定键的项目会导致性能不佳
* **`remember` 缺少键** — 当依赖项更改时,计算不会重新执行
* **参数中的对象分配** — 内联创建对象会导致重组
```kotlin
// BAD — new lambda every recomposition
Button(onClick = { viewModel.doThing(item.id) })
// GOOD — stable reference
val onClick = remember(item.id) { { viewModel.doThing(item.id) } }
Button(onClick = onClick)
```
### Kotlin 惯用法(中)
* **`!!` 使用** — 非空断言;更推荐 `?.``?:``requireNotNull``checkNotNull`
* **可以使用 `val` 的地方使用了 `var`** — 更推荐不可变性
* **Java 风格模式** — 静态工具类应使用顶层函数、getter/setter应使用属性
* **字符串拼接** — 使用字符串模板 `"Hello $name"` 而非 `"Hello " + name`
* **`when` 缺少穷举分支** — 密封类/接口应使用穷举的 `when`
* **暴露可变集合** — 公共 API 应返回 `List` 而非 `MutableList`
### Android 特定(中)
* **上下文泄漏** — 在单例/ViewModels 中存储 `Activity``Fragment` 引用
* **缺少 ProGuard 规则** — 序列化类缺少 `@Keep` 或 ProGuard 规则
* **硬编码字符串** — 面向用户的字符串未放在 `strings.xml` 或 Compose 资源中
* **缺少生命周期处理** — 在 Activity 中收集 Flow 时未使用 `repeatOnLifecycle`
### 安全(严重)
* **已导出组件暴露** — 活动、服务或接收器在没有适当防护的情况下被导出
* **不安全的加密/存储** — 自制的加密、明文存储的秘密或弱密钥库使用
* **不安全的 WebView/网络配置** — JavaScript 桥接、明文流量、过于宽松的信任设置
* **敏感日志记录** — 令牌、凭据、PII 或秘密信息被输出到日志
如果存在任何**严重**安全问题,请停止并升级给 `security-reviewer`
### Gradle 与构建(低)
* **未使用版本目录** — 硬编码版本而非使用 `libs.versions.toml`
* **不必要的依赖项** — 添加了但未使用的依赖项
* **缺少 KMP 源集** — 声明了 `androidMain` 代码,而该代码本可以是 `commonMain`
## 输出格式
```
[CRITICAL] Domain module imports Android framework
File: domain/src/main/kotlin/com/app/domain/UserUseCase.kt:3
Issue: `import android.content.Context` — domain must be pure Kotlin with no framework dependencies.
Fix: Move Context-dependent logic to data or platforms layer. Pass data via repository interface.
[HIGH] StateFlow holding mutable list
File: presentation/src/main/kotlin/com/app/ui/ListViewModel.kt:25
Issue: `_state.value.items.add(newItem)` mutates the list inside StateFlow — Compose won't detect the change.
Fix: Use `_state.update { it.copy(items = it.items + newItem) }`
```
## 摘要格式
每次审查结束时附上:
```
## Review Summary
| Severity | Count | Status |
|----------|-------|--------|
| CRITICAL | 0 | pass |
| HIGH | 1 | block |
| MEDIUM | 2 | info |
| LOW | 0 | note |
Verdict: BLOCK — HIGH issues must be fixed before merge.
```
## 批准标准
* **批准**:没有**严重**或**高**级别问题
* **阻止**:存在任何**严重**或**高**级别问题 —— 必须在合并前修复

View File

@@ -0,0 +1,173 @@
---
description: 在不打断或丢失当前任务上下文的情况下,快速回答一个附带问题。回答后自动恢复工作。
---
# 旁述指令
在任务进行中提问,获得即时、聚焦的回答——然后立即从暂停处继续。当前任务、文件和上下文绝不会被修改。
## 何时使用
* 你在 Claude 工作时对某事感到好奇,但又不想打断工作节奏
* 你需要快速解释 Claude 当前正在编辑的代码
* 你想就某个决定征求第二意见或进行澄清,而不会使任务偏离方向
* 在 Claude 继续之前,你需要理解一个错误、概念或模式
* 你想询问与当前任务无关的事情,而无需开启新会话
## 使用方法
```
/aside <your question>
/aside what does this function actually return?
/aside is this pattern thread-safe?
/aside why are we using X instead of Y here?
/aside what's the difference between foo() and bar()?
/aside should we be worried about the N+1 query we just added?
```
## 流程
### 步骤 1冻结当前任务状态
在回答任何问题之前,先在心里记下:
* 当前活动任务是什么?(正在处理哪个文件、功能或问题)
* 在调用 `/aside` 时,进行到哪一步了?
* 接下来原本要发生什么?
在旁述期间,**不要**触碰、编辑、创建或删除任何文件。
### 步骤 2直接回答问题
以最简洁但仍完整有用的形式回答问题。
* 先说答案,再说推理过程
* 保持简短——如果需要完整解释,请在任务结束后再提供
* 如果问题涉及当前正在处理的文件或代码,请精确引用(相关时包括文件路径和行号)
* 如果回答问题需要读取文件,就读它——但只读不写
将响应格式化为:
```
ASIDE: [restate the question briefly]
[Your answer here]
— Back to task: [one-line description of what was being done]
```
### 步骤 3恢复主任务
在给出答案后,立即从暂停的确切点继续执行活动任务。除非旁述回答揭示了阻碍或需要重新考虑当前方法的理由(见边缘情况),否则不要请求恢复许可。
***
## 边缘情况
**未提供问题(`/aside` 后面没有内容):**
回复:
```
ASIDE: no question provided
What would you like to know? (ask your question and I'll answer without losing the current task context)
— Back to task: [one-line description of what was being done]
```
**问题揭示了当前任务的潜在问题:**
在恢复之前清楚地标记出来:
```
ASIDE: [answer]
⚠️ Note: This answer suggests [issue] with the current approach. Want to address this before continuing, or proceed as planned?
```
等待用户的决定后再恢复。
**问题实际上是任务重定向(而非旁述问题):**
如果问题暗示要改变正在构建的内容(例如,`/aside actually, let's use Redis instead`),请澄清:
```
ASIDE: That sounds like a direction change, not just a side question.
Do you want to:
(a) Answer this as information only and keep the current plan
(b) Pause the current task and change approach
```
等待用户的回答——不要自行假设。
**问题涉及当前打开的文件或代码:**
根据实时上下文回答。如果该文件在会话早期已被读取,直接引用它。如果尚未读取,现在读取它(只读)并在回答时附带文件:行号引用。
**无活动任务(调用 `/aside` 时没有进行中的任务):**
仍然使用标准包装器,以保持响应格式一致:
```
ASIDE: [restate the question briefly]
[Your answer here]
— Back to task: no active task to resume
```
**问题需要长篇回答:**
简洁地给出核心答案,然后提供:
```
That's the short version. Want a deeper explanation after we finish [current task]?
```
**连续多个 `/aside` 问题:**
依次回答每个问题。在回答完最后一个问题后,恢复主任务。不要在一连串旁述中丢失任务状态。
**旁述回答暗示需要进行代码更改:**
记下所需的更改,但不要在旁述期间进行更改:
```
ASIDE: [answer]
📝 Worth fixing: [what should be changed]. I'll flag this after the current task unless you want to address it now.
```
**问题含糊不清或过于笼统:**
提出一个澄清性问题——一个能获取回答所需信息的最简短问题。不要提出多个问题。
***
## 示例输出
```
User: /aside what does fetchWithRetry() actually do?
ASIDE: what does fetchWithRetry() do?
fetchWithRetry() (src/api/retry.ts:12) attempts the request up to 3 times with
exponential backoff (250ms → 500ms → 1s). It only retries on 5xx responses and
network errors — 4xx errors are treated as final and not retried.
— Back to task: refactoring the auth middleware in src/middleware/auth.ts
```
```
User: /aside is the approach we're taking thread-safe?
ASIDE: is the current approach thread-safe?
No — the shared cache object in src/cache/store.ts:34 is mutated without locking.
Under concurrent requests this is a race condition. It's low risk in a single-process
Node.js server but would be a real problem with worker threads or clustering.
⚠️ Note: This could affect the feature we're building. Want to address this now or continue and fix it in a follow-up?
```
***
## 注意事项
* 在旁述期间**绝不**修改文件——仅限只读访问
* 旁述是对话暂停,不是新任务——必须始终恢复原始任务
* 保持回答聚焦:目标是快速为用户扫清障碍,而不是进行长篇大论
* 如果旁述引发了更广泛的讨论,请先完成当前任务,除非旁述揭示了阻碍
* 除非明确与任务结果相关,否则旁述内容不会保存到会话文件中

View File

@@ -345,7 +345,10 @@ Quarantine recommendation: Mark as test.fixme() until fixed
## 相关代理
此命令调用位于 `~/.claude/agents/e2e-runner.md` 的 `e2e-runner` 代理。
此命令调用由 ECC 提供的 `e2e-runner` 代理。
对于手动安装,源文件位于:
`agents/e2e-runner.md`
## 快速命令

View File

@@ -0,0 +1,72 @@
---
description: 修复 Android 和 KMP 项目的 Gradle 构建错误
---
# Gradle 构建修复
逐步修复 Android 和 Kotlin 多平台项目的 Gradle 构建和编译错误。
## 步骤 1检测构建配置
识别项目类型并运行相应的构建:
| 指示符 | 构建命令 |
|-----------|---------------|
| `build.gradle.kts` + `composeApp/` (KMP) | `./gradlew composeApp:compileKotlinMetadata 2>&1` |
| `build.gradle.kts` + `app/` (Android) | `./gradlew app:compileDebugKotlin 2>&1` |
| `settings.gradle.kts` 包含模块 | `./gradlew assemble 2>&1` |
| 配置了 Detekt | `./gradlew detekt 2>&1` |
同时检查 `gradle.properties``local.properties` 以获取配置信息。
## 步骤 2解析并分组错误
1. 运行构建命令并捕获输出
2. 将 Kotlin 编译错误与 Gradle 配置错误分开
3. 按模块和文件路径分组
4. 排序:先处理配置错误,然后按依赖顺序处理编译错误
## 步骤 3修复循环
针对每个错误:
1. **读取文件** — 错误行周围的完整上下文
2. **诊断** — 常见类别:
* 缺少导入或无法解析的引用
* 类型不匹配或不兼容的类型
* `build.gradle.kts` 中缺少依赖项
* Expect/actual 不匹配 (KMP)
* Compose 编译器错误
3. **最小化修复** — 解决错误所需的最小改动
4. **重新运行构建** — 验证修复并检查新错误
5. **继续** — 处理下一个错误
## 步骤 4防护措施
如果出现以下情况,请停止并询问用户:
* 修复引入的错误比解决的错误多
* 同一错误在 3 次尝试后仍然存在
* 错误需要添加新的依赖项或更改模块结构
* Gradle 同步本身失败(配置阶段错误)
* 错误出现在生成的代码中Room、SQLDelight、KSP
## 步骤 5总结
报告:
* 已修复的错误(模块、文件、描述)
* 剩余的错误
* 引入的新错误(应为零)
* 建议的后续步骤
## 常见的 Gradle/KMP 修复方案
| 错误 | 修复方法 |
|-------|-----|
| `commonMain` 中无法解析的引用 | 检查依赖项是否在 `commonMain.dependencies {}` 中 |
| Expect 声明没有 actual 实现 | 在每个平台源码集中添加 `actual` 实现 |
| Compose 编译器版本不匹配 | 在 `libs.versions.toml` 中统一 Kotlin 和 Compose 编译器版本 |
| 重复类 | 使用 `./gradlew dependencies` 检查是否存在冲突的依赖项 |
| KSP 错误 | 运行 `./gradlew kspCommonMainKotlinMetadata` 重新生成 |
| 配置缓存问题 | 检查是否存在不可序列化的任务输入 |

View File

@@ -0,0 +1,176 @@
---
description: 逐步修复 Kotlin/Gradle 构建错误、编译器警告和依赖项问题。调用 kotlin-build-resolver 代理进行最小化、精准的修复。
---
# Kotlin 构建与修复
此命令调用 **kotlin-build-resolver** 代理,以最小的改动增量修复 Kotlin 构建错误。
## 此命令的作用
1. **运行诊断**:执行 `./gradlew build``detekt``ktlintCheck`
2. **解析错误**:按文件分组并按严重性排序
3. **增量修复**:每次修复一个错误
4. **验证每次修复**:每次更改后重新运行构建
5. **报告摘要**:显示已修复的内容和剩余问题
## 何时使用
在以下情况使用 `/kotlin-build`
* `./gradlew build` 因错误而失败
* Kotlin 编译器报告错误
* `./gradlew detekt` 报告违规
* Gradle 依赖项解析失败
* 拉取更改后导致构建失败
## 运行的诊断命令
```bash
# Primary build check
./gradlew build 2>&1
# Static analysis
./gradlew detekt 2>&1 || echo "detekt not configured"
./gradlew ktlintCheck 2>&1 || echo "ktlint not configured"
# Dependency issues
./gradlew dependencies --configuration runtimeClasspath 2>&1 | head -100
# Optional deep refresh when caches or dependency metadata are suspect
./gradlew build --refresh-dependencies
```
## 示例会话
````text
User: /kotlin-build
Agent:
# Kotlin Build Resolution
## Initial Diagnostics
```bash
$ ./gradlew build
e: src/main/kotlin/com/example/service/UserService.kt:25:15 Unresolved reference: UserRepository
e: src/main/kotlin/com/example/routes/UserRoutes.kt:42:9 Type mismatch: inferred type is String but Int was expected
e: src/main/kotlin/com/example/routes/UserRoutes.kt:58:5 'when' expression must be exhaustive
```
Errors found: 3
## Fix 1: Unresolved Reference
File: src/main/kotlin/com/example/service/UserService.kt:25
Error: Unresolved reference: UserRepository
Cause: Missing import
```kotlin
// Added import
import com.example.repository.UserRepository
```
```bash
$ ./gradlew build
# 2 errors remaining
```
## Fix 2: Type Mismatch
File: src/main/kotlin/com/example/routes/UserRoutes.kt:42
Error: Type mismatch: inferred type is String but Int was expected
```kotlin
// Changed
val count = call.parameters["count"]
// To
val count = call.parameters["count"]?.toIntOrNull()
?: return@get call.respond(HttpStatusCode.BadRequest, "Invalid count")
```
```bash
$ ./gradlew build
# 1 error remaining
```
## Fix 3: Non-Exhaustive When
File: src/main/kotlin/com/example/routes/UserRoutes.kt:58
Error: 'when' expression must be exhaustive
```kotlin
// Added missing branch
when (user.role) {
Role.ADMIN -> handleAdmin(user)
Role.USER -> handleUser(user)
Role.MODERATOR -> handleModerator(user) // Added
}
```
```bash
$ ./gradlew build
# Build successful!
```
## Final Verification
```bash
$ ./gradlew detekt
# No issues
$ ./gradlew test
# All tests passed
```
## Summary
| Metric | Count |
|--------|-------|
| Build errors fixed | 3 |
| Detekt issues fixed | 0 |
| Files modified | 2 |
| Remaining issues | 0 |
Build Status: ✅ SUCCESS
````
## 常见的已修复错误
| 错误 | 典型修复方法 |
|-------|-------------|
| `Unresolved reference: X` | 添加导入或依赖项 |
| `Type mismatch` | 修复类型转换或赋值 |
| `'when' must be exhaustive` | 添加缺失的密封类分支 |
| `Suspend function can only be called from coroutine` | 添加 `suspend` 修饰符 |
| `Smart cast impossible` | 使用局部 `val` 或 `let` |
| `None of the following candidates is applicable` | 修复参数类型 |
| `Could not resolve dependency` | 修复版本或添加仓库 |
## 修复策略
1. **首先修复构建错误** - 代码必须能够编译
2. **其次修复 Detekt 违规** - 修复代码质量问题
3. **再次修复 ktlint 警告** - 修复格式问题
4. **一次修复一个** - 验证每次更改
5. **最小化改动** - 不进行重构,仅修复问题
## 停止条件
代理将在以下情况下停止并报告:
* 同一错误尝试修复 3 次后仍然存在
* 修复引入了更多错误
* 需要进行架构性更改
* 缺少外部依赖项
## 相关命令
* `/kotlin-test` - 构建成功后运行测试
* `/kotlin-review` - 审查代码质量
* `/verify` - 完整的验证循环
## 相关
* 代理:`agents/kotlin-build-resolver.md`
* 技能:`skills/kotlin-patterns/`

View File

@@ -0,0 +1,144 @@
---
description: 全面的Kotlin代码审查涵盖惯用模式、空安全、协程安全和安全性。调用kotlin-reviewer代理。
---
# Kotlin 代码审查
此命令调用 **kotlin-reviewer** 代理进行全面的 Kotlin 专项代码审查。
## 此命令的功能
1. **识别 Kotlin 变更**:通过 `git diff` 查找修改过的 `.kt``.kts` 文件
2. **运行构建与静态分析**:执行 `./gradlew build``detekt``ktlintCheck`
3. **安全扫描**:检查 SQL 注入、命令注入、硬编码的密钥
4. **空安全审查**:分析 `!!` 的使用、平台类型处理、不安全的转换
5. **协程审查**:检查结构化并发、调度器使用、取消操作
6. **生成报告**:按严重性对问题分类
## 使用时机
在以下情况使用 `/kotlin-review`
* 编写或修改 Kotlin 代码后
* 提交 Kotlin 变更前
* 审查包含 Kotlin 代码的拉取请求时
* 接手新的 Kotlin 代码库时
* 学习 Kotlin 惯用模式时
## 审查类别
### 严重(必须修复)
* SQL/命令注入漏洞
* 无正当理由强制解包 `!!`
* 平台类型空安全违规
* 使用 GlobalScope违反结构化并发
* 硬编码的凭证
* 不安全的反序列化
### 高(应该修复)
* 可变状态,而不可变状态即可满足
* 在协程上下文中进行阻塞调用
* 长循环中缺少取消检查
* 对密封类型使用非穷举的 `when`
* 函数过大(>50 行)
* 嵌套过深(>4 层)
### 中(考虑修复)
* 非 Kotlin 惯用写法Java 风格模式)
* 缺少尾随逗号
* 误用或嵌套作用域函数
* 大型集合链中缺少序列化
* 冗余的显式类型声明
## 运行的自动化检查
```bash
# Build check
./gradlew build
# Static analysis
./gradlew detekt
# Formatting check
./gradlew ktlintCheck
# Tests
./gradlew test
```
## 使用示例
````text
User: /kotlin-review
Agent:
# Kotlin Code Review Report
## Files Reviewed
- src/main/kotlin/com/example/service/UserService.kt (modified)
- src/main/kotlin/com/example/routes/UserRoutes.kt (modified)
## Static Analysis Results
✓ Build: Successful
✓ detekt: No issues
⚠ ktlint: 2 formatting warnings
## Issues Found
[CRITICAL] Force-Unwrap Null Safety
File: src/main/kotlin/com/example/service/UserService.kt:28
Issue: Using !! on nullable repository result
```kotlin
val user = repository.findById(id)!! // NPE risk
```
Fix: Use safe call with error handling
```kotlin
val user = repository.findById(id)
?: throw UserNotFoundException("User $id not found")
```
[HIGH] GlobalScope Usage
File: src/main/kotlin/com/example/routes/UserRoutes.kt:45
Issue: Using GlobalScope breaks structured concurrency
```kotlin
GlobalScope.launch {
notificationService.sendWelcome(user)
}
```
Fix: Use the call's coroutine scope
```kotlin
launch {
notificationService.sendWelcome(user)
}
```
## Summary
- CRITICAL: 1
- HIGH: 1
- MEDIUM: 0
Recommendation: ❌ Block merge until CRITICAL issue is fixed
````
## 批准标准
| 状态 | 条件 |
|--------|-----------|
| ✅ 批准 | 无严重或高优先级问题 |
| ⚠️ 警告 | 仅存在中优先级问题(谨慎合并) |
| ❌ 阻止 | 发现严重或高优先级问题 |
## 与其他命令的集成
* 首先使用 `/kotlin-test` 确保测试通过
* 如果构建出错,使用 `/kotlin-build`
* 提交前使用 `/kotlin-review`
* 对于非 Kotlin 专项问题,使用 `/code-review`
## 相关
* 代理:`agents/kotlin-reviewer.md`
* 技能:`skills/kotlin-patterns/`、`skills/kotlin-testing/`

View File

@@ -0,0 +1,315 @@
---
description: 为Kotlin强制执行TDD工作流程。首先编写Kotest测试然后实施。使用Kover验证80%以上的覆盖率。
---
# Kotlin TDD 命令
此命令使用 Kotest、MockK 和 Kover 为 Kotlin 代码强制执行测试驱动开发方法。
## 此命令的作用
1. **定义类型/接口**:首先搭建函数签名
2. **编写 Kotest 测试**创建全面的测试规范RED
3. **运行测试**:验证测试因正确原因而失败
4. **实现代码**编写最少的代码以通过测试GREEN
5. **重构**:在保持测试通过的同时进行改进
6. **检查覆盖率**:确保使用 Kover 达到 80% 以上的覆盖率
## 何时使用
在以下情况下使用 `/kotlin-test`
* 实现新的 Kotlin 函数或类
* 为现有的 Kotlin 代码添加测试覆盖率
* 修复错误(首先编写失败的测试)
* 构建关键业务逻辑
* 学习 Kotlin 中的 TDD 工作流程
## TDD 循环
```
RED -> Write failing Kotest test
GREEN -> Implement minimal code to pass
REFACTOR -> Improve code, tests stay green
REPEAT -> Next test case
```
## 示例会话
````text
User: /kotlin-test I need a function to validate user registration requests
Agent:
# TDD Session: Registration Validator
## Step 1: Define Interface
```kotlin
// validator/RegistrationValidator.kt
package com.example.validator
data class RegistrationRequest(
val name: String,
val email: String,
val password: String,
)
sealed class ValidationResult {
data object Valid : ValidationResult()
data class Invalid(val errors: List<String>) : ValidationResult()
}
fun validateRegistration(request: RegistrationRequest): ValidationResult {
TODO("not implemented")
}
```
## Step 2: Write Kotest Tests (RED)
```kotlin
// validator/RegistrationValidatorTest.kt
package com.example.validator
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
class RegistrationValidatorTest : FunSpec({
test("valid registration returns Valid") {
val request = RegistrationRequest(
name = "Alice",
email = "alice@example.com",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
result.shouldBeInstanceOf<ValidationResult.Valid>()
}
test("blank name returns Invalid") {
val request = RegistrationRequest(
name = "",
email = "alice@example.com",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Name is required")
}
test("invalid email returns Invalid") {
val request = RegistrationRequest(
name = "Alice",
email = "not-an-email",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Invalid email format")
}
test("short password returns Invalid") {
val request = RegistrationRequest(
name = "Alice",
email = "alice@example.com",
password = "short",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Password must be at least 8 characters")
}
test("multiple errors returns all errors") {
val request = RegistrationRequest(
name = "",
email = "bad",
password = "short",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors.size shouldBe 3
}
})
```
## Step 3: Run Tests - Verify FAIL
```bash
$ ./gradlew test
RegistrationValidatorTest > valid registration returns Valid FAILED
kotlin.NotImplementedError: An operation is not implemented
FAILED (5 tests, 0 passed, 5 failed)
```
✓ Tests fail as expected (NotImplementedError).
## Step 4: Implement Minimal Code (GREEN)
```kotlin
// validator/RegistrationValidator.kt
package com.example.validator
private val EMAIL_REGEX = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
private const val MIN_PASSWORD_LENGTH = 8
fun validateRegistration(request: RegistrationRequest): ValidationResult {
val errors = buildList {
if (request.name.isBlank()) add("Name is required")
if (!EMAIL_REGEX.matches(request.email)) add("Invalid email format")
if (request.password.length < MIN_PASSWORD_LENGTH) add("Password must be at least $MIN_PASSWORD_LENGTH characters")
}
return if (errors.isEmpty()) ValidationResult.Valid
else ValidationResult.Invalid(errors)
}
```
## Step 5: Run Tests - Verify PASS
```bash
$ ./gradlew test
RegistrationValidatorTest > valid registration returns Valid PASSED
RegistrationValidatorTest > blank name returns Invalid PASSED
RegistrationValidatorTest > invalid email returns Invalid PASSED
RegistrationValidatorTest > short password returns Invalid PASSED
RegistrationValidatorTest > multiple errors returns all errors PASSED
PASSED (5 tests, 5 passed, 0 failed)
```
✓ All tests passing!
## Step 6: Check Coverage
```bash
$ ./gradlew koverHtmlReport
Coverage: 100.0% of statements
```
✓ Coverage: 100%
## TDD Complete!
````
## 测试模式
### StringSpec最简单
```kotlin
class CalculatorTest : StringSpec({
"add two positive numbers" {
Calculator.add(2, 3) shouldBe 5
}
})
```
### BehaviorSpecBDD
```kotlin
class OrderServiceTest : BehaviorSpec({
Given("a valid order") {
When("placed") {
Then("should be confirmed") { /* ... */ }
}
}
})
```
### 数据驱动测试
```kotlin
class ParserTest : FunSpec({
context("valid inputs") {
withData("2026-01-15", "2026-12-31", "2000-01-01") { input ->
parseDate(input).shouldNotBeNull()
}
}
})
```
### 协程测试
```kotlin
class AsyncServiceTest : FunSpec({
test("concurrent fetch completes") {
runTest {
val result = service.fetchAll()
result.shouldNotBeEmpty()
}
}
})
```
## 覆盖率命令
```bash
# Run tests with coverage
./gradlew koverHtmlReport
# Verify coverage thresholds
./gradlew koverVerify
# XML report for CI
./gradlew koverXmlReport
# Open HTML report
open build/reports/kover/html/index.html
# Run specific test class
./gradlew test --tests "com.example.UserServiceTest"
# Run with verbose output
./gradlew test --info
```
## 覆盖率目标
| 代码类型 | 目标 |
|-----------|--------|
| 关键业务逻辑 | 100% |
| 公共 API | 90%+ |
| 通用代码 | 80%+ |
| 生成的代码 | 排除 |
## TDD 最佳实践
**应做:**
* 首先编写测试,在任何实现之前
* 每次更改后运行测试
* 使用 Kotest 匹配器进行表达性断言
* 使用 MockK 的 `coEvery`/`coVerify` 来处理挂起函数
* 测试行为,而非实现细节
* 包含边界情况空值、null、最大值
**不应做:**
* 在测试之前编写实现
* 跳过 RED 阶段
* 直接测试私有函数
* 在协程测试中使用 `Thread.sleep()`
* 忽略不稳定的测试
## 相关命令
* `/kotlin-build` - 修复构建错误
* `/kotlin-review` - 在实现后审查代码
* `/verify` - 运行完整的验证循环
## 相关
* 技能:`skills/kotlin-testing/`
* 技能:`skills/tdd-workflow/`

View File

@@ -1,10 +1,10 @@
---
description: 从会话中提取可重用模式,在保存前自我评估质量,并确定正确的保存位置(全局与项目)。
description: "从会话中提取可重用模式,在保存前自我评估质量,并确定正确的保存位置(全局与项目)。"
---
# /learn-eval - 提取、评估、然后保存
扩展 `/learn`,在写任何技能文件之前加入质量门保存位置决策。
扩展 `/learn`,在写任何技能文件之前加入质量门控、保存位置决策和知识放置意识
## 提取内容
@@ -52,41 +52,66 @@ origin: auto-extracted
[触发条件]
```
5. **在保存前自我评估**,使用此评分标准:
5. **质量门控 — 清单 + 整体裁决**
| 维度 | 1 | 3 | 5 |
|-----------|---|---|---|
| 具体性 | 仅抽象原则,无代码示例 | 有代表性代码示例 | 包含所有使用模式的丰富示例 |
| 可操作性 | 不清楚要做什么 | 主要步骤可理解 | 立即可操作,涵盖边界情况 |
| 范围契合度 | 过于宽泛或过于狭窄 | 基本合适,存在一些边界模糊 | 名称、触发器和内容完美匹配 |
| 非冗余性 | 几乎与另一技能相同 | 存在一些重叠但有独特视角 | 完全独特的价值 |
| 覆盖率 | 仅涵盖目标任务的一小部分 | 涵盖主要情况,缺少常见变体 | 涵盖主要情况、边界情况和陷阱 |
### 5a. 必需清单(通过实际阅读文件进行验证)
* 为每个维度评分 15
* 如果任何维度评分为 12改进草案并重新评分直到所有维度 ≥ 3
* 向用户展示评分表和最终草案
在评估草案**之前**,执行以下所有操作:
6. 请求用户确认:
* 展示:提议的保存路径 + 评分表 + 最终草案
* 在写入前等待明确确认
* \[ ] 使用关键字在 `~/.claude/skills/` 和相关项目的 `.claude/skills/` 文件中进行 grep 搜索,检查内容重叠
* \[ ] 检查 MEMORY.md项目级和全局级以查找重叠内容
* \[ ] 考虑是否追加到现有技能即可满足需求
* \[ ] 确认这是一个可复用的模式,而非一次性修复
7. 保存到确定的位置
### 5b. 整体裁决
## 第 5 步的输出格式(评分表)
综合清单结果和草案质量,然后选择**以下一项**
| 维度 | 评分 | 理由 |
|-----------|-------|-----------|
| 具体性 | N/5 | ... |
| 可操作性 | N/5 | ... |
| 范围契合度 | N/5 | ... |
| 非冗余性 | N/5 | ... |
| 覆盖率 | N/5 | ... |
| **总计** | **N/25** | |
| 裁决 | 含义 | 下一步行动 |
|---------|---------|-------------|
| **保存** | 独特、具体、范围明确 | 进行到步骤 6 |
| **改进后保存** | 有价值但需要改进 | 列出改进项 → 修订 → 重新评估(一次) |
| **吸收到 \[X]** | 应追加到现有技能 | 显示目标技能和添加内容 → 步骤 6 |
| **放弃** | 琐碎、冗余或过于抽象 | 解释原因并停止 |
**指导维度**(用于告知裁决,不进行评分):
* **具体性和可操作性**:包含可立即使用的代码示例或命令
* **范围契合度**:名称、触发条件和内容保持一致,并专注于单一模式
* **独特性**:提供现有技能未涵盖的价值(基于清单结果)
* **可复用性**:在未来的会话中存在现实的触发场景
6. **裁决特定的确认流程**
* **改进后保存**:呈现必需的改进项 + 修订后的草案 + 一次重新评估后的更新清单/裁决;如果修订后的裁决是**保存**,则在用户确认后保存,否则遵循新的裁决
* **保存**:呈现保存路径 + 清单结果 + 1行裁决理由 + 完整草案 → 在用户确认后保存
* **吸收到 \[X]**:呈现目标路径 + 添加内容diff格式 + 清单结果 + 裁决理由 → 在用户确认后追加
* **放弃**:仅显示清单结果 + 推理(无需确认)
7. 保存 / 吸收到确定的位置
## 步骤 5 的输出格式
```
### Checklist
- [x] skills/ grep: no overlap (or: overlap found → details)
- [x] MEMORY.md: no overlap (or: overlap found → details)
- [x] Existing skill append: new file appropriate (or: should append to [X])
- [x] Reusability: confirmed (or: one-off → Drop)
### Verdict: Save / Improve then Save / Absorb into [X] / Drop
**Rationale:** (1-2 sentences explaining the verdict)
```
## 设计原理
此版本用基于清单的整体裁决系统取代了之前的 5 维度数字评分标准(具体性、可操作性、范围契合度、非冗余性、覆盖度,评分 1-5。现代前沿模型Opus 4.6+)具有强大的情境判断能力 —— 将丰富的定性信号强行压缩为数字评分会丢失细微差别,并可能产生误导性的总分。整体方法让模型自然地权衡所有因素,产生更准确的保存/放弃决策,同时明确的清单确保不会跳过任何关键检查。
## 注意事项
* 不要提取琐碎的修复(拼写错误、简单的语法错误)
* 不要提取一次性问题(特定的 API 中断等)
* 专注于在未来会话中节省时间的模式
* 保持技能聚焦 — 每个技能一个模式
* 如果覆盖率评分低,在保存前添加相关变体
* 专注于那些将在未来会话中节省时间的模式
* 保持技能聚焦 — 每个技能一个模式
* 当裁决为“吸收”时,追加到现有技能,而不是创建新文件

View File

@@ -104,6 +104,14 @@ TaskOutput({ task_id: "<task_id>", block: true, timeout: 600000 })
4. 当评分 < 7 或用户不批准时强制停止。
5. 需要时(例如确认/选择/批准)使用 `AskUserQuestion` 工具进行用户交互。
## 何时使用外部编排
当工作必须拆分给需要隔离的 git 状态、独立终端或独立构建/测试执行的并行工作器时,请使用外部 tmux/工作树编排。对于轻量级分析、规划或审查(其中主会话是唯一的写入者),请使用进程内子代理。
```bash
node scripts/orchestrate-worktrees.js .claude/plan/workflow-e2e-test.json --execute
```
***
## 执行工作流程

View File

@@ -158,6 +158,61 @@ RECOMMENDATION
```
对于使用独立 git worktree 的外部 tmux-pane 工作器,请使用 `node scripts/orchestrate-worktrees.js plan.json --execute`。内置的编排模式保持进程内运行;此辅助工具适用于长时间运行或跨测试框架的会话。
当工作器需要查看主检出目录中的脏文件或未跟踪的本地文件时,请在计划文件中添加 `seedPaths`。ECC 仅在 `git worktree add` 之后,将那些选定的路径覆盖到每个工作器的工作树中,这既能保持分支隔离,又能暴露正在处理的本地脚本、计划或文档。
```json
{
"sessionName": "workflow-e2e",
"seedPaths": [
"scripts/orchestrate-worktrees.js",
"scripts/lib/tmux-worktree-orchestrator.js",
".claude/plan/workflow-e2e-test.json"
],
"workers": [
{ "name": "docs", "task": "Update orchestration docs." }
]
}
```
要导出实时 tmux/worktree 会话的控制平面快照,请运行:
```bash
node scripts/orchestration-status.js .claude/plan/workflow-visual-proof.json
```
快照包含会话活动、tmux 窗格元数据、工作器状态、目标、已播种的覆盖层以及最近的交接摘要,均以 JSON 格式保存。
## 操作员指挥中心交接
当工作流跨越多个会话、工作树或 tmux 窗格时,请在最终交接内容中附加一个控制平面块:
```markdown
控制平面
-------------
会话:
- 活动会话 ID 或别名
- 每个活动工作线程的分支 + 工作树路径
- 适用时的 tmux 窗格或分离会话名称
差异:
- git 状态摘要
- 已修改文件的 git diff --stat
- 合并/冲突风险说明
审批:
- 待处理的用户审批
- 等待确认的受阻步骤
遥测:
- 最后活动时间戳或空闲信号
- 预估的令牌或成本漂移
- 由钩子或审查器引发的策略事件
```
这使得规划者、实施者、审查者和循环工作器在操作员界面上保持清晰可辨。
## 参数
$ARGUMENTS:

View File

@@ -112,4 +112,7 @@ Agent (planner):
## 相关代理
此命令调用位于 `~/.claude/agents/planner.md` `planner` 代理。
此命令调用由 ECC 提供`planner` 代理。
对于手动安装,源文件位于:
`agents/planner.md`

View File

@@ -0,0 +1,37 @@
---
description: 分析一个草稿提示输出一个经过优化、富含ECC的版本准备粘贴并运行。不执行任务——仅输出咨询分析。
---
# /prompt-optimize
分析并优化以下提示语以实现最大化的ECC杠杆效应。
## 你的任务
对下方用户的输入应用 **prompt-optimizer** 技能。遵循6阶段分析流程
0. **项目检测** — 读取 CLAUDE.md从项目文件package.json, go.mod, pyproject.toml 等)检测技术栈
1. **意图检测** — 对任务类型进行分类(新功能、错误修复、重构、研究、测试、评审、文档、基础设施、设计)
2. **范围评估** — 评估复杂度(简单 / 低 / 中 / 高 / 史诗级),如果检测到代码库,则使用其大小作为信号
3. **ECC组件匹配** — 映射到特定的技能、命令、代理和模型层级
4. **缺失上下文检测** — 识别信息缺口。如果缺少3个以上关键项请在生成前请用户澄清
5. **工作流与模型** — 确定生命周期阶段,推荐模型层级,如果复杂度为高/史诗级,则将其拆分为多个提示语
## 输出要求
* 呈现诊断结果、推荐的ECC组件以及使用 prompt-optimizer 技能中输出格式的优化后提示语
* 提供 **完整版本**(详细)和 **快速版本**(紧凑,根据意图类型变化)
* 使用与用户输入相同的语言进行回复
* 优化后的提示语必须完整且可复制粘贴到新会话中直接使用
* 以提供调整选项或明确下一步操作(用于启动单独的执行请求)的页脚结束
## 关键
请勿执行用户的任务。仅输出分析结果和优化后的提示语。
如果用户要求直接执行,请说明 `/prompt-optimize` 仅产生咨询性输出,并告诉他们应启动一个常规的任务请求。
注意:`blueprint` 是一个**技能**,而非斜杠命令。请写作“使用蓝图技能”,而不是将其呈现为 `/...` 命令。
## 用户输入
$ARGUMENTS

View File

@@ -0,0 +1,155 @@
---
description: 从 ~/.claude/sessions/ 加载最新的会话文件,并从上次会话结束的地方恢复工作,保留完整上下文。
---
# 恢复会话命令
加载最后保存的会话状态,并在开始任何工作前完全熟悉情况。
此命令是 `/save-session` 的对应命令。
## 何时使用
* 开始新会话以继续前一天的工作时
* 因上下文限制而开始全新会话后
* 当从其他来源移交会话文件时(只需提供文件路径)
* 任何拥有会话文件并希望 Claude 在继续前完全吸收其内容的时候
## 用法
```
/resume-session # loads most recent file in ~/.claude/sessions/
/resume-session 2024-01-15 # loads most recent session for that date
/resume-session ~/.claude/sessions/2024-01-15-session.tmp # loads a specific legacy-format file
/resume-session ~/.claude/sessions/2024-01-15-abc123de-session.tmp # loads a current short-id session file
```
## 流程
### 步骤 1查找会话文件
如果未提供参数:
1. 检查 `~/.claude/sessions/`
2. 选择最近修改的 `*-session.tmp` 文件
3. 如果文件夹不存在或没有匹配的文件,告知用户:
```
在 ~/.claude/sessions/ 中未找到会话文件。
请在会话结束时运行 /save-session 来创建一个。
```
然后停止。
如果提供了参数:
* 如果看起来像日期 (`YYYY-MM-DD`),则在 `~/.claude/sessions/` 中搜索匹配
`YYYY-MM-DD-session.tmp`(旧格式)或 `YYYY-MM-DD-<shortid>-session.tmp`(当前格式)的文件,
并加载该日期最近修改的版本
* 如果看起来像文件路径,则直接读取该文件
* 如果未找到,清晰报告并停止
### 步骤 2读取整个会话文件
读取完整的文件。暂时不要总结。
### 步骤 3确认理解
使用以下确切格式回复一份结构化简报:
```
SESSION LOADED: [actual resolved path to the file]
════════════════════════════════════════════════
PROJECT: [project name / topic from file]
WHAT WE'RE BUILDING:
[2-3 sentence summary in your own words]
CURRENT STATE:
✅ Working: [count] items confirmed
🔄 In Progress: [list files that are in progress]
🗒️ Not Started: [list planned but untouched]
WHAT NOT TO RETRY:
[list every failed approach with its reason — this is critical]
OPEN QUESTIONS / BLOCKERS:
[list any blockers or unanswered questions]
NEXT STEP:
[exact next step if defined in the file]
[if not defined: "No next step defined — recommend reviewing 'What Has NOT Been Tried Yet' together before starting"]
════════════════════════════════════════════════
Ready to continue. What would you like to do?
```
### 步骤 4等待用户
请**不要**自动开始工作。请**不要**触碰任何文件。等待用户指示下一步做什么。
如果会话文件中明确定义了下一步,并且用户说"继续"或"是"或类似内容 — 则执行该确切步骤。
如果未定义下一步 — 询问用户从哪里开始,并可选择性地从"尚未尝试的内容"部分提出建议。
***
## 边界情况
**同一日期有多个会话** (`2024-01-15-session.tmp`, `2024-01-15-abc123de-session.tmp`)
加载该日期最近修改的匹配文件无论其使用的是旧的无ID格式还是当前的短ID格式。
**会话文件引用了已不存在的文件:**
在简报中注明 — "⚠️ 会话中引用了 `path/to/file.ts`,但在磁盘上未找到。"
**会话文件来自超过7天前**
注明时间间隔 — "⚠️ 此会话来自 N 天前阈值7天。情况可能已发生变化。" — 然后正常继续。
**用户直接提供了文件路径(例如,从队友处转发而来):**
读取它并遵循相同的简报流程 — 无论来源如何,格式都是相同的。
**会话文件为空或格式错误:**
报告:"找到会话文件,但似乎为空或无法读取。您可能需要使用 /save-session 创建一个新的。"
***
## 示例输出
```
SESSION LOADED: /Users/you/.claude/sessions/2024-01-15-abc123de-session.tmp
════════════════════════════════════════════════
PROJECT: my-app — JWT Authentication
WHAT WE'RE BUILDING:
User authentication with JWT tokens stored in httpOnly cookies.
Register and login endpoints are partially done. Route protection
via middleware hasn't been started yet.
CURRENT STATE:
✅ Working: 3 items (register endpoint, JWT generation, password hashing)
🔄 In Progress: app/api/auth/login/route.ts (token works, cookie not set yet)
🗒️ Not Started: middleware.ts, app/login/page.tsx
WHAT NOT TO RETRY:
❌ Next-Auth — conflicts with custom Prisma adapter, threw adapter error on every request
❌ localStorage for JWT — causes SSR hydration mismatch, incompatible with Next.js
OPEN QUESTIONS / BLOCKERS:
- Does cookies().set() work inside a Route Handler or only Server Actions?
NEXT STEP:
In app/api/auth/login/route.ts — set the JWT as an httpOnly cookie using
cookies().set('token', jwt, { httpOnly: true, secure: true, sameSite: 'strict' })
then test with Postman for a Set-Cookie header in the response.
════════════════════════════════════════════════
Ready to continue. What would you like to do?
```
***
## 注意事项
* 加载时切勿修改会话文件 — 它是一个只读的历史记录
* 简报格式是固定的 — 即使某些部分为空,也不要跳过
* "不应重试的内容"必须始终显示,即使它只是说"无" — 这太重要了,不容遗漏
* 恢复后,用户可能希望在新的会话结束时再次运行 `/save-session`,以创建一个新的带日期文件

Some files were not shown because too many files have changed in this diff Show More