@pheem49/mint 1.8.2 โ 1.10.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/Cargo.lock +55 -3
- package/Cargo.toml +2 -1
- package/Release_Note.md +297 -107
- package/bin/mint +0 -0
- package/crates/mint-cli/Cargo.toml +1 -0
- package/crates/mint-cli/src/actions.rs +233 -0
- package/crates/mint-cli/src/agent.rs +382 -127
- package/crates/mint-cli/src/gmail.rs +22 -2
- package/crates/mint-cli/src/image.rs +8 -8
- package/crates/mint-cli/src/interactive.rs +2888 -0
- package/crates/mint-cli/src/main.rs +391 -2506
- package/crates/mint-cli/src/markdown.rs +294 -0
- package/crates/mint-cli/src/onboard.rs +152 -109
- package/crates/mint-cli/src/setup.rs +143 -143
- package/crates/mint-cli/src/updater.rs +48 -6
- package/crates/mint-core/Cargo.toml +1 -0
- package/crates/mint-core/src/agent_loop.rs +1 -0
- package/crates/mint-core/src/api_server.rs +431 -65
- package/crates/mint-core/src/browser.rs +630 -102
- package/crates/mint-core/src/channels.rs +105 -6
- package/crates/mint-core/src/chat.rs +174 -11
- package/crates/mint-core/src/code_tools.rs +47 -31
- package/crates/mint-core/src/config.rs +302 -17
- package/crates/mint-core/src/image_gen.rs +299 -38
- package/crates/mint-core/src/lib.rs +10 -7
- package/crates/mint-core/src/mcp.rs +119 -6
- package/crates/mint-core/src/memory.rs +6 -1
- package/crates/mint-core/src/orchestration.rs +237 -96
- package/crates/mint-core/src/pictures.rs +67 -5
- package/crates/mint-core/src/semantic.rs +1 -1
- package/crates/mint-core/src/shell.rs +1 -1
- package/crates/mint-core/src/skills.rs +128 -25
- package/crates/mint-core/src/video_gen.rs +235 -0
- package/package.json +8 -2
- package/shared-modules/linux-audio/fluidsynth2-static.json +9 -3
- package/shared-modules/linux-audio/fluidsynth2.json +8 -2
- package/shared-modules/linux-audio/lilv.json +26 -19
- package/shared-modules/linux-audio/lv2.json +0 -1
- package/shared-modules/luajit/luajit.json +2 -2
- package/skills-lock.json +41 -0
- package/src/renderer/index-web.html +1 -1
- package/src/renderer/index.html +1 -1
- package/src/renderer/shared/components/AgentActivityTable.tsx +25 -6
- package/src/renderer/shared/constants/slashCommands.ts +27 -0
- package/src/renderer/shared/platform.ts +3 -0
- package/src/renderer/shared/types.ts +4 -0
- package/src/renderer/shared/utils/approval.tsx +86 -7
- package/src/renderer/shared/utils/fileIcons.ts +135 -0
- package/src/renderer/shared/utils/markdown.tsx +217 -88
- package/src/renderer/shared/utils/slashCommandProcessor.ts +239 -0
- package/src/renderer/src/components/ChatPanel.tsx +486 -69
- package/src/renderer/src/components/DashboardSidebar.tsx +11 -2
- package/src/renderer/src/components/ImageStudioPanel.tsx +87 -4
- package/src/renderer/src/components/MintDashboard.tsx +195 -7
- package/src/renderer/src/components/PicturesLibrary.tsx +156 -15
- package/src/renderer/src/components/Settings/AgentsTab.tsx +21 -2
- package/src/renderer/src/components/Settings/AutomationTab.tsx +316 -18
- package/src/renderer/src/components/Settings/GeneralTab.tsx +795 -413
- package/src/renderer/src/components/Settings/PluginsTab.tsx +304 -70
- package/src/renderer/src/components/Settings/ThemeTab.tsx +6 -1
- package/src/renderer/src/components/SettingsWindow.tsx +106 -3
- package/src/renderer/src/components/VeoStudioPanel.tsx +549 -0
- package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
- package/src/renderer/src/css/image-studio.css +106 -0
- package/src/renderer/src/css/settings.css +158 -0
- package/src/renderer/src/css/styles.css +368 -1
- package/src/renderer/src/css/veo-studio.css +626 -0
- package/src/renderer/src/index.css +1 -0
- package/src/renderer/src/tauri.ts +144 -17
- package/src/renderer/src-web/components/ChatPanel.tsx +385 -93
- package/src/renderer/src-web/components/DashboardSidebar.tsx +16 -1
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +87 -4
- package/src/renderer/src-web/components/MintDashboard.tsx +156 -5
- package/src/renderer/src-web/components/PicturesLibrary.tsx +155 -15
- package/src/renderer/src-web/components/Settings/AgentsTab.tsx +21 -2
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +316 -18
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +795 -411
- package/src/renderer/src-web/components/Settings/PluginsTab.tsx +304 -70
- package/src/renderer/src-web/components/Settings/ThemeTab.tsx +6 -1
- package/src/renderer/src-web/components/SettingsWindow.tsx +110 -3
- package/src/renderer/src-web/components/VeoStudioPanel.tsx +549 -0
- package/src/renderer/src-web/css/image-studio.css +106 -0
- package/src/renderer/src-web/css/settings.css +158 -0
- package/src/renderer/src-web/css/styles.css +369 -1
- package/src/renderer/src-web/css/veo-studio.css +650 -0
- package/src/renderer/src-web/index.css +1 -0
- package/src/renderer/src-web/tauri.ts +135 -16
- package/src-tauri/src/desktop.rs +18 -16
- package/src-tauri/src/discord_rpc.rs +1 -1
- package/src-tauri/src/lib.rs +155 -25
- package/src-tauri/src/webhooks.rs +45 -31
- package/src-tauri/src/workflows.rs +1 -0
- package/src-tauri/tauri.conf.json +1 -1
- package/temp_gridgeist/AGENTS.md +8 -0
- package/temp_gridgeist/CHANGELOG.md +130 -0
- package/temp_gridgeist/LICENSE +21 -0
- package/temp_gridgeist/README.md +323 -0
- package/temp_gridgeist/README.th.md +325 -0
- package/temp_gridgeist/skills/gridgeist/SKILL.md +88 -0
- package/temp_gridgeist/skills/gridgeist/agents/openai.yaml +9 -0
- package/temp_gridgeist/skills/gridgeist/assets/gridgeist-small.svg +10 -0
- package/temp_gridgeist/skills/gridgeist/assets/gridgeist.png +0 -0
- package/temp_gridgeist/skills/gridgeist/assets/gridgeist.svg +18 -0
- package/temp_gridgeist/skills/gridgeist/references/design-language.md +88 -0
- package/temp_gridgeist/skills/gridgeist/references/review-checklist.md +128 -0
- package/temp_gridgeist/skills/gridgeist/references/system-contract.md +109 -0
|
@@ -14,6 +14,8 @@ use mint_core::{
|
|
|
14
14
|
|
|
15
15
|
const RESET: &str = "\x1b[0m";
|
|
16
16
|
const MINT: &str = "\x1b[32m";
|
|
17
|
+
const GREEN: &str = "\x1b[32m";
|
|
18
|
+
const RED: &str = "\x1b[31m";
|
|
17
19
|
const BLUE: &str = "\x1b[38;2;78;201;216m";
|
|
18
20
|
const DIM: &str = "\x1b[90m";
|
|
19
21
|
const BRIGHT: &str = "\x1b[1;97m";
|
|
@@ -24,7 +26,7 @@ pub struct AgentOptions {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
pub async fn run_code_agent(task: &str, root: &Path, config: &MintConfig) -> Result<AgentResult> {
|
|
27
|
-
run_code_agent_with_image(task, root, config, None).await
|
|
29
|
+
run_code_agent_with_image(task, root, config, None, None).await
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
pub async fn run_code_agent_with_image(
|
|
@@ -32,8 +34,17 @@ pub async fn run_code_agent_with_image(
|
|
|
32
34
|
root: &Path,
|
|
33
35
|
config: &MintConfig,
|
|
34
36
|
image_data_uri: Option<String>,
|
|
37
|
+
video_data_uri: Option<String>,
|
|
35
38
|
) -> Result<AgentResult> {
|
|
36
|
-
run_code_agent_with_options(
|
|
39
|
+
run_code_agent_with_options(
|
|
40
|
+
task,
|
|
41
|
+
root,
|
|
42
|
+
config,
|
|
43
|
+
image_data_uri,
|
|
44
|
+
video_data_uri,
|
|
45
|
+
AgentOptions::default(),
|
|
46
|
+
)
|
|
47
|
+
.await
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
pub async fn run_code_agent_with_options(
|
|
@@ -41,6 +52,7 @@ pub async fn run_code_agent_with_options(
|
|
|
41
52
|
root: &Path,
|
|
42
53
|
config: &MintConfig,
|
|
43
54
|
image_data_uri: Option<String>,
|
|
55
|
+
video_data_uri: Option<String>,
|
|
44
56
|
options: AgentOptions,
|
|
45
57
|
) -> Result<AgentResult> {
|
|
46
58
|
let started_at = Instant::now();
|
|
@@ -65,30 +77,63 @@ pub async fn run_code_agent_with_options(
|
|
|
65
77
|
let _guard = ApprovalGuard(Arc::clone(&approve_approval_active));
|
|
66
78
|
|
|
67
79
|
match approval {
|
|
68
|
-
AgentApproval::WriteFile { diff, .. } => {
|
|
69
|
-
|
|
80
|
+
AgentApproval::WriteFile { path, diff, .. } => {
|
|
81
|
+
let (additions, deletions) = diff_stats(diff);
|
|
82
|
+
let colored_stats = format!(
|
|
83
|
+
"{DIM}({RESET}{GREEN}+{}{RESET} {RED}-{}{RESET}{DIM}){RESET}",
|
|
84
|
+
additions, deletions
|
|
85
|
+
);
|
|
86
|
+
let target_str = format!("{} {}", path, colored_stats);
|
|
87
|
+
print_approval_card(
|
|
88
|
+
"File Creation / Write",
|
|
89
|
+
&[("Target", &target_str), ("Action", "Write File")],
|
|
90
|
+
);
|
|
91
|
+
println!(
|
|
92
|
+
" {DIM}Proposed edit for {RESET}{BRIGHT}{}{RESET} {}:",
|
|
93
|
+
path, colored_stats
|
|
94
|
+
);
|
|
70
95
|
print_colored_diff(diff);
|
|
71
|
-
if confirm_pausing_interrupt(
|
|
96
|
+
if confirm_pausing_interrupt(
|
|
97
|
+
&format!("Approve writing file '{}'?", path),
|
|
98
|
+
&approve_approval_active,
|
|
99
|
+
) {
|
|
72
100
|
Ok(ApprovalOutcome::Approved)
|
|
73
101
|
} else {
|
|
74
102
|
Ok(ApprovalOutcome::Denied)
|
|
75
103
|
}
|
|
76
104
|
}
|
|
77
|
-
AgentApproval::ApplyPatch { diff, .. } => {
|
|
78
|
-
|
|
105
|
+
AgentApproval::ApplyPatch { path, diff, .. } => {
|
|
106
|
+
let (additions, deletions) = diff_stats(diff);
|
|
107
|
+
let colored_stats = format!(
|
|
108
|
+
"{DIM}({RESET}{GREEN}+{}{RESET} {RED}-{}{RESET}{DIM}){RESET}",
|
|
109
|
+
additions, deletions
|
|
110
|
+
);
|
|
111
|
+
let target_str = format!("{} {}", path, colored_stats);
|
|
112
|
+
print_approval_card(
|
|
113
|
+
"File Modification",
|
|
114
|
+
&[("Target", &target_str), ("Action", "Apply Patch")],
|
|
115
|
+
);
|
|
116
|
+
println!(
|
|
117
|
+
" {DIM}Proposed edit for {RESET}{BRIGHT}{}{RESET} {}:",
|
|
118
|
+
path, colored_stats
|
|
119
|
+
);
|
|
79
120
|
print_colored_diff(diff);
|
|
80
|
-
if confirm_pausing_interrupt(
|
|
121
|
+
if confirm_pausing_interrupt(
|
|
122
|
+
&format!("Approve patching file '{}'?", path),
|
|
123
|
+
&approve_approval_active,
|
|
124
|
+
) {
|
|
81
125
|
Ok(ApprovalOutcome::Approved)
|
|
82
126
|
} else {
|
|
83
127
|
Ok(ApprovalOutcome::Denied)
|
|
84
128
|
}
|
|
85
129
|
}
|
|
86
130
|
AgentApproval::RunShell { command, mode } => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
131
|
+
print_approval_card(
|
|
132
|
+
"Local Shell Command",
|
|
133
|
+
&[("Command", command), ("Mode", mode)],
|
|
134
|
+
);
|
|
90
135
|
if confirm_pausing_interrupt(
|
|
91
|
-
"Approve
|
|
136
|
+
"Approve running shell command?",
|
|
92
137
|
&approve_approval_active,
|
|
93
138
|
) {
|
|
94
139
|
Ok(ApprovalOutcome::Approved)
|
|
@@ -97,21 +142,21 @@ pub async fn run_code_agent_with_options(
|
|
|
97
142
|
}
|
|
98
143
|
}
|
|
99
144
|
AgentApproval::NoteWrite { path, .. } => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
"Approve writing this note? [y/N]",
|
|
104
|
-
&approve_approval_active,
|
|
105
|
-
) {
|
|
145
|
+
print_approval_card("Note Creation", &[("Path", path)]);
|
|
146
|
+
if confirm_pausing_interrupt("Approve writing this note?", &approve_approval_active)
|
|
147
|
+
{
|
|
106
148
|
Ok(ApprovalOutcome::Approved)
|
|
107
149
|
} else {
|
|
108
150
|
Ok(ApprovalOutcome::Denied)
|
|
109
151
|
}
|
|
110
152
|
}
|
|
111
153
|
AgentApproval::RunPlugin { name, instruction } => {
|
|
112
|
-
|
|
154
|
+
print_approval_card(
|
|
155
|
+
"Plugin Execution",
|
|
156
|
+
&[("Plugin", name), ("Detail", instruction)],
|
|
157
|
+
);
|
|
113
158
|
if confirm_pausing_interrupt(
|
|
114
|
-
&format!("Approve running plugin '{}'?
|
|
159
|
+
&format!("Approve running plugin '{}'?", name),
|
|
115
160
|
&approve_approval_active,
|
|
116
161
|
) {
|
|
117
162
|
Ok(ApprovalOutcome::Approved)
|
|
@@ -119,34 +164,38 @@ pub async fn run_code_agent_with_options(
|
|
|
119
164
|
Ok(ApprovalOutcome::Denied)
|
|
120
165
|
}
|
|
121
166
|
}
|
|
122
|
-
AgentApproval::McpTool {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
167
|
+
AgentApproval::McpTool {
|
|
168
|
+
server,
|
|
169
|
+
tool,
|
|
170
|
+
arguments,
|
|
171
|
+
} => {
|
|
172
|
+
let mut fields = vec![("Server", server.as_str()), ("Tool", tool.as_str())];
|
|
173
|
+
let formatted_args = arguments.to_string();
|
|
174
|
+
if !formatted_args.is_empty() && formatted_args != "{}" && formatted_args != "null"
|
|
175
|
+
{
|
|
176
|
+
fields.push(("Arguments", &formatted_args));
|
|
177
|
+
}
|
|
178
|
+
print_approval_card("MCP Tool Call", &fields);
|
|
179
|
+
if confirm_pausing_interrupt("Approve MCP tool call?", &approve_approval_active) {
|
|
129
180
|
Ok(ApprovalOutcome::Approved)
|
|
130
181
|
} else {
|
|
131
182
|
Ok(ApprovalOutcome::Denied)
|
|
132
183
|
}
|
|
133
184
|
}
|
|
134
185
|
AgentApproval::UserApproval { title, prompt } => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
) {
|
|
186
|
+
print_approval_card(
|
|
187
|
+
"Security Authorization",
|
|
188
|
+
&[("Title", title), ("Detail", prompt)],
|
|
189
|
+
);
|
|
190
|
+
if confirm_pausing_interrupt("Approve this request?", &approve_approval_active) {
|
|
141
191
|
Ok(ApprovalOutcome::Approved)
|
|
142
192
|
} else {
|
|
143
193
|
Ok(ApprovalOutcome::Denied)
|
|
144
194
|
}
|
|
145
195
|
}
|
|
146
196
|
AgentApproval::AskUser { question } => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
print!("Answer (leave empty to decline): ");
|
|
197
|
+
print_approval_card("Agent Question", &[("Question", question)]);
|
|
198
|
+
print!(" Answer (leave empty to decline): ");
|
|
150
199
|
let _ = std::io::Write::flush(&mut std::io::stdout());
|
|
151
200
|
let mut answer = String::new();
|
|
152
201
|
match std::io::stdin().read_line(&mut answer) {
|
|
@@ -189,39 +238,47 @@ pub async fn run_code_agent_with_options(
|
|
|
189
238
|
agent_name,
|
|
190
239
|
model_name,
|
|
191
240
|
} => {
|
|
192
|
-
if !options.fast_mode
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
241
|
+
if !options.fast_mode
|
|
242
|
+
&& let Ok(mut status) = progress_live_status.lock()
|
|
243
|
+
{
|
|
244
|
+
let label = if let (Some(a), Some(m)) = (agent_name, model_name) {
|
|
245
|
+
format!(
|
|
246
|
+
"{} ({}) is thinking ({} โข Esc to interrupt)",
|
|
247
|
+
a,
|
|
248
|
+
m,
|
|
249
|
+
format_elapsed(Duration::from_secs(elapsed_secs))
|
|
250
|
+
)
|
|
251
|
+
} else {
|
|
252
|
+
format!(
|
|
253
|
+
"Thinking ({} โข Esc to interrupt)",
|
|
254
|
+
format_elapsed(Duration::from_secs(elapsed_secs))
|
|
255
|
+
)
|
|
256
|
+
};
|
|
257
|
+
status.thinking = Some(label);
|
|
258
|
+
render_live_status(&mut status);
|
|
202
259
|
}
|
|
203
260
|
}
|
|
204
261
|
AgentProgress::Thought { thought } => {
|
|
205
|
-
if !options.fast_mode
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
262
|
+
if !options.fast_mode
|
|
263
|
+
&& let Ok(mut status) = progress_live_status.lock()
|
|
264
|
+
{
|
|
265
|
+
commit_activity_snapshot(&mut status);
|
|
266
|
+
print_timeline_note(&thought);
|
|
267
|
+
status.thinking = None;
|
|
268
|
+
render_live_status(&mut status);
|
|
212
269
|
}
|
|
213
270
|
}
|
|
214
271
|
AgentProgress::ToolStart { action, input } => {
|
|
215
272
|
if !options.fast_mode {
|
|
216
|
-
if action == "create_plan" || action == "update_plan"
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return;
|
|
273
|
+
if (action == "create_plan" || action == "update_plan")
|
|
274
|
+
&& let Some(steps) = extract_plan_steps(&input)
|
|
275
|
+
{
|
|
276
|
+
if let Ok(mut status) = progress_live_status.lock() {
|
|
277
|
+
status.thinking = None;
|
|
278
|
+
status.plan_steps = steps;
|
|
279
|
+
render_live_status(&mut status);
|
|
224
280
|
}
|
|
281
|
+
return;
|
|
225
282
|
}
|
|
226
283
|
if let Some(label) = explored_action_label(&action, &input) {
|
|
227
284
|
if let Ok(mut status) = progress_live_status.lock() {
|
|
@@ -313,12 +370,12 @@ pub async fn run_code_agent_with_options(
|
|
|
313
370
|
} => {
|
|
314
371
|
if !options.fast_mode {
|
|
315
372
|
if action == "create_plan" || action == "update_plan" {
|
|
316
|
-
if let Some(steps) = extract_plan_steps(&input)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
373
|
+
if let Some(steps) = extract_plan_steps(&input)
|
|
374
|
+
&& let Ok(mut status) = progress_live_status.lock()
|
|
375
|
+
{
|
|
376
|
+
status.thinking = None;
|
|
377
|
+
status.plan_steps = steps;
|
|
378
|
+
render_live_status(&mut status);
|
|
322
379
|
}
|
|
323
380
|
} else if command_was_run(&result)
|
|
324
381
|
&& let Some(commands) = ran_command_labels(&action, &input)
|
|
@@ -335,39 +392,38 @@ pub async fn run_code_agent_with_options(
|
|
|
335
392
|
if action == "web_search"
|
|
336
393
|
&& !result.starts_with("Web search error:")
|
|
337
394
|
&& result != "No web search results found."
|
|
395
|
+
&& let Ok(mut status) = progress_live_status.lock()
|
|
338
396
|
{
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
status.web_sources.extend(sources);
|
|
342
|
-
}
|
|
397
|
+
let sources = parse_web_search_sources(&result);
|
|
398
|
+
status.web_sources.extend(sources);
|
|
343
399
|
}
|
|
344
400
|
}
|
|
345
401
|
};
|
|
346
402
|
|
|
347
403
|
let chunk_live_status = Arc::clone(&live_status);
|
|
348
404
|
let on_chunk = |summary: String| {
|
|
349
|
-
if !options.fast_mode
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
405
|
+
if !options.fast_mode
|
|
406
|
+
&& let Ok(mut status) = chunk_live_status.lock()
|
|
407
|
+
{
|
|
408
|
+
status.thinking = None;
|
|
409
|
+
commit_activity_snapshot(&mut status);
|
|
410
|
+
clear_live_status(&mut status);
|
|
355
411
|
}
|
|
356
412
|
let formatted_summary = format_markdown_bold(&sanitize_latex(&summary));
|
|
357
413
|
print!("\n {MINT}Mint:{RESET} ");
|
|
358
414
|
render_live_summary(&formatted_summary);
|
|
359
415
|
|
|
360
416
|
// Print web search sources if any were collected
|
|
361
|
-
if let Ok(mut status) = chunk_live_status.lock()
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
status.web_sources.clear();
|
|
417
|
+
if let Ok(mut status) = chunk_live_status.lock()
|
|
418
|
+
&& !status.web_sources.is_empty()
|
|
419
|
+
{
|
|
420
|
+
println!();
|
|
421
|
+
println!(" {DIM}Sources:{RESET}");
|
|
422
|
+
for (i, (title, url)) in status.web_sources.iter().enumerate() {
|
|
423
|
+
println!(" {DIM}{}.{RESET} {BLUE}{}{RESET}", i + 1, title);
|
|
424
|
+
println!(" {DIM}{}{RESET}", url);
|
|
370
425
|
}
|
|
426
|
+
status.web_sources.clear();
|
|
371
427
|
}
|
|
372
428
|
|
|
373
429
|
println!();
|
|
@@ -378,6 +434,7 @@ pub async fn run_code_agent_with_options(
|
|
|
378
434
|
task,
|
|
379
435
|
root,
|
|
380
436
|
image_data_uri,
|
|
437
|
+
video_data_uri,
|
|
381
438
|
Some(CHAT_CLI_ID),
|
|
382
439
|
None,
|
|
383
440
|
options.fast_mode,
|
|
@@ -396,14 +453,14 @@ pub async fn run_code_agent_with_options(
|
|
|
396
453
|
}
|
|
397
454
|
};
|
|
398
455
|
agent_done.store(true, Ordering::Relaxed);
|
|
399
|
-
if !options.fast_mode
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
clear_live_status(&mut status);
|
|
456
|
+
if !options.fast_mode
|
|
457
|
+
&& let Ok(mut status) = live_status.lock()
|
|
458
|
+
{
|
|
459
|
+
status.thinking = None;
|
|
460
|
+
if res.is_err() {
|
|
461
|
+
commit_activity_snapshot(&mut status);
|
|
406
462
|
}
|
|
463
|
+
clear_live_status(&mut status);
|
|
407
464
|
}
|
|
408
465
|
let res = res.map_err(|e| anyhow!("{}", e))?;
|
|
409
466
|
|
|
@@ -436,19 +493,19 @@ fn parse_web_search_sources(result: &str) -> Vec<(String, String)> {
|
|
|
436
493
|
for line in result.lines() {
|
|
437
494
|
let trimmed = line.trim();
|
|
438
495
|
// Match numbered title lines: "1. Title text"
|
|
439
|
-
if let Some(rest) = trimmed.split_once(". ")
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
496
|
+
if let Some(rest) = trimmed.split_once(". ")
|
|
497
|
+
&& rest.0.parse::<usize>().is_ok()
|
|
498
|
+
{
|
|
499
|
+
current_title = Some(rest.1.trim().to_owned());
|
|
500
|
+
continue;
|
|
444
501
|
}
|
|
445
502
|
// Match URL lines: "URL: https://..."
|
|
446
|
-
if let Some(url) = trimmed.strip_prefix("URL: ")
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
503
|
+
if let Some(url) = trimmed.strip_prefix("URL: ")
|
|
504
|
+
&& let Some(title) = current_title.take()
|
|
505
|
+
{
|
|
506
|
+
let url = url.trim().to_owned();
|
|
507
|
+
if !url.is_empty() {
|
|
508
|
+
sources.push((title, url));
|
|
452
509
|
}
|
|
453
510
|
}
|
|
454
511
|
}
|
|
@@ -489,18 +546,73 @@ fn render_live_summary(summary: &str) {
|
|
|
489
546
|
let _ = io::stdout().flush();
|
|
490
547
|
}
|
|
491
548
|
|
|
549
|
+
fn diff_stats(diff: &str) -> (usize, usize) {
|
|
550
|
+
let mut additions = 0;
|
|
551
|
+
let mut deletions = 0;
|
|
552
|
+
for line in diff.lines() {
|
|
553
|
+
if line.starts_with("+++ ") || line.starts_with("--- ") {
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
if line.starts_with('+') {
|
|
557
|
+
additions += 1;
|
|
558
|
+
} else if line.starts_with('-') {
|
|
559
|
+
deletions += 1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
(additions, deletions)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
fn parse_hunk_header(line: &str) -> Option<(usize, usize)> {
|
|
566
|
+
let trimmed = line.trim();
|
|
567
|
+
if !trimmed.starts_with("@@") {
|
|
568
|
+
return None;
|
|
569
|
+
}
|
|
570
|
+
let parts: Vec<&str> = trimmed.split("@@").collect();
|
|
571
|
+
if parts.len() < 3 {
|
|
572
|
+
return None;
|
|
573
|
+
}
|
|
574
|
+
let header_body = parts[1].trim();
|
|
575
|
+
let mut old_start = 1;
|
|
576
|
+
let mut new_start = 1;
|
|
577
|
+
|
|
578
|
+
for token in header_body.split_whitespace() {
|
|
579
|
+
if let Some(num_str) = token.strip_prefix('-') {
|
|
580
|
+
let start_str = num_str.split(',').next().unwrap_or(num_str);
|
|
581
|
+
old_start = start_str.parse().unwrap_or(1);
|
|
582
|
+
} else if let Some(num_str) = token.strip_prefix('+') {
|
|
583
|
+
let start_str = num_str.split(',').next().unwrap_or(num_str);
|
|
584
|
+
new_start = start_str.parse().unwrap_or(1);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
Some((old_start, new_start))
|
|
588
|
+
}
|
|
589
|
+
|
|
492
590
|
fn print_colored_diff(diff: &str) {
|
|
591
|
+
let mut current_old_line = 1;
|
|
592
|
+
let mut current_new_line = 1;
|
|
593
|
+
|
|
493
594
|
for line in diff.lines() {
|
|
494
595
|
if line.starts_with("@@") {
|
|
495
|
-
|
|
596
|
+
if let Some((old_s, new_s)) = parse_hunk_header(line) {
|
|
597
|
+
current_old_line = old_s;
|
|
598
|
+
current_new_line = new_s;
|
|
599
|
+
}
|
|
600
|
+
println!(" {BLUE}{line}{RESET}");
|
|
496
601
|
} else if line.starts_with("--- ") || line.starts_with("+++ ") {
|
|
497
|
-
println!("{DIM}{line}{RESET}");
|
|
602
|
+
println!(" {DIM}{line}{RESET}");
|
|
498
603
|
} else if line.starts_with('-') {
|
|
499
|
-
|
|
604
|
+
let line_num_str = format!("{:>5}", current_old_line);
|
|
605
|
+
current_old_line += 1;
|
|
606
|
+
println!(" {DIM}{line_num_str}{RESET} \x1b[31m{line}\x1b[0m");
|
|
500
607
|
} else if line.starts_with('+') {
|
|
501
|
-
|
|
608
|
+
let line_num_str = format!("{:>5}", current_new_line);
|
|
609
|
+
current_new_line += 1;
|
|
610
|
+
println!(" {DIM}{line_num_str}{RESET} \x1b[32m{line}\x1b[0m");
|
|
502
611
|
} else {
|
|
503
|
-
|
|
612
|
+
let line_num_str = format!("{:>5}", current_new_line);
|
|
613
|
+
current_old_line += 1;
|
|
614
|
+
current_new_line += 1;
|
|
615
|
+
println!(" {DIM}{line_num_str}{RESET} {line}");
|
|
504
616
|
}
|
|
505
617
|
}
|
|
506
618
|
}
|
|
@@ -640,6 +752,80 @@ fn is_thai_combining(c: char) -> bool {
|
|
|
640
752
|
)
|
|
641
753
|
}
|
|
642
754
|
|
|
755
|
+
fn apply_wave_effect(text: &str, tick: usize) -> String {
|
|
756
|
+
let (label, metadata) = if let Some(idx) = text.rfind(" โข Esc to interrupt)") {
|
|
757
|
+
if let Some(open_paren_idx) = text[..idx].rfind('(') {
|
|
758
|
+
(&text[..open_paren_idx], &text[open_paren_idx..])
|
|
759
|
+
} else {
|
|
760
|
+
(text, "")
|
|
761
|
+
}
|
|
762
|
+
} else {
|
|
763
|
+
(text, "")
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
let trimmed_label = label.trim_end();
|
|
767
|
+
let spaces_count = label.len() - trimmed_label.len();
|
|
768
|
+
|
|
769
|
+
let chars: Vec<char> = trimmed_label.chars().collect();
|
|
770
|
+
let n = chars.len();
|
|
771
|
+
if n == 0 {
|
|
772
|
+
return text.to_string();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
let mut animated_label = String::new();
|
|
776
|
+
|
|
777
|
+
// Smooth wave movement using a sine wave function.
|
|
778
|
+
// phase shifts with tick (time), index i acts as spatial shift.
|
|
779
|
+
// 0.3 speed controls animation rate, 0.4 controls width of the wave crest.
|
|
780
|
+
let speed = 0.3;
|
|
781
|
+
let phase = tick as f32 * speed;
|
|
782
|
+
|
|
783
|
+
for (i, &c) in chars.iter().enumerate() {
|
|
784
|
+
if c == ' ' {
|
|
785
|
+
animated_label.push(c);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
let x = (i as f32 * 0.4) - phase;
|
|
790
|
+
let t = (x.sin() + 1.0) / 2.0; // Oscillates in [0.0, 1.0]
|
|
791
|
+
|
|
792
|
+
// Stop colors: Dim Gray (70, 70, 70) -> Mint Green (105, 230, 166) -> Cyan (78, 201, 216)
|
|
793
|
+
let (r, g, b) = if t < 0.3 {
|
|
794
|
+
let local_t = t / 0.3;
|
|
795
|
+
let r = 70.0 + (105.0 - 70.0) * local_t;
|
|
796
|
+
let g = 70.0 + (230.0 - 70.0) * local_t;
|
|
797
|
+
let b = 70.0 + (166.0 - 70.0) * local_t;
|
|
798
|
+
(r, g, b)
|
|
799
|
+
} else if t < 0.7 {
|
|
800
|
+
let local_t = (t - 0.3) / 0.4;
|
|
801
|
+
let r = 105.0 + (78.0 - 105.0) * local_t;
|
|
802
|
+
let g = 230.0 + (201.0 - 230.0) * local_t;
|
|
803
|
+
let b = 166.0 + (216.0 - 166.0) * local_t;
|
|
804
|
+
(r, g, b)
|
|
805
|
+
} else {
|
|
806
|
+
let local_t = (t - 0.7) / 0.3;
|
|
807
|
+
let r = 78.0 + (70.0 - 78.0) * local_t;
|
|
808
|
+
let g = 201.0 + (70.0 - 201.0) * local_t;
|
|
809
|
+
let b = 216.0 + (70.0 - 216.0) * local_t;
|
|
810
|
+
(r, g, b)
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
animated_label.push_str(&format!(
|
|
814
|
+
"\x1b[1m\x1b[38;2;{};{};{}m{}\x1b[0m",
|
|
815
|
+
r.round() as u8,
|
|
816
|
+
g.round() as u8,
|
|
817
|
+
b.round() as u8,
|
|
818
|
+
c
|
|
819
|
+
));
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
animated_label.push_str(&" ".repeat(spaces_count));
|
|
823
|
+
if !metadata.is_empty() {
|
|
824
|
+
animated_label.push_str(&format!("{BRIGHT}{metadata}{RESET}"));
|
|
825
|
+
}
|
|
826
|
+
animated_label
|
|
827
|
+
}
|
|
828
|
+
|
|
643
829
|
fn render_live_status(status: &mut LiveStatus) {
|
|
644
830
|
clear_live_status(status);
|
|
645
831
|
let mut lines = Vec::new();
|
|
@@ -663,10 +849,32 @@ fn render_live_status(status: &mut LiveStatus) {
|
|
|
663
849
|
tick,
|
|
664
850
|
));
|
|
665
851
|
if let Some(thinking) = &status.thinking {
|
|
666
|
-
let frames = &[
|
|
852
|
+
let frames = &[
|
|
853
|
+
"๐\u{FE0E}",
|
|
854
|
+
"๐\u{FE0E}",
|
|
855
|
+
"๐\u{FE0E}",
|
|
856
|
+
"๐\u{FE0E}",
|
|
857
|
+
"๐\u{FE0E}",
|
|
858
|
+
"๐\u{FE0E}",
|
|
859
|
+
"๐\u{FE0E}",
|
|
860
|
+
"๐\u{FE0E}",
|
|
861
|
+
];
|
|
667
862
|
let frame = frames[status.spinner_tick % frames.len()];
|
|
863
|
+
|
|
864
|
+
let dots_frames = &["", ".", "..", "..."];
|
|
865
|
+
let dots = dots_frames[(status.spinner_tick / 2) % dots_frames.len()];
|
|
866
|
+
|
|
668
867
|
status.spinner_tick += 1;
|
|
669
|
-
|
|
868
|
+
|
|
869
|
+
let custom_thinking = if thinking.contains("is thinking") {
|
|
870
|
+
thinking.replace("is thinking", &format!("is thinking{:<3}", dots))
|
|
871
|
+
} else {
|
|
872
|
+
thinking.replace("Thinking", &format!("Thinking{:<3}", dots))
|
|
873
|
+
};
|
|
874
|
+
|
|
875
|
+
let waved_thinking = apply_wave_effect(&custom_thinking, status.spinner_tick);
|
|
876
|
+
|
|
877
|
+
lines.push(format!(" {MINT}{frame}{RESET} {waved_thinking}"));
|
|
670
878
|
}
|
|
671
879
|
if lines.is_empty() {
|
|
672
880
|
return;
|
|
@@ -680,7 +888,7 @@ fn render_live_status(status: &mut LiveStatus) {
|
|
|
680
888
|
let stripped = strip_ansi_escapes(line);
|
|
681
889
|
let line_len = stripped.chars().filter(|&c| !is_thai_combining(c)).count();
|
|
682
890
|
let physical_lines = if width > 0 {
|
|
683
|
-
(
|
|
891
|
+
line_len.div_ceil(width)
|
|
684
892
|
} else {
|
|
685
893
|
1
|
|
686
894
|
}
|
|
@@ -753,7 +961,11 @@ fn clear_live_status(status: &mut LiveStatus) {
|
|
|
753
961
|
|
|
754
962
|
fn get_bullet(name: &str, is_thinking: bool, tick: usize) -> String {
|
|
755
963
|
let char_str = if is_thinking {
|
|
756
|
-
if (tick / 4)
|
|
964
|
+
if (tick / 4).is_multiple_of(2) {
|
|
965
|
+
"โ"
|
|
966
|
+
} else {
|
|
967
|
+
"โ"
|
|
968
|
+
}
|
|
757
969
|
} else {
|
|
758
970
|
"โ"
|
|
759
971
|
};
|
|
@@ -936,6 +1148,33 @@ async fn wait_for_escape_interrupt(approval_active: Arc<AtomicBool>) {
|
|
|
936
1148
|
}
|
|
937
1149
|
}
|
|
938
1150
|
|
|
1151
|
+
fn print_approval_card(title: &str, fields: &[(&str, &str)]) {
|
|
1152
|
+
let top_bar = format!(" {DIM}{}โฌ{}{RESET}", "โ".repeat(11), "โ".repeat(56));
|
|
1153
|
+
let bot_bar = format!(" {DIM}{}โด{}{RESET}", "โ".repeat(11), "โ".repeat(56));
|
|
1154
|
+
|
|
1155
|
+
println!();
|
|
1156
|
+
println!(
|
|
1157
|
+
" {BRIGHT}APPROVAL REQUIRED{RESET} {DIM}โข{RESET} {BLUE}{}{RESET}",
|
|
1158
|
+
title
|
|
1159
|
+
);
|
|
1160
|
+
println!("{}", top_bar);
|
|
1161
|
+
for (label, val) in fields {
|
|
1162
|
+
let val_lines: Vec<&str> = val.lines().collect();
|
|
1163
|
+
if val_lines.is_empty() {
|
|
1164
|
+
println!(" {BRIGHT}{:<10}{RESET} {DIM}โ{RESET}", label);
|
|
1165
|
+
} else {
|
|
1166
|
+
for (idx, line) in val_lines.iter().enumerate() {
|
|
1167
|
+
if idx == 0 {
|
|
1168
|
+
println!(" {BRIGHT}{:<10}{RESET} {DIM}โ{RESET} {}", label, line);
|
|
1169
|
+
} else {
|
|
1170
|
+
println!(" {DIM}โ{RESET} {}", line);
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
println!("{}", bot_bar);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
939
1178
|
fn confirm_pausing_interrupt(prompt: &str, approval_active: &AtomicBool) -> bool {
|
|
940
1179
|
approval_active.store(true, Ordering::Relaxed);
|
|
941
1180
|
let approved = crate::confirm(prompt).unwrap_or(false);
|
|
@@ -966,7 +1205,7 @@ fn format_markdown_bold(text: &str) -> String {
|
|
|
966
1205
|
} else {
|
|
967
1206
|
if c == '-' || c == '*' || c == '+' {
|
|
968
1207
|
let after = &line[idx + c.len_utf8()..];
|
|
969
|
-
if after.chars().next().
|
|
1208
|
+
if after.chars().next().is_some_and(|c| c.is_whitespace()) {
|
|
970
1209
|
is_list_item = true;
|
|
971
1210
|
marker_char = c;
|
|
972
1211
|
}
|
|
@@ -1043,27 +1282,43 @@ fn sanitize_latex(text: &str) -> String {
|
|
|
1043
1282
|
let mut s = text.to_owned();
|
|
1044
1283
|
for (pat, uni) in [
|
|
1045
1284
|
// arrows
|
|
1046
|
-
("$\\rightarrow$",
|
|
1047
|
-
("
|
|
1048
|
-
("
|
|
1049
|
-
("$\\
|
|
1285
|
+
("$\\rightarrow$", "โ"),
|
|
1286
|
+
("\\rightarrow", "โ"),
|
|
1287
|
+
("ightarrow", "โ"),
|
|
1288
|
+
("$\\leftarrow$", "โ"),
|
|
1289
|
+
("\\leftarrow", "โ"),
|
|
1290
|
+
("eftarrow", "โ"),
|
|
1291
|
+
("$\\Rightarrow$", "โ"),
|
|
1292
|
+
("\\Rightarrow", "โ"),
|
|
1293
|
+
("$\\Leftarrow$", "โ"),
|
|
1294
|
+
("\\Leftarrow$", "โ"),
|
|
1050
1295
|
("$\\leftrightarrow$", "โ"),
|
|
1296
|
+
("\\leftrightarrow", "โ"),
|
|
1051
1297
|
// comparison
|
|
1052
|
-
("$\\leq$",
|
|
1053
|
-
("
|
|
1054
|
-
("$\\
|
|
1298
|
+
("$\\leq$", "โค"),
|
|
1299
|
+
("\\leq", "โค"),
|
|
1300
|
+
("$\\geq$", "โฅ"),
|
|
1301
|
+
("\\geq", "โฅ"),
|
|
1302
|
+
("$\\neq$", "โ "),
|
|
1303
|
+
("\\neq", "โ "),
|
|
1055
1304
|
("$\\approx$", "โ"),
|
|
1305
|
+
("\\approx", "โ"),
|
|
1056
1306
|
// math
|
|
1057
1307
|
("$\\times$", "ร"),
|
|
1058
|
-
("
|
|
1059
|
-
("$\\
|
|
1308
|
+
("\\times", "ร"),
|
|
1309
|
+
("$\\div$", "รท"),
|
|
1310
|
+
("\\div", "รท"),
|
|
1311
|
+
("$\\pm$", "ยฑ"),
|
|
1312
|
+
("\\pm", "ยฑ"),
|
|
1060
1313
|
("$\\infty$", "โ"),
|
|
1061
|
-
("
|
|
1314
|
+
("\\infty", "โ"),
|
|
1315
|
+
("$\\cdot$", "ยท"),
|
|
1316
|
+
("\\cdot", "ยท"),
|
|
1062
1317
|
// sets
|
|
1063
|
-
("$\\in$",
|
|
1318
|
+
("$\\in$", "โ"),
|
|
1064
1319
|
("$\\subset$", "โ"),
|
|
1065
|
-
("$\\cup$",
|
|
1066
|
-
("$\\cap$",
|
|
1320
|
+
("$\\cup$", "โช"),
|
|
1321
|
+
("$\\cap$", "โฉ"),
|
|
1067
1322
|
] {
|
|
1068
1323
|
s = s.replace(pat, uni);
|
|
1069
1324
|
}
|