diff --git a/skills/find-literature/SKILL.md b/skills/find-literature/SKILL.md index 6637738..8e025b4 100644 --- a/skills/find-literature/SKILL.md +++ b/skills/find-literature/SKILL.md @@ -29,16 +29,21 @@ Verify installation with `asta literature --help` Run in background for comprehensive searches (30-60s): ```bash -# Saves to .asta/literature/find/YYYY-MM-DD-HH-MM-SS-{query-slug}.json by default -Bash(command="asta literature find 'query' --timeout 300", run_in_background=true) +# Save to a temporary file with explicit -o parameter (required) +Bash(command="asta literature find 'query' -o /tmp/literature-search-result.json --timeout 300", run_in_background=true) ``` -The command saves results to `.asta/literature/find/` (in current working directory) by default with an auto-generated filename. -The filename is printed to the console when the search is finished. Use the TaskOutput tool to capture the filename for later processing -After the file has been created, index it using the [asta-documents](../asta-documents/SKILL.md) skill. +After the search completes, move the file to `.asta/documents/literature/find/` and index it: ```bash -Bash(command="asta documents add file:// --name=... --summary=...") +# Create directory if it doesn't exist +mkdir -p .asta/documents/literature/find + +# Move the result file to the documents directory +mv /tmp/literature-search-result.json .asta/documents/literature/find/ + +# Index the file using asta-documents +Bash(command="asta documents add file://.asta/documents/literature/find/literature-search-result.json --name='Literature Search: ' --summary='Search results for: '") ``` Browse results with jq: diff --git a/src/asta/literature/find.py b/src/asta/literature/find.py index cd3f76c..b762157 100644 --- a/src/asta/literature/find.py +++ b/src/asta/literature/find.py @@ -1,8 +1,6 @@ """Find papers command""" import json -import re -from datetime import datetime from pathlib import Path import click @@ -13,6 +11,13 @@ @click.command() @click.argument("query") +@click.option( + "-o", + "--output", + type=click.Path(), + required=True, + help="Output file path for the results (required)", +) @click.option( "--timeout", type=int, @@ -25,24 +30,24 @@ default="infer", help="Search strategy: infer (auto-detect), fast (quick results), or diligent (comprehensive)", ) -def find(query: str, timeout: int, mode: str): +def find(query: str, output: str, timeout: int, mode: str): """Find papers matching QUERY using Asta Paper Finder. - Saves results to .asta/literature/find/ with an auto-generated filename. + Requires an output file path to save results. Examples: - # Save to default location - asta literature find "machine learning in healthcare" + # Save to specific file + asta literature find "machine learning in healthcare" -o results.json # With custom timeout - asta literature find "transformers" --timeout 60 + asta literature find "transformers" -o results.json --timeout 60 # Use fast mode for quick results - asta literature find "deep learning" --mode fast + asta literature find "deep learning" -o results.json --mode fast # Use diligent mode for comprehensive search - asta literature find "neural networks" --mode diligent + asta literature find "neural networks" -o results.json --mode diligent """ try: client = AstaPaperFinder() @@ -58,15 +63,9 @@ def find(query: str, timeout: int, mode: str): # Convert to dict for output output_data = literature_result.model_dump(mode="json", exclude_none=False) - # Generate default path: .asta/literature/find/YYYY-MM-DD-HH-MM-SS-query-slug.json - default_dir = Path.cwd() / ".asta" / "literature" / "find" - default_dir.mkdir(parents=True, exist_ok=True) - - timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") - # Create a slug from the query (lowercase, alphanumeric and hyphens only, max 50 chars) - query_slug = re.sub(r"[^a-z0-9]+", "-", query.lower()).strip("-")[:50] - filename = f"{timestamp}-{query_slug}.json" - output_path = default_dir / filename + # Use the specified output path + output_path = Path(output) + output_path.parent.mkdir(parents=True, exist_ok=True) # Save to file with open(output_path, "w") as f: diff --git a/src/asta/utils/passthrough.conf b/src/asta/utils/passthrough.conf index a0055c9..90659ed 100644 --- a/src/asta/utils/passthrough.conf +++ b/src/asta/utils/passthrough.conf @@ -11,9 +11,9 @@ passthrough { documents { tool_name = "asta-documents" - install_type = "git" - install_source = "git+ssh://git@github.com/allenai/asta-resource-repo.git" - minimum_version = "0.2.0" + install_type = "pypi" + install_source = "asta-resource-repository" + minimum_version = "0.3.0" command_name = "documents" friendly_name = "asta-documents" docstring = "Manage a local library of documents known to Asta" diff --git a/tests/test_cli.py b/tests/test_cli.py index 32dc3f8..4e3b753 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -60,25 +60,22 @@ def test_find_success_file_output(self, runner, tmp_path): } with patch("asta.literature.find.AstaPaperFinder") as MockFinder: - with patch("asta.literature.find.Path.cwd", return_value=tmp_path): - mock_instance = MagicMock() - mock_instance.find_papers.return_value = mock_result - MockFinder.return_value = mock_instance + mock_instance = MagicMock() + mock_instance.find_papers.return_value = mock_result + MockFinder.return_value = mock_instance - result = runner.invoke(cli, ["literature", "find", "test query"]) + output_file = tmp_path / "results.json" + result = runner.invoke( + cli, ["literature", "find", "test query", "-o", str(output_file)] + ) assert result.exit_code == 0 - # Verify file was created in default location - output_dir = tmp_path / ".asta" / "literature" / "find" - assert output_dir.exists() - - # Find the created file (should match pattern: YYYY-MM-DD-HH-MM-SS-test-query.json) - json_files = list(output_dir.glob("*-test-query.json")) - assert len(json_files) == 1 + # Verify file was created at specified location + assert output_file.exists() # Verify file contents - with open(json_files[0]) as f: + with open(output_file) as f: data = json.load(f) assert data["query"] == "test query" @@ -86,7 +83,7 @@ def test_find_success_file_output(self, runner, tmp_path): assert data["results"][0]["corpusId"] == 123 assert data["results"][1]["corpusId"] == 456 - def test_find_timeout_error(self, runner): + def test_find_timeout_error(self, runner, tmp_path): """Test find command with timeout error.""" with patch("asta.literature.find.AstaPaperFinder") as MockFinder: mock_instance = MagicMock() @@ -95,22 +92,28 @@ def test_find_timeout_error(self, runner): ) MockFinder.return_value = mock_instance - result = runner.invoke(cli, ["literature", "find", "test query"]) + output_file = tmp_path / "results.json" + result = runner.invoke( + cli, ["literature", "find", "test query", "-o", str(output_file)] + ) assert result.exit_code == 2 - def test_find_general_error(self, runner): + def test_find_general_error(self, runner, tmp_path): """Test find command with general error.""" with patch("asta.literature.find.AstaPaperFinder") as MockFinder: mock_instance = MagicMock() mock_instance.find_papers.side_effect = Exception("API error") MockFinder.return_value = mock_instance - result = runner.invoke(cli, ["literature", "find", "test query"]) + output_file = tmp_path / "results.json" + result = runner.invoke( + cli, ["literature", "find", "test query", "-o", str(output_file)] + ) assert result.exit_code == 1 - def test_find_custom_timeout(self, runner): + def test_find_custom_timeout(self, runner, tmp_path): """Test find command with custom timeout.""" mock_result = { "query": "test query", @@ -134,8 +137,18 @@ def test_find_custom_timeout(self, runner): mock_instance.find_papers.return_value = mock_result MockFinder.return_value = mock_instance + output_file = tmp_path / "results.json" result = runner.invoke( - cli, ["literature", "find", "test query", "--timeout", "60"] + cli, + [ + "literature", + "find", + "test query", + "-o", + str(output_file), + "--timeout", + "60", + ], ) assert result.exit_code == 0 @@ -143,7 +156,7 @@ def test_find_custom_timeout(self, runner): "test query", timeout=60, save_to_file=None, operation_mode="infer" ) - def test_find_with_mode_option(self, runner): + def test_find_with_mode_option(self, runner, tmp_path): """Test find command with different operation modes.""" mock_result = { "query": "test query", @@ -166,9 +179,19 @@ def test_find_with_mode_option(self, runner): mock_instance.find_papers.return_value = mock_result MockFinder.return_value = mock_instance + output_file = tmp_path / "results.json" # Test fast mode result = runner.invoke( - cli, ["literature", "find", "test query", "--mode", "fast"] + cli, + [ + "literature", + "find", + "test query", + "-o", + str(output_file), + "--mode", + "fast", + ], ) assert result.exit_code == 0 @@ -413,11 +436,10 @@ def test_documents_config(self): # Should have required fields assert config["tool_name"] == "asta-documents" - assert "install_type" in config - assert config["install_type"] in ("pypi", "git", "local") - assert "minimum_version" in config + assert config["install_type"] == "pypi" + assert config["install_source"] == "asta-resource-repository" + assert config["minimum_version"] == "0.3.0" assert validate_semver(config["minimum_version"]) - assert "install_source" in config assert config["command_name"] == "documents" def test_documents_help_requires_installation(self, runner):