- Extract ToolTypesTab, ToolConfigsTab, ConfigFoldersTab from inline page
- Each tab is self-contained with own state, API calls, and forms
- Slim page to 77 lines (tab switcher + composition only)
- Add barrel export for tool-workshop feature components
- Add tsconfig path alias for @/* imports
Quality gates: tsc (pass), eslint (pass)
Refs: repo-restructure Task 4.1
- Create schemas/ directory with 12 schema files covering all domains
- Extract 70+ Pydantic models from 11 router files
- Routers now import from src.schemas.{domain} instead of defining inline
- Zero inline BaseModel definitions remain in any router
Quality gates: py_compile all schemas (pass), py_compile all routers (pass)
Refs: repo-restructure Task 3.2
- Create styles/tokens.css with CSS custom properties and dark theme
- Create styles/global.css with resets, shell layout, and navigation
- Create styles/utilities.css with generic utilities and primitives
- Create styles/syntax-highlight.css with Prism.js theme
- Update main.tsx to import the 4 new style files
- Keep styles.css intact for backward compatibility
Quality gates: build (pass), lint (pass)
Refs: repo-restructure Task 2.1
- Guard ResizeObserver in terminal against internal xterm DOM changes
by tracking last width/height and only calling fit() on real resize
- Remove padding from .terminal-container and conflicting .xterm height
override that caused measurement mismatches with xterm-addon-fit
- Filter live session sidebar to active statuses only (running, building,
pending) instead of showing all sessions including stopped ones
- Add display name fallback across sidebar, sessions page, and instance
list to prevent blank names when display_name is empty
Quality gates: tsc (pass), eslint (pass)
- Fix tool-workshop test selectors to match component labels
- Fix API test mocks for axios client
- Add htmlFor attributes to form labels in tool-workshop
- Update type signatures to match API interfaces
Quality gates: 43/43 tests pass, typecheck pass, lint pass
- Remove inline edit modal from projects listing page
- Add Settings link to project cards navigating to /projects/:id/settings
- Reposition Open Workspace button to rightmost action for easier access
- Update tests for new UI flow
- Update documentation to reflect new editing workflow
- Sync specs: frontend-foundation and project-management
Quality gates: npm run lint passed
- Create orphan branches when HEAD does not exist yet\n- Use current branch as the default branch base in the toolbar\n- Keep push disabled for remote-less repos\n\nQuality gates: vitest repositories-settings-tab (passed); api pytest blocked by missing fastapi in environment
- Create normal working clones for remote repositories
- Initialize blank repositories with a main branch
- Align pull and branch helpers with unborn HEAD handling
- Gate fetch/pull on repositories with a remote
Quality gates: vitest repositories-settings-tab (passed); api pytest blocked by missing fastapi in environment
Backend tests:
- Unit tests for docker_build service (successful/failed builds, context, paths)
- Unit tests for readiness_probe service (success, timeout, retries, edge cases)
- Integration tests for config_folders API (CRUD + project overrides)
- Integration tests for tool_types API with new fields
- Integration tests for tool_configs API with new fields
Frontend tests:
- ToolWorkshopPage component tests (all 3 tabs, create/edit/delete)
- API client tests for tool_types and config_folders
Fixes:
- Add field_validator import to tool_configs.py
- Add JSON import to tool_config model
- Update frontend test button names to match UI (Create Tool Type, Add Config, Create Folder)
Quality gates: backend unit tests passing (23/23)
- Extract the repository create dialog into a shared component
- Reuse the same clone/validation flow in project settings and repository management pages
- Keep the shared UI covered with focused tests
Quality gates: tsc --noEmit, vitest run src/components/repositories-settings-tab.test.tsx
- Make the add repository dialog explicitly support cloning from a remote git server
- Keep blank repo creation as a fallback option
- Add validation and tests for the clone-first flow
Quality gates: tsc --noEmit, vitest run src/components/repositories-settings-tab.test.tsx
- Add repository creation dialog to the project settings repositories tab
- Reuse shared repository API for list/create/delete operations
- Add coverage for the create flow in the settings tab
Quality gates: tsc --noEmit, vitest run src/components/repositories-settings-tab.test.tsx
- Update OpenCode compose template with web server on port 3000
- Add default_port=3000 and interfaces=[terminal, web] to OpenCode seed data
- Remove hardcoded 8080 fallback in tunnel creation
- Fail gracefully when tool type has no default_port configured
- Update frontend ToolType API to include default_port, category, interfaces
- Add port, category, and interfaces fields to tool type creation form
- Display port and interfaces in tool type cards
- Create migration 0012 to make default_port non-nullable
- Set default_port values for existing built-in tool types
- Quality gates: typecheck ✓, build ✓, Python syntax ✓
- Add inline confirmation dialog before stopping instances
- Delete instances from state immediately without page reload
- Add health check polling every 30s for running instances
- Show tunnel error badge when tunnel is unreachable
- Add 'Fix Tunnel' button to recreate broken tunnels
- Update API client with health check and tunnel recreation endpoints
- Backend now returns stopped and error sessions too
- Active sessions include running/building/pending
- Recent sessions show stopped/error only
- Status badge shows actual status (running/building/pending)
- Fix Recent Sessions section to use anchor tag linking to instance URL
- Fix Last Session section to show URL and open it directly
- All Open buttons now link directly to Cloudflare URLs instead of navigating to project
- Show Cloudflare URL in active session cards
- Change Open button to anchor tag linking directly to instance URL
- Add CSS styling for URL display in session cards
- Falls back to project navigation if no URL available
Update handleOpen in SessionsPage to open the instance URL
in a new tab when available, instead of navigating to the
project page. Falls back to project navigation if no URL.
Backend:
- Add cloudflare_tunnel.py service for creating/deleting tunnels via Cloudflare API
- Add public_url and tunnel_id fields to ToolInstance model
- Update start_instance to create Cloudflare tunnel after container starts
- Update stop_instance to delete tunnel before stopping container
- Update delete_instance to cleanup tunnel before deletion
- Update restart_instance to recreate tunnel on restart
- Create Alembic migration 0011 for tunnel fields
- Add Cloudflare config settings (API token, zone ID, account ID, base domain)
Infrastructure:
- Add cloudflared service to docker-compose.traefik.yml
- Mount shared cloudflared_config volume between API and cloudflared containers
- Add Cloudflare env vars to API service
Frontend:
- Update instance Open button to handle both full URLs and proxy paths
The instance URL is now set to the Cloudflare tunnel public URL when available,
falling back to the API proxy path if tunnel creation fails.
The proxy was using instance.port which is a dynamically allocated
host port (e.g., 10001). But containers communicate on the Docker
network using their internal ports (8443 for code-server, 8888 for
jupyter). This caused connection failures when opening instances.
- Add default_port field to ToolType model (null for terminal-only tools)
- Create migration 0010 for default_port column
- Update seed data: code-server=8443, jupyter=8888, opencode=null
- Update proxy to use tool type's default_port instead of instance.port
- Update frontend ToolType interface to include default_port
Fixes: Opening instances now routes to correct internal container port
Wrap setAllSessions in useCallback so it has a stable reference.
This breaks the infinite re-render loop that was causing 4-10
requests per second to /users/me/sessions.
The state/sessions.tsx Session interface was missing repository_id and
project_id fields that were added to api/sessions.ts in the previous
commit. This caused a TypeScript build error when the app-shell tried
to pass API sessions to the state context.
Settings save:
- Remove exclude_none=True from user_config.py model_dump() call
- Fixes fields not updating when cleared or set to null/undefined
Session deletion:
- Add project_id and repository_id to get_user_sessions response
- Update frontend Session interface with new fields
- Fix handleDelete to use IDs instead of names, resolving 404 errors
- Create ToolConfigsPage with tool type selector, config list, and add/edit form
- Support both env and file config types
- Add route /tool-configs and navigation item
- Update API client with tool config endpoints
- Build passes successfully
Add support for tool categories, interface types, and per-tool configuration.
Backend:
- Add category and interfaces fields to ToolType model
- Create ToolConfig model for storing tool-specific settings
- Add tool_configs API endpoints (CRUD)
- Update built-in tool types with categories and interfaces:
- code-server: editor, [web]
- jupyter-notebook: notebook, [web]
- opencode: ai-assistant, [terminal]
- Update instance API to include tool type interfaces
- Create Alembic migrations 0008 and 0009
Frontend:
- Update ToolType and Session interfaces with new fields
- Conditionally show Open/Terminal buttons based on tool interfaces
- Add API client for tool configs
OpenSpec: tool-config-management change created and implemented.