bone-agent 1.4.0 → 2.0.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/bin/bone.js +39 -0
- package/package.json +25 -39
- package/LICENSE +0 -21
- package/README.md +0 -201
- package/bin/npm-wrapper.js +0 -235
- package/bin/rg +0 -0
- package/bin/rg.exe +0 -0
- package/config.yaml.example +0 -144
- package/prompts/main/ask_questions.md +0 -31
- package/prompts/main/batch_independent_calls.md +0 -5
- package/prompts/main/casual_interactions.md +0 -11
- package/prompts/main/code_references.md +0 -8
- package/prompts/main/communication_style.md +0 -12
- package/prompts/main/context_reliability.md +0 -12
- package/prompts/main/conversational_tool_calling.md +0 -15
- package/prompts/main/dream.md +0 -50
- package/prompts/main/editing_pattern.md +0 -13
- package/prompts/main/error_handling.md +0 -6
- package/prompts/main/exploration_pattern.md +0 -21
- package/prompts/main/intro.md +0 -1
- package/prompts/main/obsidian.md +0 -16
- package/prompts/main/obsidian_project.md +0 -79
- package/prompts/main/professional_objectivity.md +0 -3
- package/prompts/main/skills.md +0 -3
- package/prompts/main/targeted_searching.md +0 -10
- package/prompts/main/task_lists_pattern.md +0 -8
- package/prompts/main/temp_folder.md +0 -9
- package/prompts/main/think_before_acting.md +0 -10
- package/prompts/main/tone_and_style.md +0 -4
- package/prompts/main/tool_preferences.md +0 -24
- package/prompts/main/trust_subagent_context.md +0 -21
- package/prompts/main/when_to_use_sub_agent.md +0 -7
- package/prompts/micro/ask_questions.md +0 -1
- package/prompts/micro/batch_independent_calls.md +0 -1
- package/prompts/micro/casual_interactions.md +0 -1
- package/prompts/micro/code_references.md +0 -1
- package/prompts/micro/communication_style.md +0 -1
- package/prompts/micro/context_reliability.md +0 -1
- package/prompts/micro/conversational_tool_calling.md +0 -1
- package/prompts/micro/editing_pattern.md +0 -1
- package/prompts/micro/error_handling.md +0 -1
- package/prompts/micro/exploration_pattern.md +0 -1
- package/prompts/micro/intro.md +0 -1
- package/prompts/micro/obsidian.md +0 -4
- package/prompts/micro/obsidian_project.md +0 -5
- package/prompts/micro/professional_objectivity.md +0 -1
- package/prompts/micro/skills.md +0 -1
- package/prompts/micro/targeted_searching.md +0 -1
- package/prompts/micro/task_lists_pattern.md +0 -1
- package/prompts/micro/temp_folder.md +0 -1
- package/prompts/micro/think_before_acting.md +0 -5
- package/prompts/micro/tone_and_style.md +0 -1
- package/prompts/micro/tool_preferences.md +0 -1
- package/prompts/micro/trust_subagent_context.md +0 -1
- package/prompts/micro/when_to_use_sub_agent.md +0 -1
- package/requirements.txt +0 -9
- package/src/__init__.py +0 -11
- package/src/core/__init__.py +0 -1
- package/src/core/agentic.py +0 -1085
- package/src/core/chat_manager.py +0 -1577
- package/src/core/config_manager.py +0 -260
- package/src/core/cron.py +0 -578
- package/src/core/cron_allowlist.py +0 -118
- package/src/core/memory.py +0 -145
- package/src/core/metadata.py +0 -75
- package/src/core/retry.py +0 -71
- package/src/core/skills.py +0 -463
- package/src/core/sub_agent.py +0 -376
- package/src/core/tool_approval.py +0 -220
- package/src/core/tool_feedback.py +0 -789
- package/src/exceptions.py +0 -79
- package/src/llm/__init__.py +0 -1
- package/src/llm/client.py +0 -176
- package/src/llm/codex_provider.py +0 -350
- package/src/llm/config.py +0 -536
- package/src/llm/prompts.py +0 -494
- package/src/llm/providers.py +0 -438
- package/src/llm/streaming.py +0 -163
- package/src/llm/token_tracker.py +0 -399
- package/src/tools/__init__.py +0 -151
- package/src/tools/constants.py +0 -59
- package/src/tools/create_file.py +0 -136
- package/src/tools/directory.py +0 -389
- package/src/tools/edit.py +0 -549
- package/src/tools/file_reader.py +0 -322
- package/src/tools/helpers/__init__.py +0 -99
- package/src/tools/helpers/base.py +0 -599
- package/src/tools/helpers/converters.py +0 -44
- package/src/tools/helpers/file_helpers.py +0 -189
- package/src/tools/helpers/formatters.py +0 -411
- package/src/tools/helpers/loader.py +0 -145
- package/src/tools/helpers/parallel_executor.py +0 -231
- package/src/tools/helpers/path_resolver.py +0 -283
- package/src/tools/helpers/plugin_manifest.py +0 -185
- package/src/tools/obsidian.py +0 -96
- package/src/tools/review_sub_agent.py +0 -190
- package/src/tools/rg_search.py +0 -477
- package/src/tools/search_plugins.py +0 -177
- package/src/tools/select_option.py +0 -600
- package/src/tools/shell.py +0 -302
- package/src/tools/sub_agent.py +0 -139
- package/src/tools/task_list.py +0 -269
- package/src/tools/web_search.py +0 -61
- package/src/ui/__init__.py +0 -1
- package/src/ui/banner.py +0 -87
- package/src/ui/commands.py +0 -3131
- package/src/ui/displays.py +0 -239
- package/src/ui/loader.py +0 -284
- package/src/ui/main.py +0 -643
- package/src/ui/prompt_utils.py +0 -113
- package/src/ui/setting_selector.py +0 -590
- package/src/ui/setup_wizard.py +0 -294
- package/src/ui/sub_agent_panel.py +0 -234
- package/src/ui/tool_confirmation.py +0 -226
- package/src/utils/__init__.py +0 -1
- package/src/utils/citation_parser.py +0 -199
- package/src/utils/editor.py +0 -207
- package/src/utils/gitignore_filter.py +0 -149
- package/src/utils/logger.py +0 -254
- package/src/utils/paths.py +0 -30
- package/src/utils/result_parsers.py +0 -108
- package/src/utils/safe_commands.py +0 -243
- package/src/utils/settings.py +0 -195
- package/src/utils/user_message_logger.py +0 -120
- package/src/utils/validation.py +0 -201
- package/src/utils/web_search.py +0 -173
package/src/ui/displays.py
DELETED
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
"""UI display functions for command outputs."""
|
|
2
|
-
|
|
3
|
-
from rich.table import Table
|
|
4
|
-
from rich.panel import Panel
|
|
5
|
-
from rich import box
|
|
6
|
-
from llm import config
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def show_provider_table(current_provider: str, console):
|
|
10
|
-
"""Display provider status table.
|
|
11
|
-
|
|
12
|
-
Args:
|
|
13
|
-
current_provider: Name of the currently active provider.
|
|
14
|
-
console: Rich Console instance for output.
|
|
15
|
-
"""
|
|
16
|
-
table = Table("Provider", "Status", "Details", title="Providers", box=box.SIMPLE_HEAD)
|
|
17
|
-
for provider in config.get_providers():
|
|
18
|
-
cfg = config.get_provider_config(provider)
|
|
19
|
-
model = cfg.get("model", "N/A")
|
|
20
|
-
if provider == "local":
|
|
21
|
-
status = "✅" if cfg.get("model") else "❌ (set model path)"
|
|
22
|
-
else:
|
|
23
|
-
status = "✅" if cfg.get("api_key") else "❌ (set API key)"
|
|
24
|
-
active = " [green](active)[/green]" if provider == current_provider else ""
|
|
25
|
-
table.add_row(config.get_provider_display_name(provider), status, f"{model[:40]}{active}")
|
|
26
|
-
|
|
27
|
-
console.print(table)
|
|
28
|
-
|
|
29
|
-
help_text = """Usage: /provider <name>
|
|
30
|
-
|
|
31
|
-
Opens an editor to configure model, API key, and costs.
|
|
32
|
-
|
|
33
|
-
Examples:
|
|
34
|
-
/provider openrouter
|
|
35
|
-
/provider glm
|
|
36
|
-
/provider local
|
|
37
|
-
/provider gemini
|
|
38
|
-
/provider minimax
|
|
39
|
-
/provider anthropic
|
|
40
|
-
/provider kimi"""
|
|
41
|
-
console.print(Panel(help_text, title="[bold #5F9EA0]Provider Settings[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
42
|
-
console.print("")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def show_help_table(console):
|
|
46
|
-
"""Display command help table.
|
|
47
|
-
|
|
48
|
-
Args:
|
|
49
|
-
console: Rich Console instance for output.
|
|
50
|
-
"""
|
|
51
|
-
console.print("")
|
|
52
|
-
table = Table(show_header=True, box=box.SIMPLE_HEAD)
|
|
53
|
-
table.add_column("Command", no_wrap=True)
|
|
54
|
-
table.add_column("Description")
|
|
55
|
-
|
|
56
|
-
table.add_row("[bold #5F9EA0]/help[/bold #5F9EA0]", "Show help")
|
|
57
|
-
table.add_row("[bold #5F9EA0]/exit[/bold #5F9EA0]", "Exit chat")
|
|
58
|
-
table.add_row("[bold #5F9EA0]/config[/bold #5F9EA0]", "Show all configuration settings")
|
|
59
|
-
table.add_row("[bold #5F9EA0]/provider[/bold #5F9EA0] [name]", "Configure provider settings (model, key, costs)")
|
|
60
|
-
table.add_row("[bold #5F9EA0]/key[/bold #5F9EA0] <key>", "Set API key for current provider")
|
|
61
|
-
table.add_row("[bold #5F9EA0]/model[/bold #5F9EA0] <name>", "Set model for current provider")
|
|
62
|
-
table.add_row("[bold #5F9EA0]/usage[/bold #5F9EA0] [provider] [in|out] <cost>", "Set/view provider-specific token cost")
|
|
63
|
-
table.add_row("[bold #5F9EA0]/compact[/bold #5F9EA0]", "Compact context with an AI summary")
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
table.add_row("[bold #5F9EA0]/cd[/bold #5F9EA0] [path]", "Change working directory (no args to show current)")
|
|
67
|
-
table.add_row("[bold #5F9EA0]/edit[/bold #5F9EA0], [bold #5F9EA0]/e[/bold #5F9EA0]", "Open editor for multi-line input")
|
|
68
|
-
table.add_row("[bold #5F9EA0]/review[/bold #5F9EA0] [args], [bold #5F9EA0]/r[/bold #5F9EA0]", "Code review git changes (e.g. /review --staged, /review main..HEAD)")
|
|
69
|
-
table.add_row("[bold #5F9EA0]/skills[/bold #5F9EA0] [list|add|modify|remove|use]", "Manage reusable prompt skills")
|
|
70
|
-
table.add_row("[bold #5F9EA0]/obsidian[/bold #5F9EA0] [set|enable|disable|status|init]", "Manage vault integration, scaffold project folders")
|
|
71
|
-
table.add_row("[bold #5F9EA0]/tools[/bold #5F9EA0] [list|enable|disable|enable-group|disable-group]", "Toggle tools or groups (e.g. file_ops, task_mgmt)")
|
|
72
|
-
table.add_row("[bold #5F9EA0]/setup[/bold #5F9EA0]", "Re-run the first-run setup wizard")
|
|
73
|
-
table.add_row("[bold #5F9EA0]/cron[/bold #5F9EA0] [list|add|remove|enable|disable|run]", "Manage scheduled cron jobs")
|
|
74
|
-
table.add_row("[bold #5F9EA0]:[/bold #5F9EA0]<command>", "Run a shell command (e.g. :git status)")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.print(Panel(table, title="[bold #5F9EA0]Commands[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
78
|
-
|
|
79
|
-
# Account management section
|
|
80
|
-
console.print()
|
|
81
|
-
acct_table = Table(show_header=True, box=box.SIMPLE_HEAD)
|
|
82
|
-
acct_table.add_column("Command", no_wrap=True)
|
|
83
|
-
acct_table.add_column("Description")
|
|
84
|
-
|
|
85
|
-
acct_table.add_row("[bold #5F9EA0]/signup[/bold #5F9EA0] <email>", "Create bone-agent account and get API key")
|
|
86
|
-
acct_table.add_row("[bold #5F9EA0]/login[/bold #5F9EA0]", "Log in to an existing bone-agent account")
|
|
87
|
-
acct_table.add_row("[bold #5F9EA0]/account[/bold #5F9EA0]", "View account info and subscription status")
|
|
88
|
-
acct_table.add_row("[bold #5F9EA0]/plan[/bold #5F9EA0]", "View available plans and pricing")
|
|
89
|
-
acct_table.add_row("[bold #5F9EA0]/upgrade[/bold #5F9EA0]", "Upgrade or change your plan")
|
|
90
|
-
acct_table.add_row("[bold #5F9EA0]/manage[/bold #5F9EA0]", "Cancel subscription or update payment (Stripe portal)")
|
|
91
|
-
acct_table.add_row("[bold #5F9EA0]/rotate-key[/bold #5F9EA0]", "Invalidate current API key and generate a new one")
|
|
92
|
-
acct_table.add_row("[bold #5F9EA0]/reset-key[/bold #5F9EA0]", "Get a new API key emailed to you (lost key recovery)")
|
|
93
|
-
|
|
94
|
-
console.print(Panel(acct_table, title="[bold #5F9EA0]Account[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
95
|
-
|
|
96
|
-
# Keybinds section
|
|
97
|
-
console.print()
|
|
98
|
-
keybinds = Table(show_header=True, box=box.SIMPLE_HEAD)
|
|
99
|
-
keybinds.add_column("Keybind", no_wrap=True)
|
|
100
|
-
keybinds.add_column("Action")
|
|
101
|
-
|
|
102
|
-
keybinds.add_row("Shift+Tab", "Cycle approval mode")
|
|
103
|
-
keybinds.add_row("Ctrl+C", "Interrupt response")
|
|
104
|
-
keybinds.add_row("Ctrl+C (2x)", "Exit program")
|
|
105
|
-
|
|
106
|
-
console.print(Panel(keybinds, title="[bold #5F9EA0]Keybinds[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
107
|
-
console.print("")
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def show_cron_help_table(console):
|
|
111
|
-
"""Display cron command help table.
|
|
112
|
-
|
|
113
|
-
Args:
|
|
114
|
-
console: Rich Console instance for output.
|
|
115
|
-
"""
|
|
116
|
-
console.print("")
|
|
117
|
-
table = Table(show_header=True, box=box.SIMPLE_HEAD)
|
|
118
|
-
table.add_column("Command", no_wrap=True)
|
|
119
|
-
table.add_column("Description")
|
|
120
|
-
|
|
121
|
-
table.add_row("[bold #5F9EA0]/cron list[/bold #5F9EA0]", "Show all cron jobs (default)")
|
|
122
|
-
table.add_row("[bold #5F9EA0]/cron add[/bold #5F9EA0] <id> <schedule> <cmd>", "Add a new cron job")
|
|
123
|
-
table.add_row("[bold #5F9EA0]/cron remove[/bold #5F9EA0] <id>", "Remove a cron job")
|
|
124
|
-
table.add_row("[bold #5F9EA0]/cron enable[/bold #5F9EA0] <id>", "Enable a cron job")
|
|
125
|
-
table.add_row("[bold #5F9EA0]/cron disable[/bold #5F9EA0] <id>", "Disable a cron job")
|
|
126
|
-
table.add_row("[bold #5F9EA0]/cron run[/bold #5F9EA0] <id>", "Run a job immediately (interactive)")
|
|
127
|
-
table.add_row("[bold #5F9EA0]/cron allowlist[/bold #5F9EA0] [list|add|remove|clear]", "Manage allowed commands for a job")
|
|
128
|
-
|
|
129
|
-
console.print(Panel(table, title="[bold #5F9EA0]Commands[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
130
|
-
|
|
131
|
-
# Schedule formats section
|
|
132
|
-
console.print()
|
|
133
|
-
sched_table = Table(show_header=True, box=box.SIMPLE_HEAD)
|
|
134
|
-
sched_table.add_column("Format")
|
|
135
|
-
sched_table.add_column("Example")
|
|
136
|
-
|
|
137
|
-
sched_table.add_row("every <n> <unit>", "every 5 minutes, every 1 hour, every 3 days")
|
|
138
|
-
sched_table.add_row("daily at <time>", "daily at 8am, daily at 17:30")
|
|
139
|
-
sched_table.add_row("<day>s at <time>", "weekdays at 9am, mondays at 10:30pm")
|
|
140
|
-
sched_table.add_row("<time>", "08:00, 17:30")
|
|
141
|
-
|
|
142
|
-
console.print(Panel(sched_table, title="[bold #5F9EA0]Schedule Formats[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
143
|
-
console.print("")
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def show_skills_help_table(console):
|
|
147
|
-
"""Display skills command help table.
|
|
148
|
-
|
|
149
|
-
Args:
|
|
150
|
-
console: Rich Console instance for output.
|
|
151
|
-
"""
|
|
152
|
-
console.print("")
|
|
153
|
-
table = Table(show_header=True, box=box.SIMPLE_HEAD)
|
|
154
|
-
table.add_column("Command", no_wrap=True)
|
|
155
|
-
table.add_column("Description")
|
|
156
|
-
|
|
157
|
-
table.add_row("[bold #5F9EA0]/skills list[/bold #5F9EA0]", "List skills")
|
|
158
|
-
table.add_row("[bold #5F9EA0]/skills add[/bold #5F9EA0] <name>", "Create a skill in your editor")
|
|
159
|
-
table.add_row("[bold #5F9EA0]/skills edit[/bold #5F9EA0] <name>", "Edit an existing skill")
|
|
160
|
-
table.add_row("[bold #5F9EA0]/skills modify[/bold #5F9EA0] <name> <prompt>", "Replace a skill")
|
|
161
|
-
table.add_row("[bold #5F9EA0]/skills show[/bold #5F9EA0] <name>", "Show a skill")
|
|
162
|
-
table.add_row("[bold #5F9EA0]/skills load[/bold #5F9EA0] <name>", "Load a skill into this chat")
|
|
163
|
-
table.add_row("[bold #5F9EA0]/skills remove[/bold #5F9EA0] <name>", "Delete a skill")
|
|
164
|
-
table.add_row("[bold #5F9EA0]/skills dir[/bold #5F9EA0]", "Show the skills directory")
|
|
165
|
-
|
|
166
|
-
console.print(Panel(table, title="[bold #5F9EA0]Skills[/bold #5F9EA0]", border_style="grey23", padding=(0, 2)))
|
|
167
|
-
console.print("")
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def show_config_overview(chat_manager, console, debug_mode_container, current_provider):
|
|
171
|
-
"""Display comprehensive configuration overview.
|
|
172
|
-
|
|
173
|
-
Args:
|
|
174
|
-
chat_manager: ChatManager instance for runtime state
|
|
175
|
-
console: Rich Console instance for output
|
|
176
|
-
debug_mode_container: Dict with debug key for debug mode state
|
|
177
|
-
current_provider: Name of the currently active provider
|
|
178
|
-
"""
|
|
179
|
-
from core.config_manager import ConfigManager
|
|
180
|
-
config_manager = ConfigManager()
|
|
181
|
-
config_data = config_manager.load()
|
|
182
|
-
|
|
183
|
-
console.print()
|
|
184
|
-
|
|
185
|
-
# ===== Runtime Settings =====
|
|
186
|
-
runtime_table = Table("Setting", "Status", title="Runtime Settings", box=box.SIMPLE_HEAD)
|
|
187
|
-
debug_status = "[green]ON[/green]" if debug_mode_container.get("debug") else "[dim]OFF[/dim]"
|
|
188
|
-
runtime_table.add_row("Debug Mode", debug_status)
|
|
189
|
-
logging_status = "[green]ON[/green]" if chat_manager.markdown_logger else "[dim]OFF[/dim]"
|
|
190
|
-
runtime_table.add_row("Conversation Logging", logging_status)
|
|
191
|
-
approve_labels = {"safe": "SAFE", "accept_edits": "ACCEPT EDITS", "danger": "DANGER"}
|
|
192
|
-
approve_colors = {"safe": "green", "accept_edits": "yellow", "danger": "red"}
|
|
193
|
-
approve_mode = chat_manager.approve_mode
|
|
194
|
-
approve_color = approve_colors.get(approve_mode, "white")
|
|
195
|
-
runtime_table.add_row("Approval Mode", f"[{approve_color}]{approve_labels.get(approve_mode, approve_mode.upper())}[/{approve_color}]")
|
|
196
|
-
console.print(runtime_table)
|
|
197
|
-
|
|
198
|
-
# ===== Provider Settings =====
|
|
199
|
-
console.print()
|
|
200
|
-
provider_table = Table("Provider", "Model", "$ in/out", "API Key", title="Providers", box=box.SIMPLE_HEAD)
|
|
201
|
-
|
|
202
|
-
active_provider = config_data.get("LAST_PROVIDER", "Not set").upper()
|
|
203
|
-
provider_table.add_row("[green]Active[/green]", f"[green]{active_provider}[/green]", "", "")
|
|
204
|
-
|
|
205
|
-
def fmt(v, max_len=35):
|
|
206
|
-
return v[:max_len-3] + "..." if len(v) > max_len else v
|
|
207
|
-
|
|
208
|
-
# Local provider
|
|
209
|
-
local_model = config_data.get("LOCAL_MODEL_PATH", "Not set")
|
|
210
|
-
provider_table.add_row("Local", fmt(local_model), "N/A", "N/A")
|
|
211
|
-
|
|
212
|
-
# API providers
|
|
213
|
-
for provider in ["OpenRouter", "GLM", "OpenAI", "Gemini", "MiniMax", "Anthropic", "Kimi"]:
|
|
214
|
-
model = config_data.get(f"{provider.upper()}_MODEL", "Not set")
|
|
215
|
-
key = config_data.get(f"{provider.upper()}_API_KEY", "")
|
|
216
|
-
key_status = "[green]✓[/green]" if key else "[red]✗[/red]"
|
|
217
|
-
|
|
218
|
-
# Check for model-specific pricing
|
|
219
|
-
model_prices = config_data.get("MODEL_PRICES", {})
|
|
220
|
-
if model and model in model_prices:
|
|
221
|
-
cost_in = model_prices[model].get("cost_in", 0)
|
|
222
|
-
cost_out = model_prices[model].get("cost_out", 0)
|
|
223
|
-
if cost_in > 0 or cost_out > 0:
|
|
224
|
-
cost_str = f"${cost_in:.2f}/${cost_out:.2f}"
|
|
225
|
-
else:
|
|
226
|
-
cost_str = "Not set"
|
|
227
|
-
else:
|
|
228
|
-
cost_str = "Not set"
|
|
229
|
-
|
|
230
|
-
provider_table.add_row(provider, fmt(model), cost_str, key_status)
|
|
231
|
-
|
|
232
|
-
console.print(provider_table)
|
|
233
|
-
|
|
234
|
-
# ===== Quick Commands Reference =====
|
|
235
|
-
console.print()
|
|
236
|
-
help_text = """[bold #5F9EA0]Commands:[/bold #5F9EA0] [bold #5F9EA0]/provider[/bold #5F9EA0] <name> [bold #5F9EA0]/model[/bold #5F9EA0] <path> [bold #5F9EA0]/key[/bold #5F9EA0] <key>
|
|
237
|
-
[#5F9EA0] :[/#5F9EA0] [bold #5F9EA0]/usage[/bold #5F9EA0] [provider] [in|out] <$> [bold #5F9EA0]/config[/bold #5F9EA0]"""
|
|
238
|
-
console.print(Panel(help_text, title="[#5F9EA0]Quick Reference[/#5F9EA0]"))
|
|
239
|
-
console.print()
|
package/src/ui/loader.py
DELETED
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
"""Animated terminal effects for bone-agent — loading spinners, progress bars, and visual flair."""
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
import random
|
|
5
|
-
import threading
|
|
6
|
-
from rich.console import Console
|
|
7
|
-
from rich.text import Text
|
|
8
|
-
from rich.live import Live
|
|
9
|
-
from rich.layout import Layout
|
|
10
|
-
from rich.panel import Panel
|
|
11
|
-
from rich.table import Table
|
|
12
|
-
from rich.align import Align
|
|
13
|
-
|
|
14
|
-
console = Console()
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# ── ASCII art logo ──────────────────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
BONE_AGENT_LOGO = r"""
|
|
20
|
-
╦ ╦┌─┐┌┐ ╔╦╗┬┬ ┌─┐
|
|
21
|
-
║║║├┤ ├┴┐ ║ ││ ├┤
|
|
22
|
-
╚╩╝└─┘└─┘ ╩ ┴┴─┘└─┘
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
26
|
-
DOT_FRAMES = [" ", ". ", ".. ", "..."]
|
|
27
|
-
WAVE_FRAMES = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"]
|
|
28
|
-
BAR_FRAMES = ["◐", "◓", "◑", "◒"]
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def _wave_string(text: str, frame_idx: int, color: str = "#5F9EA0") -> Text:
|
|
32
|
-
"""Create a wave-animated string where each character oscillates vertically."""
|
|
33
|
-
result = Text()
|
|
34
|
-
n = len(WAVE_FRAMES)
|
|
35
|
-
for i, ch in enumerate(text):
|
|
36
|
-
offset = (frame_idx + i * 2) % n
|
|
37
|
-
bar_char = WAVE_FRAMES[offset]
|
|
38
|
-
# Fade intensity based on wave position (center = bright)
|
|
39
|
-
intensity = abs(offset - n // 2) / (n // 2)
|
|
40
|
-
if ch == " ":
|
|
41
|
-
result.append(" ")
|
|
42
|
-
else:
|
|
43
|
-
result.append(bar_char, style=color)
|
|
44
|
-
return result
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def display_intro_animation(provider: str = "", model: str = ""):
|
|
48
|
-
"""Play a cinematic intro animation on startup.
|
|
49
|
-
|
|
50
|
-
Shows the bone-agent logo with a typing effect, a wave animation underneath,
|
|
51
|
-
and provider/model info fading in.
|
|
52
|
-
"""
|
|
53
|
-
logo_lines = BONE_AGENT_LOGO.strip("\n").split("\n")
|
|
54
|
-
|
|
55
|
-
try:
|
|
56
|
-
with Live(console=console, transient=False, refresh_per_second=24) as live:
|
|
57
|
-
# Phase 1: Logo reveal (typewriter effect)
|
|
58
|
-
revealed_lines = []
|
|
59
|
-
for line_idx, line in enumerate(logo_lines):
|
|
60
|
-
revealed_lines.append("")
|
|
61
|
-
for ch_idx, ch in enumerate(line):
|
|
62
|
-
revealed_lines[line_idx] = line[: ch_idx + 1]
|
|
63
|
-
layout = Layout()
|
|
64
|
-
logo_text = Text("\n".join(revealed_lines), style="bold #5F9EA0")
|
|
65
|
-
layout.update(Align.center(Panel(
|
|
66
|
-
logo_text,
|
|
67
|
-
border_style="grey30",
|
|
68
|
-
padding=(1, 4),
|
|
69
|
-
subtitle=Text(" ", style="dim"),
|
|
70
|
-
)))
|
|
71
|
-
live.update(layout)
|
|
72
|
-
time.sleep(0.008)
|
|
73
|
-
time.sleep(0.04)
|
|
74
|
-
|
|
75
|
-
# Phase 2: Wave animation beneath the logo (runs for ~2 seconds)
|
|
76
|
-
wave_line = "━" * 42
|
|
77
|
-
start = time.time()
|
|
78
|
-
frame = 0
|
|
79
|
-
while time.time() - start < 1.8:
|
|
80
|
-
wave = _wave_string(wave_line, frame, color="#3a7ca5")
|
|
81
|
-
layout = Layout()
|
|
82
|
-
full = Text()
|
|
83
|
-
full.append("\n".join(logo_lines) + "\n", style="bold #5F9EA0")
|
|
84
|
-
full.append(wave)
|
|
85
|
-
layout.update(Align.center(Panel(
|
|
86
|
-
full,
|
|
87
|
-
border_style="grey30",
|
|
88
|
-
padding=(1, 4),
|
|
89
|
-
)))
|
|
90
|
-
live.update(layout)
|
|
91
|
-
frame += 1
|
|
92
|
-
time.sleep(0.05)
|
|
93
|
-
|
|
94
|
-
# Phase 3: Show tagline + provider info
|
|
95
|
-
tagline = Text(" local-first · agent-powered · terminal-native", style="dim grey60")
|
|
96
|
-
if provider and model:
|
|
97
|
-
info = Text.assemble(
|
|
98
|
-
(" ", ""),
|
|
99
|
-
(f"● {provider.upper()} ", "bold #5F9EA0"),
|
|
100
|
-
(f"{model}", "grey70"),
|
|
101
|
-
style="",
|
|
102
|
-
)
|
|
103
|
-
else:
|
|
104
|
-
info = Text("")
|
|
105
|
-
|
|
106
|
-
layout = Layout()
|
|
107
|
-
full = Text()
|
|
108
|
-
full.append("\n".join(logo_lines), style="bold #5F9EA0")
|
|
109
|
-
full.append("\n")
|
|
110
|
-
full.append(wave_line, style="#3a7ca5")
|
|
111
|
-
layout.update(Align.center(Panel(
|
|
112
|
-
Align.center(
|
|
113
|
-
Table.grid(padding=(0, 0)),
|
|
114
|
-
),
|
|
115
|
-
border_style="grey30",
|
|
116
|
-
padding=(1, 4),
|
|
117
|
-
subtitle=tagline,
|
|
118
|
-
)))
|
|
119
|
-
live.update(layout)
|
|
120
|
-
time.sleep(0.6)
|
|
121
|
-
|
|
122
|
-
# Final frame — static logo with tagline
|
|
123
|
-
final = Text()
|
|
124
|
-
final.append("\n".join(logo_lines), style="bold #5F9EA0")
|
|
125
|
-
final.append("\n")
|
|
126
|
-
final.append(wave_line, style="#3a7ca5")
|
|
127
|
-
layout.update(Align.center(Panel(
|
|
128
|
-
final,
|
|
129
|
-
border_style="grey30",
|
|
130
|
-
padding=(1, 4),
|
|
131
|
-
subtitle=tagline,
|
|
132
|
-
)))
|
|
133
|
-
live.update(layout)
|
|
134
|
-
time.sleep(0.3)
|
|
135
|
-
except Exception:
|
|
136
|
-
# Fallback: if Live fails (e.g. non-TTY), just print static logo
|
|
137
|
-
console.print(Panel(
|
|
138
|
-
Text(BONE_AGENT_LOGO.strip("\n"), style="bold #5F9EA0"),
|
|
139
|
-
border_style="grey30",
|
|
140
|
-
subtitle=" local-first · agent-powered · terminal-native",
|
|
141
|
-
))
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
# ── Spinner context manager ─────────────────────────────────────────────────
|
|
145
|
-
|
|
146
|
-
class Spinner:
|
|
147
|
-
"""A rich-based status spinner for long-running operations.
|
|
148
|
-
|
|
149
|
-
Usage:
|
|
150
|
-
with Spinner("Indexing files..."):
|
|
151
|
-
do_expensive_work()
|
|
152
|
-
"""
|
|
153
|
-
|
|
154
|
-
def __init__(self, message: str, style: str = "#5F9EA0", done_message: str = "done"):
|
|
155
|
-
self.message = message
|
|
156
|
-
self.style = style
|
|
157
|
-
self.done_message = done_message
|
|
158
|
-
self._stop = threading.Event()
|
|
159
|
-
self._thread = None
|
|
160
|
-
|
|
161
|
-
def _spin(self):
|
|
162
|
-
frame = 0
|
|
163
|
-
n = len(SPINNER_FRAMES)
|
|
164
|
-
with Live(console=console, transient=True, refresh_per_second=12) as live:
|
|
165
|
-
while not self._stop.is_set():
|
|
166
|
-
spinner = SPINNER_FRAMES[frame % n]
|
|
167
|
-
live.update(Text(f" {spinner} {self.message}", style=self.style))
|
|
168
|
-
frame += 1
|
|
169
|
-
self._stop.wait(0.08)
|
|
170
|
-
|
|
171
|
-
def __enter__(self):
|
|
172
|
-
self._stop.clear()
|
|
173
|
-
self._thread = threading.Thread(target=self._spin, daemon=True)
|
|
174
|
-
self._thread.start()
|
|
175
|
-
return self
|
|
176
|
-
|
|
177
|
-
def __exit__(self, *args):
|
|
178
|
-
self._stop.set()
|
|
179
|
-
if self._thread:
|
|
180
|
-
self._thread.join(timeout=1)
|
|
181
|
-
console.print(f" ✓ {self.message} {self.done_message}", style="dim green")
|
|
182
|
-
|
|
183
|
-
def ok(self, msg: str = ""):
|
|
184
|
-
"""Mark as done with a custom message."""
|
|
185
|
-
self.done_message = msg
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
# ── Progress bar ────────────────────────────────────────────────────────────
|
|
189
|
-
|
|
190
|
-
class ProgressBar:
|
|
191
|
-
"""A lightweight animated progress bar for terminal output.
|
|
192
|
-
|
|
193
|
-
Usage:
|
|
194
|
-
with ProgressBar("Loading", total=100) as bar:
|
|
195
|
-
for i in range(100):
|
|
196
|
-
bar.update(i + 1)
|
|
197
|
-
"""
|
|
198
|
-
|
|
199
|
-
def __init__(self, label: str = "", total: int = 100, width: int = 30,
|
|
200
|
-
fill: str = "█", empty: str = "░", color: str = "#5F9EA0"):
|
|
201
|
-
self.label = label
|
|
202
|
-
self.total = total
|
|
203
|
-
self.width = width
|
|
204
|
-
self.fill = fill
|
|
205
|
-
self.empty = empty
|
|
206
|
-
self.color = color
|
|
207
|
-
self.current = 0
|
|
208
|
-
self._stop = threading.Event()
|
|
209
|
-
|
|
210
|
-
def update(self, value: int):
|
|
211
|
-
self.current = min(value, self.total)
|
|
212
|
-
|
|
213
|
-
def _render(self) -> Text:
|
|
214
|
-
pct = self.current / self.total if self.total else 0
|
|
215
|
-
filled = int(self.width * pct)
|
|
216
|
-
bar = self.fill * filled + self.empty * (self.width - filled)
|
|
217
|
-
result = Text()
|
|
218
|
-
if self.label:
|
|
219
|
-
result.append(f" {self.label} ", style="dim")
|
|
220
|
-
result.append(bar, style=self.color)
|
|
221
|
-
result.append(f" {pct:>5.1%}", style="dim grey70")
|
|
222
|
-
return result
|
|
223
|
-
|
|
224
|
-
def _animate(self):
|
|
225
|
-
with Live(console=console, transient=True, refresh_per_second=8) as live:
|
|
226
|
-
while not self._stop.is_set():
|
|
227
|
-
live.update(self._render())
|
|
228
|
-
self._stop.wait(0.1)
|
|
229
|
-
|
|
230
|
-
def __enter__(self):
|
|
231
|
-
self._stop.clear()
|
|
232
|
-
self._thread = threading.Thread(target=self._animate, daemon=True)
|
|
233
|
-
self._thread.start()
|
|
234
|
-
return self
|
|
235
|
-
|
|
236
|
-
def __exit__(self, *args):
|
|
237
|
-
self._stop.set()
|
|
238
|
-
if self._thread:
|
|
239
|
-
self._thread.join(timeout=1)
|
|
240
|
-
# Print final state
|
|
241
|
-
console.print(self._render())
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
# ── Scan-line / matrix effect (decorative) ─────────────────────────────────
|
|
245
|
-
|
|
246
|
-
def matrix_rain(duration: float = 1.5, cols: int = 60, rows: int = 8, chars: str = "01アイウエオカキクケコ"):
|
|
247
|
-
"""Print a brief Matrix-style rain effect to the terminal.
|
|
248
|
-
|
|
249
|
-
Purely decorative — great for transitions between sections.
|
|
250
|
-
"""
|
|
251
|
-
random.seed()
|
|
252
|
-
try:
|
|
253
|
-
with Live(console=console, transient=True, refresh_per_second=16) as live:
|
|
254
|
-
start = time.time()
|
|
255
|
-
# Each column has a falling "drop" at a random row
|
|
256
|
-
drops = [random.randint(0, rows - 1) for _ in range(cols)]
|
|
257
|
-
speeds = [random.uniform(0.3, 1.0) for _ in range(cols)]
|
|
258
|
-
phases = [random.random() * duration for _ in range(cols)]
|
|
259
|
-
|
|
260
|
-
while time.time() - start < duration:
|
|
261
|
-
grid = []
|
|
262
|
-
t = time.time() - start
|
|
263
|
-
for r in range(rows):
|
|
264
|
-
row = Text()
|
|
265
|
-
for c in range(cols):
|
|
266
|
-
# Determine if this cell is "active"
|
|
267
|
-
drop_pos = drops[c] + int((t - phases[c]) * speeds[c] * rows / duration)
|
|
268
|
-
drop_pos = drop_pos % (rows + 4) # wrap around
|
|
269
|
-
dist = drop_pos - r
|
|
270
|
-
if 0 <= dist <= 3:
|
|
271
|
-
ch = random.choice(chars)
|
|
272
|
-
if dist == 0:
|
|
273
|
-
row.append(ch, style="bold white")
|
|
274
|
-
else:
|
|
275
|
-
fade = f"#{max(0, 0x10):02x}{max(0, 0x40 + (3 - dist) * 0x20):02x}{max(0, 0x10):02x}"
|
|
276
|
-
row.append(ch, style=fade)
|
|
277
|
-
else:
|
|
278
|
-
row.append(" ")
|
|
279
|
-
grid.append(row)
|
|
280
|
-
|
|
281
|
-
live.update(Text("\n").join(grid))
|
|
282
|
-
time.sleep(0.04)
|
|
283
|
-
except Exception:
|
|
284
|
-
pass # silently skip if terminal doesn't support it
|