9.1 KiB
9.1 KiB
AGENTS.md
Guide for agentic coding assistants working in the Officer monorepo.
What Is Officer
Officer is an AI-powered intranet server for small and medium businesses. It's a self-hosted platform that gives each team member a personal AI assistant, file storage, terminal, code editor, workspaces, and project management — all under centralized admin control.
Think of it as: a self-hosted, AI-native company intranet where every employee gets their own workspace with shared organizational resources and automation.
Multi-User Architecture
- Role hierarchy: Member → Admin → Owner → Super Admin
- Bootstrap flow: First user registers as Super Admin, then invites the team
- Per-user isolation: Files, sessions, settings, workspaces, and tasks are scoped per user under
$DATA_PATH/{email}/ - Shared org resources: Global tasks/skills/processes, server-level settings (SMTP, AI providers, TTS/STT/OCR), pluggable applications and resources
- Multi-scope resolution: Tasks, skills, and processes resolve user → global → native (built-in), enabling org-wide shared automation
Core Capabilities
- AI Chat — Multi-provider (Claude, OpenCode, Pi-Mono) with sessions, attachments, speech-to-text, slash commands
- File Browser — Full filesystem access per user (upload, mkdir, copy, move, delete)
- File Viewer — Preview video, images, code, text, markdown
- Terminal — WebSocket-based PTY terminal with Docker sandboxing
- Code Editor — Monaco-based IDE with file tabs
- Projects — Project management with per-project workspace layouts, git init
- Workspaces — Customizable panel-based layouts (split, resize, swap, drag)
- Automation/Skills/Tasks/Processes — Markdown-based capability definitions with YAML frontmatter
- Dev Server — Start/stop project dev servers with auto-port discovery and live proxy
- Dashboard Widgets — Clock, weather, pomodoro, daily goals, quick notes
- Settings — User preferences, server config, resource management
Quick Start Commands
Development
bun dev # Dashboard + API server (port 5000)
bun dev:emailer # Emailer workspace
Building
bun run prebuild # Run prebuild tasks
bun run build:web # Build web app
bun run build:dashboard # Build dashboard
bun run build:editor # Build editor (app + extension + runtime)
Code Quality
bun format # Format all files (Prettier, required before commit)
bun format:check # Check formatting without writing
bunx tsgo # TypeScript type checking
Note: No automated tests configured yet. Always run bun format before committing.
Project Structure
src/
├── apps/
│ └── officer-web/ # Main web UI (React 19)
│ ├── Screens/
│ │ ├── Authentication/ # Login, verify, reset password
│ │ └── Dashboard/ # All main screens (Home, Files, Chat, Terminal, Projects, etc.)
│ ├── state/ # App-specific state hooks
│ ├── lib/ # Utilities
│ └── locales/ # i18n translations
│
├── servers/
│ ├── api/ # REST API (Hono, port 5000)
│ │ ├── auth/ # Authentication (JWT + WebAuthn passkeys)
│ │ ├── users/ # User management (invite, CRUD)
│ │ ├── sessions/ # Multi-provider chat sessions
│ │ ├── workspaces/ # Workspace & project state
│ │ ├── tasks/ # Task definitions (CRUD + chat)
│ │ ├── skills/ # Skill definitions (CRUD + chat)
│ │ ├── processes/ # Process definitions (CRUD + chat)
│ │ ├── file-browser/ # Filesystem access (multi-root)
│ │ ├── terminal/ # WebSocket PTY terminal
│ │ ├── dev-server/ # Project dev server management
│ │ ├── pi/ # AI agent integration
│ │ ├── scrape/ # Web scraping (Playwright)
│ │ ├── upload/ # File uploads
│ │ ├── settings/ # User settings & state
│ │ ├── server-settings/ # Server-wide config (SMTP, AI, TTS, etc.)
│ │ ├── dock/ # Dock configuration
│ │ ├── plans/ # Markdown plans
│ │ ├── task-logs/ # Task execution logs
│ │ └── landing-page-data/# Registration status
│ └── _middlewares/ # Auth, rate limiting, CORS, body parsing
│
├── databases/
│ └── officer_db/ # JSON file-based auth store (users, passkeys, tokens)
│
└── workspaces/ # 13 shared packages
├── types/ # Central type re-exports
├── definitions/ # Constants, enums (roles, statuses, devices)
├── config/ # URL configs, env vars
├── helpers/ # cn(), formatters, slug, debounce, queue
├── hooks/ # 90+ hooks (useClient, useForm, useAuth, etc.)
├── state/ # React Query state hooks (useSettings, useChatSessions, etc.)
├── components/ # 89 components (shadcn/ui base + custom)
├── officerdev/ # Core workspace/panel framework + 11 built-in apps
├── i18n/ # Internationalization
├── injector/ # DOM manipulation for visual editing
├── widgets/ # Dashboard widgets (clock, weather, pomodoro, etc.)
├── emailer/ # React-email templates + SMTP
└── sounds/ # Audio feedback library
Path Aliases
@/→src/apps/officer-web/@@/→src/servers/@/components/*→src/workspaces/components/*
Tech Stack
- Runtime: Bun
- Language: TypeScript 5.9 (strict mode, verbatimModuleSyntax)
- Frontend: React 19, React Router, React Query, Tailwind CSS, shadcn/ui
- Backend: Hono framework, JWT auth, WebAuthn passkeys
- Storage: JSON file-based (auth store + user data), no traditional DB for most data
- Build: Vite
- AI: Claude Agent SDK, multi-provider support
Code Style Guide
Imports
Order: types → external → workspace → relative
import type { Experiment } from 'types';
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useExperiment } from 'hooks/use-experiment';
import { formatDate } from '../helpers';
Type-only imports required (verbatimModuleSyntax):
import { ActionModals, type ActionModalsTypes } from './ActionModals';
import type { FormEvent } from 'react';
TypeScript
- Strict mode always — no
any - Prefer
typeoverinterface - Colocate prop types with components as named exports
- Early returns for null/undefined guards
- Use optional chaining and nullish coalescing:
obj?.prop ?? fallback
Functions
- Arrow functions for simple/one-liners
- Regular functions for complex multi-line logic
- Named exports only (never default exports)
- Extract params type when signature gets long (no multiline params)
export const formatDate = (ts: number) => new Date(ts).toLocaleDateString();
export function calculateStats(data: DataPoint[]) { /* ... */ }
React Components
- Functional components with named prop types
- Arrow function syntax
- No useMemo/useCallback (React 19 compiler handles optimization)
type CardProps = { title: string; onClick: () => void };
export const Card = ({ title, onClick }: CardProps) => <div onClick={onClick}>{title}</div>;
State Management
- React Query for server state
- useGlobal() for UI state (backed by query cache, no Context needed)
- useWorkspacesState() for persistent workspace layouts (server-synced)
- useQueryState() for URL-synced state
- usePanelChannel() for inter-panel pub/sub communication
- Manager pattern for complex hooks — return object with state + methods
- Derived state — compute in hook, not in components
Naming Conventions
- Components:
PascalCase.tsx(e.g.,ExperimentCard.tsx) - Everything else:
kebab-case.ts(e.g.,use-experiment.ts,format-date.ts) - Hooks:
use-*.tsoruseFeatureName.ts
Formatting (Prettier)
- Semicolons: always
- Quotes: single (JS/TS), double (JSX)
- Trailing commas: all
- Indent: 2 spaces
- Line width: 120 characters
Error Handling
- Use try/catch for async operations
- Include context in error messages (IDs, resource names)
- Always async/await (never .then() chains)
General Guidelines
- Database-first approach — schema → API → UI
- Self-documenting code — clear naming, minimal comments
- Explicit over implicit — no magic
- Workspace dependencies — use
workspace:* - Multi-user aware — always consider user isolation and role-based access when adding features
- File-based storage — user data lives under
$DATA_PATH/{email}/, respect the per-user boundary