luno-for-cc
CLAUDE.md0 starsChanged 4 months agoMITView on GitHub
A Claude Code chat panel for Cursor and VS Code, running on the user's own Claude subscription. Senior engineer on a shipped, published extension — not a prototype. The toolchain is bun. npm is not used: the lockfiles are bun.lock / webview/bun.lock and npm lockfiles are gitignored on purpose — if one reappears, something ran npm install and the two will drift. bun run test is the number to beat, and it is the only place it is written down.…
- Reads credentials
In this repository14 other files it gives its agents.
- .claude/skills/vercel-composition-patterns/AGENTS.mdAGENTS.md
- .claude/skills/vercel-react-best-practices/AGENTS.mdAGENTS.md
- .claude/skills/vercel-react-view-transitions/AGENTS.mdAGENTS.md
- analyzeSkill
- auditSkill
- browserSkill
- checkSkill
- gitpushSkill
- shipSkill
- vercel-composition-patternsSkill
- vercel-react-best-practicesSkill
- vercel-react-view-transitionsSkill
- web-design-guidelinesSkill
- writing-guidelinesSkill
The file
We split this CLAUDE.md into the rules it gives an agent
11 rules. About 0 are common, 4 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 80
Never report work as complete without all four:
- Distinctive: line 88
Every claim tied to evidence actually seen: a command's output, a measured value, a screenshot
- Distinctive: line 91
If something is unverified, name which part and why.
- Very specific: line 98
NEVER: write a raw duration in framer — spread a preset from webview/src/design/motion.ts
- Very specific: line 100
NEVER: add an icon outside webview/src/design/icons.tsx
- Very specific: line 101
NEVER: use the native title attribute — use design/primitives/Tooltip.
- Very specific: line 104
NEVER: commit secrets, .env, or anything under ghost.one/
- Very specific: line 106
NEVER: add a comment that explains _what_ code does → .claude/rules/comments.md
- Distinctive: line 107
ALWAYS: run the gates before claiming done
- Very specific: line 108
ALWAYS: treat theme.css, design/motion.ts and themes/_base.scss as shared resources — never edit them from parallel agents
- Very specific: line 132
Only "instant" overrides CSS from script.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
LUNO for CC
A Claude Code chat panel for Cursor and VS Code, running on the user's own Claude subscription. Senior engineer on a shipped, published extension — not a prototype.
Language
- Respond in Russian unless explicitly asked otherwise.
- Code, comments, commit messages, docs — English.
Commands
The toolchain is bun. npm is not used: the lockfiles are bun.lock /
webview/bun.lock and npm lockfiles are gitignored on purpose — if one
reappears, something ran npm install and the two will drift.
| Task | Command |
| All gates | bun run lint (tsc ×2 → eslint → stylelint) |
| Types only | bun run lint:types |
| Tests | bun run test — expect 1647 passed, 6 skipped |
| Build | bun run build (esbuild → dist/, vite → webview/dist/) |
| Package | bun run package → luno-for-cc-<ver>.vsix, ~1.28 MB |
| Format | bun run format · check with format:check |
| Autofix everything | bun run fix |
| Browser harness | bun run harness — the webview on localhost, see /browser |
| Webview dev server | bun run dev:webview → http://localhost:5173 |
| Install both halves | bun run install:all · watch the host with bun run watch |
| Vendored skills | bun run skills:verify — fails if one was edited in place |
bun run test is the number to beat, and it is the only place it is written
down. The gate is "this many or better"; anything less is a regression to
explain, not a floor to lower.
It runs two projects through vitest.workspace.ts and prints one summary:
the host half in test/ under node, and a component half in webview/test/
under jsdom. The second is rooted in webview/ on purpose — React lives only
in webview/node_modules, and a component rendered against a second copy of it
fails on the first useState. Rendering uses react-dom/client and React 19's
own act rather than a testing-library dependency; webview/test/render.tsx is
the whole of it.
Architecture — three layers
Webview (webview/src/) React 19 + SCSS modules + framer-motion
│ reads design tokens only
│ postMessage — the stable contract between the two halves
Extension host (src/) claude CLI spawn · permissions · MCP · skills
│ spawn(binary, …) over stream-json
claude CLI the user's own install; auth, models, toolssrc/core/*imports zero VS Code APIs and is unit-tested in isolation.- The permission policy and CLI bridge live in
src/providers/. - The binary is found, never assumed: everything goes through
resolveClaudeBinary()insrc/providers/factory.ts. Reaching forbundledClaudeBinary()directly breaks a build that ships no CLI, which is exactly what this is.
Tool triggers (MANDATORY)
Fire these without being asked. Each row is a trip-wire, not a suggestion.
| Situation | Action |
| Changed anything the webview renders | Verify in the browser harness (/browser) before reporting done. "Should work" is not a result |
| Need a version-specific API — React 19, framer-motion, VS Code, Vite | context7 (resolve-library-id → query-docs). Never from memory: React 19 and framer's current API both moved recently |
| Unfamiliar library or third-party integration | Exa first, code second |
| About to make an architectural decision | Exa for how others solved it, then decide. Say what you found |
| Same error survived 2 fix attempts | Stop guessing — systematic-debugging or Exa. A third blind attempt is always waste |
| Touched permissions, token storage, or process spawning | Re-read decidePermission and the destructive/network gate. This extension holds a subscription credential and spawns processes |
| Measuring UI behaviour — duration, geometry, contrast, overshoot | Sample it in the harness. Numbers, not adjectives |
| Context filling up mid-task | Say so and offer a handoff before quality drops, not after |
| Second opinion on a risky change | cc-codex-triage |
Definition of done
Never report work as complete without all four:
bun run lintat exit 0, with no new warnings — that is tsc over both projects, eslint, stylelint. It is not a clean screen: 35 eslint warnings are real, held atwarnon purpose, and explained ineslint.config.mjs. The gate is the exit code plus the count not risingbun run testat1647 passed, 6 skippedor better- Behaviour verified where it runs — the harness for UI, tests for host logic
- Every claim tied to evidence actually seen: a command's output, a measured value, a screenshot
If something is unverified, name which part and why. Partial honesty beats a confident false "готово".
Critical rules
- NEVER hard-code a colour, radius, shadow or duration in a component —
read a token. See
docs/TOKENS.md - NEVER write a raw duration in framer — spread a preset from
webview/src/design/motion.ts - NEVER add an icon outside
webview/src/design/icons.tsx - NEVER use the native
titleattribute — usedesign/primitives/Tooltip. The one deliberate exception is the per-line title inFileDiffModal, which carries a comment saying so - NEVER commit secrets,
.env, or anything underghost.one/ - NEVER let
luno.permissionMode: autoreach a committed settings file - NEVER add a comment that explains what code does →
.claude/rules/comments.md - ALWAYS run the gates before claiming done
- ALWAYS treat
theme.css,design/motion.tsandthemes/_base.scssas shared resources — never edit them from parallel agents
Traps this codebase has already paid for
Each of these was invisible to the compiler and cost a debugging round.
- framer writes
opacityinline, and no class rule can outrank it. Converting an element tomotion.*silently kills any.someClass { opacity: … }on it. Usefilter: opacity()for state-based dimming on animated elements. - CSS Modules scope
@keyframesnames.animation: globalNameinside a module is a dead reference, and:global(name)inside ananimationshorthand compiles to a silently dead one. Share via the mixins inthemes/_motion.scss— in the source, not the output. - CSS cannot animate an exit. Anything that must animate both ways needs
framer +
AnimatePresence; a CSSanimationplays on mount and has nothing left to play when the node is simply gone. - Exits run on
--ease-soft, never--ease-out. The latter is an expo-out: ~90% of the change lands in the first third, so a dismissed panel is invisible halfway through its own duration. scrollTo({behavior:"auto"})does not mean "jump". It means "use the element'sscroll-behavior", and.loginChatScreen.module.scsssets that tosmooth— soautosilently keeps the animation you were trying to skip. Only"instant"overrides CSS from script. Cost: two full rebuild-and-measure cycles reading an unchanged 1 523 ms.- A dangling
s.someNamerenders unstyled and the build stays green. A typo in a CSS-module class name is not a compile error. prefers-reduced-motion: framer honours it only on theanimate-prop path. A value driven throughuseSpringbypasses it and needs an explicituseReducedMotion()check.- A disabled button dispatches no mouse events, and they do not reach its ancestors — hit-testing stops there. That is why Tooltip has a gate span.
window.openand<a target="_blank">do not reach the browser from a webview. Every outbound link goessend({ type: "openExternal", url })and the host callsvscode.env.openExternal. The failure is silent: the click lands, nothing happens.- Every field on the protocol is optional, so a half-plumbed one typechecks.
A value crosses five hops — card →
ChatScreen→App→ host → provider — and adding it to four of them compiles clean and drops the value on the floor. When adding a field, grep the name and count the hops. - A native radio or checkbox cannot be made to match the design system.
accent-colortints the control but the platform still draws a square focus ring around a round radio. Hide the input (visually, neverdisplay: none— it carries the semantics and the tab order) and draw a sibling.
Where the rest lives
| Topic | File |
| Token contract every theme must satisfy | docs/TOKENS.md |
| What a file becomes when it is attached | docs/ATTACHMENTS.md |
| Comment policy in full | .claude/rules/comments.md |
| Naming and code style | .claude/rules/code-style.md |
| What lives where, sizes, seams | .claude/project-map.md |
| Why a decision went that way | docs/PLAN.md (local only) |
| Design rationale | docs/DESIGN.md (local only) |
| Audits — measured findings, with proof | .claude/audits/ |
docs/PLAN.md and docs/DESIGN.md are gitignored — working notes, not part of
the published repo. Read them for context; do not assume a reader of the
repository has them.
Development environment
- OS: Windows. Shell: PowerShell primary, Git Bash available.
- Paths: forward slashes work in both. Line endings: CRLF in the working tree,
LF in git (
core.autocrlf). ghost.one/is a full copy of an unrelated project kept in-tree as a reference. It is gitignored and.vscodeignored. Never commit it, never letvscewalk it.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

