@yeyuan98/opencode-bioresearcher-plugin 1.4.0 → 1.5.0-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.
Files changed (72) hide show
  1. package/README.md +35 -20
  2. package/dist/db-tools/backends/index.d.ts +11 -0
  3. package/dist/db-tools/backends/index.js +48 -0
  4. package/dist/db-tools/backends/mongodb/backend.d.ts +15 -0
  5. package/dist/db-tools/backends/mongodb/backend.js +76 -0
  6. package/dist/db-tools/backends/mongodb/connection.d.ts +27 -0
  7. package/dist/db-tools/backends/mongodb/connection.js +107 -0
  8. package/dist/db-tools/backends/mongodb/index.d.ts +4 -0
  9. package/dist/db-tools/backends/mongodb/index.js +3 -0
  10. package/dist/db-tools/backends/mongodb/translator.d.ts +30 -0
  11. package/dist/db-tools/backends/mongodb/translator.js +407 -0
  12. package/dist/db-tools/backends/mysql/backend.d.ts +15 -0
  13. package/dist/db-tools/backends/mysql/backend.js +57 -0
  14. package/dist/db-tools/backends/mysql/connection.d.ts +25 -0
  15. package/dist/db-tools/backends/mysql/connection.js +83 -0
  16. package/dist/db-tools/backends/mysql/index.d.ts +3 -0
  17. package/dist/db-tools/backends/mysql/index.js +2 -0
  18. package/dist/db-tools/backends/mysql/translator.d.ts +7 -0
  19. package/dist/db-tools/backends/mysql/translator.js +67 -0
  20. package/dist/db-tools/core/base.d.ts +17 -0
  21. package/dist/db-tools/core/base.js +51 -0
  22. package/dist/db-tools/core/config-loader.d.ts +3 -0
  23. package/dist/db-tools/core/config-loader.js +46 -0
  24. package/dist/db-tools/core/index.d.ts +2 -0
  25. package/dist/db-tools/core/index.js +2 -0
  26. package/dist/db-tools/core/jsonc-parser.d.ts +2 -0
  27. package/dist/db-tools/core/jsonc-parser.js +77 -0
  28. package/dist/db-tools/core/validator.d.ts +16 -0
  29. package/dist/db-tools/core/validator.js +118 -0
  30. package/dist/db-tools/executor.d.ts +13 -0
  31. package/dist/db-tools/executor.js +54 -0
  32. package/dist/db-tools/index.d.ts +51 -0
  33. package/dist/db-tools/index.js +27 -0
  34. package/dist/db-tools/interface/backend.d.ts +24 -0
  35. package/dist/db-tools/interface/backend.js +1 -0
  36. package/dist/db-tools/interface/connection.d.ts +21 -0
  37. package/dist/db-tools/interface/connection.js +11 -0
  38. package/dist/db-tools/interface/index.d.ts +4 -0
  39. package/dist/db-tools/interface/index.js +4 -0
  40. package/dist/db-tools/interface/query.d.ts +60 -0
  41. package/dist/db-tools/interface/query.js +1 -0
  42. package/dist/db-tools/interface/schema.d.ts +22 -0
  43. package/dist/db-tools/interface/schema.js +1 -0
  44. package/dist/db-tools/pool.d.ts +8 -0
  45. package/dist/db-tools/pool.js +49 -0
  46. package/dist/db-tools/tools/index.d.ts +27 -0
  47. package/dist/db-tools/tools/index.js +191 -0
  48. package/dist/db-tools/tools.d.ts +27 -0
  49. package/dist/db-tools/tools.js +111 -0
  50. package/dist/db-tools/types.d.ts +94 -0
  51. package/dist/db-tools/types.js +40 -0
  52. package/dist/db-tools/utils.d.ts +33 -0
  53. package/dist/db-tools/utils.js +94 -0
  54. package/dist/index.js +2 -0
  55. package/dist/skills/bioresearcher-core/README.md +210 -210
  56. package/dist/skills/bioresearcher-core/SKILL.md +128 -128
  57. package/dist/skills/bioresearcher-core/examples/contexts.json +29 -29
  58. package/dist/skills/bioresearcher-core/examples/data-exchange-example.md +303 -303
  59. package/dist/skills/bioresearcher-core/examples/template.md +49 -49
  60. package/dist/skills/bioresearcher-core/patterns/calculator.md +215 -215
  61. package/dist/skills/bioresearcher-core/patterns/data-exchange.md +406 -406
  62. package/dist/skills/bioresearcher-core/patterns/json-tools.md +263 -263
  63. package/dist/skills/bioresearcher-core/patterns/progress.md +127 -127
  64. package/dist/skills/bioresearcher-core/patterns/retry.md +110 -110
  65. package/dist/skills/bioresearcher-core/patterns/shell-commands.md +79 -79
  66. package/dist/skills/bioresearcher-core/patterns/subagent-waves.md +186 -186
  67. package/dist/skills/bioresearcher-core/patterns/table-tools.md +260 -260
  68. package/dist/skills/bioresearcher-core/patterns/user-confirmation.md +187 -187
  69. package/dist/skills/bioresearcher-core/python/template.md +273 -273
  70. package/dist/skills/bioresearcher-core/python/template.py +323 -323
  71. package/dist/skills/env-jsonc-setup/SKILL.md +206 -0
  72. package/package.json +3 -1
@@ -1,323 +1,323 @@
1
- #!/usr/bin/env python3
2
- """Template engine for generating files from templates with placeholder replacement.
3
-
4
- This module provides functionality for:
5
- - Filling templates with placeholder replacement
6
- - Escaping text for markdown code blocks
7
- - Batch generation from template + contexts
8
-
9
- Usage:
10
- uv run python template.py fill --template template.md --context context.json --output output.md
11
- uv run python template.py generate-batches --template template.md --contexts contexts.json --output-dir ./outputs
12
- """
13
-
14
- import argparse
15
- import json
16
- import os
17
- import re
18
- import sys
19
- from pathlib import Path
20
- from typing import Any, Dict, List, Optional
21
-
22
-
23
- class TemplateEngine:
24
- """Template engine for placeholder replacement and batch generation."""
25
-
26
- PLACEHOLDER_PATTERN = re.compile(r"\{(\w+)\}")
27
-
28
- @staticmethod
29
- def escape_for_markdown(text: str) -> str:
30
- """Escape text for safe use in markdown code blocks.
31
-
32
- Args:
33
- text: Text to escape
34
-
35
- Returns:
36
- Escaped text safe for markdown code blocks
37
- """
38
- escaped = text.replace("\\", "\\\\")
39
- escaped = escaped.replace("`", "\\`")
40
- escaped = escaped.replace("$", "\\$")
41
- return escaped
42
-
43
- @staticmethod
44
- def fill_template(
45
- template: str, replacements: Dict[str, Any], escape_values: bool = False
46
- ) -> str:
47
- """Fill template by replacing {placeholder} patterns.
48
-
49
- Args:
50
- template: Template string with {placeholder} patterns
51
- replacements: Dictionary mapping placeholder names to values
52
- escape_values: If True, escape values for markdown code blocks
53
-
54
- Returns:
55
- Template with placeholders replaced
56
- """
57
-
58
- def replace_match(match: re.Match) -> str:
59
- key = match.group(1)
60
- if key not in replacements:
61
- return match.group(0)
62
- value = replacements[key]
63
- if value is None:
64
- return ""
65
- str_value = str(value)
66
- if escape_values:
67
- str_value = TemplateEngine.escape_for_markdown(str_value)
68
- return str_value
69
-
70
- return TemplateEngine.PLACEHOLDER_PATTERN.sub(replace_match, template)
71
-
72
- @staticmethod
73
- def generate_batch(
74
- template_path: str,
75
- context: Dict[str, Any],
76
- output_path: str,
77
- escape_values: bool = False,
78
- ) -> Dict[str, Any]:
79
- """Generate a single file from template and context.
80
-
81
- Args:
82
- template_path: Path to template file
83
- context: Dictionary with placeholder values
84
- output_path: Path for output file
85
- escape_values: If True, escape values for markdown
86
-
87
- Returns:
88
- Result dictionary with success status
89
- """
90
- if not os.path.exists(template_path):
91
- return {
92
- "success": False,
93
- "error": f"Template file not found: {template_path}",
94
- }
95
-
96
- try:
97
- with open(template_path, "r", encoding="utf-8") as f:
98
- template = f.read()
99
- except Exception as e:
100
- return {"success": False, "error": f"Failed to read template: {e}"}
101
-
102
- filled = TemplateEngine.fill_template(template, context, escape_values)
103
-
104
- try:
105
- output_dir = os.path.dirname(output_path)
106
- if output_dir:
107
- os.makedirs(output_dir, exist_ok=True)
108
-
109
- with open(output_path, "w", encoding="utf-8") as f:
110
- f.write(filled)
111
- except Exception as e:
112
- return {"success": False, "error": f"Failed to write output: {e}"}
113
-
114
- return {
115
- "success": True,
116
- "output_path": output_path,
117
- "template_path": template_path,
118
- }
119
-
120
- @staticmethod
121
- def generate_batches(
122
- template_path: str,
123
- contexts: List[Dict[str, Any]],
124
- output_dir: str,
125
- filename_pattern: str = "output_{index:03d}.md",
126
- escape_values: bool = False,
127
- dry_run: bool = False,
128
- verbose: bool = False,
129
- ) -> Dict[str, Any]:
130
- """Generate multiple files from template and list of contexts.
131
-
132
- Args:
133
- template_path: Path to template file
134
- contexts: List of context dictionaries
135
- output_dir: Directory for output files
136
- filename_pattern: Pattern for output filenames (use {index} placeholder)
137
- escape_values: If True, escape values for markdown
138
- dry_run: If True, don't write files
139
- verbose: If True, print progress
140
-
141
- Returns:
142
- Result dictionary with success status and generated files
143
- """
144
- if not os.path.exists(template_path):
145
- return {
146
- "success": False,
147
- "error": f"Template file not found: {template_path}",
148
- }
149
-
150
- if not contexts:
151
- return {"success": False, "error": "No contexts provided"}
152
-
153
- try:
154
- with open(template_path, "r", encoding="utf-8") as f:
155
- template = f.read()
156
- except Exception as e:
157
- return {"success": False, "error": f"Failed to read template: {e}"}
158
-
159
- if not dry_run:
160
- os.makedirs(output_dir, exist_ok=True)
161
-
162
- generated_files = []
163
- errors = []
164
-
165
- for index, context in enumerate(contexts):
166
- filename = filename_pattern.format(index=index + 1, **context)
167
- output_path = os.path.join(output_dir, filename)
168
-
169
- if dry_run:
170
- if verbose:
171
- print(f"Would generate: {output_path}")
172
- generated_files.append(output_path)
173
- continue
174
-
175
- filled = TemplateEngine.fill_template(template, context, escape_values)
176
-
177
- try:
178
- with open(output_path, "w", encoding="utf-8") as f:
179
- f.write(filled)
180
-
181
- if verbose:
182
- print(f"Generated: {output_path}")
183
- generated_files.append(output_path)
184
- except Exception as e:
185
- error_msg = f"Failed to write {output_path}: {e}"
186
- if verbose:
187
- print(f"Error: {error_msg}", file=sys.stderr)
188
- errors.append(error_msg)
189
-
190
- result = {
191
- "success": len(errors) == 0,
192
- "total_contexts": len(contexts),
193
- "generated_count": len(generated_files),
194
- "output_dir": output_dir,
195
- "generated_files": generated_files,
196
- }
197
-
198
- if errors:
199
- result["errors"] = errors
200
- result["error_count"] = len(errors)
201
-
202
- return result
203
-
204
-
205
- def cmd_fill(args: argparse.Namespace) -> None:
206
- """Handle fill command."""
207
- try:
208
- with open(args.context, "r", encoding="utf-8") as f:
209
- context = json.load(f)
210
- except Exception as e:
211
- result = {"success": False, "error": f"Failed to read context file: {e}"}
212
- print(json.dumps(result, indent=2))
213
- sys.exit(1)
214
-
215
- result = TemplateEngine.generate_batch(
216
- template_path=args.template,
217
- context=context,
218
- output_path=args.output,
219
- escape_values=args.escape,
220
- )
221
-
222
- print(json.dumps(result, indent=2))
223
-
224
- if not result["success"]:
225
- sys.exit(1)
226
-
227
-
228
- def cmd_generate_batches(args: argparse.Namespace) -> None:
229
- """Handle generate-batches command."""
230
- try:
231
- with open(args.contexts, "r", encoding="utf-8") as f:
232
- contexts = json.load(f)
233
- except Exception as e:
234
- result = {"success": False, "error": f"Failed to read contexts file: {e}"}
235
- print(json.dumps(result, indent=2))
236
- sys.exit(1)
237
-
238
- if not isinstance(contexts, list):
239
- result = {"success": False, "error": "Contexts must be a JSON array"}
240
- print(json.dumps(result, indent=2))
241
- sys.exit(1)
242
-
243
- result = TemplateEngine.generate_batches(
244
- template_path=args.template,
245
- contexts=contexts,
246
- output_dir=args.output_dir,
247
- filename_pattern=args.filename_pattern,
248
- escape_values=args.escape,
249
- dry_run=args.dry_run,
250
- verbose=args.verbose,
251
- )
252
-
253
- print(json.dumps(result, indent=2))
254
-
255
- if not result["success"]:
256
- sys.exit(1)
257
-
258
-
259
- def cmd_escape(args: argparse.Namespace) -> None:
260
- """Handle escape command."""
261
- escaped = TemplateEngine.escape_for_markdown(args.text)
262
- result = {"success": True, "original": args.text, "escaped": escaped}
263
- print(json.dumps(result, indent=2))
264
-
265
-
266
- def main() -> None:
267
- """Main entry point."""
268
- parser = argparse.ArgumentParser(
269
- description="Template engine for generating files from templates"
270
- )
271
- subparsers = parser.add_subparsers(dest="command", help="Available commands")
272
-
273
- fill_parser = subparsers.add_parser("fill", help="Fill single template")
274
- fill_parser.add_argument("--template", required=True, help="Path to template file")
275
- fill_parser.add_argument(
276
- "--context", required=True, help="Path to context JSON file"
277
- )
278
- fill_parser.add_argument("--output", required=True, help="Path for output file")
279
- fill_parser.add_argument(
280
- "--escape", action="store_true", help="Escape values for markdown"
281
- )
282
-
283
- batch_parser = subparsers.add_parser(
284
- "generate-batches", help="Generate multiple files"
285
- )
286
- batch_parser.add_argument("--template", required=True, help="Path to template file")
287
- batch_parser.add_argument(
288
- "--contexts", required=True, help="Path to contexts JSON file"
289
- )
290
- batch_parser.add_argument(
291
- "--output-dir", required=True, help="Directory for output files"
292
- )
293
- batch_parser.add_argument(
294
- "--filename-pattern",
295
- default="output_{index:03d}.md",
296
- help="Filename pattern (default: output_{index:03d}.md)",
297
- )
298
- batch_parser.add_argument(
299
- "--escape", action="store_true", help="Escape values for markdown"
300
- )
301
- batch_parser.add_argument(
302
- "--dry-run", action="store_true", help="Validate without writing"
303
- )
304
- batch_parser.add_argument("--verbose", action="store_true", help="Print progress")
305
-
306
- escape_parser = subparsers.add_parser("escape", help="Escape text for markdown")
307
- escape_parser.add_argument("--text", required=True, help="Text to escape")
308
-
309
- args = parser.parse_args()
310
-
311
- if args.command == "fill":
312
- cmd_fill(args)
313
- elif args.command == "generate-batches":
314
- cmd_generate_batches(args)
315
- elif args.command == "escape":
316
- cmd_escape(args)
317
- else:
318
- parser.print_help()
319
- sys.exit(1)
320
-
321
-
322
- if __name__ == "__main__":
323
- main()
1
+ #!/usr/bin/env python3
2
+ """Template engine for generating files from templates with placeholder replacement.
3
+
4
+ This module provides functionality for:
5
+ - Filling templates with placeholder replacement
6
+ - Escaping text for markdown code blocks
7
+ - Batch generation from template + contexts
8
+
9
+ Usage:
10
+ uv run python template.py fill --template template.md --context context.json --output output.md
11
+ uv run python template.py generate-batches --template template.md --contexts contexts.json --output-dir ./outputs
12
+ """
13
+
14
+ import argparse
15
+ import json
16
+ import os
17
+ import re
18
+ import sys
19
+ from pathlib import Path
20
+ from typing import Any, Dict, List, Optional
21
+
22
+
23
+ class TemplateEngine:
24
+ """Template engine for placeholder replacement and batch generation."""
25
+
26
+ PLACEHOLDER_PATTERN = re.compile(r"\{(\w+)\}")
27
+
28
+ @staticmethod
29
+ def escape_for_markdown(text: str) -> str:
30
+ """Escape text for safe use in markdown code blocks.
31
+
32
+ Args:
33
+ text: Text to escape
34
+
35
+ Returns:
36
+ Escaped text safe for markdown code blocks
37
+ """
38
+ escaped = text.replace("\\", "\\\\")
39
+ escaped = escaped.replace("`", "\\`")
40
+ escaped = escaped.replace("$", "\\$")
41
+ return escaped
42
+
43
+ @staticmethod
44
+ def fill_template(
45
+ template: str, replacements: Dict[str, Any], escape_values: bool = False
46
+ ) -> str:
47
+ """Fill template by replacing {placeholder} patterns.
48
+
49
+ Args:
50
+ template: Template string with {placeholder} patterns
51
+ replacements: Dictionary mapping placeholder names to values
52
+ escape_values: If True, escape values for markdown code blocks
53
+
54
+ Returns:
55
+ Template with placeholders replaced
56
+ """
57
+
58
+ def replace_match(match: re.Match) -> str:
59
+ key = match.group(1)
60
+ if key not in replacements:
61
+ return match.group(0)
62
+ value = replacements[key]
63
+ if value is None:
64
+ return ""
65
+ str_value = str(value)
66
+ if escape_values:
67
+ str_value = TemplateEngine.escape_for_markdown(str_value)
68
+ return str_value
69
+
70
+ return TemplateEngine.PLACEHOLDER_PATTERN.sub(replace_match, template)
71
+
72
+ @staticmethod
73
+ def generate_batch(
74
+ template_path: str,
75
+ context: Dict[str, Any],
76
+ output_path: str,
77
+ escape_values: bool = False,
78
+ ) -> Dict[str, Any]:
79
+ """Generate a single file from template and context.
80
+
81
+ Args:
82
+ template_path: Path to template file
83
+ context: Dictionary with placeholder values
84
+ output_path: Path for output file
85
+ escape_values: If True, escape values for markdown
86
+
87
+ Returns:
88
+ Result dictionary with success status
89
+ """
90
+ if not os.path.exists(template_path):
91
+ return {
92
+ "success": False,
93
+ "error": f"Template file not found: {template_path}",
94
+ }
95
+
96
+ try:
97
+ with open(template_path, "r", encoding="utf-8") as f:
98
+ template = f.read()
99
+ except Exception as e:
100
+ return {"success": False, "error": f"Failed to read template: {e}"}
101
+
102
+ filled = TemplateEngine.fill_template(template, context, escape_values)
103
+
104
+ try:
105
+ output_dir = os.path.dirname(output_path)
106
+ if output_dir:
107
+ os.makedirs(output_dir, exist_ok=True)
108
+
109
+ with open(output_path, "w", encoding="utf-8") as f:
110
+ f.write(filled)
111
+ except Exception as e:
112
+ return {"success": False, "error": f"Failed to write output: {e}"}
113
+
114
+ return {
115
+ "success": True,
116
+ "output_path": output_path,
117
+ "template_path": template_path,
118
+ }
119
+
120
+ @staticmethod
121
+ def generate_batches(
122
+ template_path: str,
123
+ contexts: List[Dict[str, Any]],
124
+ output_dir: str,
125
+ filename_pattern: str = "output_{index:03d}.md",
126
+ escape_values: bool = False,
127
+ dry_run: bool = False,
128
+ verbose: bool = False,
129
+ ) -> Dict[str, Any]:
130
+ """Generate multiple files from template and list of contexts.
131
+
132
+ Args:
133
+ template_path: Path to template file
134
+ contexts: List of context dictionaries
135
+ output_dir: Directory for output files
136
+ filename_pattern: Pattern for output filenames (use {index} placeholder)
137
+ escape_values: If True, escape values for markdown
138
+ dry_run: If True, don't write files
139
+ verbose: If True, print progress
140
+
141
+ Returns:
142
+ Result dictionary with success status and generated files
143
+ """
144
+ if not os.path.exists(template_path):
145
+ return {
146
+ "success": False,
147
+ "error": f"Template file not found: {template_path}",
148
+ }
149
+
150
+ if not contexts:
151
+ return {"success": False, "error": "No contexts provided"}
152
+
153
+ try:
154
+ with open(template_path, "r", encoding="utf-8") as f:
155
+ template = f.read()
156
+ except Exception as e:
157
+ return {"success": False, "error": f"Failed to read template: {e}"}
158
+
159
+ if not dry_run:
160
+ os.makedirs(output_dir, exist_ok=True)
161
+
162
+ generated_files = []
163
+ errors = []
164
+
165
+ for index, context in enumerate(contexts):
166
+ filename = filename_pattern.format(index=index + 1, **context)
167
+ output_path = os.path.join(output_dir, filename)
168
+
169
+ if dry_run:
170
+ if verbose:
171
+ print(f"Would generate: {output_path}")
172
+ generated_files.append(output_path)
173
+ continue
174
+
175
+ filled = TemplateEngine.fill_template(template, context, escape_values)
176
+
177
+ try:
178
+ with open(output_path, "w", encoding="utf-8") as f:
179
+ f.write(filled)
180
+
181
+ if verbose:
182
+ print(f"Generated: {output_path}")
183
+ generated_files.append(output_path)
184
+ except Exception as e:
185
+ error_msg = f"Failed to write {output_path}: {e}"
186
+ if verbose:
187
+ print(f"Error: {error_msg}", file=sys.stderr)
188
+ errors.append(error_msg)
189
+
190
+ result = {
191
+ "success": len(errors) == 0,
192
+ "total_contexts": len(contexts),
193
+ "generated_count": len(generated_files),
194
+ "output_dir": output_dir,
195
+ "generated_files": generated_files,
196
+ }
197
+
198
+ if errors:
199
+ result["errors"] = errors
200
+ result["error_count"] = len(errors)
201
+
202
+ return result
203
+
204
+
205
+ def cmd_fill(args: argparse.Namespace) -> None:
206
+ """Handle fill command."""
207
+ try:
208
+ with open(args.context, "r", encoding="utf-8") as f:
209
+ context = json.load(f)
210
+ except Exception as e:
211
+ result = {"success": False, "error": f"Failed to read context file: {e}"}
212
+ print(json.dumps(result, indent=2))
213
+ sys.exit(1)
214
+
215
+ result = TemplateEngine.generate_batch(
216
+ template_path=args.template,
217
+ context=context,
218
+ output_path=args.output,
219
+ escape_values=args.escape,
220
+ )
221
+
222
+ print(json.dumps(result, indent=2))
223
+
224
+ if not result["success"]:
225
+ sys.exit(1)
226
+
227
+
228
+ def cmd_generate_batches(args: argparse.Namespace) -> None:
229
+ """Handle generate-batches command."""
230
+ try:
231
+ with open(args.contexts, "r", encoding="utf-8") as f:
232
+ contexts = json.load(f)
233
+ except Exception as e:
234
+ result = {"success": False, "error": f"Failed to read contexts file: {e}"}
235
+ print(json.dumps(result, indent=2))
236
+ sys.exit(1)
237
+
238
+ if not isinstance(contexts, list):
239
+ result = {"success": False, "error": "Contexts must be a JSON array"}
240
+ print(json.dumps(result, indent=2))
241
+ sys.exit(1)
242
+
243
+ result = TemplateEngine.generate_batches(
244
+ template_path=args.template,
245
+ contexts=contexts,
246
+ output_dir=args.output_dir,
247
+ filename_pattern=args.filename_pattern,
248
+ escape_values=args.escape,
249
+ dry_run=args.dry_run,
250
+ verbose=args.verbose,
251
+ )
252
+
253
+ print(json.dumps(result, indent=2))
254
+
255
+ if not result["success"]:
256
+ sys.exit(1)
257
+
258
+
259
+ def cmd_escape(args: argparse.Namespace) -> None:
260
+ """Handle escape command."""
261
+ escaped = TemplateEngine.escape_for_markdown(args.text)
262
+ result = {"success": True, "original": args.text, "escaped": escaped}
263
+ print(json.dumps(result, indent=2))
264
+
265
+
266
+ def main() -> None:
267
+ """Main entry point."""
268
+ parser = argparse.ArgumentParser(
269
+ description="Template engine for generating files from templates"
270
+ )
271
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
272
+
273
+ fill_parser = subparsers.add_parser("fill", help="Fill single template")
274
+ fill_parser.add_argument("--template", required=True, help="Path to template file")
275
+ fill_parser.add_argument(
276
+ "--context", required=True, help="Path to context JSON file"
277
+ )
278
+ fill_parser.add_argument("--output", required=True, help="Path for output file")
279
+ fill_parser.add_argument(
280
+ "--escape", action="store_true", help="Escape values for markdown"
281
+ )
282
+
283
+ batch_parser = subparsers.add_parser(
284
+ "generate-batches", help="Generate multiple files"
285
+ )
286
+ batch_parser.add_argument("--template", required=True, help="Path to template file")
287
+ batch_parser.add_argument(
288
+ "--contexts", required=True, help="Path to contexts JSON file"
289
+ )
290
+ batch_parser.add_argument(
291
+ "--output-dir", required=True, help="Directory for output files"
292
+ )
293
+ batch_parser.add_argument(
294
+ "--filename-pattern",
295
+ default="output_{index:03d}.md",
296
+ help="Filename pattern (default: output_{index:03d}.md)",
297
+ )
298
+ batch_parser.add_argument(
299
+ "--escape", action="store_true", help="Escape values for markdown"
300
+ )
301
+ batch_parser.add_argument(
302
+ "--dry-run", action="store_true", help="Validate without writing"
303
+ )
304
+ batch_parser.add_argument("--verbose", action="store_true", help="Print progress")
305
+
306
+ escape_parser = subparsers.add_parser("escape", help="Escape text for markdown")
307
+ escape_parser.add_argument("--text", required=True, help="Text to escape")
308
+
309
+ args = parser.parse_args()
310
+
311
+ if args.command == "fill":
312
+ cmd_fill(args)
313
+ elif args.command == "generate-batches":
314
+ cmd_generate_batches(args)
315
+ elif args.command == "escape":
316
+ cmd_escape(args)
317
+ else:
318
+ parser.print_help()
319
+ sys.exit(1)
320
+
321
+
322
+ if __name__ == "__main__":
323
+ main()