From fbbce995dc28980eca4d08a7301f175559f46c47 Mon Sep 17 00:00:00 2001 From: Rodney Kinney Date: Sat, 21 Mar 2026 11:54:46 -0700 Subject: [PATCH 1/3] Add 'literature report' subcommand and use it for literature-report skill --- .../skills/literature-report/SKILL.md | 172 ++--------------- skills/literature-report/SKILL.md | 172 ++--------------- src/asta/cli.py | 2 + src/asta/literature/report.py | 132 +++++++++++++ src/asta/literature/report_client.py | 81 ++++++++ src/asta/utils/asta.conf | 5 + tests/test_cli.py | 178 ++++++++++++++++++ uv.lock | 2 +- 8 files changed, 437 insertions(+), 307 deletions(-) create mode 100644 src/asta/literature/report.py create mode 100644 src/asta/literature/report_client.py diff --git a/plugins/asta-preview/skills/literature-report/SKILL.md b/plugins/asta-preview/skills/literature-report/SKILL.md index abf7049..ac1ad83 100644 --- a/plugins/asta-preview/skills/literature-report/SKILL.md +++ b/plugins/asta-preview/skills/literature-report/SKILL.md @@ -5,18 +5,14 @@ metadata: internal: true allowed-tools: - Bash(asta literature find *) - - Bash(asta papers *) - - Write(.asta/literature/report/*) - - Edit(.asta/literature/report/*) + - Bash(asta literature report *) - Read(.asta/literature/*) - - Read(*.json) - TaskOutput - - Bash(jq *) --- # Literature Report Generation -Generate comprehensive literature reports by searching academic literature, analyzing papers, and synthesizing findings into a well-structured markdown document. +Generate comprehensive literature reports by finding relevant papers and synthesizing them into a structured written report. ## When to Use This Skill @@ -31,166 +27,36 @@ Generate comprehensive literature reports by searching academic literature, anal ### 1. Check for Existing Results **If the user provides an existing paper-finder results file:** -- Ask them to confirm the file path -- Use Read tool to verify the file exists and is valid JSON -- Skip to step 3 to process those results directly +- Skip to step 3, using that file as `-i` **If no existing results file:** -- Proceed to launch a new search below +- Proceed to step 2 -### 2. Launch Background Searches (if needed) +### 2. Find Papers -Start with the paper finder in background (30-60s): ```bash -# Uses default location .asta/literature/find/YYYY-MM-DD-HH-MM-SS-{query-slug}.json -Bash(command="asta literature find 'query' --timeout 300", run_in_background=true) - -# Or specify a custom output location -Bash(command="asta literature find 'query' --timeout 300 -o custom-results.json", run_in_background=true) +asta literature find 'query' --timeout 300 -o .asta/literature/find/YYYY-MM-DD-topic-slug.json ``` -Launch additional targeted CLI searches as needed while paper finder runs. - -### 3. Create Report File Immediately - -Don't wait for searches to complete. Create the report file early with: -- Default file path: `.asta/literature/report/YYYY-MM-DD-topic-slug.md` (in current working directory) -- Custom path: User can specify a different location if desired -- Initial structure: Title, Executive Summary (placeholder), section headings, References - -This keeps the user informed and avoids losing work. - -### 4. Process Paper Finder Results - -**If using existing results file:** -- Use the file path provided by the user -- Read it with the Read tool to understand the query and papers +Note the output path from the command. -**If launched a new search:** -- When `` arrives, use TaskOutput to get the results: -```bash -TaskOutput(task_id="") -``` -- The results file will be at `.asta/literature/find/` (check TaskOutput for the exact filename), or at the path you specified with `-o` +### 3. Generate the Report -Browse results with jq (sorted by relevance): ```bash -# Replace RESULTS_FILE with the actual path from TaskOutput or your custom path -jq '[.results | sort_by(-.relevanceScore) | .[0:10][] | {title, year, venue, corpusId, citationCount, score: .relevanceScore, authors: (.authors | if length <= 4 then [.[].name] | join(", ") else ([.[0:3][].name] | join(", ")) + ", +" + (length - 4 | tostring) + ", " + .[-1].name end), summary: .relevanceJudgement.relevanceSummary}]' RESULTS_FILE +asta literature report \ + -i .asta/literature/find/YYYY-MM-DD-topic-slug.json \ + -o .asta/literature/report/YYYY-MM-DD-topic-slug.md ``` -**Important**: Go through ALL highly relevant papers (not just top few). Extract: -- Relevance criteria and evidence -- Snippets from paper body -- Citation contexts (how others cite this work) - -### 5. Deep Dive on Key Papers +The command prints progress to stderr and the report path when done. -For each important paper, extract detailed evidence: - -**Relevance evidence**: -```bash -jq '.results[] | select(.corpusId == CORPUS_ID) | {title, criteria: [.relevanceJudgement.relevanceCriteriaJudgements[] | {concept: .name, relevance, evidence: [.relevantSnippets[].text]}]}' RESULTS_FILE -``` - -**Abstract and passages**: -```bash -jq '.results[] | select(.corpusId == CORPUS_ID) | {title, abstract, snippets: [.snippets[]? | {section: .sectionTitle, text}]}' RESULTS_FILE -``` - -**Citation contexts** (how others describe this work): -```bash -jq '[.results[] | select(.corpusId == CORPUS_ID) | .citationContexts[].text]' RESULTS_FILE -``` - -Replace `RESULTS_FILE` with the actual results file path (found in `.asta/literature/find/`, user-provided, or from TaskOutput). - -### 6. Use CLI Tools for Targeted Research - -Supplement paper finder with fast, targeted searches using `asta papers`: - -```bash -# Search for papers -asta papers search "keyword" --year 2023- --limit 20 --fields title,abstract,year,authors - -# Get paper details -asta papers get ARXIV:2005.14165 --fields title,abstract,authors,year - -# Get citations -asta papers citations CorpusId:12345 --limit 50 - -# Author papers -asta papers author search "Author Name" -asta papers author papers --limit 50 -``` - -Tips: -- Use smaller limits (10-20) for initial searches -- Recommended fields: `title,abstract,year,authors,venue,citationCount` -- Use `--format json` for piping to jq, `--format text` for quick viewing - -### 7. Update Report Progressively - -As you gather information: -- Add papers to References section immediately -- Fill in section content as themes emerge -- Update Executive Summary as understanding develops -- Use proper citation format (see below) - -**Do not wait until the end to write everything.** Edit progressively. - -### 8. Ensure Completeness Before Finishing - -Before completing: -- If launched background searches: All searches must finish (check for pending ``) -- All sections must be filled in -- All in-text citations must match references -- Executive summary reflects key findings -- Add generation date at bottom - -## JSON Structure - -Paper finder results use the `LiteratureSearchResult` format (see **Find Literature** skill for complete structure documentation). - -Key fields for report generation: -- `.query` - the search query -- `.results[]` - array of papers sorted by relevance -- `.results[].relevanceScore` - 0-1 relevance score -- `.results[].relevanceJudgement.relevanceSummary` - AI explanation -- `.results[].relevanceJudgement.relevanceCriteriaJudgements[]` - per-concept evidence -- `.results[].snippets[]` - text excerpts from paper body -- `.results[].citationContexts[]` - citation contexts from other papers - -## Citation Format - -Use citation keys with link definitions for clickable citations: - -**Inline citations**: -```markdown -This was demonstrated by [[Yao2024]]. -``` - -**References section**: -```markdown -## References - -- [[Yao2024]] Yao, S., et al. (2024). τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains. arXiv. -- [[Dou2025]] Dou, Z., et al. (2025). Another Paper Title. Venue Name. -``` - -**Link definitions** (at end of file): -```markdown -[Yao2024]: https://semanticscholar.org/p/270218537 -[Dou2025]: https://semanticscholar.org/p/123456789 -``` +### 4. Confirm and Share -This makes `[[Yao2024]]` render as `[Yao2024]` with visible brackets and clickable links to Semantic Scholar. +- Read the saved report file and share the key findings with the user +- Tell the user the file path so they can open it -## Quality Standards +## Error Handling -- **Comprehensive**: Cover all major themes and key papers -- **Evidence-based**: Support claims with paper evidence and citations -- **Synthesized**: Don't just list papers - connect ideas across papers -- **Well-structured**: Clear sections, logical flow -- **Properly cited**: All claims traced to sources -- **Up-to-date**: Focus on recent work when relevant +- **Authentication error**: Run `asta auth login` and retry +- **Timeout**: Pass a larger `--timeout` value (default is 360s) +- **No papers found**: Suggest refining the query and re-running `asta literature find` diff --git a/skills/literature-report/SKILL.md b/skills/literature-report/SKILL.md index abf7049..ac1ad83 100644 --- a/skills/literature-report/SKILL.md +++ b/skills/literature-report/SKILL.md @@ -5,18 +5,14 @@ metadata: internal: true allowed-tools: - Bash(asta literature find *) - - Bash(asta papers *) - - Write(.asta/literature/report/*) - - Edit(.asta/literature/report/*) + - Bash(asta literature report *) - Read(.asta/literature/*) - - Read(*.json) - TaskOutput - - Bash(jq *) --- # Literature Report Generation -Generate comprehensive literature reports by searching academic literature, analyzing papers, and synthesizing findings into a well-structured markdown document. +Generate comprehensive literature reports by finding relevant papers and synthesizing them into a structured written report. ## When to Use This Skill @@ -31,166 +27,36 @@ Generate comprehensive literature reports by searching academic literature, anal ### 1. Check for Existing Results **If the user provides an existing paper-finder results file:** -- Ask them to confirm the file path -- Use Read tool to verify the file exists and is valid JSON -- Skip to step 3 to process those results directly +- Skip to step 3, using that file as `-i` **If no existing results file:** -- Proceed to launch a new search below +- Proceed to step 2 -### 2. Launch Background Searches (if needed) +### 2. Find Papers -Start with the paper finder in background (30-60s): ```bash -# Uses default location .asta/literature/find/YYYY-MM-DD-HH-MM-SS-{query-slug}.json -Bash(command="asta literature find 'query' --timeout 300", run_in_background=true) - -# Or specify a custom output location -Bash(command="asta literature find 'query' --timeout 300 -o custom-results.json", run_in_background=true) +asta literature find 'query' --timeout 300 -o .asta/literature/find/YYYY-MM-DD-topic-slug.json ``` -Launch additional targeted CLI searches as needed while paper finder runs. - -### 3. Create Report File Immediately - -Don't wait for searches to complete. Create the report file early with: -- Default file path: `.asta/literature/report/YYYY-MM-DD-topic-slug.md` (in current working directory) -- Custom path: User can specify a different location if desired -- Initial structure: Title, Executive Summary (placeholder), section headings, References - -This keeps the user informed and avoids losing work. - -### 4. Process Paper Finder Results - -**If using existing results file:** -- Use the file path provided by the user -- Read it with the Read tool to understand the query and papers +Note the output path from the command. -**If launched a new search:** -- When `` arrives, use TaskOutput to get the results: -```bash -TaskOutput(task_id="") -``` -- The results file will be at `.asta/literature/find/` (check TaskOutput for the exact filename), or at the path you specified with `-o` +### 3. Generate the Report -Browse results with jq (sorted by relevance): ```bash -# Replace RESULTS_FILE with the actual path from TaskOutput or your custom path -jq '[.results | sort_by(-.relevanceScore) | .[0:10][] | {title, year, venue, corpusId, citationCount, score: .relevanceScore, authors: (.authors | if length <= 4 then [.[].name] | join(", ") else ([.[0:3][].name] | join(", ")) + ", +" + (length - 4 | tostring) + ", " + .[-1].name end), summary: .relevanceJudgement.relevanceSummary}]' RESULTS_FILE +asta literature report \ + -i .asta/literature/find/YYYY-MM-DD-topic-slug.json \ + -o .asta/literature/report/YYYY-MM-DD-topic-slug.md ``` -**Important**: Go through ALL highly relevant papers (not just top few). Extract: -- Relevance criteria and evidence -- Snippets from paper body -- Citation contexts (how others cite this work) - -### 5. Deep Dive on Key Papers +The command prints progress to stderr and the report path when done. -For each important paper, extract detailed evidence: - -**Relevance evidence**: -```bash -jq '.results[] | select(.corpusId == CORPUS_ID) | {title, criteria: [.relevanceJudgement.relevanceCriteriaJudgements[] | {concept: .name, relevance, evidence: [.relevantSnippets[].text]}]}' RESULTS_FILE -``` - -**Abstract and passages**: -```bash -jq '.results[] | select(.corpusId == CORPUS_ID) | {title, abstract, snippets: [.snippets[]? | {section: .sectionTitle, text}]}' RESULTS_FILE -``` - -**Citation contexts** (how others describe this work): -```bash -jq '[.results[] | select(.corpusId == CORPUS_ID) | .citationContexts[].text]' RESULTS_FILE -``` - -Replace `RESULTS_FILE` with the actual results file path (found in `.asta/literature/find/`, user-provided, or from TaskOutput). - -### 6. Use CLI Tools for Targeted Research - -Supplement paper finder with fast, targeted searches using `asta papers`: - -```bash -# Search for papers -asta papers search "keyword" --year 2023- --limit 20 --fields title,abstract,year,authors - -# Get paper details -asta papers get ARXIV:2005.14165 --fields title,abstract,authors,year - -# Get citations -asta papers citations CorpusId:12345 --limit 50 - -# Author papers -asta papers author search "Author Name" -asta papers author papers --limit 50 -``` - -Tips: -- Use smaller limits (10-20) for initial searches -- Recommended fields: `title,abstract,year,authors,venue,citationCount` -- Use `--format json` for piping to jq, `--format text` for quick viewing - -### 7. Update Report Progressively - -As you gather information: -- Add papers to References section immediately -- Fill in section content as themes emerge -- Update Executive Summary as understanding develops -- Use proper citation format (see below) - -**Do not wait until the end to write everything.** Edit progressively. - -### 8. Ensure Completeness Before Finishing - -Before completing: -- If launched background searches: All searches must finish (check for pending ``) -- All sections must be filled in -- All in-text citations must match references -- Executive summary reflects key findings -- Add generation date at bottom - -## JSON Structure - -Paper finder results use the `LiteratureSearchResult` format (see **Find Literature** skill for complete structure documentation). - -Key fields for report generation: -- `.query` - the search query -- `.results[]` - array of papers sorted by relevance -- `.results[].relevanceScore` - 0-1 relevance score -- `.results[].relevanceJudgement.relevanceSummary` - AI explanation -- `.results[].relevanceJudgement.relevanceCriteriaJudgements[]` - per-concept evidence -- `.results[].snippets[]` - text excerpts from paper body -- `.results[].citationContexts[]` - citation contexts from other papers - -## Citation Format - -Use citation keys with link definitions for clickable citations: - -**Inline citations**: -```markdown -This was demonstrated by [[Yao2024]]. -``` - -**References section**: -```markdown -## References - -- [[Yao2024]] Yao, S., et al. (2024). τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains. arXiv. -- [[Dou2025]] Dou, Z., et al. (2025). Another Paper Title. Venue Name. -``` - -**Link definitions** (at end of file): -```markdown -[Yao2024]: https://semanticscholar.org/p/270218537 -[Dou2025]: https://semanticscholar.org/p/123456789 -``` +### 4. Confirm and Share -This makes `[[Yao2024]]` render as `[Yao2024]` with visible brackets and clickable links to Semantic Scholar. +- Read the saved report file and share the key findings with the user +- Tell the user the file path so they can open it -## Quality Standards +## Error Handling -- **Comprehensive**: Cover all major themes and key papers -- **Evidence-based**: Support claims with paper evidence and citations -- **Synthesized**: Don't just list papers - connect ideas across papers -- **Well-structured**: Clear sections, logical flow -- **Properly cited**: All claims traced to sources -- **Up-to-date**: Focus on recent work when relevant +- **Authentication error**: Run `asta auth login` and retry +- **Timeout**: Pass a larger `--timeout` value (default is 360s) +- **No papers found**: Suggest refining the query and re-running `asta literature find` diff --git a/src/asta/cli.py b/src/asta/cli.py index 9884c70..22bb058 100644 --- a/src/asta/cli.py +++ b/src/asta/cli.py @@ -7,6 +7,7 @@ from asta.documents import documents from asta.experiment import experiment from asta.literature.find import find +from asta.literature.report import report from asta.papers.author import author from asta.papers.citations import citations from asta.papers.get import get @@ -46,6 +47,7 @@ def papers(): # Register literature subcommands literature.add_command(find) +literature.add_command(report) # Register papers subcommands papers.add_command(get) diff --git a/src/asta/literature/report.py b/src/asta/literature/report.py new file mode 100644 index 0000000..8e323db --- /dev/null +++ b/src/asta/literature/report.py @@ -0,0 +1,132 @@ +"""Generate literature report command""" + +import json +from pathlib import Path + +import click + +from asta.literature.models import LiteratureSearchResult +from asta.literature.report_client import NoraReportClient + + +def _papers_to_documents(papers: list, max_papers: int) -> list[dict]: + """Convert Paper model instances to the Document dicts expected by the API.""" + sorted_papers = sorted(papers, key=lambda p: p.relevanceScore, reverse=True) + selected = sorted_papers[:max_papers] + + documents = [] + for paper in selected: + doc: dict = { + "corpus_id": str(paper.corpusId), + "title": paper.title, + "abstract": paper.abstract or "", + "authors": [{"name": a.name} for a in paper.authors], + "snippets": [ + {"text": s.text, "section_title": s.sectionTitle} + for s in paper.snippets + ], + "year": paper.year, + "venue": paper.venue, + "relevance_judgement": paper.relevanceScore, + "final_agent_score": paper.relevanceScore, + "citation_count": paper.citationCount, + } + documents.append(doc) + + return documents + + +def _format_report(response: dict, query: str) -> str: + """Render a TaskResult as a markdown document.""" + title = response.get("report_title") or query or "Literature Report" + sections = response.get("sections", []) + + lines: list[str] = [f"# {title}", ""] + for section in sections: + if section.get("title"): + lines.append(f"## {section['title']}") + lines.append("") + if section.get("text"): + lines.append(section["text"]) + lines.append("") + + return "\n".join(lines) + + +@click.command() +@click.option( + "-i", + "--input", + "input_file", + type=click.Path(exists=True, dir_okay=False), + required=True, + help="Paper-finder results JSON file (from 'asta literature find')", +) +@click.option( + "-o", + "--output", + type=click.Path(dir_okay=False), + required=True, + help="Output file path for the generated report (markdown)", +) +@click.option( + "--query", + default=None, + help="Override the research query (defaults to the query stored in the results file)", +) +@click.option( + "--max-papers", + type=int, + default=20, + show_default=True, + help="Maximum number of papers to include (highest-relevance first)", +) +def report( + input_file: str, + output: str, + query: str | None, + max_papers: int, +): + """Generate a literature report from paper-finder results. + + Reads a results file produced by 'asta literature find', sends the papers + to the Nora report generation API, and writes a markdown summary. + + Examples: + + # Generate report from an existing results file + asta literature report -i results.json -o report.md + + # Override the query used as the report title/framing + asta literature report -i results.json -o report.md --query "transformers for NLP" + + # Include more papers + asta literature report -i results.json -o report.md --max-papers 30 + """ + try: + with open(input_file) as f: + raw = json.load(f) + + search_result = LiteratureSearchResult.model_validate(raw) + effective_query = query or search_result.query + + click.echo( + f"Loaded {len(search_result.results)} papers from {input_file}", err=True + ) + + documents = _papers_to_documents(search_result.results, max_papers) + click.echo(f"Sending {len(documents)} papers to report API…", err=True) + + client = NoraReportClient() + response = client.generate_report(documents=documents, query=effective_query) + + markdown = _format_report(response, effective_query) + + output_path = Path(output) + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text(markdown) + + click.echo(f"Report saved to: {output_path}", err=True) + + except Exception as e: + raise click.ClickException(str(e)) diff --git a/src/asta/literature/report_client.py b/src/asta/literature/report_client.py new file mode 100644 index 0000000..71c8449 --- /dev/null +++ b/src/asta/literature/report_client.py @@ -0,0 +1,81 @@ +"""Nora Report Generation API client""" + +import json +import urllib.error +import urllib.request +from typing import Any + +from asta.utils.auth_helper import get_access_token +from asta.utils.config import get_api_config, get_config_path + + +class NoraReportClient: + """Client for the Nora report generation API.""" + + def __init__(self, base_url: str | None = None, access_token: str | None = None): + if base_url is None: + try: + config = get_api_config("report") + base_url = config.get("base_url") + except (KeyError, FileNotFoundError): + base_url = None + + if not base_url: + raise ValueError( + f"No value for apis.report.base_url in {get_config_path()}. " + "base_url is required. Either provide it as a parameter or configure it in asta.conf" + ) + + if access_token is None: + access_token = get_access_token() + + self.base_url = base_url.rstrip("/") + self.access_token = access_token + self.headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {self.access_token}", + } + + def _request(self, data: dict) -> dict[str, Any]: + """POST to the generate-report endpoint and return parsed JSON.""" + url = f"{self.base_url}/generate-report" + body = json.dumps(data).encode() + req = urllib.request.Request(url, data=body, headers=self.headers, method="POST") + try: + with urllib.request.urlopen(req) as resp: + return json.loads(resp.read()) + except urllib.error.HTTPError as e: + error_body = e.read().decode("utf-8") + try: + error_msg = json.loads(error_body).get("detail", str(e)) + except json.JSONDecodeError: + error_msg = error_body or str(e) + raise Exception(f"API request failed ({e.code}): {error_msg}") from e + + def generate_report( + self, + documents: list[dict], + query: str = "", + ) -> dict[str, Any]: + """Generate a literature report from pre-retrieved documents. + + Args: + documents: List of document dicts matching the GenerateReportRequest + Document schema (corpus_id, title, abstract, authors, snippets, + and optional metadata fields). + query: The research question / topic for the report. + + Returns: + A TaskResult dict with report_title, sections, report_id, event_id, + and cost. + + Raises: + Exception: On API errors. + """ + payload = { + "query": query, + "documents": documents, + "truncated_result": False, + } + + return self._request(payload) diff --git a/src/asta/utils/asta.conf b/src/asta/utils/asta.conf index d28cb9b..5cf4dde 100644 --- a/src/asta/utils/asta.conf +++ b/src/asta/utils/asta.conf @@ -38,6 +38,11 @@ apis { base_url = ${auth.gateway_url}"/api/olmocr" model = "allenai/olmocr-2-7b-1025" } + + # Nora report generation API + report { + base_url = ${auth.gateway_url}"/api/corpus-qa" + } } # Passthrough command configurations diff --git a/tests/test_cli.py b/tests/test_cli.py index 08f779b..8a1b475 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -200,6 +200,184 @@ def test_find_with_mode_option(self, runner, tmp_path): ) +class TestReportCommand: + """Test 'asta literature report' command.""" + + RESULTS_JSON = { + "query": "transformers for NLP", + "results": [ + { + "corpus_id": 111, + "title": "Attention Is All You Need", + "abstract": "We propose a new network architecture, the Transformer.", + "year": 2017, + "authors": [{"name": "Vaswani, A.", "authorId": "1"}], + "relevance_score": 0.95, + "snippets": [{"text": "The Transformer follows an encoder-decoder structure."}], + }, + { + "corpus_id": 222, + "title": "BERT: Pre-training of Deep Bidirectional Transformers", + "abstract": "We introduce BERT.", + "year": 2019, + "authors": [{"name": "Devlin, J.", "authorId": "2"}], + "relevance_score": 0.90, + "snippets": [], + }, + ], + } + + API_RESPONSE = { + "report_title": "Transformers for NLP", + "sections": [ + {"title": "Introduction", "text": "Transformers have revolutionized NLP."}, + {"title": "Key Works", "text": "Vaswani et al. introduced the Transformer."}, + ], + "report_id": "report-abc-123", + "cost": 0.01, + } + + def test_report_missing_input(self, runner, tmp_path): + """Test report command fails without --input.""" + output_file = tmp_path / "report.md" + result = runner.invoke(cli, ["literature", "report", "-o", str(output_file)]) + assert result.exit_code != 0 + + def test_report_missing_output(self, runner, tmp_path): + """Test report command fails without --output.""" + input_file = tmp_path / "results.json" + input_file.write_text(json.dumps(self.RESULTS_JSON)) + result = runner.invoke(cli, ["literature", "report", "-i", str(input_file)]) + assert result.exit_code != 0 + + def test_report_success(self, runner, tmp_path): + """Test successful report generation writes markdown file.""" + input_file = tmp_path / "results.json" + input_file.write_text(json.dumps(self.RESULTS_JSON)) + output_file = tmp_path / "report.md" + + with patch("asta.literature.report.NoraReportClient") as MockClient: + mock_instance = MagicMock() + mock_instance.generate_report.return_value = self.API_RESPONSE + MockClient.return_value = mock_instance + + result = runner.invoke( + cli, + ["literature", "report", "-i", str(input_file), "-o", str(output_file)], + ) + + assert result.exit_code == 0, result.output + assert output_file.exists() + content = output_file.read_text() + assert "# Transformers for NLP" in content + assert "## Introduction" in content + assert "Transformers have revolutionized NLP." in content + + def test_report_passes_query_from_results(self, runner, tmp_path): + """Test that the query from the results file is passed to the client.""" + input_file = tmp_path / "results.json" + input_file.write_text(json.dumps(self.RESULTS_JSON)) + output_file = tmp_path / "report.md" + + with patch("asta.literature.report.NoraReportClient") as MockClient: + mock_instance = MagicMock() + mock_instance.generate_report.return_value = self.API_RESPONSE + MockClient.return_value = mock_instance + + runner.invoke( + cli, + ["literature", "report", "-i", str(input_file), "-o", str(output_file)], + ) + + mock_instance.generate_report.assert_called_once() + call_kwargs = mock_instance.generate_report.call_args[1] + assert call_kwargs["query"] == "transformers for NLP" + + def test_report_query_override(self, runner, tmp_path): + """Test --query overrides the query from the results file.""" + input_file = tmp_path / "results.json" + input_file.write_text(json.dumps(self.RESULTS_JSON)) + output_file = tmp_path / "report.md" + + with patch("asta.literature.report.NoraReportClient") as MockClient: + mock_instance = MagicMock() + mock_instance.generate_report.return_value = self.API_RESPONSE + MockClient.return_value = mock_instance + + runner.invoke( + cli, + [ + "literature", + "report", + "-i", + str(input_file), + "-o", + str(output_file), + "--query", + "custom query", + ], + ) + + call_kwargs = mock_instance.generate_report.call_args[1] + assert call_kwargs["query"] == "custom query" + + def test_report_max_papers_option(self, runner, tmp_path): + """Test --max-papers limits the number of documents sent.""" + input_file = tmp_path / "results.json" + input_file.write_text(json.dumps(self.RESULTS_JSON)) + output_file = tmp_path / "report.md" + + with patch("asta.literature.report.NoraReportClient") as MockClient: + mock_instance = MagicMock() + mock_instance.generate_report.return_value = self.API_RESPONSE + MockClient.return_value = mock_instance + + runner.invoke( + cli, + [ + "literature", + "report", + "-i", + str(input_file), + "-o", + str(output_file), + "--max-papers", + "1", + ], + ) + + call_kwargs = mock_instance.generate_report.call_args[1] + assert len(call_kwargs["documents"]) == 1 + # Should be the highest-relevance paper + assert call_kwargs["documents"][0]["corpus_id"] == "111" + + def test_report_api_error(self, runner, tmp_path): + """Test report command exits with code 1 on API error.""" + input_file = tmp_path / "results.json" + input_file.write_text(json.dumps(self.RESULTS_JSON)) + output_file = tmp_path / "report.md" + + with patch("asta.literature.report.NoraReportClient") as MockClient: + mock_instance = MagicMock() + mock_instance.generate_report.side_effect = Exception("API error") + MockClient.return_value = mock_instance + + result = runner.invoke( + cli, + ["literature", "report", "-i", str(input_file), "-o", str(output_file)], + ) + + assert result.exit_code == 1 + + def test_report_config(self): + """Test that the report API is configured in asta.conf.""" + from asta.utils.config import get_api_config + + config = get_api_config("report") + assert "base_url" in config + assert "/api/corpus-qa" in config["base_url"] + + class TestPassthroughUtility: """Test generic passthrough utility functions.""" diff --git a/uv.lock b/uv.lock index 8a1247c..7e6f582 100644 --- a/uv.lock +++ b/uv.lock @@ -26,7 +26,7 @@ wheels = [ [[package]] name = "asta" -version = "0.8.1" +version = "0.8.2" source = { editable = "." } dependencies = [ { name = "click" }, From 241ab7d3399e489d868ae283255b9639f8d7d082 Mon Sep 17 00:00:00 2001 From: Rodney Kinney Date: Mon, 23 Mar 2026 10:24:19 -0700 Subject: [PATCH 2/3] update version --- .claude-plugin/marketplace.json | 2 +- hooks/sync-cli-version.sh | 2 +- plugins/asta-preview/hooks/sync-cli-version.sh | 2 +- plugins/asta-preview/skills/experiment/SKILL.md | 2 +- plugins/asta-preview/skills/find-literature/SKILL.md | 2 +- plugins/asta-preview/skills/pdf-extraction/SKILL.md | 2 +- plugins/asta-preview/skills/semantic-scholar/SKILL.md | 2 +- plugins/asta/hooks/sync-cli-version.sh | 2 +- plugins/asta/skills/pdf-extraction/SKILL.md | 2 +- plugins/asta/skills/semantic-scholar/SKILL.md | 2 +- pyproject.toml | 2 +- skills/experiment/SKILL.md | 2 +- skills/find-literature/SKILL.md | 2 +- skills/pdf-extraction/SKILL.md | 2 +- skills/semantic-scholar/SKILL.md | 2 +- src/asta/__init__.py | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index a2a506f..7d8e497 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ "name": "asta", "source": "./plugins/asta", "description": "Paper search, metadata, and document management for scientific research", - "version": "0.8.2", + "version": "0.9.0", "author": { "name": "AI2 Asta Team" }, diff --git a/hooks/sync-cli-version.sh b/hooks/sync-cli-version.sh index 53f00d5..d617114 100755 --- a/hooks/sync-cli-version.sh +++ b/hooks/sync-cli-version.sh @@ -1,7 +1,7 @@ #!/bin/bash # Check if asta CLI version matches the plugin version and auto-install/update if needed -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 INSTALL_URL="git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION" # Check if asta is installed diff --git a/plugins/asta-preview/hooks/sync-cli-version.sh b/plugins/asta-preview/hooks/sync-cli-version.sh index 53f00d5..d617114 100755 --- a/plugins/asta-preview/hooks/sync-cli-version.sh +++ b/plugins/asta-preview/hooks/sync-cli-version.sh @@ -1,7 +1,7 @@ #!/bin/bash # Check if asta CLI version matches the plugin version and auto-install/update if needed -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 INSTALL_URL="git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION" # Check if asta is installed diff --git a/plugins/asta-preview/skills/experiment/SKILL.md b/plugins/asta-preview/skills/experiment/SKILL.md index 276dae9..6b3a961 100644 --- a/plugins/asta-preview/skills/experiment/SKILL.md +++ b/plugins/asta-preview/skills/experiment/SKILL.md @@ -22,7 +22,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/plugins/asta-preview/skills/find-literature/SKILL.md b/plugins/asta-preview/skills/find-literature/SKILL.md index ae34dd2..95380f2 100644 --- a/plugins/asta-preview/skills/find-literature/SKILL.md +++ b/plugins/asta-preview/skills/find-literature/SKILL.md @@ -23,7 +23,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/plugins/asta-preview/skills/pdf-extraction/SKILL.md b/plugins/asta-preview/skills/pdf-extraction/SKILL.md index 08896ce..a74c75b 100644 --- a/plugins/asta-preview/skills/pdf-extraction/SKILL.md +++ b/plugins/asta-preview/skills/pdf-extraction/SKILL.md @@ -20,7 +20,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/plugins/asta-preview/skills/semantic-scholar/SKILL.md b/plugins/asta-preview/skills/semantic-scholar/SKILL.md index 85c33d1..4befa17 100644 --- a/plugins/asta-preview/skills/semantic-scholar/SKILL.md +++ b/plugins/asta-preview/skills/semantic-scholar/SKILL.md @@ -25,7 +25,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/plugins/asta/hooks/sync-cli-version.sh b/plugins/asta/hooks/sync-cli-version.sh index 53f00d5..d617114 100755 --- a/plugins/asta/hooks/sync-cli-version.sh +++ b/plugins/asta/hooks/sync-cli-version.sh @@ -1,7 +1,7 @@ #!/bin/bash # Check if asta CLI version matches the plugin version and auto-install/update if needed -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 INSTALL_URL="git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION" # Check if asta is installed diff --git a/plugins/asta/skills/pdf-extraction/SKILL.md b/plugins/asta/skills/pdf-extraction/SKILL.md index 08896ce..a74c75b 100644 --- a/plugins/asta/skills/pdf-extraction/SKILL.md +++ b/plugins/asta/skills/pdf-extraction/SKILL.md @@ -20,7 +20,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/plugins/asta/skills/semantic-scholar/SKILL.md b/plugins/asta/skills/semantic-scholar/SKILL.md index 85c33d1..4befa17 100644 --- a/plugins/asta/skills/semantic-scholar/SKILL.md +++ b/plugins/asta/skills/semantic-scholar/SKILL.md @@ -25,7 +25,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/pyproject.toml b/pyproject.toml index b2e1f6e..b51c0bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "asta" -version = "0.8.2" +version = "0.9.0" description = "Asta CLI for scientific literature review" readme = "README.md" requires-python = ">=3.11" diff --git a/skills/experiment/SKILL.md b/skills/experiment/SKILL.md index 276dae9..6b3a961 100644 --- a/skills/experiment/SKILL.md +++ b/skills/experiment/SKILL.md @@ -22,7 +22,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/skills/find-literature/SKILL.md b/skills/find-literature/SKILL.md index ae34dd2..95380f2 100644 --- a/skills/find-literature/SKILL.md +++ b/skills/find-literature/SKILL.md @@ -23,7 +23,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/skills/pdf-extraction/SKILL.md b/skills/pdf-extraction/SKILL.md index 08896ce..a74c75b 100644 --- a/skills/pdf-extraction/SKILL.md +++ b/skills/pdf-extraction/SKILL.md @@ -20,7 +20,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/skills/semantic-scholar/SKILL.md b/skills/semantic-scholar/SKILL.md index 85c33d1..4befa17 100644 --- a/skills/semantic-scholar/SKILL.md +++ b/skills/semantic-scholar/SKILL.md @@ -25,7 +25,7 @@ This skill requires the `asta` CLI: ```bash # Install/reinstall at the correct version -PLUGIN_VERSION=0.8.2 +PLUGIN_VERSION=0.9.0 if [ "$(asta --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" != "$PLUGIN_VERSION" ]; then uv tool install --force git+https://github.com/allenai/asta-plugins.git@v$PLUGIN_VERSION fi diff --git a/src/asta/__init__.py b/src/asta/__init__.py index 7f9229f..11fde81 100644 --- a/src/asta/__init__.py +++ b/src/asta/__init__.py @@ -1,3 +1,3 @@ """Asta - Science literature research tools""" -__version__ = "0.8.2" +__version__ = "0.9.0" From 8d8591a0124a8f887fe2227dd0d01b3b5d8ace64 Mon Sep 17 00:00:00 2001 From: Rodney Kinney Date: Mon, 6 Apr 2026 11:46:49 -0700 Subject: [PATCH 3/3] Refactor literature-report skill --- skills/literature-report/SKILL.md | 103 +++++++----------- .../references/citation-conventions.md | 37 +++++++ .../workflows/convert-and-report.md | 81 ++++++++++++++ .../workflows/find-and-report.md | 50 +++++++++ .../workflows/generate-report.md | 39 +++++++ .../workflows/merge-report.md | 64 +++++++++++ 6 files changed, 310 insertions(+), 64 deletions(-) create mode 100644 skills/literature-report/references/citation-conventions.md create mode 100644 skills/literature-report/workflows/convert-and-report.md create mode 100644 skills/literature-report/workflows/find-and-report.md create mode 100644 skills/literature-report/workflows/generate-report.md create mode 100644 skills/literature-report/workflows/merge-report.md diff --git a/skills/literature-report/SKILL.md b/skills/literature-report/SKILL.md index c9d9c66..037636e 100644 --- a/skills/literature-report/SKILL.md +++ b/skills/literature-report/SKILL.md @@ -4,81 +4,56 @@ description: Create or update literature reviews/reports. Use whenever you need allowed-tools: - Bash(asta literature find *) - Bash(asta literature report *) + - Bash(asta papers get *) + - Bash(jq *) + - Bash(mkdir *) - Read(.asta/literature/*) + - Read(*) - TaskOutput --- -# Literature Reports + +Generate comprehensive literature reports by finding relevant papers and synthesizing them into structured written reports with citations. Route to the correct workflow based on what the user already has. + -Generate comprehensive literature reports by finding relevant papers and synthesizing them into a structured written report with citations. + +- **Check before searching.** Always check `.asta/literature/find/` for existing search results before running new searches. +- **Reports are user-facing.** New reports go in user-visible locations (e.g., `docs/`, project root), never inside `.asta/`. +- **Read before updating.** When updating an existing report, read it first to understand structure and content. +- **Exhaust the evidence.** When using find-literature results, go through ALL highly relevant papers — extract relevance criteria, snippets, and citation contexts from each. +- **Delegate search to the right tool.** Use the **Find Literature** skill for comprehensive searches. Use the **Semantic Scholar Lookup** skill for targeted queries (specific papers, citations, author searches). + -## Finding Papers + +Determine which workflow to follow based on what the user has: -- Check `.asta/literature/find/` for existing search results before running new searches. -- Use the **Find Literature** skill for comprehensive searches and for the result schema and jq access patterns. -- Use the **Semantic Scholar Lookup** skill for targeted queries: specific papers, citations, and author searches. +**Route A — No papers yet** → The user wants a literature report but hasn't searched yet. +Read `workflows/find-and-report.md`. -## Workflow Guidance +**Route B — Has a find-literature result** → The user provides or references an existing `.json` results file from `asta literature find`. +Read `workflows/generate-report.md`. -- **Identify or create the report file first**. Check if there's an existing report to update. New reports go in user-visible locations (e.g., `docs/`), not inside `.asta/`. -- **When updating an existing report**, read it first to understand the current structure and content. -- **When using Find Literature results**, go through ALL highly relevant papers — extract relevance criteria, snippets, and citation contexts from each. +**Route C — Has papers in another form** → The user has a collection of papers as a list of titles, DOIs, arXiv IDs, URLs, a BibTeX file, or any non-find-literature format. +Read `workflows/convert-and-report.md`. -**If the user provides an existing paper-finder results file:** -- Skip to step 3, using that file as `-i` +**Route D — Merge into existing report** → The user has an existing report and wants to incorporate new papers or findings into it. +Read `workflows/merge-report.md`. -**If no existing results file:** -- Proceed to step 2 +When the user's intent maps to multiple routes (e.g., "find papers on X and add them to my existing report"), chain the relevant workflows in sequence. + -### 2. Find Papers + +**Search results:** `.asta/literature/find/YYYY-MM-DD-topic-slug.json` +**Generated reports:** `.asta/literature/report/YYYY-MM-DD-topic-slug.md` +**User-facing reports:** User-specified path or `docs/` directory + -```bash -asta literature find 'query' --timeout 300 -o .asta/literature/find/YYYY-MM-DD-topic-slug.json -``` + +Read `references/citation-conventions.md` when the report requires formal citations with a `.bib` file and `[@key]` references. + -Note the output path from the command. - -### 3. Generate the Report - -```bash -asta literature report \ - -i .asta/literature/find/YYYY-MM-DD-topic-slug.json \ - -o .asta/literature/report/YYYY-MM-DD-topic-slug.md -``` - -The command prints progress to stderr and the report path when done. - -### 4. Confirm and Share - -- Read the saved report file and share the key findings with the user -- Tell the user the file path so they can open it - -## Citation Conventions - -**YAML frontmatter** — required for citation resolution: -```yaml ---- -bibliography: ../references.bib ---- -``` - -**Citations**: Use `[@key]` format. Every key must exist in the `.bib` file — unresolved keys won't render. - -**Adding papers to `.bib`**: As you cite a paper, fetch its metadata and append a BibTeX entry: - -```bash -asta papers get CorpusId: --fields title,authors,year,venue,externalIds,journal,publicationDate -``` - -Generate a BibTeX entry from the result. Conventions: -- **Key**: `{firstAuthorSurname}{year}` lowercased, ASCII only (e.g., `wei2022`). Append a letter for conflicts (`wei2022a`). -- **`corpus_id`**: Include `corpus_id = {}` for stable identification (survives key renames). -- **Append** to the `.bib` — never rewrite existing entries. - -**Verification**: Use the **Preview** skill to render and verify all citations resolve. - -## Error Handling - -- **Authentication error**: Run `asta auth login` and retry -- **Timeout**: Pass a larger `--timeout` value (default is 360s) -- **No papers found**: Suggest refining the query and re-running `asta literature find` + +- **Authentication error** → Run `asta auth login` and retry. +- **Timeout** → Pass a larger `--timeout` value (default is 360s). +- **No papers found** → Suggest refining the query and re-running `asta literature find`. + diff --git a/skills/literature-report/references/citation-conventions.md b/skills/literature-report/references/citation-conventions.md new file mode 100644 index 0000000..66322d3 --- /dev/null +++ b/skills/literature-report/references/citation-conventions.md @@ -0,0 +1,37 @@ +# Citation Conventions + +Follow these conventions when a report requires formal citations with a `.bib` file and `[@key]` Pandoc-style references. + +## YAML Frontmatter + +Every report with citations must include: + +```yaml +--- +bibliography: ../references.bib +--- +``` + +Adjust the relative path to point to the `.bib` file from the report's location. + +## Citation Format + +Use `[@key]` inline. Every key must exist in the `.bib` file — unresolved keys won't render. + +## Adding Papers to the `.bib` File + +As you cite a paper, fetch its metadata and append a BibTeX entry: + +```bash +asta papers get CorpusId: --fields title,authors,year,venue,externalIds,journal,publicationDate +``` + +Generate a BibTeX entry from the result. Conventions: + +- **Key format**: `{firstAuthorSurname}{year}` lowercased, ASCII only (e.g., `wei2022`). Append a letter for conflicts (`wei2022a`, `wei2022b`). +- **`corpus_id` field**: Always include `corpus_id = {}` for stable identification (survives key renames). +- **Append only**: Add new entries to the end of the `.bib` file. Never rewrite or remove existing entries. + +## Verification + +Use the **Preview** skill to render the report and verify all citations resolve. Unresolved `[@key]` references appear as literal text in the output — scan for any that didn't render as proper citations. diff --git a/skills/literature-report/workflows/convert-and-report.md b/skills/literature-report/workflows/convert-and-report.md new file mode 100644 index 0000000..c69d4f7 --- /dev/null +++ b/skills/literature-report/workflows/convert-and-report.md @@ -0,0 +1,81 @@ +# Convert External Papers to Find-Literature Format + +Use when the user has papers in a non-standard form — titles, DOIs, arXiv IDs, URLs, a BibTeX file, CSV, or any other format — and wants a literature report from them. + + + +## Step 1: Identify the papers + +Determine what the user has provided. Common forms: +- A list of paper titles +- DOIs or arXiv IDs +- Semantic Scholar corpus IDs or URLs +- A `.bib` file +- A CSV or spreadsheet +- Inline references in a document + +## Step 2: Resolve each paper via Semantic Scholar + +For each paper, look it up to get full metadata: + +```bash +# By arXiv ID +asta papers get ARXIV:2005.14165 --fields title,abstract,year,authors,venue,citationCount,corpusId + +# By DOI +asta papers get DOI:10.1234/example --fields title,abstract,year,authors,venue,citationCount,corpusId + +# By corpus ID +asta papers get CorpusId:12345 --fields title,abstract,year,authors,venue,citationCount,corpusId + +# By title search (when only title is available) +asta papers search "exact paper title" --limit 5 --fields title,abstract,year,authors,venue,citationCount,corpusId +``` + +For title searches, verify the match is correct by checking authors and year. + +## Step 3: Build the results JSON + +Construct a `LiteratureSearchResult` JSON file matching the find-literature schema: + +```json +{ + "query": "", + "results": [ + { + "corpus_id": 12345, + "title": "Paper Title", + "abstract": "...", + "year": 2023, + "authors": [{"name": "Author Name", "authorId": "123"}], + "venue": "Conference Name", + "citation_count": 50, + "relevance_score": 0.9, + "relevance_judgement": { + "relevance": 9, + "relevanceSummary": "Brief explanation of why this paper is relevant", + "relevanceCriteriaJudgements": [] + }, + "snippets": [], + "citation_contexts": [] + } + ] +} +``` + +Key points: +- Set `relevance_score` between 0 and 1 based on your judgement of relevance to the user's topic. If no clear ranking, use 0.8 for all. +- The `query` field should describe the research topic, not just repeat the user's input. +- `snippets` and `citation_contexts` will be empty since we don't have full-text access — this is fine, the report will rely on abstracts. + +Save to: `.asta/literature/find/YYYY-MM-DD-topic-slug.json` + +## Step 4: Generate the report + +Now follow `workflows/generate-report.md` using the converted results file. + + + + +If the user has a large collection (20+ papers), consider whether `asta literature find` with a well-crafted query would produce better results than manual conversion — it provides relevance scores, snippets, and citation contexts that improve report quality. + diff --git a/skills/literature-report/workflows/find-and-report.md b/skills/literature-report/workflows/find-and-report.md new file mode 100644 index 0000000..2e2bd8a --- /dev/null +++ b/skills/literature-report/workflows/find-and-report.md @@ -0,0 +1,50 @@ +# Find Papers and Generate Report + +Use when the user wants a literature report but has no papers yet. + + + +## Step 1: Clarify the query + +Ask the user to describe their research topic if the request is vague. The search query can be long and complex — a well-scoped query produces better results. + +## Step 2: Check for existing searches + +```bash +asta documents --root .asta/literature/find search --summary '' 2>/dev/null +``` +If relevant results already exist, confirm with the user whether to reuse them or run a fresh search. + +## Step 3: Run the search + +Generate a slug from the topic (e.g., `transformer-efficiency`). + +```bash +asta literature find '' --timeout 300 -o .asta/literature/find/YYYY-MM-DD-topic-slug.json +``` + +Run this in the background — it takes 30–60 seconds. When done, add it to the Asta document index +```bash +asta documents --root .asta/literature/find \ + add .asta/literature/find/YYYY-MM-DD-topic-slug.json + --name="Literature Report: " \ + --summary="" \ + --tags="find-literature,..." \ + --mime-type="application/json" +``` + +## Step 4: Review results + +After the search completes, inspect the top results: + +```bash +jq '[.results | sort_by(-.relevanceScore) | .[0:10][] | {title, year, venue, corpusId, score: .relevanceScore, summary: .relevanceJudgement.relevanceSummary}]' .asta/literature/find/YYYY-MM-DD-topic-slug.json +``` + +Share a brief summary of what was found (paper count, top themes) with the user before generating the report. If the results look thin or off-topic, suggest refining the query. + +## Step 5: Generate the report + +Now follow `workflows/generate-report.md` using the results file from step 3. + + diff --git a/skills/literature-report/workflows/generate-report.md b/skills/literature-report/workflows/generate-report.md new file mode 100644 index 0000000..91ce42a --- /dev/null +++ b/skills/literature-report/workflows/generate-report.md @@ -0,0 +1,39 @@ +# Generate Report from Find-Literature Results + +Use when the user already has a `.json` results file from `asta literature find`. + + + +## Step 1: Identify the input file + +The user provides or references a results file. If it was created by an Asta skill, +it will be in .asta/literature/find. Confirm the file exists. + +## Step 2: Generate the report + +```bash +asta literature report \ + -i .asta/literature/find/YYYY-MM-DD-topic-slug.json \ + -o .asta/literature/report/YYYY-MM-DD-topic-slug.md +``` + +Options: +- `--query "custom framing"` — override the query used as the report title/framing +- `--max-papers 30` — include more papers (default: 20, highest-relevance first) + +The command prints progress to stderr and the report path when done. + +## Step 3: Review and deliver + +Read the saved report file. Share the key findings with the user, including: +- The main themes and conclusions +- Number of papers synthesized +- The file path so they can open it + +If the user wants the report in a specific location (e.g., `docs/literature-review.md`), copy or move it there. + +## Step 4: Citations (if needed) + +If the report needs formal citations with a `.bib` file, follow `references/citation-conventions.md`. + + diff --git a/skills/literature-report/workflows/merge-report.md b/skills/literature-report/workflows/merge-report.md new file mode 100644 index 0000000..6555149 --- /dev/null +++ b/skills/literature-report/workflows/merge-report.md @@ -0,0 +1,64 @@ +# Merge New Findings into an Existing Report + +Use when the user has an existing literature report and wants to incorporate new papers or updated findings. + + + +## Step 1: Read the existing report + +Read the report file to understand: +- Current structure (sections, headings) +- Papers already cited +- The narrative arc and argument +- Citation format (inline `[@key]`, footnotes, or informal) + +## Step 2: Obtain new papers + +Determine the source of new papers. This may require chaining with another workflow: + +- **New search needed** → Follow `workflows/find-and-report.md` steps 1–4 to get a results file, then return here. +- **User provided a results file** → Use it directly. +- **User has papers in another form** → Follow `workflows/convert-and-report.md` steps 1–3 to create a results file, then return here. + +## Step 3: Generate a draft report from new papers + +```bash +asta literature report \ + -i .asta/literature/find/YYYY-MM-DD-new-topic-slug.json \ + -o .asta/literature/report/YYYY-MM-DD-new-topic-slug.md +``` + +Read the generated draft to understand what new content is available. + +## Step 4: Identify what to merge + +Compare the new draft against the existing report: +- **New themes** not covered in the existing report → add as new sections or subsections +- **Supporting evidence** for existing claims → weave into existing sections with new citations +- **Contradictory findings** → add as counterpoints or discussion within relevant sections +- **Duplicate coverage** → skip papers already represented + +## Step 5: Merge into the existing report + +Edit the existing report directly. Preserve the original structure and voice. For each addition: + +1. Place new content in the most relevant existing section, or create a new section if the theme is distinct. +2. Integrate citations naturally into the narrative — don't just append a list. +3. Update any summary or conclusion sections to reflect the expanded evidence base. +4. If using a `.bib` file, add new BibTeX entries following `references/citation-conventions.md`. + +## Step 6: Verify + +- Read the merged report end-to-end to check for coherence. +- Ensure no duplicate citations or contradictory statements. +- Confirm section flow still makes sense with the additions. +- If using formal citations, use the **Preview** skill to verify all `[@key]` references resolve. + + + + +- Existing report structure and voice are preserved +- New findings are integrated contextually, not just appended +- No duplicate papers or contradictory unsupported claims +- Citations are consistent with the existing format +