ostruct-cli 0.8.29__py3-none-any.whl → 1.0.0__py3-none-any.whl
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.
- ostruct/cli/__init__.py +3 -15
- ostruct/cli/attachment_processor.py +455 -0
- ostruct/cli/attachment_template_bridge.py +973 -0
- ostruct/cli/cli.py +157 -33
- ostruct/cli/click_options.py +775 -692
- ostruct/cli/code_interpreter.py +195 -12
- ostruct/cli/commands/__init__.py +0 -3
- ostruct/cli/commands/run.py +289 -62
- ostruct/cli/config.py +23 -22
- ostruct/cli/constants.py +89 -0
- ostruct/cli/errors.py +175 -5
- ostruct/cli/explicit_file_processor.py +0 -15
- ostruct/cli/file_info.py +97 -15
- ostruct/cli/file_list.py +43 -1
- ostruct/cli/file_search.py +68 -2
- ostruct/cli/help_json.py +235 -0
- ostruct/cli/mcp_integration.py +13 -16
- ostruct/cli/params.py +217 -0
- ostruct/cli/plan_assembly.py +335 -0
- ostruct/cli/plan_printing.py +385 -0
- ostruct/cli/progress_reporting.py +8 -56
- ostruct/cli/quick_ref_help.py +128 -0
- ostruct/cli/rich_config.py +299 -0
- ostruct/cli/runner.py +397 -190
- ostruct/cli/security/__init__.py +2 -0
- ostruct/cli/security/allowed_checker.py +41 -0
- ostruct/cli/security/normalization.py +13 -9
- ostruct/cli/security/security_manager.py +558 -17
- ostruct/cli/security/types.py +15 -0
- ostruct/cli/template_debug.py +283 -261
- ostruct/cli/template_debug_help.py +233 -142
- ostruct/cli/template_env.py +46 -5
- ostruct/cli/template_filters.py +415 -8
- ostruct/cli/template_processor.py +240 -619
- ostruct/cli/template_rendering.py +49 -73
- ostruct/cli/template_validation.py +2 -1
- ostruct/cli/token_validation.py +35 -15
- ostruct/cli/types.py +15 -19
- ostruct/cli/unicode_compat.py +283 -0
- ostruct/cli/upload_manager.py +448 -0
- ostruct/cli/validators.py +255 -54
- {ostruct_cli-0.8.29.dist-info → ostruct_cli-1.0.0.dist-info}/METADATA +230 -127
- ostruct_cli-1.0.0.dist-info/RECORD +80 -0
- ostruct/cli/commands/quick_ref.py +0 -54
- ostruct/cli/template_optimizer.py +0 -478
- ostruct_cli-0.8.29.dist-info/RECORD +0 -71
- {ostruct_cli-0.8.29.dist-info → ostruct_cli-1.0.0.dist-info}/LICENSE +0 -0
- {ostruct_cli-0.8.29.dist-info → ostruct_cli-1.0.0.dist-info}/WHEEL +0 -0
- {ostruct_cli-0.8.29.dist-info → ostruct_cli-1.0.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,385 @@
|
|
1
|
+
"""Plan printing for human-readable execution plan output.
|
2
|
+
|
3
|
+
This module renders execution plans and run summaries for human consumption
|
4
|
+
using the same data structures as JSON output per UNIFIED GUIDELINES.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import json
|
8
|
+
import sys
|
9
|
+
from datetime import datetime
|
10
|
+
from typing import Any, Dict, Optional, TextIO
|
11
|
+
|
12
|
+
from .unicode_compat import safe_emoji, safe_format
|
13
|
+
|
14
|
+
|
15
|
+
class PlanPrinter:
|
16
|
+
"""Renders execution plans for human eyes using same dict as JSON output.
|
17
|
+
|
18
|
+
This class ensures consistent rendering logic by operating on the same
|
19
|
+
data structures produced by PlanAssembler.
|
20
|
+
"""
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def human(plan: Dict[str, Any], file: Optional[TextIO] = None) -> None:
|
24
|
+
"""Print human-readable version with colors and formatting.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
plan: Plan dictionary from PlanAssembler
|
28
|
+
file: Output file (defaults to stdout)
|
29
|
+
"""
|
30
|
+
if file is None:
|
31
|
+
file = sys.stdout
|
32
|
+
|
33
|
+
plan_type = plan.get("type", "unknown").replace("_", " ").title()
|
34
|
+
emoji = safe_emoji("🔍")
|
35
|
+
if emoji:
|
36
|
+
print(f"{emoji} {plan_type}\n", file=file)
|
37
|
+
else:
|
38
|
+
print(f"{plan_type}\n", file=file)
|
39
|
+
|
40
|
+
# Header with timestamp
|
41
|
+
timestamp = plan["timestamp"]
|
42
|
+
if isinstance(timestamp, str):
|
43
|
+
# Handle ISO format timestamp
|
44
|
+
try:
|
45
|
+
timestamp_dt = datetime.fromisoformat(
|
46
|
+
timestamp.replace("Z", "+00:00")
|
47
|
+
)
|
48
|
+
except ValueError:
|
49
|
+
# Fallback for other string formats
|
50
|
+
timestamp_dt = datetime.now()
|
51
|
+
else:
|
52
|
+
# Handle numeric timestamp (legacy)
|
53
|
+
timestamp_dt = datetime.fromtimestamp(timestamp)
|
54
|
+
|
55
|
+
formatted_time = timestamp_dt.strftime("%Y-%m-%d %H:%M:%S")
|
56
|
+
print(safe_format("🕐 Generated: {}", formatted_time), file=file)
|
57
|
+
|
58
|
+
# Template and schema
|
59
|
+
template = plan.get("template", {})
|
60
|
+
schema = plan.get("schema", {})
|
61
|
+
|
62
|
+
template_status = (
|
63
|
+
safe_emoji("✅", "[OK]")
|
64
|
+
if template.get("exists", False)
|
65
|
+
else safe_emoji("❌", "[ERROR]")
|
66
|
+
)
|
67
|
+
schema_status = (
|
68
|
+
safe_emoji("✅", "[OK]")
|
69
|
+
if schema.get("exists", False)
|
70
|
+
else safe_emoji("❌", "[ERROR]")
|
71
|
+
)
|
72
|
+
|
73
|
+
# Show more helpful template warning information
|
74
|
+
template_path = template.get("path", "unknown")
|
75
|
+
template_warning = template.get("warning")
|
76
|
+
|
77
|
+
if template_warning:
|
78
|
+
# Use warning indicator for configuration issues with existing files
|
79
|
+
warning_status = (
|
80
|
+
safe_emoji("⚠️", "[WARNING]")
|
81
|
+
if template.get("exists", False)
|
82
|
+
else safe_emoji("❌", "[ERROR]")
|
83
|
+
)
|
84
|
+
print(
|
85
|
+
safe_format(
|
86
|
+
"📄 Template: {} {}", warning_status, template_path
|
87
|
+
),
|
88
|
+
file=file,
|
89
|
+
)
|
90
|
+
print(
|
91
|
+
f" Warning: {template_warning}",
|
92
|
+
file=file,
|
93
|
+
)
|
94
|
+
elif template_path == "---":
|
95
|
+
# Special case for YAML frontmatter display issue (legacy)
|
96
|
+
print(
|
97
|
+
safe_format(
|
98
|
+
"📄 Template: {} {}", template_status, template_path
|
99
|
+
),
|
100
|
+
file=file,
|
101
|
+
)
|
102
|
+
print(
|
103
|
+
" Note: Template content shown below",
|
104
|
+
file=file,
|
105
|
+
)
|
106
|
+
else:
|
107
|
+
print(
|
108
|
+
safe_format(
|
109
|
+
"📄 Template: {} {}", template_status, template_path
|
110
|
+
),
|
111
|
+
file=file,
|
112
|
+
)
|
113
|
+
|
114
|
+
print(
|
115
|
+
safe_format(
|
116
|
+
"📋 Schema: {} {}",
|
117
|
+
schema_status,
|
118
|
+
schema.get("path", "unknown"),
|
119
|
+
),
|
120
|
+
file=file,
|
121
|
+
)
|
122
|
+
|
123
|
+
# Model
|
124
|
+
if "model" in plan:
|
125
|
+
print(safe_format("🤖 Model: {}", plan["model"]), file=file)
|
126
|
+
|
127
|
+
# Security
|
128
|
+
security = plan.get("security", {})
|
129
|
+
if security:
|
130
|
+
print(
|
131
|
+
safe_format(
|
132
|
+
"🔒 Security: {}", security.get("mode", "unknown")
|
133
|
+
),
|
134
|
+
file=file,
|
135
|
+
)
|
136
|
+
allowed_paths = security.get("allowed_paths", [])
|
137
|
+
if allowed_paths:
|
138
|
+
print(
|
139
|
+
f" Allowed paths: {len(allowed_paths)} configured",
|
140
|
+
file=file,
|
141
|
+
)
|
142
|
+
|
143
|
+
# Tools
|
144
|
+
tools = plan.get("tools", {})
|
145
|
+
if tools:
|
146
|
+
enabled_tools = [
|
147
|
+
name for name, enabled in tools.items() if enabled
|
148
|
+
]
|
149
|
+
if enabled_tools:
|
150
|
+
print(
|
151
|
+
safe_format("🛠️ Tools: {}", ", ".join(enabled_tools)),
|
152
|
+
file=file,
|
153
|
+
)
|
154
|
+
|
155
|
+
print() # Blank line before attachments
|
156
|
+
|
157
|
+
# Attachments
|
158
|
+
attachments = plan.get("attachments", [])
|
159
|
+
print(safe_format("📎 Attachments ({}):", len(attachments)), file=file)
|
160
|
+
|
161
|
+
if not attachments:
|
162
|
+
print(" (none)", file=file)
|
163
|
+
else:
|
164
|
+
for att in attachments:
|
165
|
+
exists_status = (
|
166
|
+
safe_emoji("✅", "[OK]")
|
167
|
+
if att.get("exists", False)
|
168
|
+
else safe_emoji("❌", "[ERROR]")
|
169
|
+
)
|
170
|
+
targets = ", ".join(att.get("targets", []))
|
171
|
+
path = att.get("path", "unknown")
|
172
|
+
alias = att.get("alias", "unknown")
|
173
|
+
|
174
|
+
print(
|
175
|
+
f" {exists_status} {alias} → {targets}: {path}",
|
176
|
+
file=file,
|
177
|
+
)
|
178
|
+
|
179
|
+
# Show additional details for directories
|
180
|
+
if att.get("type") == "directory":
|
181
|
+
details = []
|
182
|
+
if att.get("recursive"):
|
183
|
+
details.append("recursive")
|
184
|
+
if att.get("pattern"):
|
185
|
+
details.append(f"pattern: {att['pattern']}")
|
186
|
+
if details:
|
187
|
+
print(f" ({', '.join(details)})", file=file)
|
188
|
+
|
189
|
+
# Download validation for Code Interpreter
|
190
|
+
download_validation = plan.get("download_validation", {})
|
191
|
+
if download_validation.get("enabled"):
|
192
|
+
print(safe_format("\n📥 Download Configuration:"), file=file)
|
193
|
+
print(
|
194
|
+
f" Directory: {download_validation.get('directory', 'N/A')}",
|
195
|
+
file=file,
|
196
|
+
)
|
197
|
+
|
198
|
+
# Show writability status
|
199
|
+
if download_validation.get("writable"):
|
200
|
+
print(
|
201
|
+
safe_format(
|
202
|
+
" {} Directory writable", safe_emoji("✅", "[OK]")
|
203
|
+
),
|
204
|
+
file=file,
|
205
|
+
)
|
206
|
+
else:
|
207
|
+
print(
|
208
|
+
safe_format(
|
209
|
+
" {} Directory not writable",
|
210
|
+
safe_emoji("❌", "[ERROR]"),
|
211
|
+
),
|
212
|
+
file=file,
|
213
|
+
)
|
214
|
+
|
215
|
+
# Show issues if any
|
216
|
+
issues = download_validation.get("issues", [])
|
217
|
+
if issues:
|
218
|
+
print(
|
219
|
+
safe_format(
|
220
|
+
" {} Issues:", safe_emoji("⚠️", "[WARNING]")
|
221
|
+
),
|
222
|
+
file=file,
|
223
|
+
)
|
224
|
+
for issue in issues:
|
225
|
+
print(f" - {issue}", file=file)
|
226
|
+
|
227
|
+
# Show potential conflicts
|
228
|
+
conflicts = download_validation.get("conflicts", [])
|
229
|
+
if conflicts:
|
230
|
+
print(
|
231
|
+
safe_format(
|
232
|
+
" {} Potential conflicts: {}",
|
233
|
+
safe_emoji("⚠️", "[WARNING]"),
|
234
|
+
", ".join(conflicts),
|
235
|
+
),
|
236
|
+
file=file,
|
237
|
+
)
|
238
|
+
|
239
|
+
# Variables
|
240
|
+
variables = plan.get("variables", {})
|
241
|
+
if variables:
|
242
|
+
print(
|
243
|
+
safe_format("\n📊 Variables ({}):", len(variables)), file=file
|
244
|
+
)
|
245
|
+
for name, value in variables.items():
|
246
|
+
# Truncate long values for readability
|
247
|
+
value_str = str(value)
|
248
|
+
if len(value_str) > 50:
|
249
|
+
value_str = value_str[:47] + "..."
|
250
|
+
print(f" {name}: {value_str}", file=file)
|
251
|
+
|
252
|
+
# Cost estimate
|
253
|
+
cost = plan.get("cost_estimate", {})
|
254
|
+
if cost and cost.get("approx_usd", 0) > 0:
|
255
|
+
estimated_note = " (estimated)" if cost.get("estimated") else ""
|
256
|
+
print(
|
257
|
+
safe_format(
|
258
|
+
"\n💰 Cost: ~${:.4f}{}",
|
259
|
+
cost.get("approx_usd", 0),
|
260
|
+
estimated_note,
|
261
|
+
),
|
262
|
+
file=file,
|
263
|
+
)
|
264
|
+
if "tokens" in cost:
|
265
|
+
print(f" Tokens: ~{cost['tokens']:,}", file=file)
|
266
|
+
|
267
|
+
# Execution time (for run summaries)
|
268
|
+
if plan.get("type") == "run_summary":
|
269
|
+
exec_time = plan.get("execution_time")
|
270
|
+
success = plan.get("success", True)
|
271
|
+
|
272
|
+
status_icon = (
|
273
|
+
safe_emoji("✅", "[OK]")
|
274
|
+
if success
|
275
|
+
else safe_emoji("❌", "[ERROR]")
|
276
|
+
)
|
277
|
+
print(
|
278
|
+
safe_format(
|
279
|
+
"\n{} Status: {}",
|
280
|
+
status_icon,
|
281
|
+
"Success" if success else "Failed",
|
282
|
+
),
|
283
|
+
file=file,
|
284
|
+
)
|
285
|
+
|
286
|
+
if exec_time is not None:
|
287
|
+
print(
|
288
|
+
safe_format("⏱️ Execution time: {:.2f}s", exec_time),
|
289
|
+
file=file,
|
290
|
+
)
|
291
|
+
|
292
|
+
# Show error if present
|
293
|
+
if "error" in plan:
|
294
|
+
print(
|
295
|
+
safe_format(
|
296
|
+
"{} Error: {}",
|
297
|
+
safe_emoji("❌", "[ERROR]"),
|
298
|
+
plan["error"],
|
299
|
+
),
|
300
|
+
file=file,
|
301
|
+
)
|
302
|
+
|
303
|
+
# Show cost breakdown if present
|
304
|
+
if "cost_breakdown" in plan:
|
305
|
+
cost_breakdown = plan["cost_breakdown"]
|
306
|
+
print(
|
307
|
+
safe_format(
|
308
|
+
"💰 Final cost: ${:.4f}",
|
309
|
+
cost_breakdown.get("total", 0),
|
310
|
+
),
|
311
|
+
file=file,
|
312
|
+
)
|
313
|
+
|
314
|
+
@staticmethod
|
315
|
+
def json(
|
316
|
+
plan: Dict[str, Any], file: Optional[TextIO] = None, indent: int = 2
|
317
|
+
) -> None:
|
318
|
+
"""Print JSON version of plan.
|
319
|
+
|
320
|
+
Args:
|
321
|
+
plan: Plan dictionary from PlanAssembler
|
322
|
+
file: Output file (defaults to stdout)
|
323
|
+
indent: JSON indentation level
|
324
|
+
"""
|
325
|
+
if file is None:
|
326
|
+
file = sys.stdout
|
327
|
+
|
328
|
+
json.dump(plan, file, indent=indent, default=str)
|
329
|
+
file.write("\n")
|
330
|
+
|
331
|
+
@staticmethod
|
332
|
+
def summary_line(plan: Dict[str, Any]) -> str:
|
333
|
+
"""Generate a one-line summary of the plan.
|
334
|
+
|
335
|
+
Args:
|
336
|
+
plan: Plan dictionary from PlanAssembler
|
337
|
+
|
338
|
+
Returns:
|
339
|
+
Single line summary string
|
340
|
+
"""
|
341
|
+
plan_type = plan.get("type", "unknown")
|
342
|
+
|
343
|
+
if plan_type == "execution_plan":
|
344
|
+
attachments = len(plan.get("attachments", []))
|
345
|
+
variables = len(plan.get("variables", {}))
|
346
|
+
return f"Plan: {attachments} attachments, {variables} variables, model {plan.get('model', 'unknown')}"
|
347
|
+
|
348
|
+
elif plan_type == "run_summary":
|
349
|
+
success = plan.get("success", True)
|
350
|
+
exec_time = plan.get("execution_time")
|
351
|
+
status = "Success" if success else "Failed"
|
352
|
+
time_str = f" in {exec_time:.2f}s" if exec_time else ""
|
353
|
+
return f"Summary: {status}{time_str}"
|
354
|
+
|
355
|
+
else:
|
356
|
+
return f"Plan: {plan_type}"
|
357
|
+
|
358
|
+
@staticmethod
|
359
|
+
def validate_and_print(
|
360
|
+
plan: Dict[str, Any],
|
361
|
+
output_format: str = "human",
|
362
|
+
file: Optional[TextIO] = None,
|
363
|
+
) -> None:
|
364
|
+
"""Validate plan structure and print in specified format.
|
365
|
+
|
366
|
+
Args:
|
367
|
+
plan: Plan dictionary from PlanAssembler
|
368
|
+
output_format: Output format ("human" or "json")
|
369
|
+
file: Output file (defaults to stdout)
|
370
|
+
|
371
|
+
Raises:
|
372
|
+
ValueError: If plan structure is invalid or format is unsupported
|
373
|
+
"""
|
374
|
+
from .plan_assembly import PlanAssembler
|
375
|
+
|
376
|
+
# Validate plan structure
|
377
|
+
PlanAssembler.validate_plan(plan)
|
378
|
+
|
379
|
+
# Print in requested format
|
380
|
+
if output_format == "human":
|
381
|
+
PlanPrinter.human(plan, file)
|
382
|
+
elif output_format == "json":
|
383
|
+
PlanPrinter.json(plan, file)
|
384
|
+
else:
|
385
|
+
raise ValueError(f"Unsupported output format: {output_format}")
|
@@ -47,17 +47,17 @@ class ProcessingResult:
|
|
47
47
|
class EnhancedProgressReporter:
|
48
48
|
"""Enhanced progress reporter with user-friendly language and transparency."""
|
49
49
|
|
50
|
-
def __init__(self, verbose: bool = False,
|
50
|
+
def __init__(self, verbose: bool = False, progress: str = "basic"):
|
51
51
|
"""Initialize the progress reporter.
|
52
52
|
|
53
53
|
Args:
|
54
54
|
verbose: Enable verbose logging output
|
55
|
-
|
55
|
+
progress: Progress level (none, basic, detailed)
|
56
56
|
"""
|
57
57
|
self.verbose = verbose
|
58
|
-
self.
|
59
|
-
self.should_report =
|
60
|
-
self.detailed =
|
58
|
+
self.progress = progress
|
59
|
+
self.should_report = progress != "none"
|
60
|
+
self.detailed = progress == "detailed" or verbose
|
61
61
|
|
62
62
|
def report_phase(self, phase_name: str, emoji: str = "⚙️") -> None:
|
63
63
|
"""Report the start of a major processing phase.
|
@@ -69,27 +69,6 @@ class EnhancedProgressReporter:
|
|
69
69
|
if self.should_report:
|
70
70
|
click.echo(f"{emoji} {phase_name}...", err=True)
|
71
71
|
|
72
|
-
def report_optimization(self, optimizations: List[str]) -> None:
|
73
|
-
"""Report template optimization results with user-friendly language.
|
74
|
-
|
75
|
-
Args:
|
76
|
-
optimizations: List of optimization transformations applied
|
77
|
-
"""
|
78
|
-
if not self.should_report or not optimizations:
|
79
|
-
return
|
80
|
-
|
81
|
-
if self.detailed:
|
82
|
-
click.echo("🔧 Template optimization:", err=True)
|
83
|
-
for optimization in optimizations:
|
84
|
-
# Convert technical language to user-friendly descriptions
|
85
|
-
user_friendly = self._humanize_optimization(optimization)
|
86
|
-
click.echo(f" → {user_friendly}", err=True)
|
87
|
-
else:
|
88
|
-
click.echo(
|
89
|
-
f"🔧 Optimized template for better AI performance ({len(optimizations)} improvements)",
|
90
|
-
err=True,
|
91
|
-
)
|
92
|
-
|
93
72
|
def report_file_routing(
|
94
73
|
self,
|
95
74
|
template_files: List[str],
|
@@ -322,33 +301,6 @@ class EnhancedProgressReporter:
|
|
322
301
|
else:
|
323
302
|
click.echo("✅ Validation passed", err=True)
|
324
303
|
|
325
|
-
def _humanize_optimization(self, technical_message: str) -> str:
|
326
|
-
"""Convert technical optimization messages to user-friendly language.
|
327
|
-
|
328
|
-
Args:
|
329
|
-
technical_message: Technical optimization message
|
330
|
-
|
331
|
-
Returns:
|
332
|
-
User-friendly description of the optimization
|
333
|
-
"""
|
334
|
-
# Convert technical messages to user-friendly descriptions
|
335
|
-
if (
|
336
|
-
"moved" in technical_message.lower()
|
337
|
-
and "appendix" in technical_message.lower()
|
338
|
-
):
|
339
|
-
file_name = technical_message.split("Moved ")[-1].split(
|
340
|
-
" to appendix"
|
341
|
-
)[0]
|
342
|
-
return f"Moved large file '{file_name}' to organized appendix"
|
343
|
-
elif "built structured appendix" in technical_message.lower():
|
344
|
-
return "Organized file content into structured appendix for better AI processing"
|
345
|
-
elif "moved directory" in technical_message.lower():
|
346
|
-
return technical_message.replace(
|
347
|
-
"Moved directory", "Organized directory"
|
348
|
-
)
|
349
|
-
else:
|
350
|
-
return technical_message
|
351
|
-
|
352
304
|
|
353
305
|
# Global progress reporter instance
|
354
306
|
_progress_reporter: Optional[EnhancedProgressReporter] = None
|
@@ -367,16 +319,16 @@ def get_progress_reporter() -> EnhancedProgressReporter:
|
|
367
319
|
|
368
320
|
|
369
321
|
def configure_progress_reporter(
|
370
|
-
verbose: bool = False,
|
322
|
+
verbose: bool = False, progress: str = "basic"
|
371
323
|
) -> None:
|
372
324
|
"""Configure the global progress reporter.
|
373
325
|
|
374
326
|
Args:
|
375
327
|
verbose: Enable verbose logging output
|
376
|
-
|
328
|
+
progress: Progress level (none, basic, detailed)
|
377
329
|
"""
|
378
330
|
global _progress_reporter
|
379
|
-
_progress_reporter = EnhancedProgressReporter(verbose,
|
331
|
+
_progress_reporter = EnhancedProgressReporter(verbose, progress)
|
380
332
|
|
381
333
|
|
382
334
|
def report_phase(phase_name: str, emoji: str = "⚙️") -> None:
|
@@ -0,0 +1,128 @@
|
|
1
|
+
"""Quick reference help system for ostruct CLI.
|
2
|
+
|
3
|
+
This module provides quick usage examples and patterns with rich formatting.
|
4
|
+
Uses rich-click formatting for beautiful, organized output.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.panel import Panel
|
9
|
+
from rich.syntax import Syntax
|
10
|
+
from rich.text import Text
|
11
|
+
|
12
|
+
|
13
|
+
def show_quick_ref_help() -> None:
|
14
|
+
"""Display quick reference help with rich formatting."""
|
15
|
+
console = Console(stderr=True)
|
16
|
+
|
17
|
+
# Main title
|
18
|
+
title = Text("🚀 OSTRUCT QUICK REFERENCE", style="bold bright_blue")
|
19
|
+
console.print(title)
|
20
|
+
console.print()
|
21
|
+
|
22
|
+
# File Attachment System
|
23
|
+
attachment_content = """[bold bright_blue]--file[/bold bright_blue] alias file.txt 📄 Template access (default target)
|
24
|
+
[bold bright_blue]--file[/bold bright_blue] ci:data file.csv 💻 Code Interpreter upload
|
25
|
+
[bold bright_blue]--file[/bold bright_blue] fs:docs file.pdf 🔍 File Search vector store
|
26
|
+
[bold bright_blue]--file[/bold bright_blue] prompt:config config.yaml 📄 Template access (explicit)
|
27
|
+
|
28
|
+
[bold bright_blue]--dir[/bold bright_blue] alias ./src 📁 Directory attachment (template)
|
29
|
+
[bold bright_blue]--dir[/bold bright_blue] ci:data ./datasets 📂 Code execution directory
|
30
|
+
[bold bright_blue]--dir[/bold bright_blue] fs:docs ./documentation 📁 Search directory"""
|
31
|
+
|
32
|
+
attachment_panel = Panel(
|
33
|
+
attachment_content,
|
34
|
+
title="[bold]📎 File Attachment System[/bold]",
|
35
|
+
border_style="blue",
|
36
|
+
padding=(1, 2),
|
37
|
+
)
|
38
|
+
console.print(attachment_panel)
|
39
|
+
|
40
|
+
# Multi-tool Routing
|
41
|
+
routing_content = """[bold cyan]--file ci,fs:shared data.csv[/bold cyan] Share file between Code Interpreter and File Search
|
42
|
+
[bold cyan]--file prompt,ci:config settings.json[/bold cyan] Make file available in template AND Code Interpreter"""
|
43
|
+
|
44
|
+
routing_panel = Panel(
|
45
|
+
routing_content,
|
46
|
+
title="[bold]🔄 Multi-Tool Routing[/bold]",
|
47
|
+
border_style="cyan",
|
48
|
+
padding=(1, 2),
|
49
|
+
)
|
50
|
+
console.print(routing_panel)
|
51
|
+
|
52
|
+
# File Collections
|
53
|
+
collections_content = """[bold green]--collect all:files @filelist.txt[/bold green] 📄 Attach multiple files from list
|
54
|
+
[bold green]--collect ci:data @datasets.txt[/bold green] 💻 Upload file collection to Code Interpreter"""
|
55
|
+
|
56
|
+
collections_panel = Panel(
|
57
|
+
collections_content,
|
58
|
+
title="[bold]📝 File Collections[/bold]",
|
59
|
+
border_style="green",
|
60
|
+
padding=(1, 2),
|
61
|
+
)
|
62
|
+
console.print(collections_panel)
|
63
|
+
|
64
|
+
# Variables and Tools
|
65
|
+
vars_tools_content = """[bold yellow]Variables:[/bold yellow]
|
66
|
+
[cyan]-V name=value[/cyan] Simple string variables
|
67
|
+
[cyan]-J config='{"key":"value"}'[/cyan] JSON structured data
|
68
|
+
|
69
|
+
[bold yellow]Tools:[/bold yellow]
|
70
|
+
[cyan]--enable-tool web-search[/cyan] 🌐 Real-time web search for current info
|
71
|
+
[cyan]--mcp-server label@https://server.com/sse[/cyan] MCP server integration
|
72
|
+
[cyan]--timeout 7200[/cyan] 2-hour timeout for long operations"""
|
73
|
+
|
74
|
+
vars_tools_panel = Panel(
|
75
|
+
vars_tools_content,
|
76
|
+
title="[bold]🏷️ Variables & 🔌 Tools[/bold]",
|
77
|
+
border_style="yellow",
|
78
|
+
padding=(1, 2),
|
79
|
+
)
|
80
|
+
console.print(vars_tools_panel)
|
81
|
+
|
82
|
+
# Common Patterns
|
83
|
+
console.print(Text("⚡ Common Patterns", style="bold bright_green"))
|
84
|
+
console.print()
|
85
|
+
|
86
|
+
patterns = [
|
87
|
+
(
|
88
|
+
"Basic template rendering",
|
89
|
+
"ostruct run template.j2 schema.json -V env=prod",
|
90
|
+
),
|
91
|
+
(
|
92
|
+
"Data analysis with Code Interpreter",
|
93
|
+
"ostruct run analysis.j2 schema.json --file ci:data data.csv -V task=analyze",
|
94
|
+
),
|
95
|
+
(
|
96
|
+
"Document search + processing",
|
97
|
+
"ostruct run search.j2 schema.json --file fs:docs docs/ --file config config.yaml",
|
98
|
+
),
|
99
|
+
(
|
100
|
+
"Multi-tool workflow",
|
101
|
+
"ostruct run workflow.j2 schema.json --file ci:data raw_data.csv --file fs:knowledge knowledge/ --file config config.json",
|
102
|
+
),
|
103
|
+
(
|
104
|
+
"Current information research",
|
105
|
+
'ostruct run research.j2 schema.json --enable-tool web-search -V topic="latest AI developments"',
|
106
|
+
),
|
107
|
+
]
|
108
|
+
|
109
|
+
for desc, cmd in patterns:
|
110
|
+
console.print(f"[dim]# {desc}[/dim]")
|
111
|
+
syntax = Syntax(
|
112
|
+
cmd, "bash", theme="monokai", background_color="default"
|
113
|
+
)
|
114
|
+
console.print(syntax)
|
115
|
+
console.print()
|
116
|
+
|
117
|
+
# Footer with help links
|
118
|
+
footer_content = """📖 [cyan]ostruct run --help[/cyan] Detailed options reference
|
119
|
+
📖 [cyan]ostruct run --help-debug[/cyan] Troubleshooting guide
|
120
|
+
📖 [dim]https://ostruct.readthedocs.io[/dim] Full documentation"""
|
121
|
+
|
122
|
+
footer_panel = Panel(
|
123
|
+
footer_content,
|
124
|
+
title="[bold]📖 More Help[/bold]",
|
125
|
+
border_style="blue",
|
126
|
+
padding=(1, 2),
|
127
|
+
)
|
128
|
+
console.print(footer_panel)
|