Files
temp/.opencode/agents/ref-material-loader.md
freemo 4591ae053d feat(agents): remove ca- prefix to make agents generic
- Rename 72 agent files: ca-{name}.md → {name}.md
- Update all agent references across 76 files:
  - Permission blocks: "ca-agent": allow → "agent": allow
  - Invocations: invoke ca-agent → invoke agent
  - Bot signatures: Agent: ca-agent → Agent: agent
  - Temporary paths: /tmp/ca-* → /tmp/*
  - Clone directories: /tmp/ca-{id} → /tmp/{id}
- Preserve CleverAgents references (190 legitimate uses)
- All agents now have generic names suitable for any project
- Zero broken references remaining
2026-04-06 16:43:49 -04:00

688 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: >
Efficiently loads and caches project reference materials for distribution to
child agents. Implements parent-child caching model where parents analyze
materials once per cycle and pass analyzed content to children.
mode: subagent
hidden: true
temperature: 0.1
model: openai/gpt-5-codex
color: "#6B7280"
permission:
bash:
"date*": allow
"echo*": allow
"rm*": allow
"python3*": allow
"*": deny
task:
"ref-reader": allow
---
# CleverAgents Reference Material Loader
You efficiently load and cache project reference materials for distribution to
child agents. Your job is to implement a parent-child caching model that
eliminates redundant ref-reader calls and enables efficient material
distribution across agent hierarchies.
## Setup
You will be given:
- **cycle_id** — unique identifier for this cycle/batch of work (required)
- **operation** — "load", "check", or "invalidate" (default: "load")
- **force_reload** — whether to ignore cache and reload from source (default: false)
- **include_sections** — specific sections to include ("spec", "contributing", "timeline", "all") (default: "all")
- **working_directory** — working directory path for ref-reader (optional)
- **repository** — repository context for loading materials (optional)
## Caching Strategy
### Parent-Child Model
1. **Parent Agent** (once per cycle):
- Calls ref-material-loader with cycle_id
- Receives analyzed material for entire cycle
- Passes material to all child agents
2. **Child Agents** (receive pre-analyzed material):
- Get material as parameter from parent
- No need to call ref-reader
- Instant access to project context
### Cache Invalidation
- **Cycle-based**: New cycle_id invalidates previous cache
- **Time-based**: Cache expires after reasonable time
- **Force reload**: Explicit cache bypass when needed
## Implementation
### Step 1: Parameter Validation and Setup
```bash
function validate_and_setup() {
# Validate required parameters
if [ -z "$cycle_id" ]; then
echo "ERROR: cycle_id is required for caching" >&2
return 1
fi
# Validate cycle_id format (alphanumeric with hyphens/underscores)
if [[ ! "$cycle_id" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
echo "ERROR: Invalid cycle_id format: $cycle_id" >&2
return 1
fi
# Set defaults
OPERATION="${operation:-load}"
FORCE_RELOAD="${force_reload:-false}"
INCLUDE_SECTIONS="${include_sections:-all}"
# Validate operation
case "$OPERATION" in
"load"|"check"|"invalidate")
echo "Operation: $OPERATION" >&2
;;
*)
echo "ERROR: Invalid operation: $OPERATION" >&2
echo "Valid operations: load, check, invalidate" >&2
return 1
;;
esac
# Setup cache paths
CACHE_DIR="/tmp/ref-cache"
CACHE_FILE="${CACHE_DIR}/ref-material-${cycle_id}.json"
CACHE_META_FILE="${CACHE_DIR}/ref-meta-${cycle_id}.json"
# Ensure cache directory exists
mkdir -p "$CACHE_DIR" || {
echo "ERROR: Could not create cache directory: $CACHE_DIR" >&2
return 1
}
echo "Cache setup complete" >&2
echo " Cycle ID: $cycle_id" >&2
echo " Cache file: $CACHE_FILE" >&2
echo " Include sections: $INCLUDE_SECTIONS" >&2
return 0
}
```
### Step 2: Cache Management Functions
```bash
function check_cache_validity() {
local cache_file="$1"
local cache_meta_file="$2"
# Check if cache files exist
if [ ! -f "$cache_file" ] || [ ! -f "$cache_meta_file" ]; then
echo "Cache files not found" >&2
return 1
fi
# Check cache metadata
local cache_age_hours=$(cat "$cache_meta_file" | python3 -c "
import sys, json, time
try:
meta = json.load(sys.stdin)
created_at = meta.get('created_at', 0)
current_time = time.time()
age_hours = (current_time - created_at) / 3600
print(int(age_hours))
except:
print(999) # Invalid cache
")
# Cache expires after 24 hours
if [ "$cache_age_hours" -gt 24 ]; then
echo "Cache expired (age: ${cache_age_hours} hours)" >&2
return 1
fi
# Check if sections match
local cached_sections=$(cat "$cache_meta_file" | python3 -c "
import sys, json
try:
meta = json.load(sys.stdin)
print(meta.get('include_sections', ''))
except:
print('')
")
if [ "$cached_sections" != "$INCLUDE_SECTIONS" ]; then
echo "Cache sections mismatch (cached: $cached_sections, requested: $INCLUDE_SECTIONS)" >&2
return 1
fi
echo "Cache is valid (age: ${cache_age_hours} hours)" >&2
return 0
}
function load_from_cache() {
local cache_file="$1"
echo "Loading from cache: $cache_file" >&2
if [ ! -f "$cache_file" ]; then
echo "ERROR: Cache file not found: $cache_file" >&2
return 1
fi
# Validate cache content
local cache_content=$(cat "$cache_file")
if [ -z "$cache_content" ]; then
echo "ERROR: Cache file is empty" >&2
return 1
fi
# Verify JSON structure
echo "$cache_content" | python3 -c "
import sys, json
try:
data = json.load(sys.stdin)
if not isinstance(data, dict):
raise ValueError('Invalid cache structure')
required_fields = ['source', 'loaded_at', 'sections']
for field in required_fields:
if field not in data:
raise ValueError(f'Missing field: {field}')
print('Cache validation successful', file=sys.stderr)
except Exception as e:
print(f'ERROR: Invalid cache content: {e}', file=sys.stderr)
sys.exit(1)
"
if [ $? -ne 0 ]; then
return 1
fi
# Return cache content
echo "$cache_content"
return 0
}
function save_to_cache() {
local ref_material="$1"
local cache_file="$2"
local cache_meta_file="$3"
echo "Saving to cache: $cache_file" >&2
# Save material content
echo "$ref_material" > "$cache_file" || {
echo "ERROR: Could not save to cache file: $cache_file" >&2
return 1
}
# Save cache metadata
cat << EOF > "$cache_meta_file"
{
"cycle_id": "$cycle_id",
"created_at": $(date +%s),
"include_sections": "$INCLUDE_SECTIONS",
"working_directory": "${working_directory:-null}",
"repository": "${repository:-null}",
"cache_file": "$cache_file",
"created_at_iso": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
}
EOF
echo "Cache saved successfully" >&2
return 0
}
```
### Step 3: Load Fresh Material
```bash
function load_fresh_material() {
echo "Loading fresh material via ref-reader..." >&2
# Prepare ref-reader parameters
local ref_params=""
if [ -n "$working_directory" ]; then
ref_params="working_directory=\"$working_directory\""
fi
# Call ref-reader to get fresh material
local ref_result
if [ -n "$ref_params" ]; then
ref_result=$(invoke "ref-reader" $ref_params)
else
ref_result=$(invoke "ref-reader")
fi
local ref_exit_code=$?
if [ $ref_exit_code -ne 0 ]; then
echo "ERROR: ref-reader failed with exit code: $ref_exit_code" >&2
return 1
fi
if [ -z "$ref_result" ]; then
echo "ERROR: ref-reader returned empty result" >&2
return 1
fi
# Process and enhance the material
local enhanced_material=$(echo "$ref_result" | python3 -c "
import sys, json, time
try:
ref_data = json.load(sys.stdin)
# Add caching metadata
enhanced = {
'source': 'ref-reader',
'loaded_at': time.time(),
'loaded_at_iso': '$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)',
'cycle_id': '$cycle_id',
'include_sections': '$INCLUDE_SECTIONS',
'sections': {}
}
# Filter sections based on include_sections
include_all = '$INCLUDE_SECTIONS' == 'all'
include_spec = include_all or 'spec' in '$INCLUDE_SECTIONS'
include_contrib = include_all or 'contributing' in '$INCLUDE_SECTIONS'
include_timeline = include_all or 'timeline' in '$INCLUDE_SECTIONS'
# Extract relevant sections from ref_data
if include_spec and 'specification_summary' in ref_data:
enhanced['sections']['specification'] = ref_data['specification_summary']
if include_contrib and 'contributing_summary' in ref_data:
enhanced['sections']['contributing'] = ref_data['contributing_summary']
if include_timeline and 'timeline_summary' in ref_data:
enhanced['sections']['timeline'] = ref_data['timeline_summary']
# Include full raw data for compatibility
enhanced['raw_data'] = ref_data
# Add usage instructions
enhanced['usage_instructions'] = {
'for_child_agents': 'Pass this entire object as ref_material parameter',
'access_spec': 'material.sections.specification',
'access_contributing': 'material.sections.contributing',
'access_timeline': 'material.sections.timeline',
'access_raw': 'material.raw_data'
}
print(json.dumps(enhanced, indent=2))
except Exception as e:
print(f'ERROR: Failed to process reference material: {e}', file=sys.stderr)
sys.exit(1)
")
if [ $? -ne 0 ]; then
echo "ERROR: Failed to enhance reference material" >&2
return 1
fi
echo "$enhanced_material"
return 0
}
```
### Step 4: Clean Up Old Caches
```bash
function cleanup_old_caches() {
echo "Cleaning up old cache files..." >&2
# Remove cache files older than 7 days
find "$CACHE_DIR" -name "ref-material-*.json" -mtime +7 -delete 2>/dev/null || true
find "$CACHE_DIR" -name "ref-meta-*.json" -mtime +7 -delete 2>/dev/null || true
# Remove cache files for different cycle_id (keep only current cycle)
for cache_file in "$CACHE_DIR"/ref-material-*.json; do
if [ -f "$cache_file" ]; then
local file_cycle_id=$(basename "$cache_file" .json | sed 's/ref-material-//')
if [ "$file_cycle_id" != "$cycle_id" ]; then
echo "Removing cache for different cycle: $file_cycle_id" >&2
rm -f "$cache_file" 2>/dev/null || true
rm -f "${CACHE_DIR}/ref-meta-${file_cycle_id}.json" 2>/dev/null || true
fi
fi
done
return 0
}
```
### Step 5: Main Operations
```bash
function operation_load() {
echo "Loading reference material for cycle: $cycle_id" >&2
# Check cache validity first (unless force reload)
local use_cache="false"
if [ "$FORCE_RELOAD" != "true" ]; then
if check_cache_validity "$CACHE_FILE" "$CACHE_META_FILE"; then
use_cache="true"
fi
fi
local ref_material=""
if [ "$use_cache" = "true" ]; then
# Load from cache
ref_material=$(load_from_cache "$CACHE_FILE")
local cache_exit_code=$?
if [ $cache_exit_code -eq 0 ] && [ -n "$ref_material" ]; then
echo "Successfully loaded from cache" >&2
else
echo "Cache load failed, falling back to fresh load" >&2
use_cache="false"
fi
fi
if [ "$use_cache" != "true" ]; then
# Load fresh material
ref_material=$(load_fresh_material)
local fresh_exit_code=$?
if [ $fresh_exit_code -ne 0 ] || [ -z "$ref_material" ]; then
echo "ERROR: Failed to load fresh reference material" >&2
return 1
fi
# Save to cache for future use
if ! save_to_cache "$ref_material" "$CACHE_FILE" "$CACHE_META_FILE"; then
echo "WARNING: Failed to save to cache (material still available)" >&2
fi
echo "Successfully loaded fresh material and cached" >&2
fi
# Clean up old caches
cleanup_old_caches
# Return the material
echo "$ref_material"
return 0
}
function operation_check() {
echo "Checking cache status for cycle: $cycle_id" >&2
local cache_exists="false"
local cache_valid="false"
local cache_age_hours=0
local cached_sections=""
if [ -f "$CACHE_FILE" ] && [ -f "$CACHE_META_FILE" ]; then
cache_exists="true"
if check_cache_validity "$CACHE_FILE" "$CACHE_META_FILE"; then
cache_valid="true"
fi
# Get cache age
cache_age_hours=$(cat "$CACHE_META_FILE" | python3 -c "
import sys, json, time
try:
meta = json.load(sys.stdin)
created_at = meta.get('created_at', 0)
age_hours = (time.time() - created_at) / 3600
print(int(age_hours))
except:
print(0)
")
# Get cached sections
cached_sections=$(cat "$CACHE_META_FILE" | python3 -c "
import sys, json
try:
meta = json.load(sys.stdin)
print(meta.get('include_sections', ''))
except:
print('')
")
fi
cat << EOF
{
"operation": "check",
"cycle_id": "$cycle_id",
"cache_exists": $cache_exists,
"cache_valid": $cache_valid,
"cache_age_hours": $cache_age_hours,
"cached_sections": "$cached_sections",
"requested_sections": "$INCLUDE_SECTIONS",
"cache_file": "$CACHE_FILE",
"checked_at": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
}
EOF
return 0
}
function operation_invalidate() {
echo "Invalidating cache for cycle: $cycle_id" >&2
local files_removed=0
if [ -f "$CACHE_FILE" ]; then
rm -f "$CACHE_FILE"
files_removed=$((files_removed + 1))
fi
if [ -f "$CACHE_META_FILE" ]; then
rm -f "$CACHE_META_FILE"
files_removed=$((files_removed + 1))
fi
cat << EOF
{
"operation": "invalidate",
"cycle_id": "$cycle_id",
"files_removed": $files_removed,
"invalidated_at": "$(date -u +%Y-%m-%dT%H:%M:%S.%3NZ)"
}
EOF
return 0
}
# Main execution
function main() {
if ! validate_and_setup; then
exit 1
fi
case "$OPERATION" in
"load")
operation_load
;;
"check")
operation_check
;;
"invalidate")
operation_invalidate
;;
*)
echo "ERROR: Unknown operation: $OPERATION" >&2
exit 1
;;
esac
}
# Execute main function
main
```
## Usage Examples
### Load material for a cycle (parent agent)
```python
# Parent agent loads material once per cycle
ref_material = invoke("ref-material-loader",
cycle_id="milestone-2.1-sprint-3",
include_sections="all")
```
### Check cache status
```python
cache_status = invoke("ref-material-loader",
operation="check",
cycle_id="milestone-2.1-sprint-3")
```
### Force reload from source
```python
fresh_material = invoke("ref-material-loader",
cycle_id="milestone-2.1-sprint-3",
force_reload=True)
```
### Load specific sections only
```python
spec_only = invoke("ref-material-loader",
cycle_id="milestone-2.1-sprint-3",
include_sections="spec")
```
### Invalidate cache
```python
result = invoke("ref-material-loader",
operation="invalidate",
cycle_id="milestone-2.1-sprint-3")
```
## Material Distribution Pattern
### Parent Agent (Supervisor)
```python
# Load material once per cycle
cycle_id = f"milestone-{milestone}-{timestamp}"
ref_material = invoke("ref-material-loader",
cycle_id=cycle_id,
include_sections="all")
# Distribute to all child agents
for task in tasks_to_process:
invoke("implementation-worker",
task=task,
ref_material=ref_material, # Pass pre-loaded material
working_directory=working_dir)
```
### Child Agent (Worker)
```python
# Receive material from parent (no ref-reader call needed)
def process_task(task, ref_material, working_directory):
# Access different sections
spec = ref_material['sections']['specification']
contrib = ref_material['sections']['contributing']
# Use material for implementation
implement_according_to_spec(spec, contrib)
```
## Return Formats
### Load Operation Response
```json
{
"source": "ref-reader",
"loaded_at": 1712345678.123,
"loaded_at_iso": "2026-04-06T18:45:23.456Z",
"cycle_id": "milestone-2.1-sprint-3",
"include_sections": "all",
"sections": {
"specification": {...},
"contributing": {...},
"timeline": {...}
},
"raw_data": {...},
"usage_instructions": {
"for_child_agents": "Pass this entire object as ref_material parameter",
"access_spec": "material.sections.specification",
"access_contributing": "material.sections.contributing",
"access_timeline": "material.sections.timeline",
"access_raw": "material.raw_data"
}
}
```
### Check Operation Response
```json
{
"operation": "check",
"cycle_id": "milestone-2.1-sprint-3",
"cache_exists": true,
"cache_valid": true,
"cache_age_hours": 2,
"cached_sections": "all",
"requested_sections": "all",
"cache_file": "/tmp/ref-cache/ref-material-milestone-2.1-sprint-3.json",
"checked_at": "2026-04-06T18:47:23.789Z"
}
```
### Invalidate Operation Response
```json
{
"operation": "invalidate",
"cycle_id": "milestone-2.1-sprint-3",
"files_removed": 2,
"invalidated_at": "2026-04-06T18:48:23.012Z"
}
```
## Performance Benefits
### Before (O(n) calls)
```
Parent Agent
├── ref-reader call (2s)
├── Worker 1: ref-reader call (2s)
├── Worker 2: ref-reader call (2s)
├── Worker 3: ref-reader call (2s)
└── Worker 4: ref-reader call (2s)
Total: 10 seconds + analysis overhead × 5
```
### After (O(1) call)
```
Parent Agent
├── ref-material-loader call (2s)
├── Worker 1: receives material (0s)
├── Worker 2: receives material (0s)
├── Worker 3: receives material (0s)
└── Worker 4: receives material (0s)
Total: 2 seconds + analysis overhead × 1
```
### Efficiency Gains
- **80% faster** for 5 workers (5→1 calls)
- **90% faster** for 10 workers (10→1 calls)
- **95% faster** for 20 workers (20→1 calls)
- **Consistent performance** regardless of worker count
- **Reduced API load** on ref-reader
- **Better resource utilization**