@mrciphersmith/keryx 0.3.0 → 0.3.1

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 (89) hide show
  1. package/README.md +4 -1
  2. package/dist/cli.js +8451 -4817
  3. package/dist/core.js +11700 -11374
  4. package/package.json +1 -1
  5. package/src/gdskills/bundled/agents/go-code-auditor.md +1 -1
  6. package/src/gdskills/bundled/agents/python-code-auditor.md +1 -1
  7. package/src/gdskills/bundled/install-manifest.json +271 -4
  8. package/src/gdskills/bundled/rules/core/model-selection.mdc +51 -0
  9. package/src/gdskills/bundled/skills/review/review-jev-rules/SKILL.md +267 -0
  10. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +26 -0
  11. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +1 -1
  12. package/src/gdskills/bundled/stacks/angular/agent-refs.json +4 -0
  13. package/src/gdskills/bundled/stacks/angular/governance/eval.json +1751 -0
  14. package/src/gdskills/bundled/stacks/angular/governance/scout.json +32 -0
  15. package/src/gdskills/bundled/stacks/angular/pack.json +55 -0
  16. package/src/gdskills/bundled/stacks/angular/rules/coding-style.mdc +82 -0
  17. package/src/gdskills/bundled/stacks/angular/rules/patterns.mdc +84 -0
  18. package/src/gdskills/bundled/stacks/angular/rules/security.mdc +70 -0
  19. package/src/gdskills/bundled/stacks/angular/rules/testing.mdc +73 -0
  20. package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/SKILL.md +127 -0
  21. package/src/gdskills/bundled/stacks/angular/skills/angular-build-fix/evals.json +72 -0
  22. package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/SKILL.md +98 -0
  23. package/src/gdskills/bundled/stacks/angular/skills/angular-code-review/evals.json +73 -0
  24. package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/SKILL.md +112 -0
  25. package/src/gdskills/bundled/stacks/angular/skills/angular-implementation/evals.json +74 -0
  26. package/src/gdskills/bundled/stacks/angular/skills/angular-testing/SKILL.md +102 -0
  27. package/src/gdskills/bundled/stacks/angular/skills/angular-testing/evals.json +71 -0
  28. package/src/gdskills/bundled/stacks/mobx/agent-refs.json +4 -0
  29. package/src/gdskills/bundled/stacks/mobx/governance/eval.json +904 -0
  30. package/src/gdskills/bundled/stacks/mobx/governance/scout.json +18 -0
  31. package/src/gdskills/bundled/stacks/mobx/pack.json +28 -0
  32. package/src/gdskills/bundled/stacks/mobx/rules/coding-style.mdc +91 -0
  33. package/src/gdskills/bundled/stacks/mobx/rules/patterns.mdc +122 -0
  34. package/src/gdskills/bundled/stacks/mobx/rules/security.mdc +56 -0
  35. package/src/gdskills/bundled/stacks/mobx/rules/testing.mdc +63 -0
  36. package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/SKILL.md +124 -0
  37. package/src/gdskills/bundled/stacks/mobx/skills/mobx-observable-testing/evals.json +73 -0
  38. package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/SKILL.md +149 -0
  39. package/src/gdskills/bundled/stacks/mobx/skills/mobx-store-implementation/evals.json +74 -0
  40. package/src/gdskills/bundled/stacks/nestjs/agent-refs.json +4 -0
  41. package/src/gdskills/bundled/stacks/nestjs/governance/eval.json +1308 -0
  42. package/src/gdskills/bundled/stacks/nestjs/governance/scout.json +34 -0
  43. package/src/gdskills/bundled/stacks/nestjs/pack.json +53 -0
  44. package/src/gdskills/bundled/stacks/nestjs/rules/coding-style.mdc +70 -0
  45. package/src/gdskills/bundled/stacks/nestjs/rules/patterns.mdc +83 -0
  46. package/src/gdskills/bundled/stacks/nestjs/rules/security.mdc +73 -0
  47. package/src/gdskills/bundled/stacks/nestjs/rules/testing.mdc +69 -0
  48. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/SKILL.md +157 -0
  49. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-build-fix/evals.json +70 -0
  50. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/SKILL.md +129 -0
  51. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-implementation/evals.json +71 -0
  52. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/SKILL.md +143 -0
  53. package/src/gdskills/bundled/stacks/nestjs/skills/nestjs-testing/evals.json +69 -0
  54. package/src/gdskills/bundled/stacks/nextjs-nuxt/agent-refs.json +4 -0
  55. package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/eval.json +2413 -0
  56. package/src/gdskills/bundled/stacks/nextjs-nuxt/governance/scout.json +42 -0
  57. package/src/gdskills/bundled/stacks/nextjs-nuxt/pack.json +42 -0
  58. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/coding-style.mdc +69 -0
  59. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/patterns.mdc +88 -0
  60. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/security.mdc +72 -0
  61. package/src/gdskills/bundled/stacks/nextjs-nuxt/rules/testing.mdc +64 -0
  62. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/SKILL.md +147 -0
  63. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-build-fix/evals.json +75 -0
  64. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/SKILL.md +118 -0
  65. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-code-review/evals.json +76 -0
  66. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/SKILL.md +135 -0
  67. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-implementation/evals.json +78 -0
  68. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/SKILL.md +116 -0
  69. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-testing/evals.json +75 -0
  70. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/SKILL.md +134 -0
  71. package/src/gdskills/bundled/stacks/nextjs-nuxt/skills/nextjs-nuxt-upgrade-migration/evals.json +76 -0
  72. package/src/gdskills/bundled/stacks/vue/agent-refs.json +4 -0
  73. package/src/gdskills/bundled/stacks/vue/governance/eval.json +2215 -0
  74. package/src/gdskills/bundled/stacks/vue/governance/scout.json +42 -0
  75. package/src/gdskills/bundled/stacks/vue/pack.json +42 -0
  76. package/src/gdskills/bundled/stacks/vue/rules/coding-style.mdc +73 -0
  77. package/src/gdskills/bundled/stacks/vue/rules/patterns.mdc +84 -0
  78. package/src/gdskills/bundled/stacks/vue/rules/security.mdc +60 -0
  79. package/src/gdskills/bundled/stacks/vue/rules/testing.mdc +69 -0
  80. package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/SKILL.md +137 -0
  81. package/src/gdskills/bundled/stacks/vue/skills/vue-build-fix/evals.json +72 -0
  82. package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/SKILL.md +120 -0
  83. package/src/gdskills/bundled/stacks/vue/skills/vue-code-review/evals.json +71 -0
  84. package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/SKILL.md +122 -0
  85. package/src/gdskills/bundled/stacks/vue/skills/vue-implementation/evals.json +72 -0
  86. package/src/gdskills/bundled/stacks/vue/skills/vue-testing/SKILL.md +115 -0
  87. package/src/gdskills/bundled/stacks/vue/skills/vue-testing/evals.json +72 -0
  88. package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/SKILL.md +135 -0
  89. package/src/gdskills/bundled/stacks/vue/skills/vue2-to-vue3-migration/evals.json +71 -0
@@ -0,0 +1,34 @@
1
+ [
2
+ {
3
+ "query": "Use when building or extending a NestJS module, controller, guard, interceptor, pipe, or exception filter -- choosing provider scope and registering a cross-cutting concern globally via APP_GUARD/APP_INTERCEPTOR/APP_PIPE/APP_FILTER, and keeping controllers thin with business rules delegated downstream. Scoped to authoring new behavior in an app that already boots; excludes selecting field-level checks on a request body, a finished-diff review pass, and diagnosing why an already-written module stops the app from starting.",
4
+ "decision": "fork",
5
+ "topMatch": "nestjs/nestjs-testing",
6
+ "recordedAt": "2026-09-25T06:29:56.288Z",
7
+ "skillName": "nestjs-implementation",
8
+ "justification": "Top overlap is review-backend (NestJS DI/guard/controller structure review, not authoring) and mobx-store-review-style patterns are unrelated; closest same-domain skill is review-backend which reviews finished diffs, not implementation, so fork is correct."
9
+ },
10
+ {
11
+ "query": "Use when writing or debugging a NestJS test built through @nestjs/testing: a unit test that compiles a provider via Test.createTestingModule and overrides its collaborators with overrideProvider/overrideGuard/overrideInterceptor, or an e2e test that boots a real Nest application and drives it with supertest through the actual guard/pipe/filter pipeline. Scoped to a test compiled through Nest's own testing module builder; excludes picking field-level checks for a request body, authoring the feature itself, and a plain unit test with no Nest module wiring involved.",
12
+ "decision": "fork",
13
+ "topMatch": "nestjs/nestjs-implementation",
14
+ "recordedAt": "2026-09-25T06:30:10.502Z",
15
+ "skillName": "nestjs-testing",
16
+ "justification": "Top overlap is nestjs-implementation (same pack, different category, not a substitute) and review-backend/review-testing-practices review finished diffs rather than authoring @nestjs/testing specs, so fork is correct."
17
+ },
18
+ {
19
+ "query": "Use when a NestJS app itself won't boot because of its own dependency graph: `Nest can't resolve dependencies` / UnknownDependenciesException, a circular-dependency warning between modules or providers, a missing @Injectable() decorator, or a provider that isn't exported from the module owning it. Applies the smallest root-cause fix to the module/provider graph and never widens scope or adds a module import blindly to silence the startup failure. Excludes a TypeScript compiler mismatch or module-loader failure with no Nest dependency-injection angle at all (use the ts-js-node build-fix skill), and excludes authoring behavior in a module that already boots cleanly (use nestjs-implementation).",
20
+ "decision": "fork",
21
+ "topMatch": "angular/angular-build-fix",
22
+ "recordedAt": "2026-09-25T06:30:16.866Z",
23
+ "skillName": "nestjs-build-fix",
24
+ "justification": "Top overlap is nodejs-build-fix (ts-js-node pack, generic tsc/module-resolution errors, not Nest's own DI graph) and nestjs-implementation (same pack, authoring not fixing); neither substitutes for diagnosing UnknownDependenciesException/circular-module errors, so fork is correct."
25
+ },
26
+ {
27
+ "query": "Use when writing or debugging a NestJS test built through Nest's own testing-module builder: a unit test that swaps a provider's real dependency (a repository, an HTTP client) for a fake or mock double so the unit under test runs in isolation, or an e2e test that boots a real Nest application and drives it with supertest through the actual guard/pipe/filter pipeline. Scoped to a test compiled through Nest's own testing module; excludes picking field-level checks for a request body, authoring the feature itself, and a framework-agnostic unit test that never touches Nest's module system.",
28
+ "decision": "create",
29
+ "topMatch": "nestjs/nestjs-implementation",
30
+ "recordedAt": "2026-09-25T06:33:27.947Z",
31
+ "skillName": "nestjs-testing",
32
+ "justification": "Top overlap remains nestjs-implementation (same pack, different category, authoring not testing) and review-backend/review-testing-practices review finished diffs rather than authoring @nestjs/testing specs, so fork is correct."
33
+ }
34
+ ]
@@ -0,0 +1,53 @@
1
+ {
2
+ "id": "nestjs",
3
+ "family": "framework",
4
+ "extends": "ts-js-node",
5
+ "modules": [
6
+ "nestjs-rules",
7
+ "nestjs-skills"
8
+ ],
9
+ "detectionMarkers": [
10
+ "nestjs"
11
+ ],
12
+ "provenance": {
13
+ "origin": "authored",
14
+ "sourceRef": "flow 318, Wave 4 batch 2"
15
+ },
16
+ "stability": "experimental",
17
+ "skills": {
18
+ "implement": [
19
+ "nestjs-implementation"
20
+ ],
21
+ "test": [
22
+ "nestjs-testing"
23
+ ],
24
+ "review": [],
25
+ "build-fix": [
26
+ "nestjs-build-fix"
27
+ ],
28
+ "migrate": []
29
+ },
30
+ "agentProfile": {
31
+ "displayName": "NestJS",
32
+ "auditFocus": [
33
+ "a REQUEST-scoped or TRANSIENT provider injected into a Singleton provider, sharing one request's state across others",
34
+ "a guard, interceptor, or pipe re-implemented per-controller instead of reused via APP_GUARD/APP_INTERCEPTOR/APP_PIPE or a shared decorator",
35
+ "business logic or direct ORM/repository calls inside a controller method instead of delegated to a service",
36
+ "a circular dependency between two modules or providers with no forwardRef(), or a provider resolved by string token with no matching provide entry",
37
+ "an exception filter or async controller method that lets an unhandled rejection or a raw internal error reach the HTTP response",
38
+ "a custom decorator or Reflector-based metadata read with no default when the metadata key is absent"
39
+ ],
40
+ "buildCommands": [
41
+ "nest build (or the project's package.json build script wrapping it)",
42
+ "tsc --noEmit",
43
+ "the project's lint script (eslint . or npm run lint)",
44
+ "the project's test script (nest test / jest, including e2e: npm run test:e2e)"
45
+ ],
46
+ "fixGuardrails": [
47
+ "Never widen a provider's scope to Default/Singleton just to make a DI resolution error disappear without checking whether the provider genuinely needs request-scoped state.",
48
+ "Never add a module to `imports` purely to silence an UnknownDependenciesException without confirming that module actually exports the provider being injected.",
49
+ "Never catch and swallow an exception in a controller or service just to stop a NestJS unhandled-rejection warning; use a proper exception filter or rethrow a typed HttpException.",
50
+ "Never delete or skip a failing @nestjs/testing spec to reach a green build."
51
+ ]
52
+ }
53
+ }
@@ -0,0 +1,70 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.ts"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # NestJS coding style
9
+
10
+ Narrows `core-common-rules`' stack-agnostic style rules, and `ts-js-node`'s
11
+ own TypeScript conventions, to NestJS's own idiom: decorator-driven modules,
12
+ providers, and dependency injection. Applies only to `*.ts` files in a
13
+ NestJS project.
14
+
15
+ ## Naming and file layout
16
+
17
+ - One artifact per file, named after its role: `<name>.controller.ts`,
18
+ `<name>.service.ts`, `<name>.module.ts`, `<name>.dto.ts`,
19
+ `<name>.guard.ts`, `<name>.interceptor.ts`, `<name>.filter.ts`,
20
+ `<name>.pipe.ts` — the Nest CLI's own generator convention. Do not fold a
21
+ guard or interceptor into a controller file "since it's only used there".
22
+ - Class names end in the same suffix as the file role
23
+ (`UsersController`, `UsersService`, `UsersModule`), `PascalCase`.
24
+ - Injection tokens for non-class providers (`useValue`/`useFactory`) are
25
+ `UPPER_SNAKE_CASE` string constants or `Symbol()`s exported from a single
26
+ `tokens.ts`/`constants.ts`, never an inline string literal repeated at
27
+ every injection site.
28
+
29
+ ## Decorators and typing
30
+
31
+ - Every constructor-injected dependency is typed explicitly
32
+ (`constructor(private readonly usersService: UsersService) {}`); do not
33
+ widen an injected dependency to `any` to sidestep a circular-import type
34
+ error — fix the import cycle instead (see `rules/patterns.mdc`).
35
+ - Every controller method's return type is explicit or trivially inferred
36
+ from a typed service call — never `Promise<any>`.
37
+ - DTOs are typed classes with `class-validator`/`class-transformer`
38
+ decorators, never plain `interface`s for request bodies — interfaces are
39
+ erased at runtime and `ValidationPipe` has nothing to validate against
40
+ (see `rules/security.mdc` and the repo's own `nestjs-dto.mdc` rule, which
41
+ owns the full DTO decorator checklist).
42
+ - Use `@Injectable()` on every class that is registered as a provider, even
43
+ a class with no constructor dependencies — omitting it is a common cause
44
+ of a confusing DI resolution failure covered in
45
+ `skills/nestjs-build-fix/SKILL.md`.
46
+
47
+ ## Imports and module boundaries
48
+
49
+ - Import providers only from the module that exports them; do not reach
50
+ into another feature module's `src/<feature>/*.service.ts` file directly
51
+ — import the feature module and let Nest's DI resolve it, or add the
52
+ provider to that module's `exports` array if it is meant to be shared.
53
+ - Keep a module's `providers`, `controllers`, `imports`, and `exports`
54
+ arrays sorted by the order they were added or grouped logically; do not
55
+ let an `@Module()` decorator grow past what fits on one screen without
56
+ splitting the feature into sub-modules.
57
+ - Barrel files (`index.ts` re-exporting a feature's public surface) export
58
+ only what other modules are meant to import — not internal providers,
59
+ guards, or DTOs used solely inside the module.
60
+
61
+ ## Errors
62
+
63
+ - Throw a specific built-in NestJS exception (`NotFoundException`,
64
+ `BadRequestException`, `ConflictException`, etc.) or a project-defined
65
+ subclass of `HttpException`, never a bare `throw new Error(...)` from a
66
+ controller or service that is expected to produce an HTTP response — a
67
+ plain `Error` becomes an opaque `500` with no semantic status code.
68
+ - Do not catch an exception in a service only to log it and rethrow the
69
+ same thing unchanged; catch only where you add context (wrap with a
70
+ domain-specific exception) or where you genuinely handle it.
@@ -0,0 +1,83 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.ts"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # NestJS patterns
9
+
10
+ Idiomatic module/provider/request-pipeline design for NestJS, and the
11
+ anti-patterns that recur in Nest codebases specifically. Applies only to
12
+ `*.ts` files in a NestJS project; general TypeScript design guidance still
13
+ comes from `ts-js-node`'s own rules.
14
+
15
+ ## Module and provider design
16
+
17
+ - A feature module owns its controllers, services, and the providers those
18
+ services need; it imports other feature modules only for the providers
19
+ it actually consumes, and exports only what other modules are meant to
20
+ inject.
21
+ - Break a real circular dependency between two modules with `forwardRef()`
22
+ on both sides of the `imports`/`providers` reference — but treat
23
+ `forwardRef()` as a signal to reconsider the boundary first: a genuine
24
+ circular need between two feature modules is often better modeled as a
25
+ shared third module both depend on.
26
+ - Prefer constructor injection over property injection (`@Inject()` on a
27
+ class field) except where a truly optional dependency needs
28
+ `@Optional()` — constructor injection makes required dependencies
29
+ visible in the type signature and fails fast at module-compile time.
30
+ - Default provider scope (`Default`/Singleton) is correct for stateless
31
+ services. Reach for `Scope.REQUEST` only when the provider genuinely
32
+ needs per-request state (e.g. the current user from a request-scoped
33
+ `REQUEST` token); a `REQUEST`-scoped provider re-instantiates its entire
34
+ injection subtree per request, which has a real performance cost — do
35
+ not default to it "to be safe."
36
+
37
+ ## Guards, interceptors, pipes, and filters
38
+
39
+ - Cross-cutting concerns (auth, logging, response shaping, validation)
40
+ belong in a guard/interceptor/pipe/filter, not duplicated inline in every
41
+ controller method that needs it.
42
+ - Register a cross-cutting concern that applies to (nearly) every route as
43
+ a global provider via `APP_GUARD`/`APP_INTERCEPTOR`/`APP_PIPE`/
44
+ `APP_FILTER` in a module's `providers` array (this keeps it inside Nest's
45
+ DI graph, unlike `app.useGlobalGuards()` called on the bootstrapped
46
+ instance), not by decorating every controller by hand.
47
+ - When a global guard protects most routes, mark the exceptions explicitly
48
+ with a custom decorator read via `Reflector` (e.g. `@Public()`) rather
49
+ than leaving some controllers undecorated and hoping the guard's default
50
+ is safe — an undecorated route should never be silently open just
51
+ because the guard's fallback happens to allow it.
52
+ - A guard, interceptor, or pipe class stays free of business logic: a guard
53
+ decides allow/deny, an interceptor transforms/observes the
54
+ request-response stream, a pipe transforms/validates one argument. Move
55
+ any decision that needs several service calls into the service layer and
56
+ have the guard call that service.
57
+
58
+ ## Controllers vs. services
59
+
60
+ - A controller method parses/validates input (via DTO + `ValidationPipe`),
61
+ delegates to exactly one service call for the actual work, and shapes
62
+ the HTTP response — it does not call a repository/ORM directly, does not
63
+ branch on business rules, and does not orchestrate multiple service
64
+ calls that a service method could compose instead.
65
+ - A service method has an explicit return type and throws a Nest HTTP
66
+ exception (see `rules/coding-style.mdc`) or a domain error the caller
67
+ translates — it never imports `Request`/`Response` from the HTTP
68
+ framework except for a documented streaming/file-download case.
69
+
70
+ ## Anti-patterns to avoid
71
+
72
+ - Injecting the whole `ModuleRef` or reaching for
73
+ `moduleRef.get(Provider, { strict: false })` to route around a missing
74
+ `imports`/`exports` entry — fix the module graph instead; a lazy
75
+ `ModuleRef.get` lookup hides a real DI wiring mistake.
76
+ - A provider registered with `useValue`/`useFactory` whose factory has side
77
+ effects beyond constructing the value (e.g. opening a connection with no
78
+ corresponding `OnModuleDestroy` cleanup) — pair any resource-acquiring
79
+ factory provider with the matching lifecycle hook.
80
+ - Business rules encoded only as `@Roles()`/`@Permissions()` decorator
81
+ strings scattered per-endpoint with no single source of truth for what
82
+ each role can do — centralize the role/permission matrix so a guard
83
+ reads it instead of re-deriving it per route.
@@ -0,0 +1,73 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.ts"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # NestJS security
9
+
10
+ Stack-specific request-pipeline risks for a NestJS application — where an
11
+ unvalidated or unauthorized request can reach application code — narrowing
12
+ the OWASP-style guidance in the common rules. Applies only to `*.ts` files
13
+ in a NestJS project; SQL/ORM-specific injection risk is covered by the
14
+ `review-backend` skill and the project's ORM rules, not duplicated here.
15
+
16
+ ## Input validation
17
+
18
+ - Every controller method that accepts a body, query, or route param has a
19
+ typed DTO class validated by a global `ValidationPipe` (registered once,
20
+ in `main.ts` via `app.useGlobalPipes(new ValidationPipe(...))`, or as an
21
+ `APP_PIPE` provider) — a per-controller `@UsePipes(ValidationPipe)` that
22
+ some controllers omit is the same gap as no `ValidationPipe` at all for
23
+ the endpoints that skip it.
24
+ - Enable `whitelist: true` (strip unknown properties) and
25
+ `forbidNonWhitelisted: true` (reject a request carrying an unknown
26
+ property) on the global `ValidationPipe` — without them, a DTO's
27
+ `class-validator` decorators only validate the fields they name; any
28
+ extra field the client sends still reaches the handler untouched.
29
+ - Never trust a query/param value's type from the route alone — a route
30
+ param typed `id: string` in the controller signature is still a raw
31
+ string at runtime; convert and validate it (`ParseIntPipe`,
32
+ `ParseUUIDPipe`, or a DTO with `@IsInt()`/`@IsUUID()`) rather than
33
+ passing it straight to a repository call.
34
+
35
+ ## AuthN/AuthZ
36
+
37
+ - Protect every route that must not be public with a guard
38
+ (`@UseGuards(...)` or a global `APP_GUARD`); a route with neither a guard
39
+ nor an explicit `@Public()`-style marker is not provably intentional —
40
+ treat an undecorated route in an otherwise-guarded controller as a gap to
41
+ confirm, not assume-safe.
42
+ - Read the authenticated principal from the guard-populated request object
43
+ (e.g. a custom `@CurrentUser()` param decorator backed by
44
+ `request.user`) — never from a client-supplied header, body field, or
45
+ query param that claims who the caller is.
46
+ - A role/permission check belongs in a guard evaluated before the handler
47
+ runs, not as an `if` inside the service after data has already been
48
+ fetched or mutated.
49
+
50
+ ## Response and error handling
51
+
52
+ - An exception filter (global `APP_FILTER`, or `app.useGlobalFilters(...)`)
53
+ must translate an unexpected internal error into a generic response —
54
+ never let a raw `error.message`/`error.stack` from an ORM, an HTTP
55
+ client, or an uncaught exception reach the HTTP response body; that
56
+ leaks internal schema, file paths, or connection details to the caller.
57
+ - Use NestJS's built-in `HttpException` subclasses (or your own subclass of
58
+ `HttpException`) for expected error conditions so the status code and
59
+ the response shape stay in the framework's own exception-filter
60
+ pipeline, instead of manually setting `response.status(...)` in a
61
+ controller and bypassing filters.
62
+
63
+ ## Secrets and configuration
64
+
65
+ - Read secrets (API keys, DB credentials, JWT signing keys) through
66
+ `ConfigService`/`@nestjs/config`, never hardcoded in a provider or
67
+ committed default value — a `useFactory` provider that falls back to a
68
+ literal secret when an env var is unset is the same risk as hardcoding
69
+ it.
70
+ - Do not log a full request/response body in an interceptor or middleware
71
+ applied globally without redacting known-sensitive DTO fields (passwords,
72
+ tokens, card numbers) — a broad request logger is a common accidental
73
+ secret-leak path into log storage.
@@ -0,0 +1,69 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.ts"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # NestJS testing
9
+
10
+ Test layout and conventions specific to `@nestjs/testing`, narrowing
11
+ `ts-js-node`'s general test-runner guidance to Nest's module-compiled test
12
+ style. Applies only to `*.ts` files in a NestJS project.
13
+
14
+ ## Layout and runner
15
+
16
+ - Unit tests for a provider live beside it as `<name>.service.spec.ts` /
17
+ `<name>.controller.spec.ts`; end-to-end tests live under `test/` as
18
+ `<feature>.e2e-spec.ts`, matching the Nest CLI's own generated layout and
19
+ its default `test/jest-e2e.json` config.
20
+ - Run the project's own `package.json` test scripts (typically `test` for
21
+ unit specs and `test:e2e` for the `test/` suite) rather than invoking
22
+ `jest`/`vitest` directly with ad hoc flags — they carry the project's own
23
+ module resolution and coverage config.
24
+
25
+ ## Building the test module
26
+
27
+ - Build the unit under test through `Test.createTestingModule({...}).compile()`
28
+ from `@nestjs/testing`, not by `new`-ing the class directly with hand-built
29
+ fake dependencies — a hand-built instance skips Nest's own DI wiring and
30
+ can pass while the real module graph would fail to compile.
31
+ - Replace a real dependency with a test double via `.overrideProvider(Token)
32
+ .useValue(...)` (or `.useFactory(...)`/`.useClass(...)`) on the
33
+ `TestingModule` builder, not by monkey-patching the class prototype or
34
+ mutating an already-compiled module's internals.
35
+ - For a guard/interceptor/pipe/filter under test in isolation, override it
36
+ the same way (`overrideGuard`/`overrideInterceptor`/`overridePipe`/
37
+ `overrideFilter`) rather than constructing it manually when it depends on
38
+ injected providers (e.g. `Reflector`).
39
+ - For an e2e spec, call `moduleRef.createNestApplication()` then
40
+ `app.init()` before issuing requests, and `app.close()` in `afterAll` —
41
+ a suite that never calls `app.close()` leaks open handles (DB
42
+ connections, timers) across spec files and can make later suites hang or
43
+ flake.
44
+
45
+ ## Mocking and fixtures
46
+
47
+ - Mock at the provider boundary (repository/HTTP-client provider), not by
48
+ stubbing a private method on the real service under test — the point of
49
+ `overrideProvider` is to replace what the unit depends on, not to reach
50
+ inside it.
51
+ - Build a request-scoped dependency's test double as a plain object/mock
52
+ matching its public shape (`{ findAll: jest.fn() }`), not the real
53
+ provider with `Scope.REQUEST` instantiated per test — request scope only
54
+ matters at the framework's request-handling boundary, which a unit test
55
+ bypasses entirely.
56
+ - Use `supertest` against `app.getHttpServer()` for e2e assertions on
57
+ status code and response shape; do not assert against the service's
58
+ return value directly in an e2e spec — that turns the e2e test back into
59
+ a unit test and loses the point of exercising the real HTTP pipeline
60
+ (guards, pipes, filters included).
61
+
62
+ ## Determinism
63
+
64
+ - Reset or recreate mocked providers between tests (`jest.clearAllMocks()`
65
+ in `afterEach`, or a fresh `TestingModule` per test) so one spec's mock
66
+ call count or return value cannot leak into the next.
67
+ - Do not depend on Nest's module compile order across a test file; each
68
+ `describe` block that needs a `TestingModule` builds and compiles its
69
+ own rather than sharing one built in an outer, unrelated `describe`.
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: nestjs-build-fix
3
+ description: "Use when a NestJS app itself won't boot because of its own dependency graph: `Nest can't resolve dependencies` / UnknownDependenciesException, a circular-dependency warning naming @Module()-decorated NestJS modules or @Injectable() providers requiring each other directly, a missing @Injectable() decorator, or a provider that isn't exported from the module owning it. Applies the smallest root-cause fix to the module/provider graph and never widens scope or blindly registers another module to silence the startup failure. Excludes a TypeScript compiler mismatch or module-loader failure with no Nest dependency-injection angle at all (use the ts-js-node build-fix skill), and excludes authoring behavior in a module that already boots cleanly (use nestjs-implementation)."
4
+ triggers:
5
+ - "Nest can't resolve dependencies of this provider"
6
+ - "fix this NestJS UnknownDependenciesException"
7
+ - "these two NestJS modules have a circular dependency on each other"
8
+ - "NestJS app won't bootstrap, dependency injection error"
9
+ - "this provider isn't found, NestJS DI error"
10
+ - "NestJS app throws at startup over its own module graph"
11
+ metadata:
12
+ origin: authored
13
+ category: build-fix
14
+ version: "1.0.0"
15
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
16
+ license: "MIT"
17
+ ---
18
+
19
+ # NestJS build-fix (DI resolution / circular dependency / bootstrap errors)
20
+
21
+ Resolve a NestJS-specific compile or startup failure in the module/provider
22
+ dependency graph -- not a generic `tsc` type error (see the `ts-js-node`
23
+ build-fix skill for that). Applies the smallest change that fixes the
24
+ actual root cause in the module graph. See `rules/patterns.mdc` for the
25
+ module/provider design a correct fix should restore.
26
+
27
+ ## Workflow
28
+
29
+ ### Step 1: Reproduce the failure
30
+
31
+ ```bash
32
+ npx tsc --noEmit
33
+ nest build
34
+ npm run start:dev
35
+ ```
36
+
37
+ Run the project's own `package.json` build/start scripts if they differ.
38
+ Most Nest DI errors only surface at **application bootstrap**, not at
39
+ `tsc` type-check time -- `tsc --noEmit` can pass while the app still fails
40
+ to start with an `UnknownDependenciesException`. Capture the exact error
41
+ message: it names the provider that could not be resolved and, often, the
42
+ module Nest was trying to resolve it inside.
43
+
44
+ ### Step 2: Classify the failure
45
+
46
+ - **`UnknownDependenciesException` / "Nest can't resolve dependencies of
47
+ X"**: a provider's constructor asks for a dependency Nest cannot find in
48
+ the current module's own providers or its imported modules' exports.
49
+ - **Circular dependency warning**: two modules (or two providers) depend on
50
+ each other, forming a cycle Nest cannot resolve without `forwardRef()`.
51
+ - **Missing `@Injectable()`**: a class used as a provider or injected as a
52
+ dependency has no `@Injectable()` decorator, so Nest cannot construct it
53
+ through DI at all -- a different failure shape from the two above, often
54
+ reported as the same "can't resolve dependencies" message.
55
+ - **Bootstrap failure with no clear provider name**: often a lifecycle hook
56
+ (`OnModuleInit`) throwing during app startup, not a DI graph problem at
57
+ all -- check the actual thrown error before assuming it's a wiring issue.
58
+
59
+ ### Step 3: Find the root cause
60
+
61
+ **Unknown dependency**: read the exact index Nest reports (it names which
62
+ constructor argument position failed) and trace that dependency back to its
63
+ own `@Injectable()`/provider declaration. Check three things in order: (1)
64
+ is the dependency listed in the current module's `providers`? (2) if it
65
+ lives in another module, is that module in the current module's `imports`,
66
+ **and** does that other module `export` the provider? (3) is the injected
67
+ class itself decorated with `@Injectable()`?
68
+
69
+ **Circular dependency**: identify the two modules or providers that
70
+ reference each other. Confirm it is a genuine mutual need, not an
71
+ accidental import that could instead go one direction -- see
72
+ `rules/patterns.mdc` on treating `forwardRef()` as a signal to reconsider
73
+ the boundary first.
74
+
75
+ **Missing `@Injectable()`**: check the class definition directly; a class
76
+ with no decorator that is still listed in `providers` or injected
77
+ elsewhere is the exact shape of this failure.
78
+
79
+ ### Step 4: Apply the smallest correct fix
80
+
81
+ - Missing export: add the provider to the owning module's `exports` array
82
+ -- do not instead re-declare the same provider in the consuming module's
83
+ own `providers` array, which creates two separate instances of what
84
+ should be one shared provider.
85
+ - Missing import: add the owning module to the consuming module's
86
+ `imports` array -- only after confirming the provider is actually
87
+ exported from it (step 3); adding the import alone does not fix a
88
+ missing export.
89
+ - Genuine circular dependency: apply `forwardRef()` on **both** sides of
90
+ the reference (the module-level `imports: [forwardRef(() => OtherModule)]`
91
+ and, if it's providers rather than modules, the constructor parameter's
92
+ `@Inject(forwardRef(() => OtherService))`) -- one-sided `forwardRef()`
93
+ does not resolve a genuine cycle.
94
+ - Missing `@Injectable()`: add the decorator to the class.
95
+ - A lifecycle-hook throw: fix the actual condition it's failing on (e.g. a
96
+ config value that's genuinely missing), not by swallowing the exception
97
+ inside the hook.
98
+
99
+ ### Step 5: Verify and report
100
+
101
+ Re-run the exact command from Step 1 (the app must actually boot, not just
102
+ type-check) and confirm no DI/circular-dependency warning appears in the
103
+ startup log. Report the root cause and the fix, not just "resolved".
104
+
105
+ ```
106
+ Fixed: src/orders/orders.module.ts
107
+ Root cause: OrdersService injects PricingService, but PricingModule
108
+ never exported PricingService -- it was only declared in PricingModule's
109
+ own `providers` array.
110
+ Fix: added PricingService to PricingModule's `exports` array.
111
+ Verified: npm run start:dev boots with no UnknownDependenciesException.
112
+ ```
113
+
114
+ ## Rules
115
+
116
+ - Follow `rules/patterns.mdc` for the module/provider design a fix should
117
+ restore, not just silence.
118
+ - ALWAYS fix the actual module-graph gap (missing export/import/decorator,
119
+ or a genuine `forwardRef()` cycle) -- never widen the fix beyond the
120
+ module(s) the failure actually touches.
121
+ - NEVER change a provider's scope (e.g. to `Scope.DEFAULT`) just to make a
122
+ resolution error disappear without confirming the provider doesn't
123
+ genuinely need request-scoped state.
124
+ - NEVER add a module to another module's `imports` purely to silence an
125
+ `UnknownDependenciesException` without first confirming that module
126
+ actually `export`s the provider being injected -- an import with no
127
+ matching export does not fix the error and just adds noise to the graph.
128
+ - NEVER re-declare the same provider in a second module's own `providers`
129
+ array as a workaround for a missing `exports` entry -- that creates two
130
+ separate instances of what the codebase intends to be one shared
131
+ provider.
132
+ - NEVER delete or skip a failing test to reach a green build.
133
+
134
+ ## Red Flags
135
+
136
+ | Rationalization | Why it is wrong |
137
+ |---|---|
138
+ | "I'll just add PricingModule to imports, that usually fixes these" | Without confirming PricingModule actually exports PricingService, the import alone does nothing -- verify the export exists before adding the import |
139
+ | "I'll re-declare PricingService in this module's own providers instead of fixing the export" | Creates a second, separate instance of the provider instead of sharing the one PricingModule owns -- state and side effects diverge silently |
140
+ | "I'll wrap just the failing side in forwardRef() and leave the other side as a normal import" | A genuine cycle needs forwardRef() on both sides; a one-sided fix still resolves in the wrong order and fails the same way |
141
+ | "This OnModuleInit hook throws, I'll wrap it in try/catch and swallow the error" | Bootstrap fails for a reason -- swallowing it lets the app start in a broken state instead of surfacing what's actually missing (e.g. a required config value) |
142
+
143
+ ## Verification
144
+
145
+ Do not report the fix done until all of the following hold:
146
+
147
+ - The app actually boots (`npm run start:dev` or the project's equivalent)
148
+ with no DI resolution error or circular-dependency warning, not just
149
+ that `tsc --noEmit` exits 0.
150
+ - No provider's scope was changed, and no module/provider was duplicated,
151
+ as a workaround.
152
+ - Every `forwardRef()` added for a genuine cycle appears on both sides of
153
+ the reference.
154
+ - The report states the actual root cause (missing export, missing
155
+ decorator, genuine cycle) and the fix, not just "DI error fixed".
156
+ - `git status` shows changes confined to the module(s)/provider(s) the
157
+ root cause required.
@@ -0,0 +1,70 @@
1
+ {
2
+ "triggers": {
3
+ "positive": [
4
+ "Nest can't resolve dependencies of OrdersService, help me fix this",
5
+ "Our NestJS app crashes at bootstrap with a dependency-injection stack trace naming OrdersService's constructor -- Nest cannot resolve it.",
6
+ "There's a circular dependency warning between UsersModule and AuthModule",
7
+ "Something in OrdersService's constructor keeps our NestJS app from finishing bootstrap -- Nest reports it cannot resolve the dependency.",
8
+ "Our NestJS app cannot bootstrap because Nest cannot resolve a dependency OrdersService needs -- what does the DI error at constructor index [2] actually mean?",
9
+ "Fix this NestJS build error that's blocking npm run start:dev"
10
+ ],
11
+ "negative": [
12
+ "Write unit tests for UsersService using @nestjs/testing with a mocked repository",
13
+ "Add a new NestJS module for the invoices feature with a controller and service",
14
+ "Fix this plain tsc error: Type 'string' is not assignable to type 'number' in a Node script",
15
+ "Review this NestJS PR for missing DTO validation and N+1 queries",
16
+ "go vet ./... is failing on this package, help resolve the compile error",
17
+ "Fix this Angular AOT compile error about an unresolved template reference"
18
+ ]
19
+ },
20
+ "scenarios": [
21
+ {
22
+ "id": "missing-export-not-import-alone",
23
+ "prompt": "OrdersService injects PricingService and Nest throws an UnknownDependenciesException naming OrdersService at bootstrap. I already added PricingModule to OrdersModule's imports array, but it's still failing. What am I missing?",
24
+ "strictness": "high",
25
+ "expected_behavior": [
26
+ {
27
+ "grader": "judge",
28
+ "rubric": "A correct answer identifies that adding PricingModule to OrdersModule's imports is not enough by itself: PricingModule must also list PricingService in its own exports array, since only exported providers are visible to importing modules. The fix is adding PricingService to PricingModule's exports array, not re-declaring PricingService inside OrdersModule's own providers.",
29
+ "pass_criteria": [
30
+ "States that importing PricingModule only makes it visible to OrdersModule if PricingModule actually exports PricingService -- an import with no matching export entry does not resolve the dependency",
31
+ "Names the concrete fix: add PricingService to PricingModule's exports array"
32
+ ],
33
+ "fail_criteria": [
34
+ "Recommends re-declaring PricingService directly inside OrdersModule's own providers array as the fix, instead of adding it to PricingModule's exports -- that creates a second, separate instance of the provider rather than sharing the one PricingModule owns"
35
+ ]
36
+ }
37
+ ],
38
+ "calibration": {
39
+ "known_right": "Importing PricingModule only gets you access to what it actually exports -- if PricingService is declared in PricingModule's providers but not listed in its exports array, OrdersModule still can't see it even though the module itself is imported. Check PricingModule's @Module() decorator:\n\n@Module({\n providers: [PricingService],\n exports: [PricingService], // <- this is almost certainly missing\n})\nexport class PricingModule {}\n\nAdd PricingService to that exports array. That's the fix -- OrdersModule's imports entry was already correct, the gap was on PricingModule's side.",
40
+ "known_wrong": "If the import isn't picking it up, just add PricingService directly to OrdersModule's own providers array too, alongside OrdersService. That guarantees it's available in OrdersModule's DI container regardless of what PricingModule does or doesn't export.",
41
+ "vague": "Double check that PricingModule is actually sharing PricingService properly with other modules that need it.",
42
+ "subtle_wrong": "Add PricingService to PricingModule's exports, and also add it to OrdersModule's own providers array just to be safe in case the export doesn't propagate correctly -- that way OrdersService can resolve it either way."
43
+ }
44
+ },
45
+ {
46
+ "id": "circular-dependency-both-sides",
47
+ "prompt": "OrdersModule and PricingModule genuinely need providers from each other. I added forwardRef() to OrdersModule's imports array, but PricingModule still throws a circular dependency error at bootstrap. What's wrong?",
48
+ "strictness": "high",
49
+ "expected_behavior": [
50
+ {
51
+ "grader": "judge",
52
+ "rubric": "A correct answer explains that resolving a genuine circular dependency with forwardRef() requires it on both sides of the reference, not just one -- OrdersModule's forwardRef() alone still leaves PricingModule trying to resolve OrdersModule the normal way, which fails the same way. The fix is adding forwardRef(() => OrdersModule) to PricingModule's imports as well (and the matching @Inject(forwardRef(...)) on constructor parameters if the cycle is between providers, not just modules).",
53
+ "pass_criteria": [
54
+ "States that forwardRef() must be applied on both sides of a genuine circular reference, and that having it only in OrdersModule's imports is why PricingModule still fails",
55
+ "Names the concrete fix: add forwardRef(() => OrdersModule) to PricingModule's own imports array (and @Inject(forwardRef(() => OtherService)) on the relevant constructor parameter if the providers themselves reference each other)"
56
+ ],
57
+ "fail_criteria": [
58
+ "Recommends removing forwardRef() and instead reordering the modules' imports, or merging the two modules, as the fix -- rather than applying forwardRef() symmetrically on both sides of the genuine cycle"
59
+ ]
60
+ }
61
+ ],
62
+ "calibration": {
63
+ "known_right": "forwardRef() has to be on both sides of a genuine cycle -- putting it only in OrdersModule's imports fixes how OrdersModule resolves PricingModule, but PricingModule is still trying to resolve OrdersModule the normal, eager way, which fails for the same reason. Add it symmetrically:\n\n// pricing.module.ts\n@Module({\n imports: [forwardRef(() => OrdersModule)],\n})\nexport class PricingModule {}\n\nIf the cycle is actually between the two services rather than just the modules (PricingService injects OrdersService or vice versa), the constructor also needs @Inject(forwardRef(() => OrdersService)) on that parameter, since module-level forwardRef() alone doesn't cover a provider-level cycle. Once both sides use forwardRef(), the app should bootstrap.",
64
+ "known_wrong": "Circular deps between modules are usually a sign forwardRef() is the wrong tool -- just remove it from OrdersModule's imports and instead reorder which module gets imported first in AppModule, since import order sometimes resolves it without needing forwardRef at all.",
65
+ "vague": "Circular dependencies in NestJS usually need forwardRef on both modules that reference each other, so make sure it's applied consistently.",
66
+ "subtle_wrong": "Add forwardRef(() => OrdersModule) to PricingModule's imports too, but skip the @Inject(forwardRef(...)) on the constructor parameter -- the module-level forwardRef should be enough to cover the provider injection as well, so there's no need to duplicate it at the constructor level."
67
+ }
68
+ }
69
+ ]
70
+ }