navori 0.2.0 → 0.2.2

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.
Files changed (24) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -17
  3. package/dist/assets/core/core-assets/agents/commit-pr-pilot.md +15 -9
  4. package/dist/assets/core/core-assets/lib-skills/formik.md +57 -0
  5. package/dist/assets/core/core-assets/lib-skills/joi-validation.md +73 -0
  6. package/dist/assets/core/core-assets/{presets/express-mongoose/skills → lib-skills}/mongoose.md +3 -2
  7. package/dist/assets/core/core-assets/lib-skills/redux-toolkit.md +59 -0
  8. package/dist/assets/core/core-assets/lib-skills/socketio.md +56 -0
  9. package/dist/assets/core/core-assets/lib-skills/tanstack-query.md +58 -0
  10. package/dist/assets/core/core-assets/managed/cierre-sesion.md +1 -1
  11. package/dist/assets/core/core-assets/managed/idioma-rol.md +1 -1
  12. package/dist/assets/core/core-assets/managed/operaciones-seguras.md +1 -0
  13. package/dist/assets/core/core-assets/presets/background-worker/managed/stack.md +18 -0
  14. package/dist/assets/core/core-assets/presets/background-worker/skills/job-scheduling.md +54 -0
  15. package/dist/assets/core/core-assets/presets/background-worker/skills/queue-consumers.md +55 -0
  16. package/dist/assets/core/core-assets/presets/background-worker/skills/worker-lifecycle.md +57 -0
  17. package/dist/assets/core/core-assets/presets/background-worker.json +39 -0
  18. package/dist/assets/core/core-assets/presets/express-mongoose/managed/stack.md +2 -2
  19. package/dist/assets/core/core-assets/presets/express-mongoose/skills/pr-create.md +18 -18
  20. package/dist/assets/core/core-assets/presets/express-mongoose.json +0 -12
  21. package/dist/assets/core/core-assets/settings/settings-base.json +22 -1
  22. package/dist/index.js +797 -346
  23. package/package.json +5 -4
  24. /package/dist/assets/core/core-assets/{presets/express-mongoose/skills → lib-skills}/zod-validation.md +0 -0
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: job-scheduling
3
+ description: Definir y agendar jobs en un worker (agenda / bullmq) — idempotencia, reintentos con backoff, concurrencia. Aplica al crear o tocar un job programado o recurrente.
4
+ type: reference
5
+ ---
6
+
7
+ # job-scheduling — jobs idempotentes y reintentables
8
+
9
+ Un job define **qué** hacer; el scheduler decide **cuándo** y **cuántas veces**. Como un job puede correr más de una vez (reintento, doble disparo), el handler debe ser idempotente.
10
+
11
+ ## Cuándo usar este skill
12
+
13
+ Al definir un job nuevo, agendar uno recurrente, o ajustar reintentos/concurrencia.
14
+
15
+ ## El patrón (agenda)
16
+
17
+ Un archivo por job (`<name>.job.ts`) que registra el handler; el scheduling vive aparte del handler.
18
+
19
+ ```ts
20
+ export function defineSyncJob(agenda: Agenda) {
21
+ agenda.define('sync-user', { concurrency: 5, lockLifetime: 60_000 }, async (job) => {
22
+ const { userId } = job.attrs.data as { userId: string };
23
+ // idempotente: chequea estado antes de actuar
24
+ if (await alreadySynced(userId, job.attrs.lastRunAt)) return;
25
+ await syncUser(userId);
26
+ });
27
+ }
28
+
29
+ // scheduling, separado del handler:
30
+ await agenda.every('0 * * * *', 'sync-user', { userId }); // recurrente
31
+ await agenda.schedule('in 5 minutes', 'sync-user', { userId }); // one-off
32
+ ```
33
+
34
+ bullmq es equivalente: `new Worker(name, handler, { concurrency })` + `queue.add(name, data, { repeat, attempts, backoff })`.
35
+
36
+ ## Gotchas que muerden
37
+
38
+ - **Doble disparo**: dos instancias del worker pueden tomar el mismo job. agenda usa `lockLifetime`; bullmq usa locks por job. Aun así, **el handler debe ser idempotente** — no confíes solo en el lock.
39
+ - **Reintentos sin backoff** martillan un servicio caído. Configura `attempts` + `backoff` exponencial.
40
+ - **`lockLifetime` corto** + job largo → el lock expira y otro worker lo retoma en paralelo. Ajústalo por encima de la duración real del job.
41
+
42
+ ## Reglas duras
43
+
44
+ 1. Un job por archivo `<name>.job.ts`; handler separado del scheduling.
45
+ 2. Handler **idempotente**: chequea estado antes de mutar; usa upserts/claves de dedup.
46
+ 3. Reintentos con backoff exponencial y un tope (`attempts`); sin reintento infinito.
47
+ 4. `concurrency` y `lockLifetime` explícitos y coherentes con la duración del job.
48
+ 5. Nada de trabajo no idempotente que dependa de "correr exactamente una vez".
49
+
50
+ ## Antes de declarar listo
51
+
52
+ - El handler es seguro de re-ejecutar (probado corriéndolo dos veces).
53
+ - Reintentos con backoff y tope configurados.
54
+ - `{{qualityGate.fast}}` en verde.
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: queue-consumers
3
+ description: Consumir mensajes de una cola en un worker (amqplib / bullmq) — ack/nack, dead-letter, prefetch/backpressure, idempotencia. Aplica al crear o tocar un consumidor de cola.
4
+ type: reference
5
+ ---
6
+
7
+ # queue-consumers — consumir sin perder ni duplicar
8
+
9
+ Un consumer reacciona a mensajes. La regla central: **un mensaje no se confirma (`ack`) hasta que se procesó con éxito**; si falla, se re-encola o va a dead-letter — nunca se pierde en silencio.
10
+
11
+ ## Cuándo usar este skill
12
+
13
+ Al crear un consumer, manejar fallos de procesamiento, o ajustar prefetch / dead-letter.
14
+
15
+ ## El patrón (amqplib)
16
+
17
+ ```ts
18
+ await channel.prefetch(10); // backpressure: máx 10 sin ack a la vez
19
+ await channel.consume(queue, async (msg) => {
20
+ if (!msg) return;
21
+ try {
22
+ const payload = JSON.parse(msg.content.toString());
23
+ if (await alreadyProcessed(payload.id)) { channel.ack(msg); return; } // idempotente
24
+ await handle(payload);
25
+ channel.ack(msg);
26
+ } catch (err) {
27
+ logger.error({ err }, 'consume failed');
28
+ // requeue una vez; si ya fue redelivered, mándalo a la DLQ (no requeue infinito)
29
+ channel.nack(msg, false, !msg.fields.redelivered);
30
+ }
31
+ });
32
+ ```
33
+
34
+ bullmq: lanzar dentro del `Worker` handler re-encola según `attempts`/`backoff`; al agotarse, el job queda `failed` (tu DLQ lógica).
35
+
36
+ ## Gotchas que muerden
37
+
38
+ - **`nack` con `requeue: true` siempre** → loop infinito si el mensaje es venenoso. Requeue una vez (chequea `redelivered`), luego dead-letter.
39
+ - **Sin `prefetch`** el consumer traga toda la cola en memoria. Fija un prefetch acorde a la duración del handler.
40
+ - **`ack` antes de procesar** = pérdida de mensajes si el handler crashea. Confirma **después** del éxito.
41
+ - **Mensajes duplicados** son normales (redelivery). El handler debe ser idempotente.
42
+
43
+ ## Reglas duras
44
+
45
+ 1. `ack` solo tras éxito; en fallo, `nack`/requeue acotado o dead-letter.
46
+ 2. Nunca requeue infinito de un mensaje venenoso — DLQ tras el primer redelivery.
47
+ 3. `prefetch` explícito para backpressure.
48
+ 4. Handler **idempotente**: chequea dedup antes de actuar.
49
+ 5. Errores logueados (estructurado), nunca tragados en silencio.
50
+
51
+ ## Antes de declarar listo
52
+
53
+ - Un mensaje que falla no se pierde ni hace loop infinito (va a DLQ).
54
+ - `prefetch` configurado; el handler es idempotente.
55
+ - `{{qualityGate.fast}}` en verde.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: worker-lifecycle
3
+ description: Ciclo de vida de un worker de fondo en Node/TS — bootstrap, graceful shutdown, healthcheck mínimo, sin servir HTTP de negocio. Aplica al tocar el arranque/apagado del proceso o la conexión a DB/broker.
4
+ type: reference
5
+ ---
6
+
7
+ # worker-lifecycle — arrancar y apagar limpio
8
+
9
+ Un worker no es un HTTP server: arranca conexiones, registra schedulers/consumers, y debe **apagar limpio** sin matar trabajo en vuelo. Su `main` orquesta el bootstrap y un único shutdown idempotente.
10
+
11
+ ## Cuándo usar este skill
12
+
13
+ Al tocar `index.ts`/`main.ts`, el arranque del scheduler/consumer, la conexión a Mongo/broker, o el manejo de señales.
14
+
15
+ ## El patrón
16
+
17
+ ```ts
18
+ async function main() {
19
+ const db = await connectMongo(config.mongoUri);
20
+ const broker = await connectBroker(config.amqpUrl);
21
+ const scheduler = startScheduler({ db }); // job-scheduling
22
+ const consumer = startConsumer({ broker }); // queue-consumers
23
+
24
+ const shutdown = once(async (signal: string) => {
25
+ logger.info({ signal }, 'shutting down');
26
+ await consumer.stop(); // deja de tomar mensajes nuevos
27
+ await scheduler.stop(); // deja de disparar jobs
28
+ await drainInflight(15_000); // espera lo en vuelo, con timeout
29
+ await broker.close();
30
+ await db.close();
31
+ process.exit(0);
32
+ });
33
+
34
+ for (const sig of ['SIGTERM', 'SIGINT'] as const) process.on(sig, () => shutdown(sig));
35
+ }
36
+ main().catch((err) => { logger.error({ err }, 'fatal on boot'); process.exit(1); });
37
+ ```
38
+
39
+ `once` garantiza que dos señales seguidas no disparen dos shutdowns. El orden importa: **primero dejas de aceptar trabajo**, luego drenas lo en vuelo, luego cierras conexiones.
40
+
41
+ ## Healthcheck (si el orquestador lo exige)
42
+
43
+ Un solo endpoint `/health` con un `http.createServer` mínimo está bien — **no es** una API. Devuelve `200` si las conexiones (DB, broker) están vivas. Nada de rutas de negocio aquí.
44
+
45
+ ## Reglas duras
46
+
47
+ 1. Un único punto de shutdown, idempotente (`once`), escuchando `SIGTERM` y `SIGINT`.
48
+ 2. Dejar de aceptar trabajo **antes** de drenar; drenar con timeout; cerrar conexiones al final.
49
+ 3. Nunca `process.exit` a mitad de un job sin re-encolarlo o dejarlo `nack`-eado.
50
+ 4. Sin rutas HTTP de negocio. `/health` es el único endpoint permitido.
51
+ 5. Errores de arranque → log estructurado + `exit(1)`; no arranques a medias.
52
+
53
+ ## Antes de declarar listo
54
+
55
+ - `SIGTERM` apaga limpio: sin jobs muertos a la mitad, conexiones cerradas.
56
+ - El proceso no expone endpoints de negocio.
57
+ - `{{qualityGate.fast}}` en verde.
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://navori.dev/schema/navori.preset.v1.json",
3
+ "id": "background-worker",
4
+ "displayName": "Background worker (jobs + queues)",
5
+ "extends": "core",
6
+ "extras": {
7
+ "managed": [
8
+ {
9
+ "id": "stack-background-worker",
10
+ "relPath": "presets/background-worker/managed/stack.md"
11
+ }
12
+ ],
13
+ "agents": [],
14
+ "skills": [
15
+ {
16
+ "id": "worker-lifecycle",
17
+ "relPath": "presets/background-worker/skills/worker-lifecycle.md",
18
+ "destRelPath": ".claude/skills/worker-lifecycle.md"
19
+ },
20
+ {
21
+ "id": "job-scheduling",
22
+ "relPath": "presets/background-worker/skills/job-scheduling.md",
23
+ "destRelPath": ".claude/skills/job-scheduling.md"
24
+ },
25
+ {
26
+ "id": "queue-consumers",
27
+ "relPath": "presets/background-worker/skills/queue-consumers.md",
28
+ "destRelPath": ".claude/skills/queue-consumers.md"
29
+ }
30
+ ],
31
+ "hooks": []
32
+ },
33
+ "invariants": [
34
+ "worker-lifecycle",
35
+ "job-scheduling",
36
+ "queue-consumers",
37
+ "graceful shutdown"
38
+ ]
39
+ }
@@ -1,8 +1,8 @@
1
1
  ## Stack — Express + Mongoose
2
2
 
3
- Backend HTTP sobre Express + Mongoose/MongoDB en TypeScript. Las peticiones fluyen en capas: `route → validate(Zod) → asyncHandler → controller → Model (Mongoose) → ApiResponse`. Los controllers tocan los Models directo (sin repository wrappers); los errores se propagan vía `ApiError` y las respuestas se envuelven en `ApiResponse`. El logging va por el `Logger` de winston, nunca `console.log`.
3
+ Backend HTTP sobre Express + Mongoose/MongoDB en TypeScript. Las peticiones fluyen en capas: `route → validate(schema) → asyncHandler → controller → Model (Mongoose) → ApiResponse`. Los controllers tocan los Models directo (sin repository wrappers); los errores se propagan vía `ApiError` y las respuestas se envuelven en `ApiResponse`. El logging va por el `Logger` de winston, nunca `console.log`.
4
4
 
5
- Regla de oro: nada de `res.json` / `res.status(500)` crudos; nada de `console.log`; nada de `process.env` fuera del módulo de config. La validación SIEMPRE ocurre en el boundary con Zod, y todo `ObjectId` se construye con `new Types.ObjectId(...)`. Aplica las skills `express-routes`, `mongoose`, `zod-validation`, `mongo-aggregations` y `winston-logging` según la capa que toques.
5
+ Regla de oro: nada de `res.json` / `res.status(500)` crudos; nada de `console.log`; nada de `process.env` fuera del módulo de config. La validación SIEMPRE ocurre en el boundary (con el validador del repo — Zod o Joi), y todo `ObjectId` se construye con `new Types.ObjectId(...)`. Aplica las skills `express-routes`, `mongo-aggregations` y `winston-logging` del preset según la capa que toques. Las skills de `mongoose` y de validación (`zod-validation` o `joi-validation`) se inyectan según las dependencias que detecte navori en el repo — si están en `.claude/skills/`, aplícalas.
6
6
 
7
7
  El trabajo de un ticket sigue el pipeline documentado en la skill `ticket-intake` (la orquestadora). No es un generador de specs: es un protocolo que el `leader` ejecuta invocando agentes y skills en orden, con gates objetivos y artefactos en `.claude/progress/`. Mapeo de fases a la infraestructura de navori:
8
8
 
@@ -1,26 +1,29 @@
1
1
  ---
2
2
  name: pr-create
3
- description: Crea un PR en GitHub contra la rama base del repo, delegando title/body a un modelo y validando el output. Antes de crear cualquier PR al cerrar el ciclo de un ticket (Fase 8 de ticket-intake).
3
+ description: Crea un PR en GitHub contra la rama target del repo, delegando title/body a un modelo y validando el output. Antes de crear cualquier PR al cerrar el ciclo de un ticket (Fase 8 de ticket-intake).
4
4
  type: reference
5
5
  ---
6
6
 
7
7
  <!-- candidate: workflow-backend -->
8
8
 
9
- # pr-create — crear PR contra `{{branchBase}}`
9
+ # pr-create — crear PR contra `{{prTarget}}`
10
10
 
11
11
  ## Cuándo usar este skill
12
12
 
13
- Al cierre del ciclo, tras el `APPROVED` del reviewer. Es la Fase 8 del pipeline `ticket-intake`. El pre-flight es bloqueante: ningún PR sale si el gate no está verde o si falta aprobación cuando el harness está activo.
13
+ Al cierre del ciclo, tras el `APPROVED` del reviewer (Fase 8 de `ticket-intake`). El pre-flight es bloqueante: ningún PR sale con el gate en rojo o sin aprobación (harness activo).
14
+
15
+ `{{branchBase}}` es el punto de fork; `{{prTarget}}` es la rama destino del PR. Cuando difieren, el PR y su diff van contra `{{prTarget}}`.
14
16
 
15
17
  ## Pre-flight (bloqueante — cualquier check falla → ABORT con mensaje claro)
16
18
 
17
19
  1. **Working tree limpio** o solo cambios del feature: `git status --porcelain`. Si hay cambios uncommitted que NO son del feature, párate y pregunta.
18
- 2. **No estás en rama base**: aborta si la rama es `main|master|{{branchBase}}`.
20
+ 2. **No estás en una rama protegida**: aborta si la rama actual es `main|master|{{branchBase}}|{{prTarget}}`.
19
21
  3. **`gh` autenticado**: `gh auth status`.
20
22
  4. **Gate verde en este turno**: `{{qualityGate.fast}}`. Falla → manda al implementer.
21
23
  5. **Review APPROVED** (si el harness está activo): `grep -q APPROVED .claude/progress/review_*.md`. Falla → manda al reviewer.
22
- 6. **Hay commits pusheables**: `git log origin/{{branchBase}}..HEAD --oneline`. Vacío → nada que PR-ear.
23
- 7. **Branch sincronizada**: `git fetch origin {{branchBase}}` y `git log HEAD..origin/{{branchBase}} --oneline`. Si origin va adelante, considera rebase y pregunta al usuario.
24
+ 6. **Hay commits para el PR**: `git fetch origin {{prTarget}} --quiet` y `git log origin/{{prTarget}}..HEAD --oneline`. Vacío → nada que PR-ear.
25
+ 7. **Branch sincronizada**: `git log HEAD..origin/{{prTarget}} --oneline`. Si origin va adelante, rebase y pregunta.
26
+ 8. **Arrastre de commits** (solo si `{{branchBase}}` ≠ `{{prTarget}}`): `git rev-list --count origin/{{prTarget}}..origin/{{branchBase}}`. Si es > 0, `{{branchBase}}` va adelantado de `{{prTarget}}` y el PR arrastra esos commits ajenos: avisa y sugiere `git rebase origin/{{prTarget}}` antes de abrir.
24
27
 
25
28
  ## Push y redacción
26
29
 
@@ -28,32 +31,29 @@ Si la branch no está pusheada: `git push -u origin "$current_branch"`. Si el up
28
31
 
29
32
  Delega title + body a un `Agent` (modelo rápido). Reglas que le impones:
30
33
  - **Title**: Conventional Commit con scope de tu dominio, ≤70 chars, español MX, sin emojis ni puntuación final.
31
- - **Body**: markdown con secciones fijas `## Resumen`, `## Cambios`, `## Test plan`, `## Notas` (opcional). Tono directo, sin "Generated by Claude".
32
- - **Contexto al redactor**: branch, base `{{branchBase}}`, ticket, `impl_<feature>.md`, `review_<feature>.md`, `git log {{branchBase}}..HEAD --oneline` y `--stat`.
34
+ - **Body**: secciones fijas `## Resumen`, `## Cambios`, `## Test plan`, `## Notas` (opcional). Tono directo, sin "Generated by Claude".
35
+ - **Contexto al redactor**: branch, base `{{prTarget}}`, ticket, `impl_<feature>.md`, `review_<feature>.md`, y el diff REAL del PR — `git log origin/{{prTarget}}..HEAD --oneline` + `git diff origin/{{prTarget}}...HEAD --stat`.
33
36
  - **Output**: JSON exacto `{"title": "...", "body": "..."}` sin fences.
34
37
 
35
- Valida el output: `title` ≤70 y matchea `^(feat|fix|refactor|chore|docs|style|test|build|ci|perf)(\(.+\))?: `; `body` contiene Resumen, Cambios y Test plan. Si falla, re-invoca al redactor con el feedback (máx. 2 reintentos).
38
+ Valida: `title` ≤70 y Conventional Commit; `body` con Resumen, Cambios y Test plan. Si falla, reintenta (máx. 2).
36
39
 
37
40
  ## Crear el PR
38
41
 
39
42
  ```bash
40
- gh pr create --base {{branchBase}} --title "$title" --body "$body"
43
+ gh pr create --base {{prTarget}} --title "$title" --body "$body"
41
44
  ```
42
45
 
43
46
  Captura la URL del output y muéstrasela al usuario.
44
47
 
45
48
  ## Reglas duras
46
49
 
47
- - **NUNCA `git push --force`** sin que lo pida el usuario.
48
- - **NUNCA `--base main` ni `--base master`.** Siempre `--base {{branchBase}}`.
49
- - **NUNCA `--no-verify`** en commits previos.
50
+ - **NUNCA `git push --force`** ni `--no-verify` sin que lo pida el usuario.
51
+ - **Siempre `--base {{prTarget}}`.** No lo cambies a mano; si el target del repo cambió, ajústalo con `navori configure pr-target`.
50
52
  - **Cambios uncommitted ajenos al feature** → párate y pregunta, no los arrastres al PR.
51
- - **No agregues** "Generated with Claude" ni emojis al body.
52
53
  - **Reviewers/labels**: no los agregues automático salvo que el usuario lo pida.
53
54
 
54
55
  ## Antes de declarar listo
55
56
 
56
- - El pre-flight pasó completo (gate verde, branch ≠ base, commits pusheables, review APPROVED si aplica).
57
- - El title pasó la validación Conventional Commit (≤70 chars) y el body tiene las secciones fijas.
58
- - El PR se creó contra `{{branchBase}}` y su URL quedó mostrada al usuario.
59
- - Post-PR: `mem_save` de cualquier decisión no obvia, entrada en `history.md`, `current.md` a `state: idle`, y `mem_session_summary`.
57
+ - El pre-flight pasó completo y el title/body validaron.
58
+ - El PR se creó contra `{{prTarget}}` y su URL quedó mostrada al usuario.
59
+ - Post-PR: `mem_save` de decisiones no obvias, entrada en `history.md`, `current.md` a `state: idle`, y `mem_session_summary`.
@@ -17,16 +17,6 @@
17
17
  "relPath": "presets/express-mongoose/skills/express-routes.md",
18
18
  "destRelPath": ".claude/skills/express-routes.md"
19
19
  },
20
- {
21
- "id": "mongoose",
22
- "relPath": "presets/express-mongoose/skills/mongoose.md",
23
- "destRelPath": ".claude/skills/mongoose.md"
24
- },
25
- {
26
- "id": "zod-validation",
27
- "relPath": "presets/express-mongoose/skills/zod-validation.md",
28
- "destRelPath": ".claude/skills/zod-validation.md"
29
- },
30
20
  {
31
21
  "id": "mongo-aggregations",
32
22
  "relPath": "presets/express-mongoose/skills/mongo-aggregations.md",
@@ -66,8 +56,6 @@
66
56
  "ApiResponse",
67
57
  "mongoose",
68
58
  "Types.ObjectId",
69
- "zod-validation",
70
- "z.coerce",
71
59
  "ticket-intake",
72
60
  "new-resource"
73
61
  ]
@@ -14,6 +14,18 @@
14
14
  "Bash(git branch*)",
15
15
  "Bash(git fetch*)",
16
16
  "Bash(git stash*)",
17
+ "Bash(git blame*)",
18
+ "Bash(git describe*)",
19
+ "Bash(git rev-parse*)",
20
+ "Bash(git ls-files*)",
21
+ "Bash(git cat-file*)",
22
+ "Bash(git shortlog*)",
23
+ "Bash(git tag -l*)",
24
+ "Bash(git tag --list*)",
25
+ "Bash(git config --get*)",
26
+ "Bash(git config --list*)",
27
+ "Bash(git remote -v*)",
28
+ "Bash(git remote get-url*)",
17
29
  "Read",
18
30
  "Glob",
19
31
  "Grep",
@@ -26,7 +38,16 @@
26
38
  "Bash(which:*)",
27
39
  "Bash(file:*)",
28
40
  "Bash(stat:*)",
29
- "Bash(tree:*)"
41
+ "Bash(tree:*)",
42
+ "Bash(grep:*)",
43
+ "Bash(jq:*)",
44
+ "Bash(diff:*)",
45
+ "Bash(cut:*)",
46
+ "Bash(du:*)",
47
+ "Bash(df:*)",
48
+ "Bash(realpath:*)",
49
+ "Bash(dirname:*)",
50
+ "Bash(basename:*)"
30
51
  ],
31
52
  "ask": [
32
53
  "Bash(rm -rf *)",