Code Quality
redis/RedisInsight
Code-quality standards for RedisInsight: TypeScript strictness, naming conventions (camelCase, PascalCase, UPPERSNAKECASE), linting rules, no any without reason, no !important in styles, and…
A playbook for fixing ESLint and TypeScript errors in the OpenObserve web frontend, with rule-by-rule guidance and typing conventions.
$ npx skills add openobserve/openobserve --skill eslint-error-handling -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install openobserve/openobserve eslint-error-handling --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/openobserve/openobserve.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/eslint-error-handling .claude/skills/eslint-error-handling && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "eslint-error-handling" agent skill from https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handling into .claude/skills/eslint-error-handling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "eslint-error-handling", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handlingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add openobserve/openobserve --skill eslint-error-handling -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install openobserve/openobserve eslint-error-handling --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openobserve/openobserve.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/eslint-error-handling .agents/skills/eslint-error-handling && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "eslint-error-handling" agent skill from https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handling into .agents/skills/eslint-error-handling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "eslint-error-handling", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add openobserve/openobserve --skill eslint-error-handling -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install openobserve/openobserve eslint-error-handling --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openobserve/openobserve.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/eslint-error-handling .cursor/skills/eslint-error-handling && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "eslint-error-handling" agent skill from https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handling into .cursor/skills/eslint-error-handling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "eslint-error-handling", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/openobserve/openobserve.git --path .claude/skills/eslint-error-handling--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add openobserve/openobserve --skill eslint-error-handling -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install openobserve/openobserve eslint-error-handling --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openobserve/openobserve.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/eslint-error-handling .gemini/skills/eslint-error-handling && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "eslint-error-handling" agent skill from https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handling into .gemini/skills/eslint-error-handling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "eslint-error-handling", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install openobserve/openobserve eslint-error-handlingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add openobserve/openobserve --skill eslint-error-handling -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/openobserve/openobserve.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/eslint-error-handling .github/skills/eslint-error-handling && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "eslint-error-handling" agent skill from https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handling into .github/skills/eslint-error-handling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "eslint-error-handling", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add openobserve/openobserve --skill eslint-error-handling -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install openobserve/openobserve eslint-error-handling --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/openobserve/openobserve.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/eslint-error-handling .opencode/skills/eslint-error-handling && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "eslint-error-handling" agent skill from https://github.com/openobserve/openobserve/tree/main/.claude/skills/eslint-error-handling into .opencode/skills/eslint-error-handling/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "eslint-error-handling", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
eslint-error-handlingA playbook for fixing ESLint and TypeScript errors in the OpenObserve web frontend, with rule-by-rule guidance and typing conventions.
The stack is Vue 3, TypeScript, an ESLint flat config and Prettier, and the gates are npm run lint:ci and npm run type-check:app (vue-tsc), both of which must stay at 0 errors. Six golden rules apply to every fix: never use any, prefer types at the declaration site over as casts, keep type-only fixes from changing runtime behavior, use strict equality only, keep comments to one or two lines, and fix rather than suppress, with eslint-disable or @ts-expect-error reserved for genuine false positives and given a reason.
A typing section says where a type should live by scope: inline for a one-off shape, a local Props interface or a *.types.ts sibling for component props, a feature-level types file once two or more files share a shape, and src/ts/interfaces for cross-cutting domain models and for types that mirror backend API responses, derived from the Rust struct. Extending an existing interface is preferred to inventing a parallel one. The description adds rule-by-rule before and after examples and conventions for unused code, Vue-specific rules, casts and formatting.
10 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 0e9ab49. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
npmnpxnodeFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use npm and npx, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
OpenObserve ESLint and TypeScript Fixes loads about 5.1k tokens when it runs. Until then it costs about 99 tokens; SKILL.md has 1,373 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from openobserve/openobserve at commit 0e9ab49, republished under its AGPL-3.0 licence (© openobserve). 1,373 words, ~5,083 tokens.
.claude/skills/eslint-error-handling/SKILL.md (or your agent's skills folder).web/Stack: Vue 3 + TypeScript + ESLint flat config (eslint.config.js) + Prettier.
Gates: npm run lint:ci (ESLint; -- --quiet for errors-only) and npm run type-check:app
(vue-tsc against tsconfig.app.json) — both must stay at 0 errors.
Rollout plan & rationale: see the lint & type-check strictness rollout design doc.
any. No : any, as any, any[], <any>. Enrich an interface,
narrow with unknown + a type guard, or derive the type. If a value is truly
dynamic, use unknown and narrow at use.as casts. Type the ref/variable/prop/
function-return at its declaration so the cast disappears. Casts are a last resort.typeof/String()/branches to
satisfy the compiler is a behavior change — don't).===/!==, never ==/!=).// eslint-disable / @ts-expect-error only for genuine
false positives (e.g. an intentional control-char regex), with a reason after --.Decide where a type lives by scope, not habit:
| Situation | Where the type goes |
|---|---|
| Used in one function/block, small shape | Inline at the declaration: const x: { id: string } = … |
| A component's props/emits/slots | Inline in defineProps<…>() / a local interface Props in the same SFC; for a design-system component use its *.types.ts sibling |
| Shared by 2+ files in the same feature | A types.ts (or feature.types.ts) next to the feature |
| Cross-cutting domain model (Stream, Alert, Dashboard, SearchObject…) | src/ts/interfaces/* (barrel @/ts/interfaces) |
| Mirrors a backend API response | Derive from the Rust struct and put it in src/ts/interfaces/*; name it after the BE type |
Rules of thumb:
StreamField conflict happened — multiple incompatible shapes).field: [] as string[] (this is declaration-site typing, not a value cast).// ❌ inline shape duplicated across files → they drift
function a(u: { id: string; name: string }) {}
function b(u: { id: string; name: string }) {}
// ✅ shared interface in src/ts/interfaces/user.ts
export interface User { id: string; name: string }anyWhen you hit const res: any = await someService.call(...) or
(res as any).field, do not cast — trace the response to the Rust struct and
mirror it as an interface. The backend is the source of truth for API shapes.
Steps:
// web/src/services/stream.ts
nameList: (org) => http().get(`/api/${org}/streams`) // GET /api/{org}/streamssrc/, Rust):grep -rn "streams" src/handler/http/request/stream/mod.rs # find the handler
grep -rn "pub struct ListStream" src/ # find the response struct// src/common/meta/stream.rs
pub struct ListStream { pub list: Vec<Stream>, pub total: usize }
pub struct Stream { pub name: String, pub stream_type: StreamType,
pub schema: Vec<StreamField>, /* … */ }src/ts/interfaces/* (reuse one if it already exists —
grep -rn "interface Stream" web/src/ts/interfaces first). Map Rust → TS:
String→string, usize/i64/f64→number, bool→boolean, Vec<T>→T[],
Option<T>→T | undefined (or field?:), HashMap<K,V>→Record<K, V>, enum→union.// web/src/ts/interfaces/stream.ts
export interface ListStream { list: Stream[]; total: number }
export interface Stream {
name: string;
stream_type: "logs" | "metrics" | "traces";
schema: StreamField[];
}unknown/AxiosResponse, so one
boundary annotation, then no more casts):// ❌ const res: any = await StreamService.nameList(org);
// if (res.data.list.length) … // untyped, cast-ridden
// ✅
const res = await StreamService.nameList(org);
const data = res.data as ListStream; // one boundary annotation
if (data.list.length > 0) { data.list.map((s) => s.name); } // fully typedField names must match the wire format exactly (usually snake_case from serde) —
don't rename to camelCase in the interface, or access silently returns undefined.
Only include the fields the FE actually reads; add more as needed.
Only 18 / 57 service files declare any response type, so most calls come back any.
Find every untyped awaited API result (excludes dynamic await import() — those are module
imports, not API calls):
grep -rnE ':\s*any\s*=\s*await' web/src --include='*.ts' --include='*.vue' \
| grep -v '.spec.' | grep -v 'await import('At time of writing that's ~64 sites, and they cluster hard — fix the hot functions once (in their composable/service) and dozens of call sites become typed for free:
| Function | Call sites | Endpoint (trace this to the Rust struct) |
|---|---|---|
getStreams (useStreams) | 27 | GET /api/{org}/streams → ListStream { list: Vec<Stream>, total } |
getStream (useStreams) | 8 | GET /api/{org}/streams/{name}/schema → Stream |
getDashboard | 2 | GET /api/{org}/dashboards/{id} |
commonService.list_Folders / dashboardService.list_Folders | 2 | GET /api/{org}/folders |
| long tail (users, roles, pipelines, reports, org settings, …) | 1 each | see each service file for its url |
Workflow: give useStreams.getStreams / getStream a real return type (via §1.1), delete the
: any at the 35 call sites, then work down the long tail. Type at the source (the
service/composable return), not at each call site.
@typescript-eslint/no-unused-vars (the big bucket)Single source of truth for unused code (core no-unused-vars and TS
noUnusedLocals stay off).
DELETE is the default fix. _-prefix is a narrow last resort for FUNCTION
PARAMETERS ONLY — never _-prefix an import, a local, or a function/const, because
that just hides dead code instead of removing it.
// ❌ unused import → remove it (or just the one specifier)
import { computed, ref, watch } from "vue"; // watch never used
import { computed, ref } from "vue"; // ✅
// ❌ unused local
const result = compute(); // never read
compute(); // ✅ side-effect call → keep call, drop binding
// ✅ no side effects → delete the line entirely
// ❌ unused standalone function/const — DELETE it (dead code)
const _getInitials = () => …; // ❌ `_`-prefix does NOT make it "used"
// ✅ verify no caller (grep the name), then remove the whole declaration.
// PARAMETERS:
// ❌ unused TRAILING param → REMOVE it, don't `_`-prefix
arr.map((item, index) => item.id); // index unused
arr.map((item) => item.id); // ✅ drop it
watch(src, (_newVal, _oldVal) => { … }); // ❌ both unused & trailing
watch(src, () => { … }); // ✅ drop them
// ❌ unused DESTRUCTURED element (e.g. setup's ctx) → remove the key, don't rename
setup(props, { emit: _emit, expose }) {} // ❌ emit unused
setup(props, { expose }) {} // ✅ drop `emit`
// ✅ `_`-prefix ONLY when a LATER param is used (can't drop) or a contract fixes arity:
arr.map((_item, index) => index); // _item kept because index is usedIn .vue, vue-tsc/eslint-vue already account for <template> usage, so a flagged
script var/function is genuinely dead — safe to remove.
// no-unsafe-optional-chaining — runtime throw
const n = obj?.list.length; // ❌ obj?.list can be undefined, .length throws
const n = obj?.list?.length ?? 0; // ✅
// no-self-assign
this.x = this.x; // ❌ dead / typo — did you mean another field?
// no-unreachable
return x; doStuff(); // ❌ doStuff() never runs — remove or reorder
// no-case-declarations — lexical decl leaks across cases
switch (k) { case 1: const a = 1; … } // ❌
switch (k) { case 1: { const a = 1; … } } // ✅ block-scope the case
// no-redeclare — merge or rename the duplicate declaration
// no-import-assign — never reassign an imported binding; copy to a local<!-- vue/no-mutating-props — never mutate a prop -->
<!-- ❌ --> <input @input="modelValue = $event" />
<!-- ✅ emit and let the parent own state -->
<input @input="$emit('update:modelValue', $event.target.value)" />
<!-- vue/require-v-for-key -->
<!-- ❌ --> <li v-for="item in items">{{ item }}</li>
<!-- ✅ --> <li v-for="item in items" :key="item.id">{{ item }}</li>
<!-- vue/no-use-v-if-with-v-for — don't combine on one element -->
<!-- ❌ --> <li v-for="i in items" v-if="i.active" />
<!-- ✅ filter in a computed, or wrap v-if on a <template> -->// vue/no-ref-as-operand — forgot .value
const count = ref(0);
if (count > 5) {} // ❌ compares the ref object
if (count.value > 5) {} // ✅
// vue/return-in-computed-property / vue/no-side-effects-in-computed-properties
const total = computed(() => { items.push(x); }); // ❌ side effect + no return
const total = computed(() => items.reduce((a, b) => a + b, 0)); // ✅ pure + returns<!-- vue/no-unused-components (81) — component imported/registered but never used
in the template. Remove the import (script setup) or the components:{} entry. -->
<!-- vue/no-unused-vars (10) — a template-scoped var (v-for / v-slot) is never used -->
<!-- ❌ --> <li v-for="(item, i) in items">{{ item }}</li> <!-- i unused -->
<!-- ✅ --> <li v-for="item in items">{{ item }}</li> <!-- drop it -->
<!-- vue/require-v-for-key already in §3; vue/valid-v-for (1) — v-for must have valid
syntax + a key on a real element (not on <template> without key on children) -->
<!-- vue/valid-attribute-name (1) — attribute name has illegal chars (e.g. a stray
space/quote in a :prop name) → fix the attribute name -->
<!-- vue/no-parsing-error (2) — malformed template (unclosed tag, bad directive
expression, duplicate attribute) → fix the markup; the message pinpoints it -->
<!-- vue/require-toggle-inside-transition (1) — <transition> needs a v-if/v-show/
:key/dynamic-component child to actually transition -->
<!-- ✅ --> <transition><div v-if="open" /></transition>
<!-- vue/no-reserved-component-names (1) — don't name a component after an HTML/SVG
element (e.g. `Button`, `Image`) → rename (e.g. `AppButton`) -->// vue/no-dupe-keys (12) — the same key appears in two option groups
// (props + data/computed/methods), so one silently shadows the other.
props: { value: {} }, data: () => ({ value: 1 }) // ❌ rename one
// vue/require-valid-default-prop (5) — object/array prop defaults MUST be a factory
props: { items: { type: Array, default: [] } } // ❌ shared ref
props: { items: { type: Array, default: () => [] } } // ✅ factory
// vue/prefer-import-from-vue (1) — import Vue APIs from "vue", not "@vue/*"
import { ref } from "@vue/runtime-core"; // ❌
import { ref } from "vue"; // ✅
// vue/valid-next-tick (1) — await nextTick() OR pass a callback; don't do both,
// and don't call it without using the result.
await nextTick(); // ✅// no-prototype-builtins
if (obj.hasOwnProperty(k)) {} // ❌
if (Object.prototype.hasOwnProperty.call(obj, k)) {} // ✅
// no-useless-escape — drop the needless backslash (prettier/--fix often handles it)
/\-/ → /-/
// no-empty — FIRST ask: does the block do anything? If it does NOTHING,
// DELETE it — do not comment an empty block into existence.
if (a) { doX(); } else { } // ❌ empty else that does nothing
if (a) { doX(); } // ✅ drop the dead branch entirely
// Only keep an empty block when it MUST exist (a catch that deliberately
// swallows, a required override) — then add a reason comment:
catch (e) {} // ❌
catch (e) { /* ignore: best-effort */ } // ✅ intentional no-op
// no-useless-catch — a catch that only rethrows adds nothing → remove the try/catch
// no-async-promise-executor — never pass an async fn to new Promise; hoist the body
new Promise(async (res) => { … }); // ❌no-undef — OFF permanently. TypeScript already checks undefined symbols; the rule
can't see Vue macros / auto-imports / type-only refs (thousands of false positives).no-unused-vars — OFF, superseded by the @typescript-eslint version.prettier/prettier, vue/max-attributes-per-line, vue/multi-word-component-names
— formatting/opinion; owned by the formatter or a team decision, not this gate.type-check:app)Enabled compiler flags (tsconfig.app.json): strict: true + noImplicitReturns: true.
The gate is a hard 0.
// TS7030 noImplicitReturns — make the IMPLICIT fall-off `undefined` explicit (pure no-op).
// The added return must equal today's runtime value — never invent a value, never add `break`.
function f(x: number) { if (x > 0) return "a"; } // ❌
function f(x: number) { if (x > 0) return "a"; return undefined; } // ✅
// If a declared return type forbids undefined but a path legitimately falls off,
// widen it at the DECLARATION site (`: string` → `: string | undefined`) — not a cast.noFallthroughCasesInSwitch is intentionally OFF. Unlike ESLint's no-fallthrough, the TS
flag has no // falls through comment escape hatch, so it cannot express deliberate
cascades (e.g. the cumulative dashboard schema-version migrations in
convertDashboardSchemaVersion.ts) without a behavior-risky refactor. ESLint's comment-aware
no-fallthrough already guards new code — keep intentional fallthrough marked // falls through.
(A version-bumping if-chain is the only behavior-preserving way to satisfy the TS flag; don't
retrofit it onto existing migration switches.)
// ❌ cast at the use site
const t = ref(props.tab); // Ref<string>
<Editor :lang="(t as 'sql'|'promql')" />
// ✅ type the source (prop + ref)
tab: { type: String as PropType<'sql'|'promql'|'custom'> }
const t = ref(props.tab); // Ref<'sql'|'promql'|'custom'>
<Editor :lang="t" />
// ❌ (x as unknown as string) coercing an array key
colOrder[selectedStream as unknown as string]
// ✅ express the real operation
colOrder[selectedStream.join(",")]Casts that are legitimately kept: Array as PropType<T> (required Vue syntax),
as const, as unknown as X at a genuine external boundary (reka-ui/DOM/JSON.parse/
3rd-party), and a narrowing assertion for a runtime invariant the compiler can't prove
(document it). Deriving an API type from the backend Rust struct beats any cast.
Prettier is installed but not yet enabled (prettier/prettier is off, no repo config).
When adopted, the intended standard is printWidth 80, semi, double quotes, trailingComma all, tabWidth 2 (the codebase already targets 80). It's a one-shot repo-wide reformat (~2,100
files) + a format script, done as its own commit — not something to hand-apply per file.
Once enabled, Prettier owns whitespace/quotes/commas; ESLint owns correctness.
npm run type-check:app → fix type errors first (types unblock lint clarity).npm run lint:ci -- --quiet → must be 0 errors (warnings are the ratchet backlog, allowed).warn → error (§9).error TS fails); any ESLint error fails
lint:ci. Warnings are the ratchet backlog — don't add new ones.warn → errorThe config keeps the backlog as warn so CI never blocks. To enforce a rule permanently,
drive its count to zero, then flip it. Never flip a rule that still has violations (that
turns the whole lint:ci gate red on every PR), and never flip everything at once (2,700+
errors). One rule (or one small bucket) per PR.
Per-rule procedure
npx eslint "src/**/*.{js,ts,vue}" --rule '{"<rule-id>":"error"}' -f unix 2>/dev/null \
| grep -c "<rule-id>"eslint.config.js, change that rule from "warn" to "error".npm run lint:ci must exit 0. Commit.Regenerate the live backlog any time
npx eslint "src/**/*.{js,ts,vue}" -f json 2>/dev/null | node -e '
const d=JSON.parse(require("fs").readFileSync(0,"utf8"));const by={};
for(const f of d)for(const m of f.messages)if(m.severity===1)by[m.ruleId]=(by[m.ruleId]||0)+1;
for(const[r,c]of Object.entries(by).sort((a,b)=>b[1]-a[1]))console.log(String(c).padStart(5),r);'Recommended order (ascending effort; counts are a snapshot — regenerate for live numbers):
| # | Bucket | Rules (see section) | ~count | How |
|---|---|---|---|---|
| — | Done | 4 zero-violation rules already at error (no-shadow-restricted-names, vue/valid-v-else-if, vue/no-deprecated-v-bind-sync, vue/no-v-text-v-html-on-component) | 0 | already enforced |
| 1 | Real bugs (§3, §3.1) | no-unreachable, no-self-assign, no-redeclare, no-case-declarations, no-unsafe-optional-chaining, no-import-assign, vue/no-dupe-keys, vue/no-ref-as-operand, vue/return-in-computed-property, vue/no-side-effects-in-computed-properties, vue/require-valid-default-prop, vue/require-v-for-key, vue/no-use-v-if-with-v-for, vue/valid-*, vue/no-parsing-error, vue/no-reserved-component-names, vue/require-toggle-inside-transition | ~90 | fix by hand → flip each to error |
| 2 | Mechanical (§4) | no-prototype-builtins (128), no-useless-escape (78), no-useless-catch (29), no-async-promise-executor (17), no-empty (16) | ~270 | mostly trivial |
| 3 | Vue correctness (§3, §3.1) | vue/no-mutating-props (228), vue/no-unused-components (81), vue/no-unused-vars (10) | ~320 | real refactors; ratchet down |
| 4 | Unused code (§2) | @typescript-eslint/no-unused-vars (2,072) | ~2,072 | eslint-plugin-unused-imports autofix + _-prefix; ratchet, not one PR |
TypeScript flags (separate track, tsconfig.app.json, §6): noImplicitReturns (60) →
noFallthroughCasesInSwitch (8; migration-switch refactor first) → noUnusedLocals/
noUnusedParameters only if TS (not ESLint) owns unused. noUncheckedIndexedAccess is a no‑op
until strictNullChecks (its own future initiative).
Full rationale, research, and per-bucket detail live in the lint & type-check strictness rollout design doc.
© openobserve, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .claude/skills/eslint-error-handling of openobserve/openobserve.
Open the folder on GitHubat commit 0e9ab49
OpenObserve ESLint and TypeScript Fixes next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| OpenObserve ESLint and TypeScript Fixes this skillopenobserve/openobserve | 22k | — | ~5.1k | Automated safety check: Pass | AGPL-3.0 | |
| Code Qualityredis/RedisInsight | 8.9k | — | ~1.2k | Automated safety check: Pass | Custom licence | |
| Obsidian Plugin Development Guidelinesgapmiss/obsidian-plugin-skill | 190 | — | ~4.5k | Automated safety check: Pass | MIT | |
| Cb Code QualityBlkLeg/CircuitBreaker | 201 | — | ~1.9k | Automated safety check: Pass | MIT | |
| Fallow Setupfallow-rs/fallow-skills | 129 | — | ~1.1k | Automated safety check: Pass | MIT | |
| Ban Type AssertionsFactory-AI/factory-plugins | 111 | — | ~1.7k | Automated safety check: Pass | None |
redis/RedisInsight
Code-quality standards for RedisInsight: TypeScript strictness, naming conventions (camelCase, PascalCase, UPPERSNAKECASE), linting rules, no any without reason, no !important in styles, and…
gapmiss/obsidian-plugin-skill
Rules and references for building Obsidian plugins: ESLint rules, TypeScript practices, memory cleanup, API choices, UI standards and the community submission process.
BlkLeg/CircuitBreaker
Circuit Breaker code conventions and the quality gates that actually block a push — ruff, mypy, eslint, the pytest coverage ratchet, and the make verify tiers.
fallow-rs/fallow-skills
Set up or modernize code-quality tooling for JavaScript and TypeScript projects.
Factory-AI/factory-plugins
Ban as type assertions in a package via the @typescript-eslint/consistent-type-assertions lint rule, replacing them with compiler-verified type-safe alternatives.
Arize-ai/phoenix
Migrate or upgrade TypeScript tooling in the Phoenix monorepo.
openobserve/openobserve
Splits a change into planner, coder and independent reviewer roles: you confirm a spec, a subagent implements it, and a separate reviewer checks each round's local WIP commit.
openobserve/openobserve
Produces a read-only morning brief of your open pull requests across the openobserve GitHub org, with a next step for each and a reminder for idle ones.
Works with
Categories
A playbook for fixing ESLint and TypeScript errors in the OpenObserve web frontend, with rule-by-rule guidance and typing conventions. The stack is Vue 3, TypeScript, an ESLint flat config and Prettier, and the gates are npm run lint:ci and npm run type-check:app (vue-tsc), both of which must stay at 0 errors. Six golden rules apply to every fix: never use any, prefer types at the declaration site over as casts, keep type-only fixes from changing runtime behavior, use strict equality only, keep comments to one or two lines, and fix rather than suppress, with eslint-disable or @ts-expect-error reserved for genuine false positives and given a reason.
OpenObserve ESLint and TypeScript Fixes fits situations like: fixing lint or type-check errors in the OpenObserve web frontend; deciding whether a new type belongs inline or in a shared file; replacing any and as casts with proper types; writing new Vue code that already meets the repo's lint standard.
Run `npx skills add openobserve/openobserve --skill eslint-error-handling -a claude-code`. Or copy the skill folder (.claude/skills/eslint-error-handling in openobserve/openobserve) into .claude/skills/eslint-error-handling in your project. Claude Code loads it when a task matches its description.
Run `npx skills add openobserve/openobserve --skill eslint-error-handling -a codex`. Or copy the skill folder (.claude/skills/eslint-error-handling in openobserve/openobserve) into .agents/skills/eslint-error-handling in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add openobserve/openobserve --skill eslint-error-handling -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/eslint-error-handling, .gemini/skills/eslint-error-handling, .github/skills/eslint-error-handling and .opencode/skills/eslint-error-handling in your project.
Going by SKILL.md and its folder, OpenObserve ESLint and TypeScript Fixes needs the command-line tools its instructions call (npm, npx and node). Our summary lists: Node.js with the web/ project dependencies installed.
SKILL.md contains no URLs. Its commands use npm and npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
OpenObserve ESLint and TypeScript Fixes is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.1k tokens (SKILL.md is roughly 20k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with OpenObserve ESLint and TypeScript Fixes: Code Quality (redis/RedisInsight, 8.9k stars), Obsidian Plugin Development Guidelines (gapmiss/obsidian-plugin-skill, 190 stars), Cb Code Quality (BlkLeg/CircuitBreaker, 201 stars) and Fallow Setup (fallow-rs/fallow-skills, 129 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
openobserve (a GitHub organization) maintains it in openobserve/openobserve, which has 22,306 GitHub stars. The repository holds 3 skills in this directory. The repository was last updated on October 9, 2026.
Source: openobserve/openobserve on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.