From easypaper
Generate a full academic paper from metadata using EasyPaper Python SDK. Collects metadata interactively if not provided, then generates the paper directly.
How this skill is triggered — by the user, by Claude, or both
Slash command
/easypaper:easypaper-paper-from-metadataThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use this skill when the user wants to generate an academic paper from metadata. This skill handles both metadata collection and paper generation in one unified workflow.
Use this skill when the user wants to generate an academic paper from metadata. This skill handles both metadata collection and paper generation in one unified workflow.
Recommended: Have the user prepare a metadata.json (see examples/meta.json for the full schema), then parse it as PaperGenerationRequest and run with to_metadata() + to_generate_options(). A file like examples/meta.json is fully supported.
examples/meta.json structureIf metadata is missing or incomplete, collect all required fields interactively:
Title
Idea/Hypothesis
Method
Data
Experiments/Results
References
[
"Wang, H. et al. Scientific discovery in the age of artificial intelligence. Nature 620, 47-60 (2023).",
"LeCun, Y., Bengio, Y. & Hinton, G. Deep learning. Nature 521, 436-444 (2015)."
]
Style Guide (Venue): "Which venue or style guide? Options: NeurIPS, ICML, ICLR, ACL, AAAI, COLM, Nature, or custom." (Default: "Nature")
Target Pages: "What is your target page count?" (Default: 20)
Template Path:
os.path.abspath() or pathlib.Path.resolve()./Users/username/papers/templates/nature.zip or /home/user/templates/custom.texCompile PDF: "Should the paper be compiled to PDF? (yes/no)" (Default: true)
Enable Review: "Enable VLM-based review and iterative improvement? (yes/no)" (Default: true)
Max Review Iterations: "Maximum number of review iterations (if review is enabled):" (Default: 3)
id, file_path (must be absolute), caption, descriptionfile_path values must be absolute paths. Convert relative paths to absolute using os.path.abspath() or pathlib.Path.resolve().[
{
"id": "fig:architecture",
"file_path": "/Users/username/papers/figures/architecture.png",
"caption": "System architecture diagram",
"description": "Shows the overall system design"
}
]
file_path must be absolute and file must existTables: Array of table objects (Default: empty array)
Code Repository:
local_dir type: absolute path is requiredgit_repo type: URL is requiredlocal_dir and user provides relative path, convert to absolute path using os.path.abspath() or pathlib.Path.resolve().{
"type": "local_dir",
"path": "/Users/username/projects/my_code",
"on_error": "fallback"
}
{
"type": "git_repo",
"url": "https://github.com/user/repo.git",
"ref": "main"
}
os.path.abspath() or pathlib.Path.resolve()./Users/username/papers/output/my_paper or /home/user/output/output_20250120{current_working_directory}/output_{timestamp} (converted to absolute)Before generating:
template_path, figures[].file_path, code_repository.path (if local_dir), and output_dir are all absolute paths. Convert any relative paths found.metadata.json file (with all absolute paths)Check environment:
setup-environment skill if needed)Import and initialize EasyPaper:
from easypaper import EasyPaper, PaperMetaData, PaperGenerationRequest
from pathlib import Path
# Convert config path to absolute if needed
config_path = Path("configs/openrouter.yaml").resolve() # or user-provided path
# Initialize with config
ep = EasyPaper(config_path=str(config_path))
Obtain SDK inputs (prefer loading from file):
request = PaperGenerationRequest.model_validate_json_file(path), then:
paper_metadata = request.to_metadata()options = request.to_generate_options()template_path, figures[].file_path, code_repository.path (if local_dir), and output_dir.PaperMetaData from collected content, and build runtime options for generate().PaperGenerationRequest example.Generate paper:
from pathlib import Path
from easypaper import PaperGenerationRequest
request = PaperGenerationRequest.model_validate_json_file("metadata.json")
paper_metadata = request.to_metadata()
options = request.to_generate_options()
# Resolve output_dir to absolute if present
if options.get("output_dir"):
options["output_dir"] = str(Path(options["output_dir"]).resolve())
result = await ep.generate(metadata=paper_metadata, **options)
OR use streaming for progress updates:
async for event in ep.generate_stream(metadata, **options):
print(f"{event.get('phase', '')}: {event.get('message', '')}")
Report results:
paper.tex, references.bib, paper.pdf (if compiled)result.pdf_path (authoritative)result.output_path/iteration_*_final/**/*.pdfresult.output_path/iteration_* directory PDFresult.output_path/paper.pdf fallbackTypesetter execution mode:
AGENTSYS_SELF_URL) if peer is unavailable.CRITICAL: All paths in metadata must be absolute paths. Follow these rules:
When collecting paths from user:
from pathlib import Path
absolute_path = Path(relative_path).resolve()
When reading metadata from file:
template_path, figures[].file_path, code_repository.path (if local_dir), output_dirWhen saving metadata:
Path fields that must be absolute:
template_path: LaTeX template file/directory pathfigures[].file_path: Figure image file pathscode_repository.path: Local code repository directory path (if type is local_dir)output_dir: Output directory pathconfig_path: EasyPaper configuration file pathThe metadata should match the structure in examples/meta.json, but with absolute paths:
{
"title": "...",
"idea_hypothesis": "...",
"method": "...",
"data": "...",
"experiments": "...",
"references": [...],
"style_guide": "Nature",
"target_pages": 20,
"template_path": "/absolute/path/to/template.zip",
"compile_pdf": true,
"enable_vlm_review": true,
"max_review_iterations": 3,
"figures": [
{
"id": "fig:example",
"file_path": "/absolute/path/to/figure.png",
"caption": "...",
"description": "..."
}
],
"tables": [],
"code_repository": {
"type": "local_dir",
"path": "/absolute/path/to/code",
"on_error": "fallback"
},
"output_dir": "/absolute/path/to/output"
}
Choosing between SDK one-shot and Claude-driven interactive build: If you want Claude Code to walk the folder with its own tools and co-author the metadata in conversation (with cross-skill help from
paperhub-*,exa-search,sequential-thinking), use the dedicatedinteractive-metadata-buildskill (slash command/easypaper-metadata-build) instead. The interactive skill produces the samePaperMetaDataJSON shape, so this skill consumes its output without changes.The section below describes the SDK one-shot path: a single Python call that does everything autonomously, no user dialogue. Best for batch / CI / regression eval.
When the user has a folder of research materials (code, data, PDFs, images, notes, BibTeX) instead of a ready-made metadata.json, EasyPaper can scan the folder and synthesize PaperMetaData automatically.
.py, .csv, .json, .md, .tex, .bib, .png, .pdf, etc.from easypaper import EasyPaper
from pathlib import Path
ep = EasyPaper(config_path=str(Path("configs/openrouter.yaml").resolve()))
# Step 1: Scan folder → PaperMetaData (with prose, figures, tables, venue inference)
metadata = await ep.generate_metadata_from_folder(
str(Path("path/to/materials").resolve()),
max_figures=12, # hard cap; LLM picks best subset if exceeded
max_tables=12,
vision_enrich_figures=True, # default True; uses vision model per figure
# vision_model="gpt-4o", # defaults to the config model
# max_vision_figures=8, # optional: cap vision API calls
)
# Step 2: Generate paper from the synthesized metadata
result = await ep.generate(metadata, compile_pdf=True)
FolderScanner walks the directory, classifies files by extension (IMAGE, PDF, BIB, TEXT, CODE, DATA, CONFIG).ImageExtractor, DataExtractor, PDFExtractor with LLM, etc.) producing ExtractedFragment objects. All paths stored as POSIX relative to materials_root.MetadataSynthesizer (one LLM call) merges fragments into the five prose fields (idea_hypothesis, method, data, experiments) plus title, and infers style_guide, target_pages, and per-asset section placement.max_figures / max_tables, an LLM curator selects the minimal supporting subset (rule-based fallback if no LLM).description prose, replacing the raw "Image file: …" placeholder. Results are cached by content hash on disk (~/.cache/easypaper/figure_vision/ or EASYPAPER_CACHE_DIR).| Parameter | Default | Description |
|---|---|---|
max_figures | None (no cap) | Hard upper bound on figure count |
max_tables | None (no cap) | Hard upper bound on table count |
vision_enrich_figures | True | Run vision model on each retained figure |
vision_model | same as model_name | Multimodal model id (e.g. gpt-4o) |
max_vision_figures | None (all) | Max vision API calls |
max_vision_long_edge | 896 | Downscale longest image edge before JPEG encode |
vision_cache_dir | auto | Disk cache for vision descriptions |
Unlike hand-written metadata (where all paths must be absolute), the folder pipeline sets metadata.materials_root to the folder's absolute path and stores figures[].file_path / tables[].file_path as relative POSIX paths. Downstream generation resolves them via materials_root. Do not manually convert these to absolute paths.
max_vision_figures to hard-cap the number of vision API calls.vision_enrich_figures=False disables vision entirely (useful for tests or non-multimodal models).examples/meta.json when users ask about structure (but note paths should be absolute). Treat it as a full PaperGenerationRequest sample and use to_metadata() + to_generate_options().result.pdf_path; never assume the first PDF in output dir is final.from easypaper import EasyPaper, PaperMetaData, PaperGenerationRequest directly - no API calls neededconfigs/dev.yaml (convert to absolute)setup-environment skillnpx claudepluginhub pinkgranite/easypaper --plugin easypaperSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.
Implements vector databases with Pinecone, Weaviate, Qdrant, Milvus, pgvector for semantic search, RAG, recommendations, and similarity systems. Optimizes embeddings, indexing, and hybrid search.