# Arckode UI

Framework frontend con archivos `.ark` (Single File Components), reactividad por signals, compiler propio (Vite plugin), router file-system y analyzer estático con sugerencias concretas de fix.

> **Diseñado para máxima predictibilidad de output de IA.** Hay un solo camino correcto para cada problema. Para cada violation detectada, el analyzer sugiere el fix concreto.

**Versión:** 0.29.1 — scaffold production-grade con auth, guards, layouts

---

## Activar el SKILL para tu IA

`ark new frontend` genera **AGENTS.md** y **CLAUDE.md** automáticamente en la raíz del proyecto.
Para proyectos ya existentes, copiá `CLAUDE.md.example`:

```bash
cp node_modules/arckode-ui/CLAUDE.md.example ./CLAUDE.md
```

Sin este archivo, la IA improvisa. Con el SKILL cargado, **tus componentes pasan `ark analyze` con 0 errores**.

---

## Instalación

### Opción A — Scaffold completo (recomendado)

```bash
# Crea un proyecto FUNCIONAL con todas las deps configuradas
npx arckode-ui new mi-app
cd mi-app
bun install        # o npm install / pnpm install
bun dev            # arranca http://localhost:5173
```

**Qué genera `ark new` automáticamente** (no tenés que escribir nada de esto a mano):

```
mi-app/
├─ package.json                 ← Vite 5.4 pineado, Tailwind v4, TypeScript
├─ vite.config.ts               ← Plugins arkcodeUi() + tailwindcss() ya configurados
├─ tsconfig.json                ← Strict mode + paths correctos
├─ AGENTS.md / CLAUDE.md        ← Instrucciones para IA (puntero a node_modules/arckode-ui/skills)
├─ index.html                   ← Con <div id="app"> + script type=module
├─ src/
│  ├─ main.ts                   ← Entry: lazy routes + mount(App)
│  ├─ App.ark                   ← Componente raíz con auth check on mount
│  ├─ guards.ts                 ← protectPage() y requireAuth()
│  ├─ styles/app.css            ← Tailwind v4 con @theme (tokens: primary, surface, text, etc.)
│  ├─ layouts/MainLayout.ark    ← Navbar auth-aware + slot + ToastContainer
│  ├─ pages/                    ← index.ark, login.ark, register.ark (Skeleton + EmptyState + SEO)
│  ├─ components/ui/            ← 52 primitivos canónicos (Button, Input, Card, Dialog, etc.)
│  ├─ stores/                   ← 4 stores: toast, confirm, auth (con isAuthenticated), i18n
│  ├─ composables/              ← 3 composables: useFetch, useConfirm, useRequireAuth
│  ├─ services/auth.service.ts  ← createService con login/register/me/logout
│  ├─ types/index.ts            ← DTOs: User, ApiEnvelope, PaginatedResponse
│  └─ shared/                   ← icons.ts (Lucide subset), validation.ts (createSchema)
```

No improvises con primitivos custom — los 52 que vienen scaffoldeados cubren ~98% de UI típica.

### Opción B — Instalación manual (proyecto existente)

```bash
# Producción
bun add arckode-ui

# Desarrollo (versiones EXACTAS — no cambiar)
bun add -d vite@5.4 @tailwindcss/vite@4 tailwindcss@4 typescript@5

# o con npm
npm install arckode-ui
npm install -D vite@5.4 @tailwindcss/vite@4 tailwindcss@4 typescript@5
```

> ⚠️ **Vite 5.x OBLIGATORIO** — el plugin usa `transformWithEsbuild`, que Vite 6 removió. Con Vite 6+ el build falla con `Failed to load transformWithEsbuild`. Si tu `package.json` tiene `"vite": "^6.0.0"` o `"vite": "latest"`, bajalo a `"vite": "^5.4.0"`.

> ⚠️ **Tailwind v4 OBLIGATORIO** — la sintaxis cambió. v4 usa `@import "tailwindcss"` (NO las directivas v3 `@tailwind base/components/utilities`) y el plugin oficial `@tailwindcss/vite` (NO el PostCSS plugin). Si copiás un setup de Tailwind v3, no aplica ningún estilo.

#### Configuración mínima manual

```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import { arkcodeUi } from 'arckode-ui/vite'   // ← Plugin obligatorio
import tailwindcss from '@tailwindcss/vite'   // ← Plugin oficial Tailwind v4

export default defineConfig({
  plugins: [arkcodeUi(), tailwindcss()],
})
```

```css
/* src/styles/global.css — Tailwind v4 con @theme */
@import "tailwindcss";

@theme {
  --color-primary:        oklch(0.65 0.18 250);
  --color-surface:        oklch(0.16 0 0);
  --color-text:           oklch(0.95 0 0);
  --color-text-muted:     oklch(0.65 0 0);
  --color-border:         oklch(0.25 0 0);
  /* Cualquier --color-X genera utilities bg-X, text-X, border-X automáticamente */
}
```

> **`@theme` es CSS-first** (Tailwind v4). NO hay `tailwind.config.js` ni `tailwind.config.ts`. Para customizar la línea gráfica, editás directamente este archivo. `ark theme init` genera un baseline completo con tokens semánticos.

```typescript
// src/main.ts
import './styles/global.css'
import { mount } from 'arckode-ui'
import App from './App.ark'

mount(App, '#app')
```

```html
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Mi App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
```

### Por qué el plugin de Vite es obligatorio (cómo funciona el stack)

```
.ark source (template + script + style)
         │
         ▼
  arkcodeUi() plugin (Vite plugin)
         │
         ├──▶ parser.ts        → divide en {template, script, style}
         ├──▶ template-compiler→ <template> a render function (h() calls)
         ├──▶ analyzer.ts      → valida 40+ reglas, bloquea build si hay errores
         ├──▶ scope-css.ts     → si <style scoped>: prefija con [data-ark-XXX]
         └──▶ transformWithEsbuild → strip TS types, output JS final
         │
         ▼
   JS module compilado + CSS scoped inyectado en <head>
         │
         ▼
  Vite bundle (dev: HMR / prod: Rollup)
```

Sin el plugin, los `.ark` son archivos opacos que Vite no entiende — el build falla con `Unknown file extension ".ark"`. **El plugin ES el framework**: no hay forma de usar arckode-ui sin él.

### Troubleshooting — errores comunes

| Error | Causa | Solución |
|---|---|---|
| `Failed to load transformWithEsbuild` | Tenés Vite 6+ instalado | Bajar a Vite 5.4: `bun add -d vite@5.4` |
| `Unknown file extension ".ark"` | El plugin `arkcodeUi()` no está en `vite.config.ts` | Agregar `plugins: [arkcodeUi(), ...]` |
| `arkcodeUi is not a function` | Import mal (`arckodeUi` vs `arkcodeUi`) | Correcto: `import { arkcodeUi } from 'arckode-ui/vite'` (K minúscula intermedia) |
| Tailwind no aplica estilos | Sintaxis v3 en proyecto v4, o `@tailwindcss/vite` falta | `@import "tailwindcss"` (NO `@tailwind base/...`) + `tailwindcss()` en plugins |
| `Identifier 'defineComponent' has already been declared` | Mito histórico de versiones viejas | Ya no pasa — esbuild deduplica. Importalo normalmente. |
| `[Component] X es requerido` en runtime | Usaste un primitivo (`<Button>`) sin pasar todos los props obligatorios | El primitivo te dice qué falta (`name: 'Button'` + prop faltante) |
| Build pasa pero pantalla en blanco | `mount(App, '#app')` con `#app` que no existe en `index.html` | Verificar el ID del `<div>` en `index.html` |
| `process is not defined` en runtime | Importaste algo de `arckode-ui/server` en código que va al cliente | El subpath `/server` es solo para Node. Usar `arckode-ui` (sin `/server`) en `.ark` |

## SSG — HTML estático por ruta (v0.20+)

Cada ruta tiene su propio HTML pre-rendered. Bots (Google, WhatsApp, Twitter, Discord) leen el HTML estático con `<title>`, `<meta>`, `<link rel="canonical">` correctos — sin ejecutar JS.

### Setup

**1)** Cada page del router define su head con `useHead()`:

```typescript
// src/pages/about.ark
<script lang="ts">
import { defineComponent, useHead } from 'arckode-ui'

export default defineComponent({
  name: 'AboutPage',
  setup() {
    useHead({
      title: 'Sobre nosotros · Mi App',
      description: 'Quiénes somos y qué hacemos.',
      meta: [
        { property: 'og:title', content: 'Sobre nosotros · Mi App' },
        { property: 'og:image', content: 'https://mi-app.com/og-about.png' },
        { name: 'twitter:card', content: 'summary_large_image' },
      ],
      link: [{ rel: 'canonical', href: 'https://mi-app.com/about' }],
    })
    return { state: {}, computed: {}, actions: {} }
  },
})
</script>
```

**2)** Para rutas dinámicas (`src/pages/users/[id].ark`), exportá `staticPaths`:

```typescript
// src/pages/users/[id].ark
export const staticPaths = [
  { id: '1' },
  { id: '2' },
  // O cargado de un servicio:
  // ...await userService.listIds().then(ids => ids.map(id => ({ id })))
]
```

**3)** Comandos de build (3 pasos):

```bash
bun vite build                                                       # bundle SPA normal + dist/index.html
bun vite build --ssr 'src/pages/**/*.ark' --outDir dist/_ssg          # SSR build de las pages
npx ark ssg                                                           # genera HTML por ruta
```

Output:

```
dist/
├─ index.html                ← HomePage con su useHead
├─ about/index.html          ← AboutPage con su useHead
├─ users/
│  ├─ 1/index.html           ← users/[id] con id=1
│  └─ 2/index.html           ← users/[id] con id=2
└─ _ssg/                     ← Temporal, agregar a .gitignore
```

Servís `dist/` con cualquier CDN/server estático (Vercel, Netlify, Cloudflare Pages, Nginx, etc.).

### API server programática

Si querés controlarlo desde un script propio (CI custom, integración con otro stack):

```typescript
import { renderToString, injectHeadAndBody } from 'arckode-ui/server'
import AboutPage from './pages/about.ark'   // compilado por vite SSR build

const { html, head } = renderToString(AboutPage, { /* props opcionales */ })
const template = fs.readFileSync('dist/index.html', 'utf-8')
const finalHtml = injectHeadAndBody(template, head, html)
fs.writeFileSync('dist/about/index.html', finalHtml)
```

### Limitaciones del SSG actual

- **Hidratación**: el cliente re-renderiza sobre el HTML pre-rendered (flicker mínimo). Hidratación pulida sin flicker está en backlog (v0.21).
- **Server components / per-request SSR**: no — todo el render es en build-time. Para sitios cuyo contenido cambia minuto a minuto, considerar ISR (v0.22).
- **Async setup**: `setup()` no es async hoy. Para cargar data antes del SSG, usá `staticPaths` con loaders o llamá `await` en tu script de build.

## Tu primer componente — `App.ark`

```html
<template>
  <div>
    <h1>{{ state.message }}</h1>
    <button @click="actions.toggle">{{ computed.buttonLabel }}</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, signal, computed } from 'arckode-ui'

export default defineComponent({
  name: 'App',
  props: {},
  emits: [],
  setup(props, { emit }) {
    const message = signal('Hola Arckode')
    const buttonLabel = computed(() => message.value === 'Hola Arckode' ? 'Chau' : 'Hola')

    function toggle() {
      message.value = message.value === 'Hola Arckode' ? 'Chau Arckode' : 'Hola Arckode'
    }

    return {
      state: { message },
      computed: { buttonLabel },
      actions: { toggle },
    }
  },
})
</script>
```

> **Importá `defineComponent` normalmente** para que TypeScript y tu IDE lo reconozcan. El Vite plugin ya inyecta un import al compilar — esbuild deduplica los duplicados, así que no rompe. Los 52 primitivos del scaffold (`ark new`) lo importan así.

> En el `<template>` accedés a signals SIN `.value` (Proxy auto-resuelve). En el `<script>` SÍ usás `.value` para leer/mutar.

## CLI

```bash
ark new mi-app                           # scaffold proyecto nuevo
ark generate component UserCard          # → src/components/features/UserCard.ark
ark generate page about                  # → src/pages/about.ark
ark generate page users/[id]             # → src/pages/users/[id].ark
ark generate store cart                  # → src/stores/cart.store.ts
ark generate service Product             # → src/services/product.service.ts
ark generate layout users                # → src/pages/users/_layout.ark
ark analyze [--json]                     # detecta violations en archivos .ark
ark routes                               # lista rutas detectadas en src/pages/
```

Aliases del generator: `c` (component), `p` (page), `s` (store), `sv` (service), `l` (layout).

## Reactividad — Signals

```typescript
import { signal, computed, watch, effect } from 'arckode-ui'

const count = signal(0)
count.value = 5            // set — dispara re-render
count.value                // get — suscribe al efecto activo
count.peek                 // get sin suscribirse

const double = computed(() => count.value * 2)
double.value               // readonly — recalcula al cambiar count

watch(count, (newVal, oldVal) => { /* ... */ })
effect(() => { document.title = `${count.value}` })
```

## Directivas del template

| Necesito | Sintaxis |
|----------|----------|
| Texto dinámico | `{{ state.x }}` / `{{ computed.x }}` / `{{ props.x }}` |
| Atributo dinámico | `:value="state.x"` / `:class="..."` |
| Evento | `@click="actions.handler"` |
| Evento con argumento | `@click="actions.toggle(item.id)"` |
| Tecla modificadora | `@keydown.enter="actions.search"` |
| Condicional | `v-if="..."` + `v-else-if="..."` + `v-else` |
| Visibilidad (sin desmontar) | `v-show="state.x"` |
| Lista | `v-for="item in state.items"` |
| Lista con identidad (recomendado) | `v-for="item in state.items"` + `:key="item.id"` |
| Componente hijo | `<MiComponente :prop="state.x" />` |
| Slot (en wrapper) | `<slot />` |
| Nodo controlado por librería externa | `<div uncontrolled>` (Leaflet, Chart.js — el reconciler no toca el contenido) |

> **`.value` no se usa en el template.** El renderer envuelve `state` y `computed` en un Proxy que llama `.value` automáticamente. En el `<script>`, `.value` sigue siendo obligatorio para leer y mutar signals.

> **`:key`** en `v-for` activa la reconciliación por identidad (preserva input focus, scroll position, estado interno de componentes hijos al reordenar). Sin `:key` se cae al patch posicional, que es correcto pero pierde estado al insertar/reordenar.

## Error boundaries — `onError`

Un error en `setup()` o `render` de un componente NO tumba el resto de la app. El renderer captura, dispara los handlers declarados con `onError`, y propaga por la cadena de ancestros hasta encontrar uno que retorne `true`. Si nadie suprime, se loggea y se renderiza `<span data-ark-error>`.

```typescript
import { onError, signal } from 'arckode-ui'

export default defineComponent({
  name: 'UserList',
  setup() {
    const users = signal([])

    onError((err) => {
      console.error('UserList falló:', err)
      toast.actions.error('No pudimos cargar los usuarios. Reintentá en un momento.')
      return true   // suprimir — no propagar al padre
    })

    return { state: { users }, computed: {}, actions: {} }
  },
})
```

- `return true` → suprime el error.
- `return undefined/false` → propaga al ancestro.

Para fallback de UI a nivel ruta, definí `src/pages/_error.ark`. Para errores locales con UI fallback, manejá un signal `hasError` en el propio setup.

## Scoped CSS — `<style scoped>`

Soportado nativamente desde v0.16. Cada selector se prefija con `[data-ark-{scopeId}]`. El plugin Vite inyecta el CSS en `<head>` con id determinístico (HMR-safe).

```html
<template>
  <div class="card">
    <h2 class="title">{{ props.title }}</h2>
  </div>
</template>

<script lang="ts">
export default defineComponent({
  name: 'Card',
  props: { title: { type: String, required: true } },
  setup: () => ({ state: {}, computed: {}, actions: {} }),
})
</script>

<style scoped>
.card { padding: 16px; border-radius: 12px; }
.card .title { font-size: 18px; }

/* :global() escapa el scope */
:global(body.dark) .card { background: #1a1a1a; }

/* @media recursivamente scopea */
@media (max-width: 768px) {
  .card { padding: 8px; }
}

/* @keyframes no scopea su contenido */
@keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } }
</style>
```

- `<style>` SIN `scoped` → `STYLE_BLOCK_IGNORED` (error). CSS verdaderamente global vive UNA vez en `src/styles/global.css`.
- Para layouts/responsive típicos preferí Tailwind utilities. Usá `<style scoped>` cuando necesités selectores complejos, animaciones o keyframes locales al componente.

## Named + scoped slots (v0.17+)

```html
<!-- Card.ark — definición -->
<template>
  <div class="card">
    <header><slot name="header" /></header>
    <main><slot /></main>
    <footer><slot name="footer" /></footer>
  </div>
</template>
```
```html
<!-- App.ark — uso -->
<Card>
  <template #header><h1>Mi título</h1></template>
  <template #footer><Button>Aceptar</Button></template>
  <p>Contenido principal</p>
</Card>
```

**Scoped slots** (`<DataTable>` con render custom por fila):

```html
<DataTable :rows="state.users">
  <template #default="{ row }">
    <Avatar :name="row.name" /> · <Badge>{{ row.role }}</Badge>
  </template>
</DataTable>
```

## Dynamic components (v0.17+)

```html
<!-- :is recibe el Component o un tag string -->
<component :is="computed.currentTab" :data="state.payload" />
```

## Teleport — modals/tooltips fuera del DOM tree (v0.17+)

```html
<Teleport to="#modals">
  <div class="dialog">
    <h2>Confirmar</h2>
    <Button :onClick="actions.confirm">Sí</Button>
  </div>
</Teleport>
```

Renderiza los children dentro de `#modals` (o cualquier selector CSS), dejando un placeholder vacío en su posición original. Escapa `overflow:hidden` y `z-index` de ancestros.

## Batch — coalesce mutaciones de signals (v0.17+)

```typescript
import { batch, signal } from 'arckode-ui'

const a = signal(0)
const b = signal(0)
const c = signal(0)

// Sin batch: 3 re-renders
a.value = 1; b.value = 2; c.value = 3

// Con batch: 1 solo re-render
batch(() => {
  a.value = 10
  b.value = 20
  c.value = 30
})
```

## Lazy routes — code splitting por ruta

`createRouterView` acepta `() => Promise<Component>` como `component`. Vite emite chunks separados.

```typescript
import { createRouterView } from 'arckode-ui'
import HomePage from './pages/index.ark'

const routes = [
  { path: '/',     component: HomePage },                                 // eager
  { path: '/heavy', component: () => import('./pages/heavy.ark') },        // lazy
  {
    path: '/dashboard',
    component: () => import('./pages/dashboard.ark'),
    loading: () => ({ tag: 'div', props: { class: 'p-8' }, children: ['Cargando…'] }),
    error: (err) => ({ tag: 'div', props: { class: 'text-danger' }, children: [String(err)] }),
  },
]

mount(createRouterView(routes), '#app')
```

Cache por loader fn — la primera visita carga el chunk, las subsiguientes van directo al componente.

## El analyzer — `ark analyze`

Detecta más de 30 violations comunes. Cada violation incluye un campo `fix` con la sugerencia concreta:

```
[arckode-ui] UserCard.ark:5
  HANDLER_NOT_IN_ACTIONS: El handler "handleClick" no está namespaced.

  > 5 | <button @click="handleClick">
  Fix: Reemplazar "handleClick" por "actions.handleClick" y asegurarse de que la función esté declarada en setup() y exportada en el return.actions.
```

Reglas principales:
- Estructura: `MISSING_TEMPLATE`, `MISSING_SCRIPT`, `MISSING_LANG_TS`, `WRONG_TEMPLATE_ORDER`, `DUPLICATE_SECTION`
- Componente: `MISSING_COMPONENT_NAME`, `PROP_MISSING_TYPE`, `EMIT_CAMELCASE`, `SETUP_UNKNOWN_RETURN_KEY`, `SIGNAL_IN_COMPUTED`
- Reactividad rechazada: `REF_REACTIVE_USAGE`, `PROVIDE_INJECT_USAGE`, `V_MODEL_UNSUPPORTED`
- Red: `DIRECT_FETCH_IN_COMPONENT` (solo permitido en `src/services/`)
- Template: `LOGIC_IN_TEMPLATE`, `HANDLER_NOT_IN_ACTIONS`, `VFOR_NOT_NAMESPACED`, `VIF_NOT_NAMESPACED`, `TEMPLATE_UNDEFINED_REF`, `HTML_COMMENT_IN_TEMPLATE`
- Style: **`STYLE_BLOCK_IGNORED`** (solo dispara con `<style>` SIN `scoped` — scoped es soportado), `STATIC_INLINE_STYLE`, `PRIMITIVE_INLINE`, `OVERRIDE_PRIMITIVE_STYLE`, `CSS_DUPLICATES_TAILWIND` (cross-file)
- Arquitectura: `LAYER_IMPORT_VIOLATION`, `DUPLICATE_INTERFACE` (cross-file)
- SOLID: `COMPONENT_TOO_LONG`, `TOO_MANY_PROPS` (ISP), `TOO_MANY_STORES` (SRP), `FUNCTION_TOO_LONG` (SRP), `ARROW_FUNCTION_ACTION`
- A11y: `IMG_MISSING_ALT`, `CLICK_ON_NON_INTERACTIVE`
- UX obligatorio: `BUTTON_ASYNC_NO_LOADING`, `ASYNC_ACTION_NO_ERROR_HANDLING`

Las reglas de lógica (`DIRECT_FETCH_IN_COMPONENT`, `REF_REACTIVE_USAGE`, `FUNCTION_TOO_LONG`, `ASYNC_ACTION_NO_ERROR_HANDLING`) también corren sobre los `.ts` de `src/composables/`. `fetch()` directo solo se permite en `src/services/`.

## Ejemplos incluidos

- `examples/kitchen-sink/` — demo de todas las directivas y primitivas
- `examples/tasks/` — app real de tareas con sidebar, modal, form, validación, search con debounce

Correr cualquiera:
```bash
cd examples/tasks && bun install && bun dev
```

## Documentación para IA

- `CLAUDE.md` (en este repo) — instrucciones para Claude/OpenCode al desarrollar el framework
- `~/.claude/skills/arckode-ui/SKILL.md` — guía completa para usar el framework

## Gotchas de setup

| Síntoma | Causa | Fix |
|---------|-------|-----|
| `Identifier 'defineComponent' has already been declared` | Importado manualmente + inyectado por el plugin | No importar `defineComponent` en el `<script>` |
| `Failed to load transformWithEsbuild` | Vite 6+ eliminó esa función | Usar `vite@5` |
| Tailwind no aplica estilos (texto plano) | Falta `@tailwindcss/vite` en plugins o usás directivas v3 | Agregar a `vite.config.ts` + usar `@import "tailwindcss"` |
| `ReferenceError: X is not defined` en template | Props no están en scope directo del template | Usar `props.X` en vez de `X` |
| `state.X` siempre `undefined` | `signal()` recibió un objeto en vez de un primitivo | `signal(false)` no `signal({ x: false })` |

## Stack

- TypeScript estricto
- Vite 5.x (build + dev server)
- Vitest + happy-dom (tests)
- Bun (package manager recomendado)

## APIs públicas (barrel `arckode-ui`)

| Categoría | Exports |
|-----------|---------|
| Reactividad | `signal`, `computed`, `watch`, `effect`, `Signal`, `ComputedSignal`, `StopFn` |
| Componentes | `defineComponent`, `onMount`, `onUnmount`, `onUpdate`, `onError`, `ErrorHook`, `h`, `mount`, `mountComponent` |
| Estado global | `defineStore`, `StoreInstance`, `UseStoreFn`, `PersistOptions` |
| HTTP | `createService`, `ArkServiceError`, `ServiceDefinition`, `ServiceInstance`, `RequestOptions` |
| Router | `createRouter`, `createRouterView`, `navigate`, `navigateTo`, `useRoute`, `getCurrentPath`, `RouteDefinition`, `RouteWithComponent`, `LazyComponent`, `RouteInfo` |
| Testing | `mountForTest`, `ArkTestWrapper` |
| Analyzer | `FixOp` |

Subpath: `arckode-ui/vite` exporta `arkcodeUi(options)` y `generateScopeId(filePath)`.

## Estado

817 tests pasando · 35+ reglas del analyzer · 52 primitivos UI + 4 stores + 3 composables scaffoldeados por `ark new`.

## Suprimir reglas del analyzer puntualmente

Para edge cases legítimos (demo con markup nativo, archivo vendor, etc.):

```typescript
// .ts (composable, store, service)
// arckode-disable HIGH_CYCLOMATIC_COMPLEXITY
export function classify(n: number) { /* ... */ }
```
```html
<!-- .ark -->
<!-- arckode-disable PRIMITIVE_INLINE -->
<template>
  <button class="custom">Necesito markup nativo</button>
</template>
```

Las reglas suprimidas se listan separadas por espacio. Aplica solo al archivo. Usar con criterio — las reglas existen por una razón.

## Licencia

MIT
