Skip to content

Latest commit

 

History

History
111 lines (89 loc) · 21.9 KB

File metadata and controls

111 lines (89 loc) · 21.9 KB

AGENT NOTES

Styling Guidelines

  • Reuse the existing token & utility layers before introducing new CSS variables or custom properties. Extend src/styles/tokens.css / src/styles/utilities.css if a shared pattern is needed.
  • Keep aggregate entry files (e.g., src/styles/controls.css, messaging.css, panels.css) lean—they should only @import feature-specific subfiles located inside src/styles/{components|messaging|panels}.
  • When adding new component styles, place them beside their peers in the scoped subdirectory (e.g., src/styles/messaging/new-part.css) and import them from the corresponding aggregator file.
  • Prefer smaller, focused style files (≈150 lines or less) over large monoliths. Split by component or feature area if a file grows beyond that size.
  • Co-locate reusable UI patterns (buttons, selectors, dropdowns, etc.) under src/styles/components/ and avoid redefining the same utility classes elsewhere.
  • Use the shared .window-* primitives from src/styles/components/window.css for dialog, popover, and floating-window headers, toolbars, bodies, footers, titles, and actions.
  • Authentication recovery uses lib/auth-recovery.ts and components/auth-recovery-dialog.tsx: confirm CodeNomad auth status before showing an expired-login form, reconnect in place to preserve drafts, and never replay failed mutations. Its shared window styles live in styles/components/auth-recovery.css.
  • Persistent command/search utility windows use components/dismissible-window.tsx: non-modal, no scrim, outside interactions keep them open, and explicit toggles use .icon-toggle with aria-expanded/aria-controls. Keep search state scoped to its instance/session and close it when that view becomes inactive.
  • The composer reserves /btw for native session.generate, outside ordinary prompt/command submission. Its ephemeral question/answer window uses DismissibleWindow and styles/components/session-aside.css; cancellation and inactive/session transitions fence late results without interrupting the main session.
  • OpenCode settings keep executable selection first and runtime status, install/update and service actions directly inline. Only version details and troubleshooting are collapsed disclosures at the bottom of the runtime panel; log levels remain the final settings card. Share controls with the startup recovery dialog rather than routing settings through a separate management modal. Disclosure styles live in styles/components/opencode-setup.css.
  • Full-history search/counts use the bundled pruning plugin's bounded queries, outside the transcript store. Current-session results and the global timeline navigate through bounded anchor windows; other-session previews fetch one native message only. Search/results/progress styles live in styles/components/history-search.css. See dev-docs/SESSION_HISTORY_QUERIES.md for scope, snapshot and cleanup semantics.
  • Keep agent, model, and thinking controls in the composer footer via PromptContextControls; adapt that footer with the named prompt-composer container rather than viewport-only breakpoints.
  • Composer attachments use one native-device action and project-scoped @ references. Picker/drop/clipboard bytes share prompt-input/useDeviceAttachments.ts for serialized budgets and epoch-based draft/focus fencing; keep lifecycle and read logic out of prompt-input.tsx. File listing/search validate the session directory through existing workspace ownership before reading a worktree or translated WSL root.
  • Session rows keep actions inline until their measured title, badges, and controls no longer fit. Keep responsive action styles in styles/components/session-row-actions.css; hidden inline controls remain measurable but inert, and an open overflow menu stays mounted until dismissal.
  • Session hierarchy geometry lives in styles/components/session-tree.css; connector axes follow the parent expander at every depth, including selection mode, RTL and touch layouts.
  • Session search/filter mode uses flat per-session results with an optional subsession switch; filters, sorting, worktree badges and selection use each result's own identity. Normal browsing retains the session hierarchy.
  • Never use rounded corners in UI styling; keep corners square unless the user explicitly requests otherwise for a specific change.
  • Explicit round exceptions: Yolo, MCP and plugin activation switches (shared styles/components/switches.css geometry), overlay drawer navigation buttons, and floating message scroll buttons. Other chrome remains square.
  • Tags and numeric/context/token labels also use rounded geometry via --chip-radius (--pill-radius is an alias). Register badge variants in styles/components/badges.css; use .badge-shape for utility-styled labels rather than adding a local radius.
  • The message-content popup and Chat settings share components/transcript-visibility.ts and the semantic icons from components/message-content-icons.ts; tool presentation metadata lives independently of renderers in components/tool-call/tool-presentation.ts. Popup styles live in styles/components/transcript-filters.css.
  • Tool-result images retain native state.content and render through the shared components/tool-call/output-images.tsx surface, including MCP and specialized tools. Keep image bytes out of text projections (copy/search/speech); image styles live in styles/messaging/tool-call/images.css.
  • Session timeline placement spans the transcript and composer via the session-owned mount; keep its rail layout in styles/messaging/session-timeline-rail.css and preserve compact-layout hiding.
  • Timeline geometry uses the cached structural index independently of transcript/excerpt loading. Aggregate tool markers retain the outline's representative tool name so their icon does not fall back to other; preserve that metadata through projection and persisted-index revalidation. Hover/focus previews render bounded Markdown, prefetch visible-nearby excerpts and prioritize the hovered marker through stores/timeline-previews.ts; never mount whole message/tool cards or fetch transcript windows for hover. Keep the viewport-bounded surface in styles/messaging/timeline-preview.css.
  • Document any new styling conventions or directory additions in this file so future changes remain consistent.
  • Soft palette families live in packages/ui/src/lib/soft-color-schemes.ts, with references in dev-docs/PALETTE_SOURCES.md. Keep selection independent of participant identity, and keep transcript/composer surfaces distinct. Run palette-quality.test.ts and inspect real rendered captures when changing palette colors or their token mapping.
  • Palette settings follow the resolved appearance in Auto mode. Keep the picker, single-row square swatches and trailing actions aligned. Swatch names use tooltips and accessible input labels. At narrow card widths, scroll the swatch strip beside the picker and move actions below via the palette-settings container. Swatch styles live in styles/components/theme-scheme-swatches.css.
  • Appearance mode and the saved light/dark selections are independent (lib/appearance-preferences.ts). Message/tool cards use the muted surface, inset output and the composer use the base canvas, and preferences use the same secondary surface as the main panels. Use --surface-hover-overlay for a subtle local rollover; preserve selected backgrounds beneath that overlay instead of replacing them with a generic panel color.
  • Right-panel base-canvas button rollover overrides live in styles/panels/control-hover.css; do not substitute the secondary surface merely to show hover.
  • V2 plugin activation controls use the shared rounded SUID switch geometry in styles/components/switches.css. Present Global and Project as separate per-plugin columns so every mutation has an explicit scope. Cache display snapshots by instance and worktree directory, and refresh them only while the plugin surface is visible.
  • Keep the Plugins surface minimal: plugin names and Global/Project switches only. Put per-plugin source/status/target details in the name's hover/focus tooltip. Do not add general descriptions, inline explanations, target footers or success commentary. The compact Refresh icon occupies the name-column header and spins during loading and refresh; automatic updates still follow native events, reconnects and visible demand.
  • Project and right-panel tabs share components/tab-scroll.tsx and styles/components/tab-scroll.css. Keep their native scrollbar above upright content without mirrored transforms, negative border overlaps or permanent compositing hints. Validate shared scrollbar styling and adjoining edges at fractional zoom in the browser and isolated Electron renderer fixtures (tests/browser/tab-chrome.test.ts).

Coding Principles

  • Tauri's Tao Windows input backport lives in packages/tauri-app/vendor/; preserve upstream provenance and avoid message pumping under input mutexes. Run node scripts/test-tauri-input-deadlock.mjs --baseline on Windows when changing it. See dev-docs/TAURI_WINDOWS_INPUT_DEADLOCK.md for the captured failure and override removal criteria.

  • Verified shared npm OpenCode installations at 2.0.15+ delegate version changes to native upgrade through opencode-update/native-upgrade.ts, with bundled npm scoped to the verified prefix. Native Windows image retention replaces the write preflight only on that path; first install, older migration and same-version repair retain direct npm/preflight. Never retry a failed native mutation via npm or restart the daemon implicitly. Validate with the isolated scripts/test-opencode-upgrade-native.mjs fixture.

  • Git is a full-functionality prerequisite, with directory-only degraded conversations when the backend cannot find Git. Only the explicitly opened physical folder is session authority in that mode; never infer sibling worktrees from native project IDs. Keep ancestor/descendant mutation identities covered by the deletion fence across Git availability changes. Inform agents through the owned native codenomad.git-availability instruction before prompts/custom commands, remove stale context after recovery, and keep this advisory separate from fail-closed environment synchronization. No blocking Git setup UI. Validate with scripts/test-git-degraded-native.mjs using an isolated CLI/database and provider.

  • OpenCode minimum requirements must follow demonstrated technical dependencies, never the latest published or solely tested version. Keep required, recommended/tested and unverified versions distinct in opencode/runtime-support.ts and setup diagnostics. Validate authenticated daemon metadata/contract before client use/plugin provisioning. Setup uses bundled Node/npm for a shared user npm installation and prefers PATH. The retired private ~/.local/share/codenomad/opencode tree must never be discovered or launched; ignore its receipts and saved selections without migrating or deleting it. Keep installer locking and Windows live-executable preflight in opencode-update/installation-lock.ts; register terminal PATH only on explicit installation. A running daemon restart is a separate explicit action. Configuration reload is also explicit: native location.reload rebuilds every loaded location and cancels pending Forms/permissions, so never use it as an automatic watcher fallback. Retire old wire translations without removing current identity/ownership checks. See dev-docs/OPENCODE_V2_POST_BETA.md for version boundaries and isolated validation evidence.

  • A selected CLI's service status can report stopped for a live older daemon. Preserve the bounded, read-only registration fallback in workspaces/native-service-registration.ts and authenticate historical metadata before allowing start; ensure() repeats discovery. Native service lookup is separate from plugin-root discovery, which must still use the connected daemon's config.get. Never write service registration/configuration files from the backend.

  • Profile environment variables are applied server-side before each native session prompt, custom command or session shell request, after ownership and worktree-mutation admission. Build a complete execution-host snapshot with workspaces/session-environment.ts; never send the profile environment through the browser or skip the per-send write using a cache. Reads and settings edits do not mutate native sessions. Keep native environment failures fail-closed and redact SDK request bodies. See dev-docs/SESSION_ENVIRONMENT.md.

  • One bundled codenomad.automation V2 plugin uses native discovery and backend presence. All browser/developer tools are available without a Developer Mode toggle; native instrumentation starts with the desktop host. Loading, tool availability and execution targeting are separate: retain the authenticated bridge and session/window fences. Tauri preview children receive no application capabilities; primary-renderer reload must dispose them and final-window checks must count native windows. See dev-docs/BROWSER_AUTOMATION.md.

  • Developer UI tools target the inspected native window/run, not the currently selected project or conversation. Keep backend ownership discovery separate from UI focus; agents must be able to inspect and navigate back from another session themselves. Accessibility refs expire on document/target replacement, not merely on session selection. Browser previews retain their own session attachments.

  • Desktop automation and pruning provisioning follows the authenticated OpenCode connection and its config.get global discovery directory, including reconnects. Never derive a running daemon's roots from backend/startup environment or CLI debug paths. OpenCode watches the entire discovery root recursively: keep changing presence leases outside it, in the sibling .codenomad/<root-hash>/ namespace. Retain existing outside-root storage; migrate inside-root storage while reading older backends' leases without writing there. WSL translates the daemon-reported paths through the selected distro only for filesystem access. Verify heartbeat stability with the isolated native automation fixture.

  • Follow dev-docs/CACHE_REFRESH_CONVENTIONS.md for display snapshots, coalesced trailing refreshes, stale-response fencing and authoritative mutation reads. Check existing feature semantics before adding another cache or refresh policy.

  • The shared native event relay consumes upstream events before slow routing I/O. Preserve per-session/PTY/Shell and per-recipient FIFO, validated full-location ownership, invalidation/connection/workspace fences and bounded-backlog reconnect recovery. A slow recipient must not block another or the shared SDK subscriber. Validate with the relay regressions and isolated native location fixture.

  • Worktree discovery/create/remove use workspaces/native-worktrees.ts and the native OpenCode worktree API. CodeNomad supplies the .codenomad/worktrees default, named-branch policy and verified family transactions. Git common-directory identity scopes the native inventory to the opened local repository; opaque worktree identifiers are separate from mutable branch labels. Validate through scripts/test-opencode-location-native.mjs with an isolated CLI and tests/browser/worktrees.test.ts for selector gestures.

  • Worktree inventory snapshots live in workspaces/worktree-inventory.ts: display reads serve cached data and lazily revalidate, directory authorization uses validated reads, and family transactions force fresh reads. Invalidation retains display data and fences pending scans; workspace.worktreesChanged refreshes existing UI consumers after a changed snapshot is published. Keep selector opening independent of refresh completion and suppress duplicate selection events during inventory reconciliation.

  • Worktree branch/HEAD annotations use one NUL-delimited Git worktree snapshot, intersected with native inventory and verified Git identities; effective checkout roots must respect Git configuration as well as administrative backlinks. Pending move targets belong to the store's instance/family request identity and survive selector navigation/remounts until native reconciliation settles.

  • Session pruning is a narrow V2 plugin/RPC exception under packages/server/src/opencode/session-pruning/; see dev-docs/SESSION_PRUNING_RPC.md. Bundle it with the shared server for both desktop hosts and provision through normal native plugin discovery. RPC registrations follow backend presence; clean shutdown removes that backend's lease and crashes expire. Loading never deletes content. Deletion occurs only on an explicit pruning request, without an extra enable-write switch or beta-number gate. Keep generic RPC proxy access closed. Writes validate actual storage, a fresh daemon-storage identity challenge and the native durable execution claim inside a synchronous SQLite transaction. Run isolated native concurrency/payload and client-cache regressions; tests must never target the shared daemon or a user's database.

  • Favor KISS by keeping modules narrowly scoped and limiting public APIs to what callers actually need.

  • Uphold DRY: share helpers via dedicated modules before copy/pasting logic across stores, components, or scripts.

  • Enforce single responsibility; split large files when concerns diverge (state, actions, API, events, etc.).

  • Prefer composable primitives (signals, hooks, utilities) over deep inheritance or implicit global state.

  • When adding platform integrations (SSE, IPC, SDK), isolate them in thin adapters that surface typed events/actions.

Multi-Language Support (i18n)

The UI uses a small custom i18n layer (no ICU/messageformat). When building features, never hardcode user-visible strings.

  • Runtime API: use useI18n() in components (const { t } = useI18n();) and tGlobal(...) in stores/non-component code.
    • Implementation: packages/ui/src/lib/i18n/index.tsx
  • Where messages live: packages/ui/src/lib/i18n/messages/<locale>/ as TypeScript objects ("flat.dot.keys": "string").
    • Each locale has an index.ts that merges message parts; duplicate keys throw at build time.
    • Merge helper: packages/ui/src/lib/i18n/messages/merge.ts
  • Adding a new string: add it to the appropriate .../messages/en/*.ts part file, then add the same key to each other locale’s corresponding file.
    • Missing translations fall back to English (and finally to the key), so gaps can be easy to miss.
  • Interpolation: placeholders are simple {name} replacements (word characters only). Avoid placeholders like {file-name}.
  • Pluralization: handle manually via separate keys like something.one / something.other and choose in code.
  • Adding a new language: add a new messages/<locale>/ folder + index.ts, register it in packages/ui/src/lib/i18n/index.tsx, and add it to the language picker in packages/ui/src/components/folder-selection-view.tsx.
  • Locale persistence: the selected locale is stored in app preferences (locale) and persisted via the server config (default ~/.config/codenomad/config.yaml; config.json is migration input only).
  • Avoid English-only paths: do not import enMessages directly in feature code; always go through t(...) so locale changes apply.

File Length Guidelines (Highlight Only)

We track file size as a refactoring signal. When you touch or create files, highlight oversized files so the team can plan refactors when time permits.

  • Source files: warn after ~500 lines; target limit ~800 lines
  • Test files: highlight after ~1000 lines

Behavior for agents:

  • Do not refactor solely to satisfy these thresholds.
  • When a change touches a file that exceeds the warning/limit, mention it in your final response and include the file path and approximate line count.
  • When creating new files, aim to stay under the thresholds unless there's a clear reason.

Tooling Preferences

  • Use the edit tool for modifying existing files; prefer it over other editing methods.
  • Use the write tool only when creating new files from scratch.
  • Browser rendering regressions live in packages/ui/tests/browser/, with deterministic HTTP fixtures beside them in fixtures/. Exercise the real Solid components and native event dispatcher rather than reimplementing rendering logic.
  • Transcript rows retain pointer hit testing during virtualizer scrolling (styles/messaging/virtual-follow-list.css), so nested code/tool scrollers and message controls receive gestures at their visible target.
  • Keep the global scrollbar-width default at zero specificity (:where(...) in src/index.css) so component visibility rules win. The timeline and transcript use the same standard native scrollbar: only marker rectangles narrow, icons and vertical spacing never shrink. components/timeline-virtual-list.tsx computes the timeline's exact extent from measured marker/gap primitives; never estimate hidden/offscreen rows. Manual rail browsing owns its position until an explicit transcript gesture. Native thumb drags retain ownership beyond the gesture timeout and defer window paging until release. Validate with the full stylesheet and real thumb gestures on mixed-height transcripts.
  • Run them with npm run test:browser --workspace @codenomad/ui after npx playwright install chromium. CODENOMAD_BROWSER_PATH optionally selects an existing Chromium executable; it does not target the installed application or user sessions.

V2 Runtime Launch

  • Native automation instrumentation starts automatically; no Developer Mode toggle or activation restart is required. Do not configure a fixed CDP port or a manual WebView2 profile.
  • Rebuild Electron before calling codenomad.act({ action: "restart" }). For Windows Tauri, stop and relaunch the release executable only when the linker cannot replace it; never stop the shared OpenCode daemon.

Commit Message Guidelines

  • When creating commits, use detailed commit messages: a concise conventional-style subject followed by body paragraphs that explain the user-visible behavior change, the implementation approach, important edge cases or platform considerations, and the validation or test coverage added.
  • Prefer messages that explain why the change exists and how regressions are prevented, not just a list of touched files.