8 ms·
I've had good success with something along these lines but perhaps a bit more raw: - claude takes a -p option - i have a bunch of tiny scripts, each sc
by CraigJPerry 6mo ago
I've had good success with something along these lines but perhaps a bit more raw:
- claude takes a -p option
- i have a bunch of tiny scripts, each script is an agent but it only does one tiny task
- scripts can be composed in a unix pipeline
For example:
$ git diff --staged | ai-commit-msg | git commit -F -
Where ai-commit-msg is a tiny agent:
#!/usr/bin/env bash
# ai-commit-msg: stdin=git diff, stdout=conventional commit message
# Usage: git diff --staged | ai-commit-msg
set -euo pipefail
source "${AGENTS_DIR:-$HOME/.agents}/lib/agent-lib.sh"
SYSTEM=$(load_skills \
core/unix-output.md \
core/be-concise.md \
domain/git.md \
output/plain-text.md)
SYSTEM+=$'\n\nTask: Given a git diff on stdin, output a single conventional commit message. One line only.'
run_agent "$SYSTEM"
And you can see to keep the agents themselves tiny, they rely on a little lib to load the various skills and optionally apply some guard / post-exec validator. Those validators are usually simple grep or whatever to make sure there were no writes outside a given dir but sometimes they can be to enforce output correctness (always jq in my examples so far...). In theory the guard could be another claude -p call if i needed a semantic instruction.
- avoutic 6mo agoI was looking at something similar. What does your agent-lib.sh look like?
- CraigJPerry 6mo agoSomething like this: https://github.com/craigjperry2/tiny-agents/blob/main/lib/agent-lib.sh https://github.com/craigjperry2/tiny-agents/blob/main/lib/ag...
- avoutic 6mo agoThat is probably a private repo? 404, so not something I can access
- avoutic 6mo agoBump. Would love to see your tiny agents project. But understand that it might contain something sensitive and will therefore stay private.
- CraigJPerry 6mo agoAhh apologies #!/usr/bin/env bash # agent-lib.sh — shared plumbing for all claude -p agents AGENTS_DIR="${AGENTS_DIR:-$HOME/Code/github.com/craigjperry2/tiny-agents}" SKILLS_DIR="$AGENTS_DIR/skills" CLAUDE_OPTS="${CLAUDE_OPTS:-}" # Build a system prompt by concatenating skill files # Usage: load_skills core/unix-output.md domain/git.md output/plain-text.md load_skills() { local combined="" for skill in "$@"; do local path="$SKILLS_DIR/$skill" if [[ -f "$path" ]]; then combined+=$'\n\n'"$(cat "$path")" else echo "[agent-lib] WARNING: skill not found: $skill" >&2 fi done echo "$combined" } # Core invocation: reads stdin, prepends system prompt, calls claude -p # Usage: run_agent <system_prompt> [extra claude opts...] run_agent() { local system_prompt="$1" shift local stdin_content stdin_content=$(cat) # buffer stdin if [[ -z "$stdin_content" ]]; then echo "[agent] ERROR: no input on stdin" >&2 exit 1 fi # Combine system prompt with stdin as user message printf '%s' "$stdin_content" \ | claude -p \ --system-prompt "$system_prompt" \ --output-format text \ $CLAUDE_OPTS \ "$@" } # Run agent then pipe through a guard # Usage: run_agent_guarded <guard_name> <system_prompt> run_agent_guarded() { local guard="$1" shift local system_prompt="$1" shift local output output=$(run_agent "$system_prompt" "$@") local agent_exit=$? if [[ $agent_exit -ne 0 ]]; then echo "$output" exit $agent_exit fi # Pass through guard echo "$output" | "$AGENTS_DIR/guards/$guard" exit $? } # For structured output: run agent then validate with jq run_json_agent() { local system_prompt="$1" shift run_agent "$system_prompt" --output-format text "$@" | guard-json-valid }
- lionkor 6mo agoDo you have examples of these commit messages? I have yet to see an AI write a good commit message. At least when compared to good commit messages -- if it just does better than "wip" or "fix stuff" that's not a high bar.
- CraigJPerry 6mo agoMy skills/domain/git.md looks like this: Context: You are working with Git repositories. - Commit messages follow Conventional Commits: type(scope): description - Types: feat, fix, docs, refactor, test, chore, ci, perf - Subject line max 72 chars, imperative mood, no trailing period - Reference issue numbers when relevant So it produces messages like: $ git diff HEAD~1 | bin/ai-commit-msg fix(guards): pass input to claude and tighten verdict handling
- lionkor 6mo agoMy issue with this kind of message is that it doesn't convey intent well enough. This kind of commit message will always be like "remove check from handleUser" instead of "fix authorization in xyz case". But I assume these are different schools of commits -- I prefer commits which convey WHY, not WHAT, much like source code comments.