* fix(llm/providers/claude): attach cache_control to system block, not top-level
The Anthropic Messages API does not accept `cache_control` as a top-level
request parameter — it is a per-content-block field. Passing it at the top
level raises `TypeError` in the Python SDK (which validates kwargs against
`messages.create()`'s signature) or a `400 unknown_parameter` from the API,
so every ClaudeProvider.generate() call fails.
Move `cache_control: {"type": "ephemeral"}` onto the last system-prompt
block so ephemeral prompt caching still works when a system prompt is
present, and drop it when there isn't one (nothing to cache).
Existing tests didn't catch this because `FakeMessages.create(**_params)`
accepted anything and ignored the kwargs. FakeMessages now records
`last_params`, and two regression tests assert that:
- `cache_control` never appears as a top-level param, and
- when a system prompt is set, `cache_control` rides on the last block.
Fixes#2512
* test(claude_provider): split composite isinstance+truthiness assertion (PT018)
Ruff PT018 flagged the combined `isinstance(system, list) and system` check
in `test_generate_does_not_pass_cache_control_as_top_level_param`. Split
it into two focused asserts (`isinstance(system, list)` then `assert system`)
so a failure points at the exact violation instead of a compound condition.
Behavior unchanged; 6/6 tests still pass.
Addresses CodeRabbit review on #2515.
* fix: add .gitattributes to force LF line endings for text files
npm run command-registry:check (part of npm test) fails on a fresh clone
on Windows with the common core.autocrlf=true setting: git checks out
docs/COMMAND-REGISTRY.json with CRLF, but generate-command-registry.js
always writes LF, so the strict string comparison in checkRegistry()
never matches. Forcing LF via .gitattributes makes checkouts consistent
across platforms regardless of a contributor's local autocrlf setting.
* fix: normalize CRLF line endings to LF per .gitattributes
pyproject.toml, src/llm/__init__.py, src/llm/prompt/builder.py,
src/llm/providers/claude.py, and tests/test_builder.py had CRLF line
endings committed to the repo, inconsistent with the rest of the
codebase. Renormalized via 'git add --renormalize .' now that
.gitattributes enforces eol=lf.
---------
Co-authored-by: Affaan Mustafa <me@affaanmustafa.com>
Replace invalid default model IDs (e.g. claude-sonnet-4-7) with current
claude-sonnet-4-6, claude-opus-4-8, and claude-haiku-4-5. Route system
messages to the API system field, enable ephemeral prompt caching, omit
temperature for Opus 4.7/4.8, and surface cache usage metrics. Update the
CLI model picker to match.
Co-authored-by: Vladimir Đuranović <vlada@MacBook-Pro.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
The OpenAI-compatible API can return HTTP 200 with an empty choices list
or choices[0].message = None (content-filtered responses on Gemini,
overwhelmed Ollama instances). Without a guard, both sites raise an
unhandled IndexError or AttributeError crashing the provider.
Added guard in OpenAIProvider.generate() and AstraFlowProvider.generate().
- add Vite and Redis pattern skills from closed stale PRs
- add frontend-slides support assets
- port skill-comply runner fixes and LLM prompt/provider regressions
- harden agent frontmatter validation and sync catalog counts