Skip to content

Commit 2cc5546

Browse files
ilblackdragonclaudezmanian
authored
feat(tools): production-grade coding tools, file history, and skills (#2025)
* feat(tools): add production-grade coding tools, file history, and coding skills Add dedicated coding tools inspired by Claude Code's architecture to make IronClaw a more effective coding assistant: New tools: - GlobTool: fast file pattern matching via `glob` crate, sorted by mtime, with default exclusions (.git, node_modules, target, etc.) - GrepTool: content search wrapping ripgrep with 3 output modes (content, files_with_matches, count), pagination, and context lines - FileUndoTool: restore files to pre-modification state using in-memory file history snapshots Enhanced tools: - ReadFileTool: 10MB limit, 2000-line default, binary detection, device path blocking (/dev/zero, /proc/*/fd/*) - ApplyPatchTool: uniqueness validation (error on ambiguous matches), workspace path rejection, 10MB size limit, file history integration - WriteFileTool: file history integration for undo support Updated tool descriptions to guide LLM behavior (prefer apply_patch over write_file, always read before editing, use glob/grep instead of shell). New skills: - coding: best practices for code editing, search, and file operations - commit: git commit message generation workflow - review: code review workflow with structured checklist Shared infrastructure: - DEFAULT_EXCLUDED_DIRS constant in path_utils.rs - FileHistory module with SharedFileHistory for cross-tool snapshots 66 new tests covering all tools, edge cases, and regression scenarios. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply cargo fmt formatting Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tools): address PR review — security, correctness, and robustness fixes - Move device path blocking after validate_path() to prevent traversal bypass - Add /proc/kcore, /proc/kmem to blocked paths - Reject absolute patterns and '..' in glob tool, add strip_prefix defense - Wrap glob sync I/O in spawn_blocking to avoid blocking tokio executor - Sort files_with_matches globally before pagination in grep tool - Add default exclusions for node_modules/target in grep tool - Inject ctx.extra_env into rg environment matching ShellTool policy - Use per-line strip_prefix for content mode path relativization - Change FileSnapshot.content_before to Vec<u8> for binary file support - Log snapshot errors with tracing::debug instead of silently discarding - Fix skill name mismatch: code-review → review to match directory Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(skills): rename review skill directory to code-review Aligns the directory name with the manifest name (code-review) to prevent incorrect override/dedup behavior in the bundled-skill loader. The name stays "code-review" since other domains may also need review-type skills. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(tools): add file edit guards — staleness detection, fuzzy matching, encoding preservation Add file_edit_guard module with production-grade safeguards for file editing: - ReadFileState tracks file reads with mtime for staleness detection - 4-level fuzzy matching fallback (exact → whitespace-normalized → quote-normalized → both) - UTF-16LE BOM detection and line ending style preservation (LF/CRLF/CR) - Read-before-edit enforcement for ApplyPatch and WriteFile tools - No-op edit rejection (old_string == new_string) - Shared state injection via Arc<RwLock<>> across ReadFile, WriteFile, ApplyPatch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tools): address all PR review comments — session scoping, parallelism, security - Session-scoped state: ReadFileState and FileHistory now keyed by job_id so concurrent sessions sharing the same registry don't leak state (#2025) - Parallel metadata: grep files_with_matches uses JoinSet (max 64 concurrency) instead of sequential await per file for mtime sorting - Shared env allowlist: grep_tool imports SAFE_ENV_VARS from shell.rs (made pub(crate)) instead of maintaining a divergent copy - Glob traversal: uses Component::ParentDir check instead of substring ".." match, so patterns like "foo..bar" are no longer falsely rejected - UTF-16LE in read_file: binary detection skips null-byte check for files with UTF-16LE BOM; read_file uses encoding-aware read path - Partial flag: default 2000-line truncation now marks read as partial, preventing edits against unseen content - write_file guard softened: staleness check logs warning instead of hard error (full-file replacement has lower risk than apply_patch) - Updated e2e trace to include read_file before apply_patch - Updated expected tool list in schema validation tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tools): use async metadata instead of blocking path.exists() in write_file Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(ci): fix false-positive panic detection for lifetimes in char lexer The check_no_panics.py lexer misinterpreted Rust lifetimes ('static) as char literal starts, causing in_char state to persist across lines and hide all subsequent brace-delimited blocks — including #[cfg(test)] mod tests. Reset in_char at line boundaries since Rust char literals cannot span lines. https://claude.ai/code/session_012bJjER6L5zSAFd9BqYMUmC * test: verify MCP push works * test * chore: remove test file * style: apply cargo fmt to file.rs Collapse multi-line method chain to single line per rustfmt. https://claude.ai/code/session_012bJjER6L5zSAFd9BqYMUmC * style: apply cargo fmt to file.rs Collapse multi-line method chain to single line per rustfmt. https://claude.ai/code/session_012bJjER6L5zSAFd9BqYMUmC * fix(file-tools): harden fuzzy patch matching and undo * fix(ci): formatting + wasmtime 43 cache config compatibility After merging latest staging, cargo fmt had diffs in file tools and the wasmtime cache TOML format changed (v43 dropped the `enabled` field under `[cache]`). Also removes accidental .fmt-test artifact. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(file-tools): simplify strip_trailing_whitespace Remove redundant double-pass through .lines() — the first collect+join was a no-op since .lines() already handles line endings. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tools): address PR review comments — security, correctness, tests - Add is_sensitive_path checks to GlobTool and GrepTool, matching the defense-in-depth posture of ReadFileTool/WriteFileTool/ListDirTool - Fix UTF-8 panicking byte-index slice in apply_patch error preview (old_string[..200] → chars().take(200)) - Add 10MB size guard on file_history snapshots to prevent memory exhaustion from snapshotting large files - Replace dead turn_number field with auto-incrementing sequence_number in FileHistory — callers no longer pass a hardcoded 0 - Fix glob mtime test flakiness by increasing sleep to 1100ms (above 1s filesystem granularity) - Fix emoji test to actually include emoji/non-ASCII content Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Zaki Manian <zaki@iqlusion.io>
1 parent 152e8b0 commit 2cc5546

18 files changed

Lines changed: 3709 additions & 66 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ serde_yml = "0.0.12"
130130
# Filesystem paths
131131
dirs = "6"
132132
fs4 = "0.6"
133+
glob = "0.3"
133134

134135
# Semantic versioning
135136
semver = "1"

scripts/check_no_panics.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ def sanitize_line(line: str, state: LexerState) -> str:
132132
out[i] = ch
133133
i += 1
134134

135+
# Rust char literals cannot span lines; reset if still open at EOL.
136+
if state.in_char:
137+
state.in_char = False
138+
state.char_escape = False
139+
135140
return "".join(out)
136141

137142

skills/code-review/SKILL.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
name: code-review
3+
version: "1.0.0"
4+
description: Review code changes for bugs, style, and security issues
5+
activation:
6+
keywords:
7+
- "review"
8+
- "code review"
9+
- "review changes"
10+
patterns:
11+
- "(?i)review\\s.*(code|changes|diff|PR|pull request|commit)"
12+
- "(?i)(check|look at|inspect)\\s.*(changes|diff|code)"
13+
tags:
14+
- "code-review"
15+
- "quality"
16+
max_context_tokens: 1200
17+
---
18+
19+
# Code Review Workflow
20+
21+
When the user asks to review code:
22+
23+
1. **Get the changes**: Run `shell` with `git diff` (unstaged) or `git diff --cached` (staged) or `git diff HEAD~1` (last commit) depending on context.
24+
2. **Focus on what changed**, not surrounding code. Don't review unchanged code unless it's directly relevant to the change.
25+
3. **Check for these categories:**
26+
- **Bugs**: Logic errors, off-by-one, null/undefined handling, race conditions
27+
- **Security**: Injection vulnerabilities, credential exposure, path traversal, XSS
28+
- **Error handling**: Missing error cases, swallowed errors, unclear error messages
29+
- **Edge cases**: Empty inputs, large inputs, concurrent access, unicode handling
30+
- **Style**: Inconsistency with surrounding code, unclear naming, missing/excessive comments
31+
4. **Provide actionable feedback** with specific file:line references. Don't just say "this could be improved" - say what to change and why.
32+
5. **Be proportional**: A one-line typo fix doesn't need a full security audit. Match review depth to change scope.
33+
6. If the changes look good, say so clearly. Don't invent problems.

skills/coding/SKILL.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
name: coding
3+
version: "1.0.0"
4+
description: Best practices for code editing, search, and file operations
5+
activation:
6+
keywords:
7+
- "code"
8+
- "edit"
9+
- "fix"
10+
- "implement"
11+
- "refactor"
12+
- "bug"
13+
- "function"
14+
- "class"
15+
- "file"
16+
- "module"
17+
- "test"
18+
- "compile"
19+
- "build"
20+
- "error"
21+
- "change"
22+
- "rename"
23+
- "delete"
24+
- "add"
25+
- "update"
26+
exclude_keywords:
27+
- "memory"
28+
- "routine"
29+
- "schedule"
30+
patterns:
31+
- "(?i)(add|remove|update|modify|create|delete|rename|move)\\s.*(file|function|class|method|variable|import)"
32+
- "(?i)(fix|debug|investigate|trace|find)\\s.*(bug|error|issue|crash|fail)"
33+
tags:
34+
- "development"
35+
- "coding"
36+
max_context_tokens: 1500
37+
---
38+
39+
# Coding Best Practices
40+
41+
## Tool Usage Discipline
42+
43+
- **Prefer `apply_patch` over `write_file`** for modifying existing files. It sends only the changed portion, preventing accidental full-file rewrites.
44+
- **Always `read_file` before editing.** Understand the context before changing code. Never edit a file you haven't read.
45+
- **Use `glob` for file discovery** instead of `shell` with `find` or `ls`. It's faster, safer, and returns structured results sorted by modification time.
46+
- **Use `grep` for content search** instead of `shell` with `grep` or `rg`. It provides structured output modes (content, file paths, counts) and pagination.
47+
- **Use `list_dir` for directory exploration** instead of `shell` with `ls`.
48+
- **Read before writing.** Never create or overwrite a file without reading it first (unless it's genuinely a new file).
49+
50+
## Code Change Discipline
51+
52+
- **Minimal changes.** Don't add features, refactor, or "improve" beyond what was asked. A bug fix doesn't need surrounding code cleaned up.
53+
- **No unnecessary comments or docstrings.** Only add comments where the logic isn't self-evident. Don't add type annotations or docstrings to code you didn't change.
54+
- **One thing at a time.** Make focused changes, verify with `read_file`, then move to the next change.
55+
- **Fix the pattern, not just the instance.** When you find a bug, use `grep` to search for all occurrences of the same pattern before committing a fix.
56+
57+
## Code Quality
58+
59+
- Don't introduce security vulnerabilities (command injection, XSS, SQL injection, path traversal).
60+
- Preserve existing code style and conventions. Match the indentation, naming, and patterns of surrounding code.
61+
- Test after changes when test infrastructure exists. Use `shell` to run the project's test command.
62+
- Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees.

skills/commit/SKILL.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
name: commit
3+
version: "1.0.0"
4+
description: Generate git commit messages from staged changes
5+
activation:
6+
keywords:
7+
- "commit"
8+
- "git commit"
9+
patterns:
10+
- "(?i)(create|make|write|generate)\\s.*commit"
11+
- "(?i)commit\\s.*(message|changes|staged)"
12+
tags:
13+
- "git"
14+
- "version-control"
15+
max_context_tokens: 1000
16+
---
17+
18+
# Git Commit Workflow
19+
20+
When the user asks to create a commit:
21+
22+
1. Run `shell` with `git status` to see what files are staged and unstaged.
23+
2. Run `shell` with `git diff --cached` to see the exact changes that will be committed.
24+
3. Run `shell` with `git log --oneline -5` to understand the repo's commit message style.
25+
4. Analyze the staged changes and draft a commit message:
26+
- Summarize the nature of the change (new feature, bug fix, refactor, etc.)
27+
- Keep it concise: 1-2 sentences focusing on **why**, not **what**
28+
- Match the repo's existing commit message style
29+
5. **Do not commit files that likely contain secrets** (`.env`, `credentials.json`, API keys). Warn the user if such files are staged.
30+
6. Show the proposed commit message to the user and **ask for confirmation** before running `git commit`.
31+
7. Stage any requested files with `git add <specific files>` (never use `git add -A` or `git add .`).
32+
33+
## Commit Message Format
34+
35+
If the repo doesn't have a clear style, use:
36+
```
37+
<type>: <concise description>
38+
39+
<optional body explaining why>
40+
```
41+
42+
Where type is: fix, feat, refactor, test, docs, chore.

0 commit comments

Comments
 (0)