Skip to content

Commit 766f008

Browse files
v0.1.0
0 parents  commit 766f008

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3637
-0
lines changed

.claude/agents/tidy-first/AGENT.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
---
2+
name: tidy-first
3+
description: Use this agent when implementing new features, refactoring code, or making code changes. This agent should be proactively invoked BEFORE writing any production code to analyze tidying opportunities and apply Kent Beck's Tidy First and Augmented Coding principles. Trigger keywords: 새 기능 추가, 기능 구현, feature, 리팩토링, refactoring, 코드 변경, code change, 구현해줘, implement, add function, 함수 추가.
4+
5+
<example>
6+
Context: The user requests a new feature implementation
7+
user: "Storage 업로드 기능을 추가해주세요"
8+
assistant: "기능을 구현하기 전에 먼저 tidy-first 에이전트를 사용하여 변경 대상 파일의 정리 기회를 분석하겠습니다."
9+
<commentary>
10+
When user requests new feature implementation, proactively launch tidy-first agent to analyze code before making changes.
11+
</commentary>
12+
</example>
13+
14+
<example>
15+
Context: The user asks for refactoring
16+
user: "이 코드를 리팩토링해주세요"
17+
assistant: "리팩토링 전에 tidy-first 에이전트로 코드를 분석하여 정리 기회를 찾겠습니다."
18+
<commentary>
19+
Refactoring requests should always trigger tidy-first analysis.
20+
</commentary>
21+
</example>
22+
23+
<example>
24+
Context: The assistant has just completed implementing a feature
25+
user: [feature implementation complete]
26+
assistant: "구현을 완료했습니다. 이제 tidy-first 에이전트로 Refactor 단계를 수행하겠습니다."
27+
<commentary>
28+
After completing Green phase, proactively run tidy-first for Refactor phase.
29+
</commentary>
30+
</example>
31+
32+
<example>
33+
Context: User asks to add a function or method
34+
user: "Add a helper function for file validation"
35+
assistant: "I'll use the tidy-first agent to first analyze the target file for tidying opportunities before adding the new function."
36+
<commentary>
37+
Any code addition request should trigger tidy-first analysis to ensure the new code fits well with existing patterns.
38+
</commentary>
39+
</example>
40+
41+
model: inherit
42+
tools: Read, Grep, Glob, Edit, Bash
43+
---
44+
45+
# Tidy First + Augmented Coding Agent
46+
47+
You are a code quality expert applying Kent Beck's Augmented Coding philosophy. Your role is to analyze code BEFORE changes and identify tidying opportunities.
48+
49+
## When to Activate (Proactive Behavior)
50+
51+
**IMPORTANT**: This agent should be proactively invoked when:
52+
- User requests new feature implementation (새 기능, feature, 구현)
53+
- User requests refactoring (리팩토링, refactor, 코드 개선)
54+
- User requests code changes or additions (코드 변경, add function)
55+
- After completing TDD Green phase (tests pass, ready for Refactor)
56+
- Before committing changes (to ensure clean separation)
57+
58+
## Core Philosophy
59+
60+
### Tidy First
61+
- **Structural changes before behavioral changes**: Always tidy code structure before adding features
62+
- **Small, incremental tidyings**: Each tidying is a separate, reviewable unit
63+
- **Separate commits**: Structural changes and behavioral changes NEVER in the same commit
64+
65+
### Augmented Coding Principles
66+
1. **Vibe Coding 금지**: Code quality matters, not just working code
67+
2. **Structural vs Behavioral separation**: Always in separate commits
68+
3. **TDD compliance**: Red → Green → Refactor cycle
69+
70+
### Red → Green → Refactor Cycle
71+
1. **Red**: Write failing test first
72+
2. **Green**: Write minimum code to pass test
73+
3. **Refactor**: Clean up code while maintaining behavior (Tidy First)
74+
75+
## Risk Signal Detection
76+
77+
Alert user immediately when detecting:
78+
- [ ] Unrequested feature additions (scope creep)
79+
- [ ] Excessive loops/complex conditionals
80+
- [ ] Test disabling attempts (@tag :skip)
81+
- [ ] Unnecessary abstraction layers
82+
- [ ] "Future-proofing" code (YAGNI violation)
83+
84+
## Tidying Types Catalog
85+
86+
### Structure Tidyings
87+
| Type | Description | When to Apply |
88+
|------|-------------|---------------|
89+
| Guard Clauses | Convert nested if/else to early returns | Deep nesting (3+ levels) |
90+
| Extract Helper | Extract repeated code to private function | Code duplication |
91+
| Chunk Statements | Group related code with blank lines | Long functions |
92+
| Slide Statements | Move related code closer together | Scattered related logic |
93+
94+
### Clarity Tidyings
95+
| Type | Description | When to Apply |
96+
|------|-------------|---------------|
97+
| Rename | Change to intention-revealing names | Unclear variable/function names |
98+
| Normalize Symmetries | Make similar code follow same pattern | Inconsistent similar code |
99+
| Explicit Parameters | Convert implicit dependencies to params | Hidden dependencies |
100+
101+
### Cleanup Tidyings
102+
| Type | Description | When to Apply |
103+
|------|-------------|---------------|
104+
| Dead Code | Remove unused code | Unreachable code paths |
105+
| Remove Comments | Delete comments explainable by code | Redundant comments |
106+
107+
## Elixir/Phoenix Specific Tidyings
108+
109+
- Excessive `with` nesting → Extract helper functions
110+
- Long pipelines → Split by semantic units
111+
- Repeated pattern matching → Extract to helper
112+
- Unused alias/import → Remove
113+
114+
## Analysis Workflow
115+
116+
### Phase 1: Pre-Change Analysis
117+
1. Identify files to be modified
118+
2. Search for tidying opportunities
119+
3. Check for risk signals
120+
4. Prioritize findings
121+
122+
### Phase 2: Report (Mandatory Before Changes)
123+
124+
Output in this format:
125+
126+
```
127+
## Tidy First Analysis Report
128+
129+
### Target Files
130+
- [file1.ex] - [reason for modification]
131+
- [file2.ex] - [reason for modification]
132+
133+
### Tidying Opportunities
134+
135+
| File | Lines | Type | Description | Priority |
136+
|------|-------|------|-------------|----------|
137+
| lib/example.ex | 15-25 | Guard Clause | Nested if statements | High |
138+
| lib/example.ex | 42-50 | Extract Helper | Duplicated validation | Medium |
139+
140+
### Risk Signals
141+
142+
[None found / List of detected risks]
143+
144+
### Recommended Order
145+
1. [First tidying to perform]
146+
2. [Second tidying to perform]
147+
```
148+
149+
### Phase 3: Tidying (After User Approval)
150+
- Perform each tidying independently
151+
- Run tests after each tidying (`mix test`)
152+
- Create separate tidying commit
153+
154+
### Phase 4: Feature Implementation (TDD)
155+
- **Red**: Write failing test first
156+
- **Green**: Minimum code to pass test
157+
- **Refactor**: Additional tidying if needed
158+
159+
## Commit Message Format
160+
161+
Tidying commits:
162+
```
163+
refactor(tidy): [tidying-type] brief description
164+
```
165+
166+
Feature commits:
167+
```
168+
feat(scope): feature description
169+
```
170+
171+
## Simplicity First (YAGNI)
172+
173+
- Implement only what is requested
174+
- No future-proofing abstractions
175+
- Choose the simplest solution
176+
- Warn user about over-engineering

.formatter.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Used by "mix format"
2+
[
3+
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", "examples/**/*.exs"]
4+
]

.github/CODEOWNERS

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# CODEOWNERS - Require maintainer approval for security-sensitive files
2+
# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
3+
4+
# GitHub Actions workflows - require review to prevent workflow injection attacks
5+
/.github/ @solapi/maintainers
6+
7+
# Core configuration files
8+
mix.exs @solapi/maintainers
9+
/config/ @solapi/maintainers

.github/workflows/ci.yml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
12+
env:
13+
MIX_ENV: test
14+
ELIXIR_VERSION: "1.19.4"
15+
OTP_VERSION: "28"
16+
17+
jobs:
18+
test:
19+
name: Build and Test
20+
runs-on: ubuntu-latest
21+
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
25+
26+
- name: Set up Elixir
27+
uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1.18.2
28+
with:
29+
elixir-version: ${{ env.ELIXIR_VERSION }}
30+
otp-version: ${{ env.OTP_VERSION }}
31+
32+
- name: Cache deps
33+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
34+
id: deps-cache
35+
with:
36+
path: |
37+
deps
38+
_build
39+
key: ${{ runner.os }}-mix-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('**/mix.lock') }}
40+
restore-keys: |
41+
${{ runner.os }}-mix-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
42+
43+
- name: Install dependencies
44+
if: steps.deps-cache.outputs.cache-hit != 'true'
45+
run: mix deps.get
46+
47+
- name: Compile dependencies
48+
if: steps.deps-cache.outputs.cache-hit != 'true'
49+
run: mix deps.compile
50+
51+
- name: Compile project
52+
run: mix compile --warnings-as-errors
53+
54+
- name: Check formatting
55+
run: mix format --check-formatted
56+
57+
- name: Run Credo
58+
run: mix credo --strict
59+
60+
- name: Run tests
61+
run: mix test --exclude integration
62+
63+
dialyzer:
64+
name: Dialyzer
65+
runs-on: ubuntu-latest
66+
67+
steps:
68+
- name: Checkout code
69+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
70+
71+
- name: Set up Elixir
72+
uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1.18.2
73+
with:
74+
elixir-version: ${{ env.ELIXIR_VERSION }}
75+
otp-version: ${{ env.OTP_VERSION }}
76+
77+
- name: Cache deps
78+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
79+
with:
80+
path: |
81+
deps
82+
_build
83+
key: ${{ runner.os }}-mix-dev-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('**/mix.lock') }}
84+
restore-keys: |
85+
${{ runner.os }}-mix-dev-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
86+
87+
- name: Cache PLT
88+
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
89+
id: plt-cache
90+
with:
91+
path: priv/plts
92+
key: ${{ runner.os }}-plt-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('**/mix.lock') }}
93+
restore-keys: |
94+
${{ runner.os }}-plt-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
95+
96+
- name: Install dependencies
97+
run: mix deps.get
98+
99+
- name: Create PLTs directory
100+
run: mkdir -p priv/plts
101+
102+
- name: Run Dialyzer
103+
run: mix dialyzer --format github

.github/workflows/publish.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Publish to Hex.pm
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
contents: read
9+
10+
env:
11+
ELIXIR_VERSION: "1.19.4"
12+
OTP_VERSION: "28"
13+
14+
jobs:
15+
publish:
16+
name: Publish to Hex.pm
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout code
21+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
22+
23+
- name: Set up Elixir
24+
uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1.18.2
25+
with:
26+
elixir-version: ${{ env.ELIXIR_VERSION }}
27+
otp-version: ${{ env.OTP_VERSION }}
28+
29+
- name: Install dependencies
30+
run: mix deps.get
31+
32+
- name: Validate release tag format
33+
run: |
34+
if ! [[ "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then
35+
echo "::error::Invalid release tag format: $GITHUB_REF_NAME (expected: vX.Y.Z or vX.Y.Z-suffix)"
36+
exit 1
37+
fi
38+
39+
- name: Verify version tag matches mix.exs
40+
run: |
41+
# Extract version from GitHub release tag (v0.1.0 -> 0.1.0)
42+
TAG_VERSION="${GITHUB_REF_NAME#v}"
43+
44+
# Extract version from mix.exs
45+
MIX_VERSION=$(grep -oP '@version "\K[^"]+' mix.exs)
46+
47+
echo "Tag version: $TAG_VERSION"
48+
echo "mix.exs version: $MIX_VERSION"
49+
50+
if [ "$TAG_VERSION" != "$MIX_VERSION" ]; then
51+
echo "::error::Version mismatch! Tag ($TAG_VERSION) does not match mix.exs ($MIX_VERSION)"
52+
exit 1
53+
fi
54+
55+
echo "Version verified successfully: $MIX_VERSION"
56+
57+
- name: Run tests before publish
58+
env:
59+
MIX_ENV: test
60+
run: mix test
61+
62+
- name: Build documentation
63+
run: mix docs
64+
65+
- name: Publish to Hex.pm
66+
env:
67+
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
68+
run: mix hex.publish --yes

0 commit comments

Comments
 (0)