@yeyuan98/opencode-bioresearcher-plugin 1.3.1-alpha.1 → 1.4.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 +14 -0
- package/dist/index.js +4 -1
- package/dist/misc-tools/index.d.ts +3 -0
- package/dist/misc-tools/index.js +3 -0
- package/dist/misc-tools/json-extract.d.ts +13 -0
- package/dist/misc-tools/json-extract.js +394 -0
- package/dist/misc-tools/json-infer.d.ts +13 -0
- package/dist/misc-tools/json-infer.js +199 -0
- package/dist/misc-tools/json-tools.d.ts +33 -0
- package/dist/misc-tools/json-tools.js +187 -0
- package/dist/misc-tools/json-validate.d.ts +13 -0
- package/dist/misc-tools/json-validate.js +228 -0
- package/dist/skills/bioresearcher-core/README.md +210 -0
- package/dist/skills/bioresearcher-core/SKILL.md +128 -0
- package/dist/skills/bioresearcher-core/examples/contexts.json +29 -0
- package/dist/skills/bioresearcher-core/examples/data-exchange-example.md +303 -0
- package/dist/skills/bioresearcher-core/examples/template.md +49 -0
- package/dist/skills/bioresearcher-core/patterns/calculator.md +215 -0
- package/dist/skills/bioresearcher-core/patterns/data-exchange.md +406 -0
- package/dist/skills/bioresearcher-core/patterns/json-tools.md +263 -0
- package/dist/skills/bioresearcher-core/patterns/progress.md +127 -0
- package/dist/skills/bioresearcher-core/patterns/retry.md +110 -0
- package/dist/skills/bioresearcher-core/patterns/shell-commands.md +79 -0
- package/dist/skills/bioresearcher-core/patterns/subagent-waves.md +186 -0
- package/dist/skills/bioresearcher-core/patterns/table-tools.md +260 -0
- package/dist/skills/bioresearcher-core/patterns/user-confirmation.md +187 -0
- package/dist/skills/bioresearcher-core/python/template.md +273 -0
- package/dist/skills/bioresearcher-core/python/template.py +323 -0
- package/dist/skills/long-table-summary/SKILL.md +437 -0
- package/dist/skills/long-table-summary/combine_outputs.py +336 -0
- package/dist/skills/long-table-summary/generate_prompts.py +211 -0
- package/dist/skills/long-table-summary/pyproject.toml +8 -0
- package/dist/skills/pubmed-weekly/SKILL.md +329 -329
- package/dist/skills/pubmed-weekly/pubmed_weekly.py +411 -411
- package/dist/skills/pubmed-weekly/pyproject.toml +8 -8
- package/package.json +7 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ToolContext } from '@opencode-ai/plugin/tool';
|
|
2
|
+
export declare const jsonValidate: {
|
|
3
|
+
description: string;
|
|
4
|
+
args: {
|
|
5
|
+
data: any;
|
|
6
|
+
schema: any;
|
|
7
|
+
version: any;
|
|
8
|
+
};
|
|
9
|
+
execute(args: {
|
|
10
|
+
[x: string]: any;
|
|
11
|
+
}, context: ToolContext): Promise<string>;
|
|
12
|
+
};
|
|
13
|
+
export declare const jsonExtract: {
|
|
14
|
+
description: string;
|
|
15
|
+
args: {
|
|
16
|
+
content: any;
|
|
17
|
+
path: any;
|
|
18
|
+
};
|
|
19
|
+
execute(args: {
|
|
20
|
+
[x: string]: any;
|
|
21
|
+
}, context: ToolContext): Promise<string>;
|
|
22
|
+
};
|
|
23
|
+
export declare const jsonInferSchema: {
|
|
24
|
+
description: string;
|
|
25
|
+
args: {
|
|
26
|
+
data: any;
|
|
27
|
+
title: any;
|
|
28
|
+
strict: any;
|
|
29
|
+
};
|
|
30
|
+
execute(args: {
|
|
31
|
+
[x: string]: any;
|
|
32
|
+
}, context: ToolContext): Promise<string>;
|
|
33
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
2
|
+
const zod = require('zod');
|
|
3
|
+
function getFromJSONSchema() {
|
|
4
|
+
if (zod.fromJSONSchema) {
|
|
5
|
+
return zod.fromJSONSchema;
|
|
6
|
+
}
|
|
7
|
+
const v4classic = require('zod/v4/classic/external.js');
|
|
8
|
+
if (v4classic.fromJSONSchema) {
|
|
9
|
+
return v4classic.fromJSONSchema;
|
|
10
|
+
}
|
|
11
|
+
throw new Error('fromJSONSchema not available in zod package');
|
|
12
|
+
}
|
|
13
|
+
const fromJSONSchema = getFromJSONSchema();
|
|
14
|
+
export const jsonValidate = tool({
|
|
15
|
+
description: `Validate JSON data against a JSON Schema.
|
|
16
|
+
Returns validation result with detailed error paths and messages.
|
|
17
|
+
Supports JSON Schema draft-04, draft-07, draft-2020-12, and OpenAPI 3.0.`,
|
|
18
|
+
args: {
|
|
19
|
+
data: z.any().describe("JSON data to validate (object, array, or primitive)"),
|
|
20
|
+
schema: z.any().describe("JSON Schema object to validate against"),
|
|
21
|
+
version: z.enum(["draft-04", "draft-07", "draft-2020-12", "openapi-3.0"])
|
|
22
|
+
.optional()
|
|
23
|
+
.default("draft-2020-12")
|
|
24
|
+
.describe("JSON Schema version (default: draft-2020-12)")
|
|
25
|
+
},
|
|
26
|
+
execute: async (args) => {
|
|
27
|
+
try {
|
|
28
|
+
const zodSchema = z.fromJSONSchema(args.schema, {
|
|
29
|
+
defaultTarget: args.version
|
|
30
|
+
});
|
|
31
|
+
const result = zodSchema.safeParse(args.data);
|
|
32
|
+
if (result.success) {
|
|
33
|
+
return JSON.stringify({
|
|
34
|
+
valid: true,
|
|
35
|
+
data: result.data
|
|
36
|
+
}, null, 2);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const errors = result.error.issues.map((issue) => ({
|
|
40
|
+
path: issue.path.join('.') || '(root)',
|
|
41
|
+
message: issue.message,
|
|
42
|
+
code: issue.code
|
|
43
|
+
}));
|
|
44
|
+
return JSON.stringify({
|
|
45
|
+
valid: false,
|
|
46
|
+
errors,
|
|
47
|
+
errorCount: errors.length
|
|
48
|
+
}, null, 2);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
return JSON.stringify({
|
|
53
|
+
valid: false,
|
|
54
|
+
schemaError: e instanceof Error ? e.message : 'Unknown schema error'
|
|
55
|
+
}, null, 2);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
export const jsonExtract = tool({
|
|
60
|
+
description: `Extract JSON from markdown or text content.
|
|
61
|
+
Handles code blocks (\`\`\`json) and raw {...} or [...] patterns.
|
|
62
|
+
Returns extracted JSON or error with details.`,
|
|
63
|
+
args: {
|
|
64
|
+
content: z.string().describe("Markdown or text content containing JSON"),
|
|
65
|
+
path: z.string().optional()
|
|
66
|
+
.describe("JSONPath to extract nested value (e.g., 'data.items.0')")
|
|
67
|
+
},
|
|
68
|
+
execute: async (args) => {
|
|
69
|
+
let jsonStr = null;
|
|
70
|
+
const codeBlockMatch = args.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
71
|
+
if (codeBlockMatch) {
|
|
72
|
+
jsonStr = codeBlockMatch[1].trim();
|
|
73
|
+
}
|
|
74
|
+
if (!jsonStr) {
|
|
75
|
+
const objStart = args.content.indexOf('{');
|
|
76
|
+
const arrStart = args.content.indexOf('[');
|
|
77
|
+
if (objStart !== -1 || arrStart !== -1) {
|
|
78
|
+
const startChar = objStart !== -1 && (arrStart === -1 || objStart < arrStart) ? '{' : '[';
|
|
79
|
+
const endChar = startChar === '{' ? '}' : ']';
|
|
80
|
+
const startIndex = args.content.indexOf(startChar);
|
|
81
|
+
let depth = 0;
|
|
82
|
+
let endIndex = -1;
|
|
83
|
+
for (let i = startIndex; i < args.content.length; i++) {
|
|
84
|
+
if (args.content[i] === startChar)
|
|
85
|
+
depth++;
|
|
86
|
+
if (args.content[i] === endChar)
|
|
87
|
+
depth--;
|
|
88
|
+
if (depth === 0) {
|
|
89
|
+
endIndex = i + 1;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (endIndex !== -1) {
|
|
94
|
+
jsonStr = args.content.slice(startIndex, endIndex);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!jsonStr) {
|
|
99
|
+
return JSON.stringify({
|
|
100
|
+
success: false,
|
|
101
|
+
error: "No JSON found in content"
|
|
102
|
+
}, null, 2);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
let data = JSON.parse(jsonStr);
|
|
106
|
+
if (args.path) {
|
|
107
|
+
const parts = args.path.split('.');
|
|
108
|
+
for (const part of parts) {
|
|
109
|
+
if (data === null || data === undefined) {
|
|
110
|
+
return JSON.stringify({
|
|
111
|
+
success: false,
|
|
112
|
+
error: `Path '${args.path}' not found (null/undefined at '${part}')`
|
|
113
|
+
}, null, 2);
|
|
114
|
+
}
|
|
115
|
+
data = data[part];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return JSON.stringify({
|
|
119
|
+
success: true,
|
|
120
|
+
data
|
|
121
|
+
}, null, 2);
|
|
122
|
+
}
|
|
123
|
+
catch (e) {
|
|
124
|
+
return JSON.stringify({
|
|
125
|
+
success: false,
|
|
126
|
+
error: `Invalid JSON: ${e instanceof Error ? e.message : 'Unknown error'}`,
|
|
127
|
+
extractedText: jsonStr.length > 200 ? jsonStr.slice(0, 200) + '...' : jsonStr
|
|
128
|
+
}, null, 2);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
export const jsonInferSchema = tool({
|
|
133
|
+
description: `Infer a JSON Schema from example JSON data.
|
|
134
|
+
Useful for creating schemas from sample data.`,
|
|
135
|
+
args: {
|
|
136
|
+
data: z.any().describe("Example JSON data to infer schema from"),
|
|
137
|
+
title: z.string().optional().describe("Optional schema title"),
|
|
138
|
+
strict: z.boolean().optional().default(false)
|
|
139
|
+
.describe("If true, set additionalProperties: false for objects")
|
|
140
|
+
},
|
|
141
|
+
execute: async (args) => {
|
|
142
|
+
function inferType(value, strict) {
|
|
143
|
+
if (value === null)
|
|
144
|
+
return { type: "null" };
|
|
145
|
+
if (typeof value === "string")
|
|
146
|
+
return { type: "string" };
|
|
147
|
+
if (typeof value === "boolean")
|
|
148
|
+
return { type: "boolean" };
|
|
149
|
+
if (typeof value === "number") {
|
|
150
|
+
return Number.isInteger(value) ? { type: "integer" } : { type: "number" };
|
|
151
|
+
}
|
|
152
|
+
if (Array.isArray(value)) {
|
|
153
|
+
if (value.length === 0) {
|
|
154
|
+
return { type: "array", items: {} };
|
|
155
|
+
}
|
|
156
|
+
return { type: "array", items: inferType(value[0], strict) };
|
|
157
|
+
}
|
|
158
|
+
if (typeof value === "object" && value !== null) {
|
|
159
|
+
const obj = value;
|
|
160
|
+
const properties = {};
|
|
161
|
+
const required = [];
|
|
162
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
163
|
+
properties[key] = inferType(val, strict);
|
|
164
|
+
required.push(key);
|
|
165
|
+
}
|
|
166
|
+
const schema = {
|
|
167
|
+
type: "object",
|
|
168
|
+
properties,
|
|
169
|
+
required
|
|
170
|
+
};
|
|
171
|
+
if (strict) {
|
|
172
|
+
schema.additionalProperties = false;
|
|
173
|
+
}
|
|
174
|
+
return schema;
|
|
175
|
+
}
|
|
176
|
+
return {};
|
|
177
|
+
}
|
|
178
|
+
const schema = {
|
|
179
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
180
|
+
...inferType(args.data, args.strict ?? false)
|
|
181
|
+
};
|
|
182
|
+
if (args.title) {
|
|
183
|
+
schema.title = args.title;
|
|
184
|
+
}
|
|
185
|
+
return JSON.stringify(schema, null, 2);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ToolContext } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const jsonValidate: {
|
|
4
|
+
description: string;
|
|
5
|
+
args: {
|
|
6
|
+
data: z.ZodString;
|
|
7
|
+
schema: z.ZodString;
|
|
8
|
+
};
|
|
9
|
+
execute(args: {
|
|
10
|
+
data: string;
|
|
11
|
+
schema: string;
|
|
12
|
+
}, context: ToolContext): Promise<string>;
|
|
13
|
+
};
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z, fromJSONSchema } from 'zod';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
const UNSUPPORTED_SCHEMA_FEATURES = [
|
|
5
|
+
'not', 'unevaluatedItems', 'unevaluatedProperties',
|
|
6
|
+
'if', 'then', 'else', 'dependentSchemas', 'dependentRequired'
|
|
7
|
+
];
|
|
8
|
+
const SILENTLY_IGNORED_FEATURES = [
|
|
9
|
+
'uniqueItems', 'contains', 'minContains', 'maxContains'
|
|
10
|
+
];
|
|
11
|
+
function analyzeSchemaFeatures(schema, prefix = '') {
|
|
12
|
+
const result = { unsupported: [], ignored: [] };
|
|
13
|
+
if (typeof schema !== 'object' || schema === null)
|
|
14
|
+
return result;
|
|
15
|
+
const obj = schema;
|
|
16
|
+
for (const feature of UNSUPPORTED_SCHEMA_FEATURES) {
|
|
17
|
+
if (feature in obj) {
|
|
18
|
+
result.unsupported.push(prefix ? `${prefix}.${feature}` : feature);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
for (const feature of SILENTLY_IGNORED_FEATURES) {
|
|
22
|
+
if (feature in obj) {
|
|
23
|
+
result.ignored.push(prefix ? `${prefix}.${feature}` : feature);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (obj.properties && typeof obj.properties === 'object') {
|
|
27
|
+
for (const [key, value] of Object.entries(obj.properties)) {
|
|
28
|
+
const nested = analyzeSchemaFeatures(value, prefix ? `${prefix}.properties.${key}` : `properties.${key}`);
|
|
29
|
+
result.unsupported.push(...nested.unsupported);
|
|
30
|
+
result.ignored.push(...nested.ignored);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (obj.items) {
|
|
34
|
+
const nested = analyzeSchemaFeatures(obj.items, prefix ? `${prefix}.items` : 'items');
|
|
35
|
+
result.unsupported.push(...nested.unsupported);
|
|
36
|
+
result.ignored.push(...nested.ignored);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
function formatZodError(error) {
|
|
41
|
+
return error.issues.map(issue => {
|
|
42
|
+
const result = {
|
|
43
|
+
path: issue.path.join('.') || '(root)',
|
|
44
|
+
message: issue.message,
|
|
45
|
+
code: issue.code
|
|
46
|
+
};
|
|
47
|
+
if (issue.code === 'invalid_type') {
|
|
48
|
+
const typedIssue = issue;
|
|
49
|
+
result.expected = typeof typedIssue.expected === 'string'
|
|
50
|
+
? 'string'
|
|
51
|
+
: typedIssue.expected === 'number'
|
|
52
|
+
? 'number'
|
|
53
|
+
: String(typedIssue.expected);
|
|
54
|
+
result.received = typeof typedIssue.input === 'string'
|
|
55
|
+
? 'string'
|
|
56
|
+
: String(typedIssue.input);
|
|
57
|
+
}
|
|
58
|
+
if (issue.code === 'too_small') {
|
|
59
|
+
const typedIssue = issue;
|
|
60
|
+
result.constraint = {
|
|
61
|
+
type: typedIssue.origin === 'string' ? 'minLength' : 'minimum',
|
|
62
|
+
value: String(typedIssue.minimum)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (issue.code === 'too_big') {
|
|
66
|
+
const typedIssue = issue;
|
|
67
|
+
result.constraint = {
|
|
68
|
+
type: typedIssue.origin === 'string' ? 'maxLength' : 'maximum',
|
|
69
|
+
value: String(typedIssue.maximum)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (issue.code === 'invalid_format') {
|
|
73
|
+
const formatIssue = issue;
|
|
74
|
+
result.constraint = {
|
|
75
|
+
type: 'format',
|
|
76
|
+
value: String(formatIssue.format)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export const jsonValidate = tool({
|
|
83
|
+
description: "Validate JSON data against a JSON Schema (supports Draft-4, Draft-7, Draft-2020-12, OpenAPI 3.0). Returns detailed validation results with warnings for unsupported/ignored schema features.",
|
|
84
|
+
args: {
|
|
85
|
+
data: z.string().describe("JSON data string to validate"),
|
|
86
|
+
schema: z.string().describe("JSON Schema string or file path (auto-reads from file if path detected)")
|
|
87
|
+
},
|
|
88
|
+
execute: async (args, context) => {
|
|
89
|
+
let parsedData;
|
|
90
|
+
let parsedSchema;
|
|
91
|
+
let schemaString = args.schema;
|
|
92
|
+
const isWindowsPath = /^[A-Za-z]:[/\\]/.test(args.schema);
|
|
93
|
+
const isUnixPath = /^\//.test(args.schema);
|
|
94
|
+
const isRelativePath = /^\.{1,2}(\/|\\)/.test(args.schema);
|
|
95
|
+
if (isWindowsPath || isUnixPath || isRelativePath) {
|
|
96
|
+
const resolvedPath = path.isAbsolute(args.schema)
|
|
97
|
+
? args.schema
|
|
98
|
+
: path.join(context.directory, args.schema);
|
|
99
|
+
try {
|
|
100
|
+
const fs = await import('fs');
|
|
101
|
+
schemaString = fs.readFileSync(resolvedPath, 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
return JSON.stringify({
|
|
105
|
+
success: false,
|
|
106
|
+
error: {
|
|
107
|
+
code: 'INVALID_JSON_SCHEMA',
|
|
108
|
+
message: 'Failed to read schema from file',
|
|
109
|
+
details: e instanceof Error ? e.message : 'Unknown error',
|
|
110
|
+
hints: [
|
|
111
|
+
'Check the file path for typos',
|
|
112
|
+
'Ensure file exists and is accessible',
|
|
113
|
+
'Use a relative path from the project directory'
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
}, null, 2);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
parsedData = JSON.parse(args.data);
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
return JSON.stringify({
|
|
124
|
+
success: false,
|
|
125
|
+
error: {
|
|
126
|
+
code: 'INVALID_JSON_DATA',
|
|
127
|
+
message: 'Data is not valid JSON',
|
|
128
|
+
details: e instanceof Error ? e.message : 'Parse error',
|
|
129
|
+
hints: [
|
|
130
|
+
'Ensure data is valid JSON syntax',
|
|
131
|
+
'Check for missing quotes, trailing commas, or unescaped characters'
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}, null, 2);
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
parsedSchema = JSON.parse(schemaString);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
return JSON.stringify({
|
|
141
|
+
success: false,
|
|
142
|
+
error: {
|
|
143
|
+
code: 'INVALID_JSON_SCHEMA',
|
|
144
|
+
message: 'Schema is not valid JSON',
|
|
145
|
+
details: e instanceof Error ? e.message : 'Parse error',
|
|
146
|
+
hints: [
|
|
147
|
+
'Ensure schema is valid JSON syntax',
|
|
148
|
+
'Check JSON Schema syntax at https://json-schema.org'
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
}, null, 2);
|
|
152
|
+
}
|
|
153
|
+
const analysis = analyzeSchemaFeatures(parsedSchema);
|
|
154
|
+
if (analysis.unsupported.length > 0) {
|
|
155
|
+
return JSON.stringify({
|
|
156
|
+
success: false,
|
|
157
|
+
error: {
|
|
158
|
+
code: 'UNSUPPORTED_SCHEMA_FEATURE',
|
|
159
|
+
message: 'Schema uses features not supported by Zod',
|
|
160
|
+
details: {
|
|
161
|
+
unsupportedFeatures: analysis.unsupported,
|
|
162
|
+
explanation: 'These JSON Schema features cannot be converted to Zod schemas'
|
|
163
|
+
},
|
|
164
|
+
hints: [
|
|
165
|
+
'Remove unsupported features from schema',
|
|
166
|
+
`Unsupported features: ${analysis.unsupported.join(', ')}`,
|
|
167
|
+
'Consider using a simpler schema structure'
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
}, null, 2);
|
|
171
|
+
}
|
|
172
|
+
let zodSchema;
|
|
173
|
+
try {
|
|
174
|
+
zodSchema = fromJSONSchema(parsedSchema);
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
return JSON.stringify({
|
|
178
|
+
success: false,
|
|
179
|
+
error: {
|
|
180
|
+
code: 'UNSUPPORTED_SCHEMA_FEATURE',
|
|
181
|
+
message: 'Failed to convert JSON Schema to validation schema',
|
|
182
|
+
details: e instanceof Error ? e.message : 'Conversion error',
|
|
183
|
+
hints: [
|
|
184
|
+
'Schema may use unsupported features',
|
|
185
|
+
'Check schema syntax and structure'
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
}, null, 2);
|
|
189
|
+
}
|
|
190
|
+
const result = zodSchema.safeParse(parsedData);
|
|
191
|
+
if (result.success) {
|
|
192
|
+
const response = {
|
|
193
|
+
success: true,
|
|
194
|
+
data: result.data,
|
|
195
|
+
metadata: {
|
|
196
|
+
errorCount: 0,
|
|
197
|
+
schemaFeatures: {
|
|
198
|
+
validated: Object.keys(parsedSchema).filter(k => !SILENTLY_IGNORED_FEATURES.includes(k)),
|
|
199
|
+
ignored: analysis.ignored
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
if (analysis.ignored.length > 0) {
|
|
204
|
+
response.warnings = analysis.ignored.map((feature) => ({
|
|
205
|
+
code: 'IGNORED_SCHEMA_FEATURE',
|
|
206
|
+
message: `Schema feature '${feature}' is not validated`,
|
|
207
|
+
details: 'This feature is silently ignored by Zod - data may not match intended constraints'
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
return JSON.stringify(response, null, 2);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
return JSON.stringify({
|
|
214
|
+
success: true,
|
|
215
|
+
data: null,
|
|
216
|
+
valid: false,
|
|
217
|
+
errors: formatZodError(result.error),
|
|
218
|
+
metadata: {
|
|
219
|
+
errorCount: result.error.issues.length,
|
|
220
|
+
schemaFeatures: {
|
|
221
|
+
validated: Object.keys(parsedSchema).filter(k => !SILENTLY_IGNORED_FEATURES.includes(k)),
|
|
222
|
+
ignored: analysis.ignored
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}, null, 2);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# BioResearcher Core
|
|
2
|
+
|
|
3
|
+
Core library skill providing reusable patterns and utilities for BioResearcher skills.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
BioResearcher Core provides:
|
|
8
|
+
- **Workflow patterns** for common operations (retry, progress, subagent waves)
|
|
9
|
+
- **Data handling patterns** for JSON and table operations
|
|
10
|
+
- **Python utilities** for template generation
|
|
11
|
+
- **Tool-first architecture** using existing plugin tools
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
No installation required. This skill uses only:
|
|
16
|
+
- Existing plugin tools (jsonExtract, jsonValidate, etc.)
|
|
17
|
+
- Python standard library
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### Load the Skill
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
skill bioresearcher-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Extract Skill Path
|
|
28
|
+
|
|
29
|
+
From the `<skill_files>` section in the skill tool output, extract the `<skill_path>` value for use in commands.
|
|
30
|
+
|
|
31
|
+
### Use a Pattern
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Read <skill_path>/patterns/retry.md
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Run Python Script
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv run python <skill_path>/python/template.py generate-batches \
|
|
41
|
+
--template template.md \
|
|
42
|
+
--contexts contexts.json \
|
|
43
|
+
--output-dir ./outputs
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Available Patterns
|
|
47
|
+
|
|
48
|
+
### Workflow Control
|
|
49
|
+
|
|
50
|
+
| Pattern | Description | File |
|
|
51
|
+
|---------|-------------|------|
|
|
52
|
+
| Retry | Retry with backoff using blockingTimer | `patterns/retry.md` |
|
|
53
|
+
| Progress | Track and report batch progress | `patterns/progress.md` |
|
|
54
|
+
| Subagent Waves | Parallel subagent processing | `patterns/subagent-waves.md` |
|
|
55
|
+
| Shell Commands | Cross-platform command generation | `patterns/shell-commands.md` |
|
|
56
|
+
| User Confirmation | Request user confirmation | `patterns/user-confirmation.md` |
|
|
57
|
+
|
|
58
|
+
### Data Handling
|
|
59
|
+
|
|
60
|
+
| Pattern | Description | File |
|
|
61
|
+
|---------|-------------|------|
|
|
62
|
+
| JSON Tools | jsonExtract/jsonValidate/jsonInfer | `patterns/json-tools.md` |
|
|
63
|
+
| Table Tools | Combine outputs with table tools | `patterns/table-tools.md` |
|
|
64
|
+
| Data Exchange | Main/subagent communication | `patterns/data-exchange.md` |
|
|
65
|
+
| Calculator | In-workflow calculations | `patterns/calculator.md` |
|
|
66
|
+
|
|
67
|
+
## Available Tools
|
|
68
|
+
|
|
69
|
+
These tools are always available without loading pattern files:
|
|
70
|
+
|
|
71
|
+
### JSON Tools
|
|
72
|
+
|
|
73
|
+
| Tool | Description |
|
|
74
|
+
|------|-------------|
|
|
75
|
+
| `jsonExtract` | Extract JSON from files |
|
|
76
|
+
| `jsonValidate` | Validate JSON against schemas |
|
|
77
|
+
| `jsonInfer` | Infer schemas from data |
|
|
78
|
+
|
|
79
|
+
### Utility Tools
|
|
80
|
+
|
|
81
|
+
| Tool | Description |
|
|
82
|
+
|------|-------------|
|
|
83
|
+
| `blockingTimer` | Blocking delays (0-300 seconds) |
|
|
84
|
+
| `calculator` | Mathematical expressions |
|
|
85
|
+
|
|
86
|
+
### Table Tools
|
|
87
|
+
|
|
88
|
+
| Tool | Description |
|
|
89
|
+
|------|-------------|
|
|
90
|
+
| `tableCreateFile` | Create Excel/CSV from data |
|
|
91
|
+
| `tableAppendRows` | Append rows to tables |
|
|
92
|
+
|
|
93
|
+
## Python Utilities
|
|
94
|
+
|
|
95
|
+
### Template Engine
|
|
96
|
+
|
|
97
|
+
Location: `python/template.py`
|
|
98
|
+
|
|
99
|
+
Commands:
|
|
100
|
+
- `fill` - Generate single file from template
|
|
101
|
+
- `generate-batches` - Generate multiple files from template
|
|
102
|
+
- `escape` - Escape text for markdown
|
|
103
|
+
|
|
104
|
+
See `python/template.md` for full documentation.
|
|
105
|
+
|
|
106
|
+
## Examples
|
|
107
|
+
|
|
108
|
+
### Example Files
|
|
109
|
+
|
|
110
|
+
| File | Description |
|
|
111
|
+
|------|-------------|
|
|
112
|
+
| `examples/template.md` | Example template with placeholders |
|
|
113
|
+
| `examples/contexts.json` | Example contexts for batch generation |
|
|
114
|
+
| `examples/data-exchange-example.md` | Complete data exchange workflow |
|
|
115
|
+
|
|
116
|
+
## Architecture
|
|
117
|
+
|
|
118
|
+
### Tool-First Design
|
|
119
|
+
|
|
120
|
+
This skill maximizes use of existing plugin tools:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
┌─────────────────────────────────────────────────┐
|
|
124
|
+
│ BioResearcher Core │
|
|
125
|
+
├─────────────────────────────────────────────────┤
|
|
126
|
+
│ Patterns (markdown) │ Python Utilities │
|
|
127
|
+
│ - retry.md │ - template.py │
|
|
128
|
+
│ - progress.md │ │
|
|
129
|
+
│ - subagent-waves.md │ │
|
|
130
|
+
│ - json-tools.md │ │
|
|
131
|
+
│ - table-tools.md │ │
|
|
132
|
+
│ - data-exchange.md │ │
|
|
133
|
+
│ - calculator.md │ │
|
|
134
|
+
│ - shell-commands.md │ │
|
|
135
|
+
│ - user-confirmation.md │ │
|
|
136
|
+
├─────────────────────────┴───────────────────────┤
|
|
137
|
+
│ Plugin Tools │
|
|
138
|
+
│ jsonExtract │ jsonValidate │ jsonInfer │
|
|
139
|
+
│ blockingTimer │ calculator │
|
|
140
|
+
│ tableCreateFile │ tableAppendRows │
|
|
141
|
+
└─────────────────────────────────────────────────┘
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Pattern Modules
|
|
145
|
+
|
|
146
|
+
Each pattern is a self-contained markdown file:
|
|
147
|
+
- Independent (no dependencies on other patterns)
|
|
148
|
+
- Self-documenting (explains itself)
|
|
149
|
+
- Minimal loading (agent reads only what it needs)
|
|
150
|
+
|
|
151
|
+
## Integration with Other Skills
|
|
152
|
+
|
|
153
|
+
### long-table-summary
|
|
154
|
+
|
|
155
|
+
BioResearcher Core can be used by long-table-summary:
|
|
156
|
+
- Use `jsonExtract` instead of manual JSON parsing
|
|
157
|
+
- Use `jsonValidate` for schema validation
|
|
158
|
+
- Apply retry/progress patterns
|
|
159
|
+
- Use template.py for prompt generation
|
|
160
|
+
|
|
161
|
+
### pubmed-weekly
|
|
162
|
+
|
|
163
|
+
BioResearcher Core can be used by pubmed-weekly:
|
|
164
|
+
- Apply retry pattern for download failures
|
|
165
|
+
- Use shell-commands pattern for cross-platform support
|
|
166
|
+
|
|
167
|
+
## Design Principles
|
|
168
|
+
|
|
169
|
+
1. **Tool-first**: Use existing tools before creating Python scripts
|
|
170
|
+
2. **Self-contained**: Each pattern is independent
|
|
171
|
+
3. **Minimal dependencies**: Only Python standard library
|
|
172
|
+
4. **Clear documentation**: Every file documents itself
|
|
173
|
+
5. **Standard protocols**: JSON for data exchange
|
|
174
|
+
|
|
175
|
+
## Directory Structure
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
plugin/skills/bioresearcher-core/
|
|
179
|
+
├── SKILL.md # Routing document
|
|
180
|
+
├── README.md # This file
|
|
181
|
+
├── patterns/ # Workflow patterns
|
|
182
|
+
│ ├── retry.md
|
|
183
|
+
│ ├── progress.md
|
|
184
|
+
│ ├── subagent-waves.md
|
|
185
|
+
│ ├── shell-commands.md
|
|
186
|
+
│ ├── user-confirmation.md
|
|
187
|
+
│ ├── json-tools.md
|
|
188
|
+
│ ├── table-tools.md
|
|
189
|
+
│ ├── data-exchange.md
|
|
190
|
+
│ └── calculator.md
|
|
191
|
+
├── python/ # Python utilities
|
|
192
|
+
│ ├── template.py
|
|
193
|
+
│ └── template.md
|
|
194
|
+
└── examples/ # Example files
|
|
195
|
+
├── template.md
|
|
196
|
+
├── contexts.json
|
|
197
|
+
└── data-exchange-example.md
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Contributing
|
|
201
|
+
|
|
202
|
+
When adding new patterns:
|
|
203
|
+
1. Create self-contained markdown file in `patterns/`
|
|
204
|
+
2. Follow existing pattern structure
|
|
205
|
+
3. Include examples and error handling
|
|
206
|
+
4. Update SKILL.md with new pattern reference
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
Part of the BioResearcher plugin ecosystem.
|