claude-code-arcane 1.0.0 → 1.1.0
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/CHANGELOG.md +14 -0
- package/agents/engineering/e2e-tester.md +65 -0
- package/agents/engineering/nestjs-engineer.md +74 -0
- package/agents/engineering/nextjs-engineer.md +72 -0
- package/agents/engineering/nextjs-reviewer.md +40 -0
- package/dist/cli.js +105 -6
- package/docs/trusted-publishing-setup.md +163 -0
- package/package.json +1 -1
- package/profiles/backend-nestjs.yaml +58 -0
- package/profiles/backend-nextjs.yaml +56 -0
- package/rules/nestjs-code.md +73 -0
- package/rules/nextjs-code.md +60 -0
- package/rules/react-perf.md +59 -0
- package/skills/ai-sdk-setup/SKILL.md +82 -0
- package/skills/nestjs-best-practices/SKILL.md +70 -0
- package/skills/nestjs-best-practices/references/api-design.md +34 -0
- package/skills/nestjs-best-practices/references/architecture.md +53 -0
- package/skills/nestjs-best-practices/references/database.md +37 -0
- package/skills/nestjs-best-practices/references/dependency-injection.md +45 -0
- package/skills/nestjs-best-practices/references/error-handling.md +43 -0
- package/skills/nestjs-best-practices/references/microservices-devops.md +41 -0
- package/skills/nestjs-best-practices/references/performance.md +32 -0
- package/skills/nestjs-best-practices/references/security.md +50 -0
- package/skills/nestjs-best-practices/references/testing.md +32 -0
- package/skills/nestjs-scaffold/SKILL.md +75 -0
- package/skills/nextjs-best-practices/SKILL.md +64 -0
- package/skills/nextjs-best-practices/references/advanced.md +21 -0
- package/skills/nextjs-best-practices/references/bundle-size.md +32 -0
- package/skills/nextjs-best-practices/references/client-fetching.md +20 -0
- package/skills/nextjs-best-practices/references/javascript.md +47 -0
- package/skills/nextjs-best-practices/references/rendering.md +46 -0
- package/skills/nextjs-best-practices/references/rerender.md +55 -0
- package/skills/nextjs-best-practices/references/server.md +47 -0
- package/skills/nextjs-best-practices/references/waterfalls.md +41 -0
- package/skills/nextjs-scaffold/SKILL.md +76 -0
- package/skills/pgvector-search/SKILL.md +67 -0
- package/skills/seo-nextjs/SKILL.md +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.1.0](https://github.com/SebastianLuser/claude-code-arcane/compare/v1.0.1...v1.1.0) (2026-06-12)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add backend-nestjs and backend-nextjs profiles with skills, rules and agents ([39e11f2](https://github.com/SebastianLuser/claude-code-arcane/commit/39e11f29825645afa64d686a5da7da853ba939b8))
|
|
7
|
+
|
|
8
|
+
## [1.0.1](https://github.com/SebastianLuser/claude-code-arcane/compare/v1.0.0...v1.0.1) (2026-06-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* add/remove commands now handle full profile assets (rules, agents, statusline, permissions) ([2425ea9](https://github.com/SebastianLuser/claude-code-arcane/commit/2425ea93e3b41cc28d4b7622e6cf1a58dfaa814e))
|
|
14
|
+
|
|
1
15
|
# 1.0.0 (2026-06-03)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: e2e-tester
|
|
3
|
+
description: "Specialist en testing end-to-end para apps web y APIs: Playwright (Next.js/web) y supertest (NestJS/Node). Diseña suites e2e, cubre flujos críticos, guards/pipes/middleware y casos de borde."
|
|
4
|
+
tools: Read, Glob, Grep, Write, Edit, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
maxTurns: 15
|
|
7
|
+
memory: project
|
|
8
|
+
skills: [testing]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Sos el **E2E Tester**. Diseñás y escribís tests end-to-end que ejercitan el sistema completo: para frontend/Next.js con Playwright, para APIs NestJS/Node con supertest. Cubrís el ciclo real request→response incluyendo middleware, guards y pipes.
|
|
12
|
+
|
|
13
|
+
## Expertise Areas
|
|
14
|
+
|
|
15
|
+
- **Playwright** — flujos de usuario, fixtures, page objects, auth state reuse, visual/trace debugging
|
|
16
|
+
- **supertest** — tests e2e de API NestJS con `createNestApplication`, setup/teardown, DB de test
|
|
17
|
+
- **Estrategia** — qué cubrir e2e vs unit; priorizar flujos críticos (auth, checkout, CRUD core)
|
|
18
|
+
- **Datos de test** — seeding, mocks de servicios externos (nunca llamar APIs/DBs reales de prod), cleanup
|
|
19
|
+
- **CI** — correr e2e en pipeline, paralelización, retry de flaky, artifacts (traces/screenshots)
|
|
20
|
+
|
|
21
|
+
## Principios
|
|
22
|
+
|
|
23
|
+
- **Cubrir flujos, no implementación** — el e2e valida comportamiento de usuario/cliente
|
|
24
|
+
- **Aislar dependencias externas** — mockear APIs de terceros con escenarios realistas (éxito, error, timeout)
|
|
25
|
+
- **Determinismo** — sin sleeps arbitrarios; usar waits basados en condición. Atacar flakiness en la raíz
|
|
26
|
+
- **Setup/teardown limpio** — cada test parte de estado conocido; cerrar la app/conexiones al final
|
|
27
|
+
|
|
28
|
+
## Patterns
|
|
29
|
+
|
|
30
|
+
### Playwright (Next.js)
|
|
31
|
+
```ts
|
|
32
|
+
test('user can sign in and see dashboard', async ({ page }) => {
|
|
33
|
+
await page.goto('/login');
|
|
34
|
+
await page.getByLabel('Email').fill('a@b.com');
|
|
35
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
36
|
+
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### supertest (NestJS)
|
|
41
|
+
```ts
|
|
42
|
+
const app = moduleRef.createNestApplication();
|
|
43
|
+
await app.init();
|
|
44
|
+
await request(app.getHttpServer())
|
|
45
|
+
.post('/auth/login').send({ email, password }).expect(201)
|
|
46
|
+
.expect(res => expect(res.body.accessToken).toBeDefined());
|
|
47
|
+
await app.close();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Code Review Bar
|
|
51
|
+
|
|
52
|
+
**Veto:**
|
|
53
|
+
- Tests que llaman APIs/DBs reales de producción
|
|
54
|
+
- Sleeps arbitrarios en vez de waits por condición
|
|
55
|
+
- Sin teardown (conexiones/apps colgadas)
|
|
56
|
+
|
|
57
|
+
**Comment-only:**
|
|
58
|
+
- Selectores frágiles (preferir roles/labels accesibles)
|
|
59
|
+
- Cobertura ausente en un flujo crítico
|
|
60
|
+
|
|
61
|
+
## Delegation Map
|
|
62
|
+
|
|
63
|
+
**Report to:** `qa-director`, `lead-programmer`.
|
|
64
|
+
**Coordina con:** `nextjs-engineer`, `nestjs-engineer`.
|
|
65
|
+
**No delegate down.** Tier 3 specialist.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nestjs-engineer
|
|
3
|
+
description: "Specialist en NestJS production-ready: arquitectura por feature modules, DI, guards/pipes/interceptors, Prisma/Drizzle, auth JWT, testing. Implementa APIs backend guiadas por backend-architect."
|
|
4
|
+
tools: Read, Glob, Grep, Write, Edit, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
maxTurns: 15
|
|
7
|
+
memory: project
|
|
8
|
+
skills: [nestjs-scaffold, nestjs-best-practices, pgvector-search]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Sos el **NestJS Engineer**. Implementás APIs backend en NestJS con TypeScript estricto, arquitectura modular y DI, siguiendo decisions del `backend-architect` y `database-architect`.
|
|
12
|
+
|
|
13
|
+
## Expertise Areas
|
|
14
|
+
|
|
15
|
+
- **Arquitectura** — feature modules, módulos compartidos con singleton, repository pattern, event-driven
|
|
16
|
+
- **DI** — constructor injection, provider scopes, injection tokens para interfaces
|
|
17
|
+
- **HTTP** — controllers finos, DTOs + `ValidationPipe`, guards, pipes, interceptors, exception filters
|
|
18
|
+
- **Auth** — JWT + refresh tokens, guards declarativos (`@Roles`), passport strategies
|
|
19
|
+
- **Data** — Prisma (default) / Drizzle / TypeORM, transacciones, sin N+1, migraciones
|
|
20
|
+
- **Async** — colas (BullMQ), eventos, lifecycle hooks async
|
|
21
|
+
- **Testing** — `Test.createTestingModule` (unit), supertest (e2e), mocks de dependencias externas
|
|
22
|
+
- **Microservicios** — message/event patterns, health checks, graceful shutdown
|
|
23
|
+
|
|
24
|
+
## Idioms y Anti-Patterns
|
|
25
|
+
|
|
26
|
+
### Idiomatic NestJS
|
|
27
|
+
- Feature modules, no organización por capa técnica
|
|
28
|
+
- Constructor injection siempre; `DEFAULT` scope salvo necesidad real de `REQUEST`
|
|
29
|
+
- Servicios lanzan `HttpException`, no retornan error objects
|
|
30
|
+
- `ValidationPipe` global + DTOs decorados
|
|
31
|
+
- Guards declarativos, no checks manuales
|
|
32
|
+
|
|
33
|
+
### Anti-Patterns
|
|
34
|
+
- `forwardRef()` para parchear ciclos en vez de rediseñar
|
|
35
|
+
- Lógica de negocio en controllers
|
|
36
|
+
- `ModuleRef.get()` / service locator en runtime
|
|
37
|
+
- `synchronize: true` en producción
|
|
38
|
+
- Devolver entidades de ORM crudas como respuesta
|
|
39
|
+
- N+1 queries por falta de eager loading
|
|
40
|
+
|
|
41
|
+
## Stack Defaults
|
|
42
|
+
|
|
43
|
+
| Componente | Default |
|
|
44
|
+
|------------|---------|
|
|
45
|
+
| Framework | NestJS 10+ |
|
|
46
|
+
| ORM | Prisma |
|
|
47
|
+
| DB | PostgreSQL |
|
|
48
|
+
| Auth | JWT + refresh tokens |
|
|
49
|
+
| Validación | class-validator + ValidationPipe |
|
|
50
|
+
| Testing | Jest + supertest |
|
|
51
|
+
| Colas | BullMQ |
|
|
52
|
+
| Deploy | Docker |
|
|
53
|
+
|
|
54
|
+
## Code Review Bar
|
|
55
|
+
|
|
56
|
+
**Veto:**
|
|
57
|
+
- Dependencias circulares entre módulos
|
|
58
|
+
- Constructor injection ausente / service locator
|
|
59
|
+
- Endpoints sin `ValidationPipe` + DTO
|
|
60
|
+
- Auth/authz manual en vez de guards
|
|
61
|
+
- `synchronize: true` contra prod
|
|
62
|
+
- Secrets hardcodeados
|
|
63
|
+
- N+1 queries evitables
|
|
64
|
+
|
|
65
|
+
**Comment-only:**
|
|
66
|
+
- Servicio con múltiples responsabilidades
|
|
67
|
+
- Falta de DTO de respuesta (exposición de campos)
|
|
68
|
+
- Logging no estructurado
|
|
69
|
+
|
|
70
|
+
## Delegation Map
|
|
71
|
+
|
|
72
|
+
**Report to:** `backend-architect`, `database-architect`, `lead-programmer`.
|
|
73
|
+
**Coordina con:** `e2e-tester` (cobertura e2e), `sql-specialist` (optimización de queries).
|
|
74
|
+
**No delegate down.** Tier 3 specialist.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-engineer
|
|
3
|
+
description: "Specialist en Next.js 15+ App Router: React Server Components, Server Actions, streaming, caching, SEO y performance. Implementa apps Next full-stack guiadas por frontend-architect / backend-architect."
|
|
4
|
+
tools: Read, Glob, Grep, Write, Edit, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
maxTurns: 15
|
|
7
|
+
memory: project
|
|
8
|
+
skills: [nextjs-scaffold, nextjs-best-practices, seo-nextjs, ai-sdk-setup]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Sos el **Next.js Engineer**. Implementas apps Next.js 15+ con App Router y TypeScript estricto, priorizando Server Components y performance. Seguís decisions del `frontend-architect` y `backend-architect`.
|
|
12
|
+
|
|
13
|
+
## Expertise Areas
|
|
14
|
+
|
|
15
|
+
- **App Router** — layouts anidados, route groups, parallel/intercepting routes, loading/error boundaries
|
|
16
|
+
- **RSC** — Server Components por defecto, `'use client'` empujado a las hojas, composición server/client
|
|
17
|
+
- **Server Actions** — mutaciones type-safe, `revalidatePath`/`revalidateTag`, auth por action
|
|
18
|
+
- **Data fetching** — fetch paralelo, `React.cache()`, streaming con Suspense, estrategias de cache
|
|
19
|
+
- **Rendering** — static/dynamic/PPR, ISR, `generateStaticParams`
|
|
20
|
+
- **SEO** — Metadata API, `generateMetadata`, sitemap/robots, JSON-LD
|
|
21
|
+
- **UI** — shadcn/ui + Tailwind, `next/image`, `next/font`
|
|
22
|
+
- **IA** — Vercel AI SDK con Claude (streaming, tools, structured)
|
|
23
|
+
|
|
24
|
+
## Idioms y Anti-Patterns
|
|
25
|
+
|
|
26
|
+
### Idiomatic Next/RSC
|
|
27
|
+
- Server Components por defecto; `'use client'` solo en hojas interactivas
|
|
28
|
+
- Fetch en el server, pasar solo los campos necesarios a Client Components
|
|
29
|
+
- `Promise.all` para fetches independientes — nunca cascada
|
|
30
|
+
- Server Actions con validación Zod + auth check adentro
|
|
31
|
+
- `next/image` + `generateMetadata` siempre
|
|
32
|
+
|
|
33
|
+
### Anti-Patterns
|
|
34
|
+
- `'use client'` en la raíz del árbol
|
|
35
|
+
- `useEffect` + `fetch` para datos que podían venir del server
|
|
36
|
+
- Barrel imports en el critical path
|
|
37
|
+
- Pasar objetos de DB crudos a Client Components
|
|
38
|
+
- Server Actions sin verificación de auth
|
|
39
|
+
|
|
40
|
+
## Stack Defaults
|
|
41
|
+
|
|
42
|
+
| Componente | Default |
|
|
43
|
+
|------------|---------|
|
|
44
|
+
| Framework | Next.js 15+ App Router |
|
|
45
|
+
| Lenguaje | TypeScript estricto |
|
|
46
|
+
| UI | shadcn/ui + Tailwind |
|
|
47
|
+
| Data | Server Actions + RSC |
|
|
48
|
+
| ORM | Prisma |
|
|
49
|
+
| Auth | Auth.js |
|
|
50
|
+
| Testing | Vitest + Playwright (e2e) |
|
|
51
|
+
| Deploy | Vercel |
|
|
52
|
+
|
|
53
|
+
## Code Review Bar
|
|
54
|
+
|
|
55
|
+
**Veto:**
|
|
56
|
+
- Server Action sin auth/authz
|
|
57
|
+
- `'use client'` innecesario que arrastra el árbol entero al cliente
|
|
58
|
+
- Waterfalls de fetch evitables
|
|
59
|
+
- Barrel imports pesados en el bundle inicial
|
|
60
|
+
- Secrets que cruzan a código cliente
|
|
61
|
+
- Sin `generateMetadata` en pages indexables
|
|
62
|
+
|
|
63
|
+
**Comment-only:**
|
|
64
|
+
- Falta `next/dynamic` en componentes pesados
|
|
65
|
+
- Resource hints ausentes
|
|
66
|
+
- Naming inconsistente
|
|
67
|
+
|
|
68
|
+
## Delegation Map
|
|
69
|
+
|
|
70
|
+
**Report to:** `frontend-architect`, `backend-architect`, `lead-programmer`.
|
|
71
|
+
**Coordina con:** `nextjs-reviewer` (review de performance), `e2e-tester` (cobertura e2e).
|
|
72
|
+
**No delegate down.** Tier 3 specialist.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-reviewer
|
|
3
|
+
description: "Reviewer especializado en Next.js/React: audita performance (waterfalls, bundle, re-render), correcto uso de RSC/Server Actions, SEO y seguridad. Aplica las 64 reglas de Vercel priorizadas por impacto."
|
|
4
|
+
tools: Read, Glob, Grep, Bash
|
|
5
|
+
model: sonnet
|
|
6
|
+
maxTurns: 12
|
|
7
|
+
memory: project
|
|
8
|
+
skills: [nextjs-best-practices]
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Sos el **Next.js Reviewer**. Auditás código React/Next.js contra las 64 reglas de performance de Vercel Engineering (skill `nextjs-best-practices`) y las rules `nextjs-code` / `react-perf`. Sos read-only: encontrás y reportás, no implementás.
|
|
12
|
+
|
|
13
|
+
## Metodología (dirigida por impacto)
|
|
14
|
+
|
|
15
|
+
1. **Medir/localizar primero** — si hay métricas (Lighthouse, Web Vitals, bundle analyzer), empezar por lo que señalan. No revisar todo a ciegas.
|
|
16
|
+
2. **Recorrer por prioridad de impacto:**
|
|
17
|
+
- CRITICAL: waterfalls de fetch, bundle size (barrel imports, dynamic imports), Server Actions sin auth
|
|
18
|
+
- HIGH: server-side (module state, serialización RSC), componentes definidos dentro de componentes, resource hints
|
|
19
|
+
- MEDIUM/LOW: re-render, rendering, micro-opts JS
|
|
20
|
+
3. **Reportar con severidad** — cada finding: archivo:línea, regla violada, impacto estimado, fix sugerido.
|
|
21
|
+
|
|
22
|
+
## Qué buscar (alto impacto primero)
|
|
23
|
+
|
|
24
|
+
- **Waterfalls:** `await` secuenciales que podrían ser `Promise.all`; fetches anidados innecesarios
|
|
25
|
+
- **Bundle:** barrel imports, componentes pesados sin `next/dynamic`, libs third-party bloqueando el inicial
|
|
26
|
+
- **RSC:** `'use client'` demasiado arriba en el árbol; objetos de DB crudos cruzando el boundary
|
|
27
|
+
- **Server Actions:** falta de auth/authz adentro de la action
|
|
28
|
+
- **Re-render:** componentes definidos dentro de componentes; `useEffect` que debería ser handler
|
|
29
|
+
- **SEO:** falta `generateMetadata`, canonical, sitemap
|
|
30
|
+
- **Seguridad:** secrets en código cliente, input sin validar en actions/handlers
|
|
31
|
+
|
|
32
|
+
## Output
|
|
33
|
+
|
|
34
|
+
Reporte agrupado por severidad (CRITICAL → LOW), con conteo y top fixes recomendados. No aplica cambios — entrega el plan de optimización a `nextjs-engineer`.
|
|
35
|
+
|
|
36
|
+
## Delegation Map
|
|
37
|
+
|
|
38
|
+
**Report to:** `frontend-architect`, `lead-programmer`.
|
|
39
|
+
**Entrega findings a:** `nextjs-engineer` para implementar.
|
|
40
|
+
**No delegate down.** Tier 3 specialist (read-only).
|
package/dist/cli.js
CHANGED
|
@@ -1298,8 +1298,13 @@ async function addCommand(items) {
|
|
|
1298
1298
|
);
|
|
1299
1299
|
process.exit(1);
|
|
1300
1300
|
}
|
|
1301
|
+
const claudeDir = path10.join(target, ".claude");
|
|
1301
1302
|
const added = [];
|
|
1302
1303
|
const skipped = [];
|
|
1304
|
+
const notFound = [];
|
|
1305
|
+
const addedRules = [];
|
|
1306
|
+
const addedAgents = [];
|
|
1307
|
+
let statuslineAdded = false;
|
|
1303
1308
|
for (const item of items) {
|
|
1304
1309
|
if (item.startsWith("+")) {
|
|
1305
1310
|
const profileName = item.slice(1);
|
|
@@ -1312,19 +1317,69 @@ async function addCommand(items) {
|
|
|
1312
1317
|
for (const skill of profile.skills) {
|
|
1313
1318
|
const result = addSkill(root, target, skill, manifest.installed_skills);
|
|
1314
1319
|
if (result === "added") added.push(skill);
|
|
1320
|
+
else if (result === "not-found") notFound.push(skill);
|
|
1315
1321
|
else skipped.push(skill);
|
|
1316
1322
|
}
|
|
1323
|
+
for (const rule of profile.rules.universal) {
|
|
1324
|
+
if (!manifest.installed_rules.includes(rule)) {
|
|
1325
|
+
const src = path10.join(root, "rules", `${rule}.md`);
|
|
1326
|
+
if (fs8.existsSync(src)) {
|
|
1327
|
+
ensureDir(path10.join(claudeDir, "rules"));
|
|
1328
|
+
fs8.copyFileSync(src, path10.join(claudeDir, "rules", `${rule}.md`));
|
|
1329
|
+
manifest.installed_rules.push(rule);
|
|
1330
|
+
addedRules.push(rule);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
for (const rule of profile.rules.gamedev) {
|
|
1335
|
+
if (!manifest.installed_rules.includes(rule)) {
|
|
1336
|
+
const src = path10.join(root, "rules", "gamedev", `${rule}.md`);
|
|
1337
|
+
if (fs8.existsSync(src)) {
|
|
1338
|
+
ensureDir(path10.join(claudeDir, "rules"));
|
|
1339
|
+
fs8.copyFileSync(src, path10.join(claudeDir, "rules", `${rule}.md`));
|
|
1340
|
+
manifest.installed_rules.push(rule);
|
|
1341
|
+
addedRules.push(rule);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
for (const agentDir of profile.agents) {
|
|
1346
|
+
if (!manifest.installed_agents.includes(agentDir)) {
|
|
1347
|
+
const src = path10.join(root, "agents", agentDir);
|
|
1348
|
+
if (fs8.existsSync(src)) {
|
|
1349
|
+
const dst = path10.join(claudeDir, "agents", agentDir);
|
|
1350
|
+
copyDirSync(src, dst);
|
|
1351
|
+
manifest.installed_agents.push(agentDir);
|
|
1352
|
+
addedAgents.push(agentDir);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (profileName === "statusline") {
|
|
1357
|
+
const statuslineSrc = path10.join(root, "hooks", "statusline.sh");
|
|
1358
|
+
if (fs8.existsSync(statuslineSrc)) {
|
|
1359
|
+
fs8.copyFileSync(statuslineSrc, path10.join(claudeDir, "statusline.sh"));
|
|
1360
|
+
statuslineAdded = true;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (profile.permissions.allow.length > 0 || profile.permissions.deny.length > 0) {
|
|
1364
|
+
mergePermissions(claudeDir, profile.permissions);
|
|
1365
|
+
}
|
|
1317
1366
|
if (!manifest.profiles.includes(profileName)) {
|
|
1318
1367
|
manifest.profiles.push(profileName);
|
|
1368
|
+
manifest.profile_command = manifest.profiles.filter((p) => p !== "core").join("+");
|
|
1369
|
+
}
|
|
1370
|
+
if (statuslineAdded) {
|
|
1371
|
+
addStatuslineToSettings(claudeDir);
|
|
1319
1372
|
}
|
|
1320
1373
|
} else {
|
|
1321
1374
|
const result = addSkill(root, target, item, manifest.installed_skills);
|
|
1322
1375
|
if (result === "added") added.push(item);
|
|
1376
|
+
else if (result === "not-found") notFound.push(item);
|
|
1323
1377
|
else skipped.push(item);
|
|
1324
1378
|
}
|
|
1325
1379
|
}
|
|
1326
1380
|
manifest.installed_skills.push(...added);
|
|
1327
1381
|
manifest.total_skills = manifest.installed_skills.length;
|
|
1382
|
+
manifest.total_rules = manifest.installed_rules.length;
|
|
1328
1383
|
const merged = {
|
|
1329
1384
|
loaded: manifest.profiles,
|
|
1330
1385
|
skills: manifest.installed_skills,
|
|
@@ -1334,23 +1389,51 @@ async function addCommand(items) {
|
|
|
1334
1389
|
permissions: { allow: [], deny: [] }
|
|
1335
1390
|
};
|
|
1336
1391
|
writeManifest(target, merged, manifest.profile_command, root);
|
|
1392
|
+
const totalAdded = added.length + addedRules.length + addedAgents.length + (statuslineAdded ? 1 : 0);
|
|
1337
1393
|
console.log(chalk2.bold(`
|
|
1338
|
-
Added ${
|
|
1339
|
-
for (const s of added) console.log(chalk2.green(` [ok] ${s}`));
|
|
1394
|
+
Added ${totalAdded} items:`));
|
|
1395
|
+
for (const s of added) console.log(chalk2.green(` [ok] skill: ${s}`));
|
|
1396
|
+
for (const r of addedRules) console.log(chalk2.green(` [ok] rule: ${r}`));
|
|
1397
|
+
for (const a of addedAgents) console.log(chalk2.green(` [ok] agents: ${a}/`));
|
|
1398
|
+
if (statuslineAdded) console.log(chalk2.green(" [ok] statusline.sh"));
|
|
1340
1399
|
for (const s of skipped)
|
|
1341
1400
|
console.log(chalk2.dim(` [skip] ${s} (already installed)`));
|
|
1401
|
+
for (const s of notFound)
|
|
1402
|
+
console.log(chalk2.red(` [miss] ${s} (not found in source)`));
|
|
1342
1403
|
}
|
|
1343
1404
|
function addSkill(root, target, skill, installed) {
|
|
1344
1405
|
if (installed.includes(skill)) return "skipped";
|
|
1345
1406
|
const src = path10.join(root, "skills", skill);
|
|
1346
|
-
if (!fs8.existsSync(src))
|
|
1347
|
-
console.error(chalk2.red(` Skill '${skill}' not found in skills/`));
|
|
1348
|
-
return "skipped";
|
|
1349
|
-
}
|
|
1407
|
+
if (!fs8.existsSync(src)) return "not-found";
|
|
1350
1408
|
const dst = path10.join(target, ".claude", "skills", skill);
|
|
1351
1409
|
copyDirSync(src, dst);
|
|
1352
1410
|
return "added";
|
|
1353
1411
|
}
|
|
1412
|
+
function mergePermissions(claudeDir, newPerms) {
|
|
1413
|
+
const settingsPath = path10.join(claudeDir, "settings.json");
|
|
1414
|
+
if (!fs8.existsSync(settingsPath)) return;
|
|
1415
|
+
const settings = readJsonSync(settingsPath);
|
|
1416
|
+
const perms = settings.permissions ?? { allow: [], deny: [] };
|
|
1417
|
+
const allowSet = new Set(perms.allow);
|
|
1418
|
+
for (const a of newPerms.allow) allowSet.add(a);
|
|
1419
|
+
perms.allow = [...allowSet];
|
|
1420
|
+
const denySet = new Set(perms.deny);
|
|
1421
|
+
for (const d of newPerms.deny) denySet.add(d);
|
|
1422
|
+
perms.deny = [...denySet];
|
|
1423
|
+
settings.permissions = perms;
|
|
1424
|
+
writeJsonSync(settingsPath, settings);
|
|
1425
|
+
}
|
|
1426
|
+
function addStatuslineToSettings(claudeDir) {
|
|
1427
|
+
const settingsPath = path10.join(claudeDir, "settings.json");
|
|
1428
|
+
if (!fs8.existsSync(settingsPath)) return;
|
|
1429
|
+
const settings = readJsonSync(settingsPath);
|
|
1430
|
+
if (settings.statusLine) return;
|
|
1431
|
+
settings.statusLine = {
|
|
1432
|
+
type: "command",
|
|
1433
|
+
command: "bash .claude/statusline.sh"
|
|
1434
|
+
};
|
|
1435
|
+
writeJsonSync(settingsPath, settings);
|
|
1436
|
+
}
|
|
1354
1437
|
|
|
1355
1438
|
// src/commands/remove.ts
|
|
1356
1439
|
import fs9 from "fs";
|
|
@@ -1520,10 +1603,26 @@ function removeProfile(root, target, profileName, manifest) {
|
|
|
1520
1603
|
(r) => r !== rule
|
|
1521
1604
|
);
|
|
1522
1605
|
}
|
|
1606
|
+
if (profileName === "statusline") {
|
|
1607
|
+
const statuslineFile = path11.join(target, ".claude", "statusline.sh");
|
|
1608
|
+
if (fs9.existsSync(statuslineFile)) {
|
|
1609
|
+
fs9.rmSync(statuslineFile);
|
|
1610
|
+
}
|
|
1611
|
+
removeStatuslineFromSettings(path11.join(target, ".claude"));
|
|
1612
|
+
}
|
|
1523
1613
|
manifest.profiles = remainingProfiles;
|
|
1524
1614
|
manifest.profile_command = remainingProfiles.filter((p) => p !== "core").join("+");
|
|
1525
1615
|
return { removed: true, skills: removedSkills, agents: removedAgents };
|
|
1526
1616
|
}
|
|
1617
|
+
function removeStatuslineFromSettings(claudeDir) {
|
|
1618
|
+
const settingsPath = path11.join(claudeDir, "settings.json");
|
|
1619
|
+
if (!fs9.existsSync(settingsPath)) return;
|
|
1620
|
+
const settings = JSON.parse(fs9.readFileSync(settingsPath, "utf-8"));
|
|
1621
|
+
if (settings.statusLine) {
|
|
1622
|
+
delete settings.statusLine;
|
|
1623
|
+
fs9.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1527
1626
|
|
|
1528
1627
|
// src/commands/list.ts
|
|
1529
1628
|
import fs10 from "fs";
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Migración a Trusted Publishing (OIDC) — guía futura
|
|
2
|
+
|
|
3
|
+
> **Estado actual (jun 2026):** el paquete se publica con un **token `NPM_TOKEN`** (Classic Automation, saltea 2FA) cargado como secret en GitHub Actions. Funciona y es estable. Este documento describe cómo migrar a **Trusted Publishing (OIDC)** cuando convenga.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Qué es y por qué migrar
|
|
8
|
+
|
|
9
|
+
**Trusted Publishing** usa **OpenID Connect (OIDC)**: GitHub Actions se autentica contra npm con una **identidad efímera por run** (un id-token de corta vida), en vez de un token de larga duración guardado como secret.
|
|
10
|
+
|
|
11
|
+
| | Token (actual) | Trusted Publishing (OIDC) |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Secret de larga vida | Sí (`NPM_TOKEN`) | **No** |
|
|
14
|
+
| Expira / hay que rotar | Sí (vence **2026-09-01**) | **No** |
|
|
15
|
+
| Riesgo si se filtra | Alto (válido hasta expirar) | Bajo (token efímero por run) |
|
|
16
|
+
| Provenance / attestations | Manual | **Automático** |
|
|
17
|
+
| Mantenimiento | Rotar token periódicamente | Cero |
|
|
18
|
+
|
|
19
|
+
**Motivo principal para migrar:** eliminar la rotación del token y el riesgo de que un release falle sin aviso cuando el token expire.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. ⚠️ Por qué HOY (jun 2026) no se hizo
|
|
24
|
+
|
|
25
|
+
El setup actual está varias piezas por debajo de lo que OIDC requiere. **Verificar y resolver estos puntos ANTES de migrar:**
|
|
26
|
+
|
|
27
|
+
| Requisito OIDC | Estado al jun-2026 | Acción |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `@semantic-release/npm` **≥ 13.1.0** (soporte OIDC, oct-2025) | **12.0.2** (bundleado por `semantic-release@24.2.9`) — **no soporta OIDC** | Forzar upgrade del plugin |
|
|
30
|
+
| **npm ≥ 11.5.1** en el CI | Node 22 trae **npm 10.x** | Agregar step que actualice npm |
|
|
31
|
+
| `permissions: id-token: write` en el job | No está | Editar `release.yml` |
|
|
32
|
+
| Trusted publisher configurado en npm | No está | Configurar en la web de npm |
|
|
33
|
+
| Paquete ya existe en npm | ✅ (`claude-code-arcane@1.0.0`) | OK — OIDC **no** permite el *primer* publish, pero ya está hecho |
|
|
34
|
+
|
|
35
|
+
**Bugs conocidos a revisar antes de migrar** (pueden estar resueltos en versiones nuevas):
|
|
36
|
+
- [semantic-release/npm#1069](https://github.com/semantic-release/npm/issues/1069) — `ENONPMTOKEN` aún pide token en `verifyConditions` (reportado ene-2026).
|
|
37
|
+
- [semantic-release/npm#1023](https://github.com/semantic-release/npm/issues/1023) — falla el primer publish desde maintenance branch.
|
|
38
|
+
|
|
39
|
+
> Antes de empezar, chequear que estos issues estén cerrados o tengan workaround claro.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 3. Setup paso a paso
|
|
44
|
+
|
|
45
|
+
### Paso 1 — Actualizar dependencias
|
|
46
|
+
|
|
47
|
+
Asegurar `@semantic-release/npm` ≥ 13.1.0. Como `semantic-release@24.x` puede seguir bundleando una versión vieja, declararlo como dependencia directa:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install -D @semantic-release/npm@latest semantic-release@latest
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Verificar que quedó ≥ 13.1.0:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm ls @semantic-release/npm
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Paso 2 — Editar `.github/workflows/release.yml`
|
|
60
|
+
|
|
61
|
+
Dos cambios: **(a)** agregar el permiso `id-token: write`, **(b)** actualizar npm a ≥ 11.5.1 antes del release.
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
name: Release
|
|
65
|
+
|
|
66
|
+
on:
|
|
67
|
+
push:
|
|
68
|
+
branches: [main]
|
|
69
|
+
|
|
70
|
+
permissions:
|
|
71
|
+
contents: write
|
|
72
|
+
issues: write
|
|
73
|
+
pull-requests: write
|
|
74
|
+
id-token: write # ← NUEVO: habilita OIDC
|
|
75
|
+
|
|
76
|
+
jobs:
|
|
77
|
+
release:
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
with:
|
|
82
|
+
fetch-depth: 0
|
|
83
|
+
persist-credentials: false
|
|
84
|
+
- uses: actions/setup-node@v4
|
|
85
|
+
with:
|
|
86
|
+
node-version: 22
|
|
87
|
+
cache: 'npm'
|
|
88
|
+
- run: npm install -g npm@latest # ← NUEVO: npm >= 11.5.1 para OIDC
|
|
89
|
+
- run: npm ci
|
|
90
|
+
- run: npm run build
|
|
91
|
+
- run: npm test
|
|
92
|
+
- name: Release
|
|
93
|
+
env:
|
|
94
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
95
|
+
# NPM_TOKEN ya NO es necesario una vez que OIDC funcione.
|
|
96
|
+
# Dejarlo como fallback durante la transición; quitarlo al confirmar.
|
|
97
|
+
run: npx semantic-release
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
> **Importante:** durante la transición, **no borrar** el secret `NPM_TOKEN` todavía. Token y OIDC pueden coexistir; el token queda como red de seguridad.
|
|
101
|
+
|
|
102
|
+
### Paso 3 — Configurar el Trusted Publisher en npm
|
|
103
|
+
|
|
104
|
+
1. Ir a [npmjs.com](https://www.npmjs.com) → paquete **claude-code-arcane** → **Settings**.
|
|
105
|
+
2. Sección **Trusted Publishers** (o *Publishing access*).
|
|
106
|
+
3. **Add trusted publisher** → GitHub Actions:
|
|
107
|
+
- **Organization / user:** `SebastianLuser`
|
|
108
|
+
- **Repository:** `claude-code-arcane`
|
|
109
|
+
- **Workflow filename:** `release.yml`
|
|
110
|
+
- **Environment:** (dejar vacío salvo que el job use un `environment:`)
|
|
111
|
+
4. Guardar.
|
|
112
|
+
|
|
113
|
+
### Paso 4 — Release de prueba (additive, sin riesgo)
|
|
114
|
+
|
|
115
|
+
Con el token todavía presente como fallback:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
git commit --allow-empty -m "fix: test trusted publishing via OIDC"
|
|
119
|
+
git push origin main
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Revisar el log del step **Release** en Actions:
|
|
123
|
+
- ✅ Si publica vía OIDC (suele loguear `provenance` / autenticación OIDC) → migración OK.
|
|
124
|
+
- ❌ Si falla pidiendo token (`ENONPMTOKEN`) → el soporte aún tiene asperezas; **revertir** y seguir con token.
|
|
125
|
+
|
|
126
|
+
### Paso 5 — Quitar el token (solo si el Paso 4 fue verde)
|
|
127
|
+
|
|
128
|
+
1. En GitHub → Settings → Secrets → borrar `NPM_TOKEN`.
|
|
129
|
+
2. En npm → Access Tokens → borrar el token de automatización.
|
|
130
|
+
3. Commit confirmando que ya no se usa el token.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 4. Rollback
|
|
135
|
+
|
|
136
|
+
Si algo falla después de migrar:
|
|
137
|
+
|
|
138
|
+
1. Recargar el secret `NPM_TOKEN` (regenerar token Classic Automation en npm).
|
|
139
|
+
2. Revertir los cambios del `release.yml` (`git revert` del commit de migración).
|
|
140
|
+
3. El release vuelve a funcionar con token como antes.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## 5. Checklist rápido
|
|
145
|
+
|
|
146
|
+
- [ ] `@semantic-release/npm` ≥ 13.1.0 instalado y verificado
|
|
147
|
+
- [ ] Issues #1069 / #1023 cerrados o con workaround
|
|
148
|
+
- [ ] `id-token: write` agregado al workflow
|
|
149
|
+
- [ ] Step `npm install -g npm@latest` agregado
|
|
150
|
+
- [ ] Trusted publisher configurado en npm (repo + `release.yml`)
|
|
151
|
+
- [ ] Release de prueba publicó vía OIDC (con token aún presente)
|
|
152
|
+
- [ ] Token `NPM_TOKEN` eliminado de GitHub y npm
|
|
153
|
+
- [ ] Documentado el cambio en CHANGELOG / README
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Referencias
|
|
158
|
+
|
|
159
|
+
- [npm trusted publishing GA — GitHub Changelog](https://github.blog/changelog/2025-07-31-npm-trusted-publishing-with-oidc-is-generally-available/)
|
|
160
|
+
- [Trusted publishing for npm packages — npm Docs](https://docs.npmjs.com/trusted-publishers/)
|
|
161
|
+
- [@semantic-release/npm releases (v13.1.0 = soporte OIDC)](https://github.com/semantic-release/npm/releases)
|
|
162
|
+
- [Issue #1069 — ENONPMTOKEN con OIDC](https://github.com/semantic-release/npm/issues/1069)
|
|
163
|
+
- [Issue #1023 — primer publish desde maintenance branch](https://github.com/semantic-release/npm/issues/1023)
|
package/package.json
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: backend-nestjs
|
|
2
|
+
description: "Backend NestJS — feature modules, DI, guards/pipes, Prisma, JWT, testing, microservicios. Best practices priorizadas por impacto."
|
|
3
|
+
type: base
|
|
4
|
+
|
|
5
|
+
skills:
|
|
6
|
+
# NestJS-specific (nuevas)
|
|
7
|
+
- nestjs-scaffold
|
|
8
|
+
- nestjs-best-practices
|
|
9
|
+
- pgvector-search
|
|
10
|
+
# API & data
|
|
11
|
+
- database
|
|
12
|
+
- database-indexing
|
|
13
|
+
- data-migrations
|
|
14
|
+
- api-design
|
|
15
|
+
- api-docs
|
|
16
|
+
- api-versioning
|
|
17
|
+
- caching-strategy
|
|
18
|
+
- webhooks
|
|
19
|
+
- rate-limiting
|
|
20
|
+
# Auth & security
|
|
21
|
+
- auth-strategy
|
|
22
|
+
- jwt-strategy
|
|
23
|
+
- oauth-setup
|
|
24
|
+
- rbac-abac
|
|
25
|
+
- audit-log
|
|
26
|
+
# Ops & quality
|
|
27
|
+
- testing
|
|
28
|
+
- contract-testing
|
|
29
|
+
- deps-audit
|
|
30
|
+
- env-sync
|
|
31
|
+
- observability
|
|
32
|
+
- performance
|
|
33
|
+
- async-ops
|
|
34
|
+
- ci-cd-setup
|
|
35
|
+
|
|
36
|
+
rules:
|
|
37
|
+
universal:
|
|
38
|
+
- backend-code
|
|
39
|
+
- api-code
|
|
40
|
+
- nestjs-code
|
|
41
|
+
- migration-code
|
|
42
|
+
gamedev:
|
|
43
|
+
[]
|
|
44
|
+
|
|
45
|
+
agents:
|
|
46
|
+
- engineering
|
|
47
|
+
|
|
48
|
+
permissions:
|
|
49
|
+
allow:
|
|
50
|
+
- "Bash(npm *)"
|
|
51
|
+
- "Bash(yarn *)"
|
|
52
|
+
- "Bash(pnpm *)"
|
|
53
|
+
- "Bash(npx *)"
|
|
54
|
+
- "Bash(nest *)"
|
|
55
|
+
- "Bash(docker ps*)"
|
|
56
|
+
- "Bash(docker images*)"
|
|
57
|
+
deny:
|
|
58
|
+
[]
|