@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
package/src-tauri/src/lib.rs
CHANGED
|
@@ -31,11 +31,11 @@ use integrations::{channel_inventory, list_plugins};
|
|
|
31
31
|
use mint_core::{
|
|
32
32
|
AgentApproval, AgentProgress, AppliedCodeEdit, ApprovalOutcome, ChatRequest, ChatResponse,
|
|
33
33
|
ChatSession, CodeEdit, CodeEditProposal, ImageGenRequest, InteractionMemory, MemoryStore,
|
|
34
|
-
MintConfig, PictureEntry, TtsUrl,
|
|
35
|
-
|
|
36
|
-
orchestrate_agent_loop, orchestrate_chat_stream_with_fallback,
|
|
37
|
-
propose_code_edits, save_chat_images, save_config,
|
|
38
|
-
workflows_path,
|
|
34
|
+
MintConfig, PictureEntry, TtsUrl, VideoGenRequest, VideoGenResponse, WeatherReport,
|
|
35
|
+
apply_code_edits, classify_shell_command, config_path, google_tts_urls, list_saved_pictures,
|
|
36
|
+
load_config, load_workflows, orchestrate_agent_loop, orchestrate_chat_stream_with_fallback,
|
|
37
|
+
orchestrate_chat_with_fallback, propose_code_edits, save_chat_images, save_config,
|
|
38
|
+
save_workflows, start_channels, weather, workflows_path,
|
|
39
39
|
};
|
|
40
40
|
use plugins::execute_plugin;
|
|
41
41
|
|
|
@@ -73,6 +73,7 @@ struct RuntimeStatus {
|
|
|
73
73
|
backend: &'static str,
|
|
74
74
|
config_path: String,
|
|
75
75
|
active_provider: String,
|
|
76
|
+
active_model: String,
|
|
76
77
|
available_providers: Vec<String>,
|
|
77
78
|
integrations: Value,
|
|
78
79
|
}
|
|
@@ -119,11 +120,8 @@ fn get_runtime_status() -> Result<RuntimeStatus, String> {
|
|
|
119
120
|
.display()
|
|
120
121
|
.to_string(),
|
|
121
122
|
active_provider: config.ai_provider.clone(),
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.into_iter()
|
|
125
|
-
.map(str::to_owned)
|
|
126
|
-
.collect(),
|
|
123
|
+
active_model: config.active_model().to_string(),
|
|
124
|
+
available_providers: config.available_providers(),
|
|
127
125
|
integrations: integration_status(&config),
|
|
128
126
|
})
|
|
129
127
|
}
|
|
@@ -309,6 +307,20 @@ fn update_config(app: AppHandle, config: MintConfig) -> Result<(), String> {
|
|
|
309
307
|
Ok(())
|
|
310
308
|
}
|
|
311
309
|
|
|
310
|
+
#[tauri::command]
|
|
311
|
+
fn set_active_model(
|
|
312
|
+
app: AppHandle,
|
|
313
|
+
provider: String,
|
|
314
|
+
model: Option<String>,
|
|
315
|
+
) -> Result<String, String> {
|
|
316
|
+
let mut config = load_config().map_err(|error| error.to_string())?;
|
|
317
|
+
let display_name = config
|
|
318
|
+
.set_active_model(&provider, model.as_deref())
|
|
319
|
+
.map_err(|error| error.to_string())?;
|
|
320
|
+
let _ = app.emit("settings-changed", &config);
|
|
321
|
+
Ok(display_name)
|
|
322
|
+
}
|
|
323
|
+
|
|
312
324
|
#[tauri::command]
|
|
313
325
|
fn inspect_shell_command(command: String) -> mint_core::ShellClassification {
|
|
314
326
|
classify_shell_command(&command)
|
|
@@ -316,7 +328,21 @@ fn inspect_shell_command(command: String) -> mint_core::ShellClassification {
|
|
|
316
328
|
|
|
317
329
|
#[tauri::command]
|
|
318
330
|
async fn send_chat_message(app: AppHandle, request: ChatRequest) -> Result<ChatResponse, String> {
|
|
319
|
-
let config = load_config().map_err(|error| error.to_string())?;
|
|
331
|
+
let mut config = load_config().map_err(|error| error.to_string())?;
|
|
332
|
+
if let Some(ref path) = request.workspace_path {
|
|
333
|
+
if !path.trim().is_empty()
|
|
334
|
+
&& config
|
|
335
|
+
.extra
|
|
336
|
+
.get("activeWorkspacePath")
|
|
337
|
+
.and_then(Value::as_str)
|
|
338
|
+
!= Some(path.as_str())
|
|
339
|
+
{
|
|
340
|
+
config
|
|
341
|
+
.extra
|
|
342
|
+
.insert("activeWorkspacePath".into(), Value::String(path.clone()));
|
|
343
|
+
let _ = save_config(&config);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
320
346
|
let request = request.with_document_context(&config)?;
|
|
321
347
|
|
|
322
348
|
if request.message.starts_with("/chat ") {
|
|
@@ -331,19 +357,27 @@ async fn send_chat_message(app: AppHandle, request: ChatRequest) -> Result<ChatR
|
|
|
331
357
|
|
|
332
358
|
let abort_handle = join_handle.abort_handle();
|
|
333
359
|
if !chat_id_str.is_empty() {
|
|
334
|
-
mint_core::ACTIVE_AGENTS
|
|
360
|
+
mint_core::ACTIVE_AGENTS
|
|
361
|
+
.lock()
|
|
362
|
+
.unwrap()
|
|
363
|
+
.insert(chat_id_str.clone(), abort_handle);
|
|
335
364
|
}
|
|
336
365
|
|
|
337
366
|
let res = join_handle.await;
|
|
338
367
|
|
|
339
368
|
if !chat_id_str.is_empty() {
|
|
340
|
-
mint_core::ACTIVE_AGENTS
|
|
369
|
+
mint_core::ACTIVE_AGENTS
|
|
370
|
+
.lock()
|
|
371
|
+
.unwrap()
|
|
372
|
+
.remove(&chat_id_str);
|
|
341
373
|
}
|
|
342
374
|
|
|
343
375
|
let (response, _) = match res {
|
|
344
376
|
Ok(Ok(val)) => val,
|
|
345
377
|
Ok(Err(e)) => return Err(e.to_string()),
|
|
346
|
-
Err(e) if e.is_cancelled() =>
|
|
378
|
+
Err(e) if e.is_cancelled() => {
|
|
379
|
+
return Err("Chat execution cancelled by user".to_string());
|
|
380
|
+
}
|
|
347
381
|
Err(e) => return Err(format!("Task panicked: {}", e)),
|
|
348
382
|
};
|
|
349
383
|
return Ok(response);
|
|
@@ -388,6 +422,7 @@ async fn send_chat_message(app: AppHandle, request: ChatRequest) -> Result<ChatR
|
|
|
388
422
|
let message_clone = request.message.clone();
|
|
389
423
|
let root_clone = root.clone();
|
|
390
424
|
let image_data_uri_clone = request.image_data_uri.clone();
|
|
425
|
+
let video_data_uri_clone = request.video_data_uri.clone();
|
|
391
426
|
let chat_id_clone = request.chat_id.clone();
|
|
392
427
|
let agent_id_clone = request.agent_id.clone();
|
|
393
428
|
|
|
@@ -397,6 +432,7 @@ async fn send_chat_message(app: AppHandle, request: ChatRequest) -> Result<ChatR
|
|
|
397
432
|
&message_clone,
|
|
398
433
|
&root_clone,
|
|
399
434
|
image_data_uri_clone,
|
|
435
|
+
video_data_uri_clone,
|
|
400
436
|
chat_id_clone.as_deref(),
|
|
401
437
|
agent_id_clone.as_deref(),
|
|
402
438
|
fast_mode,
|
|
@@ -409,13 +445,19 @@ async fn send_chat_message(app: AppHandle, request: ChatRequest) -> Result<ChatR
|
|
|
409
445
|
|
|
410
446
|
let abort_handle = join_handle.abort_handle();
|
|
411
447
|
if !chat_id_str.is_empty() {
|
|
412
|
-
mint_core::ACTIVE_AGENTS
|
|
448
|
+
mint_core::ACTIVE_AGENTS
|
|
449
|
+
.lock()
|
|
450
|
+
.unwrap()
|
|
451
|
+
.insert(chat_id_str.clone(), abort_handle);
|
|
413
452
|
}
|
|
414
453
|
|
|
415
454
|
let res = join_handle.await;
|
|
416
455
|
|
|
417
456
|
if !chat_id_str.is_empty() {
|
|
418
|
-
mint_core::ACTIVE_AGENTS
|
|
457
|
+
mint_core::ACTIVE_AGENTS
|
|
458
|
+
.lock()
|
|
459
|
+
.unwrap()
|
|
460
|
+
.remove(&chat_id_str);
|
|
419
461
|
}
|
|
420
462
|
|
|
421
463
|
let res = match res {
|
|
@@ -439,7 +481,21 @@ async fn stream_chat_message(
|
|
|
439
481
|
request: ChatRequest,
|
|
440
482
|
on_event: Channel<DesktopStreamEvent>,
|
|
441
483
|
) -> Result<ChatResponse, String> {
|
|
442
|
-
let config = load_config().map_err(|error| error.to_string())?;
|
|
484
|
+
let mut config = load_config().map_err(|error| error.to_string())?;
|
|
485
|
+
if let Some(ref path) = request.workspace_path {
|
|
486
|
+
if !path.trim().is_empty()
|
|
487
|
+
&& config
|
|
488
|
+
.extra
|
|
489
|
+
.get("activeWorkspacePath")
|
|
490
|
+
.and_then(Value::as_str)
|
|
491
|
+
!= Some(path.as_str())
|
|
492
|
+
{
|
|
493
|
+
config
|
|
494
|
+
.extra
|
|
495
|
+
.insert("activeWorkspacePath".into(), Value::String(path.clone()));
|
|
496
|
+
let _ = save_config(&config);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
443
499
|
let request = request.with_document_context(&config)?;
|
|
444
500
|
|
|
445
501
|
if request.message.starts_with("/chat ") {
|
|
@@ -458,19 +514,27 @@ async fn stream_chat_message(
|
|
|
458
514
|
|
|
459
515
|
let abort_handle = join_handle.abort_handle();
|
|
460
516
|
if !chat_id_str.is_empty() {
|
|
461
|
-
mint_core::ACTIVE_AGENTS
|
|
517
|
+
mint_core::ACTIVE_AGENTS
|
|
518
|
+
.lock()
|
|
519
|
+
.unwrap()
|
|
520
|
+
.insert(chat_id_str.clone(), abort_handle);
|
|
462
521
|
}
|
|
463
522
|
|
|
464
523
|
let res = join_handle.await;
|
|
465
524
|
|
|
466
525
|
if !chat_id_str.is_empty() {
|
|
467
|
-
mint_core::ACTIVE_AGENTS
|
|
526
|
+
mint_core::ACTIVE_AGENTS
|
|
527
|
+
.lock()
|
|
528
|
+
.unwrap()
|
|
529
|
+
.remove(&chat_id_str);
|
|
468
530
|
}
|
|
469
531
|
|
|
470
532
|
let (response, _) = match res {
|
|
471
533
|
Ok(Ok(val)) => val,
|
|
472
534
|
Ok(Err(e)) => return Err(e.to_string()),
|
|
473
|
-
Err(e) if e.is_cancelled() =>
|
|
535
|
+
Err(e) if e.is_cancelled() => {
|
|
536
|
+
return Err("Chat execution cancelled by user".to_string());
|
|
537
|
+
}
|
|
474
538
|
Err(e) => return Err(format!("Task panicked: {}", e)),
|
|
475
539
|
};
|
|
476
540
|
return Ok(response);
|
|
@@ -530,6 +594,7 @@ async fn stream_chat_message(
|
|
|
530
594
|
let message_clone = request.message.clone();
|
|
531
595
|
let root_clone = root.clone();
|
|
532
596
|
let image_data_uri_clone = request.image_data_uri.clone();
|
|
597
|
+
let video_data_uri_clone = request.video_data_uri.clone();
|
|
533
598
|
let chat_id_clone = request.chat_id.clone();
|
|
534
599
|
let agent_id_clone = request.agent_id.clone();
|
|
535
600
|
|
|
@@ -539,6 +604,7 @@ async fn stream_chat_message(
|
|
|
539
604
|
&message_clone,
|
|
540
605
|
&root_clone,
|
|
541
606
|
image_data_uri_clone,
|
|
607
|
+
video_data_uri_clone,
|
|
542
608
|
chat_id_clone.as_deref(),
|
|
543
609
|
agent_id_clone.as_deref(),
|
|
544
610
|
fast_mode,
|
|
@@ -551,13 +617,19 @@ async fn stream_chat_message(
|
|
|
551
617
|
|
|
552
618
|
let abort_handle = join_handle.abort_handle();
|
|
553
619
|
if !chat_id_str.is_empty() {
|
|
554
|
-
mint_core::ACTIVE_AGENTS
|
|
620
|
+
mint_core::ACTIVE_AGENTS
|
|
621
|
+
.lock()
|
|
622
|
+
.unwrap()
|
|
623
|
+
.insert(chat_id_str.clone(), abort_handle);
|
|
555
624
|
}
|
|
556
625
|
|
|
557
626
|
let res = join_handle.await;
|
|
558
627
|
|
|
559
628
|
if !chat_id_str.is_empty() {
|
|
560
|
-
mint_core::ACTIVE_AGENTS
|
|
629
|
+
mint_core::ACTIVE_AGENTS
|
|
630
|
+
.lock()
|
|
631
|
+
.unwrap()
|
|
632
|
+
.remove(&chat_id_str);
|
|
561
633
|
}
|
|
562
634
|
|
|
563
635
|
let res = match res {
|
|
@@ -600,12 +672,19 @@ fn save_interaction_agent_activity(
|
|
|
600
672
|
fn save_system_interaction(
|
|
601
673
|
chat_id: String,
|
|
602
674
|
user_text: String,
|
|
675
|
+
ai_text: Option<String>,
|
|
603
676
|
provider: String,
|
|
604
677
|
model: String,
|
|
605
678
|
) -> Result<i64, String> {
|
|
606
679
|
MemoryStore::open_default()
|
|
607
680
|
.and_then(|memory| {
|
|
608
|
-
memory.add_interaction_for_chat(
|
|
681
|
+
memory.add_interaction_for_chat(
|
|
682
|
+
&chat_id,
|
|
683
|
+
&user_text,
|
|
684
|
+
ai_text.as_deref().unwrap_or(""),
|
|
685
|
+
&provider,
|
|
686
|
+
&model,
|
|
687
|
+
)
|
|
609
688
|
})
|
|
610
689
|
.map_err(|error| error.to_string())
|
|
611
690
|
}
|
|
@@ -680,6 +759,9 @@ fn list_learned_skills(workspace_path: Option<String>) -> Result<Vec<LearnedSkil
|
|
|
680
759
|
|
|
681
760
|
let mut global_skills = Vec::new();
|
|
682
761
|
if let Some(home) = dirs::home_dir() {
|
|
762
|
+
let global_agents_path = home.join(".gemini").join("config").join("AGENTS.md");
|
|
763
|
+
mint_core::skills::load_agent_rules_file(&global_agents_path, &mut global_skills);
|
|
764
|
+
|
|
683
765
|
let global_skills_path = home.join(".config").join("mint").join("mint-skills");
|
|
684
766
|
if !global_skills_path.exists() {
|
|
685
767
|
let _ = std::fs::create_dir_all(&global_skills_path);
|
|
@@ -689,6 +771,12 @@ fn list_learned_skills(workspace_path: Option<String>) -> Result<Vec<LearnedSkil
|
|
|
689
771
|
|
|
690
772
|
let mut workspace_skills = Vec::new();
|
|
691
773
|
if let Ok(root) = workspace_root(workspace_path.as_deref()) {
|
|
774
|
+
let workspace_agents_path1 = root.join(".agents").join("AGENTS.md");
|
|
775
|
+
mint_core::skills::load_agent_rules_file(&workspace_agents_path1, &mut workspace_skills);
|
|
776
|
+
|
|
777
|
+
let workspace_agents_path2 = root.join("AGENTS.md");
|
|
778
|
+
mint_core::skills::load_agent_rules_file(&workspace_agents_path2, &mut workspace_skills);
|
|
779
|
+
|
|
692
780
|
let workspace_skills_path1 = root.join(".agents").join("skills");
|
|
693
781
|
mint_core::skills::load_skills_from_dir(&workspace_skills_path1, &mut workspace_skills);
|
|
694
782
|
|
|
@@ -843,6 +931,14 @@ async fn generate_images(request: ImageGenRequest) -> Result<DesktopImageGenResp
|
|
|
843
931
|
})
|
|
844
932
|
}
|
|
845
933
|
|
|
934
|
+
#[tauri::command]
|
|
935
|
+
async fn generate_video(request: VideoGenRequest) -> Result<VideoGenResponse, String> {
|
|
936
|
+
let config = load_config().map_err(|error| error.to_string())?;
|
|
937
|
+
mint_core::generate_video(&config, &request)
|
|
938
|
+
.await
|
|
939
|
+
.map_err(|error| error.to_string())
|
|
940
|
+
}
|
|
941
|
+
|
|
846
942
|
#[tauri::command]
|
|
847
943
|
fn list_pictures() -> Result<Vec<PictureEntry>, String> {
|
|
848
944
|
list_saved_pictures().map_err(|error| error.to_string())
|
|
@@ -857,6 +953,37 @@ fn save_pictures(
|
|
|
857
953
|
save_chat_images(images, source, message).map_err(|error| error.to_string())
|
|
858
954
|
}
|
|
859
955
|
|
|
956
|
+
#[tauri::command]
|
|
957
|
+
fn upload_file(filename: String, data_b64: String) -> Result<String, String> {
|
|
958
|
+
// Determine mime from filename
|
|
959
|
+
let mime = filename
|
|
960
|
+
.rsplit_once('.')
|
|
961
|
+
.map(|(_, ext)| match ext.to_ascii_lowercase().as_str() {
|
|
962
|
+
"mp4" => "video/mp4",
|
|
963
|
+
"webm" => "video/webm",
|
|
964
|
+
"mov" => "video/quicktime",
|
|
965
|
+
"mkv" => "video/x-matroska",
|
|
966
|
+
"avi" => "video/x-msvideo",
|
|
967
|
+
_ => "application/octet-stream",
|
|
968
|
+
})
|
|
969
|
+
.unwrap_or("application/octet-stream");
|
|
970
|
+
|
|
971
|
+
let data_uri = format!("data:{};base64,{}", mime, data_b64);
|
|
972
|
+
match save_chat_images(
|
|
973
|
+
vec![data_uri],
|
|
974
|
+
Some("upload".into()),
|
|
975
|
+
Some("uploaded".into()),
|
|
976
|
+
) {
|
|
977
|
+
Ok(mut saved) => {
|
|
978
|
+
if let Some(entry) = saved.pop() {
|
|
979
|
+
return Ok(format!("/api/pictures/{}", entry.filename));
|
|
980
|
+
}
|
|
981
|
+
Err("no entry saved".into())
|
|
982
|
+
}
|
|
983
|
+
Err(e) => Err(e.to_string()),
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
860
987
|
#[tauri::command]
|
|
861
988
|
fn open_folder(path: String) -> Result<ActionResult, String> {
|
|
862
989
|
let target_path = PathBuf::from(path.trim());
|
|
@@ -1157,8 +1284,8 @@ fn install_tray(app: &AppHandle) -> tauri::Result<()> {
|
|
|
1157
1284
|
fn install_shortcuts(app: &AppHandle) -> tauri::Result<()> {
|
|
1158
1285
|
let main_shortcut = Shortcut::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::Space);
|
|
1159
1286
|
let spotlight_shortcut = Shortcut::new(Some(Modifiers::ALT), Code::Space);
|
|
1160
|
-
let main_handler = main_shortcut
|
|
1161
|
-
let spotlight_handler = spotlight_shortcut
|
|
1287
|
+
let main_handler = main_shortcut;
|
|
1288
|
+
let spotlight_handler = spotlight_shortcut;
|
|
1162
1289
|
app.plugin(
|
|
1163
1290
|
tauri_plugin_global_shortcut::Builder::new()
|
|
1164
1291
|
.with_handler(move |app, shortcut, event| {
|
|
@@ -1217,12 +1344,14 @@ pub fn run() {
|
|
|
1217
1344
|
create_workspace_folder,
|
|
1218
1345
|
delete_workspace_item,
|
|
1219
1346
|
generate_images,
|
|
1347
|
+
generate_video,
|
|
1220
1348
|
select_workspace_directory,
|
|
1221
1349
|
get_config,
|
|
1222
1350
|
get_updater_status,
|
|
1223
1351
|
check_for_updates,
|
|
1224
1352
|
install_available_update,
|
|
1225
1353
|
update_config,
|
|
1354
|
+
set_active_model,
|
|
1226
1355
|
inspect_shell_command,
|
|
1227
1356
|
send_chat_message,
|
|
1228
1357
|
stream_chat_message,
|
|
@@ -1241,6 +1370,7 @@ pub fn run() {
|
|
|
1241
1370
|
delete_learned_skill,
|
|
1242
1371
|
list_pictures,
|
|
1243
1372
|
save_pictures,
|
|
1373
|
+
upload_file,
|
|
1244
1374
|
open_folder,
|
|
1245
1375
|
get_tts_urls,
|
|
1246
1376
|
get_weather,
|
|
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
|
|
2
2
|
|
|
3
3
|
use base64::{Engine as _, engine::general_purpose::STANDARD};
|
|
4
4
|
use hmac::{Hmac, Mac};
|
|
5
|
-
use mint_core::
|
|
5
|
+
use mint_core::load_config;
|
|
6
6
|
use serde_json::{Value, json};
|
|
7
7
|
use sha2::Sha256;
|
|
8
8
|
use tokio::{
|
|
@@ -14,10 +14,16 @@ type HmacSha256 = Hmac<Sha256>;
|
|
|
14
14
|
|
|
15
15
|
pub fn start_webhooks() {
|
|
16
16
|
tauri::async_runtime::spawn(async {
|
|
17
|
-
let
|
|
17
|
+
let (host, port) = load_config()
|
|
18
|
+
.map(|c| (c.line_webhook_host(), c.line_webhook_port()))
|
|
19
|
+
.unwrap_or_else(|_| ("127.0.0.1".into(), 3000));
|
|
20
|
+
let _ = serve(&host, port, Service::Line).await;
|
|
18
21
|
});
|
|
19
22
|
tauri::async_runtime::spawn(async {
|
|
20
|
-
let
|
|
23
|
+
let (host, port) = load_config()
|
|
24
|
+
.map(|c| (c.whatsapp_webhook_host(), c.whatsapp_webhook_port()))
|
|
25
|
+
.unwrap_or_else(|_| ("127.0.0.1".into(), 3001));
|
|
26
|
+
let _ = serve(&host, port, Service::Whatsapp).await;
|
|
21
27
|
});
|
|
22
28
|
}
|
|
23
29
|
|
|
@@ -27,8 +33,9 @@ enum Service {
|
|
|
27
33
|
Whatsapp,
|
|
28
34
|
}
|
|
29
35
|
|
|
30
|
-
async fn serve(port: u16, service: Service) -> Result<(), String> {
|
|
31
|
-
let
|
|
36
|
+
async fn serve(host: &str, port: u16, service: Service) -> Result<(), String> {
|
|
37
|
+
let addr = format!("{host}:{port}");
|
|
38
|
+
let listener = TcpListener::bind(&addr)
|
|
32
39
|
.await
|
|
33
40
|
.map_err(|error| error.to_string())?;
|
|
34
41
|
loop {
|
|
@@ -78,9 +85,28 @@ async fn handle_line(request: HttpRequest) -> Result<(&'static str, String), Str
|
|
|
78
85
|
) else {
|
|
79
86
|
continue;
|
|
80
87
|
};
|
|
81
|
-
let
|
|
82
|
-
let
|
|
83
|
-
|
|
88
|
+
let user_id = event["source"]["userId"].as_str().unwrap_or("unknown");
|
|
89
|
+
let formatted_chat_id = format!("line:{user_id}");
|
|
90
|
+
let answer = mint_core::channels::answer_channel(
|
|
91
|
+
text,
|
|
92
|
+
"Reply concisely for a LINE chat.",
|
|
93
|
+
Some(formatted_chat_id),
|
|
94
|
+
)
|
|
95
|
+
.await;
|
|
96
|
+
|
|
97
|
+
let mut messages = Vec::new();
|
|
98
|
+
if config.bridge_ack_enabled() {
|
|
99
|
+
messages.push(json!({ "type": "text", "text": config.bridge_ack_message() }));
|
|
100
|
+
}
|
|
101
|
+
messages.push(json!({ "type": "text", "text": answer }));
|
|
102
|
+
|
|
103
|
+
let _ = mint_core::HTTP_CLIENT
|
|
104
|
+
.clone()
|
|
105
|
+
.post("https://api.line.me/v2/bot/message/reply")
|
|
106
|
+
.bearer_auth(&token)
|
|
107
|
+
.json(&json!({ "replyToken": reply_token, "messages": messages }))
|
|
108
|
+
.send()
|
|
109
|
+
.await;
|
|
84
110
|
}
|
|
85
111
|
Ok(("200 OK", "ok".into()))
|
|
86
112
|
}
|
|
@@ -133,35 +159,23 @@ async fn handle_whatsapp(request: HttpRequest) -> Result<(&'static str, String),
|
|
|
133
159
|
else {
|
|
134
160
|
continue;
|
|
135
161
|
};
|
|
136
|
-
|
|
162
|
+
if config.bridge_ack_enabled() {
|
|
163
|
+
let _ = mint_core::HTTP_CLIENT.clone().post(format!("https://graph.facebook.com/v23.0/{phone_id}/messages")).bearer_auth(&access_token)
|
|
164
|
+
.json(&json!({ "messaging_product": "whatsapp", "to": to, "type": "text", "text": { "body": config.bridge_ack_message() } })).send().await;
|
|
165
|
+
}
|
|
166
|
+
let formatted_chat_id = format!("whatsapp:{to}");
|
|
167
|
+
let answer = mint_core::channels::answer_channel(
|
|
168
|
+
text,
|
|
169
|
+
"Reply concisely for a WhatsApp chat.",
|
|
170
|
+
Some(formatted_chat_id),
|
|
171
|
+
)
|
|
172
|
+
.await;
|
|
137
173
|
let _ = mint_core::HTTP_CLIENT.clone().post(format!("https://graph.facebook.com/v23.0/{phone_id}/messages")).bearer_auth(&access_token)
|
|
138
174
|
.json(&json!({ "messaging_product": "whatsapp", "to": to, "type": "text", "text": { "body": answer } })).send().await;
|
|
139
175
|
}
|
|
140
176
|
Ok(("200 OK", "ok".into()))
|
|
141
177
|
}
|
|
142
178
|
|
|
143
|
-
async fn answer(text: &str, system: &str) -> String {
|
|
144
|
-
let Ok(config) = load_config() else {
|
|
145
|
-
return "Mint config error".into();
|
|
146
|
-
};
|
|
147
|
-
orchestrate_chat(
|
|
148
|
-
&config,
|
|
149
|
-
&ChatRequest {
|
|
150
|
-
message: text.into(),
|
|
151
|
-
system_instruction: system.into(),
|
|
152
|
-
chat_id: None,
|
|
153
|
-
image_data_uri: None,
|
|
154
|
-
audio_data_uri: None,
|
|
155
|
-
document_attachment: None,
|
|
156
|
-
workspace_path: None,
|
|
157
|
-
agent_id: None,
|
|
158
|
-
},
|
|
159
|
-
)
|
|
160
|
-
.await
|
|
161
|
-
.map(|response| response.text)
|
|
162
|
-
.unwrap_or_else(|error| format!("Mint error: {error}"))
|
|
163
|
-
}
|
|
164
|
-
|
|
165
179
|
struct HttpRequest {
|
|
166
180
|
method: String,
|
|
167
181
|
path: String,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Project instructions
|
|
2
|
+
|
|
3
|
+
- Do not use any `superpowers:*` skills in this repository.
|
|
4
|
+
- Do not create or restore files under `docs/superpowers/`.
|
|
5
|
+
- Use the `solodeveling` skill at the start of every task in this repository. Resume `.solodeveling/state.md` when present and let the skill route the work to the smallest relevant lifecycle workflow.
|
|
6
|
+
- Keep one primary agent. Do not use subagents unless the user explicitly requests delegation.
|
|
7
|
+
- If `solodeveling` is unavailable, state that briefly and continue with the same evidence-driven lifecycle rather than blocking routine work.
|
|
8
|
+
- Work directly from the user's request and use only the minimum additional relevant skills.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Gridgeist are documented here. This project follows
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [1.2.0] - 2026-07-20
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added a conditional system-contract reference covering semantic color,
|
|
13
|
+
typography, layout, spacing, component grammar, states, media, motion, and
|
|
14
|
+
proportional DESIGN.md interoperability.
|
|
15
|
+
- Added paired English and Thai behavioral evaluation coverage for design-system
|
|
16
|
+
coherence and persistent-artifact boundaries.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Strengthened the core workflow and review checklist to reconcile existing
|
|
21
|
+
design-system sources, define reusable component rules, and review color/theming
|
|
22
|
+
and component coherence separately.
|
|
23
|
+
- Clarified that product content alone cannot make an underspecified broad redesign
|
|
24
|
+
brand-derived, and prohibited system definition or implementation edits while its
|
|
25
|
+
direction remains provisional.
|
|
26
|
+
- Made overlapping broad visual-skill ownership an explicit pre-thesis gate instead
|
|
27
|
+
of allowing the companion to be silently ignored or blended.
|
|
28
|
+
- Documented optional DESIGN.md intake and output in the English and Thai usage
|
|
29
|
+
guidance without making it a dependency.
|
|
30
|
+
|
|
31
|
+
## [1.1.2] - 2026-07-18
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Added compact companion coordination so Gridgeist remains the default owner of
|
|
36
|
+
product and visual direction without requiring optional context, asset, audit, or
|
|
37
|
+
verification capabilities.
|
|
38
|
+
- Added paired English and Thai evaluation scenarios for overlapping art-direction
|
|
39
|
+
skills, internationalization and user preferences, interaction alternatives, and
|
|
40
|
+
bounded performance evidence.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Strengthened the review checklist for container resilience, language direction,
|
|
45
|
+
forced colors, unobscured focus, non-drag alternatives, media stability, and
|
|
46
|
+
evidence-bounded performance review.
|
|
47
|
+
- Documented optional companion workflows and role boundaries in the English and
|
|
48
|
+
Thai READMEs without making them installation dependencies.
|
|
49
|
+
|
|
50
|
+
## [1.1.1] - 2026-07-18
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- Added isolated live smoke tests for Codex plugin clean installation, marketplace
|
|
55
|
+
refresh, reinstall, and universal installer updates on release tags and manual CI
|
|
56
|
+
runs.
|
|
57
|
+
- Added a bilingual direction-decision fixture, paired ambiguous/explicit scenarios,
|
|
58
|
+
and preserved raw regression outputs for the new collaboration boundary.
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- Reordered the English and Thai Quickstarts so the Codex plugin and universal
|
|
63
|
+
installer lead, with repository cloning and manual copying kept as fallback.
|
|
64
|
+
- Added a conditional direction-alignment gate for broad, underspecified Create and
|
|
65
|
+
Redesign work without slowing explicit directions, Reviews, or narrow repairs.
|
|
66
|
+
|
|
67
|
+
## [1.1.0] - 2026-07-16
|
|
68
|
+
|
|
69
|
+
### Added
|
|
70
|
+
|
|
71
|
+
- Added explicit update instructions for Codex plugins, `npx skills`, Git clones,
|
|
72
|
+
and manual installations in English and Thai documentation.
|
|
73
|
+
- Added dependency-free release validation and GitHub Actions checks for skill,
|
|
74
|
+
plugin, marketplace, documentation, and bilingual evaluation alignment.
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- Reframed Gridgeist around product-native structure so grids may be visible,
|
|
79
|
+
quiet, or invisible according to the established brand.
|
|
80
|
+
- Added explicit adaptation guidance for image-led, playful, technical,
|
|
81
|
+
content-heavy, and transactional interfaces.
|
|
82
|
+
- Added interaction-state inventory, verification evidence, and claim-integrity
|
|
83
|
+
requirements to the core workflow and review checklist.
|
|
84
|
+
- Strengthened behavioral evaluation guidance with repeat runs,
|
|
85
|
+
scenario-level evidence, implementation checks, and trigger boundaries.
|
|
86
|
+
- Made the documented Windows validation command explicitly use UTF-8.
|
|
87
|
+
- Pinned the public marketplace entry to the v1.1.0 release tag.
|
|
88
|
+
|
|
89
|
+
## [1.0.1] - 2026-07-14
|
|
90
|
+
|
|
91
|
+
### Fixed
|
|
92
|
+
|
|
93
|
+
- Added a Git-backed Codex marketplace catalog for two-command installation.
|
|
94
|
+
- Moved the installable skill to the standard `skills/gridgeist/` plugin path
|
|
95
|
+
so Codex plugin validation and marketplace installation use the same source.
|
|
96
|
+
|
|
97
|
+
## [1.0.0] - 2026-07-13
|
|
98
|
+
|
|
99
|
+
Gridgeist's first stable release.
|
|
100
|
+
|
|
101
|
+
### Added
|
|
102
|
+
|
|
103
|
+
- A reproducible Tracefield case study with the original baseline, exact prompt,
|
|
104
|
+
six-part rubric, verification evidence, and stated limitations.
|
|
105
|
+
- A dedicated case-study page and a field-proof section on the project website.
|
|
106
|
+
- Direct links from the English and Thai documentation to the live Tracefield
|
|
107
|
+
interface and its source repository.
|
|
108
|
+
|
|
109
|
+
### Changed
|
|
110
|
+
|
|
111
|
+
- Promoted the Codex plugin manifest from `0.1.0` to `1.0.0` to mark the public
|
|
112
|
+
skill interface and its Create, Redesign, and Review workflows as stable.
|
|
113
|
+
|
|
114
|
+
## [0.1.0] - 2026-07-13
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
|
|
118
|
+
- Initial Gridgeist agent skill with Create, Redesign, and Review workflows.
|
|
119
|
+
- Codex plugin manifest and agent-facing interface metadata.
|
|
120
|
+
- English and Thai documentation and evaluation prompts.
|
|
121
|
+
- Interactive examples and a responsive GitHub Pages website.
|
|
122
|
+
|
|
123
|
+
[Unreleased]: https://github.com/ohmiler/gridgeist/compare/v1.2.0...HEAD
|
|
124
|
+
[1.2.0]: https://github.com/ohmiler/gridgeist/compare/v1.1.2...v1.2.0
|
|
125
|
+
[1.1.2]: https://github.com/ohmiler/gridgeist/compare/v1.1.1...v1.1.2
|
|
126
|
+
[1.1.1]: https://github.com/ohmiler/gridgeist/compare/v1.1.0...v1.1.1
|
|
127
|
+
[1.1.0]: https://github.com/ohmiler/gridgeist/compare/v1.0.1...v1.1.0
|
|
128
|
+
[1.0.1]: https://github.com/ohmiler/gridgeist/compare/v1.0.0...v1.0.1
|
|
129
|
+
[1.0.0]: https://github.com/ohmiler/gridgeist/compare/v0.1.0...v1.0.0
|
|
130
|
+
[0.1.0]: https://github.com/ohmiler/gridgeist/releases/tag/v0.1.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Miler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|