* docs(readme): refresh contributors wall
* docs(readme): refresh contributors wall
* feat: kilo cli
* fix: use default model option for kilo
* chore: add agent_diff id unique test
* chore: add deepseek to docs
* feat(daemon): add DeepSeek TUI as a code agent adapter
Register `deepseek` (with `deepseek-tui` cargo-only fallback) in
AGENT_DEFS via `deepseek exec --auto [--model X] <prompt>` and plain-text
streaming. Ships `deepseek-v4-pro` / `deepseek-v4-flash` as fallback
model hints; users can paste any other id (incl. NIM / Fireworks /
SGLang routes) via the custom-model input.
Web UI gets a DeepSeek-blue gradient icon, label/alias mapping, and
docs/agent-adapters.md §5.9 documents the auth state, prompt-as-argv
Windows size limit, and the upstream gap that prevents stdin delivery
today (clap declares `prompt: String` as a required positional).
Adds .deepseek/ to .gitignore alongside the other per-agent runtime
data dirs so first-launch trust files don't leak into git.
* fix(daemon): drop unsupported deepseek-tui fallback bin
The `deepseek` dispatcher owns `exec` / `--auto`; `deepseek-tui` is the
runtime companion it invokes. Listing `deepseek-tui` in fallbackBins
advertised availability for a host that only had the TUI binary, but
buildArgs still emitted `<resolved> exec --auto <prompt>` — which
deepseek-tui itself doesn't accept, so the first /api/chat run would
fail. Upstream documents both binaries as required (npm and cargo paths
install them together), so the fallback didn't correspond to a supported
install. Pin the absence in the agents test and update docs §5.9 + the
adapter table to match.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): pre-flight DeepSeek TUI prompts against argv byte budget
DeepSeek's exec mode requires the prompt as a positional argv arg (no
`-` stdin sentinel upstream), so a fully composed OD prompt — system
text + history + skills + design-system content + the user message —
can blow Windows' ~32 KB CreateProcess limit (or Linux MAX_ARG_STRLEN
on extreme edges) and surface as a generic spawn failure instead of
a DeepSeek-specific, user-actionable message. The adapter now declares
`maxPromptArgBytes = 30_000` (leaves ~2.7 KB argv headroom for `exec
--auto --model <id>` and Windows quoting), and the /api/chat spawn
path checks the composed prompt against that budget before calling
`spawn`. Oversized prompts fail fast with `AGENT_PROMPT_TOO_LARGE`
and guidance to reduce skills/design context or pick an adapter with
stdin support.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* test(daemon): pin DeepSeek argv-budget guard with regression tests
The previous spawn-path guard inlined the byte-budget check in the
chat handler, so the only safety net for the DeepSeek argv-only
prompt-delivery shape was a static "the field exists" assertion —
nothing actually exercised the AGENT_PROMPT_TOO_LARGE path or the
short-prompt happy path. Extract the check into a pure
`checkPromptArgvBudget(def, composed)` helper in agents.ts, call it
from /api/chat before bin resolution (so the guard is order-
independent and fires regardless of whether the adapter binary is
on PATH in CI), and add a regression test that exercises both the
oversized-prompt branch (over the conservative under-Windows-
CreateProcess budget) and the short-prompt branch, plus a UTF-8
byte-vs-codepoint case and a stdin-adapter no-op case so the guard
can't silently regress or leak onto adapters that ship the prompt
over stdin.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): pre-flight DeepSeek prompts against Windows .cmd-shim quoting
The first-pass argv-byte guard only inspects the raw composed prompt, so
on Windows an npm-installed `deepseek` resolves to a `.cmd` shim and the
spawn path then wraps the call in `cmd.exe /d /s /c "<inner>"` with
every embedded `"` doubled by `quoteWindowsCommandArg`. A quote-heavy
prompt (code blocks, JSON-shaped skill seeds) under the 30,000-byte
budget can therefore still expand past CreateProcess's 32_767-char
`lpCommandLine` cap and surface as a generic spawn ENAMETOOLONG instead
of the DeepSeek-named, actionable `AGENT_PROMPT_TOO_LARGE` the budget
guard was meant to provide. Add a second pure helper
`checkWindowsCmdShimCommandLineBudget(def, resolvedBin, args)` that
mirrors the platform layer's per-arg quoting and recomputes the would-be
command line length whenever the resolved binary is a `.cmd` / `.bat`
shim, and call it from `/api/chat` after `buildArgs` / `resolveAgentBin`
so the same SSE error fires before `spawn`. Pin the new path with a
quote-heavy regression (prompt is under the byte budget but doubles
past the kernel cap) plus no-op tests for non-`.cmd` resolutions, null
bin, and stdin-only adapters so the guard can't drift back.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): extend DeepSeek argv guard to direct .exe Windows installs
The cmd-shim guard added in 9011361 early-returned for non-`.cmd` /
`.bat` resolutions, so a Windows host that resolved `deepseek` directly
to a `.exe` (cargo-installed CLI, hand-built release, anything outside
the npm shim path) bypassed the post-`buildArgs` budget check entirely.
Direct `.exe` spawns skip the `cmd.exe /d /s /c "<inner>"` wrap, but
Node/libuv still composes a CreateProcess `lpCommandLine` by walking
each argv element through `quote_cmd_arg` — every embedded `"` becomes
`\"`, backslashes adjacent to a quote get doubled. A quote-heavy
prompt (code blocks, JSON-shaped skill seeds) under the 30,000-byte
`maxPromptArgBytes` budget can therefore still expand past the kernel's
32_767-char cap on those installs and surface as a generic spawn
ENAMETOOLONG instead of the actionable `AGENT_PROMPT_TOO_LARGE` the
guard was meant to provide.
Add a sibling pure helper `checkWindowsDirectExeCommandLineBudget(def,
resolvedBin, args)` that mirrors libuv's quoting math (empty -> `""`;
no whitespace/quote -> pass-through; quote-only -> simple wrap;
otherwise per-char escape with backslash-doubling around quotes and
trailing backslashes) and recomputes the would-be command line length
whenever the resolved binary is a non-shim Windows install. The two
Windows guards are mutually exclusive: the cmd-shim guard owns
`.bat` / `.cmd`, the direct-exe guard owns everything else, so a
single oversized prompt never double-emits an SSE error. POSIX paths
on POSIX hosts still skip both guards (no CreateProcess in play), and
stdin-delivered adapters still skip the entire post-buildArgs path.
Wire the new helper into `/api/chat` after the cmd-shim check, pin
the path with a quote-heavy regression on a `C:\Program
Files\DeepSeek\deepseek.exe` resolved bin (prompt under the byte
budget but past the kernel cap once libuv-quoted), plus no-op tests
for ordinary prompts, `.cmd`/`.bat` resolutions, POSIX paths,
null/empty bin, and stdin-only adapters, plus a mutual-exclusivity
assertion so the two guards' division of labour can't drift.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): neutralize cmd.exe %var% expansion in DeepSeek argv
Wrap each `%` in `"^%"` inside `quoteWindowsCommandArg` so cmd.exe's
percent-expansion can't substitute env values into a `.cmd`-shim spawn
when the DeepSeek argv carries a prompt mentioning `%DEEPSEEK_API_KEY%`.
Mirror the change in the agents.ts helper used by the budget guard, add
`AGENT_PROMPT_TOO_LARGE` to the contracts so typed SSE consumers
recognize the existing daemon error code, and pin the literal-prompt
contract with regression tests.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* feat(web): add pet companion with Codex hatch-pet integration
Introduces a customizable floating pet companion (overlay + entry-view rail
+ composer menu + dedicated Settings → Pets section) that supports built-in
pets, user customization (glyph/image/spritesheet), and one-click adoption
of pets packaged by the upstream Codex `hatch-pet` skill via a new
`/api/codex-pets` daemon endpoint. Vendors the unmodified `hatch-pet`
skill under `skills/hatch-pet/` and adds i18n strings across all locales.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(scripts): sync community Codex pets from public catalogs
Adds `pnpm sync:community-pets` which fetches all pets from
codex-pet-share.pages.dev (paginated Supabase Functions API) and
j20.nz/hatchery (single-shot JSON), then writes each one as
`<id>/pet.json` + `<id>/spritesheet.webp` under
`\${CODEX_HOME:-\$HOME/.codex}/pets/`. The existing daemon
`codex-pets` registry already scans that folder, so synced pets
appear under Settings → Pets → Recently hatched and adopt with one
click — no manual upload. Supports --source/--out/--force/--limit
flags and validates magic bytes so HTML error pages never end up
masquerading as `.webp` files.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(daemon): tighten codex-pets validation and document vendoring
- sanitizeId now rejects ids that still contain `..` after collapsing,
closing a defensive gap on the path-traversal guard for the
`/api/codex-pets/:id/spritesheet` route.
- listCodexPets emits the sanitised folder name as the public id so the
download route resolves directly against the on-disk folder, even when
`manifest.id` differs (manual drops, sanitiser-touched manifests).
- Drop `@ts-nocheck` from `codex-pets.ts`; module is now strict-typed
with explicit interfaces, an unknown-narrowed JSON.parse path, and a
`pickString` helper guarding manifest fields one by one.
- Restrict the spritesheet response CORS header to sandboxed-iframe
callers (Origin: null) instead of unconditional `*`, matching the
existing raw-file route pattern. Same-origin web traffic does not
need the header (web proxies `/api/*` through the daemon).
- Add `skills/hatch-pet/README.md` explaining the vendoring trade-off,
provenance, and re-sync procedure.
- Add `docs/codex-pets.md` covering where pets live, how to populate the
registry without Codex installed, and the manifest contract.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(i18n): add pet.* keys to Hungarian locale
Hungarian locale was added on main after this branch diverged, so the new
pet.* dictionary keys never landed there and tsc -b reports hu's Dict as
incomplete once main is merged in.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* feat(web): atlas-driven pet animations + bundled community pets
Builds on the existing pet companion (#296) with a richer animation
loop, a curated set of community pets that ship with the repo, and a
one-click sync into ~/.codex/pets/.
- Atlas-mode rendering: PetSpriteFace can now play the full Codex 8x9
sprite atlas and swap rows from a JS-driven frame index. PetOverlay
classifies pointer interactions (idle / hover / drag-direction /
long-idle waiting) and maps them to the matching atlas row, so the
pet waves on hover, runs on drag, and falls into a waiting pose
after 6s of stillness. Single-strip pets keep their existing CSS
steps() animation, with the steps timing fixed to jump-none so frame
cells line up on cell boundaries.
- Atlas adoption: PetSettings exposes both "Use full atlas (animated)"
and "Freeze to this row" — full mode keeps every row for the
interaction state machine, single-row mode crops one strip via the
existing canvas helper. New prepareCodexAtlas downscales the atlas
to a localStorage-friendly PNG while preserving the grid layout.
- Settings tabs: pet sources are now split into Built-in / Custom /
Community tabs so each origin gets its own dedicated surface.
- Bundled pets: scripts/bake-community-pets.ts seeds a curated set
(clippit, dario, nyako-shigure, slavik, trump, tux, yelling-dario,
yorha-sit-2b) into assets/community-pets/. The daemon scans this
alongside the user's ~/.codex/pets/ root, with user pets winning
when ids collide. CodexPetSummary gains a `bundled` flag so the UI
can tag those cards with a "Bundled" pill.
- One-click community sync: daemon-side port of sync-community-pets
exposed via POST /api/codex-pets/sync. Returns the same
wrote/skipped/failed/total summary the CLI prints. Web Pet settings
surface this as a "Download community pets" button under the
Community tab.
- Avatar dropdown + hide rail: EntryView's avatar button is now a
small menu (mirrors the project-view AvatarMenu) with toggles for
hiding/showing the pet rail and opening Settings. PetRail gets a
matching × button for the same hide flow.
- Locales: 7 new pet.* keys for tabs, sync, hide/show, atlas full
mode, and the Bundled pill — translated into all 13 supported
locales.
Typechecks pass across all workspace packages; daemon + web vitest
suites stay green.
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(web): bundled-pets built-in tab, ambient atlas animations, and community sync button
The Built-in tab now sources its catalog from the bundled spritesheets
at `assets/community-pets/` instead of the eight emoji placeholders that
felt boring next to the Codex hatch-pet atlases.
- Daemon: `listCodexPets` flags `bundled: true` by curated-set membership
in `assets/community-pets/`, not by which folder the sprite happened to
be read from. Previously a fully-synced user inbox preempted every
bundled id and left the tab empty.
- Settings → Pets → Built-in renders the same sprite-card grid as
Community, filtered by `bundled: true`, and reuses the existing
`adoptCodexPet` flow. Community tab filters to non-bundled so the
curated set never appears twice.
- Community tab gains the long-promised "Download community pets"
trigger that calls `/api/codex-pets/sync` and shows an inline status
line for the run summary. Strings already existed in every locale; we
just plumbed the button.
- `PetOverlay` gets ambient atlas-row choreography — while idle, the
overlay occasionally swaps `idle` for a random non-idle row (wave /
hop / look) so the pet doesn't feel frozen. User gestures cancel the
beat and take over instantly. `pickAmbientRow` lives next to
`pickAtlasRow` so both row pickers share the fallback discipline.
- One-shot `migrateCustomPetAtlas` heals configs adopted before the
overlay learned row switching by re-downloading the full spritesheet
so hover / drag / ambient variety light up on next launch.
- `BUILT_IN_PETS` is now an empty array (the type stays for backwards
compat); legacy configs whose `petId` still points at an emoji id
(`mochi`, `pixel`, …) fall back to the user's custom slot in
`resolveActivePet` so the overlay never renders blank.
- i18n: refresh `pet.tabBuiltInHint` (drop "emoji companions") and add
`pet.builtInEmpty` across all locales.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(craft): add brand-agnostic craft references and refero-derived lint rules
Introduce `craft/` as a third top-level content axis alongside `skills/`
and `design-systems/`, holding universal (brand-agnostic) craft rules
that apply on top of any DESIGN.md. Skills opt in via a new
`od.craft.requires` front-matter array; the daemon resolves the slug
list and injects the matching files between DESIGN.md and the skill
body in the system prompt.
Initial vendor (MIT, adapted from referodesign/refero_skill): typography
craft, color craft, anti-ai-slop. Pilot wired on saas-landing.
Extend the existing lint-artifact pass with two refero-derived rules:
- P0 ai-default-indigo — solid #6366f1 / #4f46e5 / #4338ca / #8b5cf6 as
accent (not just gradients) is the most-reported AI tell.
- P1 all-caps-no-tracking — `text-transform: uppercase` rules without
≥0.06em letter-spacing.
The craft loader silently drops missing files so a skill can
forward-reference future sections (e.g. `motion`) without breaking.
* fix(daemon): skip :root token blocks in ai-default-indigo lint
The ai-default-indigo P0 check scanned the whole HTML for the raw
hex, so brands that intentionally encode indigo as `--accent: #6366f1`
in :root and consume it via var(--accent) downstream were flagged
as AI-default — a false positive that forced the agent to "fix"
valid output. Strip :root token-definition blocks (including
attribute-selector theme variants) before scanning, mirroring the
existing pattern used by the raw-hex P1 check. Hex still flagged
when it appears in component rules or inline styles.
* docs(craft): address PR #225 P3 review feedback
- craft/README.md: explain why missing craft sections are silently
dropped (forward-compatibility) instead of surfacing a warning.
- craft/typography.md: ground the 0.06em ALL CAPS tracking floor in
Bringhurst-derived typographic practice rather than presenting
the threshold as unattributed.
- craft/color.md: cover the edge case where a brand's DESIGN.md
intentionally encodes indigo as --accent — `var(--accent)` uses
remain unflagged because the linter only inspects hardcoded hex.
- docs/skills-protocol.md: link the "missing files dropped silently"
note back to craft/README.md for the canonical slug list and the
rationale behind the choice.
* fix(craft): address PR #225 P0 review feedback
- tools/pack: copy `craft/` into the packaged resource root alongside
`skills`, `design-systems`, and `frames`, so the `od.craft.requires`
integration isn't a silent no-op when the daemon resolves
`${OD_RESOURCE_ROOT}/craft` in packaged builds.
- packages/contracts: add `craftRequires?: string[]` to `SkillSummary`
(and therefore `SkillDetail`) so the field that `listSkills()`
already returns and `/api/skills(/:id)` already serializes via
`...rest` is part of the documented web/daemon contract instead of
leaking through as an untyped property.
- apps/daemon/lint-artifact: expand the indigo token-strip pass to
cover selector lists containing `:root` (e.g. `:root, [data-theme="light"]`)
and any rule whose body is custom-property-only (e.g. a
`[data-theme="dark"] { --accent: ... }` theme variant). Real
component rules with a hardcoded indigo are still preserved so the
P0 finding still fires; tests cover the new selector-list and
theme-variant cases.
* fix(craft): address PR #225 follow-up review feedback
- lint-artifact: scope the indigo token-strip to <style> blocks so the
rule-shaped regex no longer captures leading `<style>` text into the
selector (which broke `:root` recognition for token blocks that mix
`color-scheme`/etc. with `--accent`). Run the strip on the extracted
CSS instead, with a regression covering `:root { color-scheme: light;
--accent: #6366f1 }`.
- lint-artifact: tighten the custom-property-only exemption to global
theme-scope selectors (`:root`, `html`, `body`, bare attribute
selectors like `[data-theme="dark"]`). Component-local rules such as
`.cta { --cta-bg: #6366f1 }` are no longer exempted, so an agent
cannot launder default indigo through a local var. Regression test
added.
- craft/anti-ai-slop.md: stop claiming every rule below is enforced by
the linter; only several are. The unenforced rules (standard
Hero→Features→Pricing→FAQ→CTA flow, decorative blob/wave SVG
backgrounds, perfect symmetry) are now flagged inline as
"(guidance, not auto-checked)" so the contract with the lint surface
stays honest.
* fix(daemon): tighten lint-artifact iteration and :root token gating
- all-caps-no-tracking: iterate every <style> block. The previous
check called `exec` once on a non-global regex, so an artifact
whose offending uppercase rule sat in a second <style> block
(e.g. a reset block followed by a components block) slipped
past. Switch to `matchAll` and break across both loops once a
violation is found. Regression test covers a second-block
uppercase rule.
- ai-default-indigo: stop unconditionally exempting any selector
list containing `:root`. The exemption now requires both
conditions to hold: every selector in the list is global theme
scope AND the body is token-shaped (CSS custom properties or
the `color-scheme` keyword). So `:root { background: #6366f1 }`
and `:root, .cta { --cta-bg: #6366f1 }` no longer launder a
hardcoded indigo through the strip pass. Regression tests cover
both bypass shapes.
* fix(daemon): scope theme-attr exemption and strip CSS comments in token blocks
Address PR #225 review feedback on `ai-default-indigo`:
- The bare-attribute branch of `selectorListIsGlobalThemeScope` accepted
any `[attr=...]` selector, so a custom-property-only rule on a
component/state attribute (e.g. `[data-variant="primary"]`,
`[aria-current="page"]`) was treated as a global theme block and
stripped before the indigo scan — exactly the component-local indigo
laundering this lint is meant to catch. Restrict the exemption to a
small allowlist of known theme switches: `data-theme`,
`data-color-scheme`, `data-mode`.
- `stripTokenBlocksFromCss` split rule bodies on `;` and matched each
fragment from the start, so a token block whose body contained a
normal CSS comment such as `:root { /* brand accent */ --accent:
#6366f1; }` produced a fragment beginning with the comment, failed
`isTokenShapedDeclaration`, and the rule was left in scope of the
indigo scan — a false P0 on a legitimate token definition. Strip CSS
comments before splitting/classifying declarations.
Add regression coverage: arbitrary component/state attribute selectors
still trip `ai-default-indigo`; `data-color-scheme` theme variants stay
exempted; `:root` token blocks with leading, trailing, and
between-declaration CSS comments are recognized.
* fix(daemon): strip CSS comments and recognize tokens nested in at-rules
The all-caps-no-tracking scan ran against raw `<style>` content, so a
commented-out rule like `/* .eyebrow { text-transform: uppercase; } */`
matched `upperRe` and emitted a P1 for CSS the browser ignores. Strip
CSS comments from the style body before structural matching.
`stripTokenBlocksFromCss` only matched flat `selector { body }` rules,
so a media-query-wrapped token block like
`@media (prefers-color-scheme: dark) { :root { --accent: #6366f1 } }`
had its outer `@media` rule treated as the selector/body pair and the
inner `:root` token block was never stripped, producing a P0 false
positive on legitimate responsive theme CSS. Tighten the body
alternation to `[^{}]*` so the regex matches innermost rules and
recognizes the inner `:root` block directly while preserving the
outer at-rule wrapper.
* fix(daemon): align ai-default-indigo list with documented cardinal sins
The lint's AI_DEFAULT_INDIGO subset omitted #3730a3 and #a855f7, which
craft/anti-ai-slop.md lists as P0-blocked solid accents. An artifact
could hard-code one of those documented colors as a button fill and
slip past the indigo scan unless it happened to be inside a gradient.
Bring the lint set to the exact list documented in the craft doc, and
tighten the doc's wording from "etc." to an explicit enumeration that
points at AI_DEFAULT_INDIGO so the prompt contract and daemon behavior
stay in sync. Add regression tests pinning each newly-included hex.
* fix(daemon): tighten theme-scope selector and scan inline ALL CAPS
The theme-scope exemption used to accept any attribute on `:root`,
`html`, or `body` (e.g. `:root[data-variant="primary"]`), letting an
agent launder default indigo through a component/state attribute and
slip past the `ai-default-indigo` lint. The prefixed branches now
require the attribute name to be one of GLOBAL_THEME_ATTRIBUTES,
matching the bare-attribute branch.
The `all-caps-no-tracking` rule only iterated `<style>` blocks, so
inline declarations like `<span style="text-transform: uppercase">`
produced no finding even though craft/typography.md treats the
≥0.06em tracking floor as having no exceptions. Added a second scan
over `style="..."` attributes that runs the same letter-spacing
check and dedupes against the existing `<style>`-block finding so
the agent gets a single corrective signal per artifact.
* fix(daemon): align uppercase tracking px floor with the 0.06em rule
The previous absolute fallback (>=1.5px) was stricter than the craft
rule it enforces. `font-size: 12px; letter-spacing: 1px` is 0.083em
— above the 0.06em floor — but 1.5px would reject it and trigger an
unnecessary correction loop on compliant small-label CSS.
Extract `hasAdequateUppercaseTracking`: read `font-size` from the same
rule body and compare px tracking against `fontSize * 0.06`; fall back
to a conservative >=1px floor when font-size is inherited (covers the
default 16px body where 1px ≈ 0.0625em). Apply the helper to both the
<style>-block scan and the inline-style scan, and add 12–14px label
tests in both branches.
* fix(daemon): treat rem letter-spacing as absolute, not per-element em
`rem` was previously folded into the same branch as `em` and accepted
at the 0.06 threshold. But `rem` is relative to the root font-size
(16px default), not the element's own font-size, so on a 48px heading
`letter-spacing: 0.06rem` resolves to 0.96px — about 0.02em of the
element, well below the 0.06em rule the lint enforces.
Convert rem to absolute px through the 16px root assumption and reuse
the same px-vs-element-font-size resolution: same-rule `font-size: <n>px`
gives an exact `n * 0.06` floor; otherwise the conservative >=1px
fallback applies. Add regression tests for 48px headings with 0.06rem
tracking (must flag) plus the 16px-element and rem-floor matches that
must keep passing, in both <style>-block and inline-style branches.
* fix(daemon): resolve var() refs in uppercase tracking lint
`hasAdequateUppercaseTracking` only matched literal numeric values,
so a tokenized rule like `letter-spacing: var(--caps-tracking)` —
exactly the pattern the craft prompt steers artifacts toward — was
falsely reported as `all-caps-no-tracking`. Extract `--name: value`
declarations from global theme scopes (`:root`, `html`, theme-attribute
selectors) once per artifact, then expand simple `var(--name)` (and
`var(--name, fallback)`) references in the inspected rule body before
applying the existing 0.06em / px-floor / rem-conversion logic.
References without a matching token and no fallback stay in place,
preserving the conservative "missing tracking" finding.
* fix(daemon): resolve rem and var() font-size in uppercase tracking lint
Previously the px-vs-element-font-size resolution only matched
`font-size: <n>px`. Any rem-based or tokenized display size fell
through to the lenient `>= 1px` body-text fallback, so an artifact
emitting `.display { font-size: 3rem; text-transform: uppercase;
letter-spacing: 1px; }` (a ~48px heading with a 2.88px floor) slipped
past the lint that this helper exists to enforce.
Resolve `rem` font-size via the same root-font assumption already used
for tracking, and treat any explicitly declared but unresolvable unit
(`em`, `%`, `calc(...)`, an unresolved `var(...)`) conservatively —
refuse the lenient fallback so the rule must use either an `em`
letter-spacing or a verifiable px/rem font-size.
`var()` font-size declarations resolve through the existing
`resolveCssVars` pass before the size scan runs, so the same fix
catches the tokenized-display-size pattern (`--display-size: 3rem`).
* fix(daemon): parse declarations to ignore custom-prop names in uppercase tracking lint
The hasAdequateUppercaseTracking and resolveFontSizePx helpers used substring regexes against the rule body, so a token-name declaration such as `--letter-spacing: 0.08em` or `--display-font-size: 48px` could satisfy the `letter-spacing` / `font-size` checks even though it has no rendered effect — letting actual ALL-CAPS-without-tracking rules slip past the P1 lint.
Parse the declaration list, compare exact property names, and skip declarations whose property starts with `--`. Adds regression tests covering token-name letter-spacing (style-block + inline) and a token-name font-size masking the bail-out branch.
* fix(daemon): scope indigo token exemption to --accent only
Previously stripTokenBlocksFromCss removed every custom-property-only
global theme block before the ai-default-indigo scan, which let a
laundered indigo token like `:root { --primary: #6366f1 }` consumed
via `var(--primary)` slip past the lint. The craft contract is that
the only escape hatch is encoding indigo as the design system's
`--accent` token; any other token name is still the LLM-default
color hidden behind an arbitrary name. Narrow the strip pass so a
non-`--accent` token whose value carries an AI-default indigo hex
keeps the rule in scope, and add regression tests for `--primary` /
`--button-bg` global tokens feeding a CTA, including the at-rule
and theme-attribute variants.
* fix(daemon): model CSS cascade in tracking lint and detect blue→cyan trust gradients
Address PR #225 review feedback (3 comments):
- `letter-spacing` / `font-size` selection now picks the LAST matching
declaration in the rule body, modeling CSS source-order cascade.
`.eyebrow { letter-spacing: 0.08em; letter-spacing: 0.02em }` renders
the noncompliant 0.02em the browser actually shows; the previous
first-match behaviour silently passed it.
- `extractCssTokens` now records every distinct value seen for a token
across global theme scopes, and `hasAdequateUppercaseTracking`
enumerates each combination so a default-theme value below the floor
cannot be rescued by a scoped override that happened to be parsed
later (`:root { --caps-tracking: 0.02em }` +
`[data-theme="dark"] { --caps-tracking: 0.08em }` now fires).
- New `trust-gradient` P0 rule pairs blue/sky tokens against cyan
tokens in `linear-gradient(...)` bodies so `blue→cyan` two-stop
trust gradients (documented as a cardinal sin in
`craft/anti-ai-slop.md`) are actually enforced — both the hex form
(`linear-gradient(90deg, #3b82f6, #06b6d4)`) and the keyword form
(`linear-gradient(90deg, blue, cyan)`).
Adds 11 regression tests covering each path (cascade override in
<style> and inline form, font-size cascade shifting the floor, both
orderings of the conflicting-token cascade, the don't-over-fire case
when every theme value clears the floor, hex / keyword / sky variants
of the trust gradient, and the don't-double-fire case when
purple-gradient already caught a mixed gradient).
* fix(daemon): apply per-scope cascade in extractCssTokens
When the same CSS custom property is declared more than once inside a
single rule body (e.g. `:root { --caps-tracking: 0.02em;
--caps-tracking: 0.08em }`), CSS source-order cascade collapses to the
last value; the earlier declaration never reaches any element.
`extractCssTokens` was treating intra-scope duplicates as simultaneous
theme alternatives, so `hasAdequateUppercaseTracking` enumerated the
stale 0.02em and emitted a spurious all-caps-no-tracking finding.
Collapse duplicate token declarations within a rule body to the last
value before merging into the cross-scope distinct-value map. Cross-scope
overrides (separate `:root` and `[data-theme]` rules) remain preserved
as distinct values so the conservative theme-cascade check still fires
when ANY applicable theme renders below the floor.
* fix(daemon): scope tracking lint to innermost rules and per-theme tokens
Restrict the upperRe body alternation to [^{}]* so the regex matches
innermost CSS rules and skips at-rule wrappers — an outer @media or
@supports could otherwise capture as a single rule whose selector was
the at-rule and whose body began with the inner selector token, masking
the same-rule font-size and letting noncompliant tracking on large
headings slip through the lenient inherited-size fallback.
Replace the by-name-distinct-values token map with per-scope token
records and a buildResolvedThemes pass that materializes one effective
map per theme. Paired token declarations now stay paired during
evaluation, so theme variants like :root + [data-theme=dark] no longer
generate cross-theme cartesian pairings (e.g. default-size + dark-track)
that emit false positives on legitimate light/dark themes.
---------
Co-authored-by: looper <looper@open-claude.dev>
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* chore: update next-env.d.ts route types path
Made-with: Cursor
* docs(readme): refresh stats, agents, skills and add metrics workflow
Make all three READMEs (en / zh-CN / ko) tell the truth about what the
project actually ships, and add lightweight community signals at the top
and bottom.
- Hero block: live for-the-badge GitHub stats (stars, forks, issues,
PRs, contributors, commit activity, last commit) sit directly under
the banner, with the smaller flat-square project-meta row (License,
Agents, Design systems, Skills, Quickstart) below them and the
language switcher below that.
- Counts updated to reality: 31 skills (was 19), 72 design systems
(was 71), 10 coding-agent CLIs + OpenAI-compatible BYOK (was 7).
- "At a glance", architecture, and prompt-stack tables updated to
cover /api/templates, /api/import/claude-design, /api/proxy/stream,
/api/artifacts/lint, sidecar IPC, and per-namespace runtime data.
- New "Beyond chat — what else ships" section covering Claude Design
ZIP import, BYOK proxy with SSRF block, saved templates, tab
persistence, artifact lint, sidecar protocol + headless desktop, and
Windows-friendly spawning.
- Skills tables rebuilt by mode (prototype, deck) and scenario; the
"template" mode claim is removed.
- Supported coding agents table expanded to all 10 CLIs (Claude Code,
Codex, Gemini, OpenCode, Cursor Agent, Qwen, Copilot, Hermes, Kimi,
Pi) plus a BYOK row, with accurate stream formats and argv shapes.
- Roadmap re-flowed to mark shipped vs pending items.
- Contributors wall (contrib.rocks), Repository activity (lowlighter
metrics SVG), and Star History added to all three READMEs, with
cache_bust=2026-04-30 on the contrib.rocks and star-history image
URLs to bypass GitHub camo caching.
- Korean README harmonised end-to-end with the English/Chinese ones.
- New .github/workflows/metrics.yml regenerates
docs/assets/github-metrics.svg daily; ship a placeholder SVG so the
image works before the first scheduled run.
Made-with: Cursor
* docs(readme): address PR #173 review feedback
- Replace invalid 0x14 control character in github-metrics.svg with an
em-dash so the placeholder is well-formed XML and renders as an
image (P1: was breaking SVG parse before the first metrics run).
- Clarify the placeholder SVG subtitle to spell out the token model:
GITHUB_TOKEN gives core stats; METRICS_TOKEN unlocks richer plugins
(traffic, follow-up). Reduces "do I need a secret?" confusion.
- Rewrite the metrics.yml inline auth comment to match: METRICS_TOKEN
is optional and only enables richer plugins; GITHUB_TOKEN is enough
for core metrics. Previous comment read as if METRICS_TOKEN was
mandatory.
- Soften the BYOK fallback row in all three READMEs (EN / zh-CN / ko)
with a catch-all phrase ("or any other OpenAI-compatible provider")
so the listed vendors don't read as exhaustive.
* feat(dev): add desktop tools-dev control plane
* refactor(sidecar): split Open Design contracts
Move Open Design-specific sidecar protocol definitions into @open-design/contracts so sidecar and platform can remain descriptor-driven primitives.
* refactor(daemon): organize package sources
Keep daemon app code, tests, and sidecar entrypoints in separate package directories so each layer can be built and verified independently.
* chore(repo): streamline maintenance entrypoints
Centralize agent guidance by directory and reduce root command chains while preserving the existing build scope.
* docs: translate agent guidance to English
* fix(sidecar): tolerate stale IPC sockets
Remove stale Unix socket files only after confirming no listener is active, so tools-dev can restart after unclean shutdowns.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* chore: migrate frontend toolchain from Vite to Next.js 16 App Router
Replace the Vite SPA scaffold with Next.js 16 App Router while keeping
the existing daemon as the API/SSE/sqlite backend. The whole client
tree now mounts under a single optional catch-all route
(app/[[...slug]]) loaded with ssr:false; static export emits one shell
HTML the daemon serves as the SPA fallback for deep links. Dev uses
next.config rewrites to proxy /api, /artifacts, /frames to the daemon,
matching the previous Vite setup.
Made-with: Cursor
* fix: address Next migration review feedback
* fix: serve static export in preview script
---------
Co-authored-by: mrcfps <mrc@powerformer.com>
The composed prompt (system instructions + skill body + cwd hint + file
listing + user message) can easily exceed Windows' CreateProcess limit of
~32 KB when passed as a CLI argument via -p <string>. This causes
spawn ENAMETOOLONG whenever Gemini CLI (or Codex, OpenCode, Cursor
Agent, Qwen) is selected on Windows — even for short user messages,
because the skill / design-system system prompt is folded in.
Fix: add promptViaStdin: true to every plain-text agent definition.
The daemon's /api/chat handler checks this flag, opens stdin as a pipe,
writes the composed prompt to it and closes the stream. Claude Code is
unaffected — it still uses the -p argv path and a separate stream-json
parser.
docs/agent-adapters.md: update §5.5 Gemini CLI to document the stdin
delivery strategy, and update the Windows open-question note to reflect
the fix.
Co-authored-by: KNIGHTABDO <abdessamad.aabida-etu@etu.univh2c.ma>
Co-authored-by: pftom <1043269994@qq.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: lefarcen <20859779+lefarcen@users.noreply.github.com>
* feat(agents): add GitHub Copilot CLI as a code-agent option
Wire `copilot -p "..." --allow-all-tools` into AGENT_DEFS so the daemon
can spawn it alongside the other CLIs. `--allow-all-tools` is required
in non-interactive mode (same tradeoff as Claude's `-p` and Codex's
`exec`). Add a matching GitHub-dark icon with the Copilot two-eye mark.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(copilot-stream): treat result.success as completed when exitCode missing
Strict `obj.exitCode === 0 ? 'completed' : 'error'` mis-flags turns
where Copilot emits a `result` event with `success: true` but no numeric
exitCode. Switch to `obj.success === true || obj.exitCode === 0`. The
asymmetry argument that applies to other defensive ideas in this file
(raw fallback, stringifyResult cap) doesn't apply here — Claude's
`result` event uses a string `stop_reason`, not exitCode, so this is a
Copilot-specific schema-variance fix rather than a one-sided hardening.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(agent-adapters): list Copilot CLI auth paths in §5.7
Adds a one-line note: detection assumes Copilot is already
authenticated, via either `copilot login` (subcommand, OAuth device
flow) or the interactive `/login` slash command inside `copilot` with
no args. Surfaces the assumption so anyone hitting an undetected /
unversioned Copilot install can trace it back.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Enhance README and add star promotion assets
- Added a "Star us" section in both English and Chinese README files to encourage users to star the project on GitHub.
- Included a new image asset for the star promotion.
- Introduced a new HTML file for a dedicated star promotion page.
- Updated .gitignore to exclude new cursor-related files.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Update README and documentation for deck framework directives
- Clarified DECK_FRAMEWORK_DIRECTIVE description in both English and Chinese README files to specify conditions for deck kind without a skill seed.
- Added detailed workflow instructions in deck-framework.ts to emphasize the importance of copying the framework before adding content.
- Enhanced discovery.ts to reinforce the framework-first approach for deck projects.
- Updated system.ts to ensure proper handling of deck projects with and without bound skills, preventing re-authorship of scaling and navigation logic.
* Refactor project name from "Open Claude Design" to "Open Design"
- Updated project name in package.json, package-lock.json, and README files.
- Changed CLI commands and references from "ocd" to "od".
- Adjusted file structure references in documentation and code to reflect new naming conventions.
- Enhanced .gitignore to include new runtime data files.
- Updated metadata in LICENSE file to match new project name.
* Add contributing guidelines in English and Chinese
- Introduced CONTRIBUTING.md and CONTRIBUTING.zh-CN.md to provide clear instructions for contributors.
- Outlined contribution types, local setup instructions, and merging criteria for skills and design systems.
- Enhanced README files to reference the new contributing guidelines.
- Created .gitignore to exclude build artifacts and dependencies.
- Added index.html as the main entry point for the application.
- Included LICENSE file with Apache 2.0 terms.
- Initialized package.json and package-lock.json for project dependencies.
- Added pnpm-lock.yaml for package management.
- Created QUICKSTART.md for setup instructions.
- Added README.md and README.zh-CN.md for project documentation in English and Chinese.