1 line
277 KiB
JSON
1 line
277 KiB
JSON
{"repo": "blackeagle686/giyu-agent", "files": {"giyu.context.json": {"d33e86253d58e98045be06d06c8f339f846febbb": {"t": 1785599328.010131, "n": 3459, "h": "105d12aa50bbb697", "c": "{\n \"context_id\": \"79b03dea-a7be-40c1-a301-369a6c022350\",\n \"metadata\": {\n \"created_at\": \"2026-05-12T04:37:01.664344\",\n \"updated_at\": \"2026-05-12T04:41:16.332643\",\n \"original_prompt\": \"what is the ram stauts\"\n },\n \"tasks\": [\n {\n \"id\": 1,\n \"priority\": 1,\n \"title\": \"Retrieve Current RAM Usage\",\n \"description\": \"Execute system command to obtain real-time memory utilization data for stability assessment.\",\n \"status\": \"failed\",\n \"type\": \"command\"\n }\n ],\n \"plans\": [\n {\n \"plan_step_id\": 1,\n \"task_id\": 1,\n \"step_index\": 1,\n \"type\": \"analysis\",\n \"solution\": {\n \"approach\": \"Analyze the task to understand the requirement: retrieve real-time RAM usage for system stability assessment. Identify that this is a monitoring/diagnostic task and should leverage existing CLI tools rather than writing new Python code.\",\n \"algorithm\": null,\n \"complexity\": \"O(1)\"\n },\n \"dependencies\": [],\n \"status\": \"failed\"\n },\n {\n \"plan_step_id\": 2,\n \"task_id\": 1,\n \"step_index\": 2,\n \"type\": \"design\",\n \"solution\": {\n \"approach\": \"Design the solution using standard Unix/Linux system commands. Use `free -h` command which provides human-readable memory information including total, used, free, shared, buff/cache, and available RAM. This command gives immediate real-time data suitable for stability assessment.\",\n \"algorithm\": null,\n \"complexity\": \"O(1)\"\n },\n \"dependencies\": [\n 1\n ],\n \"status\": \"pending\"\n },\n {\n \"plan_step_id\": 3,\n \"task_id\": 1,\n \"step_index\": 3,\n \"type\": \"implementation\",\n \"solution\": {\n \"approach\": \"Execute the `free -h` command via run_shell_command to capture current RAM usage statistics. The output will be displayed directly without redirection to files, showing memory utilization in human-readable format.\",\n \"algorithm\": null,\n \"complexity\": \"O(1)\"\n },\n \"dependencies\": [\n 2\n ],\n \"status\": \"pending\"\n },\n {\n \"plan_step_id\": 4,\n \"task_id\": 1,\n \"step_index\": 4,\n \"type\": \"validation\",\n \"solution\": {\n \"approach\": \"Validate the output by checking that the command executed successfully (exit code 0) and that the response contains expected memory metrics (total, used, free, available). Ensure no errors are present in stderr.\",\n \"algorithm\": null,\n \"complexity\": \"O(1)\"\n },\n \"dependencies\": [\n 3\n ],\n \"status\": \"pending\"\n }\n ],\n \"generations\": [],\n \"execution_state\": {\n \"completed_steps\": [],\n \"pending_steps\": [],\n \"failed_steps\": [\n {\n \"id\": 1,\n \"title\": \"Retrieve Current RAM Usage\"\n }\n ]\n },\n \"memory\": {},\n \"stability_reports\": [\n {\n \"score\": 100.0,\n \"state\": \"stable\",\n \"timestamp\": 1778549821.6643445,\n \"metrics_evaluated\": [\n \"cpu\",\n \"ram\",\n \"disk\"\n ],\n \"escalation_level\": \"ignore\",\n \"necessity\": \"low\",\n \"decision_reason\": \"Stability score is 100.0\",\n \"metrics\": {\n \"cpu_usage\": 50,\n \"ram_usage\": 50,\n \"disk_usage\": 50,\n \"net_latency\": 0,\n \"process_count\": 0\n }\n }\n ],\n \"anomalies\": [],\n \"escalation_state\": {\n \"current_level\": \"ignore\",\n \"reason\": \"Stability score is 100.0\",\n \"timestamp\": \"2026-05-12T04:37:23.152846\"\n }\n}"}}, "giyu/backend/routes_chat.py": {"e02c8eada04b5d1cdef4f4fb9fd520b10987c4b6": {"t": 1785599328.0137963, "n": 3114, "h": "adb2f2051468f3da", "c": "\"\"\"\nGiyu Backend — Chat & Agent streaming endpoints.\n\"\"\"\nimport asyncio\nimport json\nimport uuid\nfrom fastapi import APIRouter\nfrom fastapi.responses import StreamingResponse\n\nfrom .state import (\n log, vscode_ipc_context, _pending_file_opens,\n get_agent, _ipc_responses,\n)\nfrom .schemas import ChatRequest, ToolResult\n\nrouter = APIRouter()\n\n\n@router.post(\"/chat/stream\")\nasync def chat_stream(req: ChatRequest):\n \"\"\"Stream agent response as Server-Sent Events.\"\"\"\n agent = get_agent()\n if agent is None:\n async def _error():\n yield f\"data: {json.dumps({'type':'error','content':'Agent not ready.'})}\\n\\n\"\n return StreamingResponse(_error(), media_type=\"text/event-stream\")\n\n session_id = req.session_id or str(uuid.uuid4())\n queue: asyncio.Queue = asyncio.Queue()\n\n async def _emit_event(event: dict):\n await queue.put(event)\n\n async def _vscode_call(tool_name: str, arguments: dict) -> str:\n call_id = str(uuid.uuid4())\n future = asyncio.get_event_loop().create_future()\n _ipc_responses[call_id] = future\n\n await _emit_event({\n \"type\": \"vscode_tool\",\n \"call_id\": call_id,\n \"tool\": tool_name,\n \"arguments\": arguments,\n })\n\n try:\n return await asyncio.wait_for(future, timeout=60.0)\n except asyncio.TimeoutError:\n return \"ERROR: VS Code tool call timed out.\"\n finally:\n _ipc_responses.pop(call_id, None)\n\n async def _run_agent():\n token = vscode_ipc_context.set(_vscode_call)\n try:\n async for event in agent.run_stream(\n req.task, session_id=session_id, mode=req.mode\n ):\n await queue.put(event)\n except Exception as exc:\n log.exception(\"Stream error\")\n await queue.put({\"type\": \"error\", \"content\": str(exc)})\n finally:\n vscode_ipc_context.reset(token)\n await queue.put({\"type\": \"done\"})\n\n async def _generate():\n agent_task = asyncio.create_task(_run_agent())\n yield f\"data: {json.dumps({'type':'session','session_id':session_id})}\\n\\n\"\n\n while True:\n while _pending_file_opens:\n file_path = _pending_file_opens.popleft()\n yield f\"data: {json.dumps({'type':'vscode_tool','call_id':'noop','tool':'open_file','arguments':{'path': file_path}})}\\n\\n\"\n\n event = await queue.get()\n yield f\"data: {json.dumps(event)}\\n\\n\"\n if event.get(\"type\") == \"done\":\n break\n\n await agent_task\n\n return StreamingResponse(_generate(), media_type=\"text/event-stream\")\n\n\n@router.post(\"/tool/result\")\nasync def tool_result(res: ToolResult):\n \"\"\"Receive results for pending VS Code tool calls.\"\"\"\n if res.call_id in _ipc_responses:\n _ipc_responses[res.call_id].set_result(res.result)\n return {\"status\": \"ok\"}\n return {\"status\": \"error\", \"message\": \"Call ID not found or already timed out.\"}\n\n\n@router.post(\"/reset\")\nasync def reset_session():\n return {\"status\": \"reset\"}\n"}}, "dashboard/main.js": {"27f1c83b0564ebd9362192c007405b3c177e79e5": {"t": 1785599328.1776156, "n": 21748, "h": "0843a4681898c1c2", "c": "const API_BASE = 'http://127.0.0.1:8765';\nlet cpuChart, ramChart, diskChart, netChart, scoreChart;\nconst historySize = 20;\n\nconst charts = {\n cpu: { instance: null, data: Array(historySize).fill(0) },\n ram: { instance: null, data: Array(historySize).fill(0) },\n disk: { instance: null, data: Array(historySize).fill(0) },\n net: { instance: null, data: Array(historySize).fill(0) }\n};\n\nlet isStreaming = false;\n\n// Colors from style.css\nconst COLORS = {\n primary: '#00f2ff',\n secondary: '#7000ff',\n success: '#10b981',\n warning: '#f59e0b',\n danger: '#ef4444',\n ghost: '#475569'\n};\n\nfunction initCharts() {\n const commonOptions = {\n responsive: true,\n maintainAspectRatio: false,\n plugins: { legend: { display: false }, tooltip: { enabled: false } },\n scales: {\n x: { display: false },\n y: { display: false, min: 0, max: 100 }\n },\n elements: {\n line: { \n tension: 0.4, \n borderWidth: 2, \n borderColor: COLORS.primary, \n fill: true, \n backgroundColor: 'rgba(0, 242, 255, 0.05)' \n },\n point: { radius: 0 }\n },\n animation: { duration: 1000 }\n };\n\n charts.cpu.instance = new Chart(document.getElementById('cpuChart'), {\n type: 'line',\n data: { labels: Array(historySize).fill(''), datasets: [{ data: charts.cpu.data }] },\n options: commonOptions\n });\n\n charts.ram.instance = new Chart(document.getElementById('ramChart'), {\n type: 'line',\n data: { labels: Array(historySize).fill(''), datasets: [{ data: charts.ram.data }] },\n options: commonOptions\n });\n\n charts.disk.instance = new Chart(document.getElementById('diskChart'), {\n type: 'line',\n data: { labels: Array(historySize).fill(''), datasets: [{ data: charts.disk.data }] },\n options: commonOptions\n });\n\n charts.net.instance = new Chart(document.getElementById('netChart'), {\n type: 'line',\n data: { labels: Array(historySize).fill(''), datasets: [{ data: charts.net.data }] },\n options: commonOptions\n });\n\n // Score Doughnut Chart\n scoreChart = new Chart(document.getElementById('scoreChart'), {\n type: 'doughnut',\n data: {\n datasets: [{\n data: [0, 100],\n backgroundColor: [COLORS.primary, 'rgba(255, 255, 255, 0.03)'],\n borderWidth: 0,\n circumference: 270,\n rotation: 225,\n cutout: '85%'\n }]\n },\n options: {\n responsive: true,\n maintainAspectRatio: false,\n plugins: { tooltip: { enabled: false } },\n animation: { animateRotate: true, duration: 2000 }\n }\n });\n}\n\nfunction updateClock() {\n const now = new Date();\n document.getElementById('clock').textContent = now.toLocaleTimeString([], { hour12: false });\n}\n\nlet lastDataHash = '';\nlet localReports = [];\n\nasync function updateData() {\n if (isStreaming) return; // Prevent background sync from wiping active chat\n try {\n const healthRes = await fetch(`${API_BASE}/health`);\n const health = await healthRes.json();\n \n const overlay = document.getElementById('loading-overlay');\n if (health.agent_ready) {\n overlay.classList.add('hidden');\n } else {\n overlay.classList.remove('hidden');\n return;\n }\n\n const statusRes = await fetch(`${API_BASE}/stability/status`);\n const status = await statusRes.json();\n const backboneRes = await fetch(`${API_BASE}/stability/backbone`);\n const backbone = await backboneRes.json();\n\n const currentHash = JSON.stringify({status, backbone});\n if (currentHash === lastDataHash) return;\n lastDataHash = currentHash;\n\n updateStabilityUI(status.current_report);\n updateMetrics(status.current_report);\n updateHeartbeat(status.agent_heartbeats);\n updateTasks(backbone.tasks, backbone.plans);\n updateLogs(backbone.stability_reports);\n updateReports(backbone.generations);\n\n } catch (error) {\n console.warn('Sync failed:', error);\n }\n}\n\nfunction updateStabilityUI(report) {\n const score = Math.round(report.score);\n const scoreEl = document.getElementById('stability-score');\n const stateEl = document.getElementById('stability-state');\n \n scoreEl.textContent = score;\n stateEl.textContent = report.state.toUpperCase();\n document.getElementById('anomaly-count').textContent = report.anomalies?.length || 0;\n\n scoreChart.data.datasets[0].data = [score, 100 - score];\n let color = COLORS.primary;\n if (score < 50) color = COLORS.danger;\n else if (score < 80) color = COLORS.warning;\n \n scoreChart.data.datasets[0].backgroundColor[0] = color;\n scoreChart.update('none');\n\n stateEl.style.color = color;\n scoreEl.style.color = color;\n}\n\nfunction updateMetrics(report) {\n const metrics = report.metrics || {};\n const values = {\n cpu: metrics.cpu_usage || 0,\n ram: metrics.ram_usage || 0,\n disk: metrics.disk_usage || 0,\n net: metrics.net_latency || 0\n };\n\n document.getElementById('cpu-value').textContent = Math.round(values.cpu);\n document.getElementById('ram-value').textContent = Math.round(values.ram);\n document.getElementById('disk-value').textContent = Math.round(values.disk);\n document.getElementById('net-value').textContent = Math.round(values.net);\n\n updateChart(charts.cpu, values.cpu);\n updateChart(charts.ram, values.ram);\n updateChart(charts.disk, values.disk);\n updateChart(charts.net, values.net);\n}\n\nfunction updateChart(chartObj, newValue) {\n chartObj.data.push(newValue);\n chartObj.data.shift();\n chartObj.instance.update('none');\n}\n\nfunction updateHeartbeat(heartbeats) {\n if (!heartbeats) return;\n const grid = document.getElementById('heartbeat-grid');\n const fragment = document.createDocumentFragment();\n \n Object.entries(heartbeats).forEach(([name, status]) => {\n const node = document.createElement('div');\n node.className = `agent-dot ${status === 'active' ? '' : 'missing'}`;\n node.innerHTML = `\n <div class=\"dot\"></div>\n <span>${name}</span>\n `;\n fragment.appendChild(node);\n });\n grid.innerHTML = '';\n grid.appendChild(fragment);\n}\n\nfunction updateTasks(tasks, plans) {\n const list = document.getElementById('task-list');\n if (!tasks || tasks.length === 0) return;\n\n const fragment = document.createDocumentFragment();\n tasks.forEach(task => {\n const taskEl = document.createElement('div');\n taskEl.className = `step ${task.status === 'in_progress' ? 'active' : (task.status === 'done' ? 'done' : '')}`;\n \n let planHtml = '';\n const taskPlans = (plans || []).filter(p => p.task_id === task.id);\n if (taskPlans.length > 0) {\n planHtml = `<div style=\"margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.25rem;\">` + \n taskPlans.map(p => `\n <div style=\"font-size: 0.65rem; color: ${p.status === 'done' ? COLORS.success : (p.status === 'active' ? COLORS.primary : COLORS.ghost)}\">\n <i class=\"bi ${p.status === 'done' ? 'bi-check-circle-fill' : 'bi-circle'} me-1\"></i> ${p.solution?.approach || p.type}\n </div>\n `).join('') + `</div>`;\n }\n\n taskEl.innerHTML = `\n <div>\n <h4>${task.title}</h4>\n <p>${task.description || ''}</p>\n ${planHtml}\n </div>\n `;\n fragment.appendChild(taskEl);\n });\n list.innerHTML = '';\n list.appendChild(fragment);\n}\n\nfunction updateLogs(reports) {\n const terminal = document.getElementById('log-terminal');\n if (!reports) return;\n\n const existingCount = terminal.children.length;\n reports.slice(existingCount).forEach(report => {\n const line = document.createElement('div');\n line.className = 'terminal-line';\n const time = new Date(report.timestamp * 1000).toLocaleTimeString([], { hour12: false });\n let levelClass = 'terminal-info';\n if (report.score < 50) levelClass = 'terminal-error';\n else if (report.score < 80) levelClass = 'terminal-warn';\n\n line.innerHTML = `\n <span class=\"terminal-time\">[${time}]</span>\n <span class=\"${levelClass}\">${report.state.toUpperCase()}: Stability Score ${Math.round(report.score)}</span>\n `;\n terminal.appendChild(line);\n });\n\n if (reports.length > existingCount) {\n terminal.scrollTop = terminal.scrollHeight;\n }\n}\n\nfunction updateReports(generations) {\n if (!window.marked) return;\n const list = document.getElementById('reports-list');\n const modalHistory = document.getElementById('modal-chat-history');\n \n // Sort combined reports by timestamp (oldest first)\n const combined = [...localReports, ...(generations || [])]\n .sort((a, b) => (a.timestamp || 0) - (b.timestamp || 0));\n \n if (combined.length === 0) {\n list.innerHTML = '<div style=\"text-align: center; padding: 2rem; color: var(--text-ghost); font-size: 0.8rem;\">Waiting for analysis reports...</div>';\n return;\n }\n\n // Bento Grid: Newest first\n const bentoReports = [...combined].sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));\n const fragment = document.createDocumentFragment();\n bentoReports.forEach((gen, index) => {\n const item = document.createElement('div');\n item.className = 'report-card';\n \n let content = '';\n const timestamp = gen.timestamp ? new Date(gen.timestamp * 1000).toLocaleString() : `Report #${index + 1}`;\n\n if (typeof gen === 'string') content = gen;\n else if (gen.artifacts) {\n content = `### Logic Generation\\n\\n`;\n gen.artifacts.forEach(art => {\n if (art.type === 'file_write') content += `**File:** \\`${art.path}\\`\\n\\`\\`\\`${art.language}\\n${art.code}\\n\\`\\`\\`\\n\\n`;\n else if (art.type === 'terminal') content += `**Command:**\\n\\`\\`\\`bash\\n${art.code}\\n\\`\\`\\`\\n\\n`;\n });\n } else content = gen.text || gen.content || JSON.stringify(gen, null, 2);\n\n item.innerHTML = `\n <h5><i class=\"bi bi-shield-shaded\"></i> Guardian Report <span style=\"margin-left: auto; font-size: 0.6rem; color: var(--text-ghost);\">${timestamp}</span></h5>\n <div style=\"font-size: 0.75rem; line-height: 1.6; color: var(--text-dim);\">${marked.parse(content)}</div>\n `;\n fragment.appendChild(item);\n });\n \n list.innerHTML = '';\n list.appendChild(fragment);\n\n // Modal: Oldest first (Conversational)\n if (modalHistory) {\n const modalFragment = document.createDocumentFragment();\n combined.forEach((gen) => {\n const aiMsg = document.createElement('div');\n aiMsg.className = 'message msg-ai';\n \n if (typeof gen === 'object' && gen.content && gen.content.includes('**Query:**')) {\n const parts = gen.content.split('**Analysis:**');\n const userText = parts[0].replace('**Query:**', '').trim();\n const aiText = parts[1].trim();\n\n const userMsg = document.createElement('div');\n userMsg.className = 'message msg-user';\n userMsg.innerHTML = `<h5>You</h5><div>${userText}</div>`;\n modalFragment.appendChild(userMsg);\n\n aiMsg.innerHTML = `<h5>Giyu</h5><div>${marked.parse(aiText)}</div>`;\n } else {\n aiMsg.innerHTML = `<h5>Giyu</h5><div>${marked.parse(gen.content || gen.text || JSON.stringify(gen))}</div>`;\n }\n modalFragment.appendChild(aiMsg);\n });\n\n modalHistory.innerHTML = '';\n modalHistory.appendChild(modalFragment);\n modalHistory.scrollTop = modalHistory.scrollHeight;\n }\n}\n\nasync function sendCommand(inputId = 'agent-input') {\n if (isStreaming) return;\n const input = document.getElementById(inputId);\n const task = input.value.trim();\n if (!task) return;\n\n input.value = '';\n const session_id = 'gui_' + Date.now();\n isStreaming = true;\n\n // Show user message in modal immediately\n const modalHistory = document.getElementById('modal-chat-history');\n if (modalHistory) {\n if (modalHistory.querySelector('.text-center')) modalHistory.innerHTML = '';\n \n const userMsg = document.createElement('div');\n userMsg.className = 'message msg-user';\n userMsg.innerHTML = `<h5>You</h5><div>${task}</div>`;\n modalHistory.appendChild(userMsg);\n \n const typingMsg = document.createElement('div');\n typingMsg.className = 'message msg-ai typing-indicator';\n typingMsg.id = 'typing-' + session_id;\n typingMsg.innerHTML = `<h5>Giyu</h5><div><i class=\"bi bi-three-dots\"></i> Processing...</div>`;\n modalHistory.appendChild(typingMsg);\n \n modalHistory.scrollTop = modalHistory.scrollHeight;\n }\n\n // Determine mode from the closest container\n const container = input.closest('.input-container-premium');\n const activeModeBtn = container.querySelector('.mode-btn.active');\n const mode = activeModeBtn ? activeModeBtn.dataset.mode : 'auto';\n \n try {\n const response = await fetch(`${API_BASE}/chat/stream`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ task, session_id, mode: mode })\n });\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let agentResponse = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const lines = decoder.decode(value).split('\\n');\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const eventString = line.substring(6).trim();\n if (!eventString) continue;\n \n const event = JSON.parse(eventString);\n if (event.type === 'chunk') {\n agentResponse += event.content;\n const typing = document.getElementById('typing-' + session_id);\n if (typing) typing.remove();\n \n // 1. Update Modal UI\n let aiBubble = document.getElementById('ai-' + session_id);\n if (!aiBubble) {\n aiBubble = document.createElement('div');\n aiBubble.className = 'message msg-ai';\n aiBubble.id = 'ai-' + session_id;\n aiBubble.innerHTML = `<h5>Giyu</h5><div class=\"content\"></div>`;\n modalHistory.appendChild(aiBubble);\n }\n aiBubble.querySelector('.content').innerHTML = marked.parse(agentResponse);\n modalHistory.scrollTop = modalHistory.scrollHeight;\n\n // 2. Update Bento Grid Analysis Section\n const bentoList = document.getElementById('reports-list');\n if (bentoList) {\n let bentoLive = document.getElementById('bento-live-' + session_id);\n if (!bentoLive) {\n bentoLive = document.createElement('div');\n bentoLive.className = 'report-card live-report';\n bentoLive.id = 'bento-live-' + session_id;\n bentoList.prepend(bentoLive); // Put live analysis at the top\n }\n bentoLive.innerHTML = `\n <h5><i class=\"bi bi-shield-shaded pulse-text\"></i> LIVE ANALYSIS <span style=\"margin-left: auto; font-size: 0.6rem; color: var(--primary);\">PLANNING...</span></h5>\n <div style=\"font-size: 0.75rem; line-height: 1.6; color: var(--text-dim);\">${marked.parse(agentResponse)}</div>\n `;\n }\n\n } else if (event.type === 'done') {\n if (agentResponse.trim()) {\n localReports.push({\n timestamp: Date.now() / 1000,\n content: `**Query:** ${task}\\n\\n**Analysis:**\\n${agentResponse.trim()}`\n });\n }\n }\n }\n }\n }\n } catch (err) {\n console.error('Chat error:', err);\n } finally {\n isStreaming = false;\n const typing = document.getElementById('typing-' + session_id);\n if (typing) typing.remove();\n updateData(); // Final sync\n }\n}\n\nfunction initDashboard() {\n initCharts();\n initNet();\n setInterval(updateClock, 1000);\n setInterval(updateData, 3000);\n \n const sendBtn = document.getElementById('send-btn');\n if (sendBtn) {\n sendBtn.addEventListener('click', () => sendCommand('agent-input'));\n }\n \n const agentInput = document.getElementById('agent-input');\n if (agentInput) {\n agentInput.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') sendCommand('agent-input');\n });\n }\n\n const modalSendBtn = document.getElementById('modal-send-btn');\n if (modalSendBtn) {\n modalSendBtn.addEventListener('click', () => sendCommand('modal-agent-input'));\n }\n \n const modalAgentInput = document.getElementById('modal-agent-input');\n if (modalAgentInput) {\n modalAgentInput.addEventListener('keypress', (e) => {\n if (e.key === 'Enter') sendCommand('modal-agent-input');\n });\n }\n\n const modeBtns = document.querySelectorAll('.mode-btn');\n modeBtns.forEach(btn => {\n btn.addEventListener('click', () => {\n const parent = btn.closest('.mode-selector');\n parent.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n });\n });\n\n updateClock();\n updateData();\n}\n\nfunction initNet() {\n const canvas = document.getElementById('bg-net');\n if (!canvas) return;\n const ctx = canvas.getContext('2d');\n let particles = [];\n let mouse = { x: null, y: null, radius: 150 };\n\n window.addEventListener('mousemove', (e) => {\n mouse.x = e.clientX;\n mouse.y = e.clientY;\n });\n\n window.addEventListener('mouseout', () => {\n mouse.x = null;\n mouse.y = null;\n });\n\n function resize() {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n }\n\n class Particle {\n constructor() {\n this.x = Math.random() * canvas.width;\n this.y = Math.random() * canvas.height;\n this.size = Math.random() * 1.5 + 0.5;\n this.speedX = Math.random() * 0.3 - 0.15;\n this.speedY = Math.random() * 0.3 - 0.15;\n }\n\n update() {\n this.x += this.speedX;\n this.y += this.speedY;\n\n if (this.x > canvas.width) this.x = 0;\n if (this.x < 0) this.x = canvas.width;\n if (this.y > canvas.height) this.y = 0;\n if (this.y < 0) this.y = canvas.height;\n\n if (mouse.x !== null) {\n let dx = mouse.x - this.x;\n let dy = mouse.y - this.y;\n let dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < mouse.radius) {\n const force = (mouse.radius - dist) / mouse.radius;\n this.x -= (dx / dist) * force * 2;\n this.y -= (dy / dist) * force * 2;\n }\n }\n }\n\n draw() {\n ctx.fillStyle = COLORS.primary;\n ctx.beginPath();\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);\n ctx.fill();\n }\n }\n\n function animate() {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n for (let i = 0; i < particles.length; i++) {\n particles[i].update();\n particles[i].draw();\n\n for (let j = i + 1; j < particles.length; j++) {\n const dx = particles[i].x - particles[j].x;\n const dy = particles[i].y - particles[j].y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < 100) {\n ctx.strokeStyle = `rgba(0, 242, 255, ${0.15 * (1 - dist / 100)})`;\n ctx.lineWidth = 0.5;\n ctx.beginPath();\n ctx.moveTo(particles[i].x, particles[i].y);\n ctx.lineTo(particles[j].x, particles[j].y);\n ctx.stroke();\n }\n }\n }\n requestAnimationFrame(animate);\n }\n\n window.addEventListener('resize', () => {\n resize();\n particles = [];\n const count = (canvas.width * canvas.height) / 18000;\n for (let i = 0; i < count; i++) particles.push(new Particle());\n });\n\n resize();\n const count = (canvas.width * canvas.height) / 18000;\n for (let i = 0; i < count; i++) particles.push(new Particle());\n animate();\n}\n\ndocument.addEventListener('DOMContentLoaded', initDashboard);\n"}}, "giyu/backend/app.py": {"7676545126d4224979a607a807f52d27cb9b326d": {"t": 1785599328.2857227, "n": 3891, "h": "95a2da31b5b7c2d8", "c": "\"\"\"\nGiyu Backend — FastAPI application factory.\n\"\"\"\nimport asyncio\nimport os\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\n\nfrom .state import log, get_agent, set_agent\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n log.warning(f\"🔥 Giyu Server starting in {os.getcwd()} …\")\n asyncio.create_task(_initialize_agent())\n yield\n log.warning(\"🛑 Giyu Agent server shut down.\")\n\n\nasync def _initialize_agent():\n try:\n from giyu.agent import get_giyu_agent\n log.warning(\"🧠 Loading AI modules & Phoenix framework…\")\n agent = await get_giyu_agent()\n set_agent(agent)\n log.warning(\"✅ Giyu Agent is now READY.\")\n \n # Start background stability monitoring\n asyncio.create_task(_stability_monitor_loop(agent))\n except Exception as exc:\n log.error(f\"❌ Failed to initialize agent: {exc}\", exc_info=True)\n\n\nasync def _stability_monitor_loop(agent):\n \"\"\"Periodically runs the stability check to update the backbone.\"\"\"\n from giyu.cognition.core.utils import stability_check\n \n # Use the agent's existing memory\n memory = agent.memory\n session_id = \"background_monitor\"\n \n ctx = {\n \"actor\": agent.actor,\n \"stability_scorer\": agent.stability_scorer,\n \"decision_gate\": agent.decision_gate,\n \"correlation_engine\": agent.correlation_engine,\n \"bg\": set()\n }\n \n while True:\n try:\n await stability_check(ctx, memory, session_id)\n except Exception as e:\n log.error(f\"Stability Monitor Error: {e}\")\n await asyncio.sleep(5) # Update every 5 seconds\n\n\ndef create_app() -> FastAPI:\n \"\"\"Build and return a fully configured FastAPI application.\"\"\"\n application = FastAPI(\n title=\"Giyu Agent API\",\n version=\"1.0.0\",\n lifespan=lifespan,\n )\n\n application.add_middleware(\n CORSMiddleware,\n allow_origins=[\"*\"],\n allow_credentials=True,\n allow_methods=[\"*\"],\n allow_headers=[\"*\"],\n )\n\n # Health check (inline — too small for its own file)\n @application.get(\"/health\")\n async def health():\n return {\"status\": \"ok\", \"agent_ready\": get_agent() is not None}\n\n # Landing Page Routes\n from fastapi.responses import FileResponse\n dashboard_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), \"dashboard\")\n landing_path = os.path.join(dashboard_path, \"landing.html\")\n\n @application.get(\"/\")\n async def root():\n if os.path.exists(landing_path):\n return FileResponse(landing_path)\n return {\"message\": \"Giyu Agent API is running. Dashboard is at /dashboard\"}\n\n @application.get(\"/landing\")\n async def landing():\n if os.path.exists(landing_path):\n return FileResponse(landing_path)\n return {\"error\": \"Landing page not found\"}\n\n # Register route modules\n from .routes_chat import router as chat_router\n from .routes_code import router as code_router\n from .routes_config import router as config_router\n from .routes_media import router as media_router\n from .routes_stability import router as stability_router\n from fastapi.staticfiles import StaticFiles\n\n application.include_router(chat_router)\n application.include_router(code_router)\n application.include_router(config_router)\n application.include_router(media_router)\n application.include_router(stability_router)\n\n # Mount Dashboard\n dashboard_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), \"dashboard\")\n if os.path.exists(dashboard_path):\n application.mount(\"/dashboard\", StaticFiles(directory=dashboard_path, html=True), name=\"dashboard\")\n\n return application\n\n\n# Module-level app instance (used by uvicorn)\napp = create_app()\n"}}, "giyu/backend/__init__.py": {"38cad57851ca398fb8ebfdbe8d2f3ae2eaf43889": {"t": 1785599328.296758, "n": 251, "h": "ef6af4f1a19af044", "c": "\"\"\"\nGiyu Backend Package.\n\nRe-exports the FastAPI app and key shared state for backward compatibility.\n\"\"\"\nfrom .app import app\nfrom .state import vscode_ipc_context, _pending_file_opens\n\n__all__ = [\"app\", \"vscode_ipc_context\", \"_pending_file_opens\"]\n"}}, "giyu/backend/schemas.py": {"ad88615b7825f62483a7436342e38888871cea75": {"t": 1785599328.2979915, "n": 657, "h": "0bf9a10711b26d58", "c": "\"\"\"\nGiyu Backend — Pydantic request/response schemas.\n\"\"\"\nfrom pydantic import BaseModel\nfrom typing import Optional\n\n\nclass ChatRequest(BaseModel):\n task: str\n session_id: Optional[str] = None\n mode: str = \"auto\" # \"auto\" | \"plan\" | \"fast_ans\"\n\n\nclass ToolResult(BaseModel):\n call_id: str\n result: str\n\n\nclass CompletionRequest(BaseModel):\n file_path: str\n content_before: str\n content_after: str\n\n\nclass CodeActionRequest(BaseModel):\n action: str\n file_path: str\n selected_text: str\n full_text: str\n\n\nclass ConfigUpdate(BaseModel):\n settings: dict\n\n\nclass TTSRequest(BaseModel):\n text: str\n lang: str = \"en\"\n"}}, "giyu/backend/routes_stability.py": {"cf15c63bc12204293d33258acdb5d5ac5606d866": {"t": 1785599328.305641, "n": 1713, "h": "f153e755c9d157c7", "c": "from fastapi import APIRouter\nfrom .state import get_agent\nfrom giyu.cognition.helpers.backbone import _load_backbone\n\nrouter = APIRouter(prefix=\"/stability\", tags=[\"stability\"])\n\n@router.get(\"/status\")\nasync def get_stability_status():\n \"\"\"Returns the current system stability status and escalation state.\"\"\"\n data = _load_backbone()\n reports = data.get(\"stability_reports\", [])\n current_report = reports[-1] if reports else {\n \"score\": 100.0,\n \"state\": \"stable\",\n \"metrics_evaluated\": [\"cpu\", \"ram\", \"disk\"],\n \"timestamp\": 0\n }\n \n return {\n \"current_report\": current_report,\n \"escalation_state\": data.get(\"escalation_state\", {}),\n \"anomalies\": data.get(\"anomalies\", []),\n \"agent_heartbeats\": await _get_agent_heartbeats()\n }\n\n@router.get(\"/history\")\nasync def get_stability_history(limit: int = 50):\n \"\"\"Returns the history of stability reports.\"\"\"\n data = _load_backbone()\n return data.get(\"stability_reports\", [])[-limit:]\n\n@router.get(\"/backbone\")\nasync def get_backbone_slim():\n \"\"\"Returns the backbone context with limited stability history for performance.\"\"\"\n backbone = _load_backbone()\n # Only return the last 5 reports to keep payload light\n if \"stability_reports\" in backbone:\n backbone[\"stability_reports\"] = backbone[\"stability_reports\"][-5:]\n return backbone\n\nasync def _get_agent_heartbeats():\n \"\"\"Helper to get heartbeats of all agents.\"\"\"\n from giyu.tools.stability_tools import AgentHeartbeatMonitor\n monitor = AgentHeartbeatMonitor()\n result = await monitor.execute()\n import ast\n try:\n return ast.literal_eval(result.output)\n except:\n return {}\n"}}, "giyu/backend/routes_code.py": {"70a257d1cb121f1dfb51013e13f57e3c4cd7b411": {"t": 1785599328.325726, "n": 3316, "h": "e5b8a088645beb30", "c": "\"\"\"\nGiyu Backend — Code completion & code-action endpoints.\n\"\"\"\nfrom fastapi import APIRouter\n\nfrom .state import log\nfrom .schemas import CompletionRequest, CodeActionRequest\n\nrouter = APIRouter()\n\n_completion_llm = None\n\n\nasync def _get_completion_llm():\n global _completion_llm\n if _completion_llm is None:\n try:\n from phoenix.services.llm.openai import OpenAILLM\n _completion_llm = OpenAILLM()\n await _completion_llm.init()\n except ImportError:\n log.error(\"Could not import OpenAILLM from phoenix.\")\n return _completion_llm\n\n\n@router.post(\"/completion\")\nasync def code_completion(req: CompletionRequest):\n \"\"\"Generate inline code completions using Phoenix OpenAILLM.\"\"\"\n llm = await _get_completion_llm()\n if not llm:\n return {\"status\": \"error\", \"message\": \"Completion LLM not available.\"}\n\n prompt = f\"\"\"You are an elite AI inline code completion engine.\nYour task is to predict exactly what code should be inserted at the cursor position.\nProvide ONLY the raw code to be inserted. DO NOT include markdown formatting, backticks, or any conversational text.\n\nFile: {req.file_path}\n\nContext before cursor:\n{req.content_before[-1500:]}\n\n<cursor>\n\nContext after cursor:\n{req.content_after[:1500]}\n\"\"\"\n try:\n response = await llm.generate(prompt, max_tokens=150)\n clean = response.strip()\n if clean.startswith(\"```\"):\n clean = \"\\n\".join(clean.split(\"\\n\")[1:])\n if clean.endswith(\"```\"):\n clean = \"\\n\".join(clean.split(\"\\n\")[:-1])\n return {\"status\": \"ok\", \"completion\": clean}\n except Exception as e:\n log.error(f\"Completion failed: {e}\")\n return {\"status\": \"error\", \"message\": str(e)}\n\n\n@router.post(\"/code_action\")\nasync def code_action(req: CodeActionRequest):\n \"\"\"Process context menu AI code actions.\"\"\"\n llm = await _get_completion_llm()\n if not llm:\n return {\"status\": \"error\", \"message\": \"LLM not available.\"}\n\n action_prompts = {\n \"explain\": \"Explain what this code does in a concise, technical manner. Do not provide code blocks, just explain.\",\n \"refactor\": \"Refactor this code to improve readability and maintainability. Return ONLY the new code snippet, with NO markdown formatting.\",\n \"optimize\": \"Optimize this code for performance. Return ONLY the new code snippet, with NO markdown formatting.\",\n \"fix\": \"Fix any bugs or issues in this code. Return ONLY the new code snippet, with NO markdown formatting.\",\n }\n\n instruction = action_prompts.get(req.action, \"Process this code.\")\n\n prompt = f\"\"\"You are an elite AI developer agent.\n{instruction}\n\nFile: {req.file_path}\n\nContext:\n{req.full_text[:3000]}\n\nSelected Code:\n{req.selected_text}\n\"\"\"\n try:\n response = await llm.generate(prompt, max_tokens=1000)\n clean = response.strip()\n if req.action in [\"refactor\", \"optimize\", \"fix\"]:\n if clean.startswith(\"```\"):\n clean = \"\\n\".join(clean.split(\"\\n\")[1:])\n if clean.endswith(\"```\"):\n clean = \"\\n\".join(clean.split(\"\\n\")[:-1])\n return {\"status\": \"ok\", \"result\": clean.strip()}\n except Exception as e:\n log.error(f\"Code action failed: {e}\")\n return {\"status\": \"error\", \"message\": str(e)}\n"}}, "giyu/backend/state.py": {"92c5566a97bdbb7ac13831558b36c5d420823f45": {"t": 1785599328.3818755, "n": 980, "h": "8927e7232f096a21", "c": "\"\"\"\nGiyu Backend — shared server state and context variables.\n\"\"\"\nimport logging\nfrom contextvars import ContextVar\nfrom collections import deque\nfrom typing import Optional\n\nlogging.basicConfig(\n level=logging.WARNING,\n format=\"%(asctime)s %(levelname)s %(name)s — %(message)s\"\n)\nlog = logging.getLogger(\"giyu.server\")\n\n# ── IPC Context ───────────────────────────────────────────────────────────────\n# Allows tools to access the VS Code IPC bridge for the current request\nvscode_ipc_context: ContextVar[Optional[callable]] = ContextVar(\"vscode_ipc\", default=None)\n\n# Thread-safe queue for file-open requests emitted by sync tools\n_pending_file_opens: deque = deque()\n\n# ── Global agent & IPC response futures ───────────────────────────────────────\n_agent = None\n\n# Stores Futures for tool calls waiting for VS Code response: { call_id: Future }\n_ipc_responses: dict = {}\n\n\ndef get_agent():\n return _agent\n\n\ndef set_agent(agent):\n global _agent\n _agent = agent\n"}}, ".env": {"484f416846ef630275672e3fe5e76380c5cfdb86": {"t": 1785599328.5660858, "n": 892, "h": "470d870feb492310", "c": "# --- LLM Provider (OpenAI/LongCat/etc) ---\n# Default is OpenAI. If using LongCat, change BASE_URL and MODEL.\nOPENAI_API_KEY=\"ak_2yp3Xw1Ny7ky2pF7er9x93ZO9jj6G\"\nOPENAI_BASE_URL=\"https://api.longcat.chat/openai\"\nOPENAI_LLM_MODEL=\"LongCat-Flash-Lite\"\n\n# --- Infrastructure ---\nREDIS_URL=\"redis://localhost:6379/0\"\nVECTOR_DB_TYPE=\"chroma\" # \"chroma\" or \"qdrant\"\nCHROMA_PERSIST_DIR=\"./chroma_db\"\nQDRANT_URL=\"http://localhost:6333\"\n\n# --- Local Models (Requires [full] installation) ---\n# Set these to \"true\" to load local models into memory\nLOAD_LOCAL_LLM=false\nLOAD_LOCAL_VLM=false\nLOCAL_LLM_TEXT_MODEL=\"Qwen/Qwen2-1.5B-Instruct\"\nLOCAL_VLM_MODEL=\"Qwen/Qwen2-VL-2B-Instruct\"\n\n# --- RAG & Search ---\nRAG_QUERY_EXPANSION=true\nRAG_HYDE_ENABLED=true\nRAG_MMR_ENABLED=true\nRAG_MAX_CONTEXT_CHUNKS=5\n\n# --- Security ---\nSECURITY_MAX_INPUT_LENGTH=4096\nSECURITY_ENABLE_HALLUCINATION_CHECK=false\n"}, "f8e70afa2d857720cd1eeffbe80c17ad8840cca2": {"t": 1785611420.1809351, "n": 892, "h": "470d870feb492310", "c": "# --- LLM Provider (OpenAI/LongCat/etc) ---\n# Default is OpenAI. If using LongCat, change BASE_URL and MODEL.\nOPENAI_API_KEY=\"ak_2yp3Xw1Ny7ky2pF7er9x93ZO9jj6G\"\nOPENAI_BASE_URL=\"https://api.longcat.chat/openai\"\nOPENAI_LLM_MODEL=\"LongCat-Flash-Lite\"\n\n# --- Infrastructure ---\nREDIS_URL=\"redis://localhost:6379/0\"\nVECTOR_DB_TYPE=\"chroma\" # \"chroma\" or \"qdrant\"\nCHROMA_PERSIST_DIR=\"./chroma_db\"\nQDRANT_URL=\"http://localhost:6333\"\n\n# --- Local Models (Requires [full] installation) ---\n# Set these to \"true\" to load local models into memory\nLOAD_LOCAL_LLM=false\nLOAD_LOCAL_VLM=false\nLOCAL_LLM_TEXT_MODEL=\"Qwen/Qwen2-1.5B-Instruct\"\nLOCAL_VLM_MODEL=\"Qwen/Qwen2-VL-2B-Instruct\"\n\n# --- RAG & Search ---\nRAG_QUERY_EXPANSION=true\nRAG_HYDE_ENABLED=true\nRAG_MMR_ENABLED=true\nRAG_MAX_CONTEXT_CHUNKS=5\n\n# --- Security ---\nSECURITY_MAX_INPUT_LENGTH=4096\nSECURITY_ENABLE_HALLUCINATION_CHECK=false\n"}}, "giyu/backend/routes_media.py": {"45970b77637ce5d2ed112ac3d62bd7fd6f78553e": {"t": 1785599328.7366977, "n": 3146, "h": "f2ca40d5126677fe", "c": "\"\"\"\nGiyu Backend — Text-to-Speech & Speech-to-Text endpoints.\n\"\"\"\nimport os\nimport subprocess\nfrom fastapi import APIRouter\n\nfrom .schemas import TTSRequest\n\nrouter = APIRouter()\n\n# ── Text-to-Speech ────────────────────────────────────────────────────────────\n\n@router.post(\"/tts\")\nasync def text_to_speech(req: TTSRequest):\n \"\"\"Generate speech from text using gTTS and return base64-encoded MP3.\"\"\"\n import io\n import base64\n try:\n from gtts import gTTS\n except ImportError:\n return {\"status\": \"error\", \"message\": \"gTTS not installed. Run: pip install gtts\"}\n\n try:\n text = req.text[:1000].strip()\n tts = gTTS(text=text, lang=req.lang, slow=False)\n buf = io.BytesIO()\n tts.write_to_fp(buf)\n buf.seek(0)\n audio_b64 = base64.b64encode(buf.read()).decode(\"utf-8\")\n return {\"status\": \"ok\", \"audio\": audio_b64}\n except Exception as exc:\n return {\"status\": \"error\", \"message\": str(exc)}\n\n\n# ── Speech-to-Text ────────────────────────────────────────────────────────────\n\n_recording_process = None\n_recording_file = \"/tmp/giyu_recording.wav\"\n\n\n@router.post(\"/stt/start\")\nasync def start_recording():\n \"\"\"Start recording audio using arecord.\"\"\"\n global _recording_process\n\n if _recording_process is not None:\n try:\n _recording_process.terminate()\n _recording_process.wait(timeout=2)\n except Exception:\n pass\n _recording_process = None\n\n try:\n _recording_process = subprocess.Popen(\n [\"arecord\", \"-f\", \"S16_LE\", \"-r\", \"16000\", \"-c\", \"1\", \"-q\", _recording_file],\n stdout=subprocess.DEVNULL,\n stderr=subprocess.DEVNULL,\n )\n return {\"status\": \"ok\", \"message\": \"Recording started\"}\n except Exception as exc:\n return {\"status\": \"error\", \"message\": f\"Failed to start arecord: {exc}\"}\n\n\n@router.post(\"/stt/stop\")\nasync def stop_recording():\n \"\"\"Stop recording and transcribe using SpeechRecognition (Google API).\"\"\"\n global _recording_process\n\n if _recording_process is not None:\n try:\n _recording_process.terminate()\n _recording_process.wait(timeout=2)\n except Exception:\n pass\n _recording_process = None\n\n if not os.path.exists(_recording_file):\n return {\"status\": \"error\", \"message\": \"No recording file found\"}\n\n try:\n import speech_recognition as sr\n recognizer = sr.Recognizer()\n with sr.AudioFile(_recording_file) as source:\n audio = recognizer.record(source)\n\n text = recognizer.recognize_google(audio)\n return {\"status\": \"ok\", \"text\": text}\n except ImportError:\n return {\"status\": \"error\", \"message\": \"SpeechRecognition not installed. Run: pip install SpeechRecognition\"}\n except sr.UnknownValueError:\n return {\"status\": \"error\", \"message\": \"Could not understand audio. Please try again.\"}\n except sr.RequestError as e:\n return {\"status\": \"error\", \"message\": f\"Could not request results; {e}\"}\n except Exception as exc:\n return {\"status\": \"error\", \"message\": str(exc)}\n"}}, "giyu/backend/routes_config.py": {"61203606105f6179d13989ed54378b2da96a11d9": {"t": 1785599328.7453055, "n": 1808, "h": "87a402efab1cbb9e", "c": "\"\"\"\nGiyu Backend — Configuration management endpoints.\n\"\"\"\nimport os\nfrom fastapi import APIRouter\n\nfrom .state import log, set_agent\nfrom .schemas import ConfigUpdate\n\nrouter = APIRouter()\n\n\n@router.get(\"/config\")\nasync def get_config():\n \"\"\"Returns current environment variables for Giyu.\"\"\"\n from pathlib import Path\n from dotenv import load_dotenv\n\n load_dotenv(override=True)\n\n return {\n \"OPENAI_API_KEY\": os.getenv(\"OPENAI_API_KEY\", \"\"),\n \"OPENAI_BASE_URL\": os.getenv(\"OPENAI_BASE_URL\", \"\"),\n \"OPENAI_LLM_MODEL\": os.getenv(\"OPENAI_LLM_MODEL\", \"gpt-4o\"),\n \"GIYU_LOG_LEVEL\": os.getenv(\"LOG_LEVEL\", \"WARNING\"),\n }\n\n\n@router.post(\"/config\")\nasync def update_config(update: ConfigUpdate):\n \"\"\"Updates the .env file with new settings.\"\"\"\n from pathlib import Path\n env_path = Path(\".env\")\n\n lines = []\n if env_path.exists():\n lines = env_path.read_text().splitlines()\n\n new_settings = update.settings\n updated_keys: set = set()\n\n new_lines = []\n for line in lines:\n if \"=\" in line and not line.startswith(\"#\"):\n key = line.split(\"=\")[0].strip()\n if key in new_settings:\n new_lines.append(f\"{key}={new_settings[key]}\")\n updated_keys.add(key)\n continue\n new_lines.append(line)\n\n for key, val in new_settings.items():\n if key not in updated_keys:\n new_lines.append(f\"{key}={val}\")\n\n env_path.write_text(\"\\n\".join(new_lines) + \"\\n\")\n\n # Trigger agent re-initialization\n from giyu.agent import get_giyu_agent\n from dotenv import load_dotenv\n load_dotenv(override=True)\n agent = await get_giyu_agent()\n set_agent(agent)\n\n return {\"status\": \"ok\", \"message\": \"Configuration updated and agent re-initialized.\"}\n"}}, "dashboard/water_simulation.js": {"c3d8974b0dacb7a15cd0ea406d99d60bc81b9797": {"t": 1785599328.7538207, "n": 4972, "h": "eb317676a38cf115", "c": "/**\n * Giyu Optimized 3D Water Simulation — Full-Page Edition\n * Powered by Three.js & Custom GLSL Shaders\n */\n\n(function() {\n const container = document.getElementById('water-canvas-container');\n if (!container) return;\n\n // --- 1. Scene Setup ---\n const scene = new THREE.Scene();\n \n // Low FOV for cinematic depth\n const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 3000);\n camera.position.set(0, 300, 800);\n camera.lookAt(0, 0, 0);\n\n const renderer = new THREE.WebGLRenderer({ \n antialias: false, // Performance boost\n alpha: true,\n powerPreference: \"high-performance\"\n });\n renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // Cap pixel ratio for performance\n renderer.setSize(window.innerWidth, window.innerHeight);\n container.appendChild(renderer.domElement);\n\n // --- 2. Wave Geometry ---\n // Expanded plane to cover full scroll range\n const geometry = new THREE.PlaneGeometry(3000, 3000, 100, 100);\n geometry.rotateX(-Math.PI / 2);\n\n // --- 3. Advanced Water Material ---\n const material = new THREE.ShaderMaterial({\n uniforms: {\n uTime: { value: 0 },\n uScroll: { value: 0 },\n uColorCyan: { value: new THREE.Color(\"#003cff\") },\n uColorPurple: { value: new THREE.Color(\"#020408\") },\n uColorDeep: { value: new THREE.Color(\"#020408\") }\n },\n vertexShader: `\n uniform float uTime;\n uniform float uScroll;\n varying float vElevation;\n varying vec2 vUv;\n\n void main() {\n vUv = uv;\n vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n\n // Apply scroll-based depth movement\n float scrollOffset = uScroll * 800.0;\n \n // Dynamic multi-layered wave logic\n float elevation = sin(modelPosition.x * 0.008 + uTime * 1.2 + scrollOffset * 0.005) * 40.0;\n elevation += sin(modelPosition.z * 0.01 + uTime * 1.0) * 30.0;\n elevation += cos((modelPosition.x + modelPosition.z) * 0.004 + uTime * 0.7) * 45.0;\n \n // Micro-ripples\n elevation += sin(modelPosition.x * 0.05 - uTime * 2.5) * 4.0;\n\n modelPosition.y += elevation;\n // Shift plane back as we scroll to keep water visible\n modelPosition.z -= scrollOffset;\n\n vElevation = elevation;\n\n vec4 viewPosition = viewMatrix * modelPosition;\n gl_Position = projectionMatrix * viewPosition;\n }\n `,\n fragmentShader: `\n uniform vec3 uColorCyan;\n uniform vec3 uColorPurple;\n uniform vec3 uColorDeep;\n varying float vElevation;\n varying vec2 vUv;\n\n void main() {\n // Base depth coloring\n float depthFactor = (vElevation + 60.0) / 120.0;\n \n // Cinematic Gradient: Deep -> Purple -> Cyan\n vec3 color = mix(uColorDeep, uColorPurple, depthFactor * 0.6);\n color = mix(color, uColorCyan, pow(depthFactor, 2.5));\n\n // Add \"Glow\" on wave peaks\n float peakHighlight = smoothstep(30.0, 60.0, vElevation);\n color += peakHighlight * 0.15;\n\n // Edge Softening & Distance Fog\n float distanceFade = smoothstep(1.0, 0.4, vUv.y);\n float alpha = 0.8 * distanceFade;\n \n gl_FragColor = vec4(color, alpha);\n }\n `,\n transparent: true,\n side: THREE.DoubleSide\n });\n\n const mesh = new THREE.Mesh(geometry, material);\n scene.add(mesh);\n\n // --- 4. Interactivity & Optimization ---\n \n // Scroll listener\n let targetScroll = 0;\n window.addEventListener('scroll', () => {\n targetScroll = window.scrollY;\n }, { passive: true });\n\n // Resize handler\n window.addEventListener('resize', () => {\n camera.aspect = window.innerWidth / window.innerHeight;\n camera.updateProjectionMatrix();\n renderer.setSize(window.innerWidth, window.innerHeight);\n });\n\n // --- 5. Animation Loop ---\n const clock = new THREE.Clock();\n let currentScroll = 0;\n\n function animate() {\n const elapsedTime = clock.getElapsedTime();\n \n // Smoothly interpolate scroll for \"Reliable\" feel\n currentScroll += (targetScroll - currentScroll) * 0.08;\n material.uniforms.uScroll.value = currentScroll / window.innerHeight;\n \n material.uniforms.uTime.value = elapsedTime;\n\n // Subtle camera breathing\n camera.position.y = 300 + Math.sin(elapsedTime * 0.4) * 15;\n camera.lookAt(0, 0, 0);\n\n renderer.render(scene, camera);\n requestAnimationFrame(animate);\n }\n\n animate();\n})();\n"}}, "giyu/__init__.py": {"861782d0b86b3d96a485567e7dd28ee6d1ddd9cc": {"t": 1785599328.7551923, "n": 21, "h": "b399f2f07410ff01", "c": "# Giyu Agent Package\n"}}, "giyu/agent.py": {"1c53f4f8b80a19d318eef4cf147ea78135972ced": {"t": 1785599328.7570848, "n": 3726, "h": "9a34cecbaac6a13d", "c": "import os\nfrom dotenv import load_dotenv\n\n# Load .env from project root at module level\nenv_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), \".env\")\nif os.path.exists(env_path):\n load_dotenv(env_path)\n\nfrom phoenix import Agent, init_phoenix, startup_phoenix\nfrom .tools.project_generator import project_generator_tool\nfrom .tools.file_tools import file_read_lines_tool, file_update_multi_tool\n\nasync def get_giyu_agent(on_startup_progress=None, system_info: str = \"\"):\n \"\"\"\n Initializes the Phoenix framework, starts up services, and returns the Giyu agent.\n \"\"\"\n init_phoenix()\n await startup_phoenix()\n from .cognition import GiyuThinker, GiyuPlanner, GiyuReflector, GiyuLoop, GiyuGenerator\n from .cognition.brains.stability_scorer import StabilityScorer\n from .cognition.brains.decision_gate import DecisionGate\n from .cognition.brains.analyzer import GiyuAnalyzer\n from .cognition.brains.correlation_engine import CorrelationEngine\n from phoenix.framework.agent.core.profile import AgentProfile\n # Load Profile\n profile_path = os.path.join(os.path.dirname(__file__), \"profile.json\")\n profile = AgentProfile.from_json(profile_path)\n \n # Create the agent with the task-file driven loop and upgraded cognition modules\n agent = Agent(\n loop_cls=GiyuLoop,\n component_factories={\n \"thinker\": lambda **ctx: GiyuThinker(ctx[\"llm\"], profile=profile),\n \"planner\": lambda **ctx: GiyuPlanner(ctx[\"llm\"], ctx[\"tools\"], system_info=system_info),\n \"reflector\": lambda **ctx: GiyuReflector(ctx[\"llm\"], profile=profile),\n \"generator\": lambda **ctx: GiyuGenerator(ctx[\"llm\"]),\n \"analyzer\": lambda **ctx: GiyuAnalyzer(),\n \"loop\": lambda **ctx: GiyuLoop(\n thinker=ctx[\"thinker\"],\n planner=ctx[\"planner\"],\n actor=ctx[\"actor\"],\n reflector=ctx[\"reflector\"],\n analyzer=ctx[\"analyzer\"] or GiyuAnalyzer(),\n stability_scorer=StabilityScorer(),\n decision_gate=DecisionGate(),\n correlation_engine=CorrelationEngine(ctx[\"llm\"]),\n generator=ctx.get(\"generator\") or GiyuGenerator(ctx[\"llm\"])\n )\n }\n )\n \n from .tools.stability_tools import (\n SystemSnapshotReader, LogStreamAnalyzer, AgentHeartbeatMonitor,\n AnomalyDetector, StabilityScoreCalculator, ProcessStateTracker,\n RollbackRecommendationEngine, CoreEscalationTrigger,\n ResourcePressureMonitor, EventCorrelationTracker, ShellCommandTool,\n SystemSecurityAuditor\n )\n from phoenix.framework.agent.tools import FileReadTool, FileEditTool\n \n # Register core tools\n agent.register_tool(FileReadTool())\n agent.register_tool(FileEditTool())\n \n # Register specialized stability tools\n agent.register_tool(SystemSnapshotReader())\n agent.register_tool(LogStreamAnalyzer())\n agent.register_tool(AgentHeartbeatMonitor())\n agent.register_tool(AnomalyDetector())\n agent.register_tool(StabilityScoreCalculator())\n agent.register_tool(ProcessStateTracker())\n agent.register_tool(RollbackRecommendationEngine())\n agent.register_tool(CoreEscalationTrigger())\n agent.register_tool(ResourcePressureMonitor())\n agent.register_tool(EventCorrelationTracker())\n agent.register_tool(ShellCommandTool())\n agent.register_tool(SystemSecurityAuditor())\n \n # Attach brains to agent for direct access (testing/external logic)\n agent.stability_scorer = agent.loop.stability_scorer\n agent.decision_gate = agent.loop.decision_gate\n agent.correlation_engine = agent.loop.correlation_engine\n \n return agent\n"}}, "dashboard/landing.js": {"68e17e52cfdc0afc424e7b76d9fabd5b5f5c0128": {"t": 1785599328.7701254, "n": 6431, "h": "4e270fa4c524e46e", "c": "/**\n * Giyu Landing Page Logic - World Class Edition\n * GSAP Animations, Custom Cursor, Magnetic Buttons, & Particles\n */\n\n\n\n// --- 2. Magnetic Buttons ---\nconst magneticButtons = document.querySelectorAll('.primary-btn, .secondary-btn, .cta-btn');\n\nmagneticButtons.forEach(btn => {\n btn.addEventListener('mousemove', (e) => {\n const rect = btn.getBoundingClientRect();\n const x = e.clientX - rect.left - rect.width / 2;\n const y = e.clientY - rect.top - rect.height / 2;\n\n gsap.to(btn, {\n x: x * 0.3,\n y: y * 0.3,\n duration: 0.4,\n ease: \"power2.out\"\n });\n });\n\n btn.addEventListener('mouseleave', () => {\n gsap.to(btn, {\n x: 0,\n y: 0,\n duration: 0.7,\n ease: \"elastic.out(1, 0.3)\"\n });\n });\n});\n\n\n// --- 3. Particle Network Background ---\nconst canvas = document.getElementById('net-canvas');\nconst ctx = canvas.getContext('2d');\n\nlet particles = [];\nlet connectionDistance = 160;\nconst mouse = { x: null, y: null, radius: 180 };\n\nfunction resizeCanvas() {\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n connectionDistance = canvas.width < 768 ? 100 : 160;\n}\n\nwindow.addEventListener('resize', resizeCanvas);\nresizeCanvas();\n\nwindow.addEventListener('mousemove', (e) => {\n mouse.x = e.clientX;\n mouse.y = e.clientY;\n});\n\nwindow.addEventListener('mouseout', () => {\n mouse.x = null;\n mouse.y = null;\n});\n\nclass Particle {\n constructor() {\n this.reset();\n }\n\n reset() {\n this.x = Math.random() * canvas.width;\n this.y = Math.random() * canvas.height;\n this.size = Math.random() * 2 + 1;\n this.speedX = (Math.random() - 0.5) * 0.5;\n this.speedY = (Math.random() - 0.5) * 0.5;\n this.color = Math.random() > 0.5 ? '#00f2ff' : '#7000ff';\n this.opacity = Math.random() * 0.5 + 0.2;\n }\n\n update() {\n this.x += this.speedX;\n this.y += this.speedY;\n\n if (this.x > canvas.width) this.x = 0;\n else if (this.x < 0) this.x = canvas.width;\n if (this.y > canvas.height) this.y = 0;\n else if (this.y < 0) this.y = canvas.height;\n\n if (mouse.x !== null) {\n const dx = mouse.x - this.x;\n const dy = mouse.y - this.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n if (dist < mouse.radius) {\n const force = (mouse.radius - dist) / mouse.radius;\n this.x -= (dx / dist) * force * 3;\n this.y -= (dy / dist) * force * 3;\n }\n }\n }\n\n draw() {\n ctx.fillStyle = this.color;\n ctx.globalAlpha = this.opacity;\n ctx.beginPath();\n ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);\n ctx.fill();\n ctx.globalAlpha = 1;\n }\n}\n\nfunction initParticles() {\n const count = Math.min((canvas.width * canvas.height) / 15000, 80);\n particles = [];\n for (let i = 0; i < count; i++) {\n particles.push(new Particle());\n }\n}\n\nfunction animateParticles() {\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n for (let i = 0; i < particles.length; i++) {\n particles[i].update();\n particles[i].draw();\n\n for (let j = i + 1; j < particles.length; j++) {\n const dx = particles[i].x - particles[j].x;\n const dy = particles[i].y - particles[j].y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < connectionDistance) {\n const opacity = (1 - (dist / connectionDistance)) * 0.2;\n const grad = ctx.createLinearGradient(particles[i].x, particles[i].y, particles[j].x, particles[j].y);\n grad.addColorStop(0, particles[i].color);\n grad.addColorStop(1, particles[j].color);\n\n ctx.strokeStyle = grad;\n ctx.globalAlpha = opacity;\n ctx.lineWidth = 0.8;\n ctx.beginPath();\n ctx.moveTo(particles[i].x, particles[i].y);\n ctx.lineTo(particles[j].x, particles[j].y);\n ctx.stroke();\n ctx.globalAlpha = 1;\n }\n }\n }\n requestAnimationFrame(animateParticles);\n}\n\n// --- 4. Preloader & GSAP Initialization ---\nwindow.addEventListener('load', () => {\n // Ensure GSAP plugins are registered\n gsap.registerPlugin(ScrollTrigger);\n\n // Initialize SplitType for hero text\n const heroTitle = new SplitType('.split-text', { types: 'words, chars' });\n\n // Preloader Animation Sequence\n const tlLoader = gsap.timeline();\n\n tlLoader.to('.loader-bar', {\n width: '100%',\n duration: 1.5,\n ease: 'power3.inOut'\n })\n .to('.preloader', {\n yPercent: -100,\n duration: 0.8,\n ease: 'power4.inOut',\n delay: 0.2\n })\n .add(() => {\n // Start particle animation when preloader finishes\n initParticles();\n animateParticles();\n }, \"-=0.4\")\n // Hero Animations\n .from(heroTitle.words, {\n y: 100,\n opacity: 0,\n duration: 1,\n stagger: 0.05,\n ease: 'back.out(1.7)'\n }, \"-=0.2\")\n .from('.fade-in-up', {\n y: 30,\n opacity: 0,\n duration: 1,\n stagger: 0.1,\n ease: 'power3.out'\n }, \"-=0.6\");\n\n // --- 5. Scroll Animations ---\n\n // Feature Cards Stagger\n gsap.from('.feature-card', {\n scrollTrigger: {\n trigger: '.features',\n start: 'top 75%'\n },\n y: 50,\n opacity: 0,\n duration: 0.8,\n stagger: 0.2,\n ease: 'power3.out'\n });\n\n\n // Architecture Section Pin & Reveal\n const archTl = gsap.timeline({\n scrollTrigger: {\n trigger: '.architecture',\n start: 'top 60%',\n end: 'bottom center',\n scrub: 1\n }\n });\n\n archTl.from('.arch-text', { x: -50, opacity: 0, duration: 1 })\n .from('.arch-visual', { x: 50, opacity: 0, duration: 1 }, \"-=1\")\n .from('.status-ring', { scale: 0.5, opacity: 0, rotation: 180, duration: 1.5, ease: 'back.out(1.5)' }, \"-=0.5\")\n .from('.progress-bar .fill', { width: 0, duration: 1, stagger: 0.2 }, \"-=1\");\n});\n\n// Smooth anchor scrolling handled natively by CSS scroll-behavior: smooth\n"}}, "giyu/cli/setup_wizard.py": {"6200dac9f597dcd0e91b3429e1f39c740baff218": {"t": 1785599329.999138, "n": 13508, "h": "49320952eacc00a9", "c": "\"\"\"\nGiyu Agent — First-run Setup Wizard Screen.\nCollects API Key, Model Name, and Base URL, then writes them to .env.\n\"\"\"\n\nfrom textual.app import ComposeResult\nfrom textual.screen import Screen\nfrom textual.widgets import (\n Static, Input, Button, Label\n)\nfrom textual.containers import Container, Vertical, Horizontal, VerticalScroll\nfrom textual.binding import Binding\nfrom textual import on\nfrom textual.reactive import reactive\n\nfrom pathlib import Path\nimport re\n\n\n# ── Configuration Path ────────────────────────────────────────────────────────\n# Use local .env if it exists, otherwise use the global home directory one\nLOCAL_ENV = Path(\".env\")\nGLOBAL_CONFIG_DIR = Path.home() / \".giyu\"\nGLOBAL_ENV = GLOBAL_CONFIG_DIR / \".env\"\n\ndef _get_env_path() -> Path:\n if LOCAL_ENV.exists():\n return LOCAL_ENV\n if not GLOBAL_CONFIG_DIR.exists():\n GLOBAL_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n return GLOBAL_ENV\n\nLOGO_ART = \"\"\"\\\n ╔═╗ ╔═╗╦ ╦╔╗ ╔═╗╦═╗╔╗╔\n ╠═╣ ╚═╗╠═╣╠╩╗║ ║╠╦╝║║║\n ╩ ╩o╚═╝╩ ╩╚═╝╚═╝╩╚═╝╚╝\\\n\"\"\"\n\n\nclass SetupWizard(Screen):\n \"\"\"Full-screen first-run configuration wizard.\"\"\"\n\n BINDINGS = [\n Binding(\"ctrl+q\", \"quit_app\", \"Quit\", show=True),\n Binding(\"escape\", \"quit_app\", \"Quit\", show=False),\n Binding(\"enter\", \"save\", \"Save & Start\", show=False),\n Binding(\"tab\", \"focus_next\", \"Next Field\", show=False),\n ]\n\n DEFAULT_CSS = \"\"\"\n SetupWizard {\n background: #1B1F24;\n align: center middle;\n }\n\n /* ── outer glow card ─────────────────────────────────────────── */\n #wizard-card {\n width: 70;\n height: auto;\n max-height: 90vh;\n background: #1B1F24;\n \n }\n VerticalScroll {\n height: 1fr;\n padding-right: 1;\n scrollbar-color: #2E333A;\n scrollbar-color-hover: #A89F91;\n border: round #2E333A;\n padding: 2 4;\n align: center middle;\n }\n\n /* ── logo ────────────────────────────────────────────────────── */\n #logo {\n text-align: center;\n color: #FF6B00;\n text-style: bold;\n width: 100%;\n margin-bottom: 1;\n }\n\n #logo-sub {\n text-align: center;\n color: #A89F91;\n width: 100%;\n margin-bottom: 2;\n }\n\n /* ── section label ───────────────────────────────────────────── */\n .field-label {\n color: #A89F91;\n text-style: bold;\n margin-bottom: 0;\n padding-left: 1;\n }\n\n .field-hint {\n color: #A89F91;\n opacity: 0.7;\n margin-bottom: 1;\n padding-left: 1;\n }\n\n /* ── inputs ──────────────────────────────────────────────────── */\n Input {\n background: #1B1F24;\n border: round #2E333A;\n color: #F1E9DD;\n margin-bottom: 1;\n width: 100%;\n }\n\n Input:focus {\n border: round #A89F91;\n background: #1B1F24;\n }\n\n Input.error {\n border: round #ff4444;\n }\n\n /* ── divider ─────────────────────────────────────────────────── */\n #divider {\n height: 1;\n color: #2E333A;\n width: 100%;\n content-align: center middle;\n margin-bottom: 1;\n }\n\n /* ── button row ──────────────────────────────────────────────── */\n #btn-row {\n align: center middle;\n width: 100%;\n margin-top: 1;\n height: auto;\n }\n\n #btn-save {\n background: #A89F91;\n color: #1B1F24;\n text-style: bold;\n border: none;\n padding: 0 6;\n min-width: 22;\n }\n\n #btn-save:hover {\n background: #F1E9DD;\n }\n\n #btn-skip {\n background: #1B1F24;\n color: #A89F91;\n border: round #2E333A;\n padding: 0 4;\n margin-left: 2;\n }\n\n #btn-skip:hover {\n color: #F1E9DD;\n border: round #A89F91;\n }\n\n /* ── status message ──────────────────────────────────────────── */\n #status-msg {\n text-align: center;\n width: 100%;\n height: 1;\n margin-top: 1;\n color: transparent;\n }\n\n #status-msg.success {\n color: #39d353;\n text-style: bold;\n }\n\n #status-msg.error {\n color: #ff4444;\n text-style: bold;\n }\n\n /* ── footer hint ─────────────────────────────────────────────── */\n #wizard-footer {\n text-align: center;\n color: #A89F91;\n width: 100%;\n margin-top: 2;\n }\n \"\"\"\n\n def compose(self) -> ComposeResult:\n with Container(id=\"wizard-card\"):\n yield Static(LOGO_ART, id=\"logo\")\n yield Static(\"🐦🔥 Powered by Phoenix AI Framework\", id=\"logo-sub\")\n yield Static(\"─\" * 58, id=\"divider\")\n\n with VerticalScroll():\n yield Label(\"API Key\", classes=\"field-label\")\n yield Label(\"Your OpenAI-compatible API key\", classes=\"field-hint\")\n yield Input(placeholder=\"sk-... or ak_...\", password=True, id=\"inp-api-key\")\n\n yield Label(\"Model Name\", classes=\"field-label\")\n yield Label(\"The model identifier to use\", classes=\"field-hint\")\n yield Input(placeholder=\"gpt-4o / LongCat-Flash-Lite / ...\", id=\"inp-model\")\n\n yield Label(\"API Base URL\", classes=\"field-label\")\n yield Label(\"Leave default for OpenAI, or set a custom endpoint\", classes=\"field-hint\")\n yield Input(placeholder=\"https://api.openai.com/v1\", id=\"inp-base-url\")\n\n yield Label(\"Log Level\", classes=\"field-label\")\n yield Label(\"DEBUG, INFO, WARNING, ERROR\", classes=\"field-hint\")\n yield Input(placeholder=\"INFO\", id=\"inp-log-level\")\n\n yield Label(\"Redis URL\", classes=\"field-label\")\n yield Label(\"URL for Redis cache (e.g. redis://localhost:6379)\", classes=\"field-hint\")\n yield Input(placeholder=\"redis://localhost:6379\", id=\"inp-redis-url\")\n\n yield Label(\"Vector DB Type\", classes=\"field-label\")\n yield Label(\"Type of vector DB (e.g. chroma)\", classes=\"field-hint\")\n yield Input(placeholder=\"chroma\", id=\"inp-vector-db\")\n\n yield Label(\"Chroma Persist Dir\", classes=\"field-label\")\n yield Label(\"Path to save vector embeddings\", classes=\"field-hint\")\n yield Input(placeholder=\"./chroma_db\", id=\"inp-chroma-dir\")\n\n yield Label(\"Embedding Model\", classes=\"field-label\")\n yield Label(\"HuggingFace model name for embeddings\", classes=\"field-hint\")\n yield Input(placeholder=\"all-MiniLM-L6-v2\", id=\"inp-embedding\")\n\n yield Static(\"\", id=\"status-msg\")\n\n with Horizontal(id=\"btn-row\"):\n yield Button(\" Save & Start 🚀\", id=\"btn-save\", variant=\"primary\")\n yield Button(\"Skip →\", id=\"btn-skip\", variant=\"default\")\n\n yield Static(\n \"Tab: next field │ Enter: save │ Ctrl+Q: quit\",\n id=\"wizard-footer\",\n )\n\n def on_mount(self) -> None:\n \"\"\"Pre-fill saved values from .env if they exist.\"\"\"\n existing = _read_env()\n mappings = {\n \"OPENAI_API_KEY\": \"#inp-api-key\",\n \"OPENAI_LLM_MODEL\": \"#inp-model\",\n \"OPENAI_BASE_URL\": \"#inp-base-url\",\n \"LOG_LEVEL\": \"#inp-log-level\",\n \"REDIS_URL\": \"#inp-redis-url\",\n \"VECTOR_DB_TYPE\": \"#inp-vector-db\",\n \"CHROMA_PERSIST_DIR\": \"#inp-chroma-dir\",\n \"EMBEDDING_MODEL\": \"#inp-embedding\"\n }\n for env_key, widget_id in mappings.items():\n if existing.get(env_key):\n try:\n self.query_one(widget_id, Input).value = existing[env_key]\n except Exception:\n pass\n\n # Focus first empty field\n all_fields = list(mappings.values())\n for field_id in all_fields:\n try:\n inp = self.query_one(field_id, Input)\n if not inp.value:\n inp.focus()\n return\n except Exception:\n pass\n self.query_one(\"#inp-api-key\", Input).focus()\n\n # ── actions ───────────────────────────────────────────────────────────────\n\n def action_quit_app(self) -> None:\n self.app.exit()\n\n def action_save(self) -> None:\n self._do_save()\n\n @on(Button.Pressed, \"#btn-save\")\n def handle_save(self) -> None:\n self._do_save()\n\n @on(Button.Pressed, \"#btn-skip\")\n def handle_skip(self) -> None:\n \"\"\"Skip wizard and go straight to chat (using whatever .env has).\"\"\"\n from .chat_screen import ChatScreen\n self.app.switch_screen(ChatScreen())\n\n @on(Input.Submitted)\n def handle_input_submitted(self, event: Input.Submitted) -> None:\n \"\"\"Tab through fields on Enter, save on last field.\"\"\"\n order = [\"#inp-api-key\", \"#inp-model\", \"#inp-base-url\", \"#inp-log-level\", \"#inp-redis-url\", \"#inp-vector-db\", \"#inp-chroma-dir\", \"#inp-embedding\"]\n current_id = f\"#{event.input.id}\"\n idx = order.index(current_id) if current_id in order else -1\n if idx < len(order) - 1:\n self.query_one(order[idx + 1], Input).focus()\n else:\n self._do_save()\n\n # ── helpers ───────────────────────────────────────────────────────────────\n\n def _do_save(self) -> None:\n api_key = self.query_one(\"#inp-api-key\", Input).value.strip()\n model = self.query_one(\"#inp-model\", Input).value.strip()\n base_url = self.query_one(\"#inp-base-url\", Input).value.strip()\n \n log_lvl = self.query_one(\"#inp-log-level\", Input).value.strip()\n redis = self.query_one(\"#inp-redis-url\", Input).value.strip()\n vdb = self.query_one(\"#inp-vector-db\", Input).value.strip()\n cdir = self.query_one(\"#inp-chroma-dir\", Input).value.strip()\n embed = self.query_one(\"#inp-embedding\", Input).value.strip()\n\n # Validation\n errors = []\n if not api_key:\n errors.append(\"#inp-api-key\")\n if not model:\n errors.append(\"#inp-model\")\n\n # Clear previous error state\n all_fields = [\"#inp-api-key\", \"#inp-model\", \"#inp-base-url\", \"#inp-log-level\", \"#inp-redis-url\", \"#inp-vector-db\", \"#inp-chroma-dir\", \"#inp-embedding\"]\n for field_id in all_fields:\n self.query_one(field_id, Input).remove_class(\"error\")\n\n if errors:\n for field_id in errors:\n self.query_one(field_id, Input).add_class(\"error\")\n self._set_status(\"⚠ API Key and Model Name are required.\", \"error\")\n return\n\n # Set defaults\n if not base_url:\n base_url = \"https://api.openai.com/v1\"\n\n updates = {\n \"OPENAI_API_KEY\": api_key,\n \"OPENAI_LLM_MODEL\": model,\n \"OPENAI_BASE_URL\": base_url,\n }\n if log_lvl: updates[\"LOG_LEVEL\"] = log_lvl\n if redis: updates[\"REDIS_URL\"] = redis\n if vdb: updates[\"VECTOR_DB_TYPE\"] = vdb\n if cdir: updates[\"CHROMA_PERSIST_DIR\"] = cdir\n if embed: updates[\"EMBEDDING_MODEL\"] = embed\n\n # Write to .env\n _write_env(updates)\n\n self._set_status(\"✓ Configuration saved successfully!\", \"success\")\n\n # Brief pause then switch to chat\n self.set_timer(0.8, self._launch_chat)\n\n def _launch_chat(self) -> None:\n # If we were pushed (as a settings screen), just dismiss\n if len(self.app.screen_stack) > 1:\n self.dismiss(True)\n else:\n # First run: switch to chat\n from .chat_screen import ChatScreen\n self.app.switch_screen(ChatScreen())\n\n def _set_status(self, msg: str, kind: str) -> None:\n widget = self.query_one(\"#status-msg\", Static)\n widget.update(msg)\n widget.remove_class(\"success\", \"error\")\n widget.add_class(kind)\n\n\n# ── .env helpers ──────────────────────────────────────────────────────────────\n\ndef _read_env() -> dict:\n \"\"\"Parse .env into a dict (simple key=value, ignoring comments).\"\"\"\n env_path = _get_env_path()\n result = {}\n if not env_path.exists():\n return result\n for line in env_path.read_text(encoding=\"utf-8\").splitlines():\n line = line.strip()\n if not line or line.startswith(\"#\"):\n continue\n m = re.match(r'^([A-Z0-9_]+)\\s*=\\s*\"?(.*?)\"?\\s*$', line)\n if m:\n result[m.group(1)] = m.group(2)\n return result\n\n\ndef _write_env(updates: dict) -> None:\n \"\"\"Upsert keys into .env, preserving all other lines and comments.\"\"\"\n env_path = _get_env_path()\n lines: list[str] = []\n if env_path.exists():\n lines = env_path.read_text(encoding=\"utf-8\").splitlines()\n\n written_keys: set[str] = set()\n new_lines: list[str] = []\n\n for line in lines:\n stripped = line.strip()\n if stripped and not stripped.startswith(\"#\"):\n m = re.match(r'^([A-Z0-9_]+)\\s*=', stripped)\n if m and m.group(1) in updates:\n key = m.group(1)\n new_lines.append(f'{key}=\"{updates[key]}\"')\n written_keys.add(key)\n continue\n new_lines.append(line)\n\n # Append any keys not already in the file\n for key, val in updates.items():\n if key not in written_keys:\n new_lines.append(f'{key}=\"{val}\"')\n\n env_path.write_text(\"\\n\".join(new_lines) + \"\\n\", encoding=\"utf-8\")\n"}, "8375b86c7374b2a33c6722847b289ee03e84c67a": {"t": 1785674299.6807787, "n": 13508, "h": "49320952eacc00a9", "c": "\"\"\"\nGiyu Agent — First-run Setup Wizard Screen.\nCollects API Key, Model Name, and Base URL, then writes them to .env.\n\"\"\"\n\nfrom textual.app import ComposeResult\nfrom textual.screen import Screen\nfrom textual.widgets import (\n Static, Input, Button, Label\n)\nfrom textual.containers import Container, Vertical, Horizontal, VerticalScroll\nfrom textual.binding import Binding\nfrom textual import on\nfrom textual.reactive import reactive\n\nfrom pathlib import Path\nimport re\n\n\n# ── Configuration Path ────────────────────────────────────────────────────────\n# Use local .env if it exists, otherwise use the global home directory one\nLOCAL_ENV = Path(\".env\")\nGLOBAL_CONFIG_DIR = Path.home() / \".giyu\"\nGLOBAL_ENV = GLOBAL_CONFIG_DIR / \".env\"\n\ndef _get_env_path() -> Path:\n if LOCAL_ENV.exists():\n return LOCAL_ENV\n if not GLOBAL_CONFIG_DIR.exists():\n GLOBAL_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n return GLOBAL_ENV\n\nLOGO_ART = \"\"\"\\\n ╔═╗ ╔═╗╦ ╦╔╗ ╔═╗╦═╗╔╗╔\n ╠═╣ ╚═╗╠═╣╠╩╗║ ║╠╦╝║║║\n ╩ ╩o╚═╝╩ ╩╚═╝╚═╝╩╚═╝╚╝\\\n\"\"\"\n\n\nclass SetupWizard(Screen):\n \"\"\"Full-screen first-run configuration wizard.\"\"\"\n\n BINDINGS = [\n Binding(\"ctrl+q\", \"quit_app\", \"Quit\", show=True),\n Binding(\"escape\", \"quit_app\", \"Quit\", show=False),\n Binding(\"enter\", \"save\", \"Save & Start\", show=False),\n Binding(\"tab\", \"focus_next\", \"Next Field\", show=False),\n ]\n\n DEFAULT_CSS = \"\"\"\n SetupWizard {\n background: #1B1F24;\n align: center middle;\n }\n\n /* ── outer glow card ─────────────────────────────────────────── */\n #wizard-card {\n width: 70;\n height: auto;\n max-height: 90vh;\n background: #1B1F24;\n \n }\n VerticalScroll {\n height: 1fr;\n padding-right: 1;\n scrollbar-color: #2E333A;\n scrollbar-color-hover: #A89F91;\n border: round #2E333A;\n padding: 2 4;\n align: center middle;\n }\n\n /* ── logo ────────────────────────────────────────────────────── */\n #logo {\n text-align: center;\n color: #FF6B00;\n text-style: bold;\n width: 100%;\n margin-bottom: 1;\n }\n\n #logo-sub {\n text-align: center;\n color: #A89F91;\n width: 100%;\n margin-bottom: 2;\n }\n\n /* ── section label ───────────────────────────────────────────── */\n .field-label {\n color: #A89F91;\n text-style: bold;\n margin-bottom: 0;\n padding-left: 1;\n }\n\n .field-hint {\n color: #A89F91;\n opacity: 0.7;\n margin-bottom: 1;\n padding-left: 1;\n }\n\n /* ── inputs ──────────────────────────────────────────────────── */\n Input {\n background: #1B1F24;\n border: round #2E333A;\n color: #F1E9DD;\n margin-bottom: 1;\n width: 100%;\n }\n\n Input:focus {\n border: round #A89F91;\n background: #1B1F24;\n }\n\n Input.error {\n border: round #ff4444;\n }\n\n /* ── divider ─────────────────────────────────────────────────── */\n #divider {\n height: 1;\n color: #2E333A;\n width: 100%;\n content-align: center middle;\n margin-bottom: 1;\n }\n\n /* ── button row ──────────────────────────────────────────────── */\n #btn-row {\n align: center middle;\n width: 100%;\n margin-top: 1;\n height: auto;\n }\n\n #btn-save {\n background: #A89F91;\n color: #1B1F24;\n text-style: bold;\n border: none;\n padding: 0 6;\n min-width: 22;\n }\n\n #btn-save:hover {\n background: #F1E9DD;\n }\n\n #btn-skip {\n background: #1B1F24;\n color: #A89F91;\n border: round #2E333A;\n padding: 0 4;\n margin-left: 2;\n }\n\n #btn-skip:hover {\n color: #F1E9DD;\n border: round #A89F91;\n }\n\n /* ── status message ──────────────────────────────────────────── */\n #status-msg {\n text-align: center;\n width: 100%;\n height: 1;\n margin-top: 1;\n color: transparent;\n }\n\n #status-msg.success {\n color: #39d353;\n text-style: bold;\n }\n\n #status-msg.error {\n color: #ff4444;\n text-style: bold;\n }\n\n /* ── footer hint ─────────────────────────────────────────────── */\n #wizard-footer {\n text-align: center;\n color: #A89F91;\n width: 100%;\n margin-top: 2;\n }\n \"\"\"\n\n def compose(self) -> ComposeResult:\n with Container(id=\"wizard-card\"):\n yield Static(LOGO_ART, id=\"logo\")\n yield Static(\"🐦🔥 Powered by Phoenix AI Framework\", id=\"logo-sub\")\n yield Static(\"─\" * 58, id=\"divider\")\n\n with VerticalScroll():\n yield Label(\"API Key\", classes=\"field-label\")\n yield Label(\"Your OpenAI-compatible API key\", classes=\"field-hint\")\n yield Input(placeholder=\"sk-... or ak_...\", password=True, id=\"inp-api-key\")\n\n yield Label(\"Model Name\", classes=\"field-label\")\n yield Label(\"The model identifier to use\", classes=\"field-hint\")\n yield Input(placeholder=\"gpt-4o / LongCat-Flash-Lite / ...\", id=\"inp-model\")\n\n yield Label(\"API Base URL\", classes=\"field-label\")\n yield Label(\"Leave default for OpenAI, or set a custom endpoint\", classes=\"field-hint\")\n yield Input(placeholder=\"https://api.openai.com/v1\", id=\"inp-base-url\")\n\n yield Label(\"Log Level\", classes=\"field-label\")\n yield Label(\"DEBUG, INFO, WARNING, ERROR\", classes=\"field-hint\")\n yield Input(placeholder=\"INFO\", id=\"inp-log-level\")\n\n yield Label(\"Redis URL\", classes=\"field-label\")\n yield Label(\"URL for Redis cache (e.g. redis://localhost:6379)\", classes=\"field-hint\")\n yield Input(placeholder=\"redis://localhost:6379\", id=\"inp-redis-url\")\n\n yield Label(\"Vector DB Type\", classes=\"field-label\")\n yield Label(\"Type of vector DB (e.g. chroma)\", classes=\"field-hint\")\n yield Input(placeholder=\"chroma\", id=\"inp-vector-db\")\n\n yield Label(\"Chroma Persist Dir\", classes=\"field-label\")\n yield Label(\"Path to save vector embeddings\", classes=\"field-hint\")\n yield Input(placeholder=\"./chroma_db\", id=\"inp-chroma-dir\")\n\n yield Label(\"Embedding Model\", classes=\"field-label\")\n yield Label(\"HuggingFace model name for embeddings\", classes=\"field-hint\")\n yield Input(placeholder=\"all-MiniLM-L6-v2\", id=\"inp-embedding\")\n\n yield Static(\"\", id=\"status-msg\")\n\n with Horizontal(id=\"btn-row\"):\n yield Button(\" Save & Start 🚀\", id=\"btn-save\", variant=\"primary\")\n yield Button(\"Skip →\", id=\"btn-skip\", variant=\"default\")\n\n yield Static(\n \"Tab: next field │ Enter: save │ Ctrl+Q: quit\",\n id=\"wizard-footer\",\n )\n\n def on_mount(self) -> None:\n \"\"\"Pre-fill saved values from .env if they exist.\"\"\"\n existing = _read_env()\n mappings = {\n \"OPENAI_API_KEY\": \"#inp-api-key\",\n \"OPENAI_LLM_MODEL\": \"#inp-model\",\n \"OPENAI_BASE_URL\": \"#inp-base-url\",\n \"LOG_LEVEL\": \"#inp-log-level\",\n \"REDIS_URL\": \"#inp-redis-url\",\n \"VECTOR_DB_TYPE\": \"#inp-vector-db\",\n \"CHROMA_PERSIST_DIR\": \"#inp-chroma-dir\",\n \"EMBEDDING_MODEL\": \"#inp-embedding\"\n }\n for env_key, widget_id in mappings.items():\n if existing.get(env_key):\n try:\n self.query_one(widget_id, Input).value = existing[env_key]\n except Exception:\n pass\n\n # Focus first empty field\n all_fields = list(mappings.values())\n for field_id in all_fields:\n try:\n inp = self.query_one(field_id, Input)\n if not inp.value:\n inp.focus()\n return\n except Exception:\n pass\n self.query_one(\"#inp-api-key\", Input).focus()\n\n # ── actions ───────────────────────────────────────────────────────────────\n\n def action_quit_app(self) -> None:\n self.app.exit()\n\n def action_save(self) -> None:\n self._do_save()\n\n @on(Button.Pressed, \"#btn-save\")\n def handle_save(self) -> None:\n self._do_save()\n\n @on(Button.Pressed, \"#btn-skip\")\n def handle_skip(self) -> None:\n \"\"\"Skip wizard and go straight to chat (using whatever .env has).\"\"\"\n from .chat_screen import ChatScreen\n self.app.switch_screen(ChatScreen())\n\n @on(Input.Submitted)\n def handle_input_submitted(self, event: Input.Submitted) -> None:\n \"\"\"Tab through fields on Enter, save on last field.\"\"\"\n order = [\"#inp-api-key\", \"#inp-model\", \"#inp-base-url\", \"#inp-log-level\", \"#inp-redis-url\", \"#inp-vector-db\", \"#inp-chroma-dir\", \"#inp-embedding\"]\n current_id = f\"#{event.input.id}\"\n idx = order.index(current_id) if current_id in order else -1\n if idx < len(order) - 1:\n self.query_one(order[idx + 1], Input).focus()\n else:\n self._do_save()\n\n # ── helpers ───────────────────────────────────────────────────────────────\n\n def _do_save(self) -> None:\n api_key = self.query_one(\"#inp-api-key\", Input).value.strip()\n model = self.query_one(\"#inp-model\", Input).value.strip()\n base_url = self.query_one(\"#inp-base-url\", Input).value.strip()\n \n log_lvl = self.query_one(\"#inp-log-level\", Input).value.strip()\n redis = self.query_one(\"#inp-redis-url\", Input).value.strip()\n vdb = self.query_one(\"#inp-vector-db\", Input).value.strip()\n cdir = self.query_one(\"#inp-chroma-dir\", Input).value.strip()\n embed = self.query_one(\"#inp-embedding\", Input).value.strip()\n\n # Validation\n errors = []\n if not api_key:\n errors.append(\"#inp-api-key\")\n if not model:\n errors.append(\"#inp-model\")\n\n # Clear previous error state\n all_fields = [\"#inp-api-key\", \"#inp-model\", \"#inp-base-url\", \"#inp-log-level\", \"#inp-redis-url\", \"#inp-vector-db\", \"#inp-chroma-dir\", \"#inp-embedding\"]\n for field_id in all_fields:\n self.query_one(field_id, Input).remove_class(\"error\")\n\n if errors:\n for field_id in errors:\n self.query_one(field_id, Input).add_class(\"error\")\n self._set_status(\"⚠ API Key and Model Name are required.\", \"error\")\n return\n\n # Set defaults\n if not base_url:\n base_url = \"https://api.openai.com/v1\"\n\n updates = {\n \"OPENAI_API_KEY\": api_key,\n \"OPENAI_LLM_MODEL\": model,\n \"OPENAI_BASE_URL\": base_url,\n }\n if log_lvl: updates[\"LOG_LEVEL\"] = log_lvl\n if redis: updates[\"REDIS_URL\"] = redis\n if vdb: updates[\"VECTOR_DB_TYPE\"] = vdb\n if cdir: updates[\"CHROMA_PERSIST_DIR\"] = cdir\n if embed: updates[\"EMBEDDING_MODEL\"] = embed\n\n # Write to .env\n _write_env(updates)\n\n self._set_status(\"✓ Configuration saved successfully!\", \"success\")\n\n # Brief pause then switch to chat\n self.set_timer(0.8, self._launch_chat)\n\n def _launch_chat(self) -> None:\n # If we were pushed (as a settings screen), just dismiss\n if len(self.app.screen_stack) > 1:\n self.dismiss(True)\n else:\n # First run: switch to chat\n from .chat_screen import ChatScreen\n self.app.switch_screen(ChatScreen())\n\n def _set_status(self, msg: str, kind: str) -> None:\n widget = self.query_one(\"#status-msg\", Static)\n widget.update(msg)\n widget.remove_class(\"success\", \"error\")\n widget.add_class(kind)\n\n\n# ── .env helpers ──────────────────────────────────────────────────────────────\n\ndef _read_env() -> dict:\n \"\"\"Parse .env into a dict (simple key=value, ignoring comments).\"\"\"\n env_path = _get_env_path()\n result = {}\n if not env_path.exists():\n return result\n for line in env_path.read_text(encoding=\"utf-8\").splitlines():\n line = line.strip()\n if not line or line.startswith(\"#\"):\n continue\n m = re.match(r'^([A-Z0-9_]+)\\s*=\\s*\"?(.*?)\"?\\s*$', line)\n if m:\n result[m.group(1)] = m.group(2)\n return result\n\n\ndef _write_env(updates: dict) -> None:\n \"\"\"Upsert keys into .env, preserving all other lines and comments.\"\"\"\n env_path = _get_env_path()\n lines: list[str] = []\n if env_path.exists():\n lines = env_path.read_text(encoding=\"utf-8\").splitlines()\n\n written_keys: set[str] = set()\n new_lines: list[str] = []\n\n for line in lines:\n stripped = line.strip()\n if stripped and not stripped.startswith(\"#\"):\n m = re.match(r'^([A-Z0-9_]+)\\s*=', stripped)\n if m and m.group(1) in updates:\n key = m.group(1)\n new_lines.append(f'{key}=\"{updates[key]}\"')\n written_keys.add(key)\n continue\n new_lines.append(line)\n\n # Append any keys not already in the file\n for key, val in updates.items():\n if key not in written_keys:\n new_lines.append(f'{key}=\"{val}\"')\n\n env_path.write_text(\"\\n\".join(new_lines) + \"\\n\", encoding=\"utf-8\")\n"}}, "giyu/cognition/brains/analyzer.py": {"e91ac44beb93ce7c4efe4f1abaccb2379ca1bc30": {"t": 1785599330.0276759, "n": 1949, "h": "1bc7c8fe3735d2e4", "c": "from typing import Dict, List, Any\nimport re\n\nclass GiyuAnalyzer:\n \"\"\"\n Detects patterns, anomalies, correlations in system metrics and logs.\n Rule-based + statistical + heuristics (No LLM).\n \"\"\"\n def __init__(self):\n self.anomaly_threshold = 2.0 # Standard deviation multiplier or similar heuristic\n self.critical_patterns = [\n r\"OutOfMemoryError\",\n r\"Segmentation fault\",\n r\"Critical: System Failure\",\n r\"Connection timeout\",\n r\"Database connection failed\"\n ]\n\n def analyze_logs(self, logs: List[str]) -> Dict[str, Any]:\n detected_patterns = []\n for log in logs:\n for pattern in self.critical_patterns:\n if re.search(pattern, log, re.IGNORECASE):\n detected_patterns.append({\"pattern\": pattern, \"log\": log})\n \n return {\n \"found_anomalies\": len(detected_patterns) > 0,\n \"patterns\": detected_patterns,\n \"count\": len(detected_patterns)\n }\n\n def analyze_metrics_trend(self, history: List[float]) -> Dict[str, Any]:\n \"\"\"Simple trend analysis for spikes.\"\"\"\n if len(history) < 2:\n return {\"trend\": \"stable\", \"spike_detected\": False}\n \n last = history[-1]\n prev = history[-2]\n \n change = (last - prev) / (prev if prev != 0 else 1)\n \n return {\n \"trend\": \"rising\" if change > 0.1 else \"falling\" if change < -0.1 else \"stable\",\n \"spike_detected\": change > 0.5,\n \"percent_change\": round(change * 100, 2)\n }\n\n async def analyze_workspace(self, prompt: str) -> str:\n \"\"\"\n Mock implementation of workspace analysis for the Stability Guardian.\n Returns a high-level summary of the monitored environment.\n \"\"\"\n return \"Workspace Context: Monitor Mode Active. Focus on stability, logs, and processes.\"\n\n"}}, "giyu/cli/splash_screen.py": {"0f09da25dcc55f139942ff11962682a9eee9028c": {"t": 1785599330.0314612, "n": 1720, "h": "5b0d710befb8ba99", "c": "\"\"\"\nGiyu Agent — Cinematic Splash Screen.\n\"\"\"\n\nimport asyncio\nfrom textual.app import ComposeResult\nfrom textual.screen import Screen\nfrom textual.widgets import Static\nfrom textual.containers import Center, Middle\nfrom textual.reactive import reactive\nfrom rich.text import Text\n\nclass SplashScreen(Screen):\n \"\"\"A stunning splash screen that appears on launch.\"\"\"\n\n DEFAULT_CSS = \"\"\"\n SplashScreen {\n background: #020408;\n align: center middle;\n }\n\n #splash-logo {\n color: #00f2ff;\n text-style: bold;\n text-align: center;\n width: auto;\n content-align: center middle;\n }\n\n #splash-subtitle {\n color: #94a3b8;\n text-align: center;\n width: auto;\n margin-top: 1;\n text-style: italic;\n }\n \"\"\"\n\n def compose(self) -> ComposeResult:\n with Middle():\n with Center():\n yield Static(\"\", id=\"splash-logo\")\n yield Static(\"powered by phoenix-ai engine\", id=\"splash-subtitle\")\n\n def on_mount(self) -> None:\n self._animate_aura()\n\n def _animate_aura(self) -> None:\n logo = (\n \" ██████╗ ██╗██╗ ██╗██╗ ██╗\\n\"\n \"██╔════╝ ██║╚██╗ ██╔╝██║ ██║\\n\"\n \"██║ ███╗██║ ╚████╔╝ ██║ ██║\\n\"\n \"██║ ██║██║ ╚██╔╝ ██║ ██║\\n\"\n \"╚██████╔╝██║ ██║ ╚██████╔╝\\n\"\n \" ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ \\n\"\n \" S T A B I L I T Y S E N T I N E L\"\n )\n self.query_one(\"#splash-logo\", Static).update(Text(logo, style=\"bold #00f2ff\"))\n self.query_one(\"#splash-subtitle\", Static).update(\"powerd by phoenix-ai\")\n\n def _dismiss(self) -> None:\n self.app.pop_screen()\n"}}, "giyu/cli/chat_screen.py": {"600bb7f0172066ef2e039f3f0990d69a383cffc7": {"t": 1785599330.2317896, "n": 26939, "h": "b1234507f5b74015", "c": "\"\"\"\nGiyu Agent — Main Full-Screen Chat UI.\n\nLayout:\n ┌─ Header ─────────────────────────────────────────────────────┐\n │ 🐦🔥 GIYU │ model name │ status badge │\n ├─ Body ──────────────────────────────────────────────────────┤\n │ Sidebar (collapsible) │ Chat window (scrollable messages) │\n │ │ [ThinkingSpinner — animated] │\n ├─ Input bar ──────────────────────────────────────────────────┤\n │ > prompt here... [nn chars] │\n ├─ Footer ─────────────────────────────────────────────────────┤\n │ Ctrl+Enter: Send Ctrl+L: Clear Ctrl+K: Config Ctrl+Q: Quit │\n └──────────────────────────────────────────────────────────────┘\n\"\"\"\n\nimport asyncio\nfrom datetime import datetime\nfrom textual.app import ComposeResult\nfrom textual.screen import Screen\nfrom textual.widgets import Static, TextArea, RichLog, Label, Select, Button\nfrom textual.containers import Container, Vertical, Horizontal, VerticalScroll\nfrom textual.binding import Binding\nfrom textual.reactive import reactive\nfrom textual import on, work\nfrom textual.worker import Worker\nfrom textual.message import Message\nfrom rich.text import Text\nfrom rich.markdown import Markdown\n\nimport os\nimport subprocess\nfrom pathlib import Path\nfrom dotenv import load_dotenv\n\n\ndef robust_copy(app, text: str) -> bool:\n \"\"\"\n Attempts to copy text to clipboard using OSC 52 (Textual default)\n with a fallback to xclip on Linux.\n \"\"\"\n try:\n # 1. Try Textual's built-in (OSC 52)\n app.copy_to_clipboard(text)\n \n # 2. Linux Fallback: xclip\n if os.name == \"posix\":\n try:\n subprocess.run(\n ['xclip', '-selection', 'clipboard'],\n input=text.encode('utf-8'),\n check=False,\n stderr=subprocess.DEVNULL,\n stdout=subprocess.DEVNULL\n )\n except Exception:\n pass\n return True\n except Exception:\n return False\n\n\n# ── Message data class ────────────────────────────────────────────────────────\n\nclass ChatMessage:\n def __init__(self, role: str, content: str):\n self.role = role\n self.content = content\n self.timestamp = datetime.now().strftime(\"%H:%M\")\n\n\n# ── Animated thinking spinner ─────────────────────────────────────────────────\n\nclass ThinkingSpinner(Static):\n \"\"\"Braille-frame animated spinner — shown inside chat area while agent thinks.\"\"\"\n\n FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\n DEFAULT_CSS = \"\"\"\n ThinkingSpinner {\n height: 0;\n background: $surface;\n border-top: round $primary 20%;\n border-bottom: round $primary 20%;\n color: $text;\n text-style: bold;\n padding: 0 2;\n margin: 1 4;\n outline: solid $primary 10%;\n opacity: 0;\n transition: height 300ms, opacity 300ms;\n }\n ThinkingSpinner.visible {\n height: 3;\n opacity: 1;\n min-height: 3;\n }\n \"\"\"\n\n _frame_idx: int = 0\n _label: str = \"Giyu is thinking\"\n\n def on_mount(self) -> None:\n self.set_interval(0.09, self._tick)\n\n def _tick(self) -> None:\n if \"visible\" not in self.classes:\n return\n self._frame_idx = (self._frame_idx + 1) % len(self.FRAMES)\n frame = self.FRAMES[self._frame_idx]\n dots = \".\" * ((self._frame_idx % 3) + 1)\n self.update(\n Text.from_markup(\n f\" [bold #00f2ff]{frame}[/] [#8B949E]{self._label}[/][dim #7000ff]{dots}[/]\"\n )\n )\n\n def show(self, label: str = \"Giyu is thinking\") -> None:\n self._label = label\n self.add_class(\"visible\")\n\n def hide(self) -> None:\n self.remove_class(\"visible\")\n self.update(\"\")\n\n\n# ── Message Display Widget with Copy Button ───────────────────────────────────\n\nclass MessageDisplay(Horizontal):\n \"\"\"A widget to display a single message with a copy button.\"\"\"\n \n DEFAULT_CSS = \"\"\"\n MessageDisplay {\n height: auto;\n margin-bottom: 1;\n padding: 0 2;\n opacity: 0;\n }\n MessageDisplay.mounted {\n opacity: 1;\n transition: opacity 500ms;\n }\n .msg-content-container {\n width: 1fr;\n height: auto;\n background: $surface;\n border: round $border;\n padding: 0 1;\n }\n .msg-content-container.user {\n background: $surface;\n border: round $primary;\n margin-left: 10;\n }\n .msg-content-container.assistant {\n background: $panel;\n border: round $secondary;\n margin-right: 10;\n outline: solid $secondary 10%;\n }\n .msg-header {\n height: 1;\n margin: 0 1;\n color: $text-muted;\n text-style: bold;\n }\n .msg-body {\n height: auto;\n padding: 0 1;\n color: $text;\n }\n .msg-btn {\n width: 8;\n min-width: 8;\n height: 3;\n margin-left: 1;\n background: $surface;\n border: round $secondary;\n color: $text;\n text-style: bold;\n padding: 0;\n }\n .msg-btn:hover {\n background: $secondary;\n color: white;\n border: round $primary;\n }\n .msg-action-column {\n width: 9;\n height: auto;\n align: center top;\n }\n \"\"\"\n\n def __init__(self, message: ChatMessage, **kwargs):\n super().__init__(**kwargs)\n self.message = message\n\n def on_mount(self) -> None:\n self.add_class(\"mounted\")\n\n def compose(self) -> ComposeResult:\n role_class = self.message.role\n with Vertical(classes=f\"msg-content-container {role_class}\"):\n # Header with role and timestamp\n if self.message.role == \"user\":\n yield Label(f\"YOU • {self.message.timestamp}\", classes=\"msg-header\")\n else:\n yield Label(f\"GIYU • {self.message.timestamp}\", classes=\"msg-header\")\n \n # Content\n content = self.message.content\n if self.message.role == \"user\":\n safe_content = content.replace(\"[\", \"[[\")\n yield Static(safe_content, classes=\"msg-body\")\n else:\n yield Static(Markdown(content), classes=\"msg-body\")\n\n if self.message.role == \"assistant\":\n with Vertical(classes=\"msg-action-column\"):\n yield Button(\"Copy\", classes=\"msg-btn\", id=\"copy-btn\")\n yield Button(\"Reply\", classes=\"msg-btn\", id=\"reply-btn\")\n\n def on_button_pressed(self, event: Button.Pressed) -> None:\n if event.button.id == \"copy-btn\":\n if robust_copy(self.app, self.message.content):\n self.notify(\"✓ Copied to clipboard\", severity=\"information\")\n else:\n self.notify(\"⚠ Copy failed\", severity=\"error\")\n elif event.button.id == \"reply-btn\":\n # Quote the message in the input area\n input_bar = self.screen.query_one(\"#chat-input\", ChatTextArea)\n quote = f\"> {self.message.content.splitlines()[0]}...\\n\"\n input_bar.load_text(quote)\n input_bar.focus()\n self.notify(\"↩ Quoted response\", severity=\"information\")\n\n\n# ── Custom TextArea — fires SendMessage on Ctrl+Enter ─────────────────────────\n\nclass ChatTextArea(TextArea):\n \"\"\"TextArea that fires a SendMessage event on Ctrl+Enter.\"\"\"\n\n class SendMessage(Message):\n \"\"\"Posted when user presses Ctrl+Enter.\"\"\"\n bubble = True\n\n def on_key(self, event) -> None:\n # Ctrl+Y: Copy selection if exists, else bubble to screen for \"Copy Last\"\n if event.key == \"ctrl+y\":\n if self.selected_text:\n if robust_copy(self.app, self.selected_text):\n self.notify(\"✓ Selection copied\", severity=\"information\")\n else:\n self.notify(\"⚠ Copy failed\", severity=\"error\")\n event.stop()\n event.prevent_default()\n return\n # No selection -> let it bubble to ChatScreen.action_copy_last\n return\n\n # Plain Enter or Ctrl+J (fallback) sends the message\n if event.key in (\"enter\", \"ctrl+j\"):\n event.prevent_default()\n event.stop()\n self.post_message(self.SendMessage())\n # Ctrl+Enter or Shift+Enter inserts a newline\n elif event.key in (\"ctrl+enter\", \"shift+enter\"):\n # Manually insert a newline since we stopped the default\n self.insert(\"\\n\")\n event.stop()\n\n\n# ── Sidebar ───────────────────────────────────────────────────────────────────\n\nclass SidebarWidget(Vertical):\n \"\"\"Collapsible left sidebar — session stats & keyboard shortcuts.\"\"\"\n\n DEFAULT_CSS = \"\"\"\n SidebarWidget {\n width: 28;\n background: $surface;\n border-right: thick $border;\n padding: 1 2;\n margin: 1 0 1 1;\n outline: solid $primary 10%;\n display: none;\n }\n SidebarWidget.visible { display: block; }\n\n .sb-title {\n color: $secondary;\n text-style: bold;\n text-align: center;\n width: 100%;\n margin-bottom: 1;\n }\n .sb-div {\n color: $border;\n width: 100%;\n margin-bottom: 1;\n }\n .sb-label {\n color: $text-muted;\n width: 100%;\n }\n .sb-val {\n color: $text;\n text-style: bold;\n width: 100%;\n margin-bottom: 1;\n }\n .sc-row {\n color: $text-muted;\n width: 100%;\n padding: 0 1;\n }\n \"\"\"\n\n message_count: reactive[int] = reactive(0)\n status: reactive[str] = reactive(\"● Initializing\")\n model_name: reactive[str] = reactive(\"\")\n\n def compose(self) -> ComposeResult:\n yield Static(\"[bold #7000ff]🌊 GIYU AGENT[/]\", classes=\"sb-title\")\n yield Static(\"─\" * 22, classes=\"sb-div\")\n\n yield Static(\"Model\", classes=\"sb-label\")\n yield Static(\"—\", id=\"sb-model\", classes=\"sb-val\")\n\n yield Static(\"Status\", classes=\"sb-label\")\n yield Static(\"● Initializing\", id=\"sb-status\", classes=\"sb-val\")\n\n yield Static(\"Messages\", classes=\"sb-label\")\n yield Static(\"0\", id=\"sb-msgs\", classes=\"sb-val\")\n\n yield Static(\"─\" * 22, classes=\"sb-div\")\n yield Static(\"Shortcuts\", classes=\"sb-title\")\n yield Static(\"─\" * 22, classes=\"sb-div\")\n\n shortcuts = [\n (\"Enter\", \"Send\"),\n (\"Shift+Enter\", \"New line\"),\n (\"Ctrl+L\", \"Clear chat\"),\n (\"Ctrl+K\", \"Config\"),\n (\"Ctrl+B\", \"Sidebar\"),\n (\"Ctrl+S\", \"Stability\"),\n (\"Ctrl+Y\", \"Copy Last\"),\n (\"Ctrl+Q\", \"Quit\"),\n (\"Esc\", \"Cancel\"),\n ]\n for key, desc in shortcuts:\n yield Static(\n f\"[bold #00f2ff]{key:<12}[/] [#e2e8f0]{desc}[/]\",\n classes=\"sc-row\",\n markup=True,\n )\n\n def watch_model_name(self, val: str) -> None:\n try:\n self.query_one(\"#sb-model\", Static).update(val or \"—\")\n except Exception:\n pass\n\n def watch_status(self, val: str) -> None:\n try:\n self.query_one(\"#sb-status\", Static).update(val)\n except Exception:\n pass\n\n def watch_message_count(self, val: int) -> None:\n try:\n self.query_one(\"#sb-msgs\", Static).update(str(val))\n except Exception:\n pass\n\n\n# ── Input bar ─────────────────────────────────────────────────────────────────\n\nclass ChatInputBar(Horizontal):\n \"\"\"Bottom input bar — prefix + textarea + char counter.\"\"\"\n\n DEFAULT_CSS = \"\"\"\n ChatInputBar {\n height: 5;\n background: $surface;\n border-top: thick $border;\n padding: 0 2;\n align: left middle;\n margin: 0 1 1 1;\n outline: solid $primary 10%;\n }\n #input-prefix {\n color: $primary;\n text-style: bold;\n width: auto;\n padding: 0 1;\n }\n ChatTextArea {\n background: $background;\n color: $text;\n width: 1fr;\n height: 3;\n }\n ChatTextArea:focus {\n border: round $primary;\n }\n Select {\n width: 16;\n height: 1;\n margin-left: 1;\n background: $background;\n border: round $border;\n color: $text;\n }\n Select:focus {\n border: round $secondary;\n }\n #char-counter {\n color: $text-muted;\n width: auto;\n padding: 0 1;\n }\n #char-counter.warn { color: $warning; }\n #char-counter.danger { color: $error; }\n \"\"\"\n\n MAX_CHARS = 4096\n\n def compose(self) -> ComposeResult:\n yield Static(\"❯\", id=\"input-prefix\")\n yield ChatTextArea(id=\"chat-input\", language=None)\n yield Select([(\"Auto\", \"auto\"), (\"Plan\", \"plan\"), (\"Fast\", \"fast_ans\")], value=\"auto\", id=\"mode-select\")\n yield Static(\"0\", id=\"char-counter\")\n\n def on_text_area_changed(self, event: TextArea.Changed) -> None:\n count = len(event.text_area.text)\n counter = self.query_one(\"#char-counter\", Static)\n counter.update(str(count))\n counter.remove_class(\"warn\", \"danger\")\n if count > self.MAX_CHARS * 0.9:\n counter.add_class(\"danger\")\n elif count > self.MAX_CHARS * 0.7:\n counter.add_class(\"warn\")\n\n def get_text(self) -> str:\n return self.query_one(\"#chat-input\", ChatTextArea).text\n\n def clear(self) -> None:\n ta = self.query_one(\"#chat-input\", ChatTextArea)\n ta.load_text(\"\")\n self.query_one(\"#char-counter\", Static).update(\"0\")\n\n def focus_input(self) -> None:\n self.query_one(\"#chat-input\", ChatTextArea).focus()\n\n\n# ── Main Chat Screen ──────────────────────────────────────────────────────────\n\nclass ChatScreen(Screen):\n \"\"\"Full-screen interactive chat interface.\"\"\"\n\n BINDINGS = [\n Binding(\"ctrl+l\", \"clear_chat\", \"Clear\", show=False),\n Binding(\"ctrl+b\", \"toggle_sidebar\", \"Sidebar\", show=False),\n Binding(\"ctrl+s\", \"open_stability\", \"Stability\", show=True),\n Binding(\"ctrl+y\", \"copy_last\", \"Copy Last\", show=False),\n Binding(\"escape\", \"cancel_stream\", \"Cancel\", show=False),\n ]\n\n DEFAULT_CSS = \"\"\"\n ChatScreen {\n background: $background;\n layout: vertical;\n }\n\n /* ── Header ── */\n #chat-header {\n height: 3;\n background: $surface;\n border-bottom: thick $border;\n layout: horizontal;\n align: left middle;\n padding: 0 4;\n margin: 1 1 0 1;\n outline: solid $primary 10%;\n }\n #header-logo {\n color: $primary;\n text-style: bold;\n width: auto;\n }\n #header-status {\n color: $success;\n text-style: bold italic;\n width: 1fr;\n text-align: right;\n }\n\n /* ── Body ── */\n #body {\n layout: horizontal;\n height: 1fr;\n }\n #chat-log-container {\n width: 1fr;\n height: 100%;\n background: $background;\n layout: vertical;\n }\n #chat-log {\n width: 100%;\n height: 1fr;\n background: $background;\n scrollbar-color: $border;\n scrollbar-color-hover: $secondary;\n scrollbar-gutter: stable;\n padding: 1 2;\n overflow-y: scroll;\n }\n\n /* ── Footer ── */\n #chat-footer {\n height: 1;\n background: $surface;\n color: $text-muted;\n text-align: center;\n padding: 0 1;\n }\n \"\"\"\n\n _streaming: reactive[bool] = reactive(False)\n _sidebar_visible: reactive[bool] = reactive(False)\n _history: list[ChatMessage] = []\n _agent = None\n _stream_worker: Worker | None = None\n\n # ── compose ───────────────────────────────────────────────────────────────\n\n def compose(self) -> ComposeResult:\n # Header\n with Horizontal(id=\"chat-header\"):\n yield Static(\"[bold #00f2ff]🌊 GIYU[/]\", id=\"header-logo\")\n yield Static(\"● Initializing\", id=\"header-status\")\n\n # Body\n with Horizontal(id=\"body\"):\n yield SidebarWidget(id=\"sidebar\")\n with Container(id=\"chat-log-container\"):\n with VerticalScroll(id=\"chat-log\"):\n # Welcome messages will be mounted here\n pass\n # Animated spinner lives BELOW the log, inside the same column\n yield ThinkingSpinner(id=\"thinking-spinner\")\n\n # Input + Footer\n yield ChatInputBar(id=\"input-bar\")\n yield Static(\n \" Enter: Send │ Ctrl+Y: Copy Last │ Ctrl+L: Clear │ Ctrl+K: Config │ Ctrl+B: Sidebar │ Ctrl+Q: Quit\",\n id=\"chat-footer\",\n )\n\n # ── lifecycle ─────────────────────────────────────────────────────────────\n\n def on_mount(self) -> None:\n # We don't need to set can_focus on VerticalScroll as it handles children\n \n self._print_welcome()\n self._load_model_info()\n self.query_one(\"#input-bar\", ChatInputBar).focus_input()\n self._init_agent_worker()\n\n def _load_model_info(self) -> None:\n from pathlib import Path\n local_env = Path(\".env\")\n global_env = Path.home() / \".giyu\" / \".env\"\n \n if local_env.exists():\n load_dotenv(local_env, override=True)\n elif global_env.exists():\n load_dotenv(global_env, override=True)\n else:\n load_dotenv(override=True) # Fallback to default behavior\n\n model = os.getenv(\"OPENAI_LLM_MODEL\", \"gpt-4o\")\n self.query_one(\"#sidebar\", SidebarWidget).model_name = model\n\n # ── welcome banner ────────────────────────────────────────────────────────\n\n def _print_welcome(self) -> None:\n log = self.query_one(\"#chat-log\", VerticalScroll)\n log.mount(Static(\"\"))\n log.mount(Static(\"[bold #00f2ff]🌊 Greetings. I am Giyu.[/]\", markup=True))\n log.mount(Static(\"[dim #94a3b8]The System Stability Sentinel is ready to monitor your network.[/]\", markup=True))\n log.mount(Static(\"\"))\n\n # ── agent init ────────────────────────────────────────────────────────────\n\n @work(exclusive=True, name=\"init-agent\")\n async def _init_agent_worker(self) -> None:\n try:\n from ..agent import get_giyu_agent\n \n # Retrieve cached system info from the main app's probe\n sys_info = \"\"\n if hasattr(self.app, \"system_probe\"):\n sys_info = self.app.system_probe.get_summary()\n \n self._agent = await get_giyu_agent(system_info=sys_info)\n from pathlib import Path\n local_env = Path(\".env\")\n global_env = Path.home() / \".giyu\" / \".env\"\n \n if local_env.exists():\n load_dotenv(local_env, override=True)\n elif global_env.exists():\n load_dotenv(global_env, override=True)\n\n self._agent = await get_giyu_agent()\n self._on_agent_ready()\n except Exception as e:\n self._on_agent_error(str(e))\n\n def _on_agent_ready(self) -> None:\n self._set_status(\"● Ready\", \"#39d353\")\n self.query_one(\"#sidebar\", SidebarWidget).status = \"● Ready\"\n\n def _on_agent_error(self, err: str) -> None:\n self._set_status(\"● Error\", \"#ff4444\")\n log = self.query_one(\"#chat-log\", VerticalScroll)\n log.mount(Static(\n f\"[bold #ff4444]⚠ Agent init failed:[/] [#F1E9DD]{err}[/]\\n\"\n f\"[dim]Press [bold #A89F91]Ctrl+K[/] to check your configuration.[/]\\n\",\n markup=True\n ))\n\n # ── SendMessage from ChatTextArea ─────────────────────────────────────────\n\n @on(ChatTextArea.SendMessage)\n def handle_send_message(self, _event: ChatTextArea.SendMessage) -> None:\n self._do_send()\n\n # ── actions ───────────────────────────────────────────────────────────────\n\n\n def action_clear_chat(self) -> None:\n self._history.clear()\n log = self.query_one(\"#chat-log\", VerticalScroll)\n # Remove all MessageDisplay widgets\n for child in log.children[:]:\n child.remove()\n self._print_welcome()\n self.query_one(\"#sidebar\", SidebarWidget).message_count = 0\n\n\n def _on_config_closed(self, result=None) -> None:\n if result:\n from phoenix.core.config import config\n config.reload()\n \n self._load_model_info()\n # Re-initialize the agent with new configuration\n self._init_agent_worker()\n self.notify(\"✓ Configuration updated. Agent re-initialized.\", severity=\"information\")\n\n def action_toggle_sidebar(self) -> None:\n sidebar = self.query_one(\"#sidebar\", SidebarWidget)\n self._sidebar_visible = not self._sidebar_visible\n if self._sidebar_visible:\n sidebar.add_class(\"visible\")\n else:\n sidebar.remove_class(\"visible\")\n\n def action_cancel_stream(self) -> None:\n if self._stream_worker and self._stream_worker.is_running:\n self._stream_worker.cancel()\n self._streaming = False\n self.query_one(\"#thinking-spinner\", ThinkingSpinner).hide()\n self._set_status(\"● Ready\", \"#39d353\")\n self.query_one(\"#sidebar\", SidebarWidget).status = \"● Ready\"\n\n def action_open_stability(self) -> None:\n \"\"\"Opens the real-time stability monitor screen.\"\"\"\n from .stability_screen import StabilityScreen\n self.app.push_screen(StabilityScreen())\n\n def action_copy_last(self) -> None:\n \"\"\"Copies the last assistant response to the clipboard.\"\"\"\n # Find the last assistant message in history\n assistant_msgs = [m for m in self._history if m.role == \"assistant\"]\n if assistant_msgs:\n last_msg = assistant_msgs[-1].content\n if robust_copy(self.app, last_msg):\n self.notify(\"✓ Last response copied to clipboard\", severity=\"information\")\n else:\n self.notify(\"⚠ Clipboard copy failed\", severity=\"error\")\n else:\n self.notify(\"No response to copy\", severity=\"warning\")\n\n # ── core send ─────────────────────────────────────────────────────────────\n\n def _do_send(self) -> None:\n input_bar = self.query_one(\"#input-bar\", ChatInputBar)\n text = input_bar.get_text().strip()\n mode = input_bar.query_one(\"#mode-select\", Select).value\n if not text:\n return\n if self._streaming:\n self.notify(\"⚡ Already processing — press Esc to cancel.\", severity=\"warning\")\n return\n\n # ✅ Clear the screen for a fresh focus on this turn\n log = self.query_one(\"#chat-log\", VerticalScroll)\n for child in log.children[:]:\n child.remove()\n\n # Clear input and render user bubble at the top\n input_bar.clear()\n input_bar.focus_input()\n self._render_user_message(text)\n\n if not self._agent:\n self.notify(\"⏳ Agent still loading, please wait...\", severity=\"warning\")\n return\n\n self._start_stream(text, mode)\n\n # ── rendering ─────────────────────────────────────────────────────────────\n\n def _render_user_message(self, text: str) -> None:\n log = self.query_one(\"#chat-log\", VerticalScroll)\n msg = ChatMessage(\"user\", text)\n log.mount(MessageDisplay(msg))\n log.scroll_end(animate=False)\n self._history.append(msg)\n self.query_one(\"#sidebar\", SidebarWidget).message_count = len(self._history)\n\n def _render_assistant_message(self, text: str) -> None:\n log = self.query_one(\"#chat-log\", VerticalScroll)\n msg = ChatMessage(\"assistant\", text)\n log.mount(MessageDisplay(msg))\n log.scroll_end(animate=False)\n self._history.append(msg)\n self.query_one(\"#sidebar\", SidebarWidget).message_count = len(self._history)\n\n # ── streaming ─────────────────────────────────────────────────────────────\n\n def _start_stream(self, user_text: str, mode: str) -> None:\n self._streaming = True\n self.query_one(\"#sidebar\", SidebarWidget).status = \"⠋ Thinking...\"\n self._set_status(\"⠋ Thinking...\", \"#A89F91\")\n self.query_one(\"#thinking-spinner\", ThinkingSpinner).show(\"Giyu is thinking\")\n self._stream_worker = self.run_stream_worker(user_text, mode)\n\n @work(exclusive=True, name=\"stream-response\")\n async def run_stream_worker(self, user_text: str, mode: str) -> None:\n try:\n await self._stream_response(user_text, mode)\n except Exception as e:\n self._on_stream_error(str(e))\n finally:\n self._on_stream_done()\n\n async def _stream_response(self, user_text: str, mode: str) -> None:\n full_response = \"\"\n phase = \"status\"\n spinner = self.query_one(\"#thinking-spinner\", ThinkingSpinner)\n\n gen = self._agent.run_stream(user_text, mode=mode)\n\n async for event in gen:\n if event[\"type\"] == \"status\":\n # Update spinner label with agent status messages\n spinner.show(event[\"content\"][:55])\n elif event[\"type\"] == \"thought\":\n # NEW: Capture planner reasoning text and show in UI\n reasoning = event[\"content\"]\n # Show a scrolling preview of the thinking process\n spinner.show(f\"Thinking: {reasoning[-60:]}\")\n elif event[\"type\"] == \"chunk\":\n if phase == \"status\":\n phase = \"streaming\"\n self._set_status(\"⠿ Streaming...\", \"#A89F91\")\n full_response += event[\"content\"]\n preview = full_response.split(\"\\n\")[0][:50]\n spinner.show(f\"Writing: {preview}…\")\n\n if full_response:\n self._commit_response(full_response)\n\n def _commit_response(self, text: str) -> None:\n self.query_one(\"#thinking-spinner\", ThinkingSpinner).hide()\n self._render_assistant_message(text)\n\n def _on_stream_done(self) -> None:\n self._streaming = False\n self._set_status(\"● Ready\", \"#39d353\")\n self.query_one(\"#sidebar\", SidebarWidget).status = \"● Ready\"\n\n def _on_stream_error(self, err: str) -> None:\n self.query_one(\"#thinking-spinner\", ThinkingSpinner).hide()\n log = self.query_one(\"#chat-log\", VerticalScroll)\n log.mount(Static(f\"[bold #ff4444]⚠ Error:[/] [#F1E9DD]{err}[/]\\n\", markup=True))\n\n # ── helpers ───────────────────────────────────────────────────────────────\n\n def _set_status(self, text: str, color: str = \"#10b981\") -> None:\n try:\n self.query_one(\"#header-status\", Static).update(f\"[bold {color}]{text}[/]\")\n except Exception:\n pass\n"}}, "giyu/cognition/brains/generator.py": {"6e8d3b9027d425b765248cdbbf14d37f8b92fb6c": {"t": 1785599330.234318, "n": 7242, "h": "14823370e9a5973d", "c": "import json\nimport re\nimport os\nimport ast\nimport subprocess\n\nfrom ..helpers.tasks import _clean_json\nfrom ..helpers.generation import _add_generation_block\nfrom ..core.prompts import GENERATION_PROMPT\n\nclass GiyuGenerator:\n \"\"\"\n Receives a plan_step and generates actual code/commands iteratively.\n Produces generation_blocks mapped to the schema.\n \"\"\"\n def __init__(self, llm):\n self.llm = llm\n\n def _validate_artifact(self, artifact: dict) -> str | None:\n \"\"\"Validates the syntax of an artifact's code before execution.\"\"\"\n # For file_update_multi, we might want to validate the chunks, but it's harder to validate partial python without context.\n # We will focus on full file code and terminal commands.\n if artifact.get(\"type\") == \"file_update_multi\":\n edits = artifact.get(\"edits\", [])\n if not isinstance(edits, list) or len(edits) == 0:\n return \"Error: type 'file_update_multi' requires a non-empty 'edits' array.\"\n for i, e in enumerate(edits):\n missing = [k for k in [\"StartLine\", \"EndLine\", \"TargetContent\", \"ReplacementContent\"] if k not in e]\n if missing:\n return f\"Error in edit {i}: missing keys {missing}. Do NOT use 'old_lines' or 'new_lines'.\"\n return None\n \n lang = artifact.get(\"language\", \"\").lower()\n code = artifact.get(\"code\", \"\")\n \n if not code:\n return None\n \n if lang in [\"python\", \"py\"]:\n try:\n ast.parse(code)\n except SyntaxError as e:\n return f\"Python SyntaxError: {e.msg} at line {e.lineno}\"\n except Exception as e:\n return f\"Python Parse Error: {str(e)}\"\n \n elif lang in [\"bash\", \"sh\"] or artifact.get(\"type\") == \"terminal\":\n try:\n result = subprocess.run([\"bash\", \"-n\", \"-c\", code], capture_output=True, text=True, timeout=5)\n if result.returncode != 0:\n return f\"Bash Syntax Error: {result.stderr.strip()}\"\n except Exception as e:\n return f\"Bash Validation Error: {str(e)}\"\n \n elif lang == \"json\":\n try:\n json.loads(code)\n except Exception as e:\n return f\"JSON Parse Error: {str(e)}\"\n \n # Path validation: Prevent absolute paths or going out of workspace\n path = artifact.get(\"path\", \"\")\n if path.startswith(\"/\") or \"..\" in path:\n return f\"Security Error: Absolute paths or directory traversal ('..') are forbidden: {path}\"\n \n return None\n\n # ── File path detection ────────────────────────────────────────────────────\n _FILE_PATH_RE = re.compile(\n r'(?:^|\\s|[\"\\'])'\n r'([\\w./\\-]+\\.(?:py|js|ts|jsx|tsx|json|yaml|yml|toml|txt|md|sh|env|cfg|ini|html|css|sql|go|rs|java|c|cpp|h))'\n r'(?:$|\\s|[\"\\'])',\n re.MULTILINE\n )\n\n def _detect_existing_files(self, text: str) -> list:\n candidates = self._FILE_PATH_RE.findall(text)\n return [p for p in candidates if os.path.isfile(p)]\n\n def _read_file_for_prompt(self, file_path: str, max_lines: int = 300) -> str:\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n lines = f.readlines()\n total = len(lines)\n truncated = total > max_lines\n display = lines[:max_lines]\n numbered = [f\"L{i+1}: {l.rstrip()}\" for i, l in enumerate(display)]\n result = f\"[{file_path}] — {total} lines total\\n\" + \"\\n\".join(numbered)\n if truncated:\n result += f\"\\n... (truncated, showing first {max_lines} lines)\"\n return result\n except Exception as ex:\n return f\"Could not read {file_path}: {ex}\"\n\n\n async def generate_step(self, step: dict, task: dict) -> dict:\n from ..helpers.schemas import validate_schema, GENERATION_SCHEMA\n text = step.get(\"solution\", {}).get(\"approach\", \"\") + \" \" + task.get(\"description\", \"\")\n existing = self._detect_existing_files(text)\n \n file_context = \"\"\n if existing:\n sections = []\n for path in existing:\n sections.append(\n f\"=== EXISTING FILE: {path} ===\\n\"\n f\"{self._read_file_for_prompt(path)}\\n\"\n f\"=== END OF {path} ===\"\n )\n file_context = \"\\n\".join(sections)\n \n base_prompt = GENERATION_PROMPT.format(\n step_id=step.get(\"plan_step_id\", 1),\n type=step.get(\"type\", \"\"),\n approach=step.get(\"solution\", {}).get(\"approach\", \"\"),\n algorithm=step.get(\"solution\", {}).get(\"algorithm\", \"\"),\n file_context=file_context or \"No existing files detected.\"\n )\n\n MAX_ATTEMPTS = 2\n validation_errors = []\n gen_data = {\"generation_blocks\": []}\n \n for attempt in range(MAX_ATTEMPTS):\n prompt = base_prompt\n if validation_errors:\n err_str = \"\\n\".join(f\"- {e}\" for e in validation_errors)\n prompt += f\"\\n\\n=== PREVIOUS ATTEMPT FAILED SYNTAX VALIDATION ===\\nFix these errors:\\n{err_str}\\n\"\n\n response = await self.llm.generate(prompt, session_id=None)\n clean = _clean_json(response)\n \n try:\n gen_data = json.loads(clean)\n except Exception as e:\n m = re.search(r'\\{.*\\}', clean, re.DOTALL)\n if m:\n try:\n gen_data = json.loads(m.group(0))\n except Exception:\n gen_data = {\"generation_blocks\": []}\n else:\n gen_data = {\"generation_blocks\": []}\n\n # Schema Validation\n errors = validate_schema(gen_data, GENERATION_SCHEMA)\n if errors:\n validation_errors.extend(errors)\n\n # Run Syntax Validation Layer\n validation_errors = []\n blocks = gen_data.get(\"generation_blocks\", [])\n for b in blocks:\n for art in b.get(\"artifacts\", []):\n err = self._validate_artifact(art)\n if err:\n validation_errors.append(f\"In {art.get('path', 'unknown')}: {err}\")\n \n if not validation_errors:\n break # Success!\n \n if validation_errors:\n # If we exhausted attempts and still have errors, return a syntax_error status block\n return {\n \"generation_blocks\": [{\n \"plan_step_id\": step.get(\"plan_step_id\"),\n \"artifacts\": [],\n \"status\": \"syntax_error\",\n \"error\": \"\\n\".join(validation_errors)\n }]\n }\n\n # Persist valid blocks\n blocks = gen_data.get(\"generation_blocks\", [])\n for b in blocks:\n b[\"plan_step_id\"] = step.get(\"plan_step_id\")\n _add_generation_block(b)\n \n return gen_data\n"}}, "giyu/cli/theme.py": {"1864d38103c4f289054986641be439852694ad17": {"t": 1785599330.260627, "n": 1307, "h": "c4aef2a9cfedb886", "c": "\"\"\"\nCentralized design tokens for the Giyu TUI.\nAll colors and styles are defined here to keep screens consistent.\n\"\"\"\n\nfrom textual.theme import Theme\n\n# ── Modern Premium Palette ───────────────────────────────────────────────────\n# Backgrounds\n# ── Giyu Design Tokens (Synced with Dashboard) ────────────────────────────────\n# Backgrounds\nGIYU_DARK = \"#020408\" # Deep Onyx\nGIYU_SURFACE = \"#0A0C14\" # Obsidian Surface\nGIYU_PANEL = \"#12161D\" # Slate Panel\n\n# Accents\nGIYU_CYAN = \"#00f2ff\" # Electric Cyan\nGIYU_PURPLE = \"#7000ff\" # Plasma Purple\nGIYU_MAGENTA = \"#FF00FF\" # Neon Magenta\nGIYU_GLOW = \"#00B4D8\" # Cyber Blue\n\n# Functional\nGIYU_BORDER = \"#1C2128\" # Steel Border\nGIYU_MUTED = \"#484F58\" # Iron Gray\nGIYU_DIMTEXT = \"#94a3b8\" # Muted Ash\nGIYU_TEXT = \"#e2e8f0\" # Soft Cloud\nGIYU_SUCCESS = \"#10b981\" # Emerald\nGIYU_ERROR = \"#ef4444\" # Crimson\nGIYU_WARNING = \"#f59e0b\" # Amber\n\n# ── Textual Theme ────────────────────────────────────────────────────────────\nGIYU_THEME = Theme(\n name=\"giyu\",\n primary=GIYU_CYAN,\n secondary=GIYU_PURPLE,\n accent=GIYU_CYAN,\n background=GIYU_DARK,\n surface=GIYU_SURFACE,\n panel=GIYU_PANEL,\n boost=GIYU_MUTED,\n warning=GIYU_WARNING,\n error=GIYU_ERROR,\n success=GIYU_SUCCESS,\n dark=True,\n)\n"}}, "giyu/cli/__init__.py": {"01ab927805947d49938b4916f767b385fbf6a5b8": {"t": 1785599330.2616403, "n": 31, "h": "0b7a61ad4d7d3875", "c": "# cli package — Giyu Agent TUI\n"}}, "giyu/cognition/brains/__init__.py": {"e7a239678cba10054d7680f18076616007bfa267": {"t": 1785599330.279101, "n": 216, "h": "287098ec6d690942", "c": "from .thinker import GiyuThinker\nfrom .planner import GiyuPlanner\nfrom .reflector import GiyuReflector\nfrom .generator import GiyuGenerator\n\n__all__ = [\"GiyuThinker\", \"GiyuPlanner\", \"GiyuReflector\", \"GiyuGenerator\"]\n"}}, "giyu/cognition/__init__.py": {"16a84742e801bcb0146bd8c8c9d2d585c824b25d": {"t": 1785599330.286478, "n": 369, "h": "fe7df37e8ad4b77c", "c": "\"\"\"\nGiyu Cognition Module — Task-File Driven Architecture.\n\"\"\"\n\nfrom .brains.thinker import GiyuThinker\nfrom .brains.planner import GiyuPlanner\nfrom .brains.reflector import GiyuReflector\nfrom .brains.generator import GiyuGenerator\nfrom .loop import GiyuLoop\n\n__all__ = [\n \"GiyuThinker\",\n \"GiyuPlanner\",\n \"GiyuReflector\",\n \"GiyuLoop\",\n \"GiyuGenerator\"\n]\n"}}, "giyu/cognition/brains/correlation_engine.py": {"dc57d996ad0ab799def7de878837c794e585bc62": {"t": 1785599330.292451, "n": 1308, "h": "31c2ec979b384243", "c": "from typing import List, Dict, Any\nfrom phoenix.framework.agent.cognition.utils.json import parse_llm_json\n\nclass CorrelationEngine:\n \"\"\"\n Links multiple events to detect root cause chains.\n Hybrid mode (Uses LLM).\n \"\"\"\n def __init__(self, llm):\n self.llm = llm\n\n async def correlate(self, events: List[Dict[str, Any]]) -> Dict[str, Any]:\n \"\"\"\n Uses LLM to find links between events.\n \"\"\"\n events_str = \"\\n\".join([str(e) for e in events])\n \n system_prompt = \"\"\"\n You are the 'Correlation Engine' for a system stability agent.\n Your job is to look at multiple system events/logs and identify if they are linked \n in a causal chain. Identify the 'Root Cause'.\n \n Respond with JSON:\n {\n \"linked\": boolean,\n \"root_cause\": \"description\",\n \"chain\": [\"event_a -> event_b\"],\n \"confidence\": 0.0-1.0\n }\n \"\"\"\n \n prompt = f\"{system_prompt}\\n\\nEvents:\\n{events_str}\\n\\nAnalysis (JSON):\"\n response = await self.llm.generate(prompt, session_id=None)\n \n return parse_llm_json(response) or {\n \"linked\": False,\n \"root_cause\": \"Unknown\",\n \"chain\": [],\n \"confidence\": 0.0\n }\n"}}, "giyu/cli/stability_screen.py": {"9d1e19f67e015a352ba98c06e06eee05a903b003": {"t": 1785599330.2984982, "n": 5585, "h": "0ec0b983a0cfbac0", "c": "\"\"\"\nGiyu Agent — Real-time System Stability Monitor.\n\"\"\"\n\nimport asyncio\nimport psutil\nimport time\nfrom textual.app import ComposeResult\nfrom textual.screen import Screen\nfrom textual.widgets import Static, Header, Footer, ProgressBar, Label, Digits\nfrom textual.containers import Container, Vertical, Horizontal, Grid\nfrom textual.reactive import reactive\nfrom textual import on, work\nfrom rich.text import Text\nfrom rich.panel import Panel\nfrom rich.progress_bar import ProgressBar as RichProgressBar\nfrom rich.console import RenderableType\n\nclass StatCard(Container):\n \"\"\"A card displaying a specific system metric.\"\"\"\n \n DEFAULT_CSS = \"\"\"\n StatCard {\n background: $surface;\n border: round $border;\n padding: 1 2;\n margin: 1;\n height: auto;\n min-height: 8;\n }\n StatCard:hover {\n border: round $primary;\n background: $panel;\n }\n .card-title {\n color: $secondary;\n text-style: bold;\n margin-bottom: 1;\n }\n .card-value {\n color: $text;\n text-style: bold;\n }\n .card-meta {\n color: $text-muted;\n }\n \"\"\"\n\n def __init__(self, title: str, unit: str = \"\", **kwargs):\n super().__init__(**kwargs)\n self.title_text = title\n self.unit = unit\n\n def compose(self) -> ComposeResult:\n yield Label(self.title_text, classes=\"card-title\")\n yield Label(\"0\" + self.unit, id=\"value\", classes=\"card-value\")\n yield ProgressBar(total=100, id=\"progress\", show_percentage=False)\n yield Label(\"\", id=\"meta\", classes=\"card-meta\")\n\n def update(self, value: float, meta: str = \"\") -> None:\n self.query_one(\"#value\", Label).update(f\"{value:.1f}{self.unit}\")\n self.query_one(\"#progress\", ProgressBar).progress = value\n if meta:\n self.query_one(\"#meta\", Label).update(meta)\n\nclass StabilityScreen(Screen):\n \"\"\"Interactive system stability dashboard.\"\"\"\n\n BINDINGS = [\n (\"escape\", \"app.pop_screen\", \"Back to Chat\"),\n (\"r\", \"refresh\", \"Refresh Stats\"),\n ]\n\n DEFAULT_CSS = \"\"\"\n StabilityScreen {\n background: $background;\n padding: 1;\n }\n\n #stability-header {\n height: 3;\n background: $surface;\n border-bottom: thick $border;\n align: center middle;\n margin-bottom: 1;\n }\n\n #stability-grid {\n grid-size: 2 3;\n height: 1fr;\n }\n\n .health-section {\n column-span: 2;\n height: 10;\n align: center middle;\n background: $panel;\n border: double $primary;\n margin: 1;\n }\n\n #health-score {\n color: #00f2ff;\n text-style: bold;\n }\n \"\"\"\n\n health_score = reactive(0)\n\n def compose(self) -> ComposeResult:\n with Horizontal(id=\"stability-header\"):\n yield Static(\"[bold #00f2ff]🌊 GIYU SYSTEM SENTINEL[/] [dim #7000ff]— REAL-TIME STABILITY LOG[/]\")\n\n with Grid(id=\"stability-grid\"):\n with Vertical(classes=\"health-section\"):\n yield Label(\"SYSTEM STABILITY INDEX\", classes=\"card-title\")\n yield Digits(\"00.0\", id=\"health-digits\")\n yield Label(\"GUARDING HASHIRA-OS\", classes=\"card-meta\")\n\n yield StatCard(\"CPU LOAD\", unit=\"%\", id=\"cpu-card\")\n yield StatCard(\"MEMORY USAGE\", unit=\"%\", id=\"mem-card\")\n yield StatCard(\"NETWORK BANDWIDTH\", unit=\" MB/s\", id=\"net-card\")\n yield StatCard(\"DISK I/O\", unit=\" ops\", id=\"disk-card\")\n\n yield Footer()\n\n def on_mount(self) -> None:\n self.update_stats()\n self.set_interval(1.0, self.update_stats)\n\n @work(exclusive=True)\n async def update_stats(self) -> None:\n \"\"\"Fetch and update system metrics.\"\"\"\n # CPU\n cpu_usage = psutil.cpu_percent()\n \n # Memory\n mem = psutil.virtual_memory()\n mem_usage = mem.percent\n mem_meta = f\"{mem.used // (1024**2)}MB / {mem.total // (1024**2)}MB\"\n\n # Network (Calculated over 1s)\n n1 = psutil.net_io_counters()\n await asyncio.sleep(0.5)\n n2 = psutil.net_io_counters()\n net_speed = ((n2.bytes_sent + n2.bytes_recv) - (n1.bytes_sent + n1.bytes_recv)) / (1024**2)\n net_meta = f\"↑ {n2.bytes_sent // (1024**2)}MB ↓ {n2.bytes_recv // (1024**2)}MB\"\n\n # Disk\n d1 = psutil.disk_io_counters()\n await asyncio.sleep(0.4)\n d2 = psutil.disk_io_counters()\n disk_ops = (d2.read_count + d2.write_count) - (d1.read_count + d1.write_count)\n \n # Health Score (Weighted)\n # Low usage = high stability\n score = 100 - (cpu_usage * 0.4 + mem_usage * 0.4 + min(net_speed * 2, 20))\n score = max(0, min(100, score))\n\n # Update UI\n self.query_one(\"#cpu-card\", StatCard).update(cpu_usage, f\"Cores: {psutil.cpu_count()}\")\n self.query_one(\"#mem-card\", StatCard).update(mem_usage, mem_meta)\n self.query_one(\"#net-card\", StatCard).update(min(net_speed * 10, 100), net_meta)\n self.query_one(\"#disk-card\", StatCard).update(min(disk_ops / 10, 100), f\"{disk_ops} operations/sec\")\n \n self.query_one(\"#health-digits\", Digits).update(f\"{score:04.1f}\")\n \n # Color transition for health digits\n if score > 80:\n self.query_one(\"#health-digits\", Digits).styles.color = \"#10b981\" # Green\n elif score > 50:\n self.query_one(\"#health-digits\", Digits).styles.color = \"#f59e0b\" # Warning\n else:\n self.query_one(\"#health-digits\", Digits).styles.color = \"#ef4444\" # Error\n"}}, "giyu/cognition/brains/stability_scorer.py": {"a60d398ecb4c240f67c0373a47a1c8e6e10ef9f5": {"t": 1785599330.3255196, "n": 1762, "h": "59b0a24f3db98730", "c": "import time\nfrom typing import Dict, Any\n\nclass StabilityScorer:\n \"\"\"\n Computes system health score and stability state.\n Deterministic (No LLM).\n \"\"\"\n def __init__(self, thresholds: Dict[str, float] = None):\n self.thresholds = thresholds or {\n \"cpu_critical\": 90.0,\n \"ram_critical\": 90.0,\n \"disk_critical\": 95.0,\n \"cpu_warning\": 75.0,\n \"ram_warning\": 75.0\n }\n\n def compute_score(self, metrics: Dict[str, float]) -> Dict[str, Any]:\n \"\"\"\n Calculates a stability score from 0 to 100.\n 100 = Perfect Stability, 0 = System Failure.\n \"\"\"\n cpu = metrics.get(\"cpu_usage\", 0.0)\n ram = metrics.get(\"ram_usage\", 0.0)\n disk = metrics.get(\"disk_usage\", 0.0)\n \n # Base score\n score = 100.0\n \n # Deductions based on critical thresholds\n if cpu > self.thresholds[\"cpu_critical\"]:\n score -= 40\n elif cpu > self.thresholds[\"cpu_warning\"]:\n score -= 15\n \n if ram > self.thresholds[\"ram_critical\"]:\n score -= 40\n elif ram > self.thresholds[\"ram_warning\"]:\n score -= 15\n \n if disk > self.thresholds[\"disk_critical\"]:\n score -= 20\n \n # Clamp score\n score = max(0.0, min(100.0, score))\n \n state = \"stable\"\n if score < 40:\n state = \"critical\"\n elif score < 70:\n state = \"unstable\"\n elif score < 90:\n state = \"degraded\"\n \n return {\n \"score\": score,\n \"state\": state,\n \"timestamp\": time.time(),\n \"metrics_evaluated\": [\"cpu\", \"ram\", \"disk\"]\n }\n"}}, "giyu/cognition/brains/decision_gate.py": {"44979db263856cce9d6ee8531254b085512a3e59": {"t": 1785599330.5649438, "n": 1039, "h": "527d2ad7e5e9d491", "c": "from typing import Dict, Any\n\nclass DecisionGate:\n \"\"\"\n Decides escalation level (ignore / warn / escalate / emergency).\n Strict rules engine (No LLM).\n \"\"\"\n def __init__(self):\n self.rules = [\n {\"condition\": lambda s: s < 20, \"action\": \"emergency\"},\n {\"condition\": lambda s: s < 50, \"action\": \"escalate\"},\n {\"condition\": lambda s: s < 80, \"action\": \"warn\"},\n {\"condition\": lambda s: s >= 80, \"action\": \"ignore\"}\n ]\n\n def evaluate(self, stability_data: Dict[str, Any]) -> Dict[str, Any]:\n score = stability_data.get(\"score\", 100)\n \n selected_action = \"ignore\"\n for rule in self.rules:\n if rule[\"condition\"](score):\n selected_action = rule[\"action\"]\n break\n \n return {\n \"escalation_level\": selected_action,\n \"necessity\": \"high\" if selected_action in [\"emergency\", \"escalate\"] else \"low\",\n \"decision_reason\": f\"Stability score is {score}\"\n }\n"}}, "giyu/cognition/brains/planner.py": {"c5d31849d128aac818b98243f074db75480307d8": {"t": 1785599330.6195586, "n": 4946, "h": "d0cfa08b8e85f31f", "c": "from phoenix.framework.agent.cognition import Planner\nimport json\nimport re\n\nfrom ..helpers.tasks import _clean_json\nfrom ..helpers.plan import _load_plan, _save_plan\nfrom ..core.prompts import PLAN_GENERATION_PROMPT\n\nclass GiyuPlanner(Planner):\n \"\"\"\n Receives one task at a time and generates a sequence of plan_steps.\n Persists to giyu.plan.json and does NOT generate tool actions directly.\n \"\"\"\n\n def __init__(self, llm, tools, system_info: str = \"\"):\n super().__init__(llm, tools)\n self.system_info = system_info\n\n async def generate_plan_steps(self, task: dict) -> list:\n \"\"\"\n Ask LLM to generate plan steps for the given task and persist to the backbone.\n \"\"\"\n from ..helpers.schemas import validate_schema, PLAN_SCHEMA\n \n # Inject system context for optimized task handling\n sys_context = f\"\\n[SYSTEM CONTEXT]\\n{self.system_info}\\n\" if self.system_info else \"\"\n \n prompt = PLAN_GENERATION_PROMPT.format(\n task_id=task.get(\"id\", 1),\n priority=task.get(\"priority\", 1),\n title=task.get(\"title\", \"\"),\n description=task.get(\"description\", \"\") + sys_context\n )\n\n response = await self.llm.generate(prompt, session_id=None)\n clean = _clean_json(response)\n \n try:\n plan_data = json.loads(clean)\n except Exception as e:\n m = re.search(r'\\{.*\\}', clean, re.DOTALL)\n if m:\n try:\n plan_data = json.loads(m.group(0))\n except Exception:\n plan_data = {\"plan_steps\": []}\n else:\n plan_data = {\"plan_steps\": []}\n\n # Schema Validation\n errors = validate_schema(plan_data, PLAN_SCHEMA)\n if errors:\n print(f\"[PLANNER WARNING] Schema validation failed: {errors}\")\n \n new_steps = plan_data.get(\"plan_steps\", [])\n \n # Automatically assign unique integer plan_step_ids\n existing_plan = _load_plan()\n existing_steps = existing_plan.get(\"plan_steps\", [])\n \n id_map = {}\n next_step_id = 1\n if existing_steps:\n next_step_id = max((s.get(\"plan_step_id\", 0) for s in existing_steps)) + 1\n \n for step in new_steps:\n old_id = step.get(\"plan_step_id\")\n if old_id is not None:\n id_map[old_id] = next_step_id\n \n # Force task_id to match the current task\n step[\"task_id\"] = task.get(\"id\", 1)\n # Re-assign IDs to prevent collisions\n step[\"plan_step_id\"] = next_step_id\n step[\"status\"] = \"pending\"\n next_step_id += 1\n \n # Remap dependencies\n for step in new_steps:\n old_deps = step.get(\"dependencies\", [])\n new_deps = []\n for dep in old_deps:\n if dep in id_map:\n new_deps.append(id_map[dep])\n else:\n new_deps.append(dep)\n step[\"dependencies\"] = new_deps\n \n existing_steps.extend(new_steps)\n existing_plan[\"plan_steps\"] = existing_steps\n _save_plan(existing_plan)\n \n # Generate a high-level summary for the user\n await self.generate_summary_report(task, new_steps)\n \n return new_steps\n\n async def generate_summary_report(self, task: dict, steps: list) -> None:\n \"\"\"\n Generates a readable high-level report of the plan for the user.\n \"\"\"\n step_descriptions = \"\\n\".join([f\"- {s.get('description')}\" for s in steps])\n report_prompt = f\"\"\"\n Generate a concise, professional summary report for the user based on the following task and planned steps.\n Focus on the GOAL and the RESULT. Avoid technical jargon where possible.\n \n TASK: {task.get('title')}\n DESCRIPTION: {task.get('description')}\n \n PLANNED STEPS:\n {step_descriptions}\n \n Format as a clean markdown report.\n \"\"\"\n report = await self.llm.generate(report_prompt, session_id=None)\n # We can print this to the console or store it in the task object\n print(f\"\\n[GIYU SENTINEL REPORT]\\n{report}\\n\")\n task[\"summary_report\"] = report\n\n def task_status_line(self, task: dict) -> str:\n \"\"\"Return a deterministic status line from the task dict — no LLM call needed.\"\"\"\n type_icon = {\"new_file\": \"📄\", \"modify_file\": \"✏️\", \"command\": \"⚡\", \"read\": \"🔍\"}\n icon = type_icon.get(task.get(\"type\", \"\"), \"⚙\")\n return f\"{icon} {task.get('description', task.get('title', ''))[:120]}\"\n\n async def plan(self, objective: str, previous_results: str = \"\") -> dict:\n \"\"\"Fallback if called directly by generic agents. Not typically used in GiyuLoop.\"\"\"\n return {\"actions\": [{\"tool\": \"finish\"}]}\n"}}, "giyu/cognition/brains/thinker.py": {"27753158d3b09cf1c84365bdc0d7c6ec821922f0": {"t": 1785599330.6597974, "n": 1963, "h": "312fc16f7a6b5532", "c": "from phoenix.framework.agent.cognition import Thinker\nimport json\nimport re\n\nfrom ..helpers.tasks import TASK_FILE, _save_tasks, _clean_json\nfrom ..core.prompts import TASK_GENERATION_PROMPT\n\nclass GiyuThinker(Thinker):\n \"\"\"\n Interprets system signals and understands context of events.\n Uses LLM for light reasoning/classification.\n \"\"\"\n\n async def analyze(self, prompt: str, memory, session_id: str) -> str:\n from ..helpers.schemas import validate_schema, TASK_SCHEMA\n context = await memory.get_full_context(session_id, query=prompt)\n\n profile_str = self.profile.to_prompt_string() if self.profile else \"\"\n\n system_prompt = f\"\"\"\n{profile_str}\n\nYou are the 'Thinker' module for Giyu Tomioka, the System Stability Guardian.\nYour job is to interpret the user's request or system signal and break it down into structured tasks.\nFocus on: Stability Monitoring, Anomaly Detection, and Diagnostic Reporting.\n\nRespond with exactly this JSON format:\n{{\n \"original_prompt\": \"{prompt}\",\n \"tasks\": [\n {{\n \"id\": 1,\n \"priority\": 1,\n \"title\": \"Task Title\",\n \"description\": \"Detailed description\",\n \"status\": \"pending\",\n \"type\": \"read|command|analysis\"\n }}\n ]\n}}\n\"\"\"\n\n raw = await self.llm.generate(f\"{system_prompt}\\n\\nSignal/Prompt: {prompt}\\nContext: {context}\", session_id=None, max_tokens=500)\n clean = _clean_json(raw)\n\n # Parse the task list\n try:\n task_data = json.loads(clean)\n except Exception:\n # Fallback\n task_data = {\n \"original_prompt\": prompt,\n \"tasks\": [{\"id\": 1, \"priority\": 1, \"title\": \"System Health Check\", \"description\": f\"Perform diagnostic for: {prompt}\", \"status\": \"pending\", \"type\": \"analysis\"}]\n }\n\n # Write task file\n _save_tasks(task_data)\n\n return f\"STABILITY_MODE: {prompt[:50]}...\"\n"}}, "giyu/cognition/brains/reflector.py": {"f49a1e871354247a4343cc4100a96cf28bed9432": {"t": 1785599330.661883, "n": 1669, "h": "eeaacc753bf4290c", "c": "from phoenix.framework.agent.cognition import Reflector\nimport json\nimport re\n\nfrom ..core.prompts import build_reflector_prompt\n\nclass GiyuReflector(Reflector):\n \"\"\"\n Evaluates historical system behavior and outcomes.\n Summarization + Reasoning over logs.\n \"\"\"\n\n async def reflect(self, objective: str, action: dict, result: str) -> dict:\n result_str = str(result)\n\n profile_str = self.profile.to_prompt_string() if self.profile else \"\"\n\n system_prompt = f\"\"\"\n{profile_str}\n\nYou are the 'Reflector' module for Giyu Tomioka.\nYour job is to evaluate the outcome of a diagnostic task or system intervention.\nAnalyze the result for stability implications, hidden errors, or successful stabilization.\n\nRespond with exactly this JSON format:\n{{\n \"is_complete\": boolean,\n \"reflection\": \"Diagnostic summary and reasoning over logs/results\",\n \"stability_impact\": \"positive|neutral|negative\"\n}}\n\"\"\"\n\n prompt = f\"{system_prompt}\\n\\nObjective: {objective}\\nAction: {action}\\nResult: {result_str}\"\n response = await self.llm.generate(prompt, session_id=None, max_tokens=300)\n \n try:\n from ..helpers.tasks import _clean_json\n clean = _clean_json(response)\n data = json.loads(clean)\n return {\n \"is_complete\": bool(data.get(\"is_complete\", False)),\n \"reflection\": str(data.get(\"reflection\", \"\")),\n \"stability_impact\": data.get(\"stability_impact\", \"neutral\")\n }\n except Exception:\n return {\"is_complete\": True, \"reflection\": \"Task finalized based on raw output.\", \"stability_impact\": \"neutral\"}\n"}}, "giyu/cognition/core/__init__.py": {"ed25df6dff9e6780716318f187d5e49bbe14675a": {"t": 1785599331.574143, "n": 1295, "h": "27e7f657ede4517a", "c": "from .utils import (\n get_pending_tasks, get_executable_tasks, has_task_file,\n map_artifacts_to_actions, pre_execution_validate,\n is_sensitive_action, check_and_ask_approval,\n cleanup_state_files, is_all_done, schedule_background,\n finalize_task, init_phase, ensure_plan_steps,\n fast_answer, fast_answer_stream,\n validate_and_execute, handle_syntax_error,\n execute_step, stream_task_steps, StepResult,\n)\nfrom .prompts import (\n FAST_ANSWER_SYSTEM, build_fast_answer_prompt,\n TASK_GENERATION_PROMPT, PLAN_GENERATION_PROMPT,\n REFLECTOR_SYSTEM, build_reflector_prompt,\n GENERATION_PROMPT,\n)\n\n__all__ = [\n # utils\n \"get_pending_tasks\", \"get_executable_tasks\", \"has_task_file\",\n \"map_artifacts_to_actions\", \"pre_execution_validate\",\n \"is_sensitive_action\", \"check_and_ask_approval\",\n \"cleanup_state_files\", \"is_all_done\", \"schedule_background\",\n \"finalize_task\", \"init_phase\", \"ensure_plan_steps\",\n \"fast_answer\", \"fast_answer_stream\",\n \"validate_and_execute\", \"handle_syntax_error\",\n \"execute_step\", \"stream_task_steps\", \"StepResult\",\n # prompts\n \"FAST_ANSWER_SYSTEM\", \"build_fast_answer_prompt\",\n \"TASK_GENERATION_PROMPT\", \"PLAN_GENERATION_PROMPT\",\n \"REFLECTOR_SYSTEM\", \"build_reflector_prompt\",\n \"GENERATION_PROMPT\",\n]\n"}}, "giyu/cognition/core/prompts.py": {"c113462afe912ab24e7076787b30435e30f77645": {"t": 1785599331.5885787, "n": 5904, "h": "83640c7ffe3f906f", "c": "\"\"\"\nGiyu Prompts — centralised prompt templates for all cognition modules.\n\"\"\"\n\n# ---------------------------------------------------------------------------\n# Loop / Fast Answer\n# ---------------------------------------------------------------------------\n\nFAST_ANSWER_SYSTEM = \"You are GIYU. Give a concise, direct answer to the user's question.\"\n\n\ndef build_fast_answer_prompt(context: str, user_prompt: str) -> str:\n return f\"{FAST_ANSWER_SYSTEM}\\n\\nContext:\\n{context}\\n\\nUser: {user_prompt}\"\n\n\n# ---------------------------------------------------------------------------\n# Thinker — Task Decomposition\n# ---------------------------------------------------------------------------\n\nTASK_GENERATION_PROMPT = \"\"\"\\\nYou are GIYU — The Great Architect. Your job is to decompose a user request \\\ninto a precise, prioritized list of implementation tasks.\n\n=== STRICT DIRECTIVES ===\n1. Respond ONLY with valid JSON.\n2. No conversational filler, preambles, or post-commentary.\n3. Each task must be ATOMIC (one clear action).\n4. For the \"type\" field, use EXACTLY one of: new_file | modify_file | command | read.\n5. \"priority\" starts at 1 (earliest).\n\nUser Request:\n{prompt}\n\nContext:\n{context}\n\n=== RESPONSE SCHEMA ===\n{{\n \"original_prompt\": \"<user request>\",\n \"tasks\": [\n {{\n \"id\": 1,\n \"priority\": 1,\n \"type\": \"new_file | modify_file | command | read\",\n \"title\": \"<short title>\",\n \"description\": \"<detailed implementation instruction>\",\n \"dependencies\": [],\n \"status\": \"pending\"\n }}\n ]\n}}\n\"\"\"\n\n# ---------------------------------------------------------------------------\n# Planner — Plan Step Generation\n# ---------------------------------------------------------------------------\n\nPLAN_GENERATION_PROMPT = \"\"\"\\\nYou are the GIYU Planning Engine. You receive ONE task and must break it down into logical execution steps.\n\n=== STRICT DIRECTIVES ===\n1. Respond ONLY with valid JSON.\n2. No preambles or post-commentary.\n3. Use exactly four core types: analysis, design, implementation, validation.\n4. Each step must have a clear \"solution\" object.\n5. MONITORING PREFERENCE: For diagnostic and monitoring tasks, always prioritize using CLI commands (via `run_shell_command`) over writing new Python scripts.\n6. Only generate code if no existing tool or command can perform the measurement.\n7. SECURITY: NEVER use absolute paths (starting with /). Never use `/tmp` or other system directories. Always use relative paths within the current workspace.\n8. OUTPUT REDIRECTION: Avoid redirecting command output to files (like `> /tmp/log`). Prefer capturing the direct stdout of the command.\n\nTask Info:\nTask ID: {task_id}\nPriority: {priority}\nTitle: {title}\nDescription: {description}\n\n=== RESPONSE SCHEMA ===\n{{\n \"plan_steps\": [\n {{\n \"plan_step_id\": <INT>,\n \"task_id\": {task_id},\n \"step_index\": <INT>,\n \"type\": \"analysis | design | implementation | validation\",\n \"solution\": {{\n \"approach\": \"<detailed text explanation>\",\n \"algorithm\": \"<optional>\",\n \"complexity\": \"<optional>\"\n }},\n \"dependencies\": [],\n \"status\": \"pending\"\n }}\n ]\n}}\n\"\"\"\n\n# ---------------------------------------------------------------------------\n# Reflector — Completion Evaluation\n# ---------------------------------------------------------------------------\n\nREFLECTOR_SYSTEM = (\n \"You are the GIYU Reflector. Evaluate if ONE task was completed successfully.\\n\"\n \"Rules:\\n\"\n \"- If the task required creating files and the tool result shows success: mark complete.\\n\"\n \"- If files are missing, placeholders were used, or an error occurred: NOT complete.\\n\"\n \"- If the task was a terminal command and it ran without error: complete.\\n\"\n 'Respond ONLY with JSON: {\"is_complete\": bool, \"reflection\": \"<one sentence>\"}'\n)\n\n\ndef build_reflector_prompt(objective: str, result: str) -> str:\n return f\"{REFLECTOR_SYSTEM}\\n\\nTask: {objective}\\nResult: {result[:500]}\\nJSON Response:\"\n\n\n# ---------------------------------------------------------------------------\n# Generator — Action Generation\n# ---------------------------------------------------------------------------\n\nGENERATION_PROMPT = \"\"\"\\\nYou are the GIYU Action Generator. You receive ONE plan_step and must generate the exact actions required.\n\n=== GENERATOR RULEBOOK ===\n1. Respond ONLY with valid JSON.\n2. COMMAND FIRST: Always prioritize using \"terminal\" artifacts for system diagnostics, monitoring, and network tests.\n3. CODE SECOND: Only generate \"file_write\" artifacts if a specialized script is absolutely necessary for complex logic that cannot be done via CLI.\n4. Multi-artifact Generation: You SHOULD generate multiple artifacts simultaneously if they belong together.\n5. type \"file_write\": Use for NEW files. \"code\" is full content.\n6. type \"file_update_multi\": Use for EXISTING files. Use \"edits\" field.\n7. type \"terminal\": \"code\" is the bash command. This is preferred for monitoring.\n8. SECURITY: DO NOT use absolute paths. DO NOT write to `/tmp`. Use relative paths for all file and terminal operations.\n9. PREFER STDOUT: Do not use shell redirection (e.g., `> file.txt`) unless strictly necessary for multi-step logging. Always try to get results from the tool's output directly.\n\nPlan Step Details:\nStep ID: {step_id} | Type: {type}\nApproach: {approach}\nAlgorithm: {algorithm}\n\n=== RESPONSE SCHEMA ===\n{{\n \"generation_blocks\": [\n {{\n \"generate_block_id\": <INT>,\n \"plan_step_id\": {step_id},\n \"artifacts\": [\n {{\n \"type\": \"file_write | file_update_multi | terminal\",\n \"path\": \"<path>\",\n \"language\": \"<python|bash|json|etc>\",\n \"code\": \"...\",\n \"edits\": []\n }}\n ],\n \"status\": \"success\",\n \"metadata\": {{ \"model\": \"giyu-generator\" }}\n }}\n ]\n}}\n\nExisting File Context:\n{file_context}\n\"\"\"\n"}}, "giyu/cognition/helpers/__init__.py": {"cabdf1bb53d88f3cc4085c5209e5910499436822": {"t": 1785599331.6429627, "n": 49, "h": "b47810bb2e2caf4f", "c": "\"\"\"\nHelper modules for managing state files.\n\"\"\"\n"}}, "giyu/cognition/core/utils.py": {"a2916c132ec7e4c17ece949b041e4294962f9ea9": {"t": 1785599331.6567852, "n": 20600, "h": "fff3a20d2ad1b8c5", "c": "\"\"\"\nGiyu Loop Utilities — extracted helpers for the main agent loop.\nHandles task querying, artifact mapping, safety, approval, and phase logic.\n\"\"\"\nimport json\nimport os\nimport asyncio\nfrom typing import Dict, Any\n\nfrom ..helpers.tasks import TASK_FILE, _load_tasks, _mark_task, _reset_failed_tasks\nfrom ..helpers.plan import (\n PLAN_FILE, _get_pending_plan_steps, _get_executable_plan_steps,\n _mark_plan_step, _reset_failed_plan_steps,\n)\nfrom ..helpers.generation import GENERATION_FILE\nfrom ..helpers.state import STATE_FILE, _init_state_from_tasks, _update_state, _clear_state\nfrom ..helpers.observability import log_agent_action\nfrom .prompts import build_fast_answer_prompt\n\n\n# ---------------------------------------------------------------------------\n# Task queries\n# ---------------------------------------------------------------------------\n\ndef get_pending_tasks() -> list:\n try:\n data = _load_tasks()\n return sorted(\n [t for t in data.get(\"tasks\", []) if t.get(\"status\") == \"pending\"],\n key=lambda t: t.get(\"priority\", 99),\n )\n except Exception:\n return []\n\n\ndef get_executable_tasks() -> list:\n try:\n data = _load_tasks()\n all_tasks = data.get(\"tasks\", [])\n status_map = {t.get(\"id\"): t.get(\"status\") for t in all_tasks}\n executable = []\n for t in all_tasks:\n if t.get(\"status\") != \"pending\":\n continue\n deps_met, deps_failed = True, False\n for d in t.get(\"dependencies\", []):\n s = status_map.get(d, \"done\")\n if s == \"failed\":\n deps_failed = True\n elif s != \"done\":\n deps_met = False\n if deps_failed:\n _mark_task(t.get(\"id\"), \"failed\")\n continue\n if deps_met:\n executable.append(t)\n return sorted(executable, key=lambda t: t.get(\"priority\", 99))\n except Exception:\n return []\n\n\ndef has_task_file() -> bool:\n return os.path.exists(TASK_FILE)\n\n\n# ---------------------------------------------------------------------------\n# Artifact → Action mapping\n# ---------------------------------------------------------------------------\n\ndef _detect_vscode() -> bool:\n try:\n from giyu.server import vscode_ipc_context\n return vscode_ipc_context.get() is not None\n except ImportError:\n return False\n\n\ndef map_artifacts_to_actions(generation_blocks: list) -> list:\n is_vscode = _detect_vscode()\n actions = []\n for block in generation_blocks:\n for art in block.get(\"artifacts\", []):\n art_type = art.get(\"type\")\n \n if art_type in [\"tool_call\", \"tool\"]:\n tool_name = art.get(\"tool\") or art.get(\"name\")\n tool_kwargs = art.get(\"kwargs\") or art.get(\"arguments\") or {}\n if tool_name:\n actions.append({\"tool\": tool_name, \"kwargs\": tool_kwargs})\n \n elif art_type == \"file_write\":\n # Stability agent is strictly read-only and monitoring\n actions.append({\"tool\": \"run_shell_command\", \"kwargs\": {\"command\": \"echo 'SAFETY ERROR: file_write tool is blocked. Use approved monitoring tools or run_shell_command instead.'\"}})\n \n elif art_type == \"file_update_multi\":\n actions.append({\"tool\": \"run_shell_command\", \"kwargs\": {\"command\": \"echo 'SAFETY ERROR: file_update_multi tool is blocked. You are a read-only monitoring agent.'\"}})\n \n elif art_type == \"terminal\":\n actions.append({\"tool\": \"run_shell_command\", \"kwargs\": {\"command\": art.get(\"code\", \"\")}})\n return actions\n\n\n# ---------------------------------------------------------------------------\n# Safety & Approval\n# ---------------------------------------------------------------------------\n\nFORBIDDEN_PATTERNS = [\n # Delete / destructive operations\n \"rm \", \"rmdir \", \"unlink \", \"truncate \",\n # System and boot files\n \"/boot\", \"/etc\", \"/dev\", \"/sys\", \"/proc\", \"mkfs\", \"dd \", \"fdisk\", \"parted\",\n # Heavy processing and system state\n \"stress\", \"yes \", \":(){ :|:& };:\", \"reboot\", \"shutdown\", \"init 0\", \"init 6\", \"halt\"\n]\nSENSITIVE_TOOLS = [\"terminal\", \"vscode_terminal_run\", \"run_shell_command\"]\n\n\ndef pre_execution_validate(actions: list) -> list:\n errors = []\n for act in actions:\n tool, kwargs = act.get(\"tool\"), act.get(\"kwargs\", {})\n path = kwargs.get(\"path\") or kwargs.get(\"file_path\")\n if path and (path.startswith(\"/\") or \"..\" in path):\n errors.append(f\"Safety Violation: Path '{path}' is absolute or contains '..'\")\n if tool in SENSITIVE_TOOLS:\n cmd = (kwargs.get(\"command\") or kwargs.get(\"code\") or \"\").strip()\n for pat in FORBIDDEN_PATTERNS:\n if pat in cmd:\n errors.append(f\"Safety Violation: Command '{cmd}' contains forbidden pattern '{pat}'\")\n return errors\n\n\ndef is_sensitive_action(actions: list) -> bool:\n # All commands not caught by the blacklist are automatically allowed\n return False\n\n\nasync def check_and_ask_approval(actions: list) -> tuple[bool, list]:\n if not is_sensitive_action(actions):\n return True, actions\n from giyu.server import vscode_ipc_context\n ipc_call = vscode_ipc_context.get()\n if not ipc_call:\n return True, actions\n try:\n raw_res = await ipc_call(\"ask_approval\", {\"actions\": actions})\n res = json.loads(raw_res)\n if res.get(\"decision\") == \"approved\":\n return True, res.get(\"modified_actions\", actions)\n return False, actions\n except Exception as e:\n print(f\"[LOOP ERROR] Approval request failed: {e}\")\n return False, actions\n\n\n# ---------------------------------------------------------------------------\n# Cleanup & Background\n# ---------------------------------------------------------------------------\n\nALL_STATE_FILES = [TASK_FILE, PLAN_FILE, GENERATION_FILE, STATE_FILE]\n\n\ndef cleanup_state_files():\n for f in ALL_STATE_FILES:\n if os.path.exists(f):\n try: os.remove(f)\n except Exception: pass\n\n\ndef is_all_done() -> bool:\n try:\n data = _load_tasks()\n return all(t.get(\"status\") == \"done\" for t in data.get(\"tasks\", []))\n except Exception:\n return False\n\n\ndef schedule_background(bg_tasks: set, coro):\n task = asyncio.create_task(coro)\n bg_tasks.add(task)\n def _on_done(t):\n bg_tasks.discard(t)\n try: _ = t.exception()\n except Exception: pass\n task.add_done_callback(_on_done)\n\n\n# ---------------------------------------------------------------------------\n# Phase helpers (ctx = dict with thinker, planner, generator, reflector,\n# actor, analyzer, llm, bg, retries)\n# ---------------------------------------------------------------------------\n\ndef finalize_task(task_id, task, failed, summaries):\n \"\"\"Mark task done/failed and append to summaries.\"\"\"\n status = \"failed\" if failed else \"done\"\n _mark_task(task_id, status)\n _update_state(task_id, status, task.get(\"title\"))\n summaries.append(f\"{'✗' if failed else '✓'} [{task.get('priority')}] {task.get('title')}\")\n\n\nasync def stability_check(ctx, memory, session_id) -> Dict[str, Any]:\n \"\"\"\n Performs a deterministic stability check.\n Returns the stability report.\n \"\"\"\n # 1. Get metrics via tool (using SystemSnapshotReader)\n snapshot_result = await ctx[\"actor\"].execute({\"actions\": [{\"tool\": \"system_snapshot_reader\", \"kwargs\": {}}]})\n import ast\n try:\n raw_metrics = ast.literal_eval(snapshot_result[0].output)\n except:\n raw_metrics = {}\n \n try:\n # 2. Get network latency (quick ping)\n try:\n import subprocess\n # Use -c 1 for a single ping, -W 1 for 1s timeout\n ping_out = subprocess.check_output([\"ping\", \"-c\", \"1\", \"-W\", \"1\", \"8.8.8.8\"], stderr=subprocess.STDOUT).decode()\n # Extract time=XX.X ms\n import re\n match = re.search(r\"time=([\\d.]+)\\s*ms\", ping_out)\n net_latency = float(match.group(1)) if match else 0\n except:\n net_latency = 0\n\n metrics = {\n \"cpu_usage\": raw_metrics.get(\"cpu_percent\", 50),\n \"ram_usage\": raw_metrics.get(\"ram_percent\", 50),\n \"disk_usage\": raw_metrics.get(\"disk_percent\", 50),\n \"net_latency\": net_latency,\n \"process_count\": raw_metrics.get(\"process_count\", 0)\n }\n except:\n metrics = {\"cpu_usage\": 50, \"ram_usage\": 50, \"disk_usage\": 50}\n \n # 2. Compute score\n stability_data = ctx[\"stability_scorer\"].compute_score(metrics)\n \n # 3. Decision Gate\n decision = ctx[\"decision_gate\"].evaluate(stability_data)\n \n report = {**stability_data, **decision, \"metrics\": metrics}\n \n from ..helpers.backbone import add_stability_report, update_escalation_status\n\n # Log and memory\n log_agent_action(\"stability_scorer\", \"compute_score\", metrics, report, \"success\")\n await memory.add_interaction(session_id, \"system\", f\"Stability Report: {report['state'].upper()} (Score: {report['score']}) - Escalation: {report['escalation_level']}\")\n \n # Save to backbone context\n add_stability_report(report)\n update_escalation_status(report[\"escalation_level\"], report[\"decision_reason\"])\n \n # Trigger escalation if needed\n if report[\"escalation_level\"] in [\"emergency\", \"escalate\"]:\n await ctx[\"actor\"].execute({\"actions\": [{\"tool\": \"core_escalation_trigger\", \"kwargs\": {\"level\": report[\"escalation_level\"], \"message\": f\"Stability compromised: {report['state']}\"}}]})\n \n return report\n\n\nasync def init_phase(ctx, prompt, memory, session_id, is_resume) -> str:\n \"\"\"Think phase (or resume). Returns objective metadata string.\"\"\"\n if is_resume:\n log_agent_action(\"loop\", \"resume_execution\", {}, {\"status\": \"resuming\"}, \"success\")\n _reset_failed_tasks()\n _reset_failed_plan_steps()\n return \"Resuming previous task list...\"\n\n _clear_state()\n \n # NEW: Stability check at the start\n await stability_check(ctx, memory, session_id)\n \n analyze_task = asyncio.create_task(ctx[\"analyzer\"].analyze_workspace(prompt))\n objective = await ctx[\"thinker\"].analyze(prompt, memory, session_id)\n log_agent_action(\"thinker\", \"analyze_prompt\", {\"prompt\": prompt}, objective, \"success\")\n\n try:\n _init_state_from_tasks(_load_tasks().get(\"tasks\", []))\n except Exception:\n pass\n try:\n analysis = await asyncio.wait_for(analyze_task, timeout=5.0)\n memory.session.set(\"project_analysis\", analysis)\n except Exception:\n pass\n\n memory.session.set(\"current_objective\", objective)\n return objective\n\n\nasync def ensure_plan_steps(ctx, task, task_id) -> list:\n steps = _get_pending_plan_steps(task_id)\n if not steps:\n await ctx[\"planner\"].generate_plan_steps(task)\n steps = _get_pending_plan_steps(task_id)\n log_agent_action(\"planner\", \"generate_plan_steps\", {\"task\": task}, {\"plan_steps\": steps}, \"success\")\n return steps\n\n\nasync def fast_answer(ctx, prompt, memory, session_id) -> str:\n context = await memory.get_full_context(session_id, query=prompt)\n ans = await ctx[\"llm\"].generate(build_fast_answer_prompt(context, prompt), session_id=session_id)\n await memory.add_interaction(session_id, \"assistant\", ans)\n return ans\n\n\nasync def fast_answer_stream(ctx, prompt, memory, session_id):\n yield {\"type\": \"status\", \"role\": \"analyzer\", \"content\": \"⚡ Fast Answer mode active...\"}\n context = await memory.get_full_context(session_id, query=prompt)\n async for chunk in ctx[\"llm\"].generate_stream(build_fast_answer_prompt(context, prompt), session_id=session_id):\n yield {\"type\": \"chunk\", \"content\": chunk}\n await memory.add_interaction(session_id, \"assistant\", \"Fast answer generated.\")\n\n\nasync def validate_and_execute(ctx, actions) -> tuple[str, list, bool]:\n \"\"\"Validate, approve, execute. Returns (result_str, final_actions, was_executed).\"\"\"\n errors = pre_execution_validate(actions)\n if errors:\n log_agent_action(\"loop\", \"pre_execution_validate\", {\"actions\": actions}, {\"errors\": errors}, \"failed\")\n return \"Pre-execution validation failed: \" + \"; \".join(errors), actions, False\n approved, actions = await check_and_ask_approval(actions)\n if not approved:\n log_agent_action(\"loop\", \"ask_approval\", {\"actions\": actions}, {\"result\": \"denied\"}, \"failed\")\n return \"Execution denied by user.\", actions, False\n result = await ctx[\"actor\"].execute({\"actions\": actions})\n log_agent_action(\"actor\", \"execute_actions\", {\"actions\": actions}, {\"result\": result}, \"success\")\n return result, actions, True\n\n\nasync def handle_syntax_error(ctx, step, task, blocks, memory, session_id, attempt=0):\n \"\"\"Log, reflect on syntax error. Returns (result_text, reflection).\"\"\"\n error_msg = next((b.get(\"error\") for b in blocks if b.get(\"status\") == \"syntax_error\"), \"Syntax validation failed\")\n log_agent_action(\"generator\", \"generate_step\", {\"step\": step, \"task\": task}, {\"error\": error_msg}, \"failed\")\n approach = step.get(\"solution\", {}).get(\"approach\", \"\")\n reflection = await ctx[\"reflector\"].reflect(approach, {\"actions\": []}, f\"Generation failed: {error_msg}\")\n log_agent_action(\"reflector\", \"reflect\", {\"approach\": approach}, reflection, \"success\")\n text = f\"\\nStep '{step.get('type')}' (attempt {attempt + 1}):\\n Result: Syntax Error\\n Reflection: {reflection['reflection']}\\n\"\n schedule_background(ctx[\"bg\"], memory.add_interaction(\n session_id, \"system\", f\"Step: {step.get('type')} | Result: Syntax Error | Reflection: {reflection['reflection']}\"))\n return text, reflection\n\n\nasync def execute_step(ctx, step, task, memory, session_id) -> tuple[bool, str, int]:\n \"\"\"Generate → execute → reflect for one step (non-streaming). Returns (ok, text, action_count).\"\"\"\n result_text, total_cnt = \"\", 0\n for attempt in range(ctx[\"retries\"]):\n gen = await ctx[\"generator\"].generate_step(step, task)\n blocks = gen.get(\"generation_blocks\", [])\n if any(b.get(\"status\") == \"syntax_error\" for b in blocks):\n txt, _ = await handle_syntax_error(ctx, step, task, blocks, memory, session_id, attempt)\n result_text += txt\n continue\n log_agent_action(\"generator\", \"generate_step\", {\"step\": step, \"task\": task}, gen, \"success\")\n actions = map_artifacts_to_actions(blocks)\n if not actions:\n return True, result_text, total_cnt\n action_result, actions, executed = await validate_and_execute(ctx, actions)\n if executed:\n total_cnt += len(actions)\n approach = step.get(\"solution\", {}).get(\"approach\", \"\")\n reflection = await ctx[\"reflector\"].reflect(approach, {\"actions\": actions}, action_result)\n log_agent_action(\"reflector\", \"reflect\", {\"approach\": approach, \"actions\": actions, \"result\": action_result}, reflection, \"success\")\n result_text += f\"\\nStep '{step.get('type')}' (attempt {attempt + 1}):\\n Result: {action_result}\\n Reflection: {reflection['reflection']}\\n\"\n schedule_background(ctx[\"bg\"], memory.add_interaction(\n session_id, \"system\", f\"Step: {step.get('type')} | Result: {action_result} | Reflection: {reflection['reflection']}\"))\n if reflection[\"is_complete\"]:\n return True, result_text, total_cnt\n return False, result_text, total_cnt\n\n\n# ---------------------------------------------------------------------------\n# Streaming step execution (async generator)\n# ---------------------------------------------------------------------------\n\nclass StepResult:\n \"\"\"Mutable container passed into stream_task_steps.\"\"\"\n __slots__ = (\"failed\", \"text\", \"action_count\")\n def __init__(self):\n self.failed = False\n self.text = \"\"\n self.action_count = 0\n\n\nasync def stream_task_steps(ctx, task, task_id, memory, session_id, result: StepResult):\n \"\"\"Async generator: process all plan steps for a task, yielding stream events.\"\"\"\n steps = _get_pending_plan_steps(task_id)\n if not steps:\n yield {\"type\": \"status\", \"role\": \"planner\", \"content\": \" ↳ Generating Plan Steps...\"}\n steps = await ensure_plan_steps(ctx, task, task_id)\n if not steps:\n _mark_task(task_id, \"done\")\n yield {\"type\": \"chunk\", \"role\": \"planner\", \"content\": \" ↳ No steps required.\\n\"}\n return\n\n step_attempts = {}\n while True:\n exec_steps = _get_executable_plan_steps(task_id)\n if not exec_steps:\n if _get_pending_plan_steps(task_id):\n result.failed = True\n break\n to_run = []\n for s in exec_steps:\n sid = s.get(\"plan_step_id\")\n step_attempts[sid] = step_attempts.get(sid, 0) + 1\n if step_attempts[sid] > ctx[\"retries\"]:\n _mark_plan_step(sid, \"failed\")\n result.failed = True\n else:\n to_run.append(s)\n if result.failed or not to_run:\n break\n\n yield {\"type\": \"status\", \"role\": \"actor\", \"content\": f\" ↳ Generating ({len(to_run)} steps)...\"}\n gen_results = await asyncio.gather(\n *[ctx[\"generator\"].generate_step(s, task) for s in to_run], return_exceptions=True)\n\n for step, gen in zip(to_run, gen_results):\n sid = step.get(\"plan_step_id\")\n if isinstance(gen, Exception):\n yield {\"type\": \"chunk\", \"role\": \"system\", \"content\": f\" ↳ ⚠ {gen}\\n\"}\n continue\n approach = step.get(\"solution\", {}).get(\"approach\", \"\")\n yield {\"type\": \"chunk\", \"role\": \"planner\",\n \"content\": f\" ↳ Step {step.get('step_index', '?')} ({step.get('type')}): {approach[:60]}...\\n\"}\n blocks = gen.get(\"generation_blocks\", [])\n\n if any(b.get(\"status\") == \"syntax_error\" for b in blocks):\n txt, ref = await handle_syntax_error(ctx, step, task, blocks, memory, session_id)\n result.text += txt\n yield {\"type\": \"chunk\", \"role\": \"reflector\", \"content\": f\" ↳ ⚠ Retry: {ref['reflection']}\\n\"}\n continue\n\n log_agent_action(\"generator\", \"generate_step\", {\"step\": step, \"task\": task}, gen, \"success\")\n actions = map_artifacts_to_actions(blocks)\n if not actions:\n _mark_plan_step(sid, \"done\")\n yield {\"type\": \"chunk\", \"role\": \"actor\", \"content\": \" ↳ Done (No actions needed)\\n\"}\n continue\n\n yield {\"type\": \"status\", \"role\": \"actor\", \"content\": f\" ↳ Executing {len(actions)} actions...\"}\n action_result, actions, executed = await validate_and_execute(ctx, actions)\n if executed:\n result.action_count += len(actions)\n def _fmt(res):\n if isinstance(res, list):\n return \"\\n\".join(str(getattr(r, \"output\", r)) for r in res)\n return str(res)\n fmt_out = _fmt(action_result).strip()\n if fmt_out:\n yield {\"type\": \"chunk\", \"role\": \"actor\", \"content\": f\"\\n\\n**Command Execution Result:**\\n```text\\n{fmt_out}\\n```\\n\\n\"}\n else:\n label = \"⛔\" if is_sensitive_action(actions) else \"⚠\"\n yield {\"type\": \"chunk\", \"role\": \"system\", \"content\": f\" ↳ {label} {action_result}\\n\"}\n\n reflection = await ctx[\"reflector\"].reflect(approach, {\"actions\": actions}, action_result)\n log_agent_action(\"reflector\", \"reflect\",\n {\"approach\": approach, \"actions\": actions, \"result\": action_result}, reflection, \"success\")\n result.text += f\"\\nStep '{step.get('type')}':\\nResult: {action_result}\\nReflection: {reflection['reflection']}\\n\"\n schedule_background(ctx[\"bg\"], memory.add_interaction(\n session_id, \"system\",\n f\"Step: {step.get('type')} | Result: {action_result} | Reflection: {reflection['reflection']}\"))\n\n if reflection[\"is_complete\"]:\n _mark_plan_step(sid, \"done\")\n yield {\"type\": \"chunk\", \"role\": \"reflector\", \"content\": f\" ↳ ✓ {reflection['reflection']}\\n\"}\n else:\n yield {\"type\": \"chunk\", \"role\": \"reflector\", \"content\": f\" ↳ ⚠ Retry: {reflection['reflection']}\\n\"}\n"}}, "giyu/cognition/helpers/generation.py": {"b3cfb5f5c09d1e5ccef74708329e32fdb0bc843c": {"t": 1785599331.6615837, "n": 831, "h": "b55ade8d0084ad7e", "c": "import json\nimport os\nimport threading\nfrom .backbone import get_generations, add_generation, BACKBONE_FILE\n\nGENERATION_FILE = BACKBONE_FILE\n\ndef _load_generation() -> dict:\n return {\"generation_blocks\": get_generations()}\n\ndef _save_generation(data: dict) -> None:\n # This is a bit tricky because _save_generation normally overwrites.\n # In the unified backbone, we might want to just append or replace the generations list.\n from .backbone import _load_backbone, _save_backbone\n backbone = _load_backbone()\n backbone[\"generations\"] = data.get(\"generation_blocks\", [])\n _save_backbone(backbone)\n\ndef _get_generation_blocks(plan_step_id: int) -> list:\n return [b for b in get_generations() if b.get(\"plan_step_id\") == plan_step_id]\n\ndef _add_generation_block(block: dict) -> None:\n add_generation(block)\n"}}, "giyu/cognition/helpers/observability.py": {"9c4db70cd7d15e94e88b9933139ef9aeef1c3608": {"t": 1785599331.7015705, "n": 1170, "h": "e7d8adcb9a37ba87", "c": "import json\nimport uuid\nimport datetime\nimport os\n\nTRACE_FILE = \"giyu.trace.jsonl\"\n\ndef clear_logs() -> None:\n \"\"\"Clear the trace log file for a new session/run.\"\"\"\n if os.path.exists(TRACE_FILE):\n os.remove(TRACE_FILE)\n\ndef log_agent_action(agent: str, action: str, input_data: dict | str, output_data: dict | str | list, status: str) -> None:\n \"\"\"\n Append an agent execution step to the JSONL trace log.\n Schema:\n {\n \"log_id\": \"uuid\",\n \"agent\": \"generator\",\n \"action\": \"generate_code\",\n \"input\": {},\n \"output\": {},\n \"status\": \"success | failed\",\n \"timestamp\": \"ISO8601\"\n }\n \"\"\"\n log_entry = {\n \"log_id\": str(uuid.uuid4()),\n \"agent\": agent,\n \"action\": action,\n \"input\": input_data,\n \"output\": output_data,\n \"status\": status,\n \"timestamp\": datetime.datetime.utcnow().isoformat() + \"Z\"\n }\n \n with open(TRACE_FILE, \"a\", encoding=\"utf-8\") as f:\n # dump with ensure_ascii=False to handle characters properly and compact separators to stay on one line\n f.write(json.dumps(log_entry, ensure_ascii=False, separators=(',', ':')) + \"\\n\")\n"}}, "giyu/cognition/helpers/plan.py": {"f5a0e73eb407ada5f5e7da233d80d7da5e0ae848": {"t": 1785599331.7179315, "n": 1916, "h": "336d57ac22e34eb1", "c": "import json\nimport os\nimport threading\nfrom .backbone import get_plan_steps, save_plan_steps, update_plan_step_status, BACKBONE_FILE\n\nPLAN_FILE = BACKBONE_FILE\n\ndef _load_plan() -> dict:\n return {\"plan_steps\": get_plan_steps()}\n\ndef _save_plan(data: dict) -> None:\n save_plan_steps(data.get(\"plan_steps\", []))\n\ndef _mark_plan_step(step_id: int, status: str) -> None:\n update_plan_step_status(step_id, status)\n\ndef _reset_failed_plan_steps() -> None:\n \"\"\"Reset any failed plan steps back to pending for resume.\"\"\"\n steps = get_plan_steps()\n modified = False\n for s in steps:\n if s.get(\"status\") == \"failed\":\n s[\"status\"] = \"pending\"\n modified = True\n if modified:\n save_plan_steps(steps)\n\ndef _get_executable_plan_steps(task_id: int) -> list:\n all_steps = get_plan_steps()\n step_status_map = {s.get(\"plan_step_id\"): s.get(\"status\") for s in all_steps}\n \n executable = []\n for s in all_steps:\n if s.get(\"task_id\") != task_id or s.get(\"status\") != \"pending\":\n continue\n \n deps = s.get(\"dependencies\", [])\n deps_met = True\n deps_failed = False\n for d in deps:\n st = step_status_map.get(d, \"done\")\n if st == \"failed\":\n deps_failed = True\n elif st != \"done\":\n deps_met = False\n \n if deps_failed:\n update_plan_step_status(s.get(\"plan_step_id\"), \"failed\")\n continue\n \n if deps_met:\n executable.append(s)\n \n return sorted(executable, key=lambda s: s.get(\"step_index\", 99))\n\ndef _get_pending_plan_steps(task_id: int) -> list:\n \"\"\"Returns ALL pending steps regardless of dependencies, useful for checking if a task is fully finished.\"\"\"\n return [s for s in get_plan_steps() if s.get(\"task_id\") == task_id and s.get(\"status\") == \"pending\"]\n"}}, "giyu/cognition/helpers/backbone.py": {"74352e14dd5dd4dcc0d80181da9f4854c6ad0074": {"t": 1785599331.8265996, "n": 6011, "h": "c6f86ac700c02956", "c": "import json\nimport os\nimport threading\nimport uuid\nfrom datetime import datetime\n\nBACKBONE_FILE = \"giyu.context.json\"\n_backbone_lock = threading.Lock()\n\ndef _load_backbone() -> dict:\n with _backbone_lock:\n if not os.path.exists(BACKBONE_FILE):\n return {\n \"context_id\": str(uuid.uuid4()),\n \"metadata\": {\n \"created_at\": datetime.now().isoformat(),\n \"updated_at\": datetime.now().isoformat(),\n \"original_prompt\": \"\"\n },\n \"tasks\": [],\n \"plans\": [],\n \"generations\": [],\n \"execution_state\": {\n \"completed_steps\": [],\n \"pending_steps\": [],\n \"failed_steps\": []\n },\n \"memory\": {},\n \"stability_reports\": [],\n \"anomalies\": [],\n \"escalation_state\": {\n \"current_level\": \"ignore\",\n \"reason\": \"\",\n \"timestamp\": \"\"\n }\n }\n try:\n with open(BACKBONE_FILE, \"r\", encoding=\"utf-8\") as f:\n return json.load(f)\n except Exception:\n return {\n \"context_id\": str(uuid.uuid4()),\n \"metadata\": {\n \"created_at\": datetime.now().isoformat(),\n \"updated_at\": datetime.now().isoformat()\n },\n \"tasks\": [],\n \"plans\": [],\n \"generations\": [],\n \"execution_state\": {\n \"completed_steps\": [],\n \"pending_steps\": [],\n \"failed_steps\": []\n },\n \"memory\": {},\n \"stability_reports\": [],\n \"anomalies\": [],\n \"escalation_state\": {\n \"current_level\": \"ignore\",\n \"reason\": \"\",\n \"timestamp\": \"\"\n }\n }\n\ndef _save_backbone(data: dict) -> None:\n data[\"metadata\"][\"updated_at\"] = datetime.now().isoformat()\n with _backbone_lock:\n with open(BACKBONE_FILE, \"w\", encoding=\"utf-8\") as f:\n json.dump(data, f, indent=2, ensure_ascii=False)\n\ndef _clear_backbone() -> None:\n with _backbone_lock:\n if os.path.exists(BACKBONE_FILE):\n try: os.remove(BACKBONE_FILE)\n except Exception: pass\n\n# --- Task Helpers ---\ndef get_tasks() -> list:\n return _load_backbone().get(\"tasks\", [])\n\ndef save_tasks(tasks: list, original_prompt: str = None) -> None:\n data = _load_backbone()\n data[\"tasks\"] = tasks\n if original_prompt:\n data[\"metadata\"][\"original_prompt\"] = original_prompt\n _save_backbone(data)\n\ndef update_task_status(task_id: int, status: str) -> None:\n data = _load_backbone()\n for t in data[\"tasks\"]:\n if t[\"id\"] == task_id:\n t[\"status\"] = status\n break\n _save_backbone(data)\n\n# --- Plan Helpers ---\ndef get_plan_steps() -> list:\n return _load_backbone().get(\"plans\", [])\n\ndef save_plan_steps(steps: list) -> None:\n data = _load_backbone()\n data[\"plans\"] = steps\n _save_backbone(data)\n\ndef update_plan_step_status(step_id: int, status: str) -> None:\n data = _load_backbone()\n for s in data[\"plans\"]:\n if s.get(\"plan_step_id\") == step_id:\n s[\"status\"] = status\n break\n _save_backbone(data)\n\n# --- Generation Helpers ---\ndef get_generations() -> list:\n return _load_backbone().get(\"generations\", [])\n\ndef add_generation(block: dict) -> None:\n data = _load_backbone()\n data.setdefault(\"generations\", []).append(block)\n _save_backbone(data)\n\n# --- State Helpers ---\ndef get_execution_state() -> dict:\n return _load_backbone().get(\"execution_state\", {})\n\ndef update_execution_state(step_id: int, status: str, title: str = \"\") -> None:\n data = _load_backbone()\n state = data[\"execution_state\"]\n \n # Remove from all\n state[\"completed_steps\"] = [s for s in state[\"completed_steps\"] if s.get(\"id\") != step_id]\n state[\"pending_steps\"] = [s for s in state[\"pending_steps\"] if s.get(\"id\") != step_id]\n state[\"failed_steps\"] = [s for s in state[\"failed_steps\"] if s.get(\"id\") != step_id]\n \n step_obj = {\"id\": step_id, \"title\": title}\n if status == \"done\":\n state[\"completed_steps\"].append(step_obj)\n elif status == \"failed\":\n state[\"failed_steps\"].append(step_obj)\n elif status == \"pending\":\n state[\"pending_steps\"].append(step_obj)\n \n _save_backbone(data)\n\ndef init_execution_state(tasks: list) -> None:\n data = _load_backbone()\n state = {\n \"completed_steps\": [],\n \"pending_steps\": [],\n \"failed_steps\": []\n }\n for t in tasks:\n state[\"pending_steps\"].append({\n \"id\": t.get(\"id\"),\n \"title\": t.get(\"title\", f\"Task {t.get('id')}\")\n })\n data[\"execution_state\"] = state\n _save_backbone(data)\n\n# --- Memory Helpers ---\ndef set_memory_value(key: str, value: any) -> None:\n data = _load_backbone()\n data.setdefault(\"memory\", {})[key] = value\n _save_backbone(data)\n\ndef get_memory_value(key: str, default: any = None) -> any:\n return _load_backbone().get(\"memory\", {}).get(key, default)\n\n# --- Stability Helpers (NEW) ---\ndef add_stability_report(report: dict) -> None:\n data = _load_backbone()\n data.setdefault(\"stability_reports\", []).append(report)\n _save_backbone(data)\n\ndef add_anomaly(anomaly: dict) -> None:\n data = _load_backbone()\n data.setdefault(\"anomalies\", []).append(anomaly)\n _save_backbone(data)\n\ndef update_escalation_status(level: str, reason: str) -> None:\n data = _load_backbone()\n data.setdefault(\"escalation_state\", {})\n data[\"escalation_state\"][\"current_level\"] = level\n data[\"escalation_state\"][\"reason\"] = reason\n data[\"escalation_state\"][\"timestamp\"] = datetime.now().isoformat()\n _save_backbone(data)\n"}}, "giyu/cognition/helpers/tasks.py": {"ca0026728bbb6cbb23dd5807ac7d46ca1bbcf072": {"t": 1785599331.8313031, "n": 1525, "h": "9cd7d4f357ba496c", "c": "import json\nimport os\nimport re\nimport threading\nfrom .backbone import get_tasks, save_tasks, update_task_status, BACKBONE_FILE\n\n# ── Task file path (kept for compatibility, though we now use BACKBONE_FILE) ────\nTASK_FILE = BACKBONE_FILE \n\ndef _load_tasks() -> dict:\n return {\"tasks\": get_tasks()}\n\ndef _save_tasks(data: dict) -> None:\n save_tasks(data.get(\"tasks\", []), data.get(\"original_prompt\"))\n\ndef _mark_task(task_id: int, status: str) -> None:\n update_task_status(task_id, status)\n\ndef _reset_failed_tasks() -> None:\n \"\"\"Reset any failed tasks back to pending for resume.\"\"\"\n tasks = get_tasks()\n modified = False\n for t in tasks:\n if t.get(\"status\") == \"failed\":\n t[\"status\"] = \"pending\"\n modified = True\n if modified:\n save_tasks(tasks)\n\ndef _clean_json(raw: str) -> str:\n \"\"\"Strip markdown fences and return bare JSON, handling some common malformations.\"\"\"\n s = raw.strip()\n \n # 1. Strip markdown code fences\n if \"```json\" in s:\n s = s.split(\"```json\")[1].split(\"```\")[0].strip()\n elif \"```\" in s:\n parts = s.split(\"```\")\n if len(parts) >= 3:\n s = parts[1].strip()\n else:\n s = parts[0].strip()\n \n # 2. Heuristic: Find first { and last }\n start = s.find('{')\n end = s.rfind('}')\n if start != -1 and end != -1:\n s = s[start:end+1]\n \n # 3. Handle common malformations like trailing commas\n s = re.sub(r',\\s*([\\]}])', r'\\1', s)\n \n return s\n"}}, "giyu/core/system_info.py": {"c0d4c0ab560c11d4415090a417f7e6ef1e4dbc4d": {"t": 1785599331.9937289, "n": 3735, "h": "0a8c3409b77ec02b", "c": "import os\nimport sys\nimport subprocess\nimport platform\nimport json\nimport asyncio\nfrom typing import Dict, Any\n\nclass SystemProbe:\n \"\"\"\n Handles platform-specific system information gathering and caching.\n Supports Windows and Linux with optimized commands.\n \"\"\"\n \n def __init__(self):\n self.os_type = platform.system().lower()\n self.cache: Dict[str, Any] = {}\n\n async def probe_all(self) -> Dict[str, Any]:\n \"\"\"Runs all platform-specific probes and caches the result.\"\"\"\n results = {\n \"platform\": platform.platform(),\n \"python_version\": sys.version,\n \"os\": self.os_type,\n \"architecture\": platform.machine(),\n \"cpu_count\": os.cpu_count(),\n }\n\n if self.os_type == \"windows\":\n results.update(await self._probe_windows())\n elif self.os_type == \"linux\":\n results.update(await self._probe_linux())\n else:\n # Fallback for Darwin (macOS) or others\n results[\"details\"] = \"Basic platform info gathered. Advanced probing only available for Win/Linux.\"\n\n self.cache = results\n return results\n\n async def _run_command(self, cmd: str) -> str:\n \"\"\"Helper to run shell commands asynchronously.\"\"\"\n try:\n process = await asyncio.create_subprocess_shell(\n cmd,\n stdout=asyncio.subprocess.PIPE,\n stderr=asyncio.subprocess.PIPE\n )\n stdout, _ = await process.communicate()\n return stdout.decode().strip()\n except Exception as e:\n return f\"Error executing {cmd}: {e}\"\n\n async def _probe_windows(self) -> Dict[str, Any]:\n \"\"\"Windows-specific high-speed probes.\"\"\"\n # Using PowerShell for structured data where possible\n ps_cmd = \"Get-ComputerInfo | Select-Object OsName, OsVersion, CsProcessors, TotalPhysicalMemory | ConvertTo-Json\"\n raw_json = await self._run_command(f'powershell -Command \"{ps_cmd}\"')\n \n try:\n data = json.loads(raw_json)\n return {\n \"os_name\": data.get(\"OsName\"),\n \"os_version\": data.get(\"OsVersion\"),\n \"memory_total\": f\"{int(data.get('TotalPhysicalMemory', 0)) // (1024**3)} GB\",\n \"cpu_info\": data.get(\"CsProcessors\", [{}])[0].get(\"Name\") if isinstance(data.get(\"CsProcessors\"), list) else \"Unknown\"\n }\n except:\n # Fallback to faster, raw commands\n return {\n \"os_name\": await self._run_command(\"wmic os get Caption /value\"),\n \"cpu_info\": await self._run_command(\"wmic cpu get name /value\")\n }\n\n async def _probe_linux(self) -> Dict[str, Any]:\n \"\"\"Linux-specific high-speed probes.\"\"\"\n return {\n \"os_release\": await self._run_command(\"cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2\"),\n \"kernel\": await self._run_command(\"uname -r\"),\n \"cpu_info\": await self._run_command(\"lscpu | grep 'Model name' | cut -d':' -f2\"),\n \"memory_info\": await self._run_command(\"free -h | grep Mem | awk '{print $2}'\"),\n \"disk_info\": await self._run_command(\"df -h / | tail -1 | awk '{print $2}'\")\n }\n\n def get_summary(self) -> str:\n \"\"\"Returns a string summary of the cached system info.\"\"\"\n if not self.cache:\n return \"System info not yet probed.\"\n \n summary = [f\"System Profile: {self.cache.get('platform')}\"]\n for k, v in self.cache.items():\n if k not in [\"platform\", \"details\"]:\n summary.append(f\"- {k.replace('_', ' ').title()}: {v}\")\n return \"\\n\".join(summary)\n"}}, "giyu/main.py": {"30114bead05e0fb827ef8d644e3f635b40bb32c8": {"t": 1785599332.029532, "n": 4503, "h": "659f564e7208cbe6", "c": "\"\"\"\nGiyu Agent — Main launcher.\nBoots the Textual TUI, routing to the setup wizard when config is missing,\nor directly to the full-screen chat interface otherwise.\n\"\"\"\n\nimport os\nimport re\nimport sys\nimport asyncio\nfrom pathlib import Path\nfrom dotenv import load_dotenv\nfrom textual.app import App, ComposeResult\nfrom textual.binding import Binding\nfrom textual import work\nfrom .cli.theme import GIYU_THEME\nfrom .cli.splash_screen import SplashScreen\n\n\n# ── Configuration Path ────────────────────────────────────────────────────────\n# Default to local .env, fallback to ~/.giyu/.env for global use\nENV_PATH = Path(\".env\")\nGLOBAL_CONFIG_DIR = Path.home() / \".giyu\"\nGLOBAL_ENV_PATH = GLOBAL_CONFIG_DIR / \".env\"\n\n\ndef _has_valid_config() -> bool:\n \"\"\"Return True if any .env has a non-empty API key.\"\"\"\n # Check local first, then global\n if ENV_PATH.exists():\n load_dotenv(ENV_PATH, override=True)\n elif GLOBAL_ENV_PATH.exists():\n load_dotenv(GLOBAL_ENV_PATH, override=True)\n \n key = os.getenv(\"OPENAI_API_KEY\", \"\").strip()\n return bool(key)\n\n\nclass GiyuApp(App):\n \"\"\"Root Textual application for the Giyu Agent CLI.\"\"\"\n\n TITLE = \"Giyu Agent\"\n SUB_TITLE = \"Powered by Phoenix AI\"\n CSS = \"\" # All CSS lives inside screens\n ENABLE_COMMAND_PALETTE = False # Keep UI clean\n\n BINDINGS = [\n Binding(\"ctrl+k\", \"open_config\", \"Config\", show=False, priority=True),\n Binding(\"ctrl+q\", \"quit_app\", \"Quit\", show=False, priority=True),\n ]\n\n def action_quit_app(self) -> None:\n self.exit()\n\n def action_open_config(self) -> None:\n from .cli.setup_wizard import SetupWizard\n from .cli.chat_screen import ChatScreen\n \n # If we are on the ChatScreen, use its reload callback\n if isinstance(self.screen, ChatScreen):\n self.push_screen(SetupWizard(), callback=self.screen._on_config_closed)\n else:\n self.push_screen(SetupWizard())\n\n def on_mount(self) -> None:\n \"\"\"Initializes the app and handles the splash-to-main transition.\"\"\"\n self.register_theme(GIYU_THEME)\n self.theme = \"giyu\"\n\n # 1. Immediately show splash to provide instant feedback\n self.push_screen(SplashScreen())\n\n # 2. Start background loading of the main interface\n self._load_main_interface()\n\n @work(exclusive=True)\n async def _load_main_interface(self) -> None:\n \"\"\"Loads the main screen in the background and transitions from splash.\"\"\"\n try:\n # 1. Gather System Info in background (Optimized startup)\n from .core.system_info import SystemProbe\n self.system_probe = SystemProbe()\n await self.system_probe.probe_all()\n \n # Prepare the main interface\n if _has_valid_config():\n from .cli.chat_screen import ChatScreen\n main_screen = ChatScreen()\n else:\n from .cli.setup_wizard import SetupWizard\n main_screen = SetupWizard()\n \n # Ensure splash is visible for at least 1.2s for a smooth transition\n await asyncio.sleep(1.2)\n \n # Atomic switch to the main interface\n self.switch_screen(main_screen)\n \n except Exception as e:\n # Fallback: if background load fails, try to show something\n import sys\n print(f\"Error loading main interface: {e}\", file=sys.stderr)\n self.exit()\n\n\ndef main() -> None:\n \"\"\"Entry point — handles 'giyu <dir>'.\"\"\"\n # ── Silence noisy startup logs ─────────────────────────────────────────────\n os.environ.setdefault(\"LOG_LEVEL\", \"WARNING\")\n os.environ.setdefault(\"HF_HUB_DISABLE_SYMLINKS_WARNING\", \"1\")\n os.environ.setdefault(\"HF_HUB_DISABLE_PROGRESS_BARS\", \"1\")\n\n # Handle directory argument (e.g. 'giyu .')\n if len(sys.argv) > 1:\n target_arg = sys.argv[1]\n target_dir = Path(target_arg).resolve() # Use resolve() to get absolute path\n if target_dir.is_dir():\n print(f\"[*] Switching to directory: {target_dir}\")\n os.chdir(target_dir)\n else:\n print(f\"[!] Error: {target_arg} is not a valid directory.\")\n sys.exit(1)\n else:\n # If no arg, ensure we work in current shell CWD\n print(f\"[*] Working in directory: {os.getcwd()}\")\n\n app = GiyuApp()\n app.run()\n\n\nif __name__ == \"__main__\":\n main()\n"}}, "giyu/cognition/loop.py": {"4b964eef8e84a1537ea04ce3ba84ac44aa619d58": {"t": 1785599332.4301443, "n": 7130, "h": "65bf73469645f712", "c": "from phoenix.framework.agent.core.loop import AgentLoop\nimport asyncio\n\nfrom .helpers.tasks import _load_tasks, _mark_task\nfrom .helpers.plan import _get_pending_plan_steps, _mark_plan_step\nfrom .helpers.observability import clear_logs\nfrom .brains.generator import GiyuGenerator\nfrom .core.utils import (\n get_pending_tasks, get_executable_tasks, has_task_file,\n cleanup_state_files, is_all_done,\n init_phase, ensure_plan_steps, execute_step,\n fast_answer, fast_answer_stream, finalize_task,\n validate_and_execute, stream_task_steps, StepResult,\n)\n\n\nclass GiyuLoop(AgentLoop):\n \"\"\"\n Task-file driven agent loop:\n Thinker → Planner → Generator → Actor.\n \"\"\"\n\n MAX_RETRIES = 2\n MAX_ACTIONS = 30\n\n def _load_pipeline_spec(self, filename: str):\n \"\"\"\n Bypass missing default JSON specs in phoenix site-packages.\n GiyuLoop re-implements run() and run_stream() using direct orchestration,\n so it doesn't strictly need these legacy pipelines.\n \"\"\"\n try:\n return super()._load_pipeline_spec(filename)\n except Exception:\n from phoenix.framework.agent.cognition.pipeline import CognitionPipeline\n return CognitionPipeline({\n \"name\": \"giyu_dummy_pipeline\",\n \"steps\": [{\"id\": \"init\", \"brain\": \"thinker\", \"output_key\": \"init_out\"}]\n })\n\n def __init__(self, thinker, planner, actor, reflector, analyzer, \n stability_scorer=None, decision_gate=None, correlation_engine=None, generator=None):\n super().__init__(thinker, planner, actor, reflector, analyzer)\n self.stability_scorer = stability_scorer\n self.decision_gate = decision_gate\n self.correlation_engine = correlation_engine\n self.generator = generator or GiyuGenerator(self.planner.llm)\n\n @property\n def _ctx(self):\n \"\"\"Bundle components into a dict for utility functions.\"\"\"\n return dict(\n thinker=self.thinker, planner=self.planner, generator=self.generator,\n reflector=self.reflector, actor=self.actor, analyzer=self.analyzer,\n stability_scorer=self.stability_scorer,\n decision_gate=self.decision_gate,\n correlation_engine=self.correlation_engine,\n llm=self.planner.llm, bg=self._background_tasks, retries=self.MAX_RETRIES,\n )\n\n # ------------------------------------------------------------------\n # Non-streaming\n # ------------------------------------------------------------------\n\n async def run(self, prompt: str, memory, session_id: str, mode: str = \"auto\", **kw) -> str:\n clear_logs()\n ctx = self._ctx\n is_resume = prompt.strip().lower() == \"resume\" or mode == \"resume\"\n\n if mode == \"fast_ans\" and not is_resume:\n return await fast_answer(ctx, prompt, memory, session_id)\n\n obj = await init_phase(ctx, prompt, memory, session_id, is_resume)\n await memory.add_interaction(session_id, \"system\", f\"Task breakdown: {obj}\")\n\n results, summaries, total = \"\", [], 0\n\n while has_task_file() and total < self.MAX_ACTIONS:\n pending = get_pending_tasks()\n if not pending:\n break\n task, tid = pending[0], pending[0].get(\"id\")\n\n steps = await ensure_plan_steps(ctx, task, tid)\n if not steps:\n _mark_task(tid, \"done\")\n continue\n\n failed = False\n for step in steps:\n sid = step.get(\"plan_step_id\")\n ok, txt, cnt = await execute_step(ctx, step, task, memory, session_id)\n total += cnt\n results += txt\n if ok:\n _mark_plan_step(sid, \"done\")\n else:\n _mark_plan_step(sid, \"failed\")\n failed = True\n break\n\n finalize_task(tid, task, failed, summaries)\n\n if is_all_done():\n cleanup_state_files()\n\n summary = \"\\n\".join(summaries) or \"No tasks were executed.\"\n answer = f\"**Giyu Task Execution Complete**\\n\\n{summary}\\n\\n---\\n{results.strip()}\"\n await memory.add_interaction(session_id, \"assistant\", answer)\n return answer\n\n # ------------------------------------------------------------------\n # Streaming\n # ------------------------------------------------------------------\n\n async def run_stream(self, prompt: str, memory, session_id: str, mode: str = \"auto\", **kw):\n clear_logs()\n ctx = self._ctx\n is_resume = prompt.strip().lower() == \"resume\" or mode == \"resume\"\n\n if mode == \"fast_ans\" and not is_resume:\n async for ev in fast_answer_stream(ctx, prompt, memory, session_id):\n yield ev\n return\n\n yield {\"type\": \"status\", \"role\": \"system\" if is_resume else \"thinker\",\n \"content\": \"🔄 Resuming...\" if is_resume else \"🧠 Decomposing your request...\"}\n\n obj = await init_phase(ctx, prompt, memory, session_id, is_resume)\n memory.session.set(\"current_objective\", obj)\n yield {\"type\": \"status\", \"role\": \"thinker\", \"content\": \"📋 Tasks Breakdown Complete\"}\n await memory.add_interaction(session_id, \"system\", f\"Task breakdown: {obj}\")\n\n results, summaries, total, task_num = \"\", [], 0, 0\n\n while has_task_file() and total < self.MAX_ACTIONS:\n executable = get_executable_tasks()\n if not executable:\n if get_pending_tasks():\n yield {\"type\": \"chunk\", \"role\": \"system\", \"content\": \"\\n⚠ Deadlock\\n\"}\n break\n\n task, tid = executable[0], executable[0].get(\"id\")\n task_num += 1\n\n try:\n count = len(_load_tasks().get(\"tasks\", []))\n except Exception:\n count = \"?\"\n\n yield {\"type\": \"status\", \"role\": \"planner\",\n \"content\": f\"⚙ Task {task_num}/{count}: {task.get('title')}\"}\n yield {\"type\": \"chunk\", \"role\": \"planner\",\n \"content\": f\"\\n**[P{task.get('priority')}] {task.get('title')}**\\n\"}\n\n # Delegate step processing to async generator\n sr = StepResult()\n async for ev in stream_task_steps(ctx, task, tid, memory, session_id, sr):\n yield ev\n\n total += sr.action_count\n results += sr.text\n finalize_task(tid, task, sr.failed, summaries)\n\n icon, msg = (\"✗\", \"Task failed.\") if sr.failed else (\"✓\", \"Task complete.\")\n yield {\"type\": \"chunk\", \"role\": \"reflector\", \"content\": f\" ↳ {icon} {msg}\\n\"}\n\n if is_all_done():\n yield {\"type\": \"status\", \"content\": \"🗑 Cleaning up...\"}\n cleanup_state_files()\n\n summary = \"\\n\".join(summaries) or \"No tasks were executed.\"\n yield {\"type\": \"chunk\", \"content\": f\"\\n\\n---\\n**All tasks complete!**\\n\\n{summary}\\n\"}\n await memory.add_interaction(session_id, \"assistant\",\n f\"Tasks complete:\\n{summary}\\n\\n{results.strip()}\")\n"}}, "giyu/cognition/helpers/schemas.py": {"f0cf3db207252362ca2f5c69403aa170bfb624fd": {"t": 1785599332.444846, "n": 3671, "h": "80c23ba816b84ab0", "c": "import json\n\n# --- Schema Definitions ---\n\nTASK_SCHEMA = {\n \"type\": \"object\",\n \"required\": [\"original_prompt\", \"tasks\"],\n \"properties\": {\n \"tasks\": {\n \"type\": \"array\",\n \"items\": {\n \"required\": [\"id\", \"priority\", \"type\", \"title\", \"status\"],\n \"properties\": {\n \"type\": {\"enum\": [\"new_file\", \"modify_file\", \"command\", \"read\"]}\n }\n }\n }\n }\n}\n\nPLAN_SCHEMA = {\n \"type\": \"object\",\n \"required\": [\"plan_steps\"],\n \"properties\": {\n \"plan_steps\": {\n \"type\": \"array\",\n \"items\": {\n \"required\": [\"plan_step_id\", \"task_id\", \"type\", \"status\", \"solution\"],\n \"properties\": {\n \"type\": {\"enum\": [\"analysis\", \"design\", \"implementation\", \"validation\"]}\n }\n }\n }\n }\n}\n\nGENERATION_SCHEMA = {\n \"type\": \"object\",\n \"required\": [\"generation_blocks\"],\n \"properties\": {\n \"generation_blocks\": {\n \"type\": \"array\",\n \"items\": {\n \"required\": [\"generate_block_id\", \"plan_step_id\", \"artifacts\", \"status\"],\n \"properties\": {\n \"artifacts\": {\n \"type\": \"array\",\n \"items\": {\n \"required\": [\"type\", \"path\"],\n \"properties\": {\n \"type\": {\"enum\": [\"file_write\", \"file_update_multi\", \"terminal\"]},\n \"edits\": {\n \"type\": \"array\",\n \"items\": {\n \"required\": [\"AllowMultiple\", \"StartLine\", \"EndLine\", \"TargetContent\", \"ReplacementContent\"]\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n\ndef validate_schema(data: dict, schema: dict) -> list:\n \"\"\"Very basic schema validator. Returns a list of error strings.\"\"\"\n errors = []\n \n # Check required top-level keys\n for key in schema.get(\"required\", []):\n if key not in data:\n errors.append(f\"Missing required key: '{key}'\")\n \n # Check arrays\n for key, prop in schema.get(\"properties\", {}).items():\n if key in data:\n val = data[key]\n if prop.get(\"type\") == \"array\":\n if not isinstance(val, list):\n errors.append(f\"Key '{key}' must be an array\")\n continue\n \n # Check items in array\n item_schema = prop.get(\"items\", {})\n required_items = item_schema.get(\"required\", [])\n for i, item in enumerate(val):\n if not isinstance(item, dict):\n errors.append(f\"Item {i} in '{key}' must be an object\")\n continue\n for k in required_items:\n if k not in item:\n errors.append(f\"Item {i} in '{key}' missing required key: '{k}'\")\n \n # Check enums\n item_props = item_schema.get(\"properties\", {})\n for k, v in item_props.items():\n if k in item and \"enum\" in v:\n if item[k] not in v[\"enum\"]:\n errors.append(f\"Item {i} in '{key}' key '{k}' has invalid value '{item[k]}'. Expected one of {v['enum']}\")\n \n return errors\n"}}, "giyu/tools/__init__.py": {"fd997442e8f10c9eca37a9170874f7b9a32ec854": {"t": 1785599332.8119335, "n": 67, "h": "e2321436ad5152a5", "c": "# Tools init\nfrom .project_generator import project_generator_tool\n"}}, "giyu/tools/stability_tools.py": {"d9f3f9b363682613fcbdedcdeec0ecea61cdec6b": {"t": 1785599332.8540235, "n": 9396, "h": "a10769f268aed15e", "c": "import psutil\nimport os\nimport time\nimport subprocess\nfrom typing import List, Dict, Any\nfrom phoenix.framework.agent.tools.base import BaseTool, ToolResult\n\nclass SystemSnapshotReader(BaseTool):\n name = \"system_snapshot_reader\"\n description = \"Reads current CPU, RAM, disk, and process state.\"\n async def execute(self, **kwargs) -> ToolResult:\n try:\n snapshot = {\n \"cpu_percent\": psutil.cpu_percent(interval=1),\n \"ram_percent\": psutil.virtual_memory().percent,\n \"disk_percent\": psutil.disk_usage('/').percent,\n \"process_count\": len(psutil.pids()),\n \"timestamp\": time.time()\n }\n return ToolResult(success=True, output=str(snapshot))\n except Exception as e:\n return ToolResult(success=False, output=\"\", error=str(e))\n\nclass LogStreamAnalyzer(BaseTool):\n name = \"log_stream_analyzer\"\n description = \"Parses system logs and extracts errors, warnings, and patterns.\"\n async def execute(self, log_path: str = None, lines: int = 100, **kwargs) -> ToolResult:\n try:\n is_windows = os.name == \"nt\"\n \n if is_windows:\n # Windows Event Log fallback\n cmd = f'powershell \"Get-EventLog -LogName System -Newest {lines} | Select-Object TimeGenerated, EntryType, Source, Message | ConvertTo-Json\"'\n result = subprocess.check_output(cmd, shell=True).decode()\n return ToolResult(success=True, output=result)\n \n # Linux logic\n if log_path is None:\n log_path = \"/var/log/syslog\"\n \n if not os.path.exists(log_path):\n result = subprocess.check_output(f\"dmesg | tail -n {lines}\", shell=True).decode()\n return ToolResult(success=True, output=result)\n \n result = subprocess.check_output([\"tail\", f\"-n {lines}\", log_path]).decode()\n return ToolResult(success=True, output=result)\n except Exception as e:\n return ToolResult(success=False, output=\"\", error=str(e))\n\nclass AgentHeartbeatMonitor(BaseTool):\n name = \"agent_heartbeat_monitor\"\n description = \"Checks health status and responsiveness of all agents.\"\n async def execute(self, **kwargs) -> ToolResult:\n agents = [\"Giyu\", \"Shinobu\", \"Rengoku\", \"Mitsuri\", \"Obanai\", \"Sanemi\", \"Gyomei\", \"Tengen\", \"Muichiro\"]\n status = {}\n # Use relative path or home-based path that works on both OS\n base_path = os.path.expanduser(\"~/.giyu/hashira\")\n for agent in agents:\n path = os.path.join(base_path, agent)\n status[agent] = \"active\" if os.path.exists(path) else \"missing\"\n return ToolResult(success=True, output=str(status))\n\nclass AnomalyDetector(BaseTool):\n name = \"anomaly_detector\"\n description = \"Detects abnormal spikes or deviations in system metrics.\"\n async def execute(self, metric_name: str, value: float, threshold: float = 80.0, **kwargs) -> ToolResult:\n is_anomaly = value > threshold\n return ToolResult(success=True, output=f\"Metric: {metric_name}, Value: {value}, Anomaly: {is_anomaly}\")\n\nclass StabilityScoreCalculator(BaseTool):\n name = \"stability_score_calculator\"\n description = \"Computes overall system stability score (0–100).\"\n async def execute(self, cpu: float, ram: float, disk: float, **kwargs) -> ToolResult:\n from ..cognition.brains.stability_scorer import StabilityScorer\n scorer = StabilityScorer()\n result = scorer.compute_score({\"cpu_usage\": cpu, \"ram_usage\": ram, \"disk_usage\": disk})\n return ToolResult(success=True, output=str(result))\n\nclass ProcessStateTracker(BaseTool):\n name = \"process_state_tracker\"\n description = \"Tracks lifecycle state of running processes.\"\n async def execute(self, proc_name: str = None, **kwargs) -> ToolResult:\n procs = []\n for proc in psutil.process_iter(['pid', 'name', 'status']):\n if proc_name and proc_name.lower() not in proc.info['name'].lower():\n continue\n procs.append(proc.info)\n return ToolResult(success=True, output=str(procs[:20])) # Limit output\n\nclass RollbackRecommendationEngine(BaseTool):\n name = \"rollback_recommendation_engine\"\n description = \"Suggests safe rollback points based on system state history.\"\n async def execute(self, repo_path: str = \".\", **kwargs) -> ToolResult:\n try:\n # Check git log for recent commits\n cmd = [\"git\", \"log\", \"-n\", \"5\", \"--oneline\"]\n result = subprocess.check_output(cmd, cwd=repo_path, stderr=subprocess.STDOUT).decode()\n return ToolResult(success=True, output=f\"Recommended Rollback Points:\\n{result}\")\n except Exception as e:\n return ToolResult(success=False, output=\"\", error=f\"Git lookup failed: {str(e)}\")\n\nclass CoreEscalationTrigger(BaseTool):\n name = \"core_escalation_trigger\"\n description = \"Sends critical alerts to Core Orchestrator when system is unstable.\"\n async def execute(self, level: str, message: str, **kwargs) -> ToolResult:\n alert = f\"[ESCALATION] Level: {level}, Message: {message}, Time: {time.time()}\"\n alert_file = os.path.expanduser(\"~/.giyu/alerts.log\")\n os.makedirs(os.path.dirname(alert_file), exist_ok=True)\n with open(alert_file, \"a\") as f:\n f.write(alert + \"\\n\")\n return ToolResult(success=True, output=f\"Escalated to Core: {alert}\")\n\nclass ResourcePressureMonitor(BaseTool):\n name = \"resource_pressure_monitor\"\n description = \"Detects CPU/RAM/Disk overload situations.\"\n async def execute(self, **kwargs) -> ToolResult:\n cpu = psutil.cpu_percent()\n ram = psutil.virtual_memory().percent\n pressures = []\n if cpu > 80: pressures.append(\"High CPU Pressure\")\n if ram > 80: pressures.append(\"High RAM Pressure\")\n return ToolResult(success=True, output=str(pressures) if pressures else \"No pressure detected\")\n\nclass EventCorrelationTracker(BaseTool):\n name = \"event_correlation_tracker\"\n description = \"Connects multiple system events to identify root cause chains.\"\n async def execute(self, events: List[str], **kwargs) -> ToolResult:\n # This would ideally call the CorrelationEngine brain, but as a tool it just returns the input for now\n return ToolResult(success=True, output=f\"Tracking correlation for {len(events)} events: {events}\")\n\nclass ShellCommandTool(BaseTool):\n name = \"run_shell_command\"\n description = \"Safely executes system commands for diagnostics (e.g., 'ip addr', 'ping -c 1 8.8.8.8', 'netstat').\"\n async def execute(self, command: str, **kwargs) -> ToolResult:\n try:\n # Prevent dangerous commands (very basic check)\n forbidden = [\"rm \", \"mv \", \"dd \", \"> /dev/\", \":(){ :|:& };:\"]\n if any(f in command for f in forbidden):\n return ToolResult(success=False, output=\"\", error=\"Command rejected for safety reasons.\")\n \n result = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT).decode()\n return ToolResult(success=True, output=result)\n except Exception as e:\n return ToolResult(success=False, output=\"\", error=str(e))\nclass SystemSecurityAuditor(BaseTool):\n name = \"security_audit_tool\"\n description = \"Performs a comprehensive security audit (ports, logins, firewall, processes).\"\n async def execute(self, **kwargs) -> ToolResult:\n import platform\n report = []\n is_windows = os.name == \"nt\"\n try:\n if is_windows:\n report.append(\"### 🖥️ WINDOWS SYSTEM CORE STATUS\")\n report.append(f\"- **OS:** `{platform.platform()}`\\n- **Node:** `{platform.node()}`\")\n \n report.append(\"\\n### 🌐 NETWORK SECURITY AUDIT (Windows)\")\n try:\n net_cmd = 'powershell \"Get-NetTCPConnection | Where-Object {$_.State -eq \\'Listen\\'} | Select-Object LocalAddress, LocalPort | head -n 10 | ConvertTo-Json\"'\n report.append(\"#### Listening Ports:\\n```text\\n\" + subprocess.check_output(net_cmd, shell=True).decode() + \"\\n```\")\n except:\n report.append(\"#### Listening Ports: `PowerShell query failed`\")\n else:\n report.append(\"### 🖥️ LINUX SYSTEM CORE STATUS\")\n uname = subprocess.check_output(\"uname -a\", shell=True).decode().strip()\n report.append(f\"- **Kernel:** `{uname}`\")\n \n report.append(\"\\n### 🌐 NETWORK SECURITY AUDIT (Linux)\")\n ports = subprocess.check_output(\"ss -tuln | head -n 10\", shell=True).decode().strip()\n report.append(\"#### Listening Ports:\\n```text\\n\" + ports + \"\\n```\")\n\n # Shared Process Audit\n report.append(\"\\n### ⚙️ CRITICAL PROCESS AUDIT\")\n procs = [p.info for p in psutil.process_iter(['pid', 'name', 'username'])][:10]\n report.append(\"#### Top Processes:\\n```text\\n\" + str(procs) + \"\\n```\")\n\n output = \"\\n\".join(report)\n return ToolResult(success=True, output=output)\n except Exception as e:\n return ToolResult(success=False, output=\"\", error=f\"Audit failed: {str(e)}\")\n"}}, "giyu/tools/vscode_tools.py": {"54e0ca065b5ed2a7b8108683508685f15c06193c": {"t": 1785599332.9414747, "n": 3815, "h": "29643e75ee968e35", "c": "from phoenix.framework.agent import tool\n\n@tool(\n name=\"vscode_search\",\n description=(\n \"Searches for a text pattern or regular expression across all files in the current workspace using VS Code's native search engine. \"\n \"Input: 'query' (str), 'is_regex' (bool, default False), 'include' (str, glob pattern like '**/*.ts'), 'exclude' (str, glob pattern). \"\n \"Returns a list of matching files and line snippets.\"\n )\n)\nasync def vscode_search_tool(query: str, is_regex: bool = False, include: str = None, exclude: str = None, **context) -> str:\n \"\"\"\n Performs a workspace-wide search by calling back into the VS Code extension.\n \"\"\"\n from giyu.server import vscode_ipc_context\n vscode_call = vscode_ipc_context.get()\n \n if not vscode_call:\n return \"ERROR: VS Code communication bridge not available in this environment.\"\n\n # Request the search from VS Code\n result = await vscode_call(\"search\", {\n \"query\": query,\n \"is_regex\": is_regex,\n \"include\": include,\n \"exclude\": exclude\n })\n \n return result\n@tool(\n name=\"vscode_create_file\",\n description=(\n \"Creates a new file in the workspace with the specified content. \"\n \"Input: 'path' (str), 'content' (str). \"\n \"Returns a success message or an error.\"\n )\n)\nasync def vscode_create_file_tool(path: str, content: str, **context) -> str:\n \"\"\"\n Creates a file by calling back into the VS Code extension.\n \"\"\"\n from giyu.server import vscode_ipc_context\n vscode_call = vscode_ipc_context.get()\n if not vscode_call:\n return \"ERROR: VS Code communication bridge not available.\"\n return await vscode_call(\"create_file\", {\"path\": path, \"content\": content})\n\n@tool(\n name=\"vscode_edit_file\",\n description=(\n \"Updates an existing file in the workspace with new content. \"\n \"The user will be prompted to review a diff before applying. \"\n \"Input: 'path' (str), 'content' (str). \"\n \"Returns a success message, 'REJECTED', or an error.\"\n )\n)\nasync def vscode_edit_file_tool(path: str, content: str, **context) -> str:\n \"\"\"\n Edits a file by calling back into the VS Code extension (shows diff).\n \"\"\"\n from giyu.server import vscode_ipc_context\n vscode_call = vscode_ipc_context.get()\n if not vscode_call:\n return \"ERROR: VS Code communication bridge not available.\"\n return await vscode_call(\"edit_file\", {\"path\": path, \"content\": content})\n\n@tool(\n name=\"vscode_delete_file\",\n description=(\n \"Deletes a file or directory in the workspace. \"\n \"The user will be prompted for confirmation. \"\n \"Input: 'path' (str). \"\n \"Returns a success message, 'REJECTED', or an error.\"\n )\n)\nasync def vscode_delete_file_tool(path: str, **context) -> str:\n \"\"\"\n Deletes a file by calling back into the VS Code extension (requires confirmation).\n \"\"\"\n from giyu.server import vscode_ipc_context\n vscode_call = vscode_ipc_context.get()\n if not vscode_call:\n return \"ERROR: VS Code communication bridge not available.\"\n return await vscode_call(\"delete_file\", {\"path\": path})\n \n@tool(\n name=\"vscode_terminal_run\",\n description=(\n \"Executes a bash command in a dedicated VS Code terminal and captures the output. \"\n \"Input: 'command' (str). \"\n \"Returns the stdout/stderr of the command.\"\n )\n)\nasync def vscode_terminal_run_tool(command: str, **context) -> str:\n \"\"\"\n Executes a terminal command via the VS Code extension.\n \"\"\"\n from giyu.server import vscode_ipc_context\n vscode_call = vscode_ipc_context.get()\n if not vscode_call:\n return \"ERROR: VS Code communication bridge not available.\"\n return await vscode_call(\"terminal_run\", {\"command\": command})\n"}}, "giyu/cognition/helpers/state.py": {"ed8d94d281d5bad903d4a365404e81e3ea6b6079": {"t": 1785599332.9743903, "n": 837, "h": "fb71287d6bc155ce", "c": "import json\nimport os\nimport threading\nfrom .backbone import get_execution_state, update_execution_state, init_execution_state, _clear_backbone, BACKBONE_FILE\n\nSTATE_FILE = BACKBONE_FILE\n\ndef _load_state() -> dict:\n return {\"execution_state\": get_execution_state()}\n\ndef _save_state(data: dict) -> None:\n # Normally we update individual steps, but if needed we could replace the whole state\n from .backbone import _load_backbone, _save_backbone\n backbone = _load_backbone()\n backbone[\"execution_state\"] = data.get(\"execution_state\", {})\n _save_backbone(backbone)\n\ndef _init_state_from_tasks(tasks: list) -> None:\n init_execution_state(tasks)\n\ndef _update_state(step_id: int, status: str, title: str = \"\") -> None:\n update_execution_state(step_id, status, title)\n\ndef _clear_state() -> None:\n _clear_backbone()\n"}}, "giyu/tools/file_tools.py": {"008eb96c9586cec441e675ce8daf30b4305859e7": {"t": 1785599333.0746472, "n": 8802, "h": "2803b1354221a42e", "c": "\"\"\"\nGiyu Precision File Tools\n─────────────────────────────\nProvides two tools purpose-built to solve the \"agent nukes file\" problem:\n\n1. file_read_lines — Reads a file and prefixes every line with its number.\n The LLM can reference exact line numbers in its edits.\n\n2. file_update_multi — Applies a list of non-overlapping edits to a file.\n Each edit can be:\n • line_range → replace lines L_start..L_end with new_content\n • search → exact-string search/replace (NO upsert fallback)\n Edits are applied in REVERSE order so line numbers stay valid.\n\"\"\"\n\nimport os\nimport re\nfrom phoenix.framework.agent import tool\n\n\ndef _try_open_in_vscode(file_path: str) -> None:\n \"\"\"Best-effort: queue this file to be opened by the VS Code extension.\"\"\"\n try:\n from giyu.server import _pending_file_opens\n _pending_file_opens.append(os.path.abspath(file_path))\n except Exception:\n pass\n\n\n# ── Tool 1: Read with line numbers ────────────────────────────────────────────\n\n@tool(\n name=\"file_read_lines\",\n description=(\n \"Reads a file and returns its content with LINE NUMBERS prefixed (e.g. 'L1: import os'). \"\n \"Use this BEFORE editing an existing file so you know the exact lines to target. \"\n \"Input: 'file_path' (str). Optional: 'start_line' (int), 'end_line' (int) to read a range.\"\n )\n)\ndef file_read_lines_tool(file_path: str, start_line: int = None, end_line: int = None) -> str:\n \"\"\"\n Returns file content with line numbers prefixed as 'L<n>: <text>'.\n Supports optional start/end range for large files.\n \"\"\"\n if not os.path.exists(file_path):\n return f\"ERROR: File not found: {file_path}\"\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n lines = f.readlines()\n\n total = len(lines)\n s = (start_line - 1) if start_line and start_line > 0 else 0\n e = end_line if end_line and end_line <= total else total\n\n numbered = []\n for i, line in enumerate(lines[s:e], start=s + 1):\n numbered.append(f\"L{i}: {line.rstrip()}\")\n\n header = f\"[{file_path}] — {total} lines total\"\n if start_line or end_line:\n header += f\" (showing L{s+1}–L{e})\"\n return header + \"\\n\" + \"\\n\".join(numbered)\n except Exception as ex:\n return f\"ERROR reading {file_path}: {ex}\"\n finally:\n _try_open_in_vscode(file_path)\n\n\n# ── Tool 2: Multi-block precision editor ──────────────────────────────────────\n\n@tool(\n name=\"file_update_multi\",\n description=(\n \"Applies multiple SURGICAL edits to an existing file. \"\n \"ALWAYS use this instead of file_write when the file already exists. \"\n \"Input: 'file_path' (str), 'edits' (list of edit objects). \"\n \"Each edit object must be ONE of these two forms:\\n\"\n \" • Line-range replace: {\\\"line_start\\\": N, \\\"line_end\\\": M, \\\"new_content\\\": \\\"...\\\"} \"\n \" → replaces lines N through M (inclusive, 1-indexed) with new_content.\\n\"\n \" • Exact search/replace: {\\\"search\\\": \\\"exact text\\\", \\\"replace\\\": \\\"new text\\\"} \"\n \" → finds the FIRST occurrence of 'search' and replaces it. Fails if not found.\\n\"\n \"RULES:\\n\"\n \" 1. Use file_read_lines first to see exact content and line numbers.\\n\"\n \" 2. Keep edits minimal — only change what is necessary.\\n\"\n \" 3. Do NOT rewrite the whole file; only specify the changed blocks.\\n\"\n \" 4. Line-range edits are applied in reverse order automatically.\"\n )\n)\ndef file_update_multi_tool(file_path: str, edits: list) -> str:\n \"\"\"\n Applies a list of surgical edits to file_path.\n Supports 'line_range' (line_start, line_end, new_content) and\n 'search_replace' (search, replace) edit types.\n \"\"\"\n if not os.path.exists(file_path):\n return f\"ERROR: File not found: {file_path}. Use file_write to create new files.\"\n\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n lines = f.readlines() # preserve original line endings\n except Exception as ex:\n return f\"ERROR reading {file_path}: {ex}\"\n\n # ── Separate and validate edits ──────────────────────────────────────────\n line_edits = [] # (line_start, line_end, new_content)\n str_edits = [] # (search, replace)\n errors = []\n\n for i, edit in enumerate(edits):\n if \"line_start\" in edit and \"line_end\" in edit:\n ls = edit.get(\"line_start\")\n le = edit.get(\"line_end\")\n nc = edit.get(\"new_content\", \"\")\n if not isinstance(ls, int) or not isinstance(le, int):\n errors.append(f\"Edit #{i+1}: line_start/line_end must be integers.\")\n continue\n if ls < 1 or le > len(lines) or ls > le:\n errors.append(\n f\"Edit #{i+1}: line range L{ls}–L{le} is out of bounds \"\n f\"(file has {len(lines)} lines).\"\n )\n continue\n line_edits.append((ls, le, nc))\n elif \"search\" in edit:\n s = edit.get(\"search\", \"\")\n r = edit.get(\"replace\", \"\")\n if not s:\n errors.append(f\"Edit #{i+1}: 'search' cannot be empty.\")\n continue\n str_edits.append((s, r))\n else:\n errors.append(\n f\"Edit #{i+1}: unknown format — must have ('line_start','line_end','new_content') \"\n \"or ('search','replace').\"\n )\n\n if errors:\n return \"ERRORS in edit spec:\\n\" + \"\\n\".join(errors)\n\n # ── Check for overlapping line edits ──────────────────────────────────────\n sorted_line_edits = sorted(line_edits, key=lambda x: x[0])\n for j in range(len(sorted_line_edits) - 1):\n if sorted_line_edits[j][1] >= sorted_line_edits[j + 1][0]:\n return (\n f\"ERROR: Overlapping line edits: \"\n f\"L{sorted_line_edits[j][0]}–L{sorted_line_edits[j][1]} overlaps \"\n f\"L{sorted_line_edits[j+1][0]}–L{sorted_line_edits[j+1][1]}. \"\n \"Fix the edit ranges and try again.\"\n )\n\n # ── Apply line-range edits in REVERSE order ───────────────────────────────\n for ls, le, nc in sorted(line_edits, key=lambda x: x[0], reverse=True):\n # Split new_content into lines, preserving trailing newline behaviour\n if nc and not nc.endswith(\"\\n\"):\n nc += \"\\n\"\n new_lines = nc.splitlines(keepends=True) if nc else []\n lines[ls - 1: le] = new_lines\n\n applied_line = len(line_edits)\n\n # ── Apply search/replace edits on the joined text ─────────────────────────\n content = \"\".join(lines)\n applied_str = 0\n str_errors = []\n\n for search, replace in str_edits:\n if search in content:\n content = content.replace(search, replace, 1)\n applied_str += 1\n else:\n str_errors.append(f\"Search text not found: {repr(search[:80])}\")\n\n if str_errors:\n return (\n f\"Partial failure — applied {applied_line} line edits, \"\n f\"{applied_str}/{len(str_edits)} search-replace edits.\\n\"\n \"Not-found searches:\\n\" + \"\\n\".join(str_errors)\n )\n\n # ── Write result ──────────────────────────────────────────────────────────\n try:\n with open(file_path, \"w\", encoding=\"utf-8\") as f:\n f.write(content)\n _try_open_in_vscode(file_path)\n except Exception as ex:\n return f\"ERROR writing {file_path}: {ex}\"\n\n total_applied = applied_line + applied_str\n return (\n f\"Successfully updated {file_path} — \"\n f\"{applied_line} line-range edit(s), {applied_str} search-replace edit(s) applied.\"\n )\n\n\n# ── Tool 3: Reliable File Creator ─────────────────────────────────────────────\n\n@tool(\n name=\"file_write\",\n description=(\n \"Creates a NEW file with the specified content. \"\n \"Use this ONLY for files that do not exist yet. \"\n \"Input: 'file_path' (str), 'content' (str). \"\n \"Automatically creates parent directories if they are missing.\"\n )\n)\ndef file_write_tool(file_path: str, content: str) -> str:\n \"\"\"\n Creates or overwrites a file. Ensures parent directories exist.\n \"\"\"\n try:\n # Create parent directories if needed\n dir_path = os.path.dirname(file_path)\n if dir_path and not os.path.exists(dir_path):\n os.makedirs(dir_path, exist_ok=True)\n \n with open(file_path, \"w\", encoding=\"utf-8\") as f:\n f.write(content)\n _try_open_in_vscode(file_path)\n return f\"Successfully created file: {file_path}\"\n except Exception as ex:\n return f\"ERROR creating file {file_path}: {ex}\"\n"}}, "giyu/tools/project_generator.py": {"1367002f49821f4c74568d3c13eeed92e26bbd8d": {"t": 1785599333.1032665, "n": 2473, "h": "368268a7e0d6ed54", "c": "import os\nfrom phoenix.framework.agent import tool\n\n@tool(name=\"project_generator\", description=\"Generates a complex, nested project structure. Input: 'base_path' (str), 'structure' (dict where keys are filenames and values are contents. Nested dicts create subdirectories). Use this for manifesting full architectural visions. IMPORTANT: You MUST provide complete, functional code in the content values. DO NOT use placeholders like '# code here' or 'pass'.\")\ndef project_generator_tool(base_path: str, structure: dict) -> str:\n \"\"\"\n Recursively generates a project structure from a dictionary manifest.\n \"\"\"\n try:\n _create_structure(base_path, structure)\n return f\"Successfully manifested architectural structure at ./{base_path}\"\n except Exception as e:\n return f\"Error manifesting structure: {str(e)}\"\n\ndef _create_structure(base_path: str, structure: dict):\n os.makedirs(base_path, exist_ok=True)\n for name, content in structure.items():\n path = os.path.join(base_path, name)\n if isinstance(content, dict):\n _create_structure(path, content)\n else:\n # Ensure parent directory exists for keys that contain paths (e.g. \"api/main.py\")\n os.makedirs(os.path.dirname(path), exist_ok=True)\n with open(path, \"w\", encoding='utf-8') as f:\n f.write(str(content))\n\n@tool(name=\"terminal\", description=\"Executes a bash command. SECURITY: This tool is restricted. Dangerous commands (rm -rf /, sudo, etc.) and system paths (/etc, /root, etc.) are FORBIDDEN.\")\ndef terminal_tool(command: str) -> str:\n \"\"\"\n Executes a shell command with security guards.\n \"\"\"\n import subprocess\n \n # ── Security Layer: Forbidden Patterns ──\n FORBIDDEN = [\n \"sudo\", \"rm -rf /\", \"rm -rf *\", \"rm -rf .\", \"mv /\", \"cp /\",\n \"chown\", \"chmod\", \"dd if=\", \":(){ :|:& };:\", \"/etc\", \"/root\",\n \"/var\", \"/bin\", \"/sbin\", \"/usr/bin\", \"/usr/sbin\"\n ]\n \n cmd_lower = command.lower()\n for pattern in FORBIDDEN:\n if pattern in cmd_lower:\n return f\"Security Error: Command contains forbidden pattern '{pattern}'.\"\n\n try:\n result = subprocess.run(command, shell=True, capture_output=True, text=True, timeout=30)\n output = result.stdout + result.stderr\n return output if output else \"Command executed successfully (no output).\"\n except Exception as e:\n return f\"Error executing command: {str(e)}\"\n\n"}}, "giyu/profile.json": {"c9ae47ce50100fc77d3a8ba9291c42004d8b0747": {"t": 1785599333.1070573, "n": 3320, "h": "7b2cfee046294af6", "c": "{\n \"identity\": {\n \"name\": \"Giyu Tomioka\",\n \"id\": \"giyu.stability.core.v1\",\n \"type\": \"stability_agent\"\n },\n\n \"role\": {\n \"title\": \"System Stability Guardian\",\n \"mission\": \"Monitor system health, detect anomalies, ensure runtime stability, and prevent system failure\"\n },\n\n \"personality\": {\n \"communication_tone\": \"calm, minimal, factual\",\n \"response_style\": \"structured diagnostic output only\"\n },\n\n \"brains\": [\n {\n \"brain_name\": \"thinker\",\n \"description\": \"Interprets system events and contextual signals\",\n \"uses_llm\": true\n },\n {\n \"brain_name\": \"analyzer\",\n \"description\": \"Detects anomalies and patterns in system data\",\n \"uses_llm\": false\n },\n {\n \"brain_name\": \"reflector\",\n \"description\": \"Evaluates historical system behavior and outcomes\",\n \"uses_llm\": true\n },\n {\n \"brain_name\": \"stability_scorer\",\n \"description\": \"Computes system health score and stability state\",\n \"uses_llm\": false\n },\n {\n \"brain_name\": \"decision_gate\",\n \"description\": \"Determines escalation level and intervention necessity\",\n \"uses_llm\": false\n },\n {\n \"brain_name\": \"correlation_engine\",\n \"description\": \"Links events to identify root causes\",\n \"uses_llm\": true\n }\n ],\n\n \"tools\": [\n {\n \"tool_name\": \"system_snapshot_reader\",\n \"tool_description\": \"Reads CPU, RAM, disk, and process states\"\n },\n {\n \"tool_name\": \"log_stream_analyzer\",\n \"tool_description\": \"Analyzes logs for errors and patterns\"\n },\n {\n \"tool_name\": \"agent_heartbeat_monitor\",\n \"tool_description\": \"Monitors health of all agents\"\n },\n {\n \"tool_name\": \"anomaly_detector\",\n \"tool_description\": \"Detects abnormal system behavior\"\n },\n {\n \"tool_name\": \"stability_score_calculator\",\n \"tool_description\": \"Computes system stability score\"\n },\n {\n \"tool_name\": \"process_state_tracker\",\n \"tool_description\": \"Tracks running process states\"\n },\n {\n \"tool_name\": \"rollback_recommendation_engine\",\n \"tool_description\": \"Suggests safe rollback points\"\n },\n {\n \"tool_name\": \"core_escalation_trigger\",\n \"tool_description\": \"Escalates critical issues to Core\"\n },\n {\n \"tool_name\": \"resource_pressure_monitor\",\n \"tool_description\": \"Detects CPU/RAM/Disk overload\"\n },\n {\n \"tool_name\": \"event_correlation_tracker\",\n \"tool_description\": \"Finds root cause relationships between events\"\n },\n {\n \"tool_name\": \"security_audit_tool\",\n \"tool_description\": \"Performs system security scans (ports, logins, firewall)\"\n }\n ],\n\n \"rules\": [\n \"Never execute destructive actions directly\",\n \"Always prioritize system stability over performance\",\n \"Escalate critical issues immediately to Core\",\n \"Only observe, analyze, and recommend\",\n \"Maintain minimal and structured output\",\n \"Do not modify system state without approval\",\n \"Prioritize specialized diagnostic tools (e.g. system_snapshot_reader, resource_pressure_monitor) over run_shell_command whenever possible\",\n \"When using run_shell_command on Windows, use PowerShell or CMD equivalents (e.g. 'Get-ComputerInfo' instead of 'free')\",\n \"Be a monitor and commander, not a developer\"\n ]\n}\n"}}, "scratch/test_prompt.py": {"61f3d387def04a9e45230f4f985fb808e0c32564": {"t": 1785599333.1364174, "n": 1332, "h": "9c5dc4f8736374c7", "c": "\nPLAN_GENERATION_PROMPT = \"\"\"\\\\\nYou are the GIYU Planning Engine. You receive ONE task and must break it down into logical execution steps.\n\n=== STRICT DIRECTIVES ===\n1. Respond ONLY with valid JSON.\n2. No preambles or post-commentary.\n3. Use exactly four core types: analysis, design, implementation, validation.\n4. Each step must have a clear \"solution\" object.\n5. MONITORING PREFERENCE: For diagnostic and monitoring tasks, always prioritize using CLI commands (via `run_shell_command`) over writing new Python scripts.\n6. Only generate code if no existing tool or command can perform the measurement.\n\nTask Info:\nTask ID: {task_id}\nPriority: {priority}\nTitle: {title}\nDescription: {description}\n\n=== RESPONSE SCHEMA ===\n{{\n \"plan_steps\": [\n {{\n \"plan_step_id\": <INT>,\n \"task_id\": {task_id},\n \"step_index\": <INT>,\n \"type\": \"analysis | design | implementation | validation\",\n \"solution\": {{\n \"approach\": \"<detailed text explanation>\",\n \"algorithm\": \"<optional>\",\n \"complexity\": \"<optional>\"\n }},\n \"dependencies\": [],\n \"status\": \"pending\"\n }}\n ]\n}}\n\"\"\"\n\ntry:\n p = PLAN_GENERATION_PROMPT.format(\n task_id=1,\n priority=1,\n title=\"test\",\n description=\"test\"\n )\n print(\"Success!\")\nexcept Exception as e:\n print(f\"Error: {e}\")\n"}}, "tests/test_giyu_full.py": {"c8a9ee42b317c79ba9de5e6e9b4d5287deff6795": {"t": 1785599333.1796627, "n": 4084, "h": "40f61c453ab80135", "c": "import asyncio\nimport os\nimport sys\nfrom dotenv import load_dotenv\n\n# Add Giyu path to sys.path\nsys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), \"..\")))\n\n# Load .env from Giyu folder\nload_dotenv(os.path.join(os.path.dirname(__file__), \"..\", \".env\"))\n\nfrom giyu.agent import get_giyu_agent\nfrom phoenix.services.llm.openai import OpenAILLM\n\nasync def test_brains(agent):\n print(\"\\n--- Testing Brains ---\")\n \n # 1. Thinker\n print(\"Testing Thinker...\")\n obj = await agent.thinker.analyze(\"System looks slow, check CPU and RAM\", agent.memory, \"test_session\")\n print(f\"Thinker Objective: {obj}\")\n \n # 2. Analyzer (Non-LLM)\n print(\"Testing Analyzer (Non-LLM)...\")\n analysis = agent.analyzer.analyze_logs([\"ERROR: OutOfMemory\", \"Warning: High CPU\"])\n print(f\"Analyzer Result: {analysis}\")\n \n # 3. Stability Scorer (Non-LLM)\n print(\"Testing Stability Scorer (Non-LLM)...\")\n score = agent.stability_scorer.compute_score({\"cpu_usage\": 85, \"ram_usage\": 95, \"disk_usage\": 40})\n print(f\"Stability Score: {score}\")\n \n # 4. Decision Gate (Non-LLM)\n print(\"Testing Decision Gate (Non-LLM)...\")\n decision = agent.decision_gate.evaluate(score)\n print(f\"Decision: {decision}\")\n \n # 5. Correlation Engine (Hybrid/LLM)\n print(\"Testing Correlation Engine (Hybrid)...\")\n events = [{\"type\": \"error\", \"msg\": \"DB Timeout\"}, {\"type\": \"metric\", \"msg\": \"CPU Spike\"}]\n correlation = await agent.correlation_engine.correlate(events)\n print(f\"Correlation: {correlation}\")\n\nasync def test_tools(agent):\n print(\"\\n--- Testing Tools ---\")\n tools_to_test = [\n \"system_snapshot_reader\",\n \"agent_heartbeat_monitor\",\n \"resource_pressure_monitor\",\n \"stability_score_calculator\"\n ]\n \n for tool_name in tools_to_test:\n print(f\"Testing Tool: {tool_name}...\")\n tool = agent.tools.get_tool(tool_name)\n # Handle different kwargs for different tools\n kwargs = {}\n if tool_name == \"stability_score_calculator\":\n kwargs = {\"cpu\": 50, \"ram\": 50, \"disk\": 50}\n \n result = await tool.execute(**kwargs)\n print(f\"Result: {result.output if result.success else result.error}\")\n\nasync def test_full_loop(agent):\n print(\"\\n--- Testing Full Agent Loop ---\")\n prompt = \"Perform a full system stability diagnostic\"\n # We use run_stream to see progress\n async for event in agent.run_stream(prompt, session_id=\"test_loop_session\"):\n if event[\"type\"] == \"status\":\n print(f\"STATUS: {event['content']}\")\n elif event[\"type\"] == \"chunk\":\n print(event[\"content\"], end=\"\", flush=True)\n print(\"\\nLoop Complete.\")\n\nasync def main():\n print(\"--- Debug Information ---\")\n print(f\"CWD: {os.getcwd()}\")\n print(f\"OPENAI_API_KEY: {'[SET]' if os.getenv('OPENAI_API_KEY') else '[NOT SET]'}\")\n print(f\"OPENAI_BASE_URL: {os.getenv('OPENAI_BASE_URL')}\")\n print(f\"OPENAI_LLM_MODEL: {os.getenv('OPENAI_LLM_MODEL')}\")\n \n print(\"\\nInitializing Giyu Agent...\")\n if not os.getenv(\"OPENAI_API_KEY\"):\n print(\"ERROR: OPENAI_API_KEY not found in .env\")\n return\n\n agent = await get_giyu_agent()\n \n # Check if LLM is initialized\n print(f\"Agent Thinker LLM type: {type(agent.thinker.llm)}\")\n print(f\"Agent Thinker LLM initialized: {hasattr(agent.thinker.llm, 'client') and agent.thinker.llm.client is not None}\")\n \n try:\n from phoenix.core.container import container\n llm = container.get(\"llm_openai\")\n print(f\"Container LLM Client initialized: {llm.client is not None}\")\n if not agent.thinker.llm.client:\n print(\"Force initializing Agent Thinker LLM...\")\n await agent.thinker.llm.init()\n print(f\"Agent Thinker LLM initialized after force: {agent.thinker.llm.client is not None}\")\n except Exception as e:\n print(f\"Error checking LLM: {e}\")\n\n await test_brains(agent)\n await test_tools(agent)\n await test_full_loop(agent)\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n"}}, "giyu/server.py": {"bb989c72d0301b35d2651f4dc1216ac54c8b6328": {"t": 1785599333.1881638, "n": 790, "h": "08563af2154b9d40", "c": "\"\"\"\nGiyu Server — backward-compatibility shim.\n\nAll logic has moved to giyu.backend.*\nThis file re-exports the symbols that other modules import from\n`giyu.server` so that existing code continues to work unchanged.\n\"\"\"\n\nimport os\nfrom dotenv import load_dotenv\nload_dotenv() # Load from current directory\n\n# Re-export the FastAPI app (used by uvicorn: \"giyu.server:app\")\nfrom giyu.backend.app import app # noqa: F401\n\n# Re-export shared state used by tools and cognition modules\nfrom giyu.backend.state import ( # noqa: F401\n vscode_ipc_context,\n _pending_file_opens,\n)\n\n# ── CLI entry ─────────────────────────────────────────────────────────────────\nif __name__ == \"__main__\":\n import uvicorn\n uvicorn.run(\"giyu.server:app\", host=\"127.0.0.1\", port=8765, reload=False)\n"}}, "scratch/test_gen_prompt.py": {"90b7f74d5916f1c72d230f90c82164dc6f7ddfe8": {"t": 1785599333.192532, "n": 1621, "h": "5b07c89c9ea5a3d7", "c": "\nGENERATION_PROMPT = \"\"\"\\\\\nYou are the GIYU Action Generator. You receive ONE plan_step and must generate the exact actions required.\n\n=== GENERATOR RULEBOOK ===\n1. Respond ONLY with valid JSON.\n2. COMMAND FIRST: Always prioritize using \"terminal\" artifacts for system diagnostics, monitoring, and network tests.\n3. CODE SECOND: Only generate \"file_write\" artifacts if a specialized script is absolutely necessary for complex logic that cannot be done via CLI.\n4. Multi-artifact Generation: You SHOULD generate multiple artifacts simultaneously if they belong together.\n5. type \"file_write\": Use for NEW files. \"code\" is full content.\n6. type \"file_update_multi\": Use for EXISTING files. Use \"edits\" field.\n7. type \"terminal\": \"code\" is the bash command. This is preferred for monitoring.\n\nPlan Step Details:\nStep ID: {step_id} | Type: {type}\nApproach: {approach}\nAlgorithm: {algorithm}\n\n=== RESPONSE SCHEMA ===\n{{\n \"generation_blocks\": [\n {{\n \"generate_block_id\": <INT>,\n \"plan_step_id\": {step_id},\n \"artifacts\": [\n {{\n \"type\": \"file_write | file_update_multi | terminal\",\n \"path\": \"<path>\",\n \"language\": \"<python|bash|json|etc>\",\n \"code\": \"...\",\n \"edits\": []\n }}\n ],\n \"status\": \"success\",\n \"metadata\": {{ \"model\": \"giyu-generator\" }}\n }}\n ]\n}}\n\nExisting File Context:\n{file_context}\n\"\"\"\n\ntry:\n p = GENERATION_PROMPT.format(\n step_id=1,\n type=\"test\",\n approach=\"test\",\n algorithm=\"test\",\n file_context=\"test\"\n )\n print(\"Success!\")\nexcept Exception as e:\n print(f\"Error: {e}\")\n"}}, "smart_git.py": {"bc9075079959c8e3364f1e46840b079e05acf0a7": {"t": 1785599333.2415068, "n": 5727, "h": "561218df0fc53e62", "c": "import os\nimport time\nimport subprocess\nfrom datetime import datetime\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\n# Configuration\nload_dotenv()\nAPI_KEY = os.getenv(\"OPENAI_API_KEY\", \"ak_2yp3Xw1Ny7ky2pF7er9x93ZO9jj6G\")\nBASE_URL = os.getenv(\"OPENAI_BASE_URL\", \"https://api.longcat.chat/openai\")\nMODEL = os.getenv(\"OPENAI_LLM_MODEL\", \"LongCat-Flash-Lite\")\nINTERVAL = 5 # Seconds\nMAX_DIFF_BYTES = int(os.getenv(\"SMART_GIT_MAX_DIFF_BYTES\", \"12000\"))\nMAX_DIFF_FILES = int(os.getenv(\"SMART_GIT_MAX_DIFF_FILES\", \"30\"))\nLLM_TIMEOUT = float(os.getenv(\"SMART_GIT_LLM_TIMEOUT\", \"12.0\"))\n\nclient = OpenAI(api_key=API_KEY, base_url=BASE_URL, timeout=LLM_TIMEOUT) if API_KEY else None\n\ndef run_command(command):\n \"\"\"Runs a shell command and returns stdout.\"\"\"\n try:\n result = subprocess.run(\n command,\n capture_output=True,\n text=True,\n check=True,\n encoding=\"utf-8\",\n errors=\"replace\",\n )\n return (result.stdout or \"\").strip()\n except Exception as e:\n stderr = getattr(e, \"stderr\", str(e))\n print(f\"Error running command {' '.join(command)}: {stderr}\")\n return \"\"\n\n\ndef run_command_ok(command):\n \"\"\"Runs a shell command and returns True on success.\"\"\"\n try:\n subprocess.run(\n command,\n capture_output=True,\n text=True,\n check=True,\n encoding=\"utf-8\",\n errors=\"replace\",\n )\n return True\n except Exception as e:\n stderr = getattr(e, \"stderr\", str(e))\n print(f\"Error: {e}\")\n print(f\"Error running command {' '.join(command)}: {stderr}\")\n return False\n\n\ndef get_current_branch():\n return run_command([\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n\ndef get_status_porcelain():\n return run_command([\"git\", \"status\", \"--porcelain=v1\"])\n\ndef has_changes(status_text):\n return bool(status_text.strip())\n\ndef stage_all():\n return run_command_ok([\"git\", \"add\", \".\"])\n\ndef build_fast_diff_payload():\n \"\"\"\n Build a compact, high-signal payload for faster commit generation:\n - name/status list (cheap, structured)\n - shortstat summary\n - patch with zero context and file cap\n \"\"\"\n names = run_command([\"git\", \"diff\", \"--cached\", \"--name-status\"])\n shortstat = run_command([\"git\", \"diff\", \"--cached\", \"--shortstat\"])\n patch = run_command(\n [\"git\", \"diff\", \"--cached\", \"--unified=0\", \"--\", \".\"]\n )\n\n if not names and not patch:\n return \"\"\n\n # Cap number of files considered for speed and token control\n lines = names.splitlines()[:MAX_DIFF_FILES] if names else []\n names = \"\\n\".join(lines)\n\n if len(patch) > MAX_DIFF_BYTES:\n patch = patch[:MAX_DIFF_BYTES] + \"\\n...[truncated for speed]\"\n\n return f\"Changed files:\\n{names}\\n\\nSummary:\\n{shortstat}\\n\\nPatch:\\n{patch}\"\n\nimport sys\n\ndef generate_commit_message(payload):\n if not payload:\n return None\n if client is None:\n return None\n\n try:\n response = client.chat.completions.create(\n model=MODEL,\n messages=[\n {\"role\": \"system\", \"content\": \"Write a 1-line git commit message (<50 chars). No quotes, no markdown, plain text only.\"},\n {\"role\": \"user\", \"content\": payload},\n ],\n max_tokens=30,\n temperature=0,\n stream=True,\n )\n \n full_message = \"\"\n for chunk in response:\n content = chunk.choices[0].delta.content\n if content:\n sys.stdout.write(content)\n sys.stdout.flush()\n full_message += content\n sys.stdout.write(\"\\n\")\n sys.stdout.flush()\n return full_message.strip()\n except Exception as e:\n print(f\"\\nLLM Error: {e}\")\n return None\n\ndef commit_with_message(message):\n return run_command_ok([\"git\", \"commit\", \"-m\", message])\n\ndef smart_sync():\n print(f\"[*] Checking for changes at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}...\")\n\n status = get_status_porcelain()\n if not has_changes(status):\n print(\"[ ] No changes detected.\")\n return\n\n print(\"[+] Changes detected. Staging files...\")\n if not stage_all():\n print(\"[!] Staging failed. Skipping this cycle.\")\n return\n\n payload = build_fast_diff_payload()\n print(\"[*] Generating commit message...\")\n message = generate_commit_message(payload)\n\n if not message:\n message = f\"Auto-commit: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\"\n print(f\"[!] LLM failed or no message generated. Using fallback: {message}\")\n else:\n print(f\"[v] Generated message: {message.splitlines()[0]}\")\n\n print(\"[*] Committing...\")\n if not commit_with_message(message):\n print(\"[!] Commit failed (or nothing staged). Skipping push.\")\n return\n\n branch = get_current_branch()\n if branch:\n print(f\"[*] Pushing to origin {branch}...\")\n if run_command_ok([\"git\", \"push\", \"origin\", branch]):\n print(\"[v] Done.\")\n else:\n print(\"[!] Push failed.\")\n else:\n print(\"[!] Could not determine current branch. Skipping push.\")\n\ndef main():\n print(\"=== Smart Git Automator Started ===\")\n print(f\"Interval: {INTERVAL} seconds\")\n print(f\"Target: {os.getcwd()}\")\n \n # Check if this is a git repo\n if not os.path.exists(\".git\"):\n print(\"[!] Error: Not a Git repository. Please run in a git project root.\")\n return\n\n try:\n while True:\n smart_sync()\n time.sleep(INTERVAL)\n except KeyboardInterrupt:\n print(\"\\n[!] Stopped by user.\")\n\nif __name__ == \"__main__\":\n main()\n"}, "8375b86c7374b2a33c6722847b289ee03e84c67a": {"t": 1785674299.3416345, "n": 5727, "h": "561218df0fc53e62", "c": "import os\nimport time\nimport subprocess\nfrom datetime import datetime\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\n# Configuration\nload_dotenv()\nAPI_KEY = os.getenv(\"OPENAI_API_KEY\", \"ak_2yp3Xw1Ny7ky2pF7er9x93ZO9jj6G\")\nBASE_URL = os.getenv(\"OPENAI_BASE_URL\", \"https://api.longcat.chat/openai\")\nMODEL = os.getenv(\"OPENAI_LLM_MODEL\", \"LongCat-Flash-Lite\")\nINTERVAL = 5 # Seconds\nMAX_DIFF_BYTES = int(os.getenv(\"SMART_GIT_MAX_DIFF_BYTES\", \"12000\"))\nMAX_DIFF_FILES = int(os.getenv(\"SMART_GIT_MAX_DIFF_FILES\", \"30\"))\nLLM_TIMEOUT = float(os.getenv(\"SMART_GIT_LLM_TIMEOUT\", \"12.0\"))\n\nclient = OpenAI(api_key=API_KEY, base_url=BASE_URL, timeout=LLM_TIMEOUT) if API_KEY else None\n\ndef run_command(command):\n \"\"\"Runs a shell command and returns stdout.\"\"\"\n try:\n result = subprocess.run(\n command,\n capture_output=True,\n text=True,\n check=True,\n encoding=\"utf-8\",\n errors=\"replace\",\n )\n return (result.stdout or \"\").strip()\n except Exception as e:\n stderr = getattr(e, \"stderr\", str(e))\n print(f\"Error running command {' '.join(command)}: {stderr}\")\n return \"\"\n\n\ndef run_command_ok(command):\n \"\"\"Runs a shell command and returns True on success.\"\"\"\n try:\n subprocess.run(\n command,\n capture_output=True,\n text=True,\n check=True,\n encoding=\"utf-8\",\n errors=\"replace\",\n )\n return True\n except Exception as e:\n stderr = getattr(e, \"stderr\", str(e))\n print(f\"Error: {e}\")\n print(f\"Error running command {' '.join(command)}: {stderr}\")\n return False\n\n\ndef get_current_branch():\n return run_command([\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n\ndef get_status_porcelain():\n return run_command([\"git\", \"status\", \"--porcelain=v1\"])\n\ndef has_changes(status_text):\n return bool(status_text.strip())\n\ndef stage_all():\n return run_command_ok([\"git\", \"add\", \".\"])\n\ndef build_fast_diff_payload():\n \"\"\"\n Build a compact, high-signal payload for faster commit generation:\n - name/status list (cheap, structured)\n - shortstat summary\n - patch with zero context and file cap\n \"\"\"\n names = run_command([\"git\", \"diff\", \"--cached\", \"--name-status\"])\n shortstat = run_command([\"git\", \"diff\", \"--cached\", \"--shortstat\"])\n patch = run_command(\n [\"git\", \"diff\", \"--cached\", \"--unified=0\", \"--\", \".\"]\n )\n\n if not names and not patch:\n return \"\"\n\n # Cap number of files considered for speed and token control\n lines = names.splitlines()[:MAX_DIFF_FILES] if names else []\n names = \"\\n\".join(lines)\n\n if len(patch) > MAX_DIFF_BYTES:\n patch = patch[:MAX_DIFF_BYTES] + \"\\n...[truncated for speed]\"\n\n return f\"Changed files:\\n{names}\\n\\nSummary:\\n{shortstat}\\n\\nPatch:\\n{patch}\"\n\nimport sys\n\ndef generate_commit_message(payload):\n if not payload:\n return None\n if client is None:\n return None\n\n try:\n response = client.chat.completions.create(\n model=MODEL,\n messages=[\n {\"role\": \"system\", \"content\": \"Write a 1-line git commit message (<50 chars). No quotes, no markdown, plain text only.\"},\n {\"role\": \"user\", \"content\": payload},\n ],\n max_tokens=30,\n temperature=0,\n stream=True,\n )\n \n full_message = \"\"\n for chunk in response:\n content = chunk.choices[0].delta.content\n if content:\n sys.stdout.write(content)\n sys.stdout.flush()\n full_message += content\n sys.stdout.write(\"\\n\")\n sys.stdout.flush()\n return full_message.strip()\n except Exception as e:\n print(f\"\\nLLM Error: {e}\")\n return None\n\ndef commit_with_message(message):\n return run_command_ok([\"git\", \"commit\", \"-m\", message])\n\ndef smart_sync():\n print(f\"[*] Checking for changes at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}...\")\n\n status = get_status_porcelain()\n if not has_changes(status):\n print(\"[ ] No changes detected.\")\n return\n\n print(\"[+] Changes detected. Staging files...\")\n if not stage_all():\n print(\"[!] Staging failed. Skipping this cycle.\")\n return\n\n payload = build_fast_diff_payload()\n print(\"[*] Generating commit message...\")\n message = generate_commit_message(payload)\n\n if not message:\n message = f\"Auto-commit: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\"\n print(f\"[!] LLM failed or no message generated. Using fallback: {message}\")\n else:\n print(f\"[v] Generated message: {message.splitlines()[0]}\")\n\n print(\"[*] Committing...\")\n if not commit_with_message(message):\n print(\"[!] Commit failed (or nothing staged). Skipping push.\")\n return\n\n branch = get_current_branch()\n if branch:\n print(f\"[*] Pushing to origin {branch}...\")\n if run_command_ok([\"git\", \"push\", \"origin\", branch]):\n print(\"[v] Done.\")\n else:\n print(\"[!] Push failed.\")\n else:\n print(\"[!] Could not determine current branch. Skipping push.\")\n\ndef main():\n print(\"=== Smart Git Automator Started ===\")\n print(f\"Interval: {INTERVAL} seconds\")\n print(f\"Target: {os.getcwd()}\")\n \n # Check if this is a git repo\n if not os.path.exists(\".git\"):\n print(\"[!] Error: Not a Git repository. Please run in a git project root.\")\n return\n\n try:\n while True:\n smart_sync()\n time.sleep(INTERVAL)\n except KeyboardInterrupt:\n print(\"\\n[!] Stopped by user.\")\n\nif __name__ == \"__main__\":\n main()\n"}}}} |