navori 0.2.17 → 0.2.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/core/core-assets/hooks/quality-gate-pre-commit.sh +49 -11
- package/dist/assets/core/core-assets/lib-skills/axios.md +62 -0
- package/dist/assets/core/core-assets/lib-skills/mantine-form.md +58 -0
- package/dist/assets/core/core-assets/lib-skills/react-router.md +61 -0
- package/dist/assets/plugins/cognitive/plugin.json +1 -7
- package/dist/assets/plugins/jscpd/plugin.json +1 -7
- package/dist/assets/plugins/jscpd/scripts/check-jscpd.sh +5 -6
- package/dist/assets/plugins/semgrep/plugin.json +1 -7
- package/dist/index.js +398 -9418
- package/package.json +7 -9
|
@@ -24,22 +24,60 @@ extract_cmd() {
|
|
|
24
24
|
}
|
|
25
25
|
cmd=$(extract_cmd)
|
|
26
26
|
|
|
27
|
+
# Detect the project's REAL package manager from lockfiles / package.json, so a
|
|
28
|
+
# gate command hardcoded to one PM (e.g. `pnpm run ...`) can still run in a repo
|
|
29
|
+
# that actually uses another (e.g. bun). Mirrors lib/detect.ts precedence:
|
|
30
|
+
# `packageManager` field first, then pnpm/bun/yarn/npm lockfiles. Prints the PM
|
|
31
|
+
# name (or nothing) on stdout. Git runs hooks from the repo root, so relative
|
|
32
|
+
# paths resolve against the project root.
|
|
33
|
+
detect_pm() {
|
|
34
|
+
if [ -f package.json ]; then
|
|
35
|
+
pm=$(sed -n 's/.*"packageManager"[[:space:]]*:[[:space:]]*"\([a-z]*\)@.*/\1/p' package.json | head -n1)
|
|
36
|
+
case "$pm" in pnpm|npm|yarn|bun) printf '%s' "$pm"; return 0 ;; esac
|
|
37
|
+
fi
|
|
38
|
+
if [ -f pnpm-lock.yaml ]; then printf 'pnpm'; return 0; fi
|
|
39
|
+
if [ -f bun.lockb ] || [ -f bun.lock ]; then printf 'bun'; return 0; fi
|
|
40
|
+
if [ -f yarn.lock ]; then printf 'yarn'; return 0; fi
|
|
41
|
+
if [ -f package-lock.json ]; then printf 'npm'; return 0; fi
|
|
42
|
+
return 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# True when the argument is a JS package manager (so we only try to remap the
|
|
46
|
+
# leading token of the gate command when it's actually a PM, not `make`/`ruff`).
|
|
47
|
+
is_pm() {
|
|
48
|
+
case "$1" in pnpm|npm|yarn|bun) return 0 ;; *) return 1 ;; esac
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
run_gate() {
|
|
52
|
+
echo "[navori] running quality-gate fast: $1" >&2
|
|
53
|
+
eval "$1" || {
|
|
54
|
+
echo "[navori] quality-gate fast failed. Commit/push aborted." >&2
|
|
55
|
+
exit 2
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
27
59
|
case "$cmd" in
|
|
28
60
|
'git commit'*|'git push'*)
|
|
29
61
|
gate="{{qualityGate.fast}}"
|
|
30
|
-
# Defensive: skip cleanly (exit 0) when the gate's runtime isn't on PATH —
|
|
31
|
-
# e.g. a Nix/nvm shell that hasn't loaded the project env. Never block a
|
|
32
|
-
# commit just because the tool is missing from THIS shell.
|
|
33
62
|
gate_bin="${gate%% *}"
|
|
34
|
-
if
|
|
35
|
-
|
|
36
|
-
|
|
63
|
+
if command -v "$gate_bin" >/dev/null 2>&1; then
|
|
64
|
+
run_gate "$gate"
|
|
65
|
+
else
|
|
66
|
+
# The declared runner isn't on PATH. If it's a package manager, detect the
|
|
67
|
+
# repo's real one from lockfiles and retry through it (a `pnpm run x` gate
|
|
68
|
+
# still runs in a bun-only checkout). #88: NEVER skip the gate silently —
|
|
69
|
+
# when nothing can run it, BLOCK the commit loudly instead of the old
|
|
70
|
+
# `exit 0` that handed a contributor zero quality gate without a word.
|
|
71
|
+
detected_pm="$(detect_pm)"
|
|
72
|
+
if is_pm "$gate_bin" && [ -n "$detected_pm" ] && [ "$detected_pm" != "$gate_bin" ] && command -v "$detected_pm" >/dev/null 2>&1; then
|
|
73
|
+
echo "[navori] '$gate_bin' no está en PATH; uso el package manager detectado por lockfile: '$detected_pm'." >&2
|
|
74
|
+
run_gate "$detected_pm ${gate#* }"
|
|
75
|
+
else
|
|
76
|
+
echo "[navori] quality-gate NO ejecutado: '$gate_bin' no está en PATH y no hay un package manager alternativo detectado que pueda correrlo." >&2
|
|
77
|
+
echo "[navori] Commit/push BLOQUEADO para no saltarnos el gate en silencio. Instala '$gate_bin' o usa 'git commit --no-verify' si de verdad quieres saltártelo." >&2
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
37
80
|
fi
|
|
38
|
-
echo "[navori] running quality-gate fast: $gate" >&2
|
|
39
|
-
eval "$gate" || {
|
|
40
|
-
echo "[navori] quality-gate fast failed. Commit/push aborted." >&2
|
|
41
|
-
exit 2
|
|
42
|
-
}
|
|
43
81
|
;;
|
|
44
82
|
esac
|
|
45
83
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: axios
|
|
3
|
+
description: Patrones de Axios en TS — instancia central, interceptores, tipado de respuestas, manejo de errores y cancelación. Aplica al tocar llamadas HTTP a APIs.
|
|
4
|
+
type: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Axios — convenciones
|
|
8
|
+
|
|
9
|
+
## Cuándo usar este skill
|
|
10
|
+
|
|
11
|
+
Al hacer una llamada HTTP a una API: crear un endpoint del cliente, agregar auth, mapear errores, o cancelar una request. Axios se cablea **una vez** en una instancia central con interceptores — los componentes/servicios importan esa instancia, no `axios` crudo con la URL a mano en cada llamada.
|
|
12
|
+
|
|
13
|
+
## El patrón
|
|
14
|
+
|
|
15
|
+
Una instancia por API (baseURL + interceptores), funciones tipadas encima; nunca `axios.get(fullUrl)` disperso:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
export const api = axios.create({
|
|
19
|
+
baseURL: import.meta.env.VITE_APP_NEXUS_URL,
|
|
20
|
+
timeout: 15_000,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
api.interceptors.request.use((config) => {
|
|
24
|
+
const token = getToken();
|
|
25
|
+
if (token) config.headers.Authorization = `Bearer ${token}`;
|
|
26
|
+
return config;
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
api.interceptors.response.use(
|
|
30
|
+
(res) => res,
|
|
31
|
+
(error) => Promise.reject(normalizeError(error)), // un solo shape de error
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Servicio tipado: el genérico es el TIPO DE DATA, no el del envelope.
|
|
35
|
+
export const getSession = (id: string) =>
|
|
36
|
+
api.get<Session>(`/sessions/${id}`).then((r) => r.data);
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Gotchas que muerden
|
|
40
|
+
|
|
41
|
+
- **`api.get<T>()` tipa `response.data`, no la respuesta entera.** El `T` describe el `data`; Axios envuelve en `{ data, status, headers }`. Devuelve `r.data` desde el servicio para que el caller vea `Session`, no `AxiosResponse<Session>`.
|
|
42
|
+
- **Una instancia con `baseURL`, no la URL completa por llamada.** Centraliza host/timeout/headers. Repetir `axios.get('https://…/sessions')` esparce la config y rompe al cambiar de entorno.
|
|
43
|
+
- **Auth/refresh/logging en interceptores, no copiados en cada request.** El token va en un `request.use`; el 401→refresh y el mapeo de error van en `response.use`. Nada de `headers: { Authorization }` a mano en cada endpoint.
|
|
44
|
+
- **`axios.isAxiosError(err)` antes de leer `err.response`.** En el `catch`, `err` es `unknown`. Sin el type guard, `err.response.data` explota en errores de red (donde `response` es `undefined` y solo hay `err.request`).
|
|
45
|
+
- **Un error de red NO es un error HTTP.** Timeout/DNS/offline no traen `response`. Distingue `err.response` (el server respondió con 4xx/5xx) de `err.request` (nunca llegó) para dar el mensaje correcto.
|
|
46
|
+
- **Cancela requests en vuelo con `AbortController`.** En efectos/búsqueda-as-you-type pasa `{ signal: controller.signal }` y aborta en el cleanup; sin esto, una respuesta vieja pisa a una nueva (race).
|
|
47
|
+
- **4xx/5xx ya rechazan la promesa.** No revises `res.status` en el `.then`; el flujo de error vive en `catch`/el interceptor. Solo `validateStatus` cambia esa regla, y rara vez la necesitas.
|
|
48
|
+
|
|
49
|
+
## Reglas duras
|
|
50
|
+
|
|
51
|
+
1. Todo HTTP pasa por la instancia central con `baseURL` + interceptores; nada de `axios` crudo con URL completa suelta.
|
|
52
|
+
2. Auth, refresh y normalización de error en interceptores, una sola vez.
|
|
53
|
+
3. Servicios tipados con `api.get<Data>(...)` que devuelven `.data`; el genérico es la data, no el envelope.
|
|
54
|
+
4. En `catch`, `axios.isAxiosError` antes de tocar `.response`; distingue error de red de error HTTP.
|
|
55
|
+
5. Requests cancelables (`AbortController`) donde puede haber carreras.
|
|
56
|
+
|
|
57
|
+
## Antes de declarar listo
|
|
58
|
+
|
|
59
|
+
- La llamada usa la instancia central; sin URLs absolutas ni headers de auth repetidos.
|
|
60
|
+
- Respuestas tipadas devolviendo `.data`; el error se maneja con `isAxiosError` y un shape único.
|
|
61
|
+
- Requests que compiten se cancelan en el cleanup.
|
|
62
|
+
- `{{qualityGate.fast}}` en verde.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mantine-form
|
|
3
|
+
description: Patrones de @mantine/form — useForm, getInputProps, validación con zodResolver, campos anidados y listas. Aplica al crear o tocar formularios con Mantine.
|
|
4
|
+
type: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Mantine Form — convenciones
|
|
8
|
+
|
|
9
|
+
## Cuándo usar este skill
|
|
10
|
+
|
|
11
|
+
Al crear o tocar un formulario con `@mantine/form`: cablear inputs de Mantine, validar, manejar submit, o campos anidados/listas. `useForm` es la fuente de verdad del form — no espejees sus valores en `useState`, y valida con un schema Zod (vía `mantine-form-zod-resolver`), no con funciones sueltas por campo.
|
|
12
|
+
|
|
13
|
+
## El patrón
|
|
14
|
+
|
|
15
|
+
`useForm` + `getInputProps` (esparce value/onChange/error de un jalón) + `zodResolver` para el schema:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
const schema = z.object({
|
|
19
|
+
email: z.string().email(),
|
|
20
|
+
role: z.enum(['coach', 'coachee']),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const form = useForm({
|
|
24
|
+
mode: 'uncontrolled', // menos re-renders; el default recomendado
|
|
25
|
+
initialValues: { email: '', role: 'coachee' },
|
|
26
|
+
validate: zodResolver(schema), // desde 'mantine-form-zod-resolver'
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
<form onSubmit={form.onSubmit((values) => save(values))}>
|
|
30
|
+
<TextInput {...form.getInputProps('email')} />
|
|
31
|
+
<Select data={['coach', 'coachee']} {...form.getInputProps('role')} />
|
|
32
|
+
<Button type="submit">Guardar</Button>
|
|
33
|
+
</form>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Gotchas que muerden
|
|
37
|
+
|
|
38
|
+
- **`getInputProps('campo')` cablea todo; no lo desarmes.** Ya trae `value`/`onChange`/`error`/`onBlur`. Pasar `value`/`onChange` a mano encima rompe el binding — deja que el spread mande.
|
|
39
|
+
- **`mode: 'uncontrolled'` cambia cómo lees valores.** En uncontrolled, `form.values` no re-renderiza al teclear; para reflejar un campo en la UI usa `form.watch('campo')` o `form.getValues()`. En `controlled` sí re-renderiza cada tecla (más caro en forms grandes).
|
|
40
|
+
- **Validación con `zodResolver`, no funciones por campo.** `validate: { email: (v) => … }` disemina reglas y tipos. Un schema Zod + `zodResolver` da una fuente única y el tipo por `z.infer`. Requiere el paquete `mantine-form-zod-resolver`.
|
|
41
|
+
- **Campos anidados/listas con notación de path.** `getInputProps('address.city')`, y listas con `form.insertListItem('items', {...})` / `form.removeListItem('items', i)` + `getInputProps('items.0.name')`. No manejes el array en `useState` aparte.
|
|
42
|
+
- **`initialValues` define el shape; llénalo completo.** Un campo ausente arranca `undefined` → warning uncontrolled→controlled. Para edición async usa `form.setValues(data)` / `form.initialize(data)` en un efecto, no valores a mano por render.
|
|
43
|
+
- **Submit con `form.onSubmit(handler)`.** Corre la validación y solo llama tu handler si pasa; además expone el segundo callback `(errors) => …` para enfocar el primer inválido. No valides "a mano" antes de enviar.
|
|
44
|
+
|
|
45
|
+
## Reglas duras
|
|
46
|
+
|
|
47
|
+
1. `useForm` es la única fuente del estado del form; nada de `useState` espejo.
|
|
48
|
+
2. Inputs cableados con `getInputProps`; no dupliques `value`/`onChange`.
|
|
49
|
+
3. Validación con schema Zod vía `zodResolver`; tipo por `z.infer`, sin reglas por campo.
|
|
50
|
+
4. `initialValues` completo; edición async con `setValues`/`initialize`.
|
|
51
|
+
5. Submit vía `form.onSubmit`; listas/anidados con la API de path, no arrays sueltos.
|
|
52
|
+
|
|
53
|
+
## Antes de declarar listo
|
|
54
|
+
|
|
55
|
+
- Todos los inputs usan `getInputProps`; sin estado espejo ni handlers duplicados.
|
|
56
|
+
- Validación centralizada en un schema Zod con `zodResolver`; tipos por `z.infer`.
|
|
57
|
+
- `initialValues` seteado; sin warnings uncontrolled→controlled. Submit con `form.onSubmit`.
|
|
58
|
+
- `{{qualityGate.fast}}` en verde.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-router
|
|
3
|
+
description: Patrones de React Router (v6/v7) — rutas anidadas, loaders, navegación, params y guards. Aplica al crear rutas, leer params, redirigir o proteger vistas.
|
|
4
|
+
type: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React Router — convenciones
|
|
8
|
+
|
|
9
|
+
## Cuándo usar este skill
|
|
10
|
+
|
|
11
|
+
Al tocar navegación: declarar una ruta, leer un param, redirigir, proteger una vista por rol, o cablear links. React Router es la fuente de verdad de **en qué URL estás y a dónde vas** — no dupliques la ruta en estado propio ni parsees `window.location` a mano.
|
|
12
|
+
|
|
13
|
+
## El patrón
|
|
14
|
+
|
|
15
|
+
Rutas anidadas con layout compartido vía `<Outlet />`; navegación por hooks, no por mutar `window.location`:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
const router = createBrowserRouter([
|
|
19
|
+
{
|
|
20
|
+
path: '/',
|
|
21
|
+
element: <AppLayout />, // renderiza <Outlet /> para los hijos
|
|
22
|
+
children: [
|
|
23
|
+
{ index: true, element: <Home /> },
|
|
24
|
+
{ path: 'sessions/:id', element: <SessionDetail /> },
|
|
25
|
+
{ path: '*', element: <NotFound /> },
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
function SessionDetail() {
|
|
31
|
+
const { id } = useParams(); // string | undefined, siempre
|
|
32
|
+
const navigate = useNavigate();
|
|
33
|
+
const [params, setParams] = useSearchParams();
|
|
34
|
+
// ...
|
|
35
|
+
navigate('/sessions', { replace: true }); // no <a href> manual
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Gotchas que muerden
|
|
40
|
+
|
|
41
|
+
- **`useParams()` siempre da `string | undefined`.** Nunca `number`. Convierte y valida (`Number(id)`, guard) antes de usarlo como id; una ruta mal tecleada no lanza, solo llega `undefined`.
|
|
42
|
+
- **Navegar imperativo con `useNavigate`, no `window.location`.** `window.location.href = …` recarga toda la SPA y tira el estado. Para volver: `navigate(-1)`; para redirigir sin dejar historial: `{ replace: true }`.
|
|
43
|
+
- **`<NavLink>` para tabs/menús, `<Link>` para el resto.** `NavLink` expone `isActive` en `className`/`style`/children; no reimplementes "está activo" comparando `pathname` a mano.
|
|
44
|
+
- **Search params son la URL, no `useState`.** Filtros/paginación viven en `useSearchParams` para que la vista sea linkeable y sobreviva al refresh. `setParams` reemplaza TODO el query — clona lo actual si solo cambias una clave.
|
|
45
|
+
- **Ruta protegida = un wrapper con `<Navigate>`, no un `if` suelto.** `if (!user) return <Navigate to="/login" replace />;` dentro de un guard/layout. Redirigir desde un `useEffect` parpadea la vista privada un frame.
|
|
46
|
+
- **Rutas relativas anidan; un `/` inicial las hace absolutas.** Dentro de `sessions/:id`, `navigate('edit')` va a `sessions/:id/edit`; `navigate('/edit')` va a la raíz. Es el error #1 al mover un componente de nivel.
|
|
47
|
+
|
|
48
|
+
## Reglas duras
|
|
49
|
+
|
|
50
|
+
1. Navegación por `useNavigate`/`<Link>`/`<NavLink>`; nunca `window.location` ni `<a href>` interno.
|
|
51
|
+
2. `useParams` se valida antes de usar (puede ser `undefined`); ids numéricos se convierten explícito.
|
|
52
|
+
3. Estado de filtros/paginación en `useSearchParams`, no en `useState` espejo.
|
|
53
|
+
4. Vistas protegidas por un guard con `<Navigate replace>`, no por `if` + efecto.
|
|
54
|
+
5. Layouts compartidos con rutas anidadas + `<Outlet />`; nada de repetir el chrome por página.
|
|
55
|
+
|
|
56
|
+
## Antes de declarar listo
|
|
57
|
+
|
|
58
|
+
- Sin `window.location`/`<a href>` para navegación interna; links con `<Link>`/`<NavLink>`.
|
|
59
|
+
- Params validados; el estado de la URL (filtros, tab) vive en search params.
|
|
60
|
+
- Rutas protegidas redirigen con `<Navigate replace>`; sin parpadeo de la vista privada.
|
|
61
|
+
- `{{qualityGate.fast}}` en verde.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "cognitive",
|
|
3
3
|
"name": "Cognitive complexity guardrails",
|
|
4
4
|
"description": "Detección de complejidad cognitiva alta via ESLint rule SonarJS",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.4",
|
|
6
6
|
"managed": [
|
|
7
7
|
{
|
|
8
8
|
"id": "cognitive-protocol",
|
|
@@ -34,12 +34,6 @@
|
|
|
34
34
|
"command": "bash .claude/scripts/check-cognitive.sh",
|
|
35
35
|
"timeout": 180,
|
|
36
36
|
"statusMessage": "navori/cognitive: complexity scan"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"event": "Stop",
|
|
40
|
-
"command": "bash .claude/scripts/check-cognitive.sh",
|
|
41
|
-
"timeout": 180,
|
|
42
|
-
"statusMessage": "navori/cognitive: complexity scan (cierre de sesión)"
|
|
43
37
|
}
|
|
44
38
|
]
|
|
45
39
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "jscpd",
|
|
3
3
|
"name": "jscpd (code duplication detector)",
|
|
4
4
|
"description": "Detección de duplicación de código en el diff vs branch base",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.4",
|
|
6
6
|
"managed": [
|
|
7
7
|
{
|
|
8
8
|
"id": "jscpd-protocol",
|
|
@@ -33,12 +33,6 @@
|
|
|
33
33
|
"command": "bash .claude/scripts/check-jscpd.sh",
|
|
34
34
|
"timeout": 180,
|
|
35
35
|
"statusMessage": "navori/jscpd: dup check"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"event": "Stop",
|
|
39
|
-
"command": "bash .claude/scripts/check-jscpd.sh",
|
|
40
|
-
"timeout": 180,
|
|
41
|
-
"statusMessage": "navori/jscpd: dup check (cierre de sesión)"
|
|
42
36
|
}
|
|
43
37
|
]
|
|
44
38
|
}
|
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
# `{{branchBase}}`. Skips silently if jscpd or git are absent — the tool is
|
|
4
4
|
# optional, not a hard dependency of the project.
|
|
5
5
|
#
|
|
6
|
-
# Triggered as a PreToolUse(Bash) hook
|
|
7
|
-
#
|
|
6
|
+
# Triggered as a PreToolUse(Bash) hook, gated to git commit/push — so the
|
|
7
|
+
# duplication check runs right before code lands, not on every turn.
|
|
8
8
|
|
|
9
9
|
set -euo pipefail
|
|
10
10
|
|
|
11
|
-
# PreToolUse(Bash) passes the command — gate to commit/push.
|
|
12
|
-
# passes no command — run unconditionally at session close. Extract without
|
|
11
|
+
# PreToolUse(Bash) passes the command — gate to commit/push. Extract without
|
|
13
12
|
# hard-depending on jq (not preinstalled on macOS): try jq, then node (Claude
|
|
14
13
|
# Code's own runtime), then a best-effort sed unwrap. No command extracted →
|
|
15
|
-
# empty $cmd → runs unconditionally.
|
|
14
|
+
# empty $cmd → runs unconditionally (defensive: never silently skip a commit).
|
|
16
15
|
payload=$(cat)
|
|
17
16
|
extract_cmd() {
|
|
18
17
|
if command -v jq >/dev/null 2>&1; then
|
|
@@ -69,7 +68,7 @@ jscpd \
|
|
|
69
68
|
--min-tokens 100 \
|
|
70
69
|
--min-lines 10 \
|
|
71
70
|
--mode strict \
|
|
72
|
-
--threshold
|
|
71
|
+
--threshold {{jscpdThreshold}} \
|
|
73
72
|
--reporters console \
|
|
74
73
|
--output "$tmpdir" \
|
|
75
74
|
"${files[@]}"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "semgrep",
|
|
3
3
|
"name": "semgrep (security + pattern analysis)",
|
|
4
4
|
"description": "Detección de vulnerabilidades y patrones inseguros local opt-in",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.4",
|
|
6
6
|
"managed": [
|
|
7
7
|
{
|
|
8
8
|
"id": "semgrep-protocol",
|
|
@@ -32,12 +32,6 @@
|
|
|
32
32
|
"command": "bash .claude/scripts/check-semgrep.sh",
|
|
33
33
|
"timeout": 180,
|
|
34
34
|
"statusMessage": "navori/semgrep: security scan"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"event": "Stop",
|
|
38
|
-
"command": "bash .claude/scripts/check-semgrep.sh",
|
|
39
|
-
"timeout": 180,
|
|
40
|
-
"statusMessage": "navori/semgrep: security scan (cierre de sesión)"
|
|
41
35
|
}
|
|
42
36
|
]
|
|
43
37
|
}
|