π€ Contributing to ZSHANDΒΆ
π This guide covers dev setup, workflow, conventions, and quality requirements for contributing to the ZSHAND framework.
π§ Philosophy
Every contribution should leave the codebase faster, better documented, and better tested than before. Headers are not optional β they're how we communicate intent to humans, AI, and tooling alike.
π Getting StartedΒΆ
π PrerequisitesΒΆ
| Tool | Min Version | Purpose | Install |
|---|---|---|---|
| π zsh | 5.0+ | Shell runtime | System package manager |
| π¦ git | 2.20+ | Version control | sudo apt install git |
| π direnv | β | Secrets & env management | mise install direnv |
| π just | β | Task runner / orchestration | mise install just |
| π§ͺ ShellSpec | β | BDD test framework | mise install shellspec |
| β¨ Trunk | 1.0+ | Linting & formatting | trunk.io |
| β‘ Hyperfine | β | Benchmarking | mise install hyperfine |
| π§ mise | 1.0+ | Polyglot runtime manager | mise.jdx.dev |
π οΈ Dev SetupΒΆ
Option 1: Dev Container (Recommended for new contributors)
# 1. Open in VS Code/Cursor with Dev Containers extension
# 2. Click "Reopen in Container" when prompted
# 3. Everything is set up automatically!
# - All tools installed via mise
# - zsh and zshdb ready for debugging
# - Consistent environment for all contributors
See the π³ Using Dev Containers section for details.
Option 2: Local Setup
# 1. Clone the repo
git clone https://github.com/presempathy/zshand.git ~/code/zshand
cd ~/code/zshand
# 2. Install tools via mise
mise install
# 3. Run first-time setup (installs deps, creates user config dir)
zsh setup.zsh
# 4. Enable dev mode (disables bundles, enables debug output)
export ZSHAND_DEV_MODE=1
exec zsh
# 5. Verify everything works
just test # Run full test suite
just lint # Run linters
zcheck # Framework integrity audit
Dev mode vs production
With ZSHAND_DEV_MODE=1, the framework loads individual files instead of compiled bundles, and enables debug output. Always develop in this mode so you see changes immediately without recompiling.
π Using Just (Task Runner)ΒΆ
Just is the project's task runner. It provides a consistent interface for common development tasks.
π Quick StartΒΆ
π Common TasksΒΆ
| Task | Description | Usage |
|---|---|---|
just test | Run all ShellSpec tests | just test or just test spec/path/ |
just lint | Run all linters (Trunk) | just lint |
just format | Auto-format code | just format |
just build | Rebuild all bundles | just build |
just check | Run full quality gate | just check |
β οΈ Important NotesΒΆ
- Always run
just testbefore committing to ensure tests pass - Run
just lintto catch formatting and linting issues early - Use
just checkbefore opening a PR to run the full quality gate - If a task fails, fix the errors before proceeding β don't skip tests or linting
Don't break the build
Never commit code that fails just test or just lint. These checks run in CI/CD and will block your PR. If tests are failing, fix them locally first.
π³ Using Dev ContainersΒΆ
Dev Containers provide a consistent development environment across all contributors, ensuring everyone has the same tools and dependencies.
π Quick StartΒΆ
- Open in VS Code/Cursor with the Dev Containers extension installed
- Click "Reopen in Container" when prompted, or use Command Palette:
F1βDev Containers: Reopen in Container- Wait for setup β the container will build and install dependencies automatically
π¦ What's IncludedΒΆ
The dev container automatically installs:
- zsh and zshdb (for debugging)
- mise (tool version manager)
- All development tools via mise (node, go, rust, uv, shellspec, just)
- Git and GitHub CLI
π§ ConfigurationΒΆ
The dev container configuration is in .devcontainer/devcontainer.json:
- Base image:
mcr.microsoft.com/devcontainers/base:ubuntu - Features: Common utilities, Git, Python
- Post-create: Installs zsh and zshdb
π Debugging in Dev ContainerΒΆ
The dev container includes zshdb for debugging zsh scripts:
- Set breakpoints in your
.zshfiles - Use VS Code's Debug panel (F5)
- Select "Debug Zsh Script" configuration
- Step through code with full debugging support
β οΈ Important NotesΒΆ
- All development happens inside the container β your local files are mounted
- Changes persist β files are synced between container and host
- Run
just testinside the container to ensure compatibility - Don't install tools manually β use mise or the container's package manager
First-time setup
The first time you open the dev container, it may take a few minutes to build. Subsequent opens are much faster thanks to Docker layer caching.
π§ͺ Using ShellSpecΒΆ
ShellSpec is the BDD-style testing framework used for all test suites. It provides descriptive test syntax and comprehensive assertion matchers.
π Quick StartΒΆ
# Run all tests
shellspec
# Run specific spec file
shellspec spec/functions/dcopy_spec.sh
# Run tests in a directory
shellspec spec/core/
# Run with verbose output
shellspec --format documentation
# Run with coverage (requires kcov)
shellspec --kcov
π Writing ShellSpec TestsΒΆ
Basic StructureΒΆ
#!/usr/bin/env shellspec
# ββ function_name_spec β Tests for function_name ββββββββββββββββββββββββββ
Describe "function_name"
# Setup (runs before each test)
BeforeAll 'source "${PROJECT_ROOT}/functions/function_name.zsh"'
It "returns success on valid input"
When call function_name "valid_arg"
The status should be success
The output should include "expected text"
End
It "exits with error when argument missing"
When call function_name
The status should be failure
The stderr should include "ERROR:"
End
It "handles edge cases correctly"
When call function_name ""
The status should be failure
End
End
Common MatchersΒΆ
| Matcher | Purpose | Example |
|---|---|---|
The status should be success | Check exit code 0 | The status should be success |
The status should be failure | Check non-zero exit | The status should be failure |
The output should include | Check stdout contains | The output should include "text" |
The stderr should include | Check stderr contains | The stderr should include "ERROR" |
The output should eq | Exact match | The output should eq "exact" |
The path should exist | File exists | The path "file.txt" should exist |
Test OrganizationΒΆ
spec/
βββ functions/
β βββ dcopy_spec.sh # Tests for functions/dcopy.zsh
βββ core/
β βββ 08_audit_spec.sh # Tests for core/08_audit.zsh
βββ shared_functions/
βββ 01_stderr_error_spec.sh # Tests for shared_functions/01_stderr_error.zsh
Rule: Spec files mirror the source tree structure.
β οΈ Important NotesΒΆ
- Always write tests for new functions or behavior changes
- Run tests before committing β use
just testorshellspec - Keep tests fast β avoid slow operations (network, file I/O) unless testing them
- Test edge cases β empty strings, missing args, invalid input
- Use descriptive test names β
It "does something specific"notIt "works"
Don't skip tests
All tests must pass before committing. If a test fails: 1. Read the error message carefully 2. Fix the code (not the test, unless the test is wrong) 3. Re-run the test to verify the fix 4. Run the full suite with just test before committing
π Coverage ExpectationsΒΆ
| Change Type | Minimum Coverage |
|---|---|
| New function | β Happy path + β¬ error paths + β¬ edge cases |
| Bug fix | β Regression test proving the fix |
| Refactor | β Existing tests still pass (no weakening) |
| Performance | β Behavioral tests + β‘ benchmark comparison |
π ConventionsΒΆ
π Header StandardΒΆ
Every file must have a documentation header following HEADER_STANDARD.md. This is non-negotiable.
| File Location | Tier | Title Format |
|---|---|---|
build/*.zsh (complex) | π Full | βββ box |
core/*.zsh, functions/*.zsh, hooks/*.zsh, bin/* | π Standard | ββ name β Description ββ |
shared_functions/*.zsh | π¦ Compact | ββ name β Description ββ |
config/*.conf, .zshand-deps | π·οΈ Minimal | Brief # comment block |
Quick checklist for every new file:
- β Title line or box at correct tier
- β USAGE section with signature and arguments
- β DEPENDENCIES section (even if "None")
- β EXIT CODES section
- β EXAMPLES section (copy-pasteable)
- β TESTING section (spec path, status, must-cover list)
- β SEE ALSO section (related files)
- β
Closing rule (
# ββ...ββ) - β
All dividers exactly 78 chars after
#
π’ File NamingΒΆ
| Directory | Pattern | Example |
|---|---|---|
core/ | NN_name.zsh (even numbers) | 08_audit.zsh |
shared_functions/ | NN_name.zsh | 01_stderr_error.zsh |
startup/ | NN_az_name.zsh | 24_az_safe_mode.zsh |
hooks/ | NN_name.zsh | 01_mise.zsh |
functions/ | name.zsh (no prefix) | dcopy.zsh |
widgets/ | name.zsh (no prefix) | aicmit.zsh |
bin/ | name (no extension) | aicontext |
private_functions/ | _name.zsh (underscore prefix) | _zshand_network_monitor.zsh |
π·οΈ Function NamingΒΆ
| Scope | Convention | Example |
|---|---|---|
| π Public (user-facing) | Short, memorable | zcheck, dcopy, zprime |
| π Private (framework-internal) | _az_ or _zshand_ prefix | _az_safe_mode_enter |
| π§± Shared utility | Descriptive, lowercase | stderr_error, load_file_timed |
| β¨οΈ Widget | Descriptive, lowercase | aicmit, bufcopy |
π WorkflowΒΆ
πΏ Branch StrategyΒΆ
main β stable, always passes tests
βββ feat/name β feature branches (short-lived)
βββ fix/name β bug fix branches
βββ docs/name β documentation-only changes
β Before SubmittingΒΆ
Run the full quality gate:
# 1. Lint (fixes formatting automatically)
just lint # or: trunk check
# 2. Test (all tests must pass)
just test # Full ShellSpec suite
shellspec spec/core/ # Specific directory
# 3. Verify headers
grep -c '# ββ' <your-file> # Count section dividers
# 4. Benchmark (if touching startup path)
hyperfine --warmup 3 'zsh -ic exit'
# 5. Compile and verify
zprime # Rebuild all bundles
zcheck # Framework integrity
# Or run everything at once:
just check # Runs lint + test + build
Don't break the build
All tests (just test) and linters (just lint) must pass before submitting. These checks run automatically in CI/CD and will block your PR if they fail.
π Commit MessagesΒΆ
Follow conventional commit format:
| Type | When |
|---|---|
feat | New feature or function |
fix | Bug fix |
docs | Documentation only |
perf | Performance improvement |
refactor | Code restructure (no behavior change) |
test | Adding or updating tests |
chore | Build system, deps, tooling |
π§ͺ Testing RequirementsΒΆ
Every contribution that adds or modifies behavior must include tests using ShellSpec.
See ShellSpec section above
For detailed information on writing and running ShellSpec tests, see the π§ͺ Using ShellSpec section above.
π Where Tests GoΒΆ
Spec files mirror the source tree:
functions/dcopy.zsh β spec/functions/dcopy_spec.sh
core/08_audit.zsh β spec/core/08_audit_spec.sh
shared_functions/01_stderr_error.zsh β spec/shared_functions/01_stderr_error_spec.sh
π Running TestsΒΆ
# Run all tests (recommended)
just test
# Run specific spec file
shellspec spec/functions/dcopy_spec.sh
# Run tests in a directory
shellspec spec/core/
# Run with verbose output
shellspec --format documentation
π Coverage ExpectationsΒΆ
| Change Type | Minimum Coverage |
|---|---|
| New function | β Happy path + β¬ error paths + β¬ edge cases |
| Bug fix | β Regression test proving the fix |
| Refactor | β Existing tests still pass (no weakening) |
| Performance | β Behavioral tests + β‘ benchmark comparison |
Never weaken tests
Do not delete or relax existing test assertions without explicit approval. If a test needs updating, explain why in the commit message.
Tests must pass before committing
Always run just test before committing. Failing tests will block your PR in CI/CD.
β¨ Linting & FormattingΒΆ
The framework uses Trunk to manage all linters:
| Linter | Checks | Config |
|---|---|---|
| π ShellCheck | Shell script correctness | .trunk/configs/.shellcheckrc |
| π shfmt | Shell formatting | .editorconfig |
| π markdownlint | Markdown formatting | .trunk/configs/.markdownlint.yaml |
| π gitleaks | Secret detection | Trunk default |
| π cspell | Spelling | .cspell.json |
| π prettier | Markdown/JSON formatting | .prettierrc.yaml |
π Running LintersΒΆ
trunk check # Check all changed files
trunk check --all # Check everything
trunk check <file> # Check specific file
trunk fmt # Auto-fix formatting issues
β οΈ ShellCheck OverridesΒΆ
If you must disable a ShellCheck rule:
- Always add a comment explaining why
- Prefer per-line disables over per-file
- If the same disable appears in 3+ files, add it to
.shellcheckrcinstead
β‘ Performance GuidelinesΒΆ
The framework has strict performance targets:
| Metric | Target | Measured By |
|---|---|---|
| β‘ Cold start | <50ms | hyperfine 'zsh -ic exit' |
| β‘ Warm start (P10k) | <10ms | Instant prompt to interactive |
| π Per-file budget | <100ms | ZSHAND_PERF_BUDGET enforcement |
π RulesΒΆ
- π« No network calls in startup path (hooks can defer)
- π« No subprocess spawning unless absolutely necessary
- β Use zsh builtins over external commands where possible
- β Lazy-load heavy integrations (Docker, cloud CLIs)
- β Cache results that don't change per-session
- β
Profile before and after with
ZSHAND_PROFILE_BUNDLE=1
π Benchmarking a ChangeΒΆ
# Before your change
hyperfine --warmup 3 'zsh -ic exit' --export-json before.json
# Make your change, rebuild
zprime
# After your change
hyperfine --warmup 3 'zsh -ic exit' --export-json after.json
# Compare (manual or via script)
π Adding a New FileΒΆ
Step-by-stepΒΆ
- Choose the right directory (see ARCHITECTURE.md)
- Pick the correct number prefix (even = framework, odd = user slot)
- Write the header per HEADER_STANDARD.md
- Implement the functionality
- Write tests in the mirrored spec location
- Run the quality gate (lint, test, benchmark)
- Rebuild bundles with
zprime - Verify with
zcheck
ποΈ Example: Adding a new functionΒΆ
# 1. Create the function
vi functions/my_function.zsh
# 2. Write header (Standard tier for functions/)
# Include: USAGE, DEPENDENCIES, EXIT CODES, EXAMPLES, TESTING, SEE ALSO
# 3. Create the spec
vi spec/functions/my_function_spec.sh
# 4. Verify
just test spec/functions/my_function_spec.sh
trunk check functions/my_function.zsh
zprime
π Related DocumentsΒΆ
| Document | Purpose |
|---|---|
| ποΈ ARCHITECTURE.md | Framework structure and boot sequence |
| π HEADER_STANDARD.md | Documentation header conventions |
| π§ͺ TESTING.md | Detailed testing guide |
| β‘ PERFORMANCE.md | Performance profiling and optimization |
| π¨ STYLE_GUIDE.md | Code style conventions |