ase-task-research
Research a topic in depth for the current or given task plan, by combining Internet/Web search with a grounding in the source artifacts of the project, and append the resulting research report to the
SKILL.mdplugin/skills/ase-task-research/SKILL.md
GitHub@${CLAUDE_SKILL_DIR}/../../meta/ase-control.md @${CLAUDE_SKILL_DIR}/../../meta/ase-skill.md @${CLAUDE_SKILL_DIR}/../../meta/ase-getopt.md
<purpose name="ase-task-research"> Research a Topic for a Task Plan </purpose><expand name="getopt" arg1="ase-task-research" arg2="--services|-s=(all|perplexity|brave|exa|websearch)... --refine|-r --dry-run|-d --int-source-dir="> $ARGUMENTS </expand>
<objective> *Research* a topic in depth for a task plan, grounded in both the *Internet*/*Web* and the *source artifacts* of the project. </objective>@${CLAUDE_SKILL_DIR}/../../meta/ase-format-task.md @${CLAUDE_SKILL_DIR}/../../meta/ase-format-meta.md
Procedure
<define name="search-agent"> ```text Agent( description: "Query Web Search Service: <arg1/>", subagent_type: "ase:ase-meta-search", prompt: <content/>, run_in_background: false ) ``` </define>Determine Task and Topic:
Set <topic><getopt-arguments/></topic> initially, with any leading and trailing whitespace stripped. Inherit the always existing <ase-task-id/>, <ase-session-id/>, and <ase-project-boxing/> from the current context. Do not output anything.
React on task and/or topic:
If <topic/> matches the regexp
<template> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **task given** </template>^[a-zA-Z0-9#][a-zA-Z0-9#_-]*$, or has the format<id/>:or<id/>: <text/>where <id/> matches this regexp: set <ase-task-id><id/></ase-task-id> (or to <topic/> itself for the first form) and <topic/> to <text/> (or to empty for the first two forms), call thease_task_id(id: "<ase-task-id/>", session: "<ase-session-id/>")tool of theaseMCP server to switch the task, and then only output the following <template/>:Otherwise, only output the following <template/>:
<template> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **task inherited** </template>
Treat <getopt-option-services/> as a comma-separated list of backend tokens. The getopt parser validates only the first token, so you MUST validate each remaining token yourself against the allowed set
<template> ⧉ **ASE**: ☻ skill: **ase-task-research**, ▶ ERROR: invalid `--services` token: **<token/>** </template>all,perplexity,brave,exa,websearch. If any token is not in this set, bind <token/> to that offending token, then only output the following <template/> and then immediately STOP processing the entire current skill:A backend is selected if
allis in <getopt-option-services/> OR that backend's own name is in <getopt-option-services/>.Load: call the
<template> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ ERROR: no task plan to research for </template>ase_task_load(id: "<ase-task-id/>", tag: true)tool of theaseMCP server -- with thevariantargument defaulting tosourceand theattachmentsargument defaulting toelided-- and set <plan/> to itstextoutput field without the trailing<task-plan-tag>delimiter line, and <tag/> to the content of that line. If <plan/> is empty or starts withERROR:orWARNING:, only output the following <template/> and then immediately STOP processing the entire current skill:- <if condition="<topic/> is empty"> Research the *task plan itself*: set <topic/> to a concise, self-contained summary of the `# TASK:` title and the `DOM` bullet-points of <plan/> (skipping every bullet-point in the *inert* checkbox states `[-]` and `[>]`). </if> Then only output the following <template/>: <template> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ⌕ topic: **<topic/>**, ▶ status: **topic given** </template>
Research the Web:
Derive two to four focused, complementary search queries <queries/> from <topic/> -- like its established practices and standards, its typical solutions, libraries, or patterns, and its known pitfalls. Each query is a self-contained summary of an aspect of <topic/> and MUST NEVER carry confidential project internals: no file paths, no symbols or identifiers, and no other names taken from the project or from <plan/>, as the queries leave the project towards foreign search services. Do not output anything.
Invoke the following tool once per query <query/> of <queries/> and per selected backend, emitting all invocations in one single message so they run in parallel:
If the
<expand name="search-agent" arg1="perplexity"> Call the MCP tool `perplexity_search(query: "<query/>")` from the MCP server `search-perplexity`. </expand>perplexitybackend is selected and the MCP toolperplexity_searchfrom the MCP serversearch-perplexityis available:If the
<expand name="search-agent" arg1="brave"> Call the MCP tool `brave_web_search(query: "<query/>")` from the MCP server `search-brave`. </expand>bravebackend is selected and the MCP toolbrave_web_searchfrom the MCP serversearch-braveis available:If the
<expand name="search-agent" arg1="exa"> Call the MCP tool `web_search_exa(query: "<query/>")` from the MCP server `search-exa`. </expand>exabackend is selected and the MCP toolweb_search_exafrom the MCP serversearch-exais available:If the
<expand name="search-agent" arg1="websearch"> Call the tool `WebSearch(query: "<query/>")`. </expand>websearchbackend is selected and the toolWebSearch(or, if it is not available, the toolweb_search) is available:If none of the selected backends could be invoked -- because no corresponding tool is available -- only output the following <template/> and then immediately STOP processing the entire current skill:
<template> ⧉ **ASE**: ✪ skill: **ase-task-research**, ▶ ERROR: no search backend available: **<getopt-option-services/>** </template>Consolidate all responses into the Web facts <web-facts/>: merge overlapping information, drop irrelevant and unsourced information, and keep every remaining fact together with its source URL. Do not output anything.
Ground in the Project:
Resolve the artifact kinds
<if condition="<getopt-option-int-source-dir/> is not empty"> The *code basis* of the task is the separate working copy <getopt-option-int-source-dir/> (e.g. a checkout of the branch of its `Source` key): run the command `ase artifact list` *once* with <getopt-option-int-source-dir/> as its working directory and parse its `# <kind/>:` and `- <file/>` output lines into the file lists, and read every file *exclusively* from there -- the `ase_artifact_list` tool always resolves against the *project* working copy and hence *MUST* *NOT* be used here. </if> <else> Call the `ase_artifact_list(kind: [ "spec", "code", "docs", "infr" ])` tool of the `ase` MCP server *once* and read the returned `artifacts` array of `{ kind, files }` objects into the file lists. </else>spec,code,docs, andinfrto their project-relative file lists:From these file lists, determine the files (or their parts) which are relevant for <topic/> and <plan/> -- the specification parts, source code modules, interfaces, and symbols, the documentation, and the build, test, and deployment configuration the topic touches or depends on -- and read them, strictly read-only (never change, stage, or commit anything). Condense their relevant project facts -- the current state, existing decisions, conventions, constraints, and terminology -- each with its project-relative file path, into <project-facts/>. Do not output anything.
Synthesize the Research Report:
Call the
<format> ## RESEARCH: <topic/>ase_timestamp(format: "yyyy-LL-dd HH:mm")tool of theaseMCP server and set <timestamp/> to thetextfield of its response. Then set <section/> to the research section of this run with the following <format/>, bounded to about300lines and at most10findings (the most relevant ones), where every line of every part is broken after about 100 characters, and where a part without any content states(none):Researched: <timestamp/>
Findings
- <finding-summary/>: <finding-text/> (<source-url/>) [...]
Project Grounding
<project-file/>: <project-fact/> [...]
Assessment
<assessment/>Recommendations
- <recommendation-summary/>: <recommendation-text/> [...]</format>
Here the Findings are the most relevant Web facts of <web-facts/>, each with its source URL, the Project Grounding are the most relevant project facts of <project-facts/>, each with its project-relative file path, the Assessment states in 3-8 sentences how the findings apply to the project, including the gaps (what the project lacks) and the conflicts (where the project or <plan/> deviates from the findings), and the Recommendations are the concrete, actionable consequences for <plan/>. You MUST NOT invent any finding, source URL, or project fact which is not backed by <web-facts/> resp. <project-facts/>. The section MUST NOT contain a line consisting of just
---. Do not output anything.Attach the Research Report:
- <if condition="<getopt-option-dry-run/> is `true`"> Set <status>researched (dry-run, not attached)</status> and continue with sub-step 3. </if>
Persist: update <plan/> as follows and save it, where a refused save restarts this sub-step:
If the backmatter of <plan/> contains an attachment block with the
Typekey valuetext/markdown; charset=utf-8; kind="research", it carries the sections of earlier research runs: fetch its payload via thease_task_attachment(id: "<ase-task-id/>", type: "text/markdown; charset=utf-8; kind=\"research\"")tool of theaseMCP server, whosetextoutput field is the payload verbatim, and set <payload/> to it, followed by an empty line and <section/>, so the sections accumulate in chronological order. Otherwise, set <payload/> to <section/> and append a new attachment block to the backmatter of <plan/>.Set the keys of this attachment block according to the plan <format/>:
Typetotext/markdown; charset=utf-8; kind="research",Descto <topic/> (truncated to at most 60 characters, ending in…if truncated),Createdto its already present value (or to <timestamp/> for a new block),Modifiedto <timestamp/>, andDatato <payload/> as a|4+literal block scalar, with every line -- including empty lines -- indented by exactly 4 spaces.- <if condition="<getopt-option-refine/> is `true`"> Treat the *Recommendations* of <section/> as a *refinement instruction* for the plan and apply them *in-place* to the body of <plan/>, preserving the overall structure and the format of the plan <format/>: add new bullet-points in checkbox state `[ ]` or adjust the <text/> of existing ones, but *never* rewrite unrelated bullet-points and *never* change a bullet-point in checkbox state `[x]`, `[-]`, or `[>]`. Skip recommendations which the plan already reflects. If the body actually changed, set the `Modified:` frontmatter key to <timestamp/> -- the very same value as the `Modified` key of the research attachment, so the attachment does not fall behind the plan. </if>
Change nothing else of <plan/>: especially, do not change the
Status:key, pass theEffort:key through verbatim (the task store drops it on its own if the body changed), and pass all other attachment blocks through verbatim (with their elidedDatamarkers).Call the
ase_task_save(id: "<ase-task-id/>", text: "<plan/>", base: "<tag/>")tool of theaseMCP server, so the save is refused if the plan was changed meanwhile by anybody else. Thisase_task_saveMCP tool call is the only permitted way to persist the plan -- NEVER write the plan file viaWrite/Editor by executing a shell command.On success, set <status>research attached</status>, or <status>research attached, plan refined</status> if the body was changed by sub-step 3 above.
If the save is refused because the plan was changed meanwhile, reload <plan/> and <tag/> as in step 1.4 and restart this sub-step 2 (keeping <section/>), at most three times in total, and afterwards set <status>failed: plan changed concurrently</status>.
On any other error <info/>, set <status>failed: <info/></status>.
Report:
- <if condition="<status/> starts with `failed` or <ase-project-boxing/> is `black`"> Only output the following <template/>: <template> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **<status/>** </template> </if>
- <elseif condition="<ase-project-boxing/> is `grey`"> Only output the following <template/>, where <condensed/> is the *Assessment* and the *Recommendations* of <section/>, condensed to their essentials: <template> <ase-tpl-boxed title="RESEARCH" subtitle="<ase-task-id/>"> <condensed/> </ase-tpl-boxed> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **<status/>** </template> </elseif>
- <else> Only output the following <template/>: <template> <ase-tpl-boxed title="RESEARCH" subtitle="<ase-task-id/>"> <section/> </ase-tpl-boxed> ⧉ **ASE**: ◉ task: **<ase-task-id/>**, ▶ status: **<status/>** </template> </else>
Finally, give the closing hint by expanding the following (which, depending on the configured <ase-guidance-level/>, may expand into nothing and hence emit no output at all), and then STOP processing the entire current skill:
<if condition="<getopt-option-dry-run/> is `true`"> <ase-tpl-hint level="normal"> Run `/ase-task-research` again without `--dry-run` to append the research report to the `research` attachment of the task. </ase-tpl-hint> </if> <elseif condition="<getopt-option-refine/> is not `true` and <status/> starts with `research attached`"> <ase-tpl-hint level="verbose"> Use `/ase-task-research --refine` to additionally refine the plan by the recommendations, or `/ase-task-view --full` to view the research attachment. </ase-tpl-hint> </elseif>