@yeyuan98/opencode-bioresearcher-plugin 1.3.0 → 1.3.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -137
- package/dist/skills/demo-skill/SKILL.md +41 -41
- package/dist/skills/demo-skill/demo_script.py +23 -23
- package/dist/skills/pubmed-weekly/SKILL.md +260 -0
- package/dist/skills/pubmed-weekly/pubmed_weekly.py +293 -0
- package/dist/skills/python-setup-uv/SKILL.md +141 -141
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,137 +1,140 @@
|
|
|
1
|
-
# BioResearcher Plugin
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Skills
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
| `.
|
|
87
|
-
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
"
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
1
|
+
# BioResearcher Plugin
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
OpenCode plugin that adds agents and tools for biomedical and pharmaceutical research.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
No nonsense and powerful. Agents and tools that just work for your typical biomedical and pharmaceutical research needs.
|
|
10
|
+
|
|
11
|
+
Tab to activate and start asking right away:
|
|
12
|
+
|
|
13
|
+
- The **Bioresearcher Agent** is jack-of-all-trades.
|
|
14
|
+
- The **BioresearcherDR Agent** is a specialist for biomed/pharma deep research (DR).
|
|
15
|
+
|
|
16
|
+
## Agents
|
|
17
|
+
|
|
18
|
+
### BioresearcherDR
|
|
19
|
+
|
|
20
|
+
By default, it will conduct **highly detailed and in-depth** research, easily surpassing all general purpose LLM applications.
|
|
21
|
+
|
|
22
|
+
To reduce research depth and make research process faster, start your question with `light-research`:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
light-research Significance of KRAS for cancer?
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
To skip the Question-Clarification cycle, prompt your question with `no-interview`:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
no-interview light-research Significance of KRAS for cancer?
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Tools
|
|
35
|
+
|
|
36
|
+
### Table Tools
|
|
37
|
+
|
|
38
|
+
Manipulate Excel, CSV, and ODS files with precision and smart parsing for dates, numbers, and data types.
|
|
39
|
+
|
|
40
|
+
**Enable LLMs to wrangle huge tables WITHOUT overwhelming model context.**
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
What are tools for table processing?
|
|
44
|
+
What are column names in XXX.xlsx?
|
|
45
|
+
Analyze XXX.xlsx - group by Y column, and report unique values of Z column.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Calculator
|
|
49
|
+
|
|
50
|
+
Evaluate mathematical expressions with full support for brackets, powers, and scientific notation.
|
|
51
|
+
|
|
52
|
+
**Make your model accurate with numbers. No more 3.11 > 3.9 nor 1+2=5.**
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
Use the calculator tool: (3+1.5*6-1/2)^3
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Blocking Timer
|
|
59
|
+
|
|
60
|
+
Pause execution for testing or pacing operations.
|
|
61
|
+
|
|
62
|
+
**Respect API rate limits. No more IP bans/blocks.**
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
Query XXX API. You MUST include a 0.5 second delay between two API calls with the blockingTimer tool.
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### PubMed Parser
|
|
69
|
+
|
|
70
|
+
Parse PubMed XML files to markdown or Excel format. Supports `.xml` and `.xml.gz` files.
|
|
71
|
+
|
|
72
|
+
**Analyze years of publication articles in one go. Simple and powerful.**
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
Download pubmed article data from https://ftp.ncbi.nlm.nih.gov/pubmed/updatefiles/pubmed26n1340.xml.gz and parse to Excel format.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Reference: [PubMed Download Data](https://pubmed.ncbi.nlm.nih.gov/download/).
|
|
79
|
+
|
|
80
|
+
## Skills
|
|
81
|
+
|
|
82
|
+
Skills are reusable prompt templates discovered from multiple paths:
|
|
83
|
+
|
|
84
|
+
| Path | Scope |
|
|
85
|
+
|------|-------|
|
|
86
|
+
| `.opencode/skills/` | Project |
|
|
87
|
+
| `~/.config/opencode/skills/` | Global |
|
|
88
|
+
| `.claude/skills/` | Claude Code compatible |
|
|
89
|
+
| `.agents/skills/` | Agents compatible |
|
|
90
|
+
|
|
91
|
+
This plugin provides a skill tool that overrides Opencode's built-in to support plugin-shipped skills.
|
|
92
|
+
|
|
93
|
+
See [skill-tools/README.md](skill-tools/README.md) for full documentation.
|
|
94
|
+
|
|
95
|
+
### Supplied skills
|
|
96
|
+
|
|
97
|
+
- `demo-skill`: showcase skill tool mechanisms.
|
|
98
|
+
- `python-setup-uv`: setup python runtime in your working directory with uv.
|
|
99
|
+
- `pubmed-weekly`: automated download of pubmed daily update files over the past one week.
|
|
100
|
+
|
|
101
|
+
Prompt the following and follow along:
|
|
102
|
+
|
|
103
|
+
```txt
|
|
104
|
+
Setup python uv with skill
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
Add the plugin to your `opencode.json`:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"$schema": "https://opencode.ai/config.json",
|
|
114
|
+
"plugin": [
|
|
115
|
+
"@yeyuan98/opencode-bioresearcher-plugin",
|
|
116
|
+
],
|
|
117
|
+
"mcp": {
|
|
118
|
+
"biomcp": {
|
|
119
|
+
"type": "local",
|
|
120
|
+
"command": ["uv", "run", "--with", "biomcp-python", "biomcp", "run"],
|
|
121
|
+
"enabled": true,
|
|
122
|
+
"timeout": 120000
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
BioMCP is absolutely required and might take time to load. You will need to have `uv` installed on your system too. Refer to [BioMCP guide](https://biomcp.org/getting-started) for details.
|
|
129
|
+
|
|
130
|
+
## Common Issues and Solutions
|
|
131
|
+
|
|
132
|
+
| Issue | Solution |
|
|
133
|
+
|-------|----------|
|
|
134
|
+
| Agent not appearing | Check plugin in opencode.json is typed correctly |
|
|
135
|
+
| BioMCP tool failures | Make sure you have BioMCP [installed and enabled](https://opencode.ai/docs/mcp-servers/#enable) |
|
|
136
|
+
| Research taking a long time | Check whether subagents are generating outputs; could be slow model / API throttle / overcomplicated query / etc. |
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
CC BY-NC-ND 4.0
|
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: demo-skill
|
|
3
|
-
description: Demo skill showcasing the plugin skill integration system
|
|
4
|
-
allowedTools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Demo Skill
|
|
10
|
-
|
|
11
|
-
This skill demonstrates the plugin skill integration system.
|
|
12
|
-
|
|
13
|
-
## Features Demonstrated
|
|
14
|
-
|
|
15
|
-
1. **Skill Discovery** - This skill is discovered from `plugin/skills/`
|
|
16
|
-
2. **allowedTools** - Listed above (documentation only)
|
|
17
|
-
3. **Bundled Resources** - Files in this directory are accessible
|
|
18
|
-
|
|
19
|
-
## Test Resource Resolution
|
|
20
|
-
|
|
21
|
-
Run the bundled script to verify resources are properly resolved:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
python demo_script.py
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Expected output:
|
|
28
|
-
```
|
|
29
|
-
Demo Skill - Resource Resolution Test
|
|
30
|
-
=====================================
|
|
31
|
-
Skill directory: <path to skill>
|
|
32
|
-
Status: Resources resolved correctly!
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## How It Works
|
|
36
|
-
|
|
37
|
-
1. Plugin builds with `npm run build`
|
|
38
|
-
2. `skills/` directory copied to `dist/skills/`
|
|
39
|
-
3. Skill tool discovers all `SKILL.md` files
|
|
40
|
-
4. Agent calls `skill` tool with skill name
|
|
41
|
-
5. Skill content + file list returned to agent
|
|
1
|
+
---
|
|
2
|
+
name: demo-skill
|
|
3
|
+
description: Demo skill showcasing the plugin skill integration system
|
|
4
|
+
allowedTools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Demo Skill
|
|
10
|
+
|
|
11
|
+
This skill demonstrates the plugin skill integration system.
|
|
12
|
+
|
|
13
|
+
## Features Demonstrated
|
|
14
|
+
|
|
15
|
+
1. **Skill Discovery** - This skill is discovered from `plugin/skills/`
|
|
16
|
+
2. **allowedTools** - Listed above (documentation only)
|
|
17
|
+
3. **Bundled Resources** - Files in this directory are accessible
|
|
18
|
+
|
|
19
|
+
## Test Resource Resolution
|
|
20
|
+
|
|
21
|
+
Run the bundled script to verify resources are properly resolved:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python demo_script.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Expected output:
|
|
28
|
+
```
|
|
29
|
+
Demo Skill - Resource Resolution Test
|
|
30
|
+
=====================================
|
|
31
|
+
Skill directory: <path to skill>
|
|
32
|
+
Status: Resources resolved correctly!
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## How It Works
|
|
36
|
+
|
|
37
|
+
1. Plugin builds with `npm run build`
|
|
38
|
+
2. `skills/` directory copied to `dist/skills/`
|
|
39
|
+
3. Skill tool discovers all `SKILL.md` files
|
|
40
|
+
4. Agent calls `skill` tool with skill name
|
|
41
|
+
5. Skill content + file list returned to agent
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
"""Demo script to verify skill resource resolution."""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def main():
|
|
9
|
-
print("Demo Skill - Resource Resolution Test")
|
|
10
|
-
print("=" * 40)
|
|
11
|
-
print(f"Script location: {os.path.abspath(__file__)}")
|
|
12
|
-
print(f"Python version: {sys.version.split()[0]}")
|
|
13
|
-
print()
|
|
14
|
-
print("Status: Resources resolved correctly!")
|
|
15
|
-
print()
|
|
16
|
-
print("This confirms:")
|
|
17
|
-
print(" - Skill files bundled at build time")
|
|
18
|
-
print(" - Resources discoverable via skill tool")
|
|
19
|
-
print(" - Scripts executable from skill directory")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if __name__ == "__main__":
|
|
23
|
-
main()
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Demo script to verify skill resource resolution."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
print("Demo Skill - Resource Resolution Test")
|
|
10
|
+
print("=" * 40)
|
|
11
|
+
print(f"Script location: {os.path.abspath(__file__)}")
|
|
12
|
+
print(f"Python version: {sys.version.split()[0]}")
|
|
13
|
+
print()
|
|
14
|
+
print("Status: Resources resolved correctly!")
|
|
15
|
+
print()
|
|
16
|
+
print("This confirms:")
|
|
17
|
+
print(" - Skill files bundled at build time")
|
|
18
|
+
print(" - Resources discoverable via skill tool")
|
|
19
|
+
print(" - Scripts executable from skill directory")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pubmed-weekly
|
|
3
|
+
description: Download PubMed daily update xml.gz files from the past week from NCBI FTP server
|
|
4
|
+
allowedTools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Question
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# PubMed Weekly Daily Updates Download
|
|
12
|
+
|
|
13
|
+
This skill downloads PubMed daily update xml.gz files from the past week (Monday-Sunday).
|
|
14
|
+
|
|
15
|
+
## Workflow Overview
|
|
16
|
+
|
|
17
|
+
1. **Python Environment Setup** (automatic): Checks for uv, installs via `python-setup-uv` skill if needed
|
|
18
|
+
2. **Date Calculation**: Calculates the past week's date range (Monday-Sunday)
|
|
19
|
+
3. **FTP Listing**: Fetches available xml.gz files from NCBI FTP server
|
|
20
|
+
4. **Filtering**: Filters files to include only those from the past week
|
|
21
|
+
5. **Download**: Downloads filtered files with retry logic (max 3 attempts per file)
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
- Internet connection
|
|
25
|
+
- Access to NCBI FTP server
|
|
26
|
+
- uv package manager (will be automatically installed if not present)
|
|
27
|
+
|
|
28
|
+
## Integration with python-setup-uv
|
|
29
|
+
|
|
30
|
+
This skill integrates with the `python-setup-uv` skill to ensure Python environment is properly configured.
|
|
31
|
+
|
|
32
|
+
### Prerequisite Check
|
|
33
|
+
|
|
34
|
+
Before starting the download process:
|
|
35
|
+
|
|
36
|
+
1. **Check if uv is installed:**
|
|
37
|
+
```bash
|
|
38
|
+
if [ -f "uv" ] || [ -f "uv.exe" ]; then
|
|
39
|
+
echo "uv already installed"
|
|
40
|
+
else
|
|
41
|
+
echo "uv not found, setting up..."
|
|
42
|
+
fi
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
2. **If uv is not installed:**
|
|
46
|
+
- Load the `python-setup-uv` skill using the skill tool
|
|
47
|
+
- Follow all steps EXACTLY as specified in the python-setup-uv skill
|
|
48
|
+
- Wait for uv installation to complete
|
|
49
|
+
- Continue with this skill's Step 1 below
|
|
50
|
+
|
|
51
|
+
3. **After uv is installed:**
|
|
52
|
+
- All Python commands in this skill will use the bash tool with `workdir` parameter
|
|
53
|
+
- Use `./uv run python` (Unix-like) or `uv.exe run python` (Windows cmd.exe)
|
|
54
|
+
- The bundled script `pubmed_weekly.py` will be executed using uv
|
|
55
|
+
|
|
56
|
+
## Path Resolution Strategy
|
|
57
|
+
|
|
58
|
+
This skill uses the bash tool's `workdir` parameter to handle portable script execution:
|
|
59
|
+
|
|
60
|
+
1. **Extract skill directory path** from `<skill_files>` section in skill tool output
|
|
61
|
+
- Example: `<file>C:\Users\...\plugin\skills\pubmed-weekly\pubmed_weekly.py</file>`
|
|
62
|
+
- Extract directory: `C:\Users\...\plugin\skills\pubmed-weekly\`
|
|
63
|
+
|
|
64
|
+
2. **Get current working directory** using the bash tool
|
|
65
|
+
```bash
|
|
66
|
+
WORKING_DIR=$(pwd) # Unix-like
|
|
67
|
+
# or use the default working directory from bash tool
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
3. **Use bash tool with workdir** to run Python scripts from skill directory
|
|
71
|
+
```bash
|
|
72
|
+
# bash tool will handle the workdir parameter
|
|
73
|
+
# Python's os.getcwd() will give skill directory
|
|
74
|
+
# Downloads go to working directory via --working-dir argument
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Steps
|
|
78
|
+
|
|
79
|
+
Follow these steps EXACTLY as described.
|
|
80
|
+
|
|
81
|
+
### Step 1: Calculate Week Date Range
|
|
82
|
+
|
|
83
|
+
First, determine the date range for the past week (Monday through Sunday).
|
|
84
|
+
|
|
85
|
+
Use the bash tool with:
|
|
86
|
+
- `workdir` set to the skill directory (extracted from `<skill_files>`)
|
|
87
|
+
- `command` to run the Python script
|
|
88
|
+
|
|
89
|
+
**For Unix-like shells (Git Bash / macOS / Linux):**
|
|
90
|
+
```bash
|
|
91
|
+
./uv run python pubmed_weekly.py calculate_week --working-dir="$(pwd)"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**For Windows cmd.exe:**
|
|
95
|
+
```bash
|
|
96
|
+
uv.exe run python pubmed_weekly.py calculate_week --working-dir="%CD%"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This will output the week folder name in format `YYYYMMDD-YYYYMMDD`.
|
|
100
|
+
|
|
101
|
+
**Expected output format:**
|
|
102
|
+
```
|
|
103
|
+
20250217-20250223
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Step 2: Create Download Directory
|
|
107
|
+
|
|
108
|
+
Create the directory structure for the week in the working directory:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
mkdir -p .download/pubmed-daily/<WEEK>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Replace `<WEEK>` with the actual week folder name from Step 1.
|
|
115
|
+
|
|
116
|
+
### Step 3: Fetch FTP File List
|
|
117
|
+
|
|
118
|
+
Use the bash tool with `workdir` set to the skill directory to fetch the list of files from the NCBI FTP server:
|
|
119
|
+
|
|
120
|
+
**For Unix-like shells:**
|
|
121
|
+
```bash
|
|
122
|
+
./uv run python pubmed_weekly.py fetch_files --working-dir="$(pwd)"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**For Windows cmd.exe:**
|
|
126
|
+
```bash
|
|
127
|
+
uv.exe run python pubmed_weekly.py fetch_files --working-dir="%CD%"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This will list all daily update xml.gz files available on the FTP server.
|
|
131
|
+
|
|
132
|
+
**Expected output:**
|
|
133
|
+
```
|
|
134
|
+
pubmed24n1234.xml.gz pubmed24n1235.xml.gz pubmed24n1236.xml.gz
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Step 4: Filter Files for Past Week
|
|
138
|
+
|
|
139
|
+
Use the bash tool with `workdir` set to the skill directory to filter the file list for the past week's daily updates:
|
|
140
|
+
|
|
141
|
+
**For Unix-like shells:**
|
|
142
|
+
```bash
|
|
143
|
+
./uv run python pubmed_weekly.py filter_files "<WEEK>" "<FILE_LIST>" --working-dir="$(pwd)"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**For Windows cmd.exe:**
|
|
147
|
+
```bash
|
|
148
|
+
uv.exe run python pubmed_weekly.py filter_files "<WEEK>" "<FILE_LIST>" --working-dir="%CD%"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Where:
|
|
152
|
+
- `<WEEK>` is the week folder name (e.g., `20250217-20250223`)
|
|
153
|
+
- `<FILE_LIST>` is the output from Step 3 (space-separated filenames, use quotes)
|
|
154
|
+
|
|
155
|
+
This will return a space-separated list of xml.gz files from the past week.
|
|
156
|
+
|
|
157
|
+
**Expected output:**
|
|
158
|
+
```
|
|
159
|
+
pubmed24n1234.xml.gz pubmed24n1235.xml.gz pubmed24n1236.xml.gz
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Step 5: Download Files with Retry
|
|
163
|
+
|
|
164
|
+
For each file in the filtered list, download to the target directory with retry logic:
|
|
165
|
+
|
|
166
|
+
**For Unix-like shells:**
|
|
167
|
+
```bash
|
|
168
|
+
for file in <FILE_LIST>; do
|
|
169
|
+
./uv run python pubmed_weekly.py download_file <WEEK> $file --working-dir="$(pwd)"
|
|
170
|
+
done
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**For Windows cmd.exe:**
|
|
174
|
+
```bash
|
|
175
|
+
for %f in (<FILE_LIST>) do uv.exe run python pubmed_weekly.py download_file <WEEK> %f --working-dir="%CD%"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Replace `<FILE_LIST>` with the space-separated list from Step 4.
|
|
179
|
+
|
|
180
|
+
**Download behavior:**
|
|
181
|
+
- Downloads one file at a time
|
|
182
|
+
- Retries up to 3 times if download fails
|
|
183
|
+
- Waits 2 seconds between retry attempts
|
|
184
|
+
- After 3 failed attempts, asks user whether to abort
|
|
185
|
+
|
|
186
|
+
**If a download fails after 3 retries:**
|
|
187
|
+
Use the question tool to ask:
|
|
188
|
+
- "Abort remaining downloads?" (options: "Yes" / "No")
|
|
189
|
+
|
|
190
|
+
If user selects "Yes", stop the process and report summary.
|
|
191
|
+
If user selects "No", skip the failed file and continue with the next one.
|
|
192
|
+
|
|
193
|
+
### Step 6: Verify Downloads
|
|
194
|
+
|
|
195
|
+
After all downloads complete (or are aborted), verify the downloaded files:
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
ls -lh .download/pubmed-daily/<WEEK>/
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Count the number of files downloaded and report the summary to the user.
|
|
202
|
+
|
|
203
|
+
## Python Script Details
|
|
204
|
+
|
|
205
|
+
The skill includes a bundled Python script at `pubmed_weekly.py` with the following functions:
|
|
206
|
+
|
|
207
|
+
### 1. `calculate_week()` - Calculate week date range
|
|
208
|
+
|
|
209
|
+
Returns week folder name in format `YYYYMMDD-YYYYMMDD` for the past week (Monday-Sunday).
|
|
210
|
+
|
|
211
|
+
### 2. `fetch_files()` - Fetch FTP file list
|
|
212
|
+
|
|
213
|
+
Returns list of all xml.gz filenames from NCBI FTP server.
|
|
214
|
+
|
|
215
|
+
### 3. `filter_files(week_name, file_list)` - Filter files for the week
|
|
216
|
+
|
|
217
|
+
Parameters:
|
|
218
|
+
- `week_name`: Week folder name (e.g., `20250217-20250223`)
|
|
219
|
+
- `file_list`: List of filenames from FTP server
|
|
220
|
+
|
|
221
|
+
Returns: Space-separated list of xml.gz files that fall within the date range.
|
|
222
|
+
|
|
223
|
+
### 4. `download_file(week_name, filename)` - Download single file with retry
|
|
224
|
+
|
|
225
|
+
Parameters:
|
|
226
|
+
- `week_name`: Week folder name
|
|
227
|
+
- `filename`: XML.gz filename to download
|
|
228
|
+
|
|
229
|
+
Behavior:
|
|
230
|
+
- Downloads from `ftp://ftp.ncbi.nlm.nih.gov/pubmed/updatefiles/<filename>`
|
|
231
|
+
- Saves to `<working_dir>/.download/pubmed-daily/<week_name>/<filename>`
|
|
232
|
+
- Uses `--working-dir` argument if provided, otherwise uses current directory
|
|
233
|
+
- Retries up to 3 times on failure
|
|
234
|
+
- Returns exit code 0 on success, 1 on failure (after all retries)
|
|
235
|
+
|
|
236
|
+
## Output Summary
|
|
237
|
+
|
|
238
|
+
After completion, provide the user with:
|
|
239
|
+
|
|
240
|
+
1. Week date range processed
|
|
241
|
+
2. Number of files found for the week
|
|
242
|
+
3. Number of files successfully downloaded
|
|
243
|
+
4. Number of files failed to download (if any)
|
|
244
|
+
5. Download location: `.download/pubmed-daily/<WEEK>/`
|
|
245
|
+
|
|
246
|
+
## Notes
|
|
247
|
+
|
|
248
|
+
- This skill automatically checks for and installs uv using the `python-setup-uv` skill if not present
|
|
249
|
+
- The Python script is bundled with this skill at `pubmed_weekly.py`
|
|
250
|
+
- All Python commands use the bash tool's `workdir` parameter to run from skill directory
|
|
251
|
+
- The `--working-dir` argument ensures downloads go to the user's working directory
|
|
252
|
+
- The FTP server path is: `ftp://ftp.ncbi.nlm.nih.gov/pubmed/updatefiles/`
|
|
253
|
+
- Only `.xml.gz` files are downloaded
|
|
254
|
+
- Downloads are sequential (one file at a time)
|
|
255
|
+
- Retry logic includes 2-second delays between attempts
|
|
256
|
+
- User has control to abort on persistent failures
|
|
257
|
+
- The script uses Python's built-in `urllib.request` for FTP operations - no additional Python package dependencies required
|
|
258
|
+
- Skill directory path is extracted from `<skill_files>` section for portability
|
|
259
|
+
- Windows with Git Bash: Follow Unix-like shell instructions
|
|
260
|
+
- Windows cmd.exe: Use `uv.exe run python` syntax and `%CD%` for working directory
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PubMed Weekly Daily Updates Downloader
|
|
4
|
+
|
|
5
|
+
This script handles:
|
|
6
|
+
- Calculating the past week's date range (Monday-Sunday)
|
|
7
|
+
- Fetching FTP file list from NCBI
|
|
8
|
+
- Filtering files for the specific week
|
|
9
|
+
- Downloading files with retry logic
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import re
|
|
15
|
+
import time
|
|
16
|
+
import urllib.request
|
|
17
|
+
import argparse
|
|
18
|
+
from datetime import datetime, timedelta
|
|
19
|
+
from typing import List, Tuple
|
|
20
|
+
|
|
21
|
+
# Global working directory for downloads (set via --working-dir argument)
|
|
22
|
+
WORKING_DIR = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def calculate_week() -> str:
|
|
26
|
+
"""Calculate the past week's date range (Monday-Sunday).
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Week folder name in format 'YYYYMMDD-YYYYMMDD' for the PREVIOUS week
|
|
30
|
+
"""
|
|
31
|
+
today = datetime.now()
|
|
32
|
+
|
|
33
|
+
# Find the most recent Monday of the current week
|
|
34
|
+
days_since_monday = today.weekday() # Monday = 0, Sunday = 6
|
|
35
|
+
current_monday = today - timedelta(days=days_since_monday)
|
|
36
|
+
|
|
37
|
+
# Go back one week to get the previous week's Monday
|
|
38
|
+
previous_week_monday = current_monday - timedelta(days=7)
|
|
39
|
+
|
|
40
|
+
# Calculate the previous week's Sunday (6 days after Monday)
|
|
41
|
+
previous_week_sunday = previous_week_monday + timedelta(days=6)
|
|
42
|
+
|
|
43
|
+
week_start = previous_week_monday.strftime("%Y%m%d")
|
|
44
|
+
week_end = previous_week_sunday.strftime("%Y%m%d")
|
|
45
|
+
|
|
46
|
+
return f"{week_start}-{week_end}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_date_from_filename(filename: str) -> datetime | None:
|
|
50
|
+
"""Extract date from PubMed filename.
|
|
51
|
+
|
|
52
|
+
PubMed filenames are in format: pubmed24nYYYYMMDD.xml.gz
|
|
53
|
+
The date is embedded in the number following 'n'
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
filename: PubMed filename (e.g., pubmed24n1234.xml.gz)
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
datetime object or None if date cannot be parsed
|
|
60
|
+
"""
|
|
61
|
+
# Pattern to extract the numeric part after 'n'
|
|
62
|
+
match = re.match(r"pubmed\d+n(\d+)\.xml\.gz", filename)
|
|
63
|
+
if not match:
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
number = match.group(1)
|
|
67
|
+
|
|
68
|
+
# PubMed daily update files use a specific numbering scheme
|
|
69
|
+
# The first 4 digits represent the year (e.g., 2024)
|
|
70
|
+
# The next 4 digits represent a sequential number within the year
|
|
71
|
+
# We need to convert this to a date
|
|
72
|
+
|
|
73
|
+
# For daily updates, NCBI uses a sequential number that increments daily
|
|
74
|
+
# We need to look up the actual date from the FTP directory listing
|
|
75
|
+
# which includes modification time
|
|
76
|
+
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
number = match.group(1)
|
|
80
|
+
|
|
81
|
+
# PubMed daily update files use a specific numbering scheme
|
|
82
|
+
# The first 4 digits represent the year (e.g., 2024)
|
|
83
|
+
# The next 4 digits represent a sequential number within the year
|
|
84
|
+
# We need to convert this to a date
|
|
85
|
+
|
|
86
|
+
# For daily updates, NCBI uses a sequential number that increments daily
|
|
87
|
+
# We need to look up the actual date from the FTP directory listing
|
|
88
|
+
# which includes modification time
|
|
89
|
+
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def fetch_ftp_file_list() -> List[str]:
|
|
94
|
+
"""Fetch list of xml.gz files from NCBI FTP server.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
List of xml.gz filenames from the FTP server
|
|
98
|
+
"""
|
|
99
|
+
url = "ftp://ftp.ncbi.nlm.nih.gov/pubmed/updatefiles/"
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
with urllib.request.urlopen(url) as response:
|
|
103
|
+
html_content = response.read().decode("utf-8")
|
|
104
|
+
|
|
105
|
+
# Parse HTML to extract filenames
|
|
106
|
+
# FTP directory listing returns HTML with links
|
|
107
|
+
filenames = []
|
|
108
|
+
for line in html_content.split("\n"):
|
|
109
|
+
match = re.search(r"pubmed\d+n\d+\.xml\.gz", line)
|
|
110
|
+
if match:
|
|
111
|
+
filename = match.group(0)
|
|
112
|
+
if filename not in filenames:
|
|
113
|
+
filenames.append(filename)
|
|
114
|
+
|
|
115
|
+
return sorted(filenames)
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print(f"Error fetching FTP file list: {e}", file=sys.stderr)
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def filter_files_by_date(week_name: str, file_list: List[str]) -> List[str]:
|
|
123
|
+
"""Filter files to include only those from the past week.
|
|
124
|
+
|
|
125
|
+
Since PubMed files don't encode the date directly in the filename,
|
|
126
|
+
we need to download the directory listing with timestamps and filter
|
|
127
|
+
based on modification date.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
week_name: Week folder name (YYYYMMDD-YYYYMMDD)
|
|
131
|
+
file_list: List of all xml.gz filenames
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List of filenames that fall within the date range
|
|
135
|
+
"""
|
|
136
|
+
# Parse week dates
|
|
137
|
+
start_date_str, end_date_str = week_name.split("-")
|
|
138
|
+
start_date = datetime.strptime(start_date_str, "%Y%m%d")
|
|
139
|
+
end_date = datetime.strptime(end_date_str, "%Y%m%d").replace(
|
|
140
|
+
hour=23, minute=59, second=59
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# Fetch directory listing with timestamps
|
|
144
|
+
url = "ftp://ftp.ncbi.nlm.nih.gov/pubmed/updatefiles/"
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
with urllib.request.urlopen(url) as response:
|
|
148
|
+
content = response.read().decode("utf-8", errors="ignore")
|
|
149
|
+
|
|
150
|
+
# Parse file listings with dates
|
|
151
|
+
# NCBI FTP format uses ISO date: "2026-01-30 14:02"
|
|
152
|
+
file_dates = {}
|
|
153
|
+
|
|
154
|
+
for filename in file_list:
|
|
155
|
+
# Find the line containing this file
|
|
156
|
+
# Pattern: filename followed by non-digits, then date YYYY-MM-DD HH:MM
|
|
157
|
+
pattern = re.escape(filename)
|
|
158
|
+
match = re.search(
|
|
159
|
+
rf"({pattern})[^0-9]*(\d{{4}}-\d{{2}}-\d{{2}})\s+(\d{{2}}:\d{{2}})",
|
|
160
|
+
content,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if match:
|
|
164
|
+
date_str = match.group(2)
|
|
165
|
+
time_str = match.group(3)
|
|
166
|
+
try:
|
|
167
|
+
# Parse date in ISO format: "2026-01-30 14:02"
|
|
168
|
+
file_date = datetime.strptime(
|
|
169
|
+
f"{date_str} {time_str}", "%Y-%m-%d %H:%M"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
file_dates[filename] = file_date
|
|
173
|
+
except ValueError:
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
# Filter files within date range
|
|
177
|
+
filtered_files = [
|
|
178
|
+
filename
|
|
179
|
+
for filename, file_date in file_dates.items()
|
|
180
|
+
if start_date <= file_date <= end_date
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
return sorted(filtered_files)
|
|
184
|
+
|
|
185
|
+
except Exception as e:
|
|
186
|
+
print(f"Error filtering files by date: {e}", file=sys.stderr)
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def download_file(week_name: str, filename: str, max_retries: int = 3) -> int:
|
|
191
|
+
"""Download a single file from NCBI FTP server with retry logic.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
week_name: Week folder name
|
|
195
|
+
filename: XML.gz filename to download
|
|
196
|
+
max_retries: Maximum number of retry attempts
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
0 on success, 1 on failure (after all retries)
|
|
200
|
+
"""
|
|
201
|
+
base_url = "ftp://ftp.ncbi.nlm.nih.gov/pubmed/updatefiles/"
|
|
202
|
+
url = f"{base_url}{filename}"
|
|
203
|
+
|
|
204
|
+
# Create download directory - use WORKING_DIR if set, otherwise current directory
|
|
205
|
+
base_dir = WORKING_DIR if WORKING_DIR else os.getcwd()
|
|
206
|
+
download_dir = os.path.join(base_dir, ".download", "pubmed-daily", week_name)
|
|
207
|
+
os.makedirs(download_dir, exist_ok=True)
|
|
208
|
+
|
|
209
|
+
filepath = os.path.join(download_dir, filename)
|
|
210
|
+
|
|
211
|
+
for attempt in range(max_retries):
|
|
212
|
+
try:
|
|
213
|
+
print(f"Downloading {filename} (attempt {attempt + 1}/{max_retries})...")
|
|
214
|
+
|
|
215
|
+
urllib.request.urlretrieve(url, filepath)
|
|
216
|
+
|
|
217
|
+
# Verify file was downloaded and has content
|
|
218
|
+
if os.path.exists(filepath) and os.path.getsize(filepath) > 0:
|
|
219
|
+
print(f"Successfully downloaded {filename}")
|
|
220
|
+
return 0
|
|
221
|
+
else:
|
|
222
|
+
raise Exception("Downloaded file is empty or missing")
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
print(f"Error downloading {filename}: {e}", file=sys.stderr)
|
|
226
|
+
|
|
227
|
+
if attempt < max_retries - 1:
|
|
228
|
+
print(f"Retrying in 2 seconds...")
|
|
229
|
+
time.sleep(2)
|
|
230
|
+
else:
|
|
231
|
+
print(f"Failed to download {filename} after {max_retries} attempts")
|
|
232
|
+
return 1
|
|
233
|
+
|
|
234
|
+
return 1
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def main():
|
|
238
|
+
"""Main entry point for command-line usage."""
|
|
239
|
+
parser = argparse.ArgumentParser(
|
|
240
|
+
description="PubMed Weekly Daily Updates Downloader"
|
|
241
|
+
)
|
|
242
|
+
parser.add_argument(
|
|
243
|
+
"--working-dir",
|
|
244
|
+
type=str,
|
|
245
|
+
help="Working directory for downloads (default: current directory)",
|
|
246
|
+
)
|
|
247
|
+
parser.add_argument("command", type=str, help="Command to execute")
|
|
248
|
+
parser.add_argument("args", nargs="*", help="Command arguments")
|
|
249
|
+
|
|
250
|
+
parsed = parser.parse_args()
|
|
251
|
+
|
|
252
|
+
# Set global working directory if provided
|
|
253
|
+
global WORKING_DIR
|
|
254
|
+
if parsed.working_dir:
|
|
255
|
+
WORKING_DIR = parsed.working_dir
|
|
256
|
+
|
|
257
|
+
command = parsed.command
|
|
258
|
+
args = parsed.args
|
|
259
|
+
|
|
260
|
+
if command == "calculate_week":
|
|
261
|
+
week = calculate_week()
|
|
262
|
+
print(week)
|
|
263
|
+
|
|
264
|
+
elif command == "fetch_files":
|
|
265
|
+
files = fetch_ftp_file_list()
|
|
266
|
+
print(" ".join(files))
|
|
267
|
+
|
|
268
|
+
elif command == "filter_files":
|
|
269
|
+
if len(args) < 2:
|
|
270
|
+
print("Usage: python pubmed_weekly.py filter_files <week_name> <file_list>")
|
|
271
|
+
sys.exit(1)
|
|
272
|
+
|
|
273
|
+
week_name = args[0]
|
|
274
|
+
file_list = args[1].split()
|
|
275
|
+
filtered = filter_files_by_date(week_name, file_list)
|
|
276
|
+
print(" ".join(filtered))
|
|
277
|
+
|
|
278
|
+
elif command == "download_file":
|
|
279
|
+
if len(args) < 2:
|
|
280
|
+
print("Usage: python pubmed_weekly.py download_file <week_name> <filename>")
|
|
281
|
+
sys.exit(1)
|
|
282
|
+
|
|
283
|
+
week_name = args[0]
|
|
284
|
+
filename = args[1]
|
|
285
|
+
sys.exit(download_file(week_name, filename))
|
|
286
|
+
|
|
287
|
+
else:
|
|
288
|
+
print(f"Unknown command: {command}")
|
|
289
|
+
sys.exit(1)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
if __name__ == "__main__":
|
|
293
|
+
main()
|
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: python-setup-uv
|
|
3
|
-
description: Setup Python environment with uv package manager - download binaries, create symlink, and install packages
|
|
4
|
-
allowedTools:
|
|
5
|
-
- Bash
|
|
6
|
-
- Read
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Python Environment Setup with uv
|
|
10
|
-
|
|
11
|
-
This skill sets up a Python environment using the uv package manager.
|
|
12
|
-
|
|
13
|
-
## Prerequisites
|
|
14
|
-
- Internet connection for downloading uv
|
|
15
|
-
- Python 3.8+ should be available on PATH (or uv will prompt to install it)
|
|
16
|
-
|
|
17
|
-
## Steps
|
|
18
|
-
|
|
19
|
-
**ABSOLUTE RULE:** Follow steps below EXACTLY AS IS. Do NOT skip/modify steps (nor detailed subtasks in each step) nor assume anything based on user platform information. Use URLs below EXACTLY AS IS. Follow steps below INCLUDING ALL DETAILS AND SUBSTEPS EXACTLY AS IS.
|
|
20
|
-
|
|
21
|
-
### Step 1: Ask user question about which installer to use
|
|
22
|
-
|
|
23
|
-
Use the question tool to ask which installer should be used:
|
|
24
|
-
|
|
25
|
-
- Official astral-uv installer (https://astral.sh)
|
|
26
|
-
- China mainland uv-custom installer (https://gitee.com/wangnov/uv-custom)
|
|
27
|
-
|
|
28
|
-
### Step 2: Detect Shell and Download uv Binary
|
|
29
|
-
|
|
30
|
-
First, detect your shell environment:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
# Detect shell type
|
|
34
|
-
# MSYSTEM is set by Git Bash, MINGW_PREFIX by MSYS2
|
|
35
|
-
if [ -n "$MSYSTEM" ] || [ -n "$MINGW_PREFIX" ] || command -v curl >/dev/null 2>&1; then
|
|
36
|
-
echo "Unix-like shell detected (Git Bash, bash, zsh, etc.)"
|
|
37
|
-
IS_UNIX_SHELL=true
|
|
38
|
-
else
|
|
39
|
-
echo "Windows cmd.exe detected"
|
|
40
|
-
IS_UNIX_SHELL=false
|
|
41
|
-
fi
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Then download uv based on your shell (see below).
|
|
45
|
-
|
|
46
|
-
Choose the correct `UV_INSTALLER_URL` depending on answer you received in Step 1 from the user:
|
|
47
|
-
|
|
48
|
-
- If opted "Official astral-uv", UV_INSTALLER_URL should be `https://astral.sh/uv/install.sh` (Unix-like) or `https://astral.sh/uv/install.ps1` (Windows)
|
|
49
|
-
- If opted "China mainland uv-custom", UV_INSTALLER_URL should be `https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.sh` (Unix-like) or `https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.ps1` (Windows)
|
|
50
|
-
|
|
51
|
-
**For Unix-like shells (Git Bash / macOS / Linux; use correct UV_INSTALLER_URL):**
|
|
52
|
-
```bash
|
|
53
|
-
mkdir -p .uv
|
|
54
|
-
curl -LsSf UV_INSTALLER_URL | UV_INSTALL_DIR="$(pwd)/.uv" sh
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**For Windows cmd.exe (if Git Bash unavailable; use correct UV_INSTALLER_URL):**
|
|
58
|
-
```bash
|
|
59
|
-
powershell -NoProfile -Command "New-Item -ItemType Directory -Force -Path .uv | Out-Null; $env:UV_INSTALL_DIR = (Get-Location).Path + '\.uv'; Invoke-RestMethod UV_INSTALLER_URL | Invoke-Expression"
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Step 3: Create Symlink or Copy uv to Working Directory
|
|
63
|
-
|
|
64
|
-
**For Unix-like shells (Git Bash / macOS / Linux):**
|
|
65
|
-
```bash
|
|
66
|
-
ln -sf .uv/uv uv
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**For Windows cmd.exe:**
|
|
70
|
-
|
|
71
|
-
Try symlink first, fall back to copy if no Admin rights:
|
|
72
|
-
```bash
|
|
73
|
-
cmd /c "(mklink uv .uv\uv.exe) 2>nul || copy /Y .uv\uv.exe uv.exe"
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Step 4: Create Virtual Environment and Install pandas
|
|
77
|
-
|
|
78
|
-
NOTE: this step (package installation) may timeout. If timed out, use the question tool to ask if the user would like to retry package installation. If successful, do NOT ask any question and continue to Step 5.
|
|
79
|
-
|
|
80
|
-
**For Unix-like shells:**
|
|
81
|
-
```bash
|
|
82
|
-
./uv venv
|
|
83
|
-
./uv pip install pandas
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**For Windows cmd.exe:**
|
|
87
|
-
```bash
|
|
88
|
-
uv.exe venv
|
|
89
|
-
uv.exe pip install pandas
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Step 5: Verification
|
|
93
|
-
|
|
94
|
-
**For Unix-like shells:**
|
|
95
|
-
```bash
|
|
96
|
-
./uv --version
|
|
97
|
-
./uv run python -c "import pandas; print(pandas.__version__)"
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**For Windows cmd.exe:**
|
|
101
|
-
```bash
|
|
102
|
-
uv.exe --version
|
|
103
|
-
uv.exe run python -c "import pandas; print(pandas.__version__)"
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Step 6: Update AGENTS.md
|
|
107
|
-
|
|
108
|
-
Use the question tool to ask if the user want to update AGENTS.md in WORKING DIRECTORY to "direct agents to use the installed UV Python" (options: "Yes" / "No"). If received no answer, continue to Step 7 (do NOT modify AGENTS.md NOR create directories). If received yes answer, follow steps belows.
|
|
109
|
-
|
|
110
|
-
1. If AGENTS.md not found in WORKING DIR, create an empty AGENTS.md.
|
|
111
|
-
2. Inspect AGENTS.md content. If you do not see the content block below, APPEND EXACTLY AS IS to end of AGENTS.md.
|
|
112
|
-
3. Check if `./.code/py` exist. If not, create the directories.
|
|
113
|
-
|
|
114
|
-
Content block:
|
|
115
|
-
|
|
116
|
-
```md
|
|
117
|
-
## Important note about Python
|
|
118
|
-
|
|
119
|
-
ALWAYS use the uv package manager available in WORKING DIRECTORY, including `uv add ...` or `uv pip ...` for package management and `uv run ...` to run python package executables.
|
|
120
|
-
|
|
121
|
-
ALWAYS save python scripts under path `./.code/py/` and run the script with `uv run python ...` whenever your work involves executing python scripts. Your script MUST contain concise docstrings and comments and use good engineering practices including separation of concerns.
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Step 7: Return summary to user (Usage After Setup)
|
|
125
|
-
|
|
126
|
-
**For Unix-like shells:**
|
|
127
|
-
```bash
|
|
128
|
-
./uv run python your_script.py
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
**For Windows cmd.exe:**
|
|
132
|
-
```bash
|
|
133
|
-
uv.exe run python your_script.py
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## Notes
|
|
137
|
-
- Add `.uv/` and `.venv/` to `.gitignore`
|
|
138
|
-
- `uv run` handles venv activation automatically
|
|
139
|
-
- Use `./uv add <package>` (Unix) or `uv.exe add <package>` (Windows cmd.exe) for project dependencies
|
|
140
|
-
- Windows with Git Bash: Follow Unix-like shell instructions
|
|
141
|
-
- Windows cmd.exe without Admin rights: `uv.exe` is copied instead of symlinked
|
|
1
|
+
---
|
|
2
|
+
name: python-setup-uv
|
|
3
|
+
description: Setup Python environment with uv package manager - download binaries, create symlink, and install packages
|
|
4
|
+
allowedTools:
|
|
5
|
+
- Bash
|
|
6
|
+
- Read
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Python Environment Setup with uv
|
|
10
|
+
|
|
11
|
+
This skill sets up a Python environment using the uv package manager.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
- Internet connection for downloading uv
|
|
15
|
+
- Python 3.8+ should be available on PATH (or uv will prompt to install it)
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
**ABSOLUTE RULE:** Follow steps below EXACTLY AS IS. Do NOT skip/modify steps (nor detailed subtasks in each step) nor assume anything based on user platform information. Use URLs below EXACTLY AS IS. Follow steps below INCLUDING ALL DETAILS AND SUBSTEPS EXACTLY AS IS.
|
|
20
|
+
|
|
21
|
+
### Step 1: Ask user question about which installer to use
|
|
22
|
+
|
|
23
|
+
Use the question tool to ask which installer should be used:
|
|
24
|
+
|
|
25
|
+
- Official astral-uv installer (https://astral.sh)
|
|
26
|
+
- China mainland uv-custom installer (https://gitee.com/wangnov/uv-custom)
|
|
27
|
+
|
|
28
|
+
### Step 2: Detect Shell and Download uv Binary
|
|
29
|
+
|
|
30
|
+
First, detect your shell environment:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Detect shell type
|
|
34
|
+
# MSYSTEM is set by Git Bash, MINGW_PREFIX by MSYS2
|
|
35
|
+
if [ -n "$MSYSTEM" ] || [ -n "$MINGW_PREFIX" ] || command -v curl >/dev/null 2>&1; then
|
|
36
|
+
echo "Unix-like shell detected (Git Bash, bash, zsh, etc.)"
|
|
37
|
+
IS_UNIX_SHELL=true
|
|
38
|
+
else
|
|
39
|
+
echo "Windows cmd.exe detected"
|
|
40
|
+
IS_UNIX_SHELL=false
|
|
41
|
+
fi
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then download uv based on your shell (see below).
|
|
45
|
+
|
|
46
|
+
Choose the correct `UV_INSTALLER_URL` depending on answer you received in Step 1 from the user:
|
|
47
|
+
|
|
48
|
+
- If opted "Official astral-uv", UV_INSTALLER_URL should be `https://astral.sh/uv/install.sh` (Unix-like) or `https://astral.sh/uv/install.ps1` (Windows)
|
|
49
|
+
- If opted "China mainland uv-custom", UV_INSTALLER_URL should be `https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.sh` (Unix-like) or `https://gitee.com/wangnov/uv-custom/releases/download/latest/uv-installer-custom.ps1` (Windows)
|
|
50
|
+
|
|
51
|
+
**For Unix-like shells (Git Bash / macOS / Linux; use correct UV_INSTALLER_URL):**
|
|
52
|
+
```bash
|
|
53
|
+
mkdir -p .uv
|
|
54
|
+
curl -LsSf UV_INSTALLER_URL | UV_INSTALL_DIR="$(pwd)/.uv" sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**For Windows cmd.exe (if Git Bash unavailable; use correct UV_INSTALLER_URL):**
|
|
58
|
+
```bash
|
|
59
|
+
powershell -NoProfile -Command "New-Item -ItemType Directory -Force -Path .uv | Out-Null; $env:UV_INSTALL_DIR = (Get-Location).Path + '\.uv'; Invoke-RestMethod UV_INSTALLER_URL | Invoke-Expression"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 3: Create Symlink or Copy uv to Working Directory
|
|
63
|
+
|
|
64
|
+
**For Unix-like shells (Git Bash / macOS / Linux):**
|
|
65
|
+
```bash
|
|
66
|
+
ln -sf .uv/uv uv
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**For Windows cmd.exe:**
|
|
70
|
+
|
|
71
|
+
Try symlink first, fall back to copy if no Admin rights:
|
|
72
|
+
```bash
|
|
73
|
+
cmd /c "(mklink uv .uv\uv.exe) 2>nul || copy /Y .uv\uv.exe uv.exe"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Step 4: Create Virtual Environment and Install pandas
|
|
77
|
+
|
|
78
|
+
NOTE: this step (package installation) may timeout. If timed out, use the question tool to ask if the user would like to retry package installation. If successful, do NOT ask any question and continue to Step 5.
|
|
79
|
+
|
|
80
|
+
**For Unix-like shells:**
|
|
81
|
+
```bash
|
|
82
|
+
./uv venv
|
|
83
|
+
./uv pip install pandas
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**For Windows cmd.exe:**
|
|
87
|
+
```bash
|
|
88
|
+
uv.exe venv
|
|
89
|
+
uv.exe pip install pandas
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Step 5: Verification
|
|
93
|
+
|
|
94
|
+
**For Unix-like shells:**
|
|
95
|
+
```bash
|
|
96
|
+
./uv --version
|
|
97
|
+
./uv run python -c "import pandas; print(pandas.__version__)"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**For Windows cmd.exe:**
|
|
101
|
+
```bash
|
|
102
|
+
uv.exe --version
|
|
103
|
+
uv.exe run python -c "import pandas; print(pandas.__version__)"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Step 6: Update AGENTS.md
|
|
107
|
+
|
|
108
|
+
Use the question tool to ask if the user want to update AGENTS.md in WORKING DIRECTORY to "direct agents to use the installed UV Python" (options: "Yes" / "No"). If received no answer, continue to Step 7 (do NOT modify AGENTS.md NOR create directories). If received yes answer, follow steps belows.
|
|
109
|
+
|
|
110
|
+
1. If AGENTS.md not found in WORKING DIR, create an empty AGENTS.md.
|
|
111
|
+
2. Inspect AGENTS.md content. If you do not see the content block below, APPEND EXACTLY AS IS to end of AGENTS.md.
|
|
112
|
+
3. Check if `./.code/py` exist. If not, create the directories.
|
|
113
|
+
|
|
114
|
+
Content block:
|
|
115
|
+
|
|
116
|
+
```md
|
|
117
|
+
## Important note about Python
|
|
118
|
+
|
|
119
|
+
ALWAYS use the uv package manager available in WORKING DIRECTORY, including `uv add ...` or `uv pip ...` for package management and `uv run ...` to run python package executables.
|
|
120
|
+
|
|
121
|
+
ALWAYS save python scripts under path `./.code/py/` and run the script with `uv run python ...` whenever your work involves executing python scripts. Your script MUST contain concise docstrings and comments and use good engineering practices including separation of concerns.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Step 7: Return summary to user (Usage After Setup)
|
|
125
|
+
|
|
126
|
+
**For Unix-like shells:**
|
|
127
|
+
```bash
|
|
128
|
+
./uv run python your_script.py
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**For Windows cmd.exe:**
|
|
132
|
+
```bash
|
|
133
|
+
uv.exe run python your_script.py
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Notes
|
|
137
|
+
- Add `.uv/` and `.venv/` to `.gitignore`
|
|
138
|
+
- `uv run` handles venv activation automatically
|
|
139
|
+
- Use `./uv add <package>` (Unix) or `uv.exe add <package>` (Windows cmd.exe) for project dependencies
|
|
140
|
+
- Windows with Git Bash: Follow Unix-like shell instructions
|
|
141
|
+
- Windows cmd.exe without Admin rights: `uv.exe` is copied instead of symlinked
|