Skip to content

Commit c4fbb48

Browse files
SonAIengineclaude
andcommitted
feat: AI CLI navigate tool — API 호출 후 관련 페이지 자동 이동
- navigate meta-tool 추가 (search_tools, call_tool에 이어 3번째) - API path → 프론트엔드 페이지 매핑 (get_page_for_api) - /api/workflow/* → /main?view=workflows - /api/chat/* → /main?view=new-chat - /api/admin/* → /admin?view=dashboard 등 - search_tools 결과에 "📄 Related page" 정보 포함 - LLM이 call_tool 실행 후 관련 페이지를 자동으로 navigate - 메인 윈도우: Tauri event 수신 → router.push(path) - system prompt: "call_tool 후 Related page가 있으면 navigate 호출" 지시 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6ff51f3 commit c4fbb48

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

scripts/patch-sidebar-cli.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,21 @@ if (usesSidebarLayout) {
8989
`const { quickLogout } = useQuickLogout();
9090
9191
const [isTauriApp, setIsTauriApp] = useState(false);
92-
useEffect(() => { setIsTauriApp(isTauri()); }, []);
92+
useEffect(() => {
93+
setIsTauriApp(isTauri());
94+
// Listen for navigate events from AI CLI
95+
if (isTauri()) {
96+
import('@tauri-apps/api/event').then(({ listen }) => {
97+
listen('navigate', (event: any) => {
98+
const path = event.payload?.path;
99+
if (path) {
100+
console.log('[AI CLI] Navigate to:', path);
101+
router.push(path);
102+
}
103+
});
104+
}).catch(() => {});
105+
}
106+
}, []);
93107
94108
const openCliWindow = async () => {
95109
try {

src-cli/cli.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@
374374
tc.textContent = `🔍 ${data.input?.query || 'searching...'}`;
375375
} else if (name === 'call_tool') {
376376
tc.textContent = `⚡ ${data.input?.tool_name || 'calling...'}`;
377+
} else if (name === 'navigate') {
378+
tc.textContent = `📄 ${data.input?.path || 'navigating...'}`;
377379
} else {
378380
tc.textContent = `⚡ ${name}`;
379381
}

src-tauri/src/services/llm_client.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ impl LlmClient {
8585
2. 검색 결과에서 적절한 tool을 선택하고, call_tool로 호출하세요.
8686
- tool_name은 검색 결과의 정확한 이름을 사용하세요.
8787
- arguments는 검색 결과의 파라미터 스키마에 맞게 구성하세요.
88-
3. 일반 질문이나 tool이 필요 없는 경우에는 직접 답변하세요.
88+
3. call_tool 실행 후, 검색 결과에 '📄 Related page'가 있으면 navigate로 해당 페이지를 열어주세요.
89+
- API 호출 결과를 텍스트로 정리한 뒤, 관련 페이지로 자동 이동합니다.
90+
- 사용자가 이동을 원하지 않을 수도 있으니 결과를 먼저 보여주세요.
91+
4. 일반 질문이나 tool이 필요 없는 경우에는 직접 답변하세요.
8992
9093
응답 규칙:
9194
- API 결과는 핵심 정보만 추려서 한국어로 읽기 쉽게 정리하세요.
@@ -691,6 +694,11 @@ impl LlmClient {
691694
Err(e) => format!("Call error: {}", e),
692695
}
693696
}
697+
"navigate" => {
698+
let path = tool_input["path"].as_str().unwrap_or("/");
699+
println!(" [navigate] {}", path);
700+
format!("Navigated to {}", path)
701+
}
694702
_ => format!("Unknown tool: {}", tool_name),
695703
};
696704

@@ -805,6 +813,13 @@ impl LlmClient {
805813
Err(e) => format!("Call error: {}", e),
806814
}
807815
}
816+
"navigate" => {
817+
let path = tool_input["path"].as_str().unwrap_or("/");
818+
log::info!("navigate: {}", path);
819+
// Emit navigate event to the main window
820+
let _ = app.emit_to("main", "navigate", serde_json::json!({"path": path}));
821+
format!("Navigated to {}", path)
822+
}
808823
_ => format!("Unknown tool: {}", tool_name),
809824
};
810825

src-tauri/src/services/tool_search.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,50 @@ use crate::error::{AppError, Result};
1616

1717
const DEFAULT_TOP_K: usize = 7;
1818

19+
/// API path prefix → 프론트엔드 페이지 매핑
20+
fn get_page_for_api(api_path: &str) -> Option<&'static str> {
21+
let mappings: &[(&str, &str)] = &[
22+
// Workflow
23+
("/api/workflow/list", "/main?view=workflows"),
24+
("/api/workflow/execute", "/main?view=workflows"),
25+
("/api/workflow/store", "/main?view=workflows"),
26+
("/api/workflow/canvas", "/main?view=canvas"),
27+
("/api/workflow/trace", "/main?view=workflows"),
28+
("/api/workflow/schedule", "/main?view=workflows"),
29+
// Chat
30+
("/api/chat", "/main?view=new-chat"),
31+
("/api/interaction", "/main?view=new-chat"),
32+
// LLM / Config / Admin
33+
("/api/llm", "/admin?view=dashboard"),
34+
("/api/config", "/admin?view=dashboard"),
35+
("/api/admin", "/admin?view=dashboard"),
36+
// Node / Tools
37+
("/api/node", "/main?view=workflows"),
38+
("/api/tools", "/main?view=workflows"),
39+
// Documents / RAG
40+
("/api/documents", "/main?view=documents"),
41+
("/api/retrieval", "/main?view=documents"),
42+
("/api/embedding", "/admin?view=dashboard"),
43+
// Prompt
44+
("/api/prompt", "/main?view=workflows"),
45+
// Model
46+
("/api/model", "/modelOps?view=train-monitor"),
47+
// Service Request
48+
("/api/service-request", "/main?view=service-request"),
49+
// Support
50+
("/api/support", "/support?view=inquiry"),
51+
// Main
52+
("/api/dashboard", "/main?view=main-dashboard"),
53+
];
54+
55+
for (prefix, page) in mappings {
56+
if api_path.starts_with(prefix) {
57+
return Some(page);
58+
}
59+
}
60+
None
61+
}
62+
1963
// ============================================================
2064
// Meta-tool definitions (LLM에 제공할 고정 tool 2개)
2165
// ============================================================
@@ -59,6 +103,20 @@ pub fn meta_tool_definitions() -> Vec<Value> {
59103
"required": ["tool_name"]
60104
}
61105
}),
106+
serde_json::json!({
107+
"name": "navigate",
108+
"description": "Navigate the main XGEN window to a related page. Use the 'Related page' from search_tools results. Call this AFTER call_tool to show the user the relevant UI page.",
109+
"input_schema": {
110+
"type": "object",
111+
"properties": {
112+
"path": {
113+
"type": "string",
114+
"description": "Page path from search results (e.g. '/main?view=workflows', '/admin?view=dashboard', '/main?view=canvas')"
115+
}
116+
},
117+
"required": ["path"]
118+
}
119+
}),
62120
]
63121
}
64122

@@ -104,6 +162,11 @@ pub async fn search_tools_text(
104162
lines.push(format!(" {} {}", method, path));
105163
lines.push(format!(" {}", desc));
106164

165+
// Related frontend page
166+
if let Some(page) = get_page_for_api(path) {
167+
lines.push(format!(" 📄 Related page: {}", page));
168+
}
169+
107170
// Parameters
108171
if let Some(params) = tool["parameters"].as_array() {
109172
if !params.is_empty() {

0 commit comments

Comments
 (0)