Install
$ agentstack add skill-o0000-code-paper-search-pro-paper-search-pro ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
paper-search-pro
Multi-source literature search with adjustable depth. Four tiers, five data sources orchestrated by you (the main agent). Python helpers handle deterministic work; LLM classification is delegated to parallel Inline SubAgents — no external API key required.
When to use this skill
- User wants to find academic papers / 找文献 / 论文搜索
- User is preparing a literature review, systematic review (SR), scoping review, or meta-analysis
- User wants to scope research on a topic for a thesis / proposal / coursework / news story
- User asks "what research exists on X" / "find me papers about Y"
- User uploads a query that suggests literature gathering (PICO, SPIDER, MeSH, RCT, etc.)
When NOT to use
- User wants to read a specific paper (use PDF reader / download tool)
- User wants to summarize a single known paper (use a summarizer)
- User wants to download PDFs given DOIs (use
paper-downloader-portable) - User already has a literature set and wants to write a review (use
literature-set-review/factor-outcome-review) - User wants concept explanation, not papers ("what is prospect theory" → just answer)
🔥 Execution discipline (read this BEFORE running anything)
These four rules govern every step below. Violating them is the dominant failure mode observed in real sessions — re-read them whenever you feel rushed.
Rule A — NEVER cd into the Skill directory
Reason: cd $PSP_HOME rebinds ./ to the Skill asset directory. Every ./paper-search-results/... after that lands inside the Skill folder, not where the user is working. Re-installing the Skill overwrites history; the user can't find outputs in their own working directory.
Correct pattern — execute helpers from the user's working directory using PYTHONPATH:
PYTHONPATH=$PSP_HOME \
python3 -m scripts.openalex_helper search "" --limit 30 \
> "$SEARCH_DIR/raw/openalex.json"
Where $PSP_HOME is the Skill install directory (resolved in STEP 0) and $SEARCH_DIR is an absolute path under the user's PWD (also from STEP 0). Shell cwd remains the user's PWD; ./ paths resolve to where the user expects.
Rule B — Parallelism is MANDATORY for SubAgent dispatch
When you launch classifier SubAgents (STEP 6), you must put up to 5 Task tool_use blocks inside one assistant message. Serial dispatch (one Task per message, waiting for each result) makes Standard tier take ~17 min instead of ~10. See STEP 6 worked example.
Rule C — Tell the user every time you skip a step
If you deliberately skip any STEP (because tier budget is exhausted, data is empty, or user preference), state it explicitly:
- What you skipped (e.g. "STEP 10 L3 enrichment")
- Why (tier? data? user choice?)
- What's lost (e.g. "no influentialCitationCount, no funder/license fields")
- How to recover (e.g. "re-run at
--tier deepto include this")
Never skip silently. Skipping is fine; surprising the user is not.
Rule D — Read the cited references/ file BEFORE the step
Each STEP names a references/.md. Read it before running the step's commands — the cheatsheets contain edge cases that are not duplicated in SKILL.md. Average must-read coverage across recent sessions was 5/17 — drive it higher.
Architecture at a glance
You (main agent) drive the workflow per this SKILL.md.
Python helpers do deterministic work — NO LLM inside, NO external API key.
L1 OpenAlex (primary) → deep top-100 multi-strategy
L2 PubMed (medical) → MeSH enricher (mostly; Audit-tier can search independently)
L2 arXiv (CS/preprint) → T-0~T-4 freshness sentinel
L3 Semantic Scholar → influentialCitationCount + abstract fallback
L3 CrossRef → funder / license / clinical-trial-number
Classification → Inline SubAgents (parallel, file-IPC, 5 per message)
Output → HTML (Shadcn) + MD + BibTeX/RIS/CSV + PRISMA-S log
The 4 tiers — pick first
| Tier | Wall-clock | Papers | When to pick | |------|------------|--------|--------------| | Quick | ~5-8 min | 20-60 | "查一下" / "几篇" / "before tomorrow" / fast scope | | Standard (default) | ~10-17 min | 60-180 | Scope a topic / write background / general lit search | | Deep | ~30-45 min | 180-400 | "thorough" / writing a review article / 综述写作 | | Audit | ~2-3 hr | 400-1000+ | "systematic review" / "PRISMA" / "Cochrane" / "meta-analysis" |
📖 BEFORE picking, read references/tier_decision.md. Tell the user your choice and why. For Audit, show limitations warning + get explicit confirmation before starting.
The recipe
For every literature search, follow these steps in order. Each step references a references/ file for details. Skip files only when the step is obviously trivial for the case at hand — and announce the skip per Rule C.
STEP 0 — Setup ($PSP_HOME + working directory)
📖 BEFORE THIS STEP, read: references/setup.md.
Resolve the Skill install path into $PSP_HOME — every later step uses PYTHONPATH=$PSP_HOME. The Skill ships as a SKILL.md package; different Agents install it at different paths (~/.claude/skills/, ~/.codex/skills/, ~/.agents/skills/, ~/.config/opencode/skills/, project-local .claude/skills/, etc.). Resolve $PSP_HOME once via a three-layer chain — explicit injection first, env var second, filesystem fallback third — and reuse it in every helper invocation.
# Layer 1: explicit injection. If you (the agent) already know where this
# SKILL.md lives — because your harness exposed its absolute path —
# substitute it here and skip Layers 2-3:
# export PSP_HOME=""
#
# Layer 2: agent-injected env var (Claude Code / CodeBuddy populate these).
# Layer 3: walk the known cross-agent install locations.
PSP_HOME="${PSP_HOME:-${CLAUDE_SKILL_DIR:-${CODEBUDDY_SKILL_DIR:-}}}"
if [ -z "$PSP_HOME" ]; then
for d in \
"$HOME/.claude/skills/paper-search-pro" \
"$HOME/.codex/skills/paper-search-pro" \
"$HOME/.agents/skills/paper-search-pro" \
"$HOME/.config/opencode/skills/paper-search-pro" \
"$HOME/.codeium/windsurf/skills/paper-search-pro" \
"$HOME/.config/goose/skills/paper-search-pro" \
"$HOME/.cline/skills/paper-search-pro" \
"$HOME/.roo/skills/paper-search-pro" \
"$HOME/.copilot/skills/paper-search-pro" \
"./.claude/skills/paper-search-pro" \
"./.codex/skills/paper-search-pro" \
"./.agents/skills/paper-search-pro" \
"./.cursor/skills/paper-search-pro" \
"./.opencode/skills/paper-search-pro" \
"./.windsurf/skills/paper-search-pro"; do
[ -f "$d/SKILL.md" ] && PSP_HOME="$d" && break
done
fi
[ -z "$PSP_HOME" ] && { echo "ERROR: paper-search-pro install not found. Set PSP_HOME to the directory containing SKILL.md."; exit 1; }
export PSP_HOME
echo "Using Skill install: $PSP_HOME"
Verify config keys (executed from any cwd, never cd into the Skill dir):
PYTHONPATH=$PSP_HOME python3 -c \
"from scripts.config import load_config; c = load_config(); print('OK' if c.openalex_api_key and c.ncbi_email else 'MISSING — see references/setup.md')"
If "MISSING", point the user to references/setup.md (5 keys, all free, ~15 min total) and halt.
Set up the working directory variable — every subsequent step uses $SEARCH_DIR:
SEARCH_ID="__$(date +%Y%m%d_%H%M%S)" # e.g. clt_education_quick_20260522_103045
SEARCH_DIR="$(pwd)/paper-search-results/$SEARCH_ID"
mkdir -p "$SEARCH_DIR/raw" "$SEARCH_DIR/batches" "$SEARCH_DIR/classifications"
echo "Outputs will land in: $SEARCH_DIR"
$SEARCH_DIR is now an absolute path under the user's PWD. Use "$SEARCH_DIR/..." (quoted, with the variable) in every helper command below — not ./paper-search-results/....
STEP 1 — Plan the query (MANDATORY for all tiers)
📖 BEFORE THIS STEP, read: references/query_planner.md.
Detect query language first: if the user's query contains any Chinese character (Unicode range U+4E00–U+9FFF, CJK Unified Ideographs), set UI_LANG=zh; otherwise UI_LANG=en. This single boolean controls which UI language the final HTML report renders in (paper titles / abstracts / authors / venues are NEVER translated — only the report's UI chrome). Pass --language $UI_LANG to STEP 12b.
The bundle ships with only English and Chinese dictionaries. Non-Chinese queries (including Japanese / Korean / European languages) all route to English, which is the international academic default — a Korean researcher reading an English UI is friendlier than the same researcher confronting a Chinese UI they cannot parse. Routing Japanese / Korean to Chinese was the previous heuristic; it was changed because the assumption "CJK readers can read Chinese UI" does not hold.
# Heuristic: bash regex on the raw query, applied in order — first match wins.
#
# Japanese kanji share Unicode U+4E00–U+9FFF with Chinese Han characters,
# so a kanji-containing Japanese query like "東京大学の最新研究" would
# look identical to a Chinese query at the codepoint level. To route such
# queries to EN, check for hiragana (U+3041–U+309F) or katakana
# (U+30A0–U+30FF) FIRST; real Japanese text almost always contains one or
# the other, so this signal is reliable in practice.
#
# Order matters:
# 1. hiragana/katakana present → Japanese → EN
# 2. else CJK Unified Ideograph present → Chinese → ZH
# 3. else → EN (default international)
#
# `ぁ-ヿ` covers U+3041–U+30FF (hiragana + katakana full range).
# `一-鿿` covers U+4E00–U+9FFF (CJK Unified Ideographs, full range —
# `一-龯` would stop at U+9FAF and miss 55 rare chars).
# Hangul (Korean) is not matched: those queries fall through to EN.
if [[ "$USER_QUERY" =~ [ぁ-ヿ] ]]; then
UI_LANG=en # Japanese → EN
elif [[ "$USER_QUERY" =~ [一-鿿] ]]; then
UI_LANG=zh # Chinese (no hiragana/katakana) → ZH
else
UI_LANG=en # everything else → EN
fi
Apply PICO / SPIDER / PEO depending on domain:
- Medical/clinical → PICO (Population/Intervention/Comparator/Outcome)
- Qualitative → SPIDER
- Scoping → PEO (Population/Exposure/Outcome)
- Open-ended → just extract 2-4 concept blocks + 2-5 synonyms each
Even Quick tier needs a lightweight version of this step — never skip silently. Output: 1-3 search strategies (concept blocks + year range + work type filter). Write to "$SEARCH_DIR/query_plan.json" so PRISMA-S logger can pick it up later (STEP 13).
STEP 2 — Detect source routing
📖 BEFORE THIS STEP, read: references/source_routing.md.
Decide which L2/L3 sources to enable beyond OpenAlex baseline:
- Medical signals (RCT, PRISMA, MeSH, clinical, disease names) → enable PubMed
- CS/preprint signals (preprint, arXiv, NeurIPS, transformer, "最新", 2024+) → enable arXiv
- Cross-domain (e.g. "AI in radiology") → enable both
- Pure social science / humanities → OpenAlex only
Tell the user what you decided ("I detected medical + CS signals — also searching PubMed and arXiv"). User can override with explicit instruction.
STEP 3 — Retrieve from OpenAlex (deep)
📖 BEFORE THIS STEP, read: references/openalex_helper_cheatsheet.md.
Always run OpenAlex first. For Standard+ tiers, use multi-strategy deep crawl:
PYTHONPATH=$PSP_HOME \
python3 -m scripts.openalex_helper double-sort "" \
--n 50 --year-min 2018 \
> "$SEARCH_DIR/raw/openalex.json"
For Quick tier, single-strategy is fine:
PYTHONPATH=$PSP_HOME \
python3 -m scripts.openalex_helper search "" \
--limit 30 --year-min 2018 \
> "$SEARCH_DIR/raw/openalex.json"
Subcommand parameter reference (verified against argparse):
search→--limit,--year-min,--year-max,--type(positionalquery)double-sort→--n(per-strategy),--year-min(positionalquery)seminal→--year-max,--limit(positionaltopic) — for classic high-citedreviews→--limit,--year-min(positionaltopic)journal-list→--preset Cochrane|UTD24|nature_science|medical_top,--limit(positionalquery)citation-network→--refs-limit,--cited-by-limit(positionalopenalex_id) — used in STEP 9
For Deep+Audit, also call topic-specific subcommands (e.g. seminal, reviews, journal-list). Append outputs to $SEARCH_DIR/raw/openalex_*.json and federate them all together in STEP 5.
STEP 4 — Run L2 boosters (if enabled by STEP 2)
📖 BEFORE THIS STEP, read: references/pubmed_helper_cheatsheet.md and references/arxiv_helper_cheatsheet.md.
PubMed — default mode is enrich, NOT search:
- Standard / Deep tier: enrich OA-found papers with MeSH terms (mutates the openalex.json file in place):
``bash PYTHONPATH=$PSP_HOME \ python3 -m scripts.pubmed_helper enrich \ --input-file "$SEARCH_DIR/raw/openalex.json" \ --output-file "$SEARCH_DIR/raw/openalex.json" ``
- Audit tier with explicit MeSH query: independent MeSH search (produces a new file to federate later):
``bash PYTHONPATH=$PSP_HOME \ python3 -m scripts.pubmed_helper search-mesh "Diabetes Mellitus, Type 2" \ --year-min 2020 --limit 30 --pub-type "Randomized Controlled Trial" \ > "$SEARCH_DIR/raw/pubmed.json" ``
- Generic
pubmed_helper searchis a fallback when no MeSH term is known — preferenrichorsearch-meshwhenever possible.
arXiv — only if query contains freshness signals (preprint, 最新, 2024+):
PYTHONPATH=$PSP_HOME \
python3 -m scripts.arxiv_helper freshness "" \
--days 4 --limit 30 \
> "$SEARCH_DIR/raw/arxiv.json"
Subcommand reference:
arxiv_helper freshness --days N --limit M [--all-cats]arxiv_helper search --limit M --sort submitted|relevance|lastUpdated [--all-cats]arxiv_helper get
STEP 5 — Federate (dedup + merge)
📖 BEFORE THIS STEP, read: references/source_routing.md §"Field priority table".
Combine all retrieval results into a single deduped KG. Default output is a dict keyed by canonical_key — that's what rcs_parser expects later, so do NOT pass --as-list:
PYTHONPATH=$PSP_HOME \
python3 -m scripts.federated_kg_resolver \
--input-files "$SEARCH_DIR/raw/openalex.json" \
"$SEARCH_DIR/raw/pubmed.json" \
"$SEARCH_DIR/raw/arxiv.json" \
--output "$SEARCH_DIR/kg.json"
Pass only the input files you actually produced — skip ones that were not enabled by STEP 2. This handles DOI normalization (arXiv X→x case), version stripping, E5b guard (same title+year but different DOIs are kept separate), and field-priority merge.
--as-list exists but is only for consumers that want a sorted list (by citation_count); do not use it in this pipeline.
STEP 6 — Classify in parallel batches (LLM happens here — main agent + SubAgents)
📖 BEFORE THIS STEP, read: references/classifier_subagent_prompt.md and references/rcs_rubric.md.
Split the KG into batches of 10 papers each. Write to "$SEARCH_DIR/batches/batch_NNN.jsonl".
Before dispatch, expand $PSP_HOME/references/rcs_rubric.md into the actual absolute path (e.g. /Users/alice/.claude/skills/paper-search-pro/references/rcs_rubric.md) and substitute it for {rubric_path} in the classifier prompt template. Each SubAgent runs in its own shell where $PSP_HOME is not exported — passing the literal $PSP_HOME token would leave the SubAgent unable to find the rubric, which silently degrades scoring quality. See references/classifier_subagent_prompt.md for the full placeholder table.
🔥 PARALLELISM IS MANDATORY (Rule B):
You MUST dispatch up to 5 classifier SubAgents in a single assistant message using multiple Task tool_use blocks. Serial dispatch (one Task per message, waiting for each result) is the single biggest performance failure observed — it inflates Standard tier from ~10 min to ~17 min.
✅ CORRECT — in ONE assistant message:
Task tool_use #1 → subagent_type="general-purpose", prompt=""
Task tool_use #2 → subagent_type="general-purpose", prompt=""
Task tool_u
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [O0000-code](https://github.com/O0000-code)
- **Source:** [O0000-code/paper-search-pro](https://github.com/O0000-code/paper-search-pro)
- **License:** Apache-2.0
- **Homepage:** https://o0000-code.github.io/paper-search-pro/
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.