| name | skill-researcher |
| description | Conduct general research using web search, documentation, and codebase exploration. Invoke for general research tasks. |
| allowed-tools | Task, Bash, Edit, Read, Write |
Researcher Skill
Thin wrapper that delegates general research to general-research-agent subagent.
IMPORTANT: This skill implements the skill-internal postflight pattern. After the subagent returns,
this skill handles all postflight operations (status update, artifact linking, git commit) before returning.
This eliminates the "continue" prompt issue between skill return and orchestrator.
Context References
Reference (do not load eagerly):
- Path:
.opencode/context/formats/return-metadata-file.md - Metadata file schema
- Path:
.opencode/context/patterns/postflight-control.md - Marker file protocol
- Path:
.opencode/context/patterns/file-metadata-exchange.md - File I/O helpers
- Path:
.opencode/context/patterns/jq-escaping-workarounds.md - jq escaping patterns (Issue #1132)
Note: This skill is a thin wrapper with internal postflight. Context is loaded by the delegated agent.
Trigger Conditions
This skill activates when:
- Task type is "general", "meta", "markdown", "latex", or "typst"
- Research is needed for implementation planning
- Documentation or external resources need to be gathered
Execution Flow
Stage 1: Input Validation
Validate required inputs:
task_number - Must be provided and exist in state.json
focus_prompt - Optional focus for research direction
task_data=$(jq -r --argjson num "$task_number" \
'.active_projects[] | select(.project_number == $num)' \
specs/state.json)
if [ -z "$task_data" ]; then
return error "Task $task_number not found"
fi
task_type=$(echo "$task_data" | jq -r '.task_type // "general"')
status=$(echo "$task_data" | jq -r '.status')
project_name=$(echo "$task_data" | jq -r '.project_name')
description=$(echo "$task_data" | jq -r '.description // ""')
Stage 2: Preflight Status Update
Update task status to "researching" BEFORE invoking subagent.
.opencode/scripts/update-task-status.sh preflight "$task_number" research "$session_id"
This atomically updates state.json (status, timestamps, session_id), TODO.md task entry, and TODO.md Task Order section. If the script exits non-zero, abort and keep current status.
Stage 3: Create Postflight Marker
Create the marker file to prevent premature termination:
padded_num=$(printf "%03d" "$task_number")
mkdir -p "specs/${padded_num}_${project_name}"
cat > "specs/${padded_num}_${project_name}/.postflight-pending" << EOF
{
"session_id": "${session_id}",
"skill": "skill-researcher",
"task_number": ${task_number},
"operation": "research",
"reason": "Postflight pending: status update, artifact linking, git commit",
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"stop_hook_active": false
}
EOF
Stage 3a: Read Artifact Number
Read next_artifact_number from state.json (or fall back to directory scanning for legacy tasks):
artifact_number=$(jq -r --argjson num "$task_number" \
'.active_projects[] | select(.project_number == $num) | .next_artifact_number // 1' \
specs/state.json)
if [ "$artifact_number" = "null" ] || [ -z "$artifact_number" ]; then
padded_num=$(printf "%03d" "$task_number")
count=$(ls "specs/${padded_num}_${project_name}/reports/"*[0-9][0-9]*.md 2>/dev/null | wc -l)
artifact_number=$((count + 1))
fi
artifact_padded=$(printf "%02d" "$artifact_number")
Stage 4a: Memory Retrieval (Auto)
Retrieve relevant memories from the memory system to inject into the delegation context.
Skip if: clean_flag is true in the delegation context (from --clean command flag).
if [ "$clean_flag" != "true" ]; then
memory_context=$(bash .opencode/scripts/memory-retrieve.sh "$description" "$task_type" "$focus_prompt" 2>/dev/null) || memory_context=""
fi
If memory_context is non-empty, it will be injected into the Stage 5 prompt alongside the format specification from Stage 4b. If empty, no memory block is injected.
Stage 4c: Collect Prior Implementation Context
If task status is "implementing" or "partial", collect existing implementation artifacts from the task directory and prepare them for injection into the subagent prompt.
prior_implementation_context=""
if [ "$status" = "implementing" ] || [ "$status" = "partial" ]; then
task_dir="specs/${padded_num}_${project_name}"
if [ -d "$task_dir/summaries" ]; then
for f in $(ls -1 "$task_dir/summaries/"*.md 2>/dev/null | sort -V); do
prior_implementation_context+="\n\n## Summary: $(basename "$f")\n\n$(cat "$f")"
done
fi
if [ -d "$task_dir/handoffs" ]; then
for f in $(ls -1 "$task_dir/handoffs/"*.md 2>/dev/null | sort -V | tail -3); do
prior_implementation_context+="\n\n## Handoff: $(basename "$f")\n\n$(cat "$f")"
done
fi
if [ -d "$task_dir/progress" ]; then
for f in $(ls -1 "$task_dir/progress/"*.json 2>/dev/null | sort -V | tail -1); do
prior_implementation_context+="\n\n## Progress: $(basename "$f")\n\n$(cat "$f")"
done
fi
if [ -d "$task_dir/plans" ]; then
latest_plan=$(ls -1 "$task_dir/plans/"*.md 2>/dev/null | sort -V | tail -1)
if [ -n "$latest_plan" ]; then
prior_implementation_context+="\n\n## Plan: $(basename "$latest_plan")\n\n$(cat "$latest_plan")"
fi
fi
if [ -n "$prior_implementation_context" ]; then
line_count=$(echo -e "$prior_implementation_context" | wc -l)
if [ "$line_count" -gt 500 ]; then
prior_implementation_context=$(echo -e "$prior_implementation_context" | head -n 500)
prior_implementation_context+="\n\n[NOTE: Prior implementation context truncated from $line_count lines to 500 lines budget]"
fi
fi
fi
If no artifacts are found or status does not match, prior_implementation_context remains an empty string.
Stage 4: Prepare Delegation Context
Prepare delegation context for the subagent:
{
"session_id": "sess_{timestamp}_{random}",
"delegation_depth": 1,
"delegation_path": ["orchestrator", "research", "skill-researcher"],
"timeout": 3600,
"task_context": {
"task_number": N,
"task_name": "{project_name}",
"description": "{description}",
"task_type": "{task_type}"
},
"artifact_number": "{artifact_number from Stage 3a}",
"focus_prompt": "{optional focus}",
"effort_flag": "{effort_flag from command, null if not set}",
"model_flag": "{model_flag from command, null if not set}",
"roadmap_path": "specs/ROADMAP.md",
"metadata_file_path": "specs/{NNN}_{SLUG}/.return-meta.json",
"prior_implementation_context": "{prior_implementation_context from Stage 4c, or empty string}"
}
Note: The artifact_number field tells the agent which sequence number to use for artifact naming (e.g., 01, 02).
Model/Effort Flags: If model_flag is set (haiku, sonnet, opus), pass it as the model parameter on the Task tool to override the agent's frontmatter default. If effort_flag is set (fast, hard), include it as prompt context for reasoning depth guidance.
Stage 4b: Read and Inject Format Specification
Read the report format file and prepare it for injection into the subagent prompt. This ensures the subagent always has the full format specification in its context, regardless of whether it reads the file itself.
format_content=$(cat .opencode/context/formats/report-format.md)
The format content will be included as a delimited section in the Stage 5 prompt (see below).
Stage 5: Invoke Subagent
CRITICAL: You MUST use the Task tool to spawn the subagent.
Required Tool Invocation:
Tool: Task (NOT Skill)
Parameters:
- subagent_type: "general-research-agent"
- prompt: [Include task_context, delegation_context, focus_prompt, metadata_file_path,
AND the format specification from Stage 4b as shown below]
- description: "Execute research for task {N}"
Format Injection: Include the format specification from Stage 4b in the prompt as a clearly-delimited section:
<artifact-format-specification>
## CRITICAL: Report Format Requirements
You MUST follow this format specification exactly when writing the research report.
Non-compliance will be caught by postflight validation.
{format_content from Stage 4b}
</artifact-format-specification>
Place this section AFTER the delegation context JSON and BEFORE any other instructions.
Prior Implementation Context Injection: If prior_implementation_context from Stage 4c is non-empty, include it in the prompt as a separate block:
<prior-implementation-context>
## Prior Implementation Context (auto-injected)
The following artifacts were found for this task. Use this context to avoid redundant research. Do NOT re-read the files listed below; use the injected content directly.
{prior_implementation_context from Stage 4c}
</prior-implementation-context>
Place the prior implementation context block AFTER the format specification and BEFORE the memory context block. Do NOT inject an empty <prior-implementation-context> block when no prior artifacts were found.
Memory Context Injection: If memory_context from Stage 4a is non-empty, include it in the prompt as a separate block:
{memory_context from Stage 4a -- already wrapped in <memory-context> tags}
Place the memory context block AFTER the prior implementation context and BEFORE the task-specific instructions. Do NOT inject an empty <memory-context> block when no memories were retrieved.
DO NOT use Skill(general-research-agent) - this will FAIL.
The subagent will:
- Search codebase for related patterns
- Search web for documentation and examples
- Analyze findings and synthesize recommendations
- Create research report in
specs/{NNN}_{SLUG}/reports/
- Write metadata to
specs/{NNN}_{SLUG}/.return-meta.json
- Return a brief text summary (NOT JSON)
Stage 5b: Self-Execution Fallback
CRITICAL: If you performed the work above WITHOUT using the Task tool (i.e., you read files,
wrote artifacts, or updated metadata directly instead of spawning a subagent), you MUST write a
.return-meta.json file now before proceeding to postflight. Use the schema from
return-metadata-file.md with status value "researched" and the appropriate artifact information.
If you DID use the Task tool (Stage 5), skip this stage -- the subagent already wrote the metadata.
Postflight (ALWAYS EXECUTE)
The following stages MUST execute after work is complete, whether the work was done by a
subagent (Stage 5) or inline (Stage 5b). Do NOT skip these stages for any reason.
Stage 6: Parse Subagent Return (Read Metadata File)
Read the metadata file:
metadata_file="specs/${padded_num}_${project_name}/.return-meta.json"
if [ -f "$metadata_file" ] && jq empty "$metadata_file" 2>/dev/null; then
status=$(jq -r '.status' "$metadata_file")
artifact_path=$(jq -r '.artifacts[0].path // ""' "$metadata_file")
artifact_type=$(jq -r '.artifacts[0].type // ""' "$metadata_file")
artifact_summary=$(jq -r '.artifacts[0].summary // ""' "$metadata_file")
memory_candidates=$(jq -c '.memory_candidates // []' "$metadata_file")
else
echo "Error: Invalid or missing metadata file"
status="failed"
fi