Skip to content

Conversation

@Quentor1ne
Copy link

@Quentor1ne Quentor1ne commented Jan 21, 2026

Base Branch

  • This PR targets the develop branch (required for all feature/fix PRs)
  • This PR targets main (hotfix only - maintainers)

Description

Adds require_skills frontmatter support to .claude/rules/ with timing control (when field). Rules can now specify when skills should run: during planning, per subtask, end of coding, or QA phase. Also includes a macOS build fix for EPERM copyfile errors.

Related Issue

N/A

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 📚 Documentation
  • ♻️ Refactor
  • 🧪 Test

Area

  • Frontend
  • Backend
  • Fullstack

Commit Message Format

feat(rules): add require_skills with timing control to rules

Checklist

  • I've synced with develop branch
  • I've tested my changes locally
  • I've followed the code principles (SOLID, DRY, KISS)
  • My PR is small and focused (< 400 lines ideally)
  • (Python only) All file operations specify encoding="utf-8" for text files

Platform Testing Checklist

  • Windows tested (either on Windows or via CI)
  • macOS tested (either on macOS or via CI)
  • Linux tested (CI covers this)
  • Used centralized platform/ module instead of direct process.platform checks
  • No hardcoded paths (used findExecutable() or platform abstractions)

CI/Testing Requirements

  • All CI checks pass on all platforms (Windows, macOS, Linux)
  • All existing tests pass
  • New features include test coverage
  • Bug fixes include regression tests

Screenshots

N/A - Backend feature, no UI changes.

Feature Toggle

  • N/A - Feature is complete and ready for all users

Breaking Changes

Breaking: No

Summary by CodeRabbit

  • New Features

    • Claude Rules support: path-based rules can be matched to implementation-plan files and injected into agent prompts; matched rule names and summarized required skills are shown during agent initialization.
    • Project-level toggle to enable/disable Claude Rules; env vars reflect project setting.
  • Documentation

    • Added a comprehensive Claude Rules guide covering setup, rule format, matching, and troubleshooting.
  • Tests

    • Added unit tests for glob-matching behavior used in rule matching.
  • Chores

    • Frontend packaging and linting/tooling migrated to ESLint and revised build/package scripts; package version adjusted.

✏️ Tip: You can customize this high-level summary in your review settings.

@CLAassistant
Copy link

CLAassistant commented Jan 21, 2026

CLA assistant check
All committers have signed the CLA.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Quentor1ne, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the agent's ability to adhere to project-specific guidelines by introducing a robust system for dynamic rule and skill enforcement. It allows for fine-grained control over when and where agents apply specific skills, making their autonomous development more precise and context-aware. Additionally, it addresses a critical macOS build stability issue, improving the overall developer experience.

Highlights

  • Dynamic Skill Requirements with Timing Control: Introduced the ability to define require_skills in .claude/rules/ frontmatter, allowing specific skills to be invoked at different stages of the agent's workflow (planning, per subtask, end of coding, or QA phase).
  • Path-Based Rule Loading: Implemented a system to selectively load rules from .claude/rules/ based on glob patterns defined in their frontmatter, matching against files specified in the implementation_plan.json. During the planning phase, all rules are loaded.
  • Explicit Rule Control: Added a new USE_CLAUDE_RULES environment variable and useClaudeRules project setting, providing explicit control over whether .claude/rules/ are loaded, defaulting to follow the USE_CLAUDE_MD setting.
  • macOS Build Fix: Resolved EPERM copyfile errors during macOS builds by introducing a prebuild-mac.sh script that utilizes the ditto command to pre-copy Python site-packages before electron-builder runs.
  • Comprehensive Documentation: Added a new guide (guides/features/claude-rules-support.md) detailing the configuration, usage, and troubleshooting of the .claude/rules/ feature, including examples for skill timing and path filtering.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Thanks for your first PR!

A maintainer will review it soon. Please make sure:

  • Your branch is synced with develop
  • CI checks pass
  • You've followed our contribution guide

Welcome to the Auto Claude community!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds Claude Code Rules support: backend rule discovery, parsing, glob matching, and aggregation from .claude/rules/; injection of matched rule content and structured required-skills into the agent system prompt (planning vs coding flows); frontend wiring for a per-project useClaudeRules toggle; docs and unit tests for glob matching.

Changes

Cohort / File(s) Summary
Backend Rule Loader & Client
apps/backend/core/client.py
Added should_use_claude_rules() and load_claude_rules() plus helpers for frontmatter parsing, _match_glob_pattern (** support), rules discovery/aggregation, extraction of required skills, and integration into create_client() system prompt (handles files-from-plan vs planning flows; filters skills by agent_type).
Frontend Env Wiring
apps/frontend/src/main/agent/agent-process.ts
Adds USE_CLAUDE_RULES env var based on project.settings.useClaudeRules (true/false/undefined preserves previous USE_CLAUDE_MD semantics).
Frontend Types
apps/frontend/src/shared/types/project.ts
Added useClaudeRules?: boolean to ProjectSettings.
Documentation
guides/features/claude-rules-support.md
New guide describing rule file format, env vars, matching behavior, examples, injection timing (planning vs coding), and troubleshooting.
Unit Tests
apps/backend/core/test_client.py
New tests for _match_glob_pattern covering nested ** patterns, leading/trailing **, extensions, and negative cases.
Frontend Packaging Changes
apps/frontend/package.json
Version and build/lint tooling changes (ESLint migration, updated packaging scripts, expanded build metadata). Review build scripts and dependency updates.

Sequence Diagram(s)

sequenceDiagram
    participant Frontend
    participant Backend as create_client()
    participant Rules as RulesLoader
    participant FS as ".claude/rules/"

    Frontend->>Backend: request client (project, model, agent_type, ...)
    Backend->>Backend: should_use_claude_rules()
    alt rules enabled
        Backend->>Rules: load_claude_rules(project_dir, files_to_check)
        Rules->>FS: discover rule files
        FS-->>Rules: return rule files
        Rules->>Rules: parse frontmatter & match globs (files_to_check or all)
        Rules-->>Backend: (combined_rules_content, matched_rule_names, required_skills)
        Backend->>Backend: filter skills by agent_type & phase
        Backend->>Backend: append rules + skills to system prompt
        Backend-->>Frontend: return ClaudeSDKClient (prompt contains rule content)
    else rules disabled
        Backend-->>Frontend: return ClaudeSDKClient (base prompt)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

feature, area/fullstack, size/L

Suggested reviewers

  • AndyMik90
  • AlexMadera

Poem

🐰 I hopped through .claude/rules/ tonight,
YAML lanterns glowing soft and bright.
Paths matched true, skills lined in rows,
Prompts grew wiser as the rabbit knows.
Hooray — the code-hopping rabbit shows!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding require_skills with timing control (when field) to the Claude Rules system, which is the primary feature in this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces support for Claude Code rules, allowing for path-based rule application and timing control for required skills. This is a significant feature enhancement that improves the agent's ability to adhere to project-specific guidelines. The changes also include a macOS build fix related to file copying. The documentation for the new feature is comprehensive and well-structured.


if [ -d "$SRC_DIR" ]; then
echo "[prebuild-mac] Pre-copying site-packages with ditto..."
rm -rf "$FRONTEND_DIR/dist"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The command rm -rf "$FRONTEND_DIR/dist" is very aggressive as it deletes the entire dist directory. While this might be intended to ensure a clean state, it could potentially remove other build artifacts or cause issues if subsequent build steps expect dist to be partially populated. It would be safer to only remove the specific python-site-packages directory within dist to avoid unintended side effects.

Suggested change
rm -rf "$FRONTEND_DIR/dist"
rm -rf "$DEST_DIR"

Comment on lines 880 to 881
import re
match = re.search(r'\[(.*)\]', stripped)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The import re statements are placed inside the _parse_rule_frontmatter function. Importing modules inside a function can lead to performance overhead if the function is called frequently. It's generally better practice to place imports at the top of the file.

                    import re
                    match = re.search(r'\[(.*)\]', stripped)

Comment on lines 925 to 959
if "**" in pattern:
# Split pattern into parts
parts = pattern.split("**")
if len(parts) == 2:
prefix, suffix = parts
prefix = prefix.rstrip("/")
suffix = suffix.lstrip("/")

# Check if filepath starts with prefix
if prefix and not filepath.startswith(prefix):
return False

# Check if filepath ends with suffix pattern
if suffix:
# Get the part after the prefix
if prefix:
remaining = filepath[len(prefix):].lstrip("/")
else:
remaining = filepath

# The suffix might contain wildcards
if "*" in suffix:
# Match the suffix pattern against the end of remaining
# Try matching against each possible suffix of remaining
remaining_parts = remaining.split("/")
suffix_parts = suffix.split("/")

for i in range(len(remaining_parts) - len(suffix_parts) + 1):
candidate = "/".join(remaining_parts[i:])
if fnmatch.fnmatch(candidate, suffix):
return True
return False
else:
return remaining.endswith(suffix) or fnmatch.fnmatch(remaining, f"*/{suffix}")
return True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The custom logic for handling ** in glob patterns is quite complex. Python's fnmatch module in Python 3.9+ and later versions natively supports ** for recursive wildcard matching. If the target Python environment is 3.9 or newer, this custom logic might be redundant and could be simplified by relying on fnmatch's built-in capabilities, reducing potential edge cases and improving readability.

Comment on lines 26 to 28
rm -rf "$FRONTEND_DIR/dist"
mkdir -p "$(dirname "$DEST_DIR")"
ditto "$SRC_DIR" "$DEST_DIR"

This comment was marked as outdated.

@Quentor1ne
Copy link
Author

I have read the CLA Document and I hereby sign the CLA

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Around line 872-891: The parser currently uses fixed thresholds (indent >= 4 /
indent < 4) to detect nested skill properties which fails for 2-space YAML;
change it to track the base indentation of the current list item and use that
for comparisons: when you create current_skill (where you detect a new "-
skill:"/list item) record skill_base_indent = indent, then replace checks that
use "indent >= 4" with "indent > skill_base_indent" when parsing nested keys
like "when" and "paths", and replace the end-of-skills check "indent < 4" with
"indent <= skill_base_indent" to close the skill block; update references to
current_skill and in_skills accordingly so nested properties are recognized
regardless of 2- or 4-space YAML.
- Around line 825-828: Add a single module-level "import re" at the top of
apps/backend/core/client.py and remove all inline "import re" statements inside
the function(s); locate occurrences by searching for uses like "match =
re.search(...)" and the list comprehension "paths = [p.strip().strip("'\"") for
p in match.group(1).split(",")]" and delete the local imports around those
blocks (the inline imports around the match/paths logic and the similar blocks
at the other occurrences) so the code uses the top-level re module instead of
importing inside the function.
- Around line 1555-1561: The prompt text stored in skill_instruction incorrectly
instructs users to manually invoke skills with slash commands; update the string
assigned to skill_instruction to say that the Claude Agent SDK automatically
selects and invokes relevant skills based on SKILL.md and context rather than
using manual slash syntax, e.g., replace the sentence "Use the Skill tool to
invoke skills. Example: `/review`, `/security-audit`" with a brief statement
that skills are automatically selected and invoked by Claude when relevant and
that users should ensure SKILL.md descriptions and context are accurate.
- Around line 925-959: The current matching branch only supports a single "**"
because it splits pattern into parts and only handles len(parts) == 2; update
the logic to handle patterns with multiple "**" occurrences by either: 1)
delegating complex patterns to a glob library with recursive "**" support (e.g.,
wcmatch) before falling back to fnmatch, or 2) converting the pattern (using the
variables pattern and parts) into a proper regex that treats each "**" as a
recursive wildcard and then run that regex against filepath instead of using
fnmatch.fnmatch; ensure the new code still preserves the existing prefix/suffix
checks and returns the same boolean semantics for simple patterns.

In `@apps/frontend/scripts/prebuild-mac.sh`:
- Line 5: The script currently enables "set -e" which stops on errors but
doesn't catch use of undefined variables; update the prebuild script to enable
stricter variable checking by adding "set -u" alongside "set -e" (i.e., change
the bootstrap of the script where "set -e" appears so it becomes "set -euo
pipefail" or at minimum add "-u") to fail fast on unset variables and prevent
subtle build-time bugs.

In `@guides/features/claude-rules-support.md`:
- Around line 145-157: Update the markdown code block that shows the console
output (the block starting with the triple backticks before "Security settings:
.claude_settings.json" and ending with the closing triple backticks) to include
a language specifier by changing the opening fence from ``` to ```text so the
block reads ```text and keep the closing ``` unchanged; this affects the snippet
containing ".claude_settings.json", "Sandbox enabled", and the listed
.claude/rules entries.
- Around line 161-177: The directory-tree code block starting with
".claude/rules/" is missing a language specifier; update the opening code fence
for that block to include "text" (i.e., change the opening ``` to ```text) so
the directory structure is rendered as plain text and preserves formatting.
- Around line 196-215: The fenced code blocks in the three numbered
troubleshooting items ("Check USE_CLAUDE_MD is set", "Check console output for
\"rules matched\"", and "Verify frontmatter format") lack surrounding blank
lines and violate MD031; fix by adding a blank line before and after each fenced
block so each fenced block is separated from the list text (i.e., ensure a blank
line precedes the ```bash / ```text / ```yaml opening fences and a blank line
follows each closing ```), updating those sections in
guides/features/claude-rules-support.md where the headings and list items
appear.
- Around line 130-140: Update the fenced code block that begins with
"Implementation Plan:" so its opening fence includes a language specifier (e.g.,
change the opening ``` to ```text) to enable proper syntax highlighting and
linting; locate the triple-backtick block containing "Implementation Plan:" in
guides/features/claude-rules-support.md and replace the fence accordingly.
- Around line 219-228: The JSON snippet for context.json is missing blank lines
before and after the fenced code block; edit the markdown so the fenced JSON
block that shows "patterns" and "api_route_pattern" is separated by a blank line
above the opening ```json and a blank line after the closing ``` to ensure
proper rendering and readability, and keep the shown keys ("patterns",
"api_route_pattern") intact so readers notice if patterns are empty.

Comment on lines 219 to 234
Check `context.json` in the spec folder - it should contain distilled patterns from your rules:
```json
{
"patterns": {
"api_route_pattern": "Use withCsrfProtection wrapper...",
...
}
}
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Add blank lines around JSON code block.

🔧 Suggested fix
 Check `context.json` in the spec folder - it should contain distilled patterns from your rules:
+
 ```json
 {
   "patterns": {
     "api_route_pattern": "Use withCsrfProtection wrapper...",
     ...
   }
 }

If patterns are empty, the rule parsing may have failed silently.

</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
Check `context.json` in the spec folder - it should contain distilled patterns from your rules:

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

220-220: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🤖 Prompt for AI Agents
In `@guides/features/claude-rules-support.md` around lines 219 - 228, The JSON
snippet for context.json is missing blank lines before and after the fenced code
block; edit the markdown so the fenced JSON block that shows "patterns" and
"api_route_pattern" is separated by a blank line above the opening ```json and a
blank line after the closing ``` to ensure proper rendering and readability, and
keep the shown keys ("patterns", "api_route_pattern") intact so readers notice
if patterns are empty.

@Quentor1ne
Copy link
Author

Feedback Addressed

All CodeRabbit review feedback has been addressed in the latest commits:

Code fixes (client.py):

  • ✅ Moved import re and import fnmatch to top of file (no inline imports)
  • ✅ Fixed indent parsing to use dynamic skill_base_indent (supports 2-space YAML)
  • ✅ Rewrote glob matching with regex to support multiple ** patterns
  • ✅ Updated skill instruction text to reflect automatic invocation

Documentation fixes (claude-rules-support.md):

  • ✅ Added language specifiers to all fenced code blocks
  • ✅ Added blank lines around fenced code blocks (MD031)

Removed local workaround:

  • ✅ Removed prebuild-mac.sh and related package.json changes (local fix, not part of feature)

Ready for maintainer review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Around line 1034-1064: The current de-duplication uses seen_skills keyed only
by skill name, which collapses distinct requirements across phases/paths; update
the logic in both branches where skills are added (the "if files_to_check is
None" branch and the pattern-match branch inside the loop over all_rules) to
compute a composite key (e.g., combine skill.get("skill",""),
skill.get("phase",""), and a stable representation of skill.get("paths",[])) and
use that composite key instead of just skill_name when checking/adding to
seen_skills so all_required_skills retains separate entries for the same skill
in different phases or with different paths (refer to seen_skills,
all_required_skills, and the for rule_path, patterns, skills, content in
all_rules loop).
- Around line 811-894: The parser treats a nested "paths:" under a structured
skill as a new top-level skill because it only tracks in_paths/in_skills; add a
third state (e.g., in_skill_paths) and use the existing current_skill and
skill_base_indent to detect when "paths:" belongs to the current skill (indent >
skill_base_indent). When you see "paths:" while current_skill is non-None and
indent > skill_base_indent, set in_skill_paths and parse inline arrays into
current_skill["paths"] or collect subsequent "- " list entries into
current_skill["paths"] (stripping quotes) until you hit a line with indent <=
skill_base_indent or a non-list/comment, then clear in_skill_paths; ensure
existing logic still finalizes current_skill into skills when closing the skill
block.
♻️ Duplicate comments (1)
apps/frontend/scripts/prebuild-mac.sh (1)

24-27: Avoid deleting the entire dist directory.

This still removes unrelated build artifacts; only the target python-site-packages path should be cleared.

🔧 Safer deletion
-  rm -rf "$FRONTEND_DIR/dist"
+  rm -rf "$DEST_DIR"

matched_rules = []
matched_names = []
# Track skills by name to avoid duplicates (keep first occurrence)
seen_skills: set[str] = set()

This comment was marked as outdated.

@Quentor1ne
Copy link
Author

Additional Fixes (93446b1)

Addressed two more issues from CodeRabbit review:

  1. Nested paths: list parsing - Added in_skill_paths state to properly handle multi-line paths: arrays under structured skills. Previously, nested path list items were incorrectly treated as new skill entries.

  2. Skill de-duplication preserves variants - Changed deduplication key from just skill name to composite (skill, when, paths). Same skill with different when timing or paths filters are now preserved separately.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Around line 905-911: The end-of-skills branch currently stops skills parsing
without reprocessing the same line, causing a top-level "paths:" that appears
after require_skills to be ignored; update the branch that sets in_skills =
False / in_skill_paths = False (referencing current_skill, skills, in_skills,
in_skill_paths) to first flush current_skill into skills as now, then inspect
the same stripped line for a top-level "paths:" token and, if present, enter the
paths parsing state (set in_skill_paths = True and in_skills = False) and handle
the line as the start of the paths block (i.e., parse or mark it for the
existing paths-parsing logic) so that rule-level paths are not dropped when
"paths:" follows the skills block.
- Around line 942-975: The current glob-to-regex translation in the loop that
builds regex_pattern (the block handling pattern[i:i+2] == "**") incorrectly
skips a trailing slash after "**", collapsing path separators and overmatching;
change the "**" translation to a segment-aware regex such as "(?:[^/]+/)*" (or
an equivalent that matches zero or more directory segments including their
slashes) and remove the code that advances past a following "/" so slashes are
preserved; keep the rest of the loop logic and final anchoring (regex_pattern =
"^" + regex_pattern + "$") intact.

@Quentor1ne Quentor1ne force-pushed the feature/claude-rules-support branch from 93446b1 to dfe1248 Compare January 21, 2026 02:17
@sentry
Copy link

sentry bot commented Jan 21, 2026

Codecov Report

❌ Patch coverage is 5.33333% with 284 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
apps/backend/core/client.py 5.99% 251 Missing ⚠️
apps/backend/core/test_client.py 0.00% 33 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/backend/core/client.py (1)

1-24: Address the ruff format linting failure.

The pipeline reports that ruff format check failed for this file. Run ruff format apps/backend/ to auto-fix the formatting issues before merging.

🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Around line 744-752: The loop over rule files using rules_dir.rglob("*.md")
currently only appends rules when both paths and rule_content are present (see
_parse_rule_frontmatter and the rules.append line), which silently skips files
with no frontmatter paths (global rules); update the logic to either (a) allow
appending rules that have rule_content even if paths is empty (so global rules
are loaded) or (b) add a logger.debug/logging statement inside the try block
when a file is skipped (i.e., when not (paths and rule_content)) that includes
the rule_path and whether paths or rule_content was missing so users can see why
a rule wasn’t loaded, keeping the exception handling unchanged.
- Around line 673-710: Add unit tests for the _match_glob_pattern function to
cover nested-path glob semantics: create tests that assert
_match_glob_pattern("src/**/*.ts", "src/a/b/c/file.ts") is True,
_match_glob_pattern("**/test/**/*.ts", "foo/test/bar/baz.ts") is True,
_match_glob_pattern("src/**/test/**", "src/deep/nested/test/files") is True, and
_match_glob_pattern("src/**/test/*.ts", "src/contest.ts") is False; put them in
the appropriate test module for apps/backend/core (use the same import path used
by existing tests) and use the test framework’s assertion helpers to make
expectations explicit so future regressions in _match_glob_pattern are caught.

Comment on lines 673 to 723
# Handle ** patterns by converting to regex
if "**" in pattern:
# Convert glob pattern to regex
# Escape regex special characters except * and ?
regex_pattern = ""
i = 0
while i < len(pattern):
if pattern[i:i+2] == "**":
i += 2
# Check if ** is followed by /
if i < len(pattern) and pattern[i] == "/":
# **/ matches zero or more directory segments (segment-aware)
# Use (?:.*/)? to match zero or more path segments including slashes
regex_pattern += "(?:.*/)?"
i += 1 # Skip the /
else:
# ** at end or before non-/ matches remaining path
regex_pattern += ".*"
elif pattern[i] == "*":
# * matches any characters except /
regex_pattern += "[^/]*"
i += 1
elif pattern[i] == "?":
# ? matches any single character except /
regex_pattern += "[^/]"
i += 1
elif pattern[i] in ".^$+{}[]|()":
# Escape regex special characters
regex_pattern += "\\" + pattern[i]
i += 1
else:
regex_pattern += pattern[i]
i += 1

# Match the full path
regex_pattern = "^" + regex_pattern + "$"
return bool(re.match(regex_pattern, filepath))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Test the glob matching logic with sample patterns
python3 << 'EOF'
import re

def _match_glob_pattern(pattern: str, filepath: str) -> bool:
    pattern = pattern.replace("\\", "/").strip("/")
    filepath = filepath.replace("\\", "/").strip("/")
    
    if "**" in pattern:
        regex_pattern = ""
        i = 0
        while i < len(pattern):
            if pattern[i:i+2] == "**":
                i += 2
                if i < len(pattern) and pattern[i] == "/":
                    regex_pattern += "(?:.*/)?"
                    i += 1
                else:
                    regex_pattern += ".*"
            elif pattern[i] == "*":
                regex_pattern += "[^/]*"
                i += 1
            elif pattern[i] == "?":
                regex_pattern += "[^/]"
                i += 1
            elif pattern[i] in ".^$+{}[]|()":
                regex_pattern += "\\" + pattern[i]
                i += 1
            else:
                regex_pattern += pattern[i]
                i += 1
        regex_pattern = "^" + regex_pattern + "$"
        return bool(re.match(regex_pattern, filepath))
    return False

# Test cases
tests = [
    ("src/**/*.ts", "src/foo/bar/file.ts", True),
    ("src/**/*.ts", "src/file.ts", True),
    ("**/test/**/*.ts", "foo/test/bar/baz.ts", True),
    ("src/**/test/**", "src/deep/nested/test/files", True),
    ("src/**/*.ts", "src/contest.ts", True),
    ("src/**/test/*.ts", "src/contest.ts", False),
]

print("Testing glob patterns:")
all_pass = True
for pattern, filepath, expected in tests:
    result = _match_glob_pattern(pattern, filepath)
    status = "✓" if result == expected else "✗"
    if result != expected:
        all_pass = False
        # Print the generated regex for debugging
        regex_pattern = ""
        i = 0
        pat_copy = pattern.replace("\\", "/").strip("/")
        while i < len(pat_copy):
            if pat_copy[i:i+2] == "**":
                i += 2
                if i < len(pat_copy) and pat_copy[i] == "/":
                    regex_pattern += "(?:.*/)?"
                    i += 1
                else:
                    regex_pattern += ".*"
            elif pat_copy[i] == "*":
                regex_pattern += "[^/]*"
                i += 1
            elif pat_copy[i] == "?":
                regex_pattern += "[^/]"
                i += 1
            elif pat_copy[i] in ".^$+{}[]|()":
                regex_pattern += "\\" + pat_copy[i]
                i += 1
            else:
                regex_pattern += pat_copy[i]
                i += 1
        regex_pattern = "^" + regex_pattern + "$"
        print(f"{status} Pattern '{pattern}' vs '{filepath}': got {result}, expected {expected}")
        print(f"   Generated regex: {regex_pattern}")
    else:
        print(f"{status} Pattern '{pattern}' vs '{filepath}': got {result}, expected {expected}")

print(f"\nAll tests passed: {all_pass}")
EOF

Repository: AndyMik90/Auto-Claude

Length of output: 151


🏁 Script executed:

sed -n '673,710p' apps/backend/core/client.py

Repository: AndyMik90/Auto-Claude

Length of output: 1531


🏁 Script executed:

#!/bin/bash
# Test simpler cases to identify which pattern causes the timeout
python3 << 'EOF'
import re
import signal

def timeout_handler(signum, frame):
    raise TimeoutError("Regex matching timed out")

def _match_glob_pattern(pattern: str, filepath: str) -> bool:
    pattern = pattern.replace("\\", "/").strip("/")
    filepath = filepath.replace("\\", "/").strip("/")
    
    if "**" in pattern:
        regex_pattern = ""
        i = 0
        while i < len(pattern):
            if pattern[i:i+2] == "**":
                i += 2
                if i < len(pattern) and pattern[i] == "/":
                    regex_pattern += "(?:.*/)?"
                    i += 1
                else:
                    regex_pattern += ".*"
            elif pattern[i] == "*":
                regex_pattern += "[^/]*"
                i += 1
            elif pattern[i] == "?":
                regex_pattern += "[^/]"
                i += 1
            elif pattern[i] in ".^$+{}[]|()":
                regex_pattern += "\\" + pattern[i]
                i += 1
            else:
                regex_pattern += pattern[i]
                i += 1
        regex_pattern = "^" + regex_pattern + "$"
        return bool(re.match(regex_pattern, filepath))
    return False

# Test each case individually with timeout
tests = [
    ("src/**/*.ts", "src/foo/bar/file.ts"),
    ("src/**/*.ts", "src/file.ts"),
    ("**/test/**/*.ts", "foo/test/bar/baz.ts"),
]

print("Testing individual cases:")
for pattern, filepath in tests:
    try:
        signal.signal(signal.SIGALRM, timeout_handler)
        signal.alarm(5)  # 5 second timeout per test
        result = _match_glob_pattern(pattern, filepath)
        signal.alarm(0)  # Cancel alarm
        print(f"✓ '{pattern}' vs '{filepath}': {result}")
    except TimeoutError:
        print(f"✗ '{pattern}' vs '{filepath}': TIMEOUT (catastrophic backtracking)")
    except Exception as e:
        print(f"✗ '{pattern}' vs '{filepath}': ERROR - {e}")
EOF

Repository: AndyMik90/Auto-Claude

Length of output: 227


🏁 Script executed:

find apps/backend -type f -name "*.py" | xargs grep -l "_match_glob_pattern\|test.*glob" | head -20

Repository: AndyMik90/Auto-Claude

Length of output: 132


🏁 Script executed:

sed -n '650,715p' apps/backend/core/client.py

Repository: AndyMik90/Auto-Claude

Length of output: 2520


🏁 Script executed:

grep -A 20 -B 5 "_match_glob_pattern\|test.*glob" apps/backend/analysis/test_discovery.py | head -80

Repository: AndyMik90/Auto-Claude

Length of output: 1022


🏁 Script executed:

sed -n '650,730p' apps/backend/core/client.py

Repository: AndyMik90/Auto-Claude

Length of output: 2966


🏁 Script executed:

grep -r "_match_glob_pattern" apps/backend --include="*.py" -B 2 -A 2 | head -40

Repository: AndyMik90/Auto-Claude

Length of output: 714


Add unit tests for the _match_glob_pattern function to ensure glob matching handles nested paths correctly.

The function lacks test coverage. While manual testing shows basic patterns work (e.g., src/**/*.ts correctly matches src/foo/bar/file.ts), add tests to document expected behavior and catch edge cases:

  • src/**/*.ts matching src/a/b/c/file.ts
  • **/test/**/*.ts matching foo/test/bar/baz.ts
  • src/**/test/** matching src/deep/nested/test/files
  • src/**/test/*.ts NOT matching src/contest.ts
🤖 Prompt for AI Agents
In `@apps/backend/core/client.py` around lines 673 - 710, Add unit tests for the
_match_glob_pattern function to cover nested-path glob semantics: create tests
that assert _match_glob_pattern("src/**/*.ts", "src/a/b/c/file.ts") is True,
_match_glob_pattern("**/test/**/*.ts", "foo/test/bar/baz.ts") is True,
_match_glob_pattern("src/**/test/**", "src/deep/nested/test/files") is True, and
_match_glob_pattern("src/**/test/*.ts", "src/contest.ts") is False; put them in
the appropriate test module for apps/backend/core (use the same import path used
by existing tests) and use the test framework’s assertion helpers to make
expectations explicit so future regressions in _match_glob_pattern are caught.

@Quentor1ne
Copy link
Author

Long story short. I love the idea of Auto Claude. And of course, I started using it.
Only to find out the hard way that everything it created was somehow off, broken, weird, and wrong.
A day later, it was clear, no way to make it follow the rules. :) So now it does. at least for me. Hopefully, it helps everyone. Or if someone has a better idea for solving it... good. Happy to use.

have to admit that skills are so fun and uncharted territory, that it's hard to say how they should actually fit into the picture in the long run. Maybe they need some UI at some point.

Cheers!

Quentor1ne and others added 10 commits January 21, 2026 11:30
Implements Claude Code's convention of path-based rules in .claude/rules/.
Rules are markdown files with YAML frontmatter containing glob patterns
that determine which files trigger the rule.

Features:
- Parses YAML frontmatter with 'paths' arrays from rule files
- Matches glob patterns including ** for directory depth
- Loads rules based on files in implementation_plan.json
- Falls back to loading all rules during planning phases
- New USE_CLAUDE_RULES env var (defaults to following USE_CLAUDE_MD)
- UI setting support via useClaudeRules in ProjectSettings

Example rule format:
---
paths:
  - src/app/api/**/*.ts
  - src/components/**/*.tsx
---

# Rule content here...

This enables Auto-Claude to respect project-specific coding standards,
security patterns, and conventions defined in .claude/rules/.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Parse structured require_skills format in rule frontmatter
- Support `when` timing: planning, per_subtask, end_of_coding, qa_phase
- Support optional `paths` filter for narrowing skill execution
- Generate agent-aware skill instructions based on current phase
- Add prebuild-mac.sh to fix EPERM copyfile issues on macOS

Docs: guides/features/claude-rules-support.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Move import re/fnmatch to top of file (no inline imports)
- Fix indent parsing to use dynamic base indent (supports 2-space YAML)
- Improve glob matching with regex to support multiple ** patterns
- Update skill instruction text to reflect automatic invocation
- Add set -euo pipefail to prebuild-mac.sh for stricter checking
- Add language specifiers to markdown code blocks
- Add blank lines around fenced code blocks (MD031)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This removes the prebuild-mac.sh script and package.json changes
that were a local workaround for EPERM copyfile issues. These
are not part of the upstream feature contribution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add in_skill_paths state to properly parse multi-line paths: lists
  under structured skills (fixes silent parsing failure)
- Change skill deduplication to use composite key (skill, when, paths)
  instead of just skill name (preserves different configurations of
  same skill across phases)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- When skills block ends, reprocess line for top-level paths:
  that may follow require_skills: in frontmatter
- Use segment-aware regex (?:.*/)? for **/ patterns instead of
  greedy .* to prevent overmatching across path segments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When rules in .claude/rules/ have content but no paths: frontmatter,
they are now logged at debug level for visibility.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests cover nested-path glob semantics:
- src/**/*.ts matching nested directories
- **/test/**/*.ts with leading/trailing double star
- src/**/test/** with deeply nested paths
- src/**/test/*.ts NOT matching partial names (contest vs test)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Quentor1ne Quentor1ne force-pushed the feature/claude-rules-support branch from 84e3e74 to b955c85 Compare January 21, 2026 09:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Around line 570-576: When parsing frontmatter in apps/backend/core/client.py,
the in_paths block currently ends on encountering a non-list top-level line but
doesn't reprocess that same 'stripped' line, causing 'require_skills' (and other
keys) to be skipped; modify the logic around the in_paths branch so that when
you detect a non-list top-level line you set in_paths = False and immediately
re-evaluate the same 'stripped' value against the top-level key handlers (i.e.,
don't consume the line). Concretely, change the current "elif in_paths: ... elif
stripped and not stripped.startswith('#'): in_paths = False" flow so the
non-list branch either uses a continue to re-run the top-level checks on the
same line or replaces the elif with an if and falls through to the following
in_skills / require_skills checks; refer to the variables/functions 'in_paths',
'in_skills', 'paths', and the 'stripped' line handling to locate the spot to
adjust.

When parsing rule frontmatter, the require_skills key was being skipped
if it immediately followed the paths array because:
1. The paths block didn't continue after processing
2. The require_skills check used elif instead of if

This fix:
- Adds continue statements after processing paths: and require_skills: keys
- Changes elif to if for require_skills:, in_paths, and in_skills checks
- Adds continue after processing path list items
- Allows proper fall-through when paths array ends to check for other keys

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/backend/core/client.py`:
- Around line 689-728: Add unit tests in the existing test_client.py that
exercise the glob-matching branch in client.py which converts "**" patterns to
regex (the code that builds regex_pattern and returns re.match or falls back to
fnmatch.fnmatch); include positive and negative assertions for these edge cases:
pattern "**" (should match any non-empty and empty filepath as applicable),
pattern "**.ts" (no slash after ** — ensure it matches filenames ending with .ts
but not across path separators), an explicit empty filepath input, and negative
cases for each to guard regressions. Use the same matching entry-point used by
current tests (call the function that takes pattern and filepath) and assert
True/False accordingly.

Quentor1ne and others added 2 commits January 21, 2026 12:47
Addresses CodeRabbit review feedback:
- Test standalone ** pattern matches any path (including empty)
- Test **.ts pattern without slash matches .ts files
- Test empty filepath with specific patterns
- Test empty pattern edge case

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@AndyMik90 AndyMik90 force-pushed the develop branch 2 times, most recently from 67a743f to e83e445 Compare January 21, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants