Skip to content

Commit 0d4faac

Browse files
authored
chore(claude): optimize config for agentic coding (#606)
1 parent 0d89097 commit 0d4faac

File tree

14 files changed

+269
-2609
lines changed

14 files changed

+269
-2609
lines changed

.claude/CLAUDE.md

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Works on macOS default bash. **Prohibited features:**
2020
- `${array[-1]}` (negative indexing - Bash 4.3+)
2121
- `&>>` redirect (Bash 4.0+)
2222

23-
See @.claude/rules/bash-style.md for complete compatibility guide.
23+
See `.claude/rules/bash-style.md` for complete compatibility guide (auto-loaded when editing `src/` or `tests/`).
2424

2525
### Quality Standards
2626

@@ -90,19 +90,10 @@ Invoke with `/skill-name`:
9090
| `/gh-issue <N>` | GitHub issue → branch → implement → PR |
9191
| `/pr [#N]` | Push branch and create PR |
9292

93-
## Code Standards
94-
95-
### Bash Style
96-
@.claude/rules/bash-style.md
97-
98-
### Testing
99-
@.claude/rules/testing.md
100-
101-
### TDD Workflow
102-
@.claude/rules/tdd-workflow.md
103-
10493
## Path-Scoped Guidelines
10594

95+
Rules auto-load based on file paths being edited (via `paths:` frontmatter in each rule file).
96+
10697
### `src/**/*.sh`
10798
- Small, portable functions
10899
- Bash 3.0+ compatibility (no associative arrays, no `[[`, no `${var,,}`)

.claude/rules/bash-style.md

Lines changed: 27 additions & 301 deletions
Original file line numberDiff line numberDiff line change
@@ -10,322 +10,48 @@ paths:
1010

1111
bashunit must work on **Bash 3.0+** (macOS default). These features are **prohibited**:
1212

13-
### ❌ Forbidden Features
13+
| Feature | Bash ver | Alternative |
14+
|---------|----------|-------------|
15+
| `declare -A` (associative arrays) | 4.0+ | Parallel indexed arrays |
16+
| `[[ ]]` (test operator) || `[ ]` with `=` not `==` |
17+
| `${var,,}` / `${var^^}` (case) | 4.0+ | `tr '[:upper:]' '[:lower:]'` |
18+
| `${array[-1]}` (negative index) | 4.3+ | `${array[${#array[@]}-1]}` |
19+
| `&>>` (append both) | 4.0+ | `>> file 2>&1` |
1420

15-
**Associative arrays** (Bash 4.0+):
16-
```bash
17-
# ❌ DON'T
18-
declare -A map
19-
map["key"]="value"
20-
21-
# ✅ DO - Use indexed arrays or workarounds
22-
declare -a keys=("key1" "key2")
23-
declare -a values=("val1" "val2")
24-
```
25-
26-
**`[[` test operator** - Use `[` instead:
27-
```bash
28-
# ❌ DON'T
29-
if [[ "$var" == "value" ]]; then
30-
31-
# ✅ DO
32-
if [ "$var" = "value" ]; then
33-
```
34-
35-
**Case conversion** (`${var,,}`, `${var^^}`):
36-
```bash
37-
# ❌ DON'T
38-
lowercase="${var,,}"
39-
40-
# ✅ DO
41-
lowercase=$(echo "$var" | tr '[:upper:]' '[:lower:]')
42-
```
43-
44-
**Negative array indexing** (`${array[-1]}`):
45-
```bash
46-
# ❌ DON'T
47-
last="${array[-1]}"
48-
49-
# ✅ DO
50-
last="${array[${#array[@]}-1]}"
51-
```
52-
53-
**`&>>` redirect** (Bash 4.0+):
54-
```bash
55-
# ❌ DON'T
56-
command &>> file
57-
58-
# ✅ DO
59-
command >> file 2>&1
60-
```
61-
62-
## Coding Style
63-
64-
Follow [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html) with these specifics:
65-
66-
### Indentation & Formatting
67-
68-
- **2 spaces** (no tabs)
69-
- Use `shfmt -w .` to format
70-
- Maximum line length: 120 characters (soft limit)
71-
72-
### Function Naming
73-
74-
**Namespace all public functions:**
75-
```bash
76-
# ✅ Public functions
77-
function bashunit::assert_equals() { ... }
78-
function bashunit::mock() { ... }
79-
80-
# ✅ Private/internal functions (leading underscore)
81-
function _internal_helper() { ... }
82-
```
83-
84-
### Variable Naming
85-
86-
```bash
87-
# ✅ Local variables - lowercase with underscores
88-
local test_name="example"
89-
local file_path="/path/to/file"
90-
91-
# ✅ Global/exported - uppercase
92-
export BASHUNIT_LOG_JUNIT="false"
93-
readonly BASHUNIT_VERSION="0.32.0"
94-
95-
# ✅ Function parameters - clear names
96-
function process_file() {
97-
local input_file="$1"
98-
local output_dir="${2:-./output}"
99-
}
100-
```
21+
## Coding Conventions
10122

102-
### Quoting
23+
- **2 spaces** indent, no tabs — enforced by `shfmt -w .`
24+
- **120 chars** max line length (soft)
25+
- Follow [Google Shell Style Guide](https://google.github.io/styleguide/shellguide.html)
26+
- Always quote variables unless explicit word splitting is needed
27+
- Use `$()` for command substitution, never backticks
10328

104-
**Always quote variables** unless you explicitly need word splitting:
29+
### Naming
10530

106-
```bash
107-
# ✅ DO
108-
echo "$variable"
109-
[[ -f "$file_path" ]]
110-
command --arg="$value"
111-
112-
# ❌ DON'T (unless intentional word splitting)
113-
echo $variable
114-
[[ -f $file_path ]]
115-
```
116-
117-
### Error Handling
31+
- **Public functions:** `bashunit::function_name`
32+
- **Private functions:** `_function_name` (leading underscore)
33+
- **Local variables:** `lowercase_with_underscores`
34+
- **Globals/exports:** `UPPERCASE_WITH_UNDERSCORES`
11835

119-
Use `set -euo pipefail` judiciously:
120-
121-
```bash
122-
# ✅ In scripts
123-
#!/usr/bin/env bash
124-
set -euo pipefail
125-
126-
# ⚠️ In functions - be cautious
127-
# Don't use in functions that expect to handle failures
128-
function might_fail() {
129-
local result
130-
result=$(command_that_might_fail) || return 1
131-
echo "$result"
132-
}
133-
```
134-
135-
### Function Documentation
136-
137-
Document all public functions:
36+
### Function Docs (public functions)
13837

13938
```bash
14039
##
141-
# Brief description of what the function does
142-
#
143-
# Arguments:
144-
# $1 - First parameter description
145-
# $2 - Second parameter description (optional, default: "value")
146-
#
147-
# Returns:
148-
# 0 on success
149-
# 1 on validation failure
150-
# 2 on execution error
151-
#
152-
# Example:
153-
# bashunit::my_function "input" "optional"
40+
# Brief description
41+
# Arguments: $1 - desc, $2 - desc (optional, default: "x")
42+
# Returns: 0 success, 1 failure
15443
##
155-
function bashunit::my_function() {
156-
local required="$1"
157-
local optional="${2:-default}"
158-
159-
# Implementation
160-
}
16144
```
16245

163-
## ShellCheck Compliance
164-
165-
All code must pass ShellCheck:
166-
167-
```bash
168-
make sa
169-
# or
170-
shellcheck -x $(find . -name "*.sh")
171-
```
172-
173-
**Common directives:**
174-
175-
```bash
176-
# Disable specific check with reason
177-
# shellcheck disable=SC2034 # Variable appears unused
178-
local unused_var="value"
179-
180-
# Source external file for shellcheck
181-
# shellcheck source=src/assertions.sh
182-
source "$(dirname "${BASH_SOURCE[0]}")/assertions.sh"
183-
```
184-
185-
## Portability
186-
187-
### Path Handling
188-
189-
```bash
190-
# ✅ Use BASH_SOURCE for relative paths
191-
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
192-
193-
# ✅ Use dirname/basename
194-
parent_dir="$(dirname "$file_path")"
195-
filename="$(basename "$file_path")"
196-
```
197-
198-
### Command Substitution
199-
200-
```bash
201-
# ✅ Use $() instead of backticks
202-
result=$(command arg1 arg2)
203-
204-
# ❌ DON'T
205-
result=`command arg1 arg2`
206-
```
207-
208-
### Temporary Files
209-
210-
```bash
211-
# ✅ Use bashunit globals
212-
echo "content" > "$temp_file"
213-
mkdir -p "$temp_dir"
214-
215-
# ⚠️ If creating manually, ensure cleanup
216-
cleanup() {
217-
rm -rf "$temp_file"
218-
}
219-
trap cleanup EXIT
220-
```
221-
222-
## Performance Considerations
223-
224-
### Avoid Subshells When Possible
225-
226-
```bash
227-
# ✅ Better
228-
local count=0
229-
while read -r line; do
230-
((count++))
231-
done < file
232-
233-
# ⚠️ Slower (creates subshell)
234-
local count
235-
count=$(wc -l < file)
236-
```
237-
238-
### Use Built-ins Over External Commands
239-
240-
```bash
241-
# ✅ Built-in
242-
[[ -f "$file" ]] && echo "exists"
243-
244-
# ⚠️ External command (slower)
245-
test -f "$file" && echo "exists"
246-
```
247-
248-
## Code Organization
249-
25046
### File Structure
25147

252-
```bash
253-
#!/usr/bin/env bash
254-
# Brief file description
255-
256-
# Constants
257-
readonly CONSTANT_VALUE="value"
258-
259-
# Global variables
260-
declare -g global_var=""
261-
262-
# Private functions
263-
function _private_helper() { ... }
264-
265-
# Public functions
266-
function bashunit::public_function() { ... }
267-
```
268-
269-
### Sourcing Dependencies
270-
271-
```bash
272-
# ✅ Relative to script location
273-
source "$(dirname "${BASH_SOURCE[0]}")/dependency.sh"
274-
275-
# ✅ With error checking
276-
if [[ ! -f "$dependency_path" ]]; then
277-
echo "Error: Cannot find dependency" >&2
278-
return 1
279-
fi
280-
source "$dependency_path"
281-
```
282-
283-
## Security
284-
285-
### Input Validation
286-
287-
```bash
288-
# ✅ Validate inputs
289-
function process_user_input() {
290-
local input="$1"
291-
292-
if [[ -z "$input" ]]; then
293-
echo "Error: Input required" >&2
294-
return 1
295-
fi
296-
297-
# Process sanitized input
298-
}
299-
```
300-
301-
### Safe File Operations
302-
303-
```bash
304-
# ✅ Check before operations
305-
if [[ -w "$file" ]]; then
306-
echo "data" > "$file"
307-
fi
308-
309-
# ✅ Use -- to prevent flag injection
310-
rm -- "$user_provided_file"
311-
```
312-
313-
## Anti-Patterns to Avoid
48+
Constants -> Globals -> Private functions -> Public functions
31449

315-
**Global state without cleanup**
316-
**Unquoted variables**
317-
**Ignoring command failures silently**
318-
**Using eval without sanitization**
319-
**Hardcoded paths** (use relative or configurable)
320-
**Functions > 50 lines** (refactor into smaller pieces)
321-
**Deep nesting** (> 3 levels, extract functions)
50+
Source deps relative to script: `"$(dirname "${BASH_SOURCE[0]}")/dep.sh"`
32251

323-
## Validation
52+
## ShellCheck
32453

325-
Before committing:
54+
All code must pass `make sa`. Use directives sparingly with reason:
32655
```bash
327-
make sa # ShellCheck
328-
make lint # EditorConfig
329-
shfmt -w . # Format
330-
./bashunit tests/ # All tests pass
56+
# shellcheck disable=SC2034 # Variable used by caller
33157
```

0 commit comments

Comments
 (0)