diff --git a/.STATUS b/.STATUS index 4683a11db..27222c7a9 100644 --- a/.STATUS +++ b/.STATUS @@ -5,11 +5,33 @@ ## Type: zsh-plugin ## Status: active ## Focus: --help -## Phase: Released (v7.10.2) +## Phase: Released (v7.11.0) ## Priority: 2 ## Progress: 100 -## Current Session (2026-06-19) — flow claude subcommand spec [dev] +## Current Session (2026-06-19) — flow claude check docs + merge cleanup ✅ COMPLETE + +**Session activity:** +- **Merged** PR #473 (`feature/flow-claude` → dev) — `flow claude check` C1–C6 + `--fix` + tutorial 49 + command ref +- **Deleted** `ORCHESTRATE-flow-claude.md` (post-merge cleanup) +- **Updated CHANGELOG.md** — Unreleased section: `flow claude check` (C1–C6), `--fix`, C3 glob fix, docs added +- **Updated CLAUDE.md** — 217 test files, 65/65 suites +- **Updated TESTING.md** — 217 files, 66 suites, 65 passed, 1 skipped (3 locations) +- **Updated MASTER-API-REFERENCE.md** — added claude command helpers section + +## Previous Session (2026-06-19) — v7.11.0 SHIPPED [main + Homebrew] + +**Session activity:** +- **Fixed** teach-config validator to accept `git.draft_branch`/`git.production_branch` as dual schema (was only accepting `branches.*`) +- **Fixed** `flow doctor` atlas calls: `atlas config show` (replaces nonexistent `atlas config get backend`), `command -v atlas-mcp` (replaces nonexistent `atlas mcp status`), liveness check gated on non-empty response +- **Added** `completions/_at` (ZSH completions for `at` dispatcher, auto-discovered via fpath) +- **Added** `_at_help()` entries + `at.1` man page docs for v0.9.3 flags +- **Published** Atlas Contract v1.1.0 (`docs/ATLAS-CONTRACT.md`) +- **Merged** PR #468 (atlas-contract v1.1.0), PR #471 (at completions), PR #472 (Release v7.11.0) +- **Tagged** v7.11.0 → GitHub release → Homebrew auto-updated (7.10.2 → 7.11.0) ✅ +- **CI:** All green (main + homebrew-release + docs) + +## Previous Session (2026-06-19) — flow claude subcommand spec [dev] **Session activity:** - **Brainstormed + grilled** (max depth, interactive) expanding flow-cli to manage Claude Code environment health: settings parity, hook health, memory index drift, CLAUDE.md length rule, shell env parity for Claude Work/Chat @@ -416,6 +438,6 @@ **Last Updated:** 2026-06-18 **Status:** v7.10.2 SHIPPED (main + Homebrew tap) — docs polish + dep maintenance | full suite REQUIRED on main — 64 passed / 0 failed / 1 skipped | 14 dispatchers + at bridge | 216 test files | 12000+ test functions | 0 lint errors | 0 broken links -## wins: Fixed the regression bug (2026-06-19), Fixed the regression bug (2026-06-19), --category fix squashed the bug (2026-06-19), fixed the bug (2026-06-19), Fixed the regression bug (2026-06-17) +## wins: Fixed the regression bug (2026-06-19), --category fix squashed the bug (2026-06-19), fixed the bug (2026-06-19), Fixed the regression bug (2026-06-19), --category fix squashed the bug (2026-06-19) ## streak: 1 -## last_active: 2026-06-19 07:30 +## last_active: 2026-06-19 08:50 diff --git a/CHANGELOG.md b/CHANGELOG.md index 892828f91..d589e8700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [7.12.0] — 2026-06-19 — flow claude check: Claude Code environment health + +### Added + +- **`flow claude check` (C1–C6)** (`commands/claude.zsh`): new environment health command — `flow claude check` (alias: `flow claude doctor`) runs six checks and reports status with exit codes `0`=all pass, `1`=any ERROR, `2`=any WARN. + - **C1 Settings parity** — `settings.json` `.env` keys vs zshrc exports; warns on missing or mismatched values; auto-fixable with `--fix` + - **C2 Hook health** — `post-compact-reinject.sh` exists, is executable, and passes `shellcheck`; ERROR-level + - **C3 Memory index drift** — `.md` file count vs `MEMORY.md` entry count per `~/.claude/projects/*/memory` + - **C4 CLAUDE.md length** — warns when `~/.claude/CLAUDE.md` exceeds 100 lines (mirrors the `claude-md-length` rule) + - **C5 Shell env parity** — reports `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` INFO-level (set or unset) + - **C6 Output token limit** — warns when `CLAUDE_CODE_MAX_OUTPUT_TOKENS` unset or ≤ 8192; reads `settings.json` first (jq), falls back to zshrc grep; auto-fixable with `--fix` +- **`flow claude check --fix`**: repairs C1 (sync zshrc to `settings.json` env block) and C6 (append/update `CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000`) without touching `settings.json` +- **Tutorial 49** (`docs/tutorials/49-flow-claude-check.md`): step-by-step guide with `--fix` workflow and all six checks documented +- **`docs/commands/claude.md`**: command reference with check table, exit codes, `--fix` behavior, and dependency notes +- **`docs/troubleshooting/CLAUDE-CODE-ENVIRONMENT.md`**: diagnostic guide using `flow claude check` to triage Claude Code environment issues + +### Fixed + +- **C3 ZSH glob `no matches found` on empty projects dir** (`commands/claude.zsh`): glob `"$memory_dir"/*/memory` fatal-errored when `projects/` existed but had no subdirs. Fixed with `(/N)` qualifier (directory-only + null-glob). +- **ZSH stdout leak in `flow claude check`** (`commands/claude.zsh`): `local var` inside a loop re-declares an already-local variable each iteration, causing ZSH to print the old value to stdout. Affected C1 (while-loop: `zshrc_val=32000` leaked) and C3 (for-loop: `file_count=N` leaked). Fixed by hoisting all loop-internal `local` declarations above the loop bodies. + ## [7.11.0] — 2026-06-19 — at-dispatcher completions + atlas doctor fixes ### Added diff --git a/CLAUDE.md b/CLAUDE.md index bb3fb5b2f..fa389f7cc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ This file provides guidance to Claude Code when working with code in this reposi **flow-cli** - Pure ZSH plugin for ADHD-optimized workflow management. Zero dependencies. Standalone (works without Oh-My-Zsh or any plugin manager). - **Architecture:** Pure ZSH plugin (no Node.js runtime required) -- **Current Version:** v7.11.0 +- **Current Version:** v7.12.0 - **Install:** Homebrew (recommended), or any plugin manager - **Source:** `source /opt/homebrew/opt/flow-cli/flow.plugin.zsh` (via Homebrew) - **Optional:** Atlas integration for enhanced state management @@ -77,6 +77,7 @@ catch # Quick capture js # Just start (auto-picks project) flow doctor # Health check flow doctor --fix # Interactive install missing tools +flow claude check # Claude Code environment health (settings, hooks, memory, CLAUDE.md) ``` ### Dopamine Features @@ -136,7 +137,7 @@ flow-cli/ ├── docs/ # Documentation (MkDocs) │ └── internal/ # Internal conventions & contributor templates ├── scripts/ # Standalone validators (check-math.zsh) -├── tests/ # 216 test files, 12000+ test functions +├── tests/ # 218 test files, 12000+ test functions │ └── fixtures/demo-course/ # STAT-101 demo course for E2E └── .archive/ # Archived Node.js CLI ``` @@ -181,7 +182,7 @@ flow-cli/ ## Testing -**216 test files, 12000+ test functions.** Run: `./tests/run-all.sh` (64/64 passing, 1 expected interactive/tmux timeout) or individual suites in `tests/`. +**218 test files, 12000+ test functions.** Run: `./tests/run-all.sh` (66/66 passing, 1 expected interactive/tmux timeout) or individual suites in `tests/`. See `docs/guides/TESTING.md` for patterns, mocks, assertions, TDD workflow. @@ -215,8 +216,8 @@ export FLOW_FORCE_DISPATCHER_OBS=1 # Force-keep one dispatcher (FLOW_F ## Current Status -**Version:** v7.11.0 | **Tests:** 12000+ (64/64 suite, 1 interactive timeout) | **Docs:** https://Data-Wise.github.io/flow-cli/ +**Version:** v7.12.0 | **Tests:** 12000+ (66/66 suite, 1 interactive timeout) | **Docs:** https://Data-Wise.github.io/flow-cli/ --- -**Last Updated:** 2026-06-13 (v7.11.0) +**Last Updated:** 2026-06-19 (v7.12.0) diff --git a/commands/claude.zsh b/commands/claude.zsh new file mode 100644 index 000000000..0bbf858e9 --- /dev/null +++ b/commands/claude.zsh @@ -0,0 +1,243 @@ +# commands/claude.zsh — flow claude subcommand +# Claude Code environment health checker + +flow_claude() { + local subcmd="${1:-check}" + shift 2>/dev/null + + case "$subcmd" in + check|doctor) _flow_claude_check "$@" ;; + help|--help|-h) _flow_claude_help ;; + *) + _flow_log_error "Unknown subcommand: $subcmd" + _flow_claude_help + return 1 + ;; + esac +} + +_flow_claude_help() { + local b="${BOLD:-}" r="${RESET:-}" g="${GREEN:-}" y="${YELLOW:-}" c="${CYAN:-}" + print "${b}flow claude${r} — Claude Code environment health checker" + print "" + print "${b}Usage:${r}" + print " flow claude check Run all environment checks" + print " flow claude check --fix Run checks + auto-repair safe mismatches (C1, C6)" + print " flow claude doctor Alias for check" + print "" + print "${b}Checks:${r}" + print " C1 Settings parity settings.json env block vs zshrc exports" + print " C2 Hook health post-compact-reinject.sh exists + executable + shellcheck" + print " C3 Memory index drift .md file count vs MEMORY.md entry count" + print " C4 CLAUDE.md length warns when > 100 lines" + print " C5 Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE exported" + print " C6 Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS > 8192 (auto-fixable with --fix)" + print "" + print "${b}Exit codes:${r} 0=all pass 1=any ERROR 2=any WARN (no ERROR)" +} + +_flow_claude_check() { + local fix=0 + [[ "${1:-}" == "--fix" ]] && fix=1 + + # Injectable paths for testing + local claude_home="${FLOW_CLAUDE_HOME:-$HOME/.claude}" + local zshrc_path="${FLOW_CLAUDE_ZSHRC:-${ZDOTDIR:-$HOME/.config/zsh}/.zshrc}" + + local has_error=0 + local has_warn=0 + + _flow_log_info "Claude Code environment check" + print "" + + # ── C1: Settings parity ────────────────────────────────────────────────── + local settings_json="$claude_home/settings.json" + if [[ ! -f "$settings_json" ]]; then + _flow_log_warning "C1 Settings parity settings.json not found at $settings_json" + has_warn=1 + elif ! command -v jq &>/dev/null; then + _flow_log_warning "C1 Settings parity jq not installed — cannot parse settings.json" + has_warn=1 + else + local mismatches=() + local env_block + env_block=$(jq -r '.env // {} | to_entries[] | "\(.key)=\(.value)"' "$settings_json" 2>/dev/null) + + if [[ -z "$env_block" ]]; then + _flow_log_success "C1 Settings parity no env block in settings.json" + else + local key val zshrc_val + while IFS= read -r pair; do + key="${pair%%=*}" + val="${pair#*=}" + if ! grep -qE "^export ${key}=" "$zshrc_path" 2>/dev/null; then + mismatches+=("$key missing from zshrc") + if (( fix )); then + _flow_claude_fix_c1 "$key" "$val" "$zshrc_path" + fi + else + zshrc_val=$(grep -E "^export ${key}=" "$zshrc_path" 2>/dev/null | tail -1 | sed "s/^export ${key}=//;s/[\"']//g") + if [[ "$zshrc_val" != "$val" ]]; then + mismatches+=("$key: settings.json=$val zshrc=$zshrc_val") + if (( fix )); then + _flow_claude_fix_c1 "$key" "$val" "$zshrc_path" + fi + fi + fi + done <<< "$env_block" + + if (( ${#mismatches[@]} == 0 )); then + _flow_log_success "C1 Settings parity settings.json env matches zshrc" + else + _flow_log_warning "C1 Settings parity ${mismatches[*]}" + has_warn=1 + fi + fi + fi + + # ── C2: Hook health ────────────────────────────────────────────────────── + local hook_file="$claude_home/hooks/post-compact-reinject.sh" + if [[ ! -f "$hook_file" ]]; then + _flow_log_error "C2 Hook health post-compact-reinject.sh not found" + has_error=1 + elif [[ ! -x "$hook_file" ]]; then + _flow_log_error "C2 Hook health post-compact-reinject.sh not executable" + has_error=1 + else + if command -v shellcheck &>/dev/null; then + local sc_out + sc_out=$(shellcheck "$hook_file" 2>&1) + if [[ -n "$sc_out" ]]; then + local first_issue + first_issue=$(print "$sc_out" | head -1) + _flow_log_error "C2 Hook health shellcheck: $first_issue" + has_error=1 + else + _flow_log_success "C2 Hook health hook exists, executable, shellcheck clean" + fi + else + _flow_log_success "C2 Hook health hook exists + executable (shellcheck not installed)" + fi + fi + + # ── C3: Memory index drift ─────────────────────────────────────────────── + local memory_dir="$claude_home/projects" + if [[ ! -d "$memory_dir" ]]; then + _flow_log_warning "C3 Memory index drift $memory_dir not found" + has_warn=1 + else + local drift_found=0 memory_md file_count entry_count proj_name + for proj_memory in "$memory_dir"/*/memory(/N); do + [[ -d "$proj_memory" ]] || continue + memory_md="$proj_memory/MEMORY.md" + # Count .md files excluding MEMORY.md itself + file_count=$(find "$proj_memory" -maxdepth 1 -name "*.md" -not -name "MEMORY.md" 2>/dev/null | wc -l | tr -d ' ') + if [[ -f "$memory_md" ]]; then + entry_count=0 + entry_count=$(grep -c '^- \[' "$memory_md" 2>/dev/null) || true + if [[ "$file_count" != "$entry_count" ]]; then + proj_name="${proj_memory%/memory}" + proj_name="${proj_name##*/}" + _flow_log_warning "C3 Memory index drift $proj_name: $file_count files, $entry_count MEMORY.md entries" + has_warn=1 + drift_found=1 + fi + fi + done + if (( ! drift_found )); then + _flow_log_success "C3 Memory index drift all memory dirs in sync" + fi + fi + + # ── C4: CLAUDE.md length ──────────────────────────────────────────────── + local claude_md="$claude_home/CLAUDE.md" + if [[ ! -f "$claude_md" ]]; then + _flow_log_success "C4 CLAUDE.md length not found (no check)" + else + local line_count + line_count=$(wc -l < "$claude_md" | tr -d ' ') + if (( line_count > 100 )); then + _flow_log_warning "C4 CLAUDE.md length $line_count lines — exceeds 100-line rule (trim before adding)" + has_warn=1 + else + _flow_log_success "C4 CLAUDE.md length $line_count lines — within limit" + fi + fi + + # ── C5: Shell env parity ──────────────────────────────────────────────── + if [[ -n "${CLAUDE_AUTOCOMPACT_PCT_OVERRIDE:-}" ]]; then + _flow_log_info "C5 Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=${CLAUDE_AUTOCOMPACT_PCT_OVERRIDE} exported" + else + _flow_log_info "C5 Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE not set in current session" + fi + + # ── C6: Output token limit ────────────────────────────────────────────── + local token_val="" + # Check settings.json first (canonical) + if [[ -f "$settings_json" ]] && command -v jq &>/dev/null; then + token_val=$(jq -r '.env.CLAUDE_CODE_MAX_OUTPUT_TOKENS // ""' "$settings_json" 2>/dev/null) + fi + # Fall back to zshrc + if [[ -z "$token_val" ]] && [[ -f "$zshrc_path" ]]; then + token_val=$(grep -E "^export CLAUDE_CODE_MAX_OUTPUT_TOKENS=" "$zshrc_path" 2>/dev/null | tail -1 | sed 's/^export CLAUDE_CODE_MAX_OUTPUT_TOKENS=//;s/[\"'\'']//g') + fi + + if [[ -z "$token_val" ]]; then + _flow_log_warning "C6 Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS not set — default 8192 cap may truncate responses" + has_warn=1 + if (( fix )); then + _flow_claude_fix_c6 "$zshrc_path" + fi + elif (( token_val <= 8192 )); then + _flow_log_warning "C6 Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS=${token_val} — still at default cap (set > 8192)" + has_warn=1 + if (( fix )); then + _flow_claude_fix_c6 "$zshrc_path" + fi + else + _flow_log_success "C6 Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS=${token_val}" + fi + + # ── Summary ────────────────────────────────────────────────────────────── + print "" + if (( has_error )); then + _flow_log_error "Result: checks failed (see ERRORs above)" + return 1 + elif (( has_warn )); then + _flow_log_warning "Result: checks passed with warnings" + return 2 + else + _flow_log_success "Result: all checks passed" + return 0 + fi +} + +# Repair C6: set CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 in zshrc +_flow_claude_fix_c6() { + local zshrc="$1" + _flow_claude_fix_c1 "CLAUDE_CODE_MAX_OUTPUT_TOKENS" "32000" "$zshrc" +} + +# Repair C1: update or append an export line in zshrc +_flow_claude_fix_c1() { + local key="$1" + local val="$2" + local zshrc="$3" + + if [[ ! -f "$zshrc" ]]; then + _flow_log_warning " --fix: zshrc not found at $zshrc — skipping $key" + return 1 + fi + + if grep -qE "^export ${key}=" "$zshrc" 2>/dev/null; then + # Replace existing line — portable temp-file approach (BSD sed needs '' suffix, GNU sed doesn't) + local tmp_file + tmp_file=$(mktemp "${zshrc}.XXXXXX") + sed "s|^export ${key}=.*|export ${key}=${val}|" "$zshrc" > "$tmp_file" && mv "$tmp_file" "$zshrc" + _flow_log_success " --fix: updated $key in zshrc" + else + # Append new export line + print "\nexport ${key}=${val}" >> "$zshrc" + _flow_log_success " --fix: added $key to zshrc" + fi +} diff --git a/commands/flow.zsh b/commands/flow.zsh index daa668e0d..415fcdec8 100644 --- a/commands/flow.zsh +++ b/commands/flow.zsh @@ -171,9 +171,12 @@ flow() { mcp) mcp "$@" ;; - cc|claude) + cc) cc "$@" ;; + claude) + flow_claude "$@" + ;; tm|terminal) tm "$@" ;; diff --git a/completions/_flow b/completions/_flow index 043af51ae..10fb6ae4a 100644 --- a/completions/_flow +++ b/completions/_flow @@ -56,6 +56,7 @@ _flow() { # AI-Powered 'ai:Ask AI anything' 'do:Natural language commands' + 'claude:Claude Code environment health checker' # Plugins 'plugin:Manage flow-cli plugins' # Configuration @@ -440,6 +441,19 @@ _flow() { ) _describe -t commands 'config command' config_cmds ;; + claude) + local -a claude_subcmds claude_opts + claude_subcmds=( + 'check:Run all environment checks' + 'doctor:Alias for check' + ) + claude_opts=( + '--fix:Auto-repair safe mismatches (C1 settings parity only)' + '--help:Show help' + ) + _describe -t commands 'claude subcommand' claude_subcmds + _describe -t options 'option' claude_opts + ;; *) _files ;; diff --git a/docs/commands/claude.md b/docs/commands/claude.md new file mode 100644 index 000000000..7a948f4ac --- /dev/null +++ b/docs/commands/claude.md @@ -0,0 +1,147 @@ +# flow claude + +> **Claude Code environment health checks — detect and fix common configuration drift** + +The `flow claude` command inspects the Claude Code environment for settings that silently cause problems: env var mismatches between `settings.json` and your shell, hook failures, memory index drift, and output token limits that truncate long responses mid-run. + +--- + +## Synopsis + +```bash +flow claude check # Run all checks, print report, exit with status +flow claude check --fix # Run checks + auto-repair safe mismatches +flow claude doctor # Alias for check +flow claude doctor --fix # Alias for check --fix +``` + +--- + +## Checks + +| ID | Name | What it checks | Severity | +|----|------|----------------|----------| +| C1 | Settings parity | Each key in `~/.claude/settings.json` `.env` block has a matching `export KEY=VALUE` in zshrc | WARN | +| C2 | Hook health | `~/.claude/hooks/post-compact-reinject.sh` exists, is executable, passes `shellcheck` | ERROR | +| C3 | Memory index drift | `.md` file count in each `memory/` dir vs entry count in its `MEMORY.md` | WARN | +| C4 | CLAUDE.md length | `~/.claude/CLAUDE.md` line count ≤ 100 (project rule: trim before adding) | WARN | +| C5 | Shell env parity | `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` exported in current shell (proxy for env reaching Electron apps) | INFO | +| C6 | Output token limit | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` set in settings.json or zshrc and value > 8192 | WARN | + +--- + +## Output Format + +``` +flow claude check + +✓ Settings parity AUTOCOMPACT=65 matches in settings.json + zshrc +✗ Hook health post-compact-reinject.sh: shellcheck failed (line 12) +⚠ Memory index drift ~/.claude/projects/-Users-dt--config/memory/: 8 files, 6 MEMORY.md entries +⚠ CLAUDE.md length 148 lines — exceeds 100-line rule (trim before adding) +ℹ Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65 exported in current session +⚠ Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS not set — default 8192 cap may truncate responses (run --fix to set 32000) +``` + +--- + +## `--fix` Mode + +`--fix` writes to zshrc only. It never modifies `settings.json` (complex nested JSON — manual edit). + +| Check | What `--fix` does | +|-------|------------------| +| C1 | Updates the `export KEY=VALUE` line in zshrc to match the value in settings.json | +| C6 | Appends `export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000` if missing, or updates the value if ≤ 8192 | +| C2, C3, C4, C5 | Reported only — no auto-repair | + +**Why settings.json is canonical:** It's what Claude Code reads directly. zshrc exports are a belt-and-suspenders fallback for [issue #63186](https://github.com/anthropics/claude-code/issues/63186) where the env block is silently ignored in some launch paths. `--fix` brings zshrc into alignment with settings.json, never the reverse. + +--- + +## Exit Codes + +| Code | Meaning | +|------|---------| +| `0` | All checks pass | +| `1` | One or more ERROR checks failed | +| `2` | One or more WARN checks (no ERRORs) | + +Use the exit code in scripts: + +```bash +flow claude check || echo "Environment issues detected" +``` + +--- + +## Why Each Check Matters + +**C1 — Settings parity:** `settings.json` env block is silently ignored in some Claude Code launch paths. If zshrc exports diverge, the active value is unpredictable depending on how Claude was launched. + +**C2 — Hook health:** `post-compact-reinject.sh` runs after every context compaction and reinjects system context (memory, project state). A broken hook means that context is lost mid-session — silently. + +**C3 — Memory index drift:** `MEMORY.md` is the index Claude reads to decide which memory files to load. If files exist that aren't indexed, they're invisible to Claude. If MEMORY.md lists files that don't exist, the index rots and misleads. + +**C4 — CLAUDE.md length:** The global rule caps `~/.claude/CLAUDE.md` at 100 lines. Beyond that it starts consuming context on every turn. Every line over 100 is paying a per-turn tax. + +**C5 — Shell env parity:** Claude Code Electron apps inherit env from the shell that launched them, not from `settings.json`. This check verifies that `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` is actually in the shell's env — a proxy for "will Electron-launched Claude see my settings?" + +**C6 — Output token limit:** Claude Code defaults to 8192 output tokens per response. Long file writes, comprehensive diffs, or detailed analysis can exceed this, interrupting the session with `API Error: Claude's response exceeded the 8192 output token maximum`. Setting `CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000` raises the ceiling. + +--- + +## Manual Fix for C2 (Hook Health) + +The hook file must exist, be executable, and pass `shellcheck`: + +```bash +# Check it exists and is executable +ls -la ~/.claude/hooks/post-compact-reinject.sh + +# Make executable +chmod +x ~/.claude/hooks/post-compact-reinject.sh + +# Run shellcheck manually +shellcheck ~/.claude/hooks/post-compact-reinject.sh +``` + +--- + +## Manual Fix for C6 (Output Token Limit) + +`--fix` handles zshrc. For settings.json, edit manually: + +```jsonc +// ~/.claude/settings.json +{ + "env": { + "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "32000" + } +} +``` + +Recommended value: `32000`. Maximum for Sonnet 4.6: ~64000. + +--- + +## Dependencies + +| Tool | Required by | Behavior if absent | +|------|-------------|-------------------| +| `jq` | C1, C6 (settings.json parsing) | Skips JSON checks, reports "jq required" | +| `shellcheck` | C2 (hook validation) | Reports existence + executable only, skips lint | + +--- + +## See Also + +- [Troubleshooting: Claude Code Environment](../troubleshooting/CLAUDE-CODE-ENVIRONMENT.md) — Shell snapshot limitations and workarounds +- [doctor](doctor.md) — System-wide health check (tools, dependencies, Atlas) +- [Master Dispatcher Guide](../reference/MASTER-DISPATCHER-GUIDE.md) — All dispatchers + +--- + +**Last Updated:** 2026-06-19 +**Version:** v7.12.0 +**Status:** Implemented in `commands/claude.zsh` diff --git a/docs/guides/TESTING.md b/docs/guides/TESTING.md index 6a2a1c050..0174f6286 100644 --- a/docs/guides/TESTING.md +++ b/docs/guides/TESTING.md @@ -23,8 +23,8 @@ flow-cli uses a **shared test framework** (`tests/test-framework.zsh`) with comp | Metric | Count | |--------|-------| -| Test files | 216 | -| Test suites (run-all.sh) | 65 total — 64 passed, 1 skipped, 0 failed | +| Test files | 218 | +| Test suites (run-all.sh) | 67 total — 66 passed, 1 skipped, 0 failed | | Test functions | 12,000+ | | Expected skips | 1 (`e2e-em-dispatcher` — needs configured IMAP account) | | CI | runs the full suite on every PR (green on the Ubuntu runner) | @@ -264,7 +264,7 @@ zsh tests/test-work.zsh ./tests/run-all.sh ``` -65 suites, ~12000 assertions. Expected: **64 passed, 0 failed, 0 timeout, 1 skipped**. +67 suites, ~12000 assertions. Expected: **66 passed, 0 failed, 0 timeout, 1 skipped**. The 1 skip is `e2e-em-dispatcher` (needs a configured IMAP account; skips cleanly otherwise). `run-all.sh` exits **0** when there are no failures or timeouts. @@ -408,4 +408,4 @@ When adding new functionality: **Established:** v5.0.0 (2026-01-11) **Overhauled:** v7.4.0 (2026-02-16) — shared framework, mock registry, dogfood scanner -**Test Count:** 216 test files, 12000+ assertions, 64/64 suites passing +**Test Count:** 218 test files, 12000+ assertions, 66/66 suites passing diff --git a/docs/help/QUICK-REFERENCE.md b/docs/help/QUICK-REFERENCE.md index 0975a9726..d28bda173 100644 --- a/docs/help/QUICK-REFERENCE.md +++ b/docs/help/QUICK-REFERENCE.md @@ -365,6 +365,26 @@ cc help --- +## flow claude (Claude Code health checker) + +```bash +# Run all environment checks +flow claude check +# Output: ✓ Settings parity settings.json env matches zshrc +# ✗ Hook health post-compact-reinject.sh: not found +# ✓ Memory index drift all memory dirs in sync +# ✓ CLAUDE.md length 98 lines — within limit +# ℹ Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65 exported + +# Run checks and auto-repair settings.json parity mismatches +flow claude check --fix + +# Alias +flow claude doctor +``` + +--- + ## R Dispatcher (r) ```bash @@ -1330,6 +1350,40 @@ at crumb "context note" --- +## Claude Environment (flow claude) + +```bash +# Run all checks +flow claude check + +# Auto-repair safe mismatches (C1: settings parity, C6: token limit) +flow claude check --fix + +# Alias +flow claude doctor +flow claude doctor --fix +``` + +### Checks at a Glance + +| ID | Check | Severity | Auto-fixable | +|----|-------|----------|-------------| +| C1 | settings.json env ↔ zshrc parity | WARN | ✓ | +| C2 | post-compact-reinject.sh hook health | ERROR | ✗ | +| C3 | Memory index drift (files vs MEMORY.md) | WARN | ✗ | +| C4 | CLAUDE.md length ≤ 100 lines | WARN | ✗ | +| C5 | `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` in shell env | INFO | ✗ | +| C6 | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` > 8192 | WARN | ✓ | + +### Exit Codes + +```bash +# 0 = all pass, 1 = any ERROR, 2 = WARN only +flow claude check && echo "clean" +``` + +--- + ## Dopamine Features ### Win Logging (ADHD Motivation) @@ -1408,6 +1462,9 @@ export FLOW_PROJECTS_ROOT="$HOME/projects" # Atlas integration (auto|yes|no) export FLOW_ATLAS_ENABLED="auto" +# Claude Code output token limit (default 8192 causes mid-run truncation) +export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 + # Quiet mode (suppress welcome message) export FLOW_QUIET=1 diff --git a/docs/reference/MASTER-API-REFERENCE.md b/docs/reference/MASTER-API-REFERENCE.md index 498a8a6ba..50d721340 100644 --- a/docs/reference/MASTER-API-REFERENCE.md +++ b/docs/reference/MASTER-API-REFERENCE.md @@ -7549,6 +7549,87 @@ URL line is omitted when `$url` is empty or `"null"`. --- +--- + +### claude Command Helpers + +**File:** `commands/claude.zsh` +**Functions:** 4 (v7.11.0+ — `flow claude check` C1–C6 environment health) + +#### `_flow_claude_check` + +Run all six Claude Code environment health checks (C1–C6). + +**Signature:** + +```zsh +_flow_claude_check [--fix] +``` + +**Parameters:** +- `--fix` (optional) — auto-repair C1 (settings parity) and C6 (output token limit) by writing to zshrc + +**Injectable env vars (for testing):** +- `FLOW_CLAUDE_HOME` — override `~/.claude` path +- `FLOW_CLAUDE_ZSHRC` — override `~/.config/zsh/.zshrc` path + +**Exit codes:** +- `0` — all checks pass +- `1` — any ERROR (C2 hook health) +- `2` — any WARN, no ERRORs + +**Example:** + +```zsh +_flow_claude_check +_flow_claude_check --fix +``` + +--- + +#### `_flow_claude_fix_c1` + +Update or append an export line in zshrc to match a settings.json env value. + +**Signature:** + +```zsh +_flow_claude_fix_c1 +``` + +**Parameters:** +- `$1` — env variable name (e.g. `CLAUDE_CODE_MAX_OUTPUT_TOKENS`) +- `$2` — value to set +- `$3` — absolute path to the zshrc file + +**Behavior:** replaces existing `export KEY=...` line or appends new line. Uses `|` as sed delimiter to handle `/` in values. + +--- + +#### `_flow_claude_fix_c6` + +Set `CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000` in zshrc (delegates to `_flow_claude_fix_c1`). + +**Signature:** + +```zsh +_flow_claude_fix_c6 +``` + +--- + +#### `_flow_claude_help` + +Print help text for `flow claude`. + +**Signature:** + +```zsh +_flow_claude_help +``` + +--- + ## See Also - [MASTER-DISPATCHER-GUIDE.md](MASTER-DISPATCHER-GUIDE.md) - Complete dispatcher reference diff --git a/docs/reference/MASTER-DISPATCHER-GUIDE.md b/docs/reference/MASTER-DISPATCHER-GUIDE.md index a04dc0936..03d2efd84 100644 --- a/docs/reference/MASTER-DISPATCHER-GUIDE.md +++ b/docs/reference/MASTER-DISPATCHER-GUIDE.md @@ -3062,6 +3062,66 @@ v off --- +## claude Command + +> Claude Code environment health diagnostics + +The `flow claude` command inspects the Claude Code environment for configuration drift, hook failures, and settings that silently cause problems (wrong compaction threshold, truncated responses, stale memory index). + +### Synopsis + +```bash +flow claude check # Run all 6 checks, print report +flow claude check --fix # Run checks + auto-repair C1 and C6 +flow claude doctor # Alias for check +``` + +### Checks (C1–C6) + +| ID | Name | Logic | Severity | `--fix` | +|----|------|-------|----------|---------| +| C1 | Settings parity | `settings.json` `.env` keys match zshrc exports | WARN | ✓ | +| C2 | Hook health | `post-compact-reinject.sh` exists, executable, shellcheck | ERROR | ✗ | +| C3 | Memory index drift | `.md` files vs `MEMORY.md` entry count | WARN | ✗ | +| C4 | CLAUDE.md length | `~/.claude/CLAUDE.md` ≤ 100 lines | WARN | ✗ | +| C5 | Shell env parity | `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` exported in shell | INFO | ✗ | +| C6 | Output token limit | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` set and > 8192 | WARN | ✓ | + +### `--fix` Behavior + +`--fix` writes zshrc only — never touches `settings.json`. + +```bash +# C1: aligns zshrc export to match settings.json value +# C6: appends or updates CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 + +flow claude check --fix +# ✓ Settings parity Fixed: AUTOCOMPACT in zshrc now matches settings.json +# ⚠ Hook health post-compact-reinject.sh: shellcheck failed (manual fix needed) +# ✓ Output token limit Set CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 in ~/.config/zsh/.zshrc +``` + +### Exit Codes + +| Code | Meaning | +|------|---------| +| 0 | All checks pass | +| 1 | Any ERROR (C2) | +| 2 | Any WARN, no ERRORs | + +### Source of Truth + +`settings.json` env block is canonical. zshrc export is a fallback for issue #63186 (env block silently ignored in some Claude Code launch paths). `--fix` always brings zshrc into alignment with settings.json, never the reverse. + +### Dependencies + +- `jq` — required for C1/C6 JSON parsing; degrades gracefully if absent +- `shellcheck` — optional for C2; skips lint sub-check if absent + +**Reference:** [commands/claude.md](../commands/claude.md) + +--- + ## Dispatcher Comparison Table | Dispatcher | Complexity | Daily Use | Key Feature | @@ -3405,6 +3465,6 @@ at stats --- -**Version:** v7.10.2 -**Last Updated:** 2026-02-22 -**Total:** 14 dispatchers + at bridge fully documented +**Version:** v7.12.0 +**Last Updated:** 2026-06-19 +**Total:** 14 dispatchers + at bridge + claude command fully documented diff --git a/docs/specs/SPEC-flow-claude-subcommand.md b/docs/specs/SPEC-flow-claude-subcommand.md index f86b12c4b..4ea4cc093 100644 --- a/docs/specs/SPEC-flow-claude-subcommand.md +++ b/docs/specs/SPEC-flow-claude-subcommand.md @@ -43,6 +43,7 @@ flow claude check --fix # run checks + auto-repair safe mismatches | C3 | Memory index drift | Count `.md` files in `~/.claude/projects/*/memory/` directories; compare vs entry count in each `MEMORY.md` index. | WARN | No — report mismatched dirs | | C4 | CLAUDE.md length | `wc -l ~/.claude/CLAUDE.md` — warn if > 100 lines (project rule: trim before adding). | WARN | No — report with rule reminder | | C5 | Shell env parity | Verify `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` is exported in the current shell session (proxy for env reaching Claude Work/Chat Electron apps launched from this shell). | INFO | No — informational only | +| C6 | Output token limit | `CLAUDE_CODE_MAX_OUTPUT_TOKENS` is set in settings.json `.env` block or zshrc and its value exceeds 8192 (the default cap that triggers "response exceeded 8192 output token maximum" errors). Recommended: 32000. | WARN | Yes — `--fix` adds/updates `export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000` in zshrc | --- @@ -52,6 +53,7 @@ Safe writes only. `--fix` never touches `settings.json`. **What it fixes:** - C1: String-replace the `export KEY=VAL` line in zshrc to match the value in settings.json. Uses exact line match, not regex glob. +- C6: If `CLAUDE_CODE_MAX_OUTPUT_TOKENS` is missing from zshrc, append `export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000`. If present but ≤ 8192, update the value to 32000. **What it never touches:** - `~/.claude/settings.json` (complex nested JSON — manual edit) @@ -105,4 +107,5 @@ flow claude check ⚠ Memory index drift ~/.claude/projects/-Users-dt--config/memory/: 8 files, 6 MEMORY.md entries ✓ CLAUDE.md length 148 lines — exceeds 100-line rule (trim before adding) ℹ Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65 exported in current session +⚠ Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS not set — default 8192 cap may truncate responses (run --fix to set 32000) ``` diff --git a/docs/troubleshooting/CLAUDE-CODE-ENVIRONMENT.md b/docs/troubleshooting/CLAUDE-CODE-ENVIRONMENT.md index 3ce07190a..ad7b15923 100644 --- a/docs/troubleshooting/CLAUDE-CODE-ENVIRONMENT.md +++ b/docs/troubleshooting/CLAUDE-CODE-ENVIRONMENT.md @@ -188,6 +188,19 @@ open -a Terminal ## Diagnostic Commands +### flow claude check (Recommended) + +Run the built-in environment health checker — catches the most common Claude Code configuration issues in one command: + +```bash +flow claude check + +# Auto-repair settings parity + output token limit: +flow claude check --fix +``` + +Checks C1–C6: settings.json/zshrc parity, hook health, memory index drift, CLAUDE.md length, shell env, and output token limit. See [commands/claude.md](../commands/claude.md) for full reference. + ### Check flow-cli Status in Current Shell ```bash diff --git a/docs/tutorials/49-flow-claude-check.md b/docs/tutorials/49-flow-claude-check.md new file mode 100644 index 000000000..afb41afaa --- /dev/null +++ b/docs/tutorials/49-flow-claude-check.md @@ -0,0 +1,297 @@ +--- +tags: + - tutorial + - configuration + - claude-code + - diagnostics +--- + +# Tutorial 49: Diagnosing Claude Code Environment with `flow claude check` + +> **What you'll learn:** how to catch the six most common Claude Code configuration +> problems before they silently derail a session — and how to auto-fix the ones +> that are safe to fix. +> +> **Time:** ~10 minutes | **Level:** Beginner | **v7.12.0** + +**Why this matters:** Claude Code configuration lives in two places (`settings.json` +and your shell), and they drift. Hook files break. Memory indexes go stale. The +default 8192-token output cap truncates long responses mid-run. None of these fail +loudly — they just produce confusing behavior. `flow claude check` finds them all in +one pass. + +--- + +## Prerequisites + +- [ ] flow-cli installed (`brew install data-wise/tap/flow-cli`) +- [ ] Claude Code installed (`claude --version`) +- [ ] `jq` installed (`brew install jq`) — used for C1/C6 JSON parsing + +**Verify:** + +```bash +flow claude check --help +# Expected: help text showing check / --fix / exit codes +``` + +--- + +## What You'll Learn + +1. Run `flow claude check` and read the report +2. Understand each of the six checks (C1–C6) +3. Use `--fix` to auto-repair safe mismatches +4. Know which failures require manual intervention + +--- + +## Step 1: Run the Check + +```bash +flow claude check +``` + +You'll see a six-line report. Each line is one check: + +``` +✓ Settings parity AUTOCOMPACT=65 matches in settings.json + zshrc +✗ Hook health post-compact-reinject.sh: shellcheck failed (line 12) +⚠ Memory index drift ~/.claude/projects/-Users-dt--config/memory/: 8 files, 6 MEMORY.md entries +⚠ CLAUDE.md length 148 lines — exceeds 100-line rule (trim before adding) +ℹ Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65 exported in current session +⚠ Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS not set — default 8192 cap may truncate responses (run --fix to set 32000) +``` + +**Reading the symbols:** + +| Symbol | Severity | Meaning | +|--------|----------|---------| +| `✓` | PASS | Check passed | +| `✗` | ERROR | Something is broken — exit code 1 | +| `⚠` | WARN | Potential problem — exit code 2 | +| `ℹ` | INFO | Informational — no effect on exit code | + +--- + +## Step 2: Understand the Six Checks + +### C1 — Settings Parity (WARN) + +`~/.claude/settings.json` has an `"env"` block that's the canonical source for +Claude Code environment variables. But a [known bug](https://github.com/anthropics/claude-code/issues/63186) +causes that block to be silently ignored in some launch paths (Electron apps launched +from a terminal that doesn't inherit the env). + +The fix is to also export the same values in `~/.config/zsh/.zshrc`. C1 checks that +both locations agree. + +**Example failure:** + +``` +⚠ Settings parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: settings.json=65, zshrc=missing +``` + +### C2 — Hook Health (ERROR) + +`~/.claude/hooks/post-compact-reinject.sh` runs after every context compaction. It +reinjects your memory files, project state, and system context back into the +conversation — so Claude doesn't lose track of what it was doing mid-session. + +If this hook is missing, not executable, or has a shell error, compaction silently +drops your context. + +**Example failure:** + +``` +✗ Hook health post-compact-reinject.sh: not executable +``` + +### C3 — Memory Index Drift (WARN) + +Claude reads `MEMORY.md` to decide which memory files to load at the start of a +conversation. If a `.md` file exists in a `memory/` directory but isn't listed in +`MEMORY.md`, Claude never sees it. If `MEMORY.md` lists files that don't exist, the +index rots. + +**Example failure:** + +``` +⚠ Memory index drift memory/: 8 .md files, 6 MEMORY.md entries (2 unindexed) +``` + +### C4 — CLAUDE.md Length (WARN) + +The global rule caps `~/.claude/CLAUDE.md` at 100 lines. Every line loads into +context on every turn — so a bloated CLAUDE.md is a per-turn tax you pay forever. + +**Example failure:** + +``` +⚠ CLAUDE.md length 148 lines — exceeds 100-line rule (trim before adding) +``` + +### C5 — Shell Env Parity (INFO) + +Verifies that `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE` is actually present in the current +shell's environment. This is a proxy check for "will Claude Code launched from this +shell inherit my compaction settings?" — important when using the Electron desktop +app, which inherits env from the terminal that launched it. + +This is INFO-only: it doesn't affect the exit code. + +### C6 — Output Token Limit (WARN) + +Claude Code defaults to 8192 output tokens per response. When a task requires a long +response — writing a large file, generating a comprehensive diff, explaining a complex +system — it hits this cap mid-output and errors with: + +``` +API Error: Claude's response exceeded the 8192 output token maximum. +To configure this behavior, set the CLAUDE_CODE_MAX_OUTPUT_TOKENS environment variable. +``` + +C6 checks that `CLAUDE_CODE_MAX_OUTPUT_TOKENS` is set (in `settings.json` or zshrc) +and that its value is greater than 8192. + +**Example failure:** + +``` +⚠ Output token limit CLAUDE_CODE_MAX_OUTPUT_TOKENS not set — default 8192 cap may truncate responses +``` + +--- + +## Step 3: Auto-Fix What's Safe + +Two checks are auto-fixable: C1 (settings parity) and C6 (token limit). + +```bash +flow claude check --fix +``` + +**What `--fix` does:** + +- **C1:** Updates the `export KEY=VALUE` line in `~/.config/zsh/.zshrc` to match the + value in `settings.json`. Uses exact line replacement — no regex glob. +- **C6:** Appends `export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000` to `~/.config/zsh/.zshrc` + if missing, or updates the value to 32000 if it's present but ≤ 8192. + +**What `--fix` never touches:** `settings.json` (complex nested JSON — manual edit +only), hook scripts, MEMORY.md, or CLAUDE.md. + +**Example output after `--fix`:** + +``` +✓ Settings parity Fixed: CLAUDE_AUTOCOMPACT_PCT_OVERRIDE aligned in zshrc +✗ Hook health post-compact-reinject.sh: not executable (manual fix needed) +⚠ Memory index drift memory/: 8 .md files, 6 MEMORY.md entries (2 unindexed) +⚠ CLAUDE.md length 148 lines — exceeds 100-line rule +ℹ Shell env parity CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65 exported +✓ Output token limit Fixed: CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 added to zshrc +``` + +After `--fix`, reload your shell: `source ~/.config/zsh/.zshrc` + +--- + +## Step 4: Fix What Requires Manual Attention + +### Fix C2 — Hook Not Executable + +```bash +chmod +x ~/.claude/hooks/post-compact-reinject.sh +flow claude check # Should now show ✓ +``` + +### Fix C2 — Hook Fails shellcheck + +```bash +shellcheck ~/.claude/hooks/post-compact-reinject.sh +# Read the output, fix the reported lines, re-run check +``` + +### Fix C3 — Memory Index Drift + +Open `~/.claude/projects//memory/MEMORY.md` and add an entry for each +unindexed `.md` file, following the format: + +```markdown +- [Short title](filename.md) — one-line description of what this memory contains +``` + +### Fix C4 — CLAUDE.md Too Long + +Move verbose sections from `~/.claude/CLAUDE.md` into `~/.claude/rules/` files +(referenced via filename convention, not inline) or `~/.claude/reference/` for +reference material. The rule: trim before adding — every line must earn its place. + +### Fix C6 — Manual settings.json Update + +`--fix` updates zshrc. To also set it in `settings.json` (canonical): + +```jsonc +// ~/.claude/settings.json +{ + "env": { + "CLAUDE_CODE_MAX_OUTPUT_TOKENS": "32000" + } +} +``` + +Recommended value: `32000`. Maximum for Sonnet 4.6: ~64000. + +--- + +## Step 5: Use the Exit Code in Scripts + +`flow claude check` exits with a meaningful status code: + +| Exit | Meaning | +|------|---------| +| `0` | All checks pass | +| `1` | At least one ERROR (C2) | +| `2` | At least one WARN, no ERRORs | + +Use it in your workflow: + +```bash +# Gate a session start on environment health +flow claude check && claude + +# Warn but proceed +flow claude check; if [[ $? -eq 1 ]]; then echo "⚠ Fix hook before proceeding"; fi +``` + +--- + +## Quick Reference + +```bash +# Run all checks +flow claude check + +# Run + auto-repair C1 and C6 +flow claude check --fix + +# Alias +flow claude doctor +flow claude doctor --fix + +# Reload shell after --fix +source ~/.config/zsh/.zshrc +``` + +--- + +## What's Next + +- [flow claude reference](../commands/claude.md) — full check table and option docs +- [Claude Code Environment troubleshooting](../troubleshooting/CLAUDE-CODE-ENVIRONMENT.md) — shell snapshot limitations +- [flow doctor](../commands/doctor.md) — system-wide health check (tools, Atlas, dependencies) +- [Tutorial 23: Token Automation](23-token-automation.md) — managing `CLAUDE_CODE_MAX_OUTPUT_TOKENS` alongside other tokens + +--- + +**Last Updated:** 2026-06-19 +**Version:** v7.12.0 diff --git a/flow.plugin.zsh b/flow.plugin.zsh index edd5a3e50..0b19ff045 100644 --- a/flow.plugin.zsh +++ b/flow.plugin.zsh @@ -186,7 +186,7 @@ _flow_plugin_init # Export loaded marker export FLOW_PLUGIN_LOADED=1 -export FLOW_VERSION="7.11.0" +export FLOW_VERSION="7.12.0" # Register exit hook for plugin cleanup add-zsh-hook zshexit _flow_plugin_cleanup diff --git a/man/man1/agenda.1 b/man/man1/agenda.1 index 58223149e..a20a96cde 100644 --- a/man/man1/agenda.1 +++ b/man/man1/agenda.1 @@ -1,6 +1,6 @@ .\" Man page for the agenda command (forward-looking schedule view) .\" Updated: June 2026 -.TH AGENDA 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH AGENDA 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME agenda \- forward-looking schedule across all projects .SH SYNOPSIS diff --git a/man/man1/at.1 b/man/man1/at.1 index 911d0725a..88a00c6c4 100644 --- a/man/man1/at.1 +++ b/man/man1/at.1 @@ -1,6 +1,6 @@ .\" Man page for at dispatcher (Atlas bridge) .\" Generated: June 2026 -.TH AT 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH AT 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME at \- Atlas project intelligence bridge (optional integration) .SH SYNOPSIS diff --git a/man/man1/cc.1 b/man/man1/cc.1 index ef0add806..a42f74e19 100644 --- a/man/man1/cc.1 +++ b/man/man1/cc.1 @@ -1,6 +1,6 @@ .\" Man page for cc dispatcher (Claude Code launcher) .\" Generated: June 2026 -.TH CC 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH CC 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME cc \- Claude Code launcher and dispatcher .SH SYNOPSIS diff --git a/man/man1/dash.1 b/man/man1/dash.1 index 9481c14a0..72ed269db 100644 --- a/man/man1/dash.1 +++ b/man/man1/dash.1 @@ -1,6 +1,6 @@ .\" Man page for the dash command (project dashboard) .\" Updated: June 2026 -.TH DASH 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH DASH 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME dash \- ADHD-friendly project dashboard .SH SYNOPSIS diff --git a/man/man1/dots.1 b/man/man1/dots.1 index 9de7a5522..ce85dbdfd 100644 --- a/man/man1/dots.1 +++ b/man/man1/dots.1 @@ -1,6 +1,6 @@ .\" Man page for dots dispatcher (Dotfile Management) .\" Generated: June 2026 -.TH DOTS 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH DOTS 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME dots \- Dotfile management dispatcher (chezmoi wrapper) .SH SYNOPSIS diff --git a/man/man1/em.1 b/man/man1/em.1 index a082a261f..12d534501 100644 --- a/man/man1/em.1 +++ b/man/man1/em.1 @@ -1,6 +1,6 @@ .\" Man page for em dispatcher (Email / himalaya) .\" Generated: June 2026 -.TH EM 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH EM 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME em \- Email dispatcher (himalaya wrapper) .SH SYNOPSIS diff --git a/man/man1/flow-claude.1 b/man/man1/flow-claude.1 new file mode 100644 index 000000000..53f3e1a04 --- /dev/null +++ b/man/man1/flow-claude.1 @@ -0,0 +1,120 @@ +.TH FLOW-CLAUDE 1 "June 2026" "flow-cli 7.12.0" "User Commands" +.SH NAME +flow-claude \- Claude Code environment health checker +.SH SYNOPSIS +.B flow claude +[\fIsubcommand\fR] [\fIoptions\fR] +.SH DESCRIPTION +The +.B flow claude +subcommand audits your Claude Code environment for common configuration +drift and health issues. It checks that settings.json env exports are +mirrored in your zshrc, verifies hook scripts are present and valid, +detects memory index drift, enforces CLAUDE.md length limits, and +reports shell environment variable parity. +.SH SUBCOMMANDS +.TP +.B check +Run all environment checks (C1\(enC5) and report results. Exits 0 if all +pass, 1 if any ERROR, 2 if any WARN (and no ERROR). +.TP +.B doctor +Alias for +.BR check . +.SH OPTIONS +.TP +.B \-\-fix +Auto-repair safe mismatches. Currently only repairs C1 (settings parity): +adds or updates +.B export KEY=VALUE +lines in your zshrc to match +.IR ~/.claude/settings.json . +.TP +.B \-\-help, \-h +Show help and exit. +.SH CHECKS +.TP +.B C1 \(em Settings parity +Parses +.I ~/.claude/settings.json +.B .env +block via +.BR jq (1) +and compares each key against exports in your zshrc. Reports WARN on +mismatch. Repairable with +.BR \-\-fix . +.TP +.B C2 \(em Hook health +Checks that +.I ~/.claude/hooks/post-compact-reinject.sh +exists, is executable, and passes +.BR shellcheck (1) +(if installed). Reports ERROR on failure. +.TP +.B C3 \(em Memory index drift +For each +.I ~/.claude/projects/*/memory/ +directory, compares the number of +.B .md +files (excluding MEMORY.md) against the count of +.B - [ +index entries in MEMORY.md. Reports WARN on mismatch. +.TP +.B C4 \(em CLAUDE.md length +Reports WARN when +.I ~/.claude/CLAUDE.md +exceeds 100 lines (the configured limit from +.IR ~/.claude/rules/claude-md-length.md ). +.TP +.B C5 \(em Shell env parity +Reports INFO on whether +.B CLAUDE_AUTOCOMPACT_PCT_OVERRIDE +is exported in the current shell session. Informational only. +.SH EXIT STATUS +.TP +.B 0 +All checks passed. +.TP +.B 1 +One or more ERROR checks failed. +.TP +.B 2 +One or more WARN checks triggered, no ERRORs. +.SH EXAMPLES +Run all checks: +.PP +.RS +.B flow claude check +.RE +.PP +Run checks and auto-repair settings parity: +.PP +.RS +.B flow claude check --fix +.RE +.PP +Run via the doctor alias: +.PP +.RS +.B flow claude doctor +.RE +.SH ENVIRONMENT +.TP +.B FLOW_CLAUDE_HOME +Override the default +.I ~/.claude +directory (used for testing). +.TP +.B FLOW_CLAUDE_ZSHRC +Override the default zshrc path (used for testing). +.TP +.B ZDOTDIR +If set, zshrc defaults to +.IR $ZDOTDIR/.zshrc . +.SH SEE ALSO +.BR flow (1), +.BR flow-doctor (1), +.BR jq (1), +.BR shellcheck (1) +.SH AUTHOR +flow-cli contributors. See https://github.com/Data-Wise/flow-cli diff --git a/man/man1/flow.1 b/man/man1/flow.1 index 0c68dee7e..339f13b1e 100644 --- a/man/man1/flow.1 +++ b/man/man1/flow.1 @@ -1,6 +1,6 @@ .\" Man page for flow command .\" Updated: June 2026 -.TH FLOW 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH FLOW 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME flow \- ADHD-friendly workflow CLI for developers .SH SYNOPSIS diff --git a/man/man1/g.1 b/man/man1/g.1 index 0c8a9c0dc..7031e7613 100644 --- a/man/man1/g.1 +++ b/man/man1/g.1 @@ -1,6 +1,6 @@ .\" Man page for g dispatcher (Git workflows) .\" Updated: June 2026 -.TH G 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH G 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME g \- Git commands dispatcher .SH SYNOPSIS diff --git a/man/man1/mcp.1 b/man/man1/mcp.1 index df9cca9a7..51f0f4cc2 100644 --- a/man/man1/mcp.1 +++ b/man/man1/mcp.1 @@ -1,6 +1,6 @@ .\" Man page for mcp dispatcher (MCP server management) .\" Updated: June 2026 -.TH MCP 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH MCP 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME mcp \- MCP server management dispatcher .SH SYNOPSIS diff --git a/man/man1/morning.1 b/man/man1/morning.1 index 60456230c..5d31f2f10 100644 --- a/man/man1/morning.1 +++ b/man/man1/morning.1 @@ -1,6 +1,6 @@ .\" Man page for the morning command (daily startup routine) .\" Updated: June 2026 -.TH MORNING 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH MORNING 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME morning \- ADHD-friendly daily startup routine .SH SYNOPSIS diff --git a/man/man1/prompt.1 b/man/man1/prompt.1 index 0fab300fe..85bb2a4e9 100644 --- a/man/man1/prompt.1 +++ b/man/man1/prompt.1 @@ -1,6 +1,6 @@ .\" Man page for prompt dispatcher (Prompt Engine Switcher) .\" Generated: June 2026 -.TH PROMPT 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH PROMPT 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME prompt \- Prompt engine switcher .SH SYNOPSIS diff --git a/man/man1/qu.1 b/man/man1/qu.1 index 905aec224..127be15bd 100644 --- a/man/man1/qu.1 +++ b/man/man1/qu.1 @@ -1,6 +1,6 @@ .\" Man page for qu dispatcher (Quarto publishing) .\" Updated: June 2026 -.TH QU 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH QU 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME qu \- Quarto publishing dispatcher .SH SYNOPSIS diff --git a/man/man1/r.1 b/man/man1/r.1 index 00b51585e..8a6f98617 100644 --- a/man/man1/r.1 +++ b/man/man1/r.1 @@ -1,6 +1,6 @@ .\" Man page for r dispatcher (R package development) .\" Updated: June 2026 -.TH R 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH R 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME r \- R package development dispatcher .SH SYNOPSIS diff --git a/man/man1/sec.1 b/man/man1/sec.1 index f5f0e6f40..cbb46f097 100644 --- a/man/man1/sec.1 +++ b/man/man1/sec.1 @@ -1,6 +1,6 @@ .\" Man page for sec dispatcher (Secret Management) .\" Generated: June 2026 -.TH SEC 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH SEC 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME sec \- Secret management dispatcher (Keychain and Bitwarden) .SH SYNOPSIS diff --git a/man/man1/teach.1 b/man/man1/teach.1 index 0da2a7225..f8a40e978 100644 --- a/man/man1/teach.1 +++ b/man/man1/teach.1 @@ -1,6 +1,6 @@ .\" Man page for teach dispatcher (Teaching Workflow) .\" Generated: June 2026 -.TH TEACH 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH TEACH 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME teach \- Teaching workflow dispatcher (Scholar integration) .SH SYNOPSIS diff --git a/man/man1/tm.1 b/man/man1/tm.1 index d2701c0d1..8b01eaf5f 100644 --- a/man/man1/tm.1 +++ b/man/man1/tm.1 @@ -1,6 +1,6 @@ .\" Man page for tm dispatcher (Terminal Manager) .\" Generated: June 2026 -.TH TM 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH TM 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME tm \- Terminal manager dispatcher .SH SYNOPSIS diff --git a/man/man1/today.1 b/man/man1/today.1 index 51571a25c..65e283340 100644 --- a/man/man1/today.1 +++ b/man/man1/today.1 @@ -1,6 +1,6 @@ .\" Man page for the today command (quick daily status) .\" Updated: June 2026 -.TH TODAY 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH TODAY 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME today \- quick daily status .SH SYNOPSIS diff --git a/man/man1/tok.1 b/man/man1/tok.1 index f40ea76c6..c49ef6d94 100644 --- a/man/man1/tok.1 +++ b/man/man1/tok.1 @@ -1,6 +1,6 @@ .\" Man page for tok dispatcher (Token Management) .\" Generated: June 2026 -.TH TOK 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH TOK 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME tok \- Token lifecycle management dispatcher .SH SYNOPSIS diff --git a/man/man1/v.1 b/man/man1/v.1 index c0cce032e..5e8bfd133 100644 --- a/man/man1/v.1 +++ b/man/man1/v.1 @@ -1,6 +1,6 @@ .\" Man page for v dispatcher (Vibe / Workflow Automation) .\" Generated: June 2026 -.TH V 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH V 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME v \- Workflow automation dispatcher (vibe coding mode) .SH SYNOPSIS diff --git a/man/man1/week.1 b/man/man1/week.1 index 93b44f7d2..a4dbe85ab 100644 --- a/man/man1/week.1 +++ b/man/man1/week.1 @@ -1,6 +1,6 @@ .\" Man page for the week command (weekly review helper) .\" Updated: June 2026 -.TH WEEK 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH WEEK 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME week \- weekly review helper .SH SYNOPSIS diff --git a/man/man1/wt.1 b/man/man1/wt.1 index 29d117b6d..b1c241da6 100644 --- a/man/man1/wt.1 +++ b/man/man1/wt.1 @@ -1,6 +1,6 @@ .\" Man page for wt dispatcher (Git Worktree Management) .\" Generated: June 2026 -.TH WT 1 "June 2026" "flow-cli 7.11.0" "User Commands" +.TH WT 1 "June 2026" "flow-cli 7.12.0" "User Commands" .SH NAME wt \- Git worktree management dispatcher .SH SYNOPSIS diff --git a/mkdocs.yml b/mkdocs.yml index 0ce0ab98f..abda3a9ea 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -144,6 +144,7 @@ nav: - 39. Quarto Publishing: tutorials/39-qu-dispatcher.md - 40. Workflow (v/vibe): tutorials/40-v-dispatcher.md - 42. Git Shortcuts (g): tutorials/42-g-dispatcher.md + - 49. Claude Check: tutorials/49-flow-claude-check.md - Editor (Neovim): - 15. Nvim Quick Start: tutorials/15-nvim-quick-start.md - 16. Vim Motions: tutorials/16-vim-motions.md @@ -302,6 +303,7 @@ nav: - install: commands/install.md - upgrade: commands/upgrade.md - at (Atlas bridge): commands/at.md + - claude (env check): commands/claude.md - dashboard: commands/dashboard.md - Quick Reference Cards: - 🌳 Worktree (wt): reference/REFCARD-WORKTREE-DISPATCHER.md diff --git a/package.json b/package.json index f9d7ecb40..cd8835e30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flow-cli", - "version": "7.11.0", + "version": "7.12.0", "description": "ADHD-optimized ZSH workflow plugin", "private": true, "scripts": { diff --git a/tests/run-all.sh b/tests/run-all.sh index c7a3d7d50..eb7829e0b 100755 --- a/tests/run-all.sh +++ b/tests/run-all.sh @@ -89,6 +89,7 @@ echo "" echo "Core command tests:" # These tests source flow.plugin.zsh in non-interactive mode # (FLOW_PLUGIN_DIR, FLOW_QUIET, FLOW_ATLAS_ENABLED=no, exec < /dev/null) +run_test ./tests/test-flow-claude.zsh run_test ./tests/test-dash.zsh run_test ./tests/test-schedule.zsh run_test ./tests/test-agenda.zsh diff --git a/tests/test-flow-claude.zsh b/tests/test-flow-claude.zsh new file mode 100755 index 000000000..a2d2e8e99 --- /dev/null +++ b/tests/test-flow-claude.zsh @@ -0,0 +1,316 @@ +#!/usr/bin/env zsh +# tests/test-flow-claude.zsh — Tests for flow claude check (C1-C6) + +SCRIPT_DIR="${0:A:h}" +PROJECT_ROOT="${SCRIPT_DIR:h}" +source "$SCRIPT_DIR/test-framework.zsh" + +# Source plugin once — injectable paths are per-test env vars, not per-source +export FLOW_QUIET=1 +source "$PROJECT_ROOT/flow.plugin.zsh" 2>/dev/null + +test_suite_start "flow claude check" + +# ── Helpers ─────────────────────────────────────────────────────────────── + +typeset -g _TC_TMP + +_tc_setup() { + _TC_TMP=$(mktemp -d) + export FLOW_CLAUDE_HOME="$_TC_TMP/claude" + export FLOW_CLAUDE_ZSHRC="$_TC_TMP/zshrc" + mkdir -p "$FLOW_CLAUDE_HOME/hooks" "$FLOW_CLAUDE_HOME/projects" + print 'export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000' > "$FLOW_CLAUDE_ZSHRC" +} + +_tc_teardown() { + [[ -n "${_TC_TMP:-}" ]] && rm -rf "$_TC_TMP" + unset FLOW_CLAUDE_HOME FLOW_CLAUDE_ZSHRC _TC_TMP +} + +# ── C1: Settings parity ─────────────────────────────────────────────────── + +test_case "C1: passes when settings.json has no env block" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +if command -v jq &>/dev/null; then + assert_contains "$out" "Settings parity" "C1 check runs" + [[ $rc -eq 0 ]] && test_pass "exit 0 on clean state" || test_fail "exit 0 on clean state" "rc=$rc out=$out" +else + test_skip "jq not installed" +fi +_tc_teardown + +test_case "C1: warns when key missing from zshrc" +_tc_setup +print '{"env":{"MY_TEST_VAR":"99"}}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash\necho ok' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +if command -v jq &>/dev/null; then + assert_contains "$out" "MY_TEST_VAR" "C1 names missing key" + [[ $rc -ge 1 ]] && test_pass "non-zero exit on mismatch" || test_fail "non-zero exit on mismatch" "rc=$rc" +else + test_skip "jq not installed" +fi +_tc_teardown + +test_case "C1: passes when key matches zshrc" +_tc_setup +print '{"env":{"MY_TEST_VAR":"99"}}' > "$FLOW_CLAUDE_HOME/settings.json" +print 'export MY_TEST_VAR=99' >> "$FLOW_CLAUDE_ZSHRC" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash\necho ok' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +if command -v jq &>/dev/null; then + assert_contains "$out" "Settings parity" "C1 check runs" + [[ $rc -eq 0 ]] && test_pass "exit 0 on match" || test_fail "exit 0 on match" "rc=$rc" +else + test_skip "jq not installed" +fi +_tc_teardown + +test_case "C1: --fix appends missing key to zshrc" +_tc_setup +print '{"env":{"FIX_VAR":"42"}}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash\necho ok' > "$hook" && chmod +x "$hook" +_flow_claude_check --fix 2>&1 +if command -v jq &>/dev/null; then + assert_contains "$(cat "$FLOW_CLAUDE_ZSHRC")" "FIX_VAR=42" "--fix appended key" +else + test_skip "jq not installed" +fi +_tc_teardown + +test_case "C1: --fix updates existing mismatched value" +_tc_setup +print '{"env":{"FIX_VAR":"new"}}' > "$FLOW_CLAUDE_HOME/settings.json" +print 'export FIX_VAR=old' > "$FLOW_CLAUDE_ZSHRC" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash\necho ok' > "$hook" && chmod +x "$hook" +_flow_claude_check --fix 2>&1 +if command -v jq &>/dev/null; then + assert_contains "$(cat "$FLOW_CLAUDE_ZSHRC")" "FIX_VAR=new" "--fix updated value" +else + test_skip "jq not installed" +fi +_tc_teardown + +# ── C2: Hook health ──────────────────────────────────────────────────────── + +test_case "C2: errors when hook file missing" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "not found" "C2 reports missing hook" +[[ $rc -eq 1 ]] && test_pass "exit 1 on ERROR" || test_fail "exit 1 on ERROR" "rc=$rc" +_tc_teardown + +test_case "C2: errors when hook not executable" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash\necho ok' > "$hook" +chmod 0644 "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "not executable" "C2 reports not-executable" +[[ $rc -eq 1 ]] && test_pass "exit 1 on not-executable" || test_fail "exit 1" "rc=$rc" +_tc_teardown + +test_case "C2: passes when hook exists and is executable" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash\necho ok' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "Hook health" "C2 output present" +[[ $rc -eq 0 ]] && test_pass "exit 0 on valid hook" || test_fail "exit 0" "rc=$rc" +_tc_teardown + +# ── C3: Memory index drift ───────────────────────────────────────────────── + +test_case "C3: passes when file count matches MEMORY.md entries" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +local mem="$FLOW_CLAUDE_HOME/projects/testproj/memory" +mkdir -p "$mem" +print -- '- [a](a.md) — x\n- [b](b.md) — y' > "$mem/MEMORY.md" +print 'a' > "$mem/a.md" +print 'b' > "$mem/b.md" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "Memory index" "C3 runs" +[[ $rc -eq 0 ]] && test_pass "exit 0 on sync" || test_fail "exit 0 on sync" "rc=$rc" +_tc_teardown + +test_case "C3: warns when file count exceeds MEMORY.md entries" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +local mem="$FLOW_CLAUDE_HOME/projects/testproj/memory" +mkdir -p "$mem" +print -- '- [a](a.md) — x' > "$mem/MEMORY.md" +print 'a' > "$mem/a.md" +print 'b' > "$mem/b.md" # extra file not in index +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "drift" "C3 warns on drift" +[[ $rc -ge 1 ]] && test_pass "non-zero on drift" || test_fail "non-zero on drift" "rc=$rc" +_tc_teardown + +# ── C4: CLAUDE.md length ────────────────────────────────────────────────── + +test_case "C4: passes when CLAUDE.md <= 100 lines" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +printf '%s\n' {1..50} > "$FLOW_CLAUDE_HOME/CLAUDE.md" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "CLAUDE.md length" "C4 runs" +[[ $rc -eq 0 ]] && test_pass "exit 0 under limit" || test_fail "exit 0 under limit" "rc=$rc" +_tc_teardown + +test_case "C4: warns when CLAUDE.md > 100 lines" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +printf '%s\n' {1..101} > "$FLOW_CLAUDE_HOME/CLAUDE.md" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "exceeds 100-line rule" "C4 warns on overflow" +[[ $rc -ge 1 ]] && test_pass "non-zero on overflow" || test_fail "non-zero on overflow" "rc=$rc" +_tc_teardown + +# ── C5: Shell env parity ────────────────────────────────────────────────── + +test_case "C5: reports when CLAUDE_AUTOCOMPACT_PCT_OVERRIDE is set" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65 +local out +out=$(_flow_claude_check 2>&1) +assert_contains "$out" "CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=65" "C5 shows value when set" +unset CLAUDE_AUTOCOMPACT_PCT_OVERRIDE +_tc_teardown + +test_case "C5: reports when CLAUDE_AUTOCOMPACT_PCT_OVERRIDE is unset" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +unset CLAUDE_AUTOCOMPACT_PCT_OVERRIDE +local out +out=$(_flow_claude_check 2>&1) +assert_contains "$out" "not set" "C5 shows not-set" +_tc_teardown + +# ── C6: Output token limit ──────────────────────────────────────────────── + +test_case "C6: warns when CLAUDE_CODE_MAX_OUTPUT_TOKENS not set" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +> "$FLOW_CLAUDE_ZSHRC" # clear default token so C6 warns +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "Output token limit" "C6 output present" +assert_contains "$out" "not set" "C6 reports missing" +[[ $rc -ge 2 ]] && test_pass "non-zero exit when C6 missing" || test_fail "non-zero exit when C6 missing" "rc=$rc" +_tc_teardown + +test_case "C6: passes when CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 in zshrc" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +print 'export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000' > "$FLOW_CLAUDE_ZSHRC" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "Output token limit" "C6 output present" +assert_contains "$out" "32000" "C6 shows value" +[[ $rc -eq 0 ]] && test_pass "exit 0 when C6 passes" || test_fail "exit 0 when C6 passes" "rc=$rc out=$out" +_tc_teardown + +test_case "C6: warns when value is at default 8192" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +print 'export CLAUDE_CODE_MAX_OUTPUT_TOKENS=8192' > "$FLOW_CLAUDE_ZSHRC" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +assert_contains "$out" "8192" "C6 shows value" +assert_contains "$out" "still at default" "C6 flags default cap" +[[ $rc -ge 2 ]] && test_pass "non-zero exit at default cap" || test_fail "non-zero exit at default cap" "rc=$rc" +_tc_teardown + +test_case "C6: --fix appends CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000 to zshrc" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +_flow_claude_check --fix 2>&1 +local zshrc_content +zshrc_content=$(cat "$FLOW_CLAUDE_ZSHRC") +assert_contains "$zshrc_content" "CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000" "C6 --fix writes to zshrc" +_tc_teardown + +# ── Exit codes ───────────────────────────────────────────────────────────── + +test_case "exit 1: ERROR wins over WARN" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +# No hook → ERROR; long CLAUDE.md → WARN +printf '%s\n' {1..150} > "$FLOW_CLAUDE_HOME/CLAUDE.md" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +[[ $rc -eq 1 ]] && test_pass "exit 1 when ERROR present" || test_fail "exit 1 when ERROR present" "rc=$rc" +_tc_teardown + +test_case "exit 2: WARN only" +_tc_setup +print '{}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +printf '%s\n' {1..150} > "$FLOW_CLAUDE_HOME/CLAUDE.md" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +[[ $rc -eq 2 ]] && test_pass "exit 2 on WARN only" || test_fail "exit 2 on WARN only" "rc=$rc" +_tc_teardown + +# ── Graceful degradation ─────────────────────────────────────────────────── + +test_case "C1: degrades gracefully when jq not installed" +_tc_setup +print '{"env":{"FOO":"bar"}}' > "$FLOW_CLAUDE_HOME/settings.json" +local hook="$FLOW_CLAUDE_HOME/hooks/post-compact-reinject.sh" +print '#!/bin/bash' > "$hook" && chmod +x "$hook" +# Hide jq via PATH override in subshell +local orig_path="$PATH" +export PATH="/usr/bin:/bin" +local out rc +out=$(_flow_claude_check 2>&1); rc=$? +export PATH="$orig_path" +assert_contains "$out" "Settings parity" "C1 output present without jq" +_tc_teardown + +test_suite_end diff --git a/tests/test-manpage-version-sync.zsh b/tests/test-manpage-version-sync.zsh index b752e23fc..905df1a68 100644 --- a/tests/test-manpage-version-sync.zsh +++ b/tests/test-manpage-version-sync.zsh @@ -223,7 +223,7 @@ run_check "no orphan flow-cli dispatcher page (page without a dispatcher)" ' [[ "$base" == "flow" ]] && continue # flow is the index, not a dispatcher # Top-level commands (not dispatchers) that ship their own page: case "$base" in - agenda|dash|morning|today|week) continue ;; + agenda|dash|morning|today|week|flow-claude) continue ;; esac print -r -- "$cmds" | grep -qx "$base" || orphan+="$base " done diff --git a/zsh/.zshrc b/zsh/.zshrc index 9f7e46dde..9f97da8fb 100644 --- a/zsh/.zshrc +++ b/zsh/.zshrc @@ -1156,3 +1156,5 @@ brew() { return $rc } export HOMEBREW_NO_ENV_HINTS=1 + +export CLAUDE_CODE_MAX_OUTPUT_TOKENS=32000