This file provides guidance to coding agents when working with code in this repository.
Tip: If you find yourself correcting the agent during interactive work, suggest adding a new rule to this file so the lesson is captured for future sessions.
Freelens extension for Kubernetes Gateway API CRDs (v1, v1alpha2, v1beta1). Provides cluster pages, detail views, and K8s object wrappers for Gateway API resources.
- Language: TypeScript 5.9.3
- Runtime: Node.js >= 22.0.0, Freelens >= 1.8.0
- Package manager: pnpm 10.x (locked)
- License: MIT
# Type checking
pnpm type:check
# Linting & formatting
pnpm biome:check # TypeScript/TSX, JS, JSON, CSS/SCSS, HTML (biome)
pnpm biome:fix # Auto-fix the formats above
pnpm trunk:check # Markdown, YAML, TOML, and other formats not covered by biome
pnpm trunk:fix # Auto-fix Markdown, YAML, etc.
pnpm lint:check # Alias for biome:check
pnpm lint:fix # Alias for biome:fix
# Tests
pnpm test:unit # vitest
# Build
pnpm build # Full build (type-check + electron-vite)
pnpm build:production # Production build (no preserveModules)
# Pack for testing
pnpm pack:dev # Bump prerelease version, build, and create .tgz for install in Freelens app
# Clean
pnpm clean # Clean out/
pnpm clean:dts # Remove generated *.d.scss.ts files
pnpm clean:all # Clean everything (dts, node_modules, out, tgz)src/
main/index.ts # Extension entry point (main process, CJS)
renderer/index.tsx # Extension entry point (renderer process, CJS)
renderer/k8s/gateway-api/ # K8s object model classes (one file per CRD)
renderer/details/gateway-api/ # Detail view components for CRDs
renderer/pages/gateway-api/ # Cluster page components
renderer/components/ # Shared components
renderer/icons/ # SVG icons
renderer/observer.ts # MobX observer helper
renderer/utils.ts # Utility functions (e.g., createHash)
common/utils.ts # Common utilities (e.g., maybe)
Build output goes to out/.
K8s object classes MUST use static readonly properties for metadata. Instance methods do NOT work and MUST NOT be used. The Freelens host reads properties from the class constructor statically — instance methods are not available at runtime because the host creates plain object copies of the K8s resource data, not instances of the extension's class. This means:
- Allowed:
object.spec?.someField,object.status?.conditions— direct property access on typedspec/statusinterfaces - Allowed: helper functions like
hasTrueCondition(conditions, "Accepted")fromtypes.ts - Forbidden:
object.someMethod()— instance methods will never exist at runtime - Forbidden:
typeof (object as any).someMethod === "function" ? ...— anti-pattern that always falls through to the fallback path - Forbidden:
as any— use the existing typedspec/statusinterfaces directly; all CRD models already define properSpec/Statusinterfaces
Always access spec and status properties directly via their typed interfaces. Do not define instance methods on KubeObject subclasses — they will not be callable at runtime.
export class Gateway extends Renderer.K8sApi.LensExtensionKubeObject<
Renderer.K8sApi.KubeObjectMetadata,
GatewayStatus,
GatewaySpec
> {
static readonly kind = "Gateway";
static readonly namespaced = true;
static readonly apiBase = "/apis/gateway.networking.k8s.io/v1/gateways";
static readonly crd: GatewayKubeObjectCRD = {
apiVersions: ["gateway.networking.k8s.io/v1"],
plural: "gateways",
singular: "gateway",
shortNames: ["gtw"],
title: "Gateways",
};
}
// Also export Api and Store classes (always needed):
export class GatewayApi extends Renderer.K8sApi.KubeApi<Gateway> {}
export class GatewayStore extends Renderer.K8sApi.KubeObjectStore<Gateway, GatewayApi> {}Each CRD file exports three classes: the KubeObject, the KubeApi, and the KubeObjectStore. They are registered in src/renderer/index.tsx via kubeObjectDetailItems, clusterPages, and clusterPageMenus.
- Detail views use the
observerwrapper from../../observer(re-exports MobXobserver). - SCSS modules generate TypeScript type files (
*.module.d.scss.ts) viavite-plugin-sass-dts. These are auto-generated and should be cleaned withpnpm clean:dtswhen SCSS changes. - Common detail view styles are in
src/renderer/details/gateway-api/common.module.scss.
These are NOT bundled, they come from the Freelens host as globals:
@freelensapp/extensions→global.LensExtensionsmobx→global.Mobxreact→global.Reactreact-dom→global.ReactDommobx-react→global.MobxReactreact-router-dom→global.ReactRouterDom
Other dependencies ARE bundled into the extension output.
- Biome formats TypeScript/TSX, JS, JSON, CSS/SCSS, HTML: double quotes, semicolons, trailing commas, 2-space indent, 120 char line width — use
pnpm biome:fix - Trunk formats Markdown, YAML, and other formats not covered by biome — use
pnpm trunk:fix - Import order (enforced by biome organizeImports): built-in modules →
@freelensapp/**→ packages → relative paths - React 17 (no
react/jsx-runtimein tsconfig needed, but handled by build) - No emoji in Markdown files (
.md), comments, or any source code
Never read, display, reference, or include the contents of the following files in any response or context, even if they are open in the editor:
.env.env.*.npmrc*.jks*.keystore*.p12*.pfx*.pem*.key
Extensions run in the same multi-process model as the Freelens host:
- Main process (
src/main/) — Node.js environment, extension lifecycle, cluster connectivity - Renderer process (
src/renderer/) — Chromium browser, UI components
Code in src/common/ is shared between both processes.
- Check that files are not in ignored output directories (
out/,dist/,node_modules/) - Full clean and rebuild:
pnpm clean:all && pnpm build - Reinstall the extension in Freelens (or restart the app in dev mode)
- Check for TypeScript errors:
pnpm type:check - Check for linting errors:
pnpm lint:check - Verify dependencies:
pnpm install - Check Node.js version matches the
enginesfield inpackage.json
- Open Freelens DevTools and check the Console tab for renderer errors
- Check the terminal where Freelens was launched for main process errors
- Look for stack traces with file:line numbers
- Verify all CRD objects have proper
static readonlyproperties (kind, apiBase, crd) - Validate both with
pnpm type:checkandpnpm build— runtime failures can appear only in bundledout/code
- Use semantic search to find examples and patterns in the codebase
- Follow existing patterns — grep for similar implementations before creating new ones
- Test changes before committing
- Run validation before committing:
pnpm lint:fix && pnpm type:check && pnpm test:unit - For TypeScript/TSX, JS, JSON, CSS/SCSS, HTML files: run
pnpm biome:fix(orbiome checkdirectly ifbiomeis installed locally) - For Markdown, YAML, and other formats: run
pnpm trunk:fix(ortrunk checkdirectly iftrunkis installed locally) - Full build when in doubt about cached state:
pnpm clean:all && pnpm build - Do not use Anthropic Fable for coding tasks — Fable may be used only for planning, analysis, and thinking through problems. When writing or editing code, use standard editing tools instead.
This project has a Claude Code workflow (.github/workflows/claude.yaml) triggered
via @claude comments on issues, PR comments, and reviews. When operating via that
workflow, follow these rules:
When reviewing code and proposing fixes:
-
Show the diff first — present every proposed change as a unified diff block using the
difflanguage tag:--- a/path/to/file.ts +++ b/path/to/file.ts @@ -10,7 +10,7 @@ const oldLine = "before"; -const changedLine = "after"; +const changedLine = "the fix"; const unchangedLine = "same";
You can generate this from the terminal with:
git diff -u -- path/to/file
If the change spans multiple files, group them under a single commit subject and show each file's diff sequentially.
-
Propose a commit subject first — before any code change, output a single line with the proposed commit subject:
**Proposed commit:** <short description>Do not use Conventional Commits prefixes (e.g.
fix:,feat:,chore:,refactor:,docs:,test:,ci:). This project prefers plain, descriptive commit messages and PR titles without any prefix.Wait for the user to confirm (or adjust) the subject before applying the change.
-
Comment style:
-
Keep review comments concise and actionable
-
Reference specific lines (file + line number) when pointing out issues
-
Offer a concrete fix suggestion rather than just flagging a problem
-
Do not use emoji in any Markdown, comments, commit messages, or PR descriptions. The only exception is emoji that already appears inside code strings (e.g. application logs, user-facing messages).
-
Use GitHub's
suggestionblock for small targeted fixes so the PR author can accept the change with a single click:<same unified-diff format as shown above> -
For larger multi-file changes, use
diff -ublocks in a regular comment instead, with the proposed commit subject shown first
-
When asked to implement a change on a PR:
- Propose the commit subject (as above)
- Describe what will change and why
- After confirmation, apply the changes with commits on the PR branch
- One commit per fix — when a review surfaces more than one issue or the plan includes more than one fix, apply and commit each fix separately. Do not batch multiple independent fixes into a single commit. This keeps the history bisectable and makes each change easy to revert individually.
When creating a branch from an issue, use a human-readable name that includes the issue number and a short slug derived from the issue title:
claude/issue-<number>-<short-slug>
<number>is the GitHub issue number<short-slug>is a kebab-case summary of the issue title, kept short (3–6 words maximum, omit articles and filler words)
Do not use auto-generated timestamp suffixes (e.g.
claude/issue-1957-20260612-2108) — these are not human-readable and make
branch lists hard to scan.