Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add rules/coddy-project/coddy-agent/ui-spagit clone --depth 1 https://github.com/coddy-project/coddy-agentWhat it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.00000 | $0.02430 |
| Opus 5 | $0.00000 | $0.01215 |
| Sonnet 5 | $0.00000 | $0.00486 |
| Haiku 4.5 | $0.00000 | $0.00243 |
Grade A, and why
ui-spa scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 2d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 28 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Embedded UI (external/ui)
- Rebuild
go:embedassets after UI changes:make build TAGS="http ui"(runsui-build). - Thinking disclosure row - duration must sit next to the thinking label, not at the trailing edge of the column. Markup keeps
.thinking-durinside.thinking-leftinThinkingMessage.tsx. Styles use.thinking-left { gap: 0 5px; }instyles.css. Do not drive label vs timer spacing withjustify-content: space-betweenonsummary.thinking-summary. - Workspace context chips (
WorkspaceChips.tsx, first child of.composer-card) - folder / branch pills plus a worktree checkbox (realinput[type=checkbox],composer-worktree-checkbox), backed byGET /coddy/workspace/contextandPOST /coddy/sessions/{id}/workspace. Keep the pure logic inchat/workspaceContext.tsand the MRU recents inchat/workspaceRecents.ts(do not inline them). The folder menu is Claude Desktop style: Recent rows (current marked ✓) +Open folder…opening theWorkspaceFolderModal.tsxfilesystem browser (rows navigate, Open picks the browsed folder). The branch chip renders only whenis_git_repo; the checkbox is checked+disabled when the session already runs in a linked worktree. Chosen once: with a non-empty transcript the chips lock (workspaceLocked) and the server returns 409. Menus reuse themode-menufamily: anchored portal on desktop (opens-downon hero,opens-updocked), bottom sheet onisMobileShell. Pre-session picks are pending inApp.tsx(pendingWorkspaceRef) and applied on first send beforePOST /v1/responses. Contract:DESIGN.md(Composer workspace chips),docs/ui.md(Per-session workspace). - Composer context ring (indicator left of Send in
Composer.tsx)- The ring itself is only a stroked arc (relative context fill). Do not place a numeric percent label inside or on top of it. Percent usage and token counts belong in the tooltip only.
- Idle home (
contextIdlewhen there is nosessionId): keep the arc at zero fill. Tooltip body is exactly: first lineNo context usage yet, second lineMax context <n>(no session usage lines, no model name line). - With an active session (including hero with
#/s/...in the hash): show fill fromtokenUsagevsmaxContextTokens. Tooltip lists percent line, optional Input/Output/Total line(s) whentokenUsageis present, then Max context line. Do not add aModel …line in this tooltip (Mode exposesagent/plan;Modelis the YAML backend pill next to it). - Tooltip presentation: reuse the
rail-tipstyling (same family as narrow navbar hints). Anchor it above the ring, horizontally centered on it, with a comfortable width (usecomposer-context-tip-host/composer-context-tiprules instyles.css; avoid a cramped single-column tooltip). - Prefer no extra chrome on the meter (no bordered "button tile" behind the SVG);
.context-ringstyling stays minimal. Stroke colors come from--coddy-context-ring-inner/--coddy-context-ring-fg(defined per theme instyles.css).
- Send/stop circle (
Composer.tsx#btn-send) -composer-iconis a perfect circle (border-radius: 50%, square box). The sharedcomposer-run-iconclass is also used by scheduler run/stop buttons. Play uses ~22px ▶; stop uses.composer-stop-square(14x14px, centered). Ring + stop stay incomposer-bar-actionson the right (DESIGN.md, Composer primary action). - Model selector menu (
Composer.tsx,mode-menu--llm) - backend ids arevendor/model. Keep the filter/group/threshold logic inchat/llmModelMenu.ts(do not inline it): vendor headers (mode-menu-group-label) render only when more than one vendor is present (shouldGroupLlmModels); a filter input (mode-menu-filter,data-testid="model-menu-filter", auto-focused) renders only when the backend count exceedsLLM_MENU_FILTER_THRESHOLD(5) and matches vendor / model name / full id case-insensitively (filterLlmModels); rows scroll under a ~5-row cap (mode-menu-scroll,max-height: min(175px, 50vh)instyles.css). Rows showdisplayLlmId(model name only) with the full id intitle; Enter picks the first match, Escape closes, empty result rendersmodel-menu-empty. - Mobile menu sheet - on narrow shells (
isMobileShellviashellBreakpoint.ts,max-width: 1199px) theMode/Model/Reasoningportal menus render as a full-width bottom sheet (mode-menu--sheet, same family as the slash/at picker sheet) over a dimmed scrim (mode-menu-backdrop--scrim), not the anchoredmode-menu--portaldropdown. Drive this frommenuUseSheet = isMobileShellinComposer.tsx; the sheet overrides the desktopmode-menu--llmwidth cap and uses a46vhscroll cap. Keep the anchor positioning only on the desktop portal branch. - Tool call timer - while an unresolved
permission_promptreferences a tool call id,ToolCallMessagefreezes thethinking-durlabel (permissionWaitingviapermissionPendingToolCallIds). - Permission after reload - SSE rows persist in
localStorage(permissionPromptSessionStore.ts); pendingrun_command/ fs tools without a tool result also get a syntheticpermission_promptonGET .../messagesmerge (restorePermissionPrompts.ts). Stop glyph is.composer-send-glyph>.composer-stop-square(never both classes on one node). - Composer keyboard shortcuts -
EnterandCtrl+Entersend on desktop;Shift+Enterinserts a newline (browser default, not intercepted). On mobile/narrow viewports (max-width: 1199pxshell stack breakpoint,isMobileShellviauseSyncExternalStoreoversubscribeShellStackfromshellBreakpoint.ts),Enterinserts a newline and send is button-only. The pickerEnterhandlers (slash/at menus) take precedence and run before the send logic on all viewports. - Multimodal model flag -
GET /v1/modelsexposesmultimodal: boolper entry from YAMLmodels[].multimodal.App.tsxreads it intoModelInfo.multimodal, derivesllmModelMultimodal(useMemoover currentllmModel), and passes it throughChatScreen→ComposerasllmModelMultimodal?: boolean. Only render file attachment UI (file picker button, attachment previews) whenllmModelMultimodalistrue; keep the prop optional so the component degrades gracefully when models are not configured. After a successfulPUT /coddy/configsave,SettingsfiresonConfigSaved→App.tsxbumpsmodelsEpoch→ re-fetches/v1/modelsso the attachment button appears without a page reload. - File attachment flow - Composer holds
attachedFiles: File[]state; the hidden<input type="file">ref (data-testid="composer-file-input") feedssetAttachedFiles. When sending, if files are present,onSend(text, files)passes them up;App.tsxreads each as a data URL (FileReader) and addsinline_files: [{name, data_url}]to thePOST /v1/responsesbody. Foragent/planturns: the backend saves each file to~/.coddy/sessions/<id>/assets/with0o444permissions and injects a<coddy_session_assets>XML annotation into the user message content so the model canreadorcpthe files. The SPA strips this annotation from the display (and from copy-to-clipboard) viastripCoddyAttachmentsForUserDisplayinstripCoddyAttachments.tsand instead renders file chips (name + type icon) above the user bubble usingmsg-user-files/msg-user-file-chipCSS classes;parseSessionAssetFilesin the same file re-derives chip metadata from the XML on page reload so chips persist. For direct YAML model: each entry becomes animage_urlcontent part sent inline to the provider. Multiple files in one request are supported; duplicate asset names are disambiguated with_1,_2suffixes bySavePartsToAssetsininternal/session/assets.go. - Mobile MQ helpers (
shellBreakpoint.ts) -subscribeShellStack,snapshotShellStack,serverSnapshotShellStackare exported foruseSyncExternalStoreuse anywhere in the SPA. Do not duplicate these in component-local functions. - Authoritative layout and tokens remain in the repo root
DESIGN.md.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 2d ago First seen · 28 lines · 0 tokens per session scan A d0839e5c6d87
ui-spa is a cursor rule published in the GitHub repository coddy-project/coddy-agent (127 stars, last pushed 4d ago), licensed MIT. It costs nothing until one of its globs matches a file; then it loads 2,430 tokens. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other cursor rules, from other repositories
angular-20
This rule provides comprehensive best practices and coding standards for Angular development, focusing on modern TypeScript, standalone components, signals, and performance optimizations.
dev-standard
Apache Superset development standards and guidelines for Cursor IDE.
cli-error-handling
CLI command error handling patterns.
prefer-direct-imports-over-module-mocks
Prefer extracting a testable core over vi.mock / vi.resetModules when unit tests need to reach production logic entangled with config, env, or singletons.
control-plane-descriptors
Control plane descriptor and instance implementation patterns.
family-instance-domain-actions
Family instance domain action implementation patterns.