@yeyuan98/opencode-bioresearcher-plugin 1.5.0-alpha.0 → 1.5.1
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 +48 -36
- package/dist/index.js +8 -6
- package/dist/skills/bioresearcher-tests/README.md +90 -0
- package/dist/skills/bioresearcher-tests/SKILL.md +255 -0
- package/dist/skills/bioresearcher-tests/pyproject.toml +6 -0
- package/dist/skills/bioresearcher-tests/resources/json_samples/in_markdown.md.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/json_samples/nested_object.json.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/json_samples/schema_draft7.json.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/json_samples/simple_array.json.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/json_samples/simple_object.json.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/obo_sample.obo.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/pubmed_sample.xml.gz +0 -0
- package/dist/skills/bioresearcher-tests/resources/table_sample.xlsx.gz +0 -0
- package/dist/skills/bioresearcher-tests/test_cases/json_tests.md +137 -0
- package/dist/skills/bioresearcher-tests/test_cases/misc_tests.md +141 -0
- package/dist/skills/bioresearcher-tests/test_cases/parser_tests.md +80 -0
- package/dist/skills/bioresearcher-tests/test_cases/skill_tests.md +59 -0
- package/dist/skills/bioresearcher-tests/test_cases/table_tests.md +194 -0
- package/dist/skills/bioresearcher-tests/test_runner.py +607 -0
- package/dist/skills/env-jsonc-setup/SKILL.md +206 -206
- package/dist/skills/long-table-summary/SKILL.md +224 -153
- package/dist/skills/long-table-summary/combine_outputs.py +55 -9
- package/dist/skills/long-table-summary/generate_prompts.py +9 -0
- package/dist/skills/pubmed-weekly/pubmed_weekly.py +130 -29
- package/dist/{db-tools → tools/db}/backends/mysql/translator.js +23 -23
- package/dist/{db-tools → tools/db}/tools.js +34 -34
- package/dist/{misc-tools → tools/misc}/json-validate.js +4 -5
- package/dist/tools/parser/obo/index.d.ts +2 -0
- package/dist/tools/parser/obo/index.js +2 -0
- package/dist/tools/parser/obo/obo.d.ts +17 -0
- package/dist/tools/parser/obo/obo.js +216 -0
- package/dist/tools/parser/obo/types.d.ts +166 -0
- package/dist/tools/parser/obo/utils.d.ts +21 -0
- package/dist/tools/parser/obo/utils.js +411 -0
- package/dist/tools/parser/pubmed/types.js +1 -0
- package/dist/{skill-tools → tools/skill}/registry.js +1 -1
- package/package.json +1 -1
- package/dist/db-tools/executor.d.ts +0 -13
- package/dist/db-tools/executor.js +0 -54
- package/dist/db-tools/pool.d.ts +0 -8
- package/dist/db-tools/pool.js +0 -49
- package/dist/db-tools/tools/index.d.ts +0 -27
- package/dist/db-tools/tools/index.js +0 -191
- package/dist/db-tools/types.d.ts +0 -94
- package/dist/db-tools/types.js +0 -40
- package/dist/misc-tools/json-tools.d.ts +0 -33
- package/dist/misc-tools/json-tools.js +0 -187
- package/dist/skill/frontmatter.d.ts +0 -2
- package/dist/skill/frontmatter.js +0 -65
- package/dist/skill/index.d.ts +0 -3
- package/dist/skill/index.js +0 -2
- package/dist/skill/registry.d.ts +0 -11
- package/dist/skill/registry.js +0 -64
- package/dist/skill/tool.d.ts +0 -9
- package/dist/skill/tool.js +0 -115
- package/dist/skill/types.d.ts +0 -22
- package/dist/skill/types.js +0 -7
- /package/dist/{db-tools → tools/db}/backends/index.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/index.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/backend.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/backend.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/connection.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/connection.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/index.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/index.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/translator.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mongodb/translator.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/backend.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/backend.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/connection.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/connection.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/index.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/index.js +0 -0
- /package/dist/{db-tools → tools/db}/backends/mysql/translator.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/core/base.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/core/base.js +0 -0
- /package/dist/{db-tools → tools/db}/core/config-loader.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/core/config-loader.js +0 -0
- /package/dist/{db-tools → tools/db}/core/index.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/core/index.js +0 -0
- /package/dist/{db-tools → tools/db}/core/jsonc-parser.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/core/jsonc-parser.js +0 -0
- /package/dist/{db-tools → tools/db}/core/validator.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/core/validator.js +0 -0
- /package/dist/{db-tools → tools/db}/index.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/index.js +0 -0
- /package/dist/{db-tools → tools/db}/interface/backend.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/interface/backend.js +0 -0
- /package/dist/{db-tools → tools/db}/interface/connection.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/interface/connection.js +0 -0
- /package/dist/{db-tools → tools/db}/interface/index.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/interface/index.js +0 -0
- /package/dist/{db-tools → tools/db}/interface/query.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/interface/query.js +0 -0
- /package/dist/{db-tools → tools/db}/interface/schema.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/interface/schema.js +0 -0
- /package/dist/{db-tools → tools/db}/tools.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/utils.d.ts +0 -0
- /package/dist/{db-tools → tools/db}/utils.js +0 -0
- /package/dist/{misc-tools → tools/misc}/calculator.d.ts +0 -0
- /package/dist/{misc-tools → tools/misc}/calculator.js +0 -0
- /package/dist/{misc-tools → tools/misc}/index.d.ts +0 -0
- /package/dist/{misc-tools → tools/misc}/index.js +0 -0
- /package/dist/{misc-tools → tools/misc}/json-extract.d.ts +0 -0
- /package/dist/{misc-tools → tools/misc}/json-extract.js +0 -0
- /package/dist/{misc-tools → tools/misc}/json-infer.d.ts +0 -0
- /package/dist/{misc-tools → tools/misc}/json-infer.js +0 -0
- /package/dist/{misc-tools → tools/misc}/json-validate.d.ts +0 -0
- /package/dist/{misc-tools → tools/misc}/timer.d.ts +0 -0
- /package/dist/{misc-tools → tools/misc}/timer.js +0 -0
- /package/dist/{parser-tools/pubmed → tools/parser/obo}/types.js +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/index.d.ts +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/index.js +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/pubmed.d.ts +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/pubmed.js +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/types.d.ts +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/utils.d.ts +0 -0
- /package/dist/{parser-tools → tools/parser}/pubmed/utils.js +0 -0
- /package/dist/{skill-tools → tools/skill}/frontmatter.d.ts +0 -0
- /package/dist/{skill-tools → tools/skill}/frontmatter.js +0 -0
- /package/dist/{skill-tools → tools/skill}/index.d.ts +0 -0
- /package/dist/{skill-tools → tools/skill}/index.js +0 -0
- /package/dist/{skill-tools → tools/skill}/registry.d.ts +0 -0
- /package/dist/{skill-tools → tools/skill}/tool.d.ts +0 -0
- /package/dist/{skill-tools → tools/skill}/tool.js +0 -0
- /package/dist/{skill-tools → tools/skill}/types.d.ts +0 -0
- /package/dist/{skill-tools → tools/skill}/types.js +0 -0
- /package/dist/{table-tools → tools/table}/index.d.ts +0 -0
- /package/dist/{table-tools → tools/table}/index.js +0 -0
- /package/dist/{table-tools → tools/table}/tools.d.ts +0 -0
- /package/dist/{table-tools → tools/table}/tools.js +0 -0
- /package/dist/{table-tools → tools/table}/utils.d.ts +0 -0
- /package/dist/{table-tools → tools/table}/utils.js +0 -0
|
@@ -21,6 +21,21 @@ import argparse
|
|
|
21
21
|
from datetime import datetime, timedelta
|
|
22
22
|
from typing import List, Dict, Any
|
|
23
23
|
|
|
24
|
+
MONTH_MAP = {
|
|
25
|
+
"Jan": 1,
|
|
26
|
+
"Feb": 2,
|
|
27
|
+
"Mar": 3,
|
|
28
|
+
"Apr": 4,
|
|
29
|
+
"May": 5,
|
|
30
|
+
"Jun": 6,
|
|
31
|
+
"Jul": 7,
|
|
32
|
+
"Aug": 8,
|
|
33
|
+
"Sep": 9,
|
|
34
|
+
"Oct": 10,
|
|
35
|
+
"Nov": 11,
|
|
36
|
+
"Dec": 12,
|
|
37
|
+
}
|
|
38
|
+
|
|
24
39
|
|
|
25
40
|
def calculate_week() -> str:
|
|
26
41
|
"""Calculate the past week's date range (Monday-Sunday).
|
|
@@ -46,6 +61,116 @@ def calculate_week() -> str:
|
|
|
46
61
|
return f"{week_start}-{week_end}"
|
|
47
62
|
|
|
48
63
|
|
|
64
|
+
def infer_year(month: int, day: int, hour: int, minute: int) -> int:
|
|
65
|
+
"""Infer year for MMM DD HH:MM format.
|
|
66
|
+
|
|
67
|
+
Uses current year if date is not in the future.
|
|
68
|
+
Uses previous year if inferred date is in the future.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
month: Month number (1-12)
|
|
72
|
+
day: Day of month
|
|
73
|
+
hour: Hour (0-23)
|
|
74
|
+
minute: Minute (0-59)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Inferred year as integer
|
|
78
|
+
"""
|
|
79
|
+
now = datetime.now()
|
|
80
|
+
date_this_year = datetime(now.year, month, day, hour, minute)
|
|
81
|
+
|
|
82
|
+
if date_this_year > now:
|
|
83
|
+
return now.year - 1
|
|
84
|
+
return now.year
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def parse_ftp_listing_to_dict(content: str) -> Dict[str, datetime]:
|
|
88
|
+
"""Parse FTP directory listing into {filename: datetime} dict.
|
|
89
|
+
|
|
90
|
+
Supports multiple date formats with regex fallback chain:
|
|
91
|
+
1. Unix ls format - MMM DD HH:MM (current year)
|
|
92
|
+
2. Unix ls format - MMM DD YYYY (older files)
|
|
93
|
+
3. ISO 8601 format - YYYY-MM-DD HH:MM
|
|
94
|
+
4. European format - DD-MMM-YYYY HH:MM
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
content: Raw FTP directory listing content
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Dictionary mapping filename to datetime object
|
|
101
|
+
"""
|
|
102
|
+
file_dates = {}
|
|
103
|
+
|
|
104
|
+
for line in content.split("\n"):
|
|
105
|
+
line = line.strip()
|
|
106
|
+
if not line or line.startswith("total"):
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
filename = None
|
|
110
|
+
file_date = None
|
|
111
|
+
|
|
112
|
+
match = re.match(
|
|
113
|
+
r"^\S+\s+\d+\s+\S+\s+\S+\s+\d+\s+(\w{3})\s+(\d{1,2})\s+(\d{2}:\d{2})\s+(.+)$",
|
|
114
|
+
line,
|
|
115
|
+
)
|
|
116
|
+
if match:
|
|
117
|
+
month_str, day_str, time_str, fn = match.groups()
|
|
118
|
+
month = MONTH_MAP.get(month_str)
|
|
119
|
+
if month:
|
|
120
|
+
day = int(day_str)
|
|
121
|
+
hour, minute = map(int, time_str.split(":"))
|
|
122
|
+
year = infer_year(month, day, hour, minute)
|
|
123
|
+
filename = fn
|
|
124
|
+
file_date = datetime(year, month, day, hour, minute)
|
|
125
|
+
|
|
126
|
+
if not file_date:
|
|
127
|
+
match = re.match(
|
|
128
|
+
r"^\S+\s+\d+\s+\S+\s+\S+\s+\d+\s+(\w{3})\s+(\d{1,2})\s+(\d{4})\s+(.+)$",
|
|
129
|
+
line,
|
|
130
|
+
)
|
|
131
|
+
if match:
|
|
132
|
+
month_str, day_str, year_str, fn = match.groups()
|
|
133
|
+
month = MONTH_MAP.get(month_str)
|
|
134
|
+
if month:
|
|
135
|
+
year = int(year_str)
|
|
136
|
+
day = int(day_str)
|
|
137
|
+
filename = fn
|
|
138
|
+
file_date = datetime(year, month, day)
|
|
139
|
+
|
|
140
|
+
if not file_date:
|
|
141
|
+
match = re.match(r"^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+(.+)$", line)
|
|
142
|
+
if match:
|
|
143
|
+
date_str, time_str, fn = match.groups()
|
|
144
|
+
datetime_str = f"{date_str} {time_str}"
|
|
145
|
+
try:
|
|
146
|
+
file_date = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M")
|
|
147
|
+
filename = fn
|
|
148
|
+
except ValueError:
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
if not file_date:
|
|
152
|
+
match = re.match(
|
|
153
|
+
r"^(\d{1,2})-(\w{3})-(\d{4})\s+(\d{2}:\d{2})\s+(.+)$", line
|
|
154
|
+
)
|
|
155
|
+
if match:
|
|
156
|
+
day_str, month_str, year_str, time_str, fn = match.groups()
|
|
157
|
+
month = MONTH_MAP.get(month_str)
|
|
158
|
+
if month:
|
|
159
|
+
day = int(day_str)
|
|
160
|
+
year = int(year_str)
|
|
161
|
+
hour, minute = map(int, time_str.split(":"))
|
|
162
|
+
try:
|
|
163
|
+
file_date = datetime(year, month, day, hour, minute)
|
|
164
|
+
filename = fn
|
|
165
|
+
except ValueError:
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
if filename and file_date and filename not in file_dates:
|
|
169
|
+
file_dates[filename] = file_date
|
|
170
|
+
|
|
171
|
+
return file_dates
|
|
172
|
+
|
|
173
|
+
|
|
49
174
|
def parse_date_from_filename(filename: str) -> datetime | None:
|
|
50
175
|
"""Extract date from PubMed filename.
|
|
51
176
|
|
|
@@ -147,37 +272,13 @@ def filter_files_by_date(week_name: str, file_list: List[str]) -> List[str]:
|
|
|
147
272
|
with urllib.request.urlopen(url) as response:
|
|
148
273
|
content = response.read().decode("utf-8", errors="ignore")
|
|
149
274
|
|
|
150
|
-
|
|
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
|
|
275
|
+
file_dates = parse_ftp_listing_to_dict(content)
|
|
175
276
|
|
|
176
|
-
# Filter files within date range
|
|
277
|
+
# Filter files within date range AND in provided file_list
|
|
177
278
|
filtered_files = [
|
|
178
|
-
|
|
179
|
-
for
|
|
180
|
-
if start_date <=
|
|
279
|
+
f
|
|
280
|
+
for f in file_list
|
|
281
|
+
if f in file_dates and start_date <= file_dates[f] <= end_date
|
|
181
282
|
]
|
|
182
283
|
|
|
183
284
|
return sorted(filtered_files)
|
|
@@ -40,28 +40,28 @@ export function mapMySQLColumnsToSchema(rows) {
|
|
|
40
40
|
comment: row.Comment || row.COLUMN_COMMENT || row.comment || null,
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
|
-
export const LIST_TABLES_SQL = `
|
|
44
|
-
SELECT
|
|
45
|
-
TABLE_NAME,
|
|
46
|
-
TABLE_TYPE,
|
|
47
|
-
ENGINE,
|
|
48
|
-
TABLE_ROWS,
|
|
49
|
-
CREATE_TIME,
|
|
50
|
-
TABLE_COMMENT
|
|
51
|
-
FROM information_schema.TABLES
|
|
52
|
-
WHERE TABLE_SCHEMA = DATABASE()
|
|
53
|
-
ORDER BY TABLE_NAME
|
|
43
|
+
export const LIST_TABLES_SQL = `
|
|
44
|
+
SELECT
|
|
45
|
+
TABLE_NAME,
|
|
46
|
+
TABLE_TYPE,
|
|
47
|
+
ENGINE,
|
|
48
|
+
TABLE_ROWS,
|
|
49
|
+
CREATE_TIME,
|
|
50
|
+
TABLE_COMMENT
|
|
51
|
+
FROM information_schema.TABLES
|
|
52
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
53
|
+
ORDER BY TABLE_NAME
|
|
54
54
|
`;
|
|
55
|
-
export const DESCRIBE_TABLE_SQL = `
|
|
56
|
-
SELECT
|
|
57
|
-
COLUMN_NAME as Field,
|
|
58
|
-
COLUMN_TYPE as Type,
|
|
59
|
-
IS_NULLABLE as \`Null\`,
|
|
60
|
-
COLUMN_KEY as \`Key\`,
|
|
61
|
-
COLUMN_DEFAULT as \`Default\`,
|
|
62
|
-
EXTRA as Extra,
|
|
63
|
-
COLUMN_COMMENT as Comment
|
|
64
|
-
FROM information_schema.COLUMNS
|
|
65
|
-
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
|
|
66
|
-
ORDER BY ORDINAL_POSITION
|
|
55
|
+
export const DESCRIBE_TABLE_SQL = `
|
|
56
|
+
SELECT
|
|
57
|
+
COLUMN_NAME as Field,
|
|
58
|
+
COLUMN_TYPE as Type,
|
|
59
|
+
IS_NULLABLE as \`Null\`,
|
|
60
|
+
COLUMN_KEY as \`Key\`,
|
|
61
|
+
COLUMN_DEFAULT as \`Default\`,
|
|
62
|
+
EXTRA as Extra,
|
|
63
|
+
COLUMN_COMMENT as Comment
|
|
64
|
+
FROM information_schema.COLUMNS
|
|
65
|
+
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?
|
|
66
|
+
ORDER BY ORDINAL_POSITION
|
|
67
67
|
`;
|
|
@@ -13,17 +13,17 @@ async function getOrCreateBackend() {
|
|
|
13
13
|
return backend;
|
|
14
14
|
}
|
|
15
15
|
export const dbQuery = tool({
|
|
16
|
-
description: `Execute a SELECT query on the database with named parameters.
|
|
17
|
-
|
|
18
|
-
**Examples:**
|
|
19
|
-
- Simple query: \`SELECT * FROM users LIMIT 10\`
|
|
20
|
-
- With named params: \`SELECT * FROM users WHERE status = :status AND created_at > :date\`
|
|
21
|
-
- Join query: \`SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id\`
|
|
22
|
-
|
|
23
|
-
**Parameters:**
|
|
24
|
-
- Use named placeholders like \`:paramName\` for parameters
|
|
25
|
-
- Pass parameter values in the \`params\` object: \`{ "status": "active", "date": "2024-01-01" }\`
|
|
26
|
-
|
|
16
|
+
description: `Execute a SELECT query on the database with named parameters.
|
|
17
|
+
|
|
18
|
+
**Examples:**
|
|
19
|
+
- Simple query: \`SELECT * FROM users LIMIT 10\`
|
|
20
|
+
- With named params: \`SELECT * FROM users WHERE status = :status AND created_at > :date\`
|
|
21
|
+
- Join query: \`SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id\`
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- Use named placeholders like \`:paramName\` for parameters
|
|
25
|
+
- Pass parameter values in the \`params\` object: \`{ "status": "active", "date": "2024-01-01" }\`
|
|
26
|
+
|
|
27
27
|
**Note:** Only SELECT statements are allowed (read-only access).`,
|
|
28
28
|
args: {
|
|
29
29
|
sql: z.string().describe('SELECT SQL query to execute. Use named placeholders like :name for parameters.'),
|
|
@@ -48,15 +48,15 @@ export const dbQuery = tool({
|
|
|
48
48
|
},
|
|
49
49
|
});
|
|
50
50
|
export const dbListTables = tool({
|
|
51
|
-
description: `List all tables/collections in the current database with metadata.
|
|
52
|
-
|
|
53
|
-
**Returns:**
|
|
54
|
-
- Table/collection names and types (TABLE, VIEW, COLLECTION)
|
|
55
|
-
- Storage engine (InnoDB, MyISAM, etc.) for MySQL
|
|
56
|
-
- Approximate row count
|
|
57
|
-
- Creation time (MySQL only)
|
|
58
|
-
- Table comments
|
|
59
|
-
|
|
51
|
+
description: `List all tables/collections in the current database with metadata.
|
|
52
|
+
|
|
53
|
+
**Returns:**
|
|
54
|
+
- Table/collection names and types (TABLE, VIEW, COLLECTION)
|
|
55
|
+
- Storage engine (InnoDB, MyISAM, etc.) for MySQL
|
|
56
|
+
- Approximate row count
|
|
57
|
+
- Creation time (MySQL only)
|
|
58
|
+
- Table comments
|
|
59
|
+
|
|
60
60
|
**Usage:** Call this first to discover what tables are available before using dbDescribeTable or dbQuery.`,
|
|
61
61
|
args: {},
|
|
62
62
|
execute: async (_args, _context) => {
|
|
@@ -72,20 +72,20 @@ export const dbListTables = tool({
|
|
|
72
72
|
},
|
|
73
73
|
});
|
|
74
74
|
export const dbDescribeTable = tool({
|
|
75
|
-
description: `Get the column schema for a specific table/collection.
|
|
76
|
-
|
|
77
|
-
**Returns for each column/field:**
|
|
78
|
-
- Field name
|
|
79
|
-
- Data type (VARCHAR, INT, TEXT, DATETIME, etc.)
|
|
80
|
-
- Nullable status
|
|
81
|
-
- Key type (PRI for primary key, UNI for unique, MUL for multiple)
|
|
82
|
-
- Default value
|
|
83
|
-
- Extra info (auto_increment, etc.)
|
|
84
|
-
- Column comments
|
|
85
|
-
|
|
86
|
-
**Workflow:**
|
|
87
|
-
1. Use \`dbListTables\` first to see available tables/collections
|
|
88
|
-
2. Use this tool to understand the column/field structure
|
|
75
|
+
description: `Get the column schema for a specific table/collection.
|
|
76
|
+
|
|
77
|
+
**Returns for each column/field:**
|
|
78
|
+
- Field name
|
|
79
|
+
- Data type (VARCHAR, INT, TEXT, DATETIME, etc.)
|
|
80
|
+
- Nullable status
|
|
81
|
+
- Key type (PRI for primary key, UNI for unique, MUL for multiple)
|
|
82
|
+
- Default value
|
|
83
|
+
- Extra info (auto_increment, etc.)
|
|
84
|
+
- Column comments
|
|
85
|
+
|
|
86
|
+
**Workflow:**
|
|
87
|
+
1. Use \`dbListTables\` first to see available tables/collections
|
|
88
|
+
2. Use this tool to understand the column/field structure
|
|
89
89
|
3. Use \`dbQuery\` to query the data`,
|
|
90
90
|
args: {
|
|
91
91
|
table_name: z.string().describe('Name of the table/collection to describe. Use dbListTables to see available tables.'),
|
|
@@ -89,10 +89,8 @@ export const jsonValidate = tool({
|
|
|
89
89
|
let parsedData;
|
|
90
90
|
let parsedSchema;
|
|
91
91
|
let schemaString = args.schema;
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const isRelativePath = /^\.{1,2}(\/|\\)/.test(args.schema);
|
|
95
|
-
if (isWindowsPath || isUnixPath || isRelativePath) {
|
|
92
|
+
const looksLikeInlineJson = /^\s*[\[{]/.test(args.schema);
|
|
93
|
+
if (!looksLikeInlineJson) {
|
|
96
94
|
const resolvedPath = path.isAbsolute(args.schema)
|
|
97
95
|
? args.schema
|
|
98
96
|
: path.join(context.directory, args.schema);
|
|
@@ -110,7 +108,8 @@ export const jsonValidate = tool({
|
|
|
110
108
|
hints: [
|
|
111
109
|
'Check the file path for typos',
|
|
112
110
|
'Ensure file exists and is accessible',
|
|
113
|
-
'Use a relative path from the project directory'
|
|
111
|
+
'Use a relative path from the project directory',
|
|
112
|
+
'Or provide schema as inline JSON string (starting with { or [)'
|
|
114
113
|
]
|
|
115
114
|
}
|
|
116
115
|
}, null, 2);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ToolContext } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const parse_obo_file: {
|
|
4
|
+
description: string;
|
|
5
|
+
args: {
|
|
6
|
+
filePath: z.ZodString;
|
|
7
|
+
outputFileName: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
8
|
+
outputDir: z.ZodOptional<z.ZodString>;
|
|
9
|
+
verbose: z.ZodDefault<z.ZodBoolean>;
|
|
10
|
+
};
|
|
11
|
+
execute(args: {
|
|
12
|
+
filePath: string;
|
|
13
|
+
outputFileName: string;
|
|
14
|
+
verbose: boolean;
|
|
15
|
+
outputDir?: string | undefined;
|
|
16
|
+
}, context: ToolContext): Promise<string>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
3
|
+
import { join, resolve } from 'path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { parseLine, createEmptyTermFrame, createEmptyTypedefFrame, createEmptyInstanceFrame, processTagValue, frameToCSVRow, generateCSV } from './utils.js';
|
|
6
|
+
function formatError(error) {
|
|
7
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
8
|
+
return JSON.stringify({ error: message }, null, 2);
|
|
9
|
+
}
|
|
10
|
+
function resolvePath(filePath, basePath) {
|
|
11
|
+
if (!basePath) {
|
|
12
|
+
return filePath;
|
|
13
|
+
}
|
|
14
|
+
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
15
|
+
return resolve(basePath, filePath);
|
|
16
|
+
}
|
|
17
|
+
if (filePath.startsWith('/') || /^[A-Za-z]:/.test(filePath)) {
|
|
18
|
+
return filePath;
|
|
19
|
+
}
|
|
20
|
+
return resolve(basePath, filePath);
|
|
21
|
+
}
|
|
22
|
+
function ensureDir(dirPath) {
|
|
23
|
+
if (!existsSync(dirPath)) {
|
|
24
|
+
mkdirSync(dirPath, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function parseOBOFile(content) {
|
|
28
|
+
const lines = content.split('\n');
|
|
29
|
+
const header = {
|
|
30
|
+
subsetdefs: [],
|
|
31
|
+
synonymtypedefs: []
|
|
32
|
+
};
|
|
33
|
+
const frames = [];
|
|
34
|
+
let currentFrame = null;
|
|
35
|
+
let inHeader = true;
|
|
36
|
+
let termCount = 0;
|
|
37
|
+
let typedefCount = 0;
|
|
38
|
+
let instanceCount = 0;
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const trimmed = line.trim();
|
|
41
|
+
if (!trimmed)
|
|
42
|
+
continue;
|
|
43
|
+
if (trimmed.startsWith('!')) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (trimmed === '[Term]') {
|
|
47
|
+
inHeader = false;
|
|
48
|
+
if (currentFrame) {
|
|
49
|
+
frames.push(currentFrame);
|
|
50
|
+
}
|
|
51
|
+
currentFrame = createEmptyTermFrame();
|
|
52
|
+
termCount++;
|
|
53
|
+
}
|
|
54
|
+
else if (trimmed === '[Typedef]') {
|
|
55
|
+
inHeader = false;
|
|
56
|
+
if (currentFrame) {
|
|
57
|
+
frames.push(currentFrame);
|
|
58
|
+
}
|
|
59
|
+
currentFrame = createEmptyTypedefFrame();
|
|
60
|
+
typedefCount++;
|
|
61
|
+
}
|
|
62
|
+
else if (trimmed === '[Instance]') {
|
|
63
|
+
inHeader = false;
|
|
64
|
+
if (currentFrame) {
|
|
65
|
+
frames.push(currentFrame);
|
|
66
|
+
}
|
|
67
|
+
currentFrame = createEmptyInstanceFrame();
|
|
68
|
+
instanceCount++;
|
|
69
|
+
}
|
|
70
|
+
else if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
|
|
71
|
+
if (currentFrame) {
|
|
72
|
+
frames.push(currentFrame);
|
|
73
|
+
currentFrame = null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (inHeader) {
|
|
77
|
+
const parsed = parseLine(line);
|
|
78
|
+
if (parsed) {
|
|
79
|
+
const { tag, value } = parsed;
|
|
80
|
+
switch (tag) {
|
|
81
|
+
case 'format-version':
|
|
82
|
+
header.format_version = value;
|
|
83
|
+
break;
|
|
84
|
+
case 'data-version':
|
|
85
|
+
header.data_version = value;
|
|
86
|
+
break;
|
|
87
|
+
case 'date':
|
|
88
|
+
header.date = value;
|
|
89
|
+
break;
|
|
90
|
+
case 'saved-by':
|
|
91
|
+
header.saved_by = value;
|
|
92
|
+
break;
|
|
93
|
+
case 'auto-generated-by':
|
|
94
|
+
header.auto_generated_by = value;
|
|
95
|
+
break;
|
|
96
|
+
case 'import':
|
|
97
|
+
if (!header.import)
|
|
98
|
+
header.import = [];
|
|
99
|
+
if (Array.isArray(header.import)) {
|
|
100
|
+
header.import.push(value);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
header.import = [header.import, value];
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
case 'default-namespace':
|
|
107
|
+
header.default_namespace = value;
|
|
108
|
+
break;
|
|
109
|
+
case 'ontology':
|
|
110
|
+
header.ontology = value;
|
|
111
|
+
break;
|
|
112
|
+
case 'remark':
|
|
113
|
+
if (!header.remark)
|
|
114
|
+
header.remark = [];
|
|
115
|
+
if (Array.isArray(header.remark)) {
|
|
116
|
+
header.remark.push(value);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
header.remark = [header.remark, value];
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
case 'property_value':
|
|
123
|
+
if (!header.property_values)
|
|
124
|
+
header.property_values = [];
|
|
125
|
+
header.property_values.push({ tag: '', value });
|
|
126
|
+
break;
|
|
127
|
+
case 'owl-axioms':
|
|
128
|
+
header.owl_axioms = value;
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (currentFrame) {
|
|
134
|
+
const parsed = parseLine(line);
|
|
135
|
+
if (parsed && parsed.tag) {
|
|
136
|
+
processTagValue(currentFrame, parsed.tag, parsed.value);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (currentFrame) {
|
|
141
|
+
frames.push(currentFrame);
|
|
142
|
+
}
|
|
143
|
+
const stats = {
|
|
144
|
+
total: termCount + typedefCount + instanceCount,
|
|
145
|
+
terms: termCount,
|
|
146
|
+
typedefs: typedefCount,
|
|
147
|
+
instances: instanceCount
|
|
148
|
+
};
|
|
149
|
+
return { document: { header, frames }, stats };
|
|
150
|
+
}
|
|
151
|
+
export const parse_obo_file = tool({
|
|
152
|
+
description: 'Parse OBO (Open Biological and Biomedical Ontology) files and convert to CSV format. Handles Term, Typedef, and Instance frames with proper escaping and comment handling.',
|
|
153
|
+
args: {
|
|
154
|
+
filePath: z.string()
|
|
155
|
+
.describe('Path to OBO file (.obo)'),
|
|
156
|
+
outputFileName: z.string()
|
|
157
|
+
.optional()
|
|
158
|
+
.default('obo_output.csv')
|
|
159
|
+
.describe('Custom output CSV file name (default: obo_output.csv)'),
|
|
160
|
+
outputDir: z.string()
|
|
161
|
+
.optional()
|
|
162
|
+
.describe('Custom output directory (default: ./tmp/opencode/<sessionId>/)'),
|
|
163
|
+
verbose: z.boolean()
|
|
164
|
+
.default(false)
|
|
165
|
+
.describe('Enable verbose logging for debugging')
|
|
166
|
+
},
|
|
167
|
+
execute: async (args, context) => {
|
|
168
|
+
const verbose = args.verbose ?? false;
|
|
169
|
+
try {
|
|
170
|
+
const { filePath, outputFileName = 'obo_output.csv', outputDir } = args;
|
|
171
|
+
if (verbose)
|
|
172
|
+
console.log('Starting OBO file parsing...');
|
|
173
|
+
const resolvedPath = resolvePath(outputDir || './tmp/opencode', context.directory);
|
|
174
|
+
ensureDir(resolvedPath);
|
|
175
|
+
if (verbose)
|
|
176
|
+
console.log(`Output directory: ${resolvedPath}`);
|
|
177
|
+
if (verbose)
|
|
178
|
+
console.log(`Reading file: ${filePath}`);
|
|
179
|
+
const fileContent = readFileSync(filePath, 'utf-8');
|
|
180
|
+
if (verbose)
|
|
181
|
+
console.log('Parsing OBO document...');
|
|
182
|
+
const { document, stats } = parseOBOFile(fileContent);
|
|
183
|
+
if (verbose) {
|
|
184
|
+
console.log(`Parsing complete:`);
|
|
185
|
+
console.log(` Total frames: ${stats.total}`);
|
|
186
|
+
console.log(` Terms: ${stats.terms}`);
|
|
187
|
+
console.log(` Typedefs: ${stats.typedefs}`);
|
|
188
|
+
console.log(` Instances: ${stats.instances}`);
|
|
189
|
+
}
|
|
190
|
+
const csvRows = [];
|
|
191
|
+
if (verbose)
|
|
192
|
+
console.log('Flattening frames to CSV rows...');
|
|
193
|
+
for (const frame of document.frames) {
|
|
194
|
+
csvRows.push(frameToCSVRow(frame));
|
|
195
|
+
}
|
|
196
|
+
if (verbose)
|
|
197
|
+
console.log(`Converted ${csvRows.length} frames to CSV rows`);
|
|
198
|
+
const csvContent = generateCSV(csvRows);
|
|
199
|
+
const outputPath = join(resolvedPath, outputFileName);
|
|
200
|
+
writeFileSync(outputPath, csvContent, 'utf-8');
|
|
201
|
+
if (verbose)
|
|
202
|
+
console.log(`Wrote CSV file: ${outputPath}`);
|
|
203
|
+
return JSON.stringify({
|
|
204
|
+
success: true,
|
|
205
|
+
filePath: outputPath,
|
|
206
|
+
stats: stats,
|
|
207
|
+
message: `Successfully parsed ${stats.total} frames (${stats.terms} terms, ${stats.typedefs} typedefs, ${stats.instances} instances) to ${outputPath}`
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
if (verbose)
|
|
212
|
+
console.error(`Error: ${error.message}`);
|
|
213
|
+
return formatError(error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|