Skip to content

Commit 0bf9229

Browse files
Merge branch 'main' into cf-1087-field-injection-class-filter
2 parents 3b03249 + c455256 commit 0bf9229

477 files changed

Lines changed: 119222 additions & 25540 deletions

File tree

Some content is hidden

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

.claude/hooks/bash-guard.sh

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
# PreToolUse hook: Block Bash calls that should use dedicated tools.
3+
# Exit 0 = allow, Exit 2 = block (message on stderr).
4+
5+
INPUT=$(cat 2>/dev/null || true)
6+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || true)
7+
8+
[ -z "$COMMAND" ] && exit 0
9+
10+
# Strip leading env vars (FOO=bar cmd ...) and whitespace to get the actual command
11+
STRIPPED=$(echo "$COMMAND" | sed 's/^[[:space:]]*\([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]*[[:space:]]*\)*//')
12+
FIRST_CMD=$(echo "$STRIPPED" | awk '{print $1}')
13+
14+
case "$FIRST_CMD" in
15+
grep|egrep|fgrep|rg)
16+
echo "BLOCKED: Use the Grep tool instead of \`$FIRST_CMD\`. It provides better output and permissions handling." >&2
17+
exit 2
18+
;;
19+
find)
20+
echo "BLOCKED: Use the Glob tool instead of \`find\`. Glob is faster and returns results sorted by modification time." >&2
21+
exit 2
22+
;;
23+
cat|head|tail)
24+
echo "BLOCKED: Use the Read tool instead of \`$FIRST_CMD\`. Read provides line numbers and supports images/PDFs." >&2
25+
exit 2
26+
;;
27+
awk)
28+
echo "BLOCKED: Use the Grep tool or Read tool instead of \`awk\`." >&2
29+
exit 2
30+
;;
31+
sed)
32+
if echo "$COMMAND" | grep -qE '(^|[[:space:]])sed[[:space:]]+-i'; then
33+
echo "BLOCKED: Use the Edit tool instead of \`sed -i\`. Edit tracks changes properly." >&2
34+
exit 2
35+
fi
36+
;;
37+
esac
38+
39+
# echo with file redirection (echo "..." > file)
40+
if echo "$STRIPPED" | grep -qE '^echo\b.*[[:space:]]>'; then
41+
echo "BLOCKED: Use the Write tool instead of \`echo >\`. Write provides proper file creation." >&2
42+
exit 2
43+
fi
44+
45+
exit 0

.claude/hooks/post-compact.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
# PreCompact hook: Inject state preservation guidance before context compaction.
3+
4+
cd "$CLAUDE_PROJECT_DIR" 2>/dev/null || exit 0
5+
6+
STATE=""
7+
8+
BRANCH=$(git branch --show-current 2>/dev/null)
9+
[ -n "$BRANCH" ] && STATE="${STATE}Branch: ${BRANCH}\n"
10+
11+
DIRTY=$(git status --porcelain 2>/dev/null)
12+
if [ -n "$DIRTY" ]; then
13+
COUNT=$(echo "$DIRTY" | wc -l | tr -d ' ')
14+
STATE="${STATE}Uncommitted files (${COUNT}):\n${DIRTY}\n"
15+
fi
16+
17+
UPSTREAM=$(git rev-parse --abbrev-ref '@{upstream}' 2>/dev/null)
18+
if [ -n "$UPSTREAM" ]; then
19+
AHEAD=$(git rev-list --count "${UPSTREAM}..HEAD" 2>/dev/null)
20+
[ "$AHEAD" -gt 0 ] 2>/dev/null && STATE="${STATE}Unpushed commits: ${AHEAD}\n"
21+
fi
22+
23+
RECENT=$(git log --oneline -5 2>/dev/null)
24+
[ -n "$RECENT" ] && STATE="${STATE}Recent commits:\n${RECENT}\n"
25+
26+
LATEST_HANDOFF=$(ls -t "$CLAUDE_PROJECT_DIR/.claude/handoffs/"*.md 2>/dev/null | head -1)
27+
if [ -n "$LATEST_HANDOFF" ] && [ -f "$LATEST_HANDOFF" ]; then
28+
HANDOFF_CONTENT=$(head -40 "$LATEST_HANDOFF" 2>/dev/null)
29+
[ -n "$HANDOFF_CONTENT" ] && STATE="${STATE}\nHandoff context:\n${HANDOFF_CONTENT}\n"
30+
fi
31+
32+
STATE="${STATE}\nProject conventions to preserve:\n"
33+
STATE="${STATE}- Python 3.9+, uv for all tooling, ruff + mypy via prek\n"
34+
STATE="${STATE}- Verification: uv run prek (single command for lint/format/types)\n"
35+
STATE="${STATE}- Pre-push: uv run prek run --from-ref origin/<base>\n"
36+
STATE="${STATE}- Conventional commits: fix:, feat:, refactor:, test:, chore:\n"
37+
STATE="${STATE}- Result type: Success(value) / Failure(error), check with is_successful()\n"
38+
STATE="${STATE}- Language singleton: set_current_language() / current_language()\n"
39+
STATE="${STATE}- libcst for code transforms, ast for read-only analysis\n"
40+
41+
[ -z "$STATE" ] && exit 0
42+
43+
EXPANDED=$(printf '%b' "$STATE")
44+
jq -n --arg msg "PRESERVE the following session state through compaction:
45+
$EXPANDED" '{"systemMessage": $msg}'
46+
47+
exit 0

.claude/hooks/post-edit-lint.sh

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#!/usr/bin/env bash
2-
# Everyone is on macOS so this should be fine, we don't account for Windows
32
set -euo pipefail
43

54
input=$(cat)
@@ -10,6 +9,5 @@ if [[ -z "$file_path" || ! -f "$file_path" ]]; then
109
fi
1110

1211
if [[ "$file_path" == *.py ]]; then
13-
# First run auto-fixes formatting; second run catches real lint errors
1412
uv run prek --files "$file_path" 2>/dev/null || uv run prek --files "$file_path"
1513
fi

.claude/hooks/require-read.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env bash
2+
# PreToolUse hook: Block Write/Edit on existing files that haven't been Read first.
3+
# Exit 0 = allow, Exit 2 = block (message on stderr).
4+
5+
INPUT=$(cat 2>/dev/null || true)
6+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)
7+
8+
[ -z "$FILE_PATH" ] && exit 0
9+
10+
# New files don't need prior reads
11+
[ ! -f "$FILE_PATH" ] && exit 0
12+
13+
TRACKER="$CLAUDE_PROJECT_DIR/.claude/.read-tracker"
14+
15+
if [ ! -f "$TRACKER" ]; then
16+
echo "BLOCKED: Read \`$(basename "$FILE_PATH")\` first before modifying it." >&2
17+
exit 2
18+
fi
19+
20+
if grep -qxF "$FILE_PATH" "$TRACKER"; then
21+
exit 0
22+
fi
23+
24+
echo "BLOCKED: Read \`$(basename "$FILE_PATH")\` first before modifying it." >&2
25+
exit 2

.claude/hooks/status-line.sh

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env bash
2+
# Status line: derive context from git state.
3+
4+
input=$(cat)
5+
project_dir=$(echo "$input" | jq -r '.workspace.project_dir')
6+
7+
user=$(whoami)
8+
branch=$(git -C "$project_dir" branch --show-current 2>/dev/null)
9+
10+
changed=$(git -C "$project_dir" diff --name-only HEAD 2>/dev/null)
11+
[ -z "$changed" ] && changed=$(git -C "$project_dir" diff --name-only 2>/dev/null)
12+
[ -z "$changed" ] && changed=$(git -C "$project_dir" diff --name-only --cached 2>/dev/null)
13+
14+
if [ -n "$changed" ]; then
15+
area=$(echo "$changed" | sed 's|/.*||' | sort | uniq -c | sort -rn | head -1 | awk '{print $2}')
16+
else
17+
area=""
18+
fi
19+
20+
context=""
21+
case "$area" in
22+
codeflash)
23+
subsystem=$(echo "$changed" | grep '^codeflash/' | sed 's|^codeflash/||; s|/.*||' | sort | uniq -c | sort -rn | head -1 | awk '{print $2}')
24+
[ -n "$subsystem" ] && context="editing $subsystem" ;;
25+
tests)
26+
target=$(echo "$changed" | grep '^tests/' | sed 's|^tests/||; s|/.*||' | sort -u | head -1)
27+
[ -n "$target" ] && context="testing $target" ;;
28+
.claude)
29+
context="configuring claude" ;;
30+
esac
31+
32+
if [ -z "$context" ] && [ -n "$branch" ]; then
33+
case "$branch" in
34+
feat/*|cf-*) context="building: ${branch#feat/}" ;;
35+
fix/*) context="fixing: ${branch#fix/}" ;;
36+
refactor/*) context="refactoring: ${branch#refactor/}" ;;
37+
test/*) context="testing: ${branch#test/}" ;;
38+
chore/*) context="chore: ${branch#chore/}" ;;
39+
esac
40+
fi
41+
42+
dirty=""
43+
if [ -n "$(git -C "$project_dir" status --porcelain 2>/dev/null)" ]; then
44+
dirty=" *"
45+
fi
46+
47+
status="$user | codeflash"
48+
[ -n "$context" ] && status="$status | $context"
49+
[ -n "$branch" ] && status="$status | $branch$dirty"
50+
echo "$status"

.claude/hooks/track-read.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
# PostToolUse hook: Track Read calls for the require-read guard.
3+
4+
INPUT=$(cat 2>/dev/null || true)
5+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null || true)
6+
7+
[ -z "$FILE_PATH" ] && exit 0
8+
9+
TRACKER="$CLAUDE_PROJECT_DIR/.claude/.read-tracker"
10+
grep -qxF "$FILE_PATH" "$TRACKER" 2>/dev/null || echo "$FILE_PATH" >> "$TRACKER"
11+
exit 0

.claude/rules/code-style.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
- **Python**: 3.9+ syntax
55
- **Package management**: Always use `uv`, never `pip`
66
- **Tooling**: Ruff for linting/formatting, mypy strict mode, prek for pre-commit checks
7-
- **Comments**: Minimal - only explain "why", not "what"
8-
- **Docstrings**: Do not add docstrings to new or changed code unless the user explicitly asks for them — not even one-liners. The codebase intentionally keeps functions self-documenting through clear naming and type annotations
9-
- **Types**: Match the type annotation style of surrounding code — the codebase uses annotations, so add them in new code
10-
- **Naming**: NEVER use leading underscores (`_function_name`) - Python has no true private functions, use public names
7+
- **Comments**: Minimal only explain "why", not "what"
8+
- **Docstrings**: Do not add docstrings unless the user explicitly asks
9+
- **Types**: Match the type annotation style of surrounding code
10+
- **Naming**: No leading underscores (`_function_name`) Python has no true private functions
1111
- **Paths**: Always use absolute paths
12-
- **Encoding**: Always pass `encoding="utf-8"` to `open()`, `read_text()`, `write_text()`, etc. in new or changed code — Windows defaults to `cp1252` which breaks on non-ASCII content. Don't flag pre-existing code that lacks it unless you're already modifying that line.
13-
- **Verification**: Use `uv run prek` to verify code — it handles ruff, ty, mypy in one pass. Don't run `ruff`, `mypy`, or `python -c "import ..."` separately; `prek` is the single verification command
12+
- **Encoding**: Always pass `encoding="utf-8"` to `open()`, `read_text()`, `write_text()` in new or changed code
13+
- **Verification**: Use `uv run prek` — it handles ruff, ty, mypy in one pass. Don't run them separately
14+
- **Code transforms**: Use `libcst` for code modification/transformation. `ast` is acceptable for read-only analysis

.claude/rules/debugging.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Debugging
2+
3+
## Root cause first
4+
5+
When encountering a bug, investigate the root cause. Don't patch symptoms. If you're about to add a try/except, a fallback default, or a defensive check — ask whether the real fix is upstream.
6+
7+
## Isolated testing
8+
9+
Prefer running individual test functions over full suites. Only run the full suite when explicitly asked or before pushing.
10+
11+
- Single function: `uv run pytest tests/test_foo.py::TestBar::test_baz -v`
12+
- Single module: `uv run pytest tests/test_foo.py -v`
13+
- Full suite: only when asked, or before `git push`
14+
15+
When debugging a specific endpoint or integration, test it directly instead of running the entire pipeline end-to-end.
16+
17+
## Subprocess failures
18+
19+
When a subprocess fails, always log stdout and stderr. "Exit code 1" with no output is useless.

.claude/rules/git.md

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,35 @@
1-
# Git Commits & Pull Requests
1+
# Git
22

33
## Commits
4+
45
- Never commit, amend, or push without explicit permission
5-
- Don't commit intermediate states — wait until the full implementation is complete, reviewed, and explicitly approved before committing. If the user corrects direction mid-implementation, incorporate the correction before any commit
6-
- Always create a new branch from `main` before starting any new work — never commit directly to `main` or reuse an existing feature branch for unrelated changes
7-
- Use conventional commit format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
8-
- Keep commits atomic - one logical change per commit
9-
- Commit message body should be concise (1-2 sentences max)
10-
- Merge for simple syncs, rebase when branches have diverged significantly
11-
- When committing to an external/third-party repo, follow that repo's own conventions for versioning, changelog, and CI
12-
- Pre-commit: Run `uv run prek` before committing — fix any issues before creating the commit
13-
- Pre-push: Run `uv run prek run --from-ref origin/<base>` to check all changed files against the PR base — this matches CI behavior and catches issues that per-commit prek misses. To detect the base branch: `gh pr view --json baseRefName -q .baseRefName 2>/dev/null || echo main`
6+
- Don't commit intermediate states — wait until the full implementation is complete and approved
7+
- Always create a new branch from `main` — never commit directly to `main`
8+
- Conventional format: `fix:`, `feat:`, `refactor:`, `docs:`, `test:`, `chore:`
9+
- First line: imperative verb + what changed, under 72 characters
10+
- Body for *why*, not *what* — the diff shows what changed
11+
- One purpose per commit: a bug fix, a new function, a refactor — not all three
12+
- A commit that adds a function also adds its tests and exports — that's one logical change
13+
14+
## Sizing
15+
16+
- Too small: renaming a variable in one commit, updating its references in another
17+
- Right size: adding a function with its tests, `__init__` export, and usage update
18+
- Too large: implementing an entire subsystem in one commit
19+
20+
## Pre-commit / Pre-push
21+
22+
- Pre-commit: Run `uv run prek` before committing
23+
- Pre-push: Run `uv run prek run --from-ref origin/<base>` to check all changed files against the PR base
1424

1525
## Pull Requests
16-
- PR titles should use conventional format
17-
- Keep the PR body short and straight to the point
26+
27+
- PR titles use conventional format
28+
- Keep the PR body short and to the point
1829
- If related to a Linear issue, include `CF-#` in the body
19-
- Branch naming: `cf-#-title` (lowercase, hyphenated), no other prefixes/suffixes
30+
- Branch naming: `cf-#-title` (lowercase, hyphenated)
31+
32+
## Branch Hygiene
33+
34+
- Delete feature branches locally after merging (`git branch -d <branch>`)
35+
- Use `/clean_gone` to prune local branches whose remote tracking branch has been deleted

.claude/rules/github.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# GitHub Interactions
2+
3+
ALWAYS use MCP GitHub tools (`mcp__github__*`) for GitHub operations. Check for a matching MCP tool first — only fall back to `gh` via Bash when no MCP tool exists for the operation.
4+
5+
This also applies to other MCP-connected services (Linear, Granola). MCP first, CLI second.

0 commit comments

Comments
 (0)