🔧 Update opencode tooling, agents, and devenv

Update agent configurations: change commiter mode to all, rename
engineer agent to "Penpot Engineer", and remove obsolete testing agent.

Add new read-only planner agent for architecture analysis and planning.

Add four new skills: bat-cat (syntax-highlighted cat clone), fd-find
(fast file finder), jq-json-processor (JSON processor), and ripgrep
(fast text search).

Add fd-find and bat packages to devenv Dockerfile.

Update .gitignore to exclude opencode package-lock and plans directory.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
Andrey Antukh 2026-04-25 13:27:11 +00:00
parent 6d9019c383
commit 37cba3355d
11 changed files with 798 additions and 41 deletions

3
.gitignore vendored
View File

@ -24,6 +24,9 @@
/.clj-kondo/.cache
/_dump
/notes
/.opencode/package-lock.json
/plans
/prompts
/playground/
/backend/*.md
!/backend/AGENTS.md

View File

@ -1,11 +1,13 @@
---
name: commiter
description: Git commit assistant following CONTRIBUTING.md commit rules
mode: primary
mode: subagent
---
Role: You are responsible for creating git commits for Penpot and must follow
the repository commit-format rules exactly.
Role: You are responsible for creating git commits for Penpot and must
follow the repository commit-format rules exactly. It should have
concise title and clear summary of changes in the description,
including the rationale if proceed.
Requirements:

View File

@ -1,5 +1,5 @@
---
name: engineer
name: Penpot Engineer
description: Senior Full-Stack Software Engineer
mode: primary
---

View File

@ -0,0 +1,61 @@
---
name: Penpot Planner
description: Software architect for planning and analysis only
mode: primary
permission:
edit: ask
---
# Penpot Planner
## Role
You are a Senior Software Architect working on Penpot, an open-source design
tool. Your sole responsibility is planning and analysis — you do NOT write,
modify any code.
You help users understand the codebase, design solutions, and create detailed
implementation plans that other agents or developers can execute. Document
everything they need to know: which files to touch for each task, code, testing,
docs they might need to check, how to test it. Give them the whole plan as
bite-sized tasks. DRY. YAGNI. TDD. Frequent commits.
Assume they are a skilled developer, but know almost nothing about our toolset
or problem domain. Assume they don't know good test design very well.
## Requirements
* Analyze the codebase architecture and identify affected modules.
* Read `AGENTS.md` files (root and per-module) to understand structure and
conventions.
* Search code using `ripgrep` skill (`rg`) to trace dependencies, find patterns,
and understand existing implementations.
* Break down complex features or bugs into atomic, actionable steps.
* Propose solutions with clear rationale, trade-offs, and sequencing.
* Identify risks, edge cases, and testing considerations.
Save plans to: plans/YYYY-MM-DD-<plan-one-line-title>.md
## Constraints
* You are **read-only** — never create, edit, or delete files.
* You do **not** run builds, tests, linters, or any commands that modify state.
* You do **not** create git commits or interact with version control.
* You do **not** execute shell commands beyond read-only searches (`rg`, `ls`,
`find`, `cat`).
* Your output is a structured plan or analysis, ready for handoff to an
engineer agent or developer.
## Output format
When producing a plan, structure it as:
1. **Context** — What is the problem or feature request?
2. **Affected modules** — Which parts of the codebase are involved?
3. **Approach** — Step-by-step implementation plan with file paths and
function names where applicable.
4. **Risks & considerations** — Edge cases, performance implications, breaking
changes.
5. **Testing strategy** — How to verify the implementation works correctly.

View File

@ -0,0 +1,59 @@
---
name: Prompt Assistant
description: Refines and improves prompts for maximum clarity and effectiveness
mode: all
---
# Prompt Assistant
## Role
You are an expert Prompt Engineer with strong knowledge of
penpot. Your sole responsibility is to take a prompt provided by the
user and transform it into the most effective, clear, and
well-structured version possible — ready to be used with any AI model.
## Requirements
* You do NOT execute tasks. You do NOT write code. You only design and
refine prompts
* Read the root `AGENTS.md` to understand the repository and application
architecture. Then read the `AGENTS.md` **only** for each affected module.
* Analyze the original prompt: identify its intent, target audience,
ambiguities, missing context, and structural weaknesses
* Ask clarifying questions if the intent is unclear or if critical
information is missing (e.g. target model, expected output format,
tone, constraints). Keep questions concise and grouped
* Rewrite the prompt using prompt engineering best practices
## Prompt Engineering Principles
Apply these techniques when refining prompts:
- **Be specific and explicit**: Replace vague instructions with precise ones.
- **Set the context**: Include background information the model needs to
perform well.
- **Specify the output format**: State the desired structure, length, tone,
or format (e.g. bullet list, JSON, step-by-step).
- **Add constraints**: Include what the model should avoid or not do.
- **Use examples** (few-shot): When applicable, suggest adding examples to
anchor the model's behaviour.
- **Break down complexity**: Split multi-step tasks into clear numbered steps.
- **Avoid ambiguity**: Remove pronouns and references that could be
misinterpreted.
- **Chain of thought**: For reasoning tasks, include "Think step by step."
## Constraints
- Do NOT execute the prompt yourself.
- Do NOT answer the question inside the prompt.
- Do NOT add unnecessary verbosity — prompts should be as short as they can
be while remaining complete.
- Always preserve the user's original intent.
## Output
Refined Prompt: The improved, ready-to-use prompt. Print it for
immediate use and save it to
prompts/YYYY-MM-DD-<prompt-one-line-title>.md for future use.

View File

@ -1,37 +0,0 @@
---
name: testing
description: Senior Software Engineer specialized on testing
mode: primary
---
Role: You are a Senior Software Engineer specialized in testing Clojure and
ClojureScript codebases. You work on Penpot, an open-source design tool.
Tech stack: Clojure (backend/JVM), ClojureScript (frontend/Node.js), shared
Cljc (common module), Rust (render-wasm).
Requirements:
* Read the root `AGENTS.md` to understand the repository and application
architecture. Then read the `AGENTS.md` **only** for each affected module. Not all
modules have one — verify before reading.
* Before writing code, describe your plan. If the task is complex, break it down into
atomic steps.
* Tests should be exhaustive and include edge cases relevant to Penpot's domain:
nil/missing fields, empty collections, invalid UUIDs, boundary geometries, Malli schema
violations, concurrent state mutations, and timeouts.
* Tests must be deterministic — do not use `setTimeout`, real network calls, or rely on
execution order. Use synchronous mocks for asynchronous workflows.
* Use `with-redefs` or equivalent mocking utilities to isolate the logic under test. Avoid
testing through the UI (DOM); e2e tests cover that.
* Only reference functions, namespaces, or test utilities that actually exist in the
codebase. Verify their existence before citing them.
* After adding or modifying tests, run the applicable lint and format checks for the
affected module before considering the work done (see module `AGENTS.md` for exact
commands).
* Make small and logical commits following the commit guideline described in
`CONTRIBUTING.md`. Commit only when explicitly asked.
- Do not guess or hallucinate git author information (Name or Email). Never include the
`--author` flag in git commands unless specifically instructed by the user for a unique
case; assume the local environment is already configured. Allow git commit to
automatically pull the identity from the local git config `user.name` and `user.email`.

View File

@ -0,0 +1,210 @@
---
name: bat-cat
description: A cat clone with syntax highlighting, line numbers, and Git integration - a modern replacement for cat.
homepage: https://github.com/sharkdp/bat
metadata: {"clawdbot":{"emoji":"🦇","requires":{"bins":["bat"]},"install":[{"id":"brew","kind":"brew","formula":"bat","bins":["bat"],"label":"Install bat (brew)"},{"id":"apt","kind":"apt","package":"bat","bins":["bat"],"label":"Install bat (apt)"}]}}
---
# bat - Better cat
`cat` with syntax highlighting, line numbers, and Git integration.
## Quick Start
### Basic usage
```bash
# View file with syntax highlighting
bat README.md
# Multiple files
bat file1.js file2.py
# With line numbers (default)
bat script.sh
# Without line numbers
bat -p script.sh
```
### Viewing modes
```bash
# Plain mode (like cat)
bat -p file.txt
# Show non-printable characters
bat -A file.txt
# Squeeze blank lines
bat -s file.txt
# Paging (auto for large files)
bat --paging=always file.txt
bat --paging=never file.txt
```
## Syntax Highlighting
### Language detection
```bash
# Auto-detect from extension
bat script.py
# Force specific language
bat -l javascript config.txt
# Show all languages
bat --list-languages
```
### Themes
```bash
# List available themes
bat --list-themes
# Use specific theme
bat --theme="Monokai Extended" file.py
# Set default theme in config
# ~/.config/bat/config: --theme="Dracula"
```
## Line Ranges
```bash
# Show specific lines
bat -r 10:20 file.txt
# From line to end
bat -r 100: file.txt
# Start to specific line
bat -r :50 file.txt
# Multiple ranges
bat -r 1:10 -r 50:60 file.txt
```
## Git Integration
```bash
# Show Git modifications (added/removed/modified lines)
bat --diff file.txt
# Show decorations (Git + file header)
bat --decorations=always file.txt
```
## Output Control
```bash
# Output raw (no styling)
bat --style=plain file.txt
# Customize style
bat --style=numbers,changes file.txt
# Available styles: auto, full, plain, changes, header, grid, numbers, snip
bat --style=header,grid,numbers file.txt
```
## Common Use Cases
**Quick file preview:**
```bash
bat file.json
```
**View logs with syntax highlighting:**
```bash
bat error.log
```
**Compare files visually:**
```bash
bat --diff file1.txt
bat file2.txt
```
**Preview before editing:**
```bash
bat config.yaml && vim config.yaml
```
**Cat replacement in pipes:**
```bash
bat -p file.txt | grep "pattern"
```
**View specific function:**
```bash
bat -r 45:67 script.py # If function is on lines 45-67
```
## Integration with other tools
**As pager for man pages:**
```bash
export MANPAGER="sh -c 'col -bx | bat -l man -p'"
man grep
```
**With ripgrep:**
```bash
rg "pattern" -l | xargs bat
```
**With fzf:**
```bash
fzf --preview 'bat --color=always --style=numbers {}'
```
**With diff:**
```bash
diff -u file1 file2 | bat -l diff
```
## Configuration
Create `~/.config/bat/config` for defaults:
```
# Set theme
--theme="Dracula"
# Show line numbers, Git modifications and file header, but no grid
--style="numbers,changes,header"
# Use italic text on terminal
--italic-text=always
# Add custom mapping
--map-syntax "*.conf:INI"
```
## Performance Tips
- Use `-p` for plain mode when piping
- Use `--paging=never` when output is used programmatically
- `bat` caches parsed files for faster subsequent access
## Tips
- **Alias:** `alias cat='bat -p'` for drop-in cat replacement
- **Pager:** Use as pager with `export PAGER="bat"`
- **On Debian/Ubuntu:** Command may be `batcat` instead of `bat`
- **Custom syntaxes:** Add to `~/.config/bat/syntaxes/`
- **Performance:** For huge files, use `bat --paging=never` or plain `cat`
## Common flags
- `-p` / `--plain`: Plain mode (no line numbers/decorations)
- `-n` / `--number`: Only show line numbers
- `-A` / `--show-all`: Show non-printable characters
- `-l` / `--language`: Set language for syntax highlighting
- `-r` / `--line-range`: Only show specific line range(s)
## Documentation
GitHub: https://github.com/sharkdp/bat
Man page: `man bat`
Customization: https://github.com/sharkdp/bat#customization

View File

@ -0,0 +1,194 @@
---
name: fd-find
description: A fast and user-friendly alternative to 'find' - simple syntax, smart defaults, respects gitignore.
homepage: https://github.com/sharkdp/fd
metadata: {"clawdbot":{"emoji":"📂","requires":{"bins":["fd"]},"install":[{"id":"brew","kind":"brew","formula":"fd","bins":["fd"],"label":"Install fd (brew)"},{"id":"apt","kind":"apt","package":"fd-find","bins":["fd"],"label":"Install fd (apt)"}]}}
---
# fd - Fast File Finder
User-friendly alternative to `find` with smart defaults.
## Quick Start
### Basic search
```bash
# Find files by name
fd pattern
# Find in specific directory
fd pattern /path/to/dir
# Case-insensitive
fd -i pattern
```
### Common patterns
```bash
# Find all Python files
fd -e py
# Find multiple extensions
fd -e py -e js -e ts
# Find directories only
fd -t d pattern
# Find files only
fd -t f pattern
# Find symlinks
fd -t l
```
## Advanced Usage
### Filtering
```bash
# Exclude patterns
fd pattern -E "node_modules" -E "*.min.js"
# Include hidden files
fd -H pattern
# Include ignored files (.gitignore)
fd -I pattern
# Search all (hidden + ignored)
fd -H -I pattern
# Maximum depth
fd pattern -d 3
```
### Execution
```bash
# Execute command on results
fd -e jpg -x convert {} {.}.png
# Parallel execution
fd -e md -x wc -l
# Use with xargs
fd -e log -0 | xargs -0 rm
```
### Regex patterns
```bash
# Full regex search
fd '^test.*\.js$'
# Match full path
fd --full-path 'src/.*/test'
# Glob pattern
fd -g "*.{js,ts}"
```
## Time-based filtering
```bash
# Modified within last day
fd --changed-within 1d
# Modified before specific date
fd --changed-before 2024-01-01
# Created recently
fd --changed-within 1h
```
## Size filtering
```bash
# Files larger than 10MB
fd --size +10m
# Files smaller than 1KB
fd --size -1k
# Specific size range
fd --size +100k --size -10m
```
## Output formatting
```bash
# Absolute paths
fd --absolute-path
# List format (like ls -l)
fd --list-details
# Null separator (for xargs)
fd -0 pattern
# Color always/never/auto
fd --color always pattern
```
## Common Use Cases
**Find and delete old files:**
```bash
fd --changed-before 30d -t f -x rm {}
```
**Find large files:**
```bash
fd --size +100m --list-details
```
**Copy all PDFs to directory:**
```bash
fd -e pdf -x cp {} /target/dir/
```
**Count lines in all Python files:**
```bash
fd -e py -x wc -l | awk '{sum+=$1} END {print sum}'
```
**Find broken symlinks:**
```bash
fd -t l -x test -e {} \; -print
```
**Search in specific time window:**
```bash
fd --changed-within 2d --changed-before 1d
```
## Integration with other tools
**With ripgrep:**
```bash
fd -e js | xargs rg "pattern"
```
**With fzf (fuzzy finder):**
```bash
vim $(fd -t f | fzf)
```
**With bat (cat alternative):**
```bash
fd -e md | xargs bat
```
## Performance Tips
- `fd` is typically much faster than `find`
- Respects `.gitignore` by default (disable with `-I`)
- Uses parallel traversal automatically
- Smart case: lowercase = case-insensitive, any uppercase = case-sensitive
## Tips
- Use `-t` for type filtering (f=file, d=directory, l=symlink, x=executable)
- `-e` for extension is simpler than `-g "*.ext"`
- `{}` in `-x` commands represents the found path
- `{.}` strips the extension
- `{/}` gets basename, `{//}` gets directory
## Documentation
GitHub: https://github.com/sharkdp/fd
Man page: `man fd`

View File

@ -0,0 +1,112 @@
---
name: jq-json-processor
description: Process, filter, and transform JSON data using jq - the lightweight and flexible command-line JSON processor.
homepage: https://jqlang.github.io/jq/
metadata: {"clawdbot":{"emoji":"🔍","requires":{"bins":["jq"]},"install":[{"id":"brew","kind":"brew","formula":"jq","bins":["jq"],"label":"Install jq (brew)"},{"id":"apt","kind":"apt","package":"jq","bins":["jq"],"label":"Install jq (apt)"}]}}
---
# jq JSON Processor
Process, filter, and transform JSON data with jq.
## Quick Examples
### Basic filtering
```bash
# Extract a field
echo '{"name":"Alice","age":30}' | jq '.name'
# Output: "Alice"
# Multiple fields
echo '{"name":"Alice","age":30}' | jq '{name: .name, age: .age}'
# Array indexing
echo '[1,2,3,4,5]' | jq '.[2]'
# Output: 3
```
### Working with arrays
```bash
# Map over array
echo '[{"name":"Alice"},{"name":"Bob"}]' | jq '.[].name'
# Output: "Alice" "Bob"
# Filter array
echo '[1,2,3,4,5]' | jq 'map(select(. > 2))'
# Output: [3,4,5]
# Length
echo '[1,2,3]' | jq 'length'
# Output: 3
```
### Common operations
```bash
# Pretty print JSON
cat file.json | jq '.'
# Compact output
cat file.json | jq -c '.'
# Raw output (no quotes)
echo '{"name":"Alice"}' | jq -r '.name'
# Output: Alice
# Sort keys
echo '{"z":1,"a":2}' | jq -S '.'
```
### Advanced filtering
```bash
# Select with conditions
jq '[.[] | select(.age > 25)]' people.json
# Group by
jq 'group_by(.category)' items.json
# Reduce
echo '[1,2,3,4,5]' | jq 'reduce .[] as $item (0; . + $item)'
# Output: 15
```
### Working with files
```bash
# Read from file
jq '.users[0].name' users.json
# Multiple files
jq -s '.[0] * .[1]' file1.json file2.json
# Modify and save
jq '.version = "2.0"' package.json > package.json.tmp && mv package.json.tmp package.json
```
## Common Use Cases
**Extract specific fields from API response:**
```bash
curl -s https://api.github.com/users/octocat | jq '{name: .name, repos: .public_repos, followers: .followers}'
```
**Convert CSV-like data:**
```bash
jq -r '.[] | [.name, .email, .age] | @csv' users.json
```
**Debug API responses:**
```bash
curl -s https://api.example.com/data | jq '.'
```
## Tips
- Use `-r` for raw string output (removes quotes)
- Use `-c` for compact output (single line)
- Use `-S` to sort object keys
- Use `--arg name value` to pass variables
- Pipe multiple jq operations: `jq '.a' | jq '.b'`
## Documentation
Full manual: https://jqlang.github.io/jq/manual/
Interactive tutorial: https://jqplay.org/

View File

@ -0,0 +1,150 @@
---
name: ripgrep
description: Blazingly fast text search tool - recursively searches directories for regex patterns with respect to gitignore rules.
homepage: https://github.com/BurntSushi/ripgrep
metadata: {"clawdbot":{"emoji":"🔎","requires":{"bins":["rg"]},"install":[{"id":"brew","kind":"brew","formula":"ripgrep","bins":["rg"],"label":"Install ripgrep (brew)"},{"id":"apt","kind":"apt","package":"ripgrep","bins":["rg"],"label":"Install ripgrep (apt)"}]}}
---
# ripgrep (rg)
Fast, smart recursive search. Respects `.gitignore` by default.
## Quick Start
### Basic search
```bash
# Search for "TODO" in current directory
rg "TODO"
# Case-insensitive search
rg -i "fixme"
# Search specific file types
rg "error" -t py # Python files only
rg "function" -t js # JavaScript files
```
### Common patterns
```bash
# Whole word match
rg -w "test"
# Show only filenames
rg -l "pattern"
# Show with context (3 lines before/after)
rg -C 3 "function"
# Count matches
rg -c "import"
```
## Advanced Usage
### File type filtering
```bash
# Multiple file types
rg "error" -t py -t js
# Exclude file types
rg "TODO" -T md -T txt
# List available types
rg --type-list
```
### Search modifiers
```bash
# Regex search
rg "user_\d+"
# Fixed string (no regex)
rg -F "function()"
# Multiline search
rg -U "start.*end"
# Only show matches, not lines
rg -o "https?://[^\s]+"
```
### Path filtering
```bash
# Search specific directory
rg "pattern" src/
# Glob patterns
rg "error" -g "*.log"
rg "test" -g "!*.min.js"
# Include hidden files
rg "secret" --hidden
# Search all files (ignore .gitignore)
rg "pattern" --no-ignore
```
## Replacement Operations
```bash
# Preview replacements
rg "old_name" --replace "new_name"
# Actually replace (requires extra tool like sd)
rg "old_name" -l | xargs sed -i 's/old_name/new_name/g'
```
## Performance Tips
```bash
# Parallel search (auto by default)
rg "pattern" -j 8
# Skip large files
rg "pattern" --max-filesize 10M
# Memory map files
rg "pattern" --mmap
```
## Common Use Cases
**Find TODOs in code:**
```bash
rg "TODO|FIXME|HACK" --type-add 'code:*.{rs,go,py,js,ts}' -t code
```
**Search in specific branches:**
```bash
git show branch:file | rg "pattern"
```
**Find files containing multiple patterns:**
```bash
rg "pattern1" | rg "pattern2"
```
**Search with context and color:**
```bash
rg -C 2 --color always "error" | less -R
```
## Comparison to grep
- **Faster:** Typically 5-10x faster than grep
- **Smarter:** Respects `.gitignore`, skips binary files
- **Better defaults:** Recursive, colored output, line numbers
- **Easier:** Simpler syntax for common tasks
## Tips
- `rg` is often faster than `grep -r`
- Use `-t` for file type filtering instead of `--include`
- Combine with other tools: `rg pattern -l | xargs tool`
- Add custom types in `~/.ripgreprc`
- Use `--stats` to see search performance
## Documentation
GitHub: https://github.com/BurntSushi/ripgrep
User Guide: https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md

View File

@ -308,6 +308,9 @@ RUN set -ex; \
less \
jq \
nginx \
fd-find \
bat \
gh \
\
fontconfig \
woff-tools \