Clean up generated files and fix 6 dashboard bugs (#142)

* chore: gitignore local/generated files and remove from tracking

Remove user-specific files (MEMORY.md, user_profile.md, prompts.py)
from source control. Add patterns for credentials, backups, and
generated content to .gitignore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve 6 dashboard bugs — chat, /bugs, /swarm/events, WebSocket, marketplace, sidebar

1. Chat non-functional: CSRF middleware silently blocked HTMX POSTs.
   Added CSRF token transmission via hx-headers in base.html.

2. /bugs → 500: Route missing template vars (total, stats, filter_status).

3. /swarm/events → 500: Called .event_type.value on a plain str
   (SparkEvent.event_type is str, not enum). Also fixed timestamp
   and source field mismatches in the template.

4. WebSocket reconnect loop: No WS endpoint existed at /swarm/live,
   only an HTTP GET. Added @router.websocket("/live") using ws_manager.

5. Marketplace "Agent not found": Nav links /marketplace/ui matched
   the /{agent_id} catch-all. Added explicit /marketplace/ui route
   with enriched template context.

6. Agents sidebar "LOADING...": /swarm/agents/sidebar endpoint was
   missing. Added route returning the existing sidebar partial.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: restore src/timmy/prompts.py to source control

prompts.py is imported by timmy.agent and is production code,
not a user-local file. Re-add to tracking and remove from .gitignore.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Trip T <trip@local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexander Whitestone
2026-03-07 21:26:41 -05:00
committed by GitHub
parent b615595100
commit 3bf7187482
8 changed files with 92 additions and 115 deletions

17
.gitignore vendored
View File

@@ -17,6 +17,10 @@ env/
.env
.env.*
!.env.example
discord_credentials.txt
# Backup / temp files
*~
# SQLite — never commit databases or WAL/SHM artifacts
*.db
@@ -55,6 +59,19 @@ src/data/
*.swo
.claude/
# Local content — user-specific or generated
MEMORY.md
memory/self/user_profile.md
TIMMYTIME
introduction.txt
messages.txt
morning_briefing.txt
markdown_report.md
data/timmy_soul.jsonl
scripts/migrate_to_zeroclaw.py
src/infrastructure/db_pool.py
workspace/
# macOS
.DS_Store
.AppleDouble

View File

@@ -1,52 +0,0 @@
# Timmy Hot Memory
> Working RAM — always loaded, ~300 lines max, pruned monthly
> Last updated: 2026-02-26
---
## Current Status
**Agent State:** Operational
**Mode:** Development
**Active Tasks:** 0
**Pending Decisions:** None
---
## Standing Rules
1. **Sovereignty First** — No cloud dependencies
2. **Local-Only Inference** — Ollama on localhost
3. **Privacy by Design** — Telemetry disabled
4. **Tool Minimalism** — Use tools only when necessary
5. **Memory Discipline** — Write handoffs at session end
---
## Agent Roster
| Agent | Role | Status |
|-------|------|--------|
| Timmy | Core | Active |
---
## User Profile
**Name:** Not
## Key Decisions
(none yet)
---
## Pending Actions
- [ ] Learn user's name
---
*Prune date: 2026-02-25*

View File

@@ -1,43 +0,0 @@
# User Profile
> Learned information about the user. Updated continuously.
## Basic Information
**Name:** Not
**Location:** (unknown)
**Occupation:** (unknown)
**Technical Level:** (to be assessed)
## Interests & Expertise
- (to be learned from conversations)
## Preferences
### Communication
- Response style: (default: concise, technical)
- Detail level: (default: medium)
- Humor: (default: minimal)
### Tools
- Auto-tool usage: (default: minimal)
- Confirmation required for: shell commands, file writes
### Memory
- Personalization: Enabled
- Context retention: 20 messages (working), 100 (short-term)
## Important Facts
- (to be extracted from conversations)
## Relationship History
- First session: 2026-02-25
- Total sessions: 1
- Key milestones: (none yet)
---
*Last updated: 2026-02-27*

View File

@@ -47,28 +47,41 @@ async def api_list_agents():
@router.get("/marketplace")
async def marketplace_json(request: Request):
"""Marketplace JSON API (backward compat)."""
return await api_list_agents()
@router.get("/marketplace/ui", response_class=HTMLResponse)
async def marketplace_ui(request: Request):
"""Marketplace page — returns JSON for API requests, HTML for browser."""
# Check if client wants JSON (common test clients don't set Accept header)
accept = request.headers.get("accept", "")
# Return JSON if Accept header indicates JSON OR if no preference (default to JSON for API)
if "application/json" in accept or accept == "*/*" or not accept:
return await api_list_agents()
# Browser request - return HTML
"""Marketplace HTML page."""
try:
brain = BrainClient()
tasks = await brain.get_pending_tasks(limit=20)
except Exception:
tasks = []
# Enrich agents with fields the template expects
enriched = []
for agent in AGENT_CATALOG:
a = dict(agent)
a.setdefault("status", a.get("default_status", "active"))
a.setdefault("tasks_completed", 0)
a.setdefault("total_earned", 0)
enriched.append(a)
active = sum(1 for a in enriched if a["status"] == "active")
return templates.TemplateResponse(
request,
"marketplace.html",
{
"agents": AGENT_CATALOG,
"agents": enriched,
"pending_tasks": tasks,
"message": "Personas deprecated — use Brain Task Queue",
"page_title": "Agent Marketplace",
"active_count": active,
"planned_count": 0,
}
)

View File

@@ -4,11 +4,12 @@ import json
import logging
from typing import Optional
from fastapi import APIRouter, Request
from fastapi import APIRouter, Request, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
from spark.engine import spark_engine
from dashboard.templating import templates
from infrastructure.ws_manager.handler import ws_manager
logger = logging.getLogger(__name__)
@@ -31,13 +32,13 @@ async def swarm_events(
if agent_id:
events = [e for e in events if e.agent_id == agent_id]
if event_type:
events = [e for e in events if e.event_type.value == event_type]
events = [e for e in events if e.event_type == event_type]
# Prepare summary and event types for template
summary = {}
event_types = set()
for e in events:
etype = e.event_type.value
etype = e.event_type
event_types.add(etype)
summary[etype] = summary.get(etype, 0) + 1
@@ -60,7 +61,7 @@ async def swarm_live(request: Request):
"""Live swarm activity page."""
status = spark_engine.status()
events = spark_engine.get_timeline(limit=20)
return templates.TemplateResponse(
request,
"swarm_live.html",
@@ -69,3 +70,34 @@ async def swarm_live(request: Request):
"events": events,
},
)
@router.websocket("/live")
async def swarm_ws(websocket: WebSocket):
"""WebSocket endpoint for live swarm updates."""
await ws_manager.connect(websocket)
try:
while True:
await websocket.receive_text()
except WebSocketDisconnect:
ws_manager.disconnect(websocket)
@router.get("/agents/sidebar", response_class=HTMLResponse)
async def agents_sidebar(request: Request):
"""Sidebar partial showing agent status for the home page."""
from config import settings
agents = [
{
"id": "default",
"name": settings.agent_name,
"status": "idle",
"type": "local",
"capabilities": "chat,reasoning,research,planning",
"last_seen": None,
}
]
return templates.TemplateResponse(
request, "partials/swarm_agents_sidebar.html", {"agents": agents}
)

View File

@@ -89,7 +89,9 @@ async def mission_control(request: Request):
@router.get("/bugs", response_class=HTMLResponse)
async def bugs_page(request: Request):
return templates.TemplateResponse(request, "bugs.html", {"bugs": []})
return templates.TemplateResponse(request, "bugs.html", {
"bugs": [], "total": 0, "stats": {}, "filter_status": None,
})
@router.get("/self-coding", response_class=HTMLResponse)

View File

@@ -15,6 +15,14 @@
<link rel="stylesheet" href="/static/style.css?v=5" />
{% block extra_styles %}{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.3/dist/htmx.min.js" crossorigin="anonymous"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var match = document.cookie.match(/csrf_token=([^;]+)/);
if (match) {
document.body.setAttribute('hx-headers', JSON.stringify({"X-CSRF-Token": match[1]}));
}
});
</script>
<script defer src="https://cdn.jsdelivr.net/npm/marked@15.0.7/marked.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/dompurify@3.2.4/dist/purify.min.js"></script>
</head>

View File

@@ -63,14 +63,14 @@
</thead>
<tbody>
{% for event in events %}
<tr class="event-row" data-type="{{ event.event_type.value }}">
<td class="event-time">{{ event.timestamp[11:19] }}</td>
<tr class="event-row" data-type="{{ event.event_type }}">
<td class="event-time">{{ event.created_at[11:19] }}</td>
<td>
<span class="mc-badge mc-badge-{{ event.event_type.value.split('.')[0] }}">
{{ event.event_type.value }}
<span class="mc-badge mc-badge-{{ event.event_type.split('.')[0] }}">
{{ event.event_type }}
</span>
</td>
<td>{{ event.source }}</td>
<td>{{ event.agent_id or '-' }}</td>
<td>
{% if event.task_id %}
<a href="/swarm/events?task_id={{ event.task_id }}">{{ event.task_id[:8] }}...</a>