Quick reference for development workflows and processes.
For user documentation, see docs/ directory and README.md.
- docs/developer-guide/index.md: Getting started, development setup, commit guidelines
- docs/developer-guide/architecture.md: System design, architecture patterns, design decisions
- docs/developer-guide/implementation.md: Module reference, implementation details, technical gotchas
Framework: Bats (Bash Automated Testing System)
# Using test runner (RECOMMENDED)
./tests/run-tests.sh unit # Run all unit tests
./tests/run-tests.sh integration # Run all integration tests
./tests/run-tests.sh all # Run all tests (~137 tests)
./tests/run-tests.sh quick # Unit tests in parallel
# Direct bats invocation
bats tests/unit/config.bats # Specific unit test
bats tests/integration/run.bats # Specific integration testStructure: tests/unit/ (one file per lib/*.sh) + tests/integration/ (one file per CLI command)
Details: See docs/developer-guide/implementation.md#testing-implementation
When making interface changes:
- Code implementation and tests
lib/help.sh: CLI help textdocs/: User-facing documentation- README.md and docs/index.md: Keep synchronized (Key Features must match)
- Verify with
mkdocs serve(activate venv first:source venv/bin/activate)
Hierarchy:
README.md → Brief overview, links to docs
docs/index.md → Landing page (must sync with README.md)
docs/getting-started/ → Installation, tutorials
docs/user-guide/ → Complete usage guide
docs/reference/ → CLI command reference (per-command pages)
lib/help.sh → CLI help text
docs/developer-guide/ → Architecture, implementation, layer docs
Key Features (README.md and docs/index.md):
- 🎯 **Feature Name:** Punchy one-liner description.CLI help option formatting:
--long-option, -s <arg> Description text
Nested bullets: Use 4 spaces (not 2)
Lists after text: Add blank line before lists (required for mkdocs rendering)
Subcommand ordering: init → new → edit → config → run → task → cat → open → tasks → list → help
- Add case to
wireflow.shsubcommand dispatcher - Implement in
lib/core.sh(or new lib file if complex) - Add help function to
lib/help.sh - Add
-hcheck to subcommand case - Add integration test:
tests/integration/<subcommand>.bats - Add unit tests:
tests/unit/<lib>.bats - Document in
docs/reference/<subcommand>.md(per-command page)
- Update loading logic in
lib/config.sh - Update display in
show_config()inlib/core.sh - Document in
docs/user-guide/configuration.md - Add tests in
tests/unit/config.batsandtests/integration/config.bats
- Modify
lib/api.sh - Test both streaming and batch modes
- Update token estimation if request structure changes
Global execution-mode flags use string values "true"/"false". Always compare with quoted strings:
# Correct
if [[ "$DRY_RUN" == "true" ]]; then
if [[ "$COUNT_TOKENS" != "true" ]]; then
if [[ "$STREAM_MODE" == "true" ]] && [[ "$ENABLE_THINKING" != "true" ]]; then
# Incorrect - avoid unquoted or -eq comparisons
if [[ $DRY_RUN ]]; then # Wrong: tests if variable is set, not value
if [[ "$DRY_RUN" -eq 1 ]]; then # Wrong: these are strings, not integersCommon boolean globals: DRY_RUN, COUNT_TOKENS, STREAM_MODE, ENABLE_THINKING, ENABLE_CITATIONS
When adding options to parse_common_option() in lib/execute.sh:
# Value option (PARSE_CONSUMED=2)
--my-option)
[[ $# -eq 0 ]] && { echo "Error: --my-option requires argument" >&2; return 1; }
MY_OPTION="$1"
CONFIG_SOURCE_MAP[MY_OPTION]="cli"
PARSE_CONSUMED=2
;;
# Flag option (PARSE_CONSUMED=1)
--my-flag)
MY_FLAG="true"
CONFIG_SOURCE_MAP[MY_FLAG]="cli"
PARSE_CONSUMED=1
;;Note: Do NOT add shift inside the case - the function already shifts after storing $1 in opt.
Use collect_all_dependencies() for resolving component dependencies (system prompts or tasks). It uses nameref to avoid subshell variable loss:
local -A TRACKER=()
mapfile -t components < <(collect_all_dependencies \
"TRACKER" \
"WIREFLOW_PROMPT_PREFIX" \
"BUILTIN_WIREFLOW_PROMPT_PREFIX" \
"${ROOT_COMPONENTS[@]}")Use --slurpfile instead of --argjson to avoid "Argument list too long" errors:
# Correct - reads from file
jq -n --slurpfile data "$temp_file" '{ content: $data[0] }'
# Incorrect - passes via command line (subject to ARG_MAX)
jq -n --argjson data "$large_json" '{ content: $data }'Current version: 0.6.0 (pre-release)
Location: WIREFLOW_VERSION in wireflow.sh (line 14)
Version files to update:
wireflow.sh(line 14)README.md(line 5)docs/index.md(line 5)
- Update RELEASE-NOTES.md - Add version section with narrative
- Update CHANGELOG.md - Add version with Added/Changed/Fixed sections and commit refs
- Bump version in wireflow.sh, README.md, docs/index.md
- Commit:
chore: Release version X.Y.Z - Tag:
git tag -a vX.Y.Z -m "Release version X.Y.Z ..." - Verify:
wfw --version,git show vX.Y.Z --no-patch - Push:
git push origin main --tags
- 0.x.x: Pre-release (API may change)
- MAJOR: Breaking CLI/config changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
Commit format:
<type>: <subject>
<body>
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Types: feat, fix, docs, test, refactor, style, chore
Subject: Max 72 chars, imperative mood
This project uses the global ~/.claude/settings.json for all permissions and settings.
The tlmgr command manages all tool repositories from the umbrella ~/tools directory:
tlmgr --json summary # Overall status of all 14 repos
tlmgr --json list # Detailed status with branches
tlmgr changes # Show uncommitted changes
tlmgr unpushed # Show unpushed commitsAlways use tlmgr (not relative paths like ./bin/tools-manager.sh).
Auto-allowed git operations:
- Read: status, diff, log, show, branch, grep, blame
- Write: add, commit, push, pull, checkout, switch, restore, stash
Require confirmation:
- Destructive: merge, rebase, reset, cherry-pick, revert
- Force operations: push --force
- Repository changes: clone, init, submodule
Python: pytest, pip, poetry, uv (install, run, sync)
Node: npm (test, run, install), node
Build: make, bash scripts in ./scripts/
Utilities: find, grep, rg, cat, ls, tree, jq, yq, head, tail, wc
Documents: pandoc, md2docx, mdformat
All permissions are centralized in ~/.claude/settings.json:
- Sandbox is disabled globally
- Full read/write access to ~/tools/** and ~/agents/**
- Standard security protections (no ~/.ssh, .env files, etc.)
- Consistent behavior across all projects
No project-specific .claude/ folders are needed.