← Nest Desk
API
Tokens

Run Nest Desk from your own code

Both metered lanes are available over HTTP and each returns one JSON object. The division of labour: the page reads the files the way Nest does - every @Module, every constructor injection and how it resolves (with the message Nest prints when it cannot), circular imports, main.ts and its ValidationPipe options, every route with its guards, roles, pipes and body, every DTO with its class-validator decorators, and what each route returns - and hands its findings to the run as facts. The run reviews the feature or rewrites the files; the page then holds the reply to its own reading and re-scans every rewrite (nestreply.js).

Base URL and the response shape

Every endpoint lives under https://api.skillsafe.ai/v1/app-api, and every response has one shape:

{ "ok": true,  "data":  { ... } }
{ "ok": false, "error": { "code": "...", "message": "..." } }

The token is minted for this app (the guest call takes {"slug":"nest-desk"}), so no slug header is needed afterwards. Send it as Authorization: Bearer YOUR_TOKEN. The run body is the input object, sent bare.

StatusCodeMeaning
400validation_errorThe body is not a JSON object, or a required key (task, feature, code, facts) is missing. The page always sends all six of task, feature, code, note, carry, facts; note and carry are plain sentences when there is nothing to say.
401unauthorizedNo token, or a stale one. Mint a guest token or sign in again.
402insufficient_creditsThe balance is under min_credits. Price with /estimate first.
403forbiddenA guest token tried to run: both lanes are metered and need a personal sign-in.
404not_foundUnknown job id.
429rate_limitedBack off and retry.
5xxserver_errorTransient. Retry with the same Idempotency-Key so a retry never double-bills.

The task field comes first

Two lanes, chosen by task, in the order the work runs - review, then rewrite:

A missing or unknown task is answered as review. Both lanes send the same six fields. The facts are the page's reading - files, modules, injections and what resolves them, the bootstrap, the routes, the findings with the page's values, the skill's checklist and what the paste cannot show - and a line starting "must:" is a rule the reply has to hold. The page's handoff puts the review, as sentences, in carry for the rewrite lane. A paste longer than 40,000 characters is sent whole file by file in priority order (main.ts, modules, controllers, DTOs, services, entities); the files that do not fit are cut to their decorators, classes and the lines around each finding, every finding is still in the facts, and a rewrite keeps the cut files as pasted.

The input

FieldRequiredWhat
taskyesreview or rewrite. If it is missing or neither, do review and set "task": "review".
featureyesthe feature's name and the size of the paste.
codeyesthe pasted files, each under a "// file: path" line. A line "// [... lines A-B ... cut by the page ...]" marks code the page read but did not send; every finding in it is in the facts.
notesend it (a sentence when none)what the team says about the feature, in their words, or a sentence saying there is no note.
carrysend it (a sentence when none)rewrite task: the review of these files, as sentences; otherwise a sentence saying nothing is carried.
factsyesthe page's reading: files, modules, injections (class | module | index | token | resolved by), the bootstrap, routes (method | path | handler | guards | roles | pipes | body | validated), the findings (id | rule | severity | file:line | subject | evidence | the page's value), the skill's checklist by kind, and what the paste cannot show (kind | what). A line starting "must:" applies to your reply.
retry_notenooptional; if present, your previous reply was unusable - follow it.

The rules the model applies

From the source skill, @affaan-m/nestjs-patterns - the only standard the run applies:

House rules

The replies

review

Judge the paste against the skill. Give every page finding exactly one verdict; add what the scan could not compute and the code clearly shows; say what the paste cannot show; then order the fixes.

  1. summary - one sentence, at most 45 words: the state of this feature and the most important fix, naming the classes or routes.
  2. verdicts - one object per page finding, in id order: id (F1 ...), verdict (confirmed or cleared), why (one sentence from this code - the class, the line, the route).
  3. extra - issues the code shows that no finding covers, at most 5: id (X1, X2 ...), kind (one of: structure, di, exports, global-pipe, dto-validation, thin-controllers, response-shape, guards, config, errors, testing, production), file (a pasted path), issue (one sentence), fix (one sentence). [] when there is nothing to add.
  4. unseen - one object per gap of the paste, covering every kind the facts list under what the paste cannot show: kind (one of: bootstrap, root-module, unpasted-module, unpasted-class, tests, runtime), what (one sentence: what that missing piece would decide for this feature, naming it).
  5. fixes - the changes to make, in the order to make them (what breaks the boot first, then what lets bad input in or secrets out, then the rest): order (1, 2, 3 ...), ids (the finding and extra ids it settles, e.g. "F1, F5"), file (the path to change, or a new path), change (one sentence: the edit, using the page's value where there is one), why (one sentence). Every confirmed error and warning is in some fix.
  6. caveats - at most 4 short strings; [] when there is nothing to say.
{
  "task": "review",
  "summary": "one sentence",
  "verdicts": [ { "id": "F1", "verdict": "confirmed", "why": "one sentence" } ],
  "extra": [ { "id": "X1", "kind": "guards", "file": "src/feature/feature.service.ts", "issue": "one sentence", "fix": "one sentence" } ],
  "unseen": [ { "kind": "root-module", "what": "one sentence" } ],
  "fixes": [ { "order": 1, "ids": "F1", "file": "src/feature/feature.module.ts", "change": "one sentence", "why": "one sentence" } ],
  "caveats": [ ]
}

The page holds it to its own reading (check() in nestreply.js; any warning makes the reply flagged): the reply answers the task it was sent; every page finding gets exactly one verdict, confirmed or cleared, with a reason, and no verdict names an id the page did not raise; a finding the page computes from the code itself (an unresolved injection, a circular import, an export nothing provides, a missing or incomplete global pipe, an untyped body, a DTO with no validator, roles no guard reads) is flagged when cleared; every gap of the paste the facts list is covered by its kind; extra issues carry an id X1, X2 ..., a checklist kind and a pasted file; fixes are numbered 1, 2, 3 ... and every confirmed error and warning is in some fix; a summary; no web link, no gendered pronoun and no talk of the request's field names.

rewrite

Return the corrected files. Apply the review when one is carried (keep what it cleared, fix what it confirmed, in its order); otherwise fix every error and warning in the page's findings. Change only what a finding or the review asks for; keep every route (method and path), every class and the code style; keep the team's note.

  1. summary - one sentence, at most 45 words: what the rewrite fixed and what it left, naming files.
  2. files - every pasted file, whole, under its own path: path, content (the complete file - imports included, nothing elided). A new file (a response DTO, a request DTO replacing an interface) gets a new path beside the module that owns it. A file you remove on purpose is left out, and a change says so with its path. Never put a "// file:" line inside a content.
  3. changes - one object per edit: ids (the finding or review ids it fixes, e.g. "F2, F3"), file (the path), what (one sentence: what changed).
  4. kept - one object per finding left as it is: id, why (one sentence). [] when every finding is fixed.
  5. caveats - at most 4 short strings; [] when there is nothing to say.
{
  "task": "rewrite",
  "summary": "one sentence",
  "files": [ { "path": "src/feature/feature.module.ts", "content": "the whole file" } ],
  "changes": [ { "ids": "F1", "file": "src/feature/feature.module.ts", "what": "one sentence" } ],
  "kept": [ ],
  "caveats": [ ]
}

The page puts the returned files back together (a file it cut from the run is kept as pasted) and re-scans them with the same free check: a pasted file, class or route (method and path) missing with no change explaining it is flagged; a change that claims to fix a finding the re-scan still raises is flagged; a finding that still fires and is neither fixed nor kept with a reason is flagged; a finding the rewrite introduces is flagged; a file marker inside a file's content, an empty file, a set of files returned unchanged and an empty change log are flagged. A finding is identified by its rule and subject, never by its number, so renumbering cannot hide one.

Worked examples

Three runs from the page's free samples (invented code): the review of a users feature, its rewrite carrying that review, and the review of an orders module that cannot boot. Each input is exactly what the page sends; each reply is the saved reply the sample replays, generated with every finding id taken from the engine - but the code, the facts and long strings are shortened in this display only, and every cut is listed under the example. The last line is the page's own check of the full reply.

review - A users feature

The page read 7 files and 3 routes and raised 8 findings (F1 input-untyped, F2 entity-leak, F3 entity-leak, F4 entity-leak, F5 pipe-options, F6 dto-prop-undecorated, F7 param-no-pipe, F8 plain-error).

Input (code is 3488 characters; the first 1396 are shown; facts is 5027 characters; the first 1389 are shown):

{
 "task": "review",
 "feature": "Feature: Users\nPaste: 7 files, 115 lines, 1 module, 3 routes, 8 findings",
 "code": "// file: src/main.ts\nimport { ValidationPipe } from '@nestjs/common';\nimport { NestFactory } from '@nestjs/core';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.setGlobalPrefix('api');\n  app.useGlobalPipes(new ValidationPipe({ transform: true }));\n  await app.listen(3000);\n}\nbootstrap();\n\n// file: src/users/users.module.ts\nimport { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { User } from './entities/user.entity';\nimport { UsersController } from './users.controller';\nimport { UsersService } from './users.service';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// file: src/users/users.controller.ts\nimport { Body, Controller, Get, Param, ParseUUIDPipe, Patch, Post } from '@nestjs/common';\nimport { CreateUserInput } from './dto/create-user.input';\nimport { UpdateUserDto } from './dto/update-user.dto';\nimport { User } from './entities/user.entity';\nimport { UsersService } from './users.service';\n\n@Controller('users')\nexport class UsersController {\n  constructor(private readonly usersService: UsersService) {}\n\n  @Post()\n  create(@Body() input: CreateUserInput): Promise<User> {\n    return this.usersService.create(input);\n  }\n\n[... 2092 more characters cut from this display ...]",
 "note": "Users sign up through this create route; ids are uuids. Keep the route paths as they are.",
 "carry": "Nothing is carried into a review - it works from the files and the page's findings.",
 "facts": "The page's reading: 7 files, 115 lines - 1 module, 1 controller, 1 provider class, 1 DTO, 1 entity, 3 routes.\nFiles (path | lines | classes):\n- src/main.ts | 11 | -\n- src/users/users.module.ts | 13 | UsersModule (module)\n- src/users/users.controller.ts | 25 | UsersController (controller)\n- src/users/users.service.ts | 28 | UsersService (service)\n- src/users/dto/create-user.input.ts | 5 | CreateUserInput (interface)\n- src/users/dto/update-user.dto.ts | 14 | UpdateUserDto (dto)\n- src/users/entities/user.entity.ts | 19 | User (entity)\nModules (module | imports | controllers | providers | exports):\n- UsersModule | TypeOrmModule.forFeature([User]) | UsersController | UsersService | UsersService\nInjections (class | module | index | token | resolved by):\n- UsersController | UsersModule | 0 | UsersService | provided by UsersModule\n- UsersService | UsersModule | 0 | UserRepository | TypeOrmModule.forFeature([User])\nBootstrap: src/main.ts - global prefix 'api'; ValidationPipe new ValidationPipe({ transform: true }) (src/main.ts); interceptors none; filters none; guards none.\nRoutes (method | path | handler | guards | roles | pipes | body | validated):\n- POST | /api/users | UsersController.create | none | - | - | CreateUserInput | no - interface\n- GET | /api/users/:id | UsersController.findOne | none | - | - | - | -\n- PATCH | /api/users/:id | UsersController.update | none | - |\n\n[... 3638 more characters cut from this display ...]"
}

Reply:

{
 "task": "review",
 "summary": "The create route takes the CreateUserInput interface, so nothing is validated, and all 3 routes return the User entity with its passwordHash; fix the pipe options and the body type first, then add a UserResponseDto.",
 "verdicts": [
  {
   "id": "F1",
   "verdict": "confirmed",
   "why": "CreateUserInput is an interface, so POST /api/users has no class for the pipe to read and any property reaches users.save()."
  },
  {
   "id": "F2",
   "verdict": "confirmed",
   "why": "UsersController.create returns Promise<User>, and the saved entity carries passwordHash straight back to the caller."
  },
  {
   "id": "F3",
   "verdict": "confirmed",
   "why": "UsersController.findOne returns the entity from findOneBy, passwordHash included."
  },
  {
   "id": "F4",
   "verdict": "confirmed",
   "why": "UsersController.update returns what UsersService.update saves, which is the full User entity."
  },
  {
   "id": "F5",
   "verdict": "confirmed",
   "why": "main.ts only sets transform: true, so unknown properties are neither stripped nor rejected."
  },
  {
   "id": "F6",
   "verdict": "confirmed",
   "why": "UpdateUserDto.nickname has no decorator while name and email do, so it slips through unchecked today and would be stripped once whitelist is on."
  },
  {
   "id": "F7",
   "verdict": "confirmed",
   "why": "The note says ids are uuids, and GET /api/users/:id passes any string to findOneBy while the PATCH route already uses ParseUUIDPipe."
  },
  {
   "id": "F8",
   "verdict": "confirmed",
   "why": "UsersService.findOne throws a plain Error for a missing user, which the client sees as a 500 instead of a 404."
  }
 ],
 "extra": [],
 "unseen": [
  {
   "kind": "root-module",
   "what": "AppModule would show whether a global APP_INTERCEPTOR or APP_FILTER already serializes responses and shapes errors for this feature."
  },
  {
   "kind": "tests",
   "what": "No spec is pasted, so nothing proves an unknown property on POST /api/users is rejected with a 400 once the pipe is fixed."
  },
  {
   "kind": "runtime",
   "what": "Whether the signup route is rate limited and logged with a correlation id cannot be read from these files."
  }
 ],
 "fixes": [
  {
   "order": 1,
   "ids": "F5",
   "file": "src/main.ts",
   "change": "Register new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }) as the global pipe.",
   "why": "Unknown properties must be rejected before any DTO change can be trusted."
  },
  {
   "order": 2,
   "ids": "F1, F6",
   "file": "src/users/dto/create-user.dto.ts",
   "change": "Replace the CreateUserInput interface with a CreateUserDto class (email, name, password with class-validator decorators) and give UpdateUserDto.nickname @IsOptional() @IsString().",
   "why": "The create body becomes a class the pipe can validate, and no property of the update body is left undecorated."
  },
  {
   "order": 3,
   "ids": "F2, F3, F4",
   "file": "src/users/dto/user-response.dto.ts",
   "change": "Add a UserResponseDto with id, email, name and createdAt, and make every UsersService method return it instead of the User entity.",
   "why": "passwordHash must never leave the service."
  },
  {
   "order": 4,
   "ids": "F7, F8",
   "file": "src/users/users.controller.ts",
   "change": "Parse the id of GET /api/users/:id with ParseUUIDPipe and throw NotFoundException from UsersService for a missing user.",
   "why": "A malformed id becomes a 400 and a missing user a 404, not a lookup and a 500."
  }
 ],
 "caveats": []
}

What step 5 prints for it: F1 confirmed | F2 confirmed | F3 confirmed | F4 confirmed | F5 confirmed | F6 confirmed | F7 confirmed | F8 confirmed

The page's check: kept - nothing to flag

rewrite - A users feature

The input carries the saved review in carry, as the page's handoff does. The page's re-scan of the returned files: 8 findings before, 0 after.

Input (code is 3488 characters; the first 1396 are shown; facts is 5139 characters; the first 1389 are shown):

{
 "task": "rewrite",
 "feature": "Feature: Users\nPaste: 7 files, 115 lines, 1 module, 3 routes, 8 findings",
 "code": "// file: src/main.ts\nimport { ValidationPipe } from '@nestjs/common';\nimport { NestFactory } from '@nestjs/core';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.setGlobalPrefix('api');\n  app.useGlobalPipes(new ValidationPipe({ transform: true }));\n  await app.listen(3000);\n}\nbootstrap();\n\n// file: src/users/users.module.ts\nimport { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { User } from './entities/user.entity';\nimport { UsersController } from './users.controller';\nimport { UsersService } from './users.service';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [UsersController],\n  providers: [UsersService],\n  exports: [UsersService],\n})\nexport class UsersModule {}\n\n// file: src/users/users.controller.ts\nimport { Body, Controller, Get, Param, ParseUUIDPipe, Patch, Post } from '@nestjs/common';\nimport { CreateUserInput } from './dto/create-user.input';\nimport { UpdateUserDto } from './dto/update-user.dto';\nimport { User } from './entities/user.entity';\nimport { UsersService } from './users.service';\n\n@Controller('users')\nexport class UsersController {\n  constructor(private readonly usersService: UsersService) {}\n\n  @Post()\n  create(@Body() input: CreateUserInput): Promise<User> {\n    return this.usersService.create(input);\n  }\n\n[... 2092 more characters cut from this display ...]",
 "note": "Users sign up through this create route; ids are uuids. Keep the route paths as they are.",
 "carry": "The review confirmed F1, F2, F3, F4, F5, F6, F7 and F8.\nFixes, in order: 1) F5 - src/main.ts: Register new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true }) as the global pipe. 2) F1, F6 - src/users/dto/create-user.dto.ts: Replace the CreateUserInput interface with a CreateUserDto class (email, name, password with class-validator decorators) and give UpdateUserDto.nickname @IsOptional() @IsString(). 3) F2, F3, F4 - src/users/dto/user-response.dto.ts: Add a UserResponseDto with id, email, name and createdAt, and make every UsersService method return it instead of the User entity. 4) F7, F8 - src/users/users.controller.ts: Parse the id of GET /api/users/:id with ParseUUIDPipe and throw NotFoundException from UsersService for a missing user.\nKeep every cleared finding as it is and say so; fix the rest.",
 "facts": "The page's reading: 7 files, 115 lines - 1 module, 1 controller, 1 provider class, 1 DTO, 1 entity, 3 routes.\nFiles (path | lines | classes):\n- src/main.ts | 11 | -\n- src/users/users.module.ts | 13 | UsersModule (module)\n- src/users/users.controller.ts | 25 | UsersController (controller)\n- src/users/users.service.ts | 28 | UsersService (service)\n- src/users/dto/create-user.input.ts | 5 | CreateUserInput (interface)\n- src/users/dto/update-user.dto.ts | 14 | UpdateUserDto (dto)\n- src/users/entities/user.entity.ts | 19 | User (entity)\nModules (module | imports | controllers | providers | exports):\n- UsersModule | TypeOrmModule.forFeature([User]) | UsersController | UsersService | UsersService\nInjections (class | module | index | token | resolved by):\n- UsersController | UsersModule | 0 | UsersService | provided by UsersModule\n- UsersService | UsersModule | 0 | UserRepository | TypeOrmModule.forFeature([User])\nBootstrap: src/main.ts - global prefix 'api'; ValidationPipe new ValidationPipe({ transform: true }) (src/main.ts); interceptors none; filters none; guards none.\nRoutes (method | path | handler | guards | roles | pipes | body | validated):\n- POST | /api/users | UsersController.create | none | - | - | CreateUserInput | no - interface\n- GET | /api/users/:id | UsersController.findOne | none | - | - | - | -\n- PATCH | /api/users/:id | UsersController.update | none | - |\n\n[... 3750 more characters cut from this display ...]"
}

Reply (long strings shortened for display - see the cuts below):

{
 "task": "rewrite",
 "summary": "The pipe now whitelists and rejects unknown properties, CreateUserDto replaces the interface, every route returns UserResponseDto instead of the entity, and missing or malformed ids give a 404 or a 400.",
 "files": [
  {
   "path": "src/main.ts",
   "content": "import { ValidationPipe } from '@nestjs/common';\nimport { NestFactory } from '@nestjs/core';\nimport { AppModule } from './app.module';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.setGlobalPrefix('api');\n  app.useGlobalPipes(new ValidationPipe({ whitelist: true, [... 90 more characters cut from this display]"
  },
  {
   "path": "src/users/users.module.ts",
   "content": "import { Module } from '@nestjs/common';\nimport { TypeOrmModule } from '@nestjs/typeorm';\nimport { User } from './entities/user.entity';\nimport { UsersController } from './users.controller';\nimport { UsersService } from './users.service';\n\n@Module({\n  imports: [TypeOrmModule.forFeature([User])],\n  controllers: [... 106 more characters cut from this display]"
  },
  {
   "path": "src/users/users.controller.ts",
   "content": "import { Body, Controller, Get, Param, ParseUUIDPipe, Patch, Post } from '@nestjs/common';\nimport { CreateUserDto } from './dto/create-user.dto';\nimport { UpdateUserDto } from './dto/update-user.dto';\nimport { UserResponseDto } from './dto/user-response.dto';\nimport { UsersService } from './users.service'; [... 550 more characters cut from this display]"
  },
  {
   "path": "src/users/users.service.ts",
   "content": "import { Injectable, NotFoundException } from '@nestjs/common';\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { hash } from 'bcrypt';\nimport { Repository } from 'typeorm';\nimport { CreateUserDto } from './dto/create-user.dto';\nimport { UpdateUserDto } from './dto/update-user.dto';\nimport { UserResponseDto [... 993 more characters cut from this display]"
  },
  {
   "path": "src/users/dto/update-user.dto.ts",
   "content": "import { IsEmail, IsOptional, IsString, Length } from 'class-validator';\n\nexport class UpdateUserDto {\n  @IsOptional()\n  @IsString()\n  @Length(2, 80)\n  name?: string;\n\n  @IsOptional()\n  @IsEmail()\n  email?: string;\n\n  @IsOptional()\n  @IsString()\n  nickname?: string;\n}"
  },
  {
   "path": "src/users/entities/user.entity.ts",
   "content": "import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm';\n\n@Entity()\nexport class User {\n  @PrimaryGeneratedColumn('uuid')\n  id: string;\n\n  @Column({ unique: true })\n  email: string;\n\n  @Column()\n  name: string;\n\n  @Column()\n  passwordHash: string;\n\n  @CreateDateColumn()\n  createdAt: Date;\n}"
  },
  {
   "path": "src/users/dto/create-user.dto.ts",
   "content": "import { IsEmail, IsString, Length, MinLength } from 'class-validator';\n\nexport class CreateUserDto {\n  @IsEmail()\n  email: string;\n\n  @IsString()\n  @Length(2, 80)\n  name: string;\n\n  @IsString()\n  @MinLength(12)\n  password: string;\n}"
  },
  {
   "path": "src/users/dto/user-response.dto.ts",
   "content": "import { User } from '../entities/user.entity';\n\nexport class UserResponseDto {\n  id: string;\n  email: string;\n  name: string;\n  createdAt: Date;\n\n  static from(user: User): UserResponseDto {\n    const dto = new UserResponseDto();\n    dto.id = user.id;\n    dto.email = user.email;\n    dto.name = user.name; [... 58 more characters cut from this display]"
  }
 ],
 "changes": [
  {
   "ids": "F5",
   "file": "src/main.ts",
   "what": "The global pipe is now new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true })."
  },
  {
   "ids": "F1",
   "file": "src/users/dto/create-user.dto.ts",
   "what": "CreateUserDto, a validated class, replaces the CreateUserInput interface, and src/users/dto/create-user.input.ts is removed."
  },
  {
   "ids": "F6",
   "file": "src/users/dto/update-user.dto.ts",
   "what": "UpdateUserDto.nickname gets @IsOptional() @IsString()."
  },
  {
   "ids": "F2, F3, F4",
   "file": "src/users/users.service.ts",
   "what": "Every UsersService method returns UserResponseDto.from(user), so passwordHash never leaves the service."
  },
  {
   "ids": "F7",
   "file": "src/users/users.controller.ts",
   "what": "GET /api/users/:id parses its id with ParseUUIDPipe."
  },
  {
   "ids": "F8",
   "file": "src/users/users.service.ts",
   "what": "A missing user throws NotFoundException from a private load method."
  }
 ],
 "kept": [],
 "caveats": [
  "UserResponseDto is mapped by hand; switch to ClassSerializerInterceptor with @Exclude() if the team prefers serializers."
 ]
}

Cut from this display only (the saved reply and the check use the full text): files[0].content (394 characters, 304 shown); files[1].content (417 characters, 311 shown); files[2].content (857 characters, 307 shown); files[3].content (1312 characters, 319 shown); files[7].content (364 characters, 306 shown).

What step 5 prints for it: src/main.ts (11 lines) | src/users/users.module.ts (13 lines) | src/users/users.controller.ts (25 lines) | src/users/users.service.ts (34 lines) | src/users/dto/update-user.dto.ts (16 lines) | src/users/entities/user.entity.ts (19 lines) | src/users/dto/create-user.dto.ts (14 lines) | src/users/dto/user-response.dto.ts (17 lines)

The page's check: kept - The rewrite removes src/users/dto/create-user.input.ts (a change explains it).; The rewrite adds 2 files: src/users/dto/create-user.dto.ts and src/users/dto/user-response.dto.ts.

review - An orders module

The page read 9 files and 3 routes and raised 2 findings (F1 di-unresolved, F2 circular-import).

Input (code is 5638 characters; the first 1394 are shown; facts is 4261 characters; the first 1398 are shown):

{
 "task": "review",
 "feature": "Feature: Orders and payments\nPaste: 9 files, 161 lines, 3 modules, 3 routes, 2 findings",
 "code": "// file: src/main.ts\nimport { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';\nimport { NestFactory, Reflector } from '@nestjs/core';\nimport { AppModule } from './app.module';\nimport { HttpExceptionFilter } from './common/filters/http-exception.filter';\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule, { bufferLogs: true });\n  app.useGlobalPipes(\n    new ValidationPipe({\n      whitelist: true,\n      forbidNonWhitelisted: true,\n      transform: true,\n      transformOptions: { enableImplicitConversion: true },\n    }),\n  );\n  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));\n  app.useGlobalFilters(new HttpExceptionFilter());\n  await app.listen(3000);\n}\nbootstrap();\n\n// file: src/orders/orders.module.ts\nimport { Module } from '@nestjs/common';\nimport { PaymentsModule } from '../payments/payments.module';\nimport { OrdersController } from './orders.controller';\nimport { OrdersService } from './orders.service';\n\n@Module({\n  imports: [PaymentsModule],\n  controllers: [OrdersController],\n  providers: [OrdersService],\n  exports: [OrdersService],\n})\nexport class OrdersModule {}\n\n// file: src/orders/orders.controller.ts\nimport { Body, Controller, Get, Param, ParseUUIDPipe, Post, Req, UseGuards } from '@nestjs/common';\nimport { JwtAuthGuard } from '../auth/jwt-auth.guard';\nimport { AuthenticatedRequest } from\n\n[... 4244 more characters cut from this display ...]",
 "note": "An order must check that the buyer exists before payment is authorized.",
 "carry": "Nothing is carried into a review - it works from the files and the page's findings.",
 "facts": "The page's reading: 9 files, 161 lines - 3 modules, 2 controllers, 2 provider classes, 2 DTOs, 0 entities, 3 routes.\nFiles (path | lines | classes):\n- src/main.ts | 20 | -\n- src/orders/orders.module.ts | 12 | OrdersModule (module)\n- src/orders/orders.controller.ts | 22 | OrdersController (controller)\n- src/orders/orders.service.ts | 30 | OrdersService (service)\n- src/orders/dto/create-order.dto.ts | 24 | OrderItemDto (dto), CreateOrderDto (dto)\n- src/payments/payments.module.ts | 12 | PaymentsModule (module)\n- src/payments/payments.controller.ts | 20 | PaymentsController (controller)\n- src/payments/payments.service.ts | 13 | PaymentsService (service)\n- src/users/users.module.ts | 8 | UsersModule (module)\nModules (module | imports | controllers | providers | exports):\n- OrdersModule | PaymentsModule | OrdersController | OrdersService | OrdersService\n- PaymentsModule | OrdersModule | PaymentsController | PaymentsService | PaymentsService\n- UsersModule | - | - | UsersService | UsersService\nInjections (class | module | index | token | resolved by):\n- OrdersController | OrdersModule | 0 | OrdersService | provided by OrdersModule\n- OrdersService | OrdersModule | 0 | PaymentsService | exported by PaymentsModule\n- OrdersService | OrdersModule | 1 | UsersService | unresolved\n- PaymentsController | PaymentsModule | 0 | PaymentsService | provided by PaymentsModule\n- PaymentsController |\n\n[... 2863 more characters cut from this display ...]"
}

Reply:

{
 "task": "review",
 "summary": "OrdersModule cannot boot: OrdersService injects UsersService, which OrdersModule never imports, and OrdersModule and PaymentsModule import each other without forwardRef(); import UsersModule and wrap both sides of the cycle.",
 "verdicts": [
  {
   "id": "F1",
   "verdict": "confirmed",
   "why": "OrdersModule imports only PaymentsModule, and UsersService is provided and exported by UsersModule, which nothing in the orders folder imports."
  },
  {
   "id": "F2",
   "verdict": "confirmed",
   "why": "orders.module.ts imports PaymentsModule and payments.module.ts imports OrdersModule, both as bare references."
  }
 ],
 "extra": [
  {
   "id": "X1",
   "kind": "structure",
   "file": "src/orders/orders.service.ts",
   "issue": "OrdersService keeps placed orders in an in-memory Map, so they vanish on restart and differ between instances.",
   "fix": "Persist orders through a repository provider owned by OrdersModule."
  }
 ],
 "unseen": [
  {
   "kind": "root-module",
   "what": "AppModule would show whether OrdersModule, PaymentsModule and UsersModule are all registered and whether any of them is global."
  },
  {
   "kind": "unpasted-class",
   "what": "JwtAuthGuard and HttpExceptionFilter are not pasted, so what the guard checks and the error envelope the filter sends cannot be read."
  },
  {
   "kind": "tests",
   "what": "No spec is pasted, so nothing shows the module graph compiling in a Test.createTestingModule run."
  },
  {
   "kind": "runtime",
   "what": "Whether refunds are rate limited and audit logged is not visible from these files."
  }
 ],
 "fixes": [
  {
   "order": 1,
   "ids": "F1",
   "file": "src/orders/orders.module.ts",
   "change": "Add UsersModule to the imports of OrdersModule (UsersModule exports UsersService).",
   "why": "Without it Nest stops at boot with the resolution error the page quotes."
  },
  {
   "order": 2,
   "ids": "F2",
   "file": "src/payments/payments.module.ts",
   "change": "Use forwardRef(() => PaymentsModule) in the imports of OrdersModule and forwardRef(() => OrdersModule) in the imports of PaymentsModule.",
   "why": "Each module is otherwise undefined when the other is built."
  },
  {
   "order": 3,
   "ids": "X1",
   "file": "src/orders/orders.service.ts",
   "change": "Move the order store behind a repository provider instead of a Map.",
   "why": "Orders must survive a restart and be shared by every instance."
  }
 ],
 "caveats": [
  "forwardRef() keeps the cycle; moving the refund route into OrdersModule would remove it."
 ]
}

What step 5 prints for it: F1 confirmed | F2 confirmed

The page's check: kept - nothing to flag

Build the input yourself

Load the engine in node (global.window = global; require("./nestkit.js"); require("./nestreply.js")), then:

const K = NestKit, R = NestReply;
const f = K.desk({ name: "Orders", note: "",
  code: "// file: src/orders/orders.module.ts\n@Module({ controllers: [OrdersController], providers: [OrdersService] })\nexport class OrdersModule {}\n"
    + "// file: src/orders/orders.service.ts\n@Injectable()\nexport class OrdersService {\n  constructor(private readonly users: UsersService) {}\n}\n"
    + "// file: src/users/users.module.ts\n@Module({ providers: [UsersService], exports: [UsersService] })\nexport class UsersModule {}\n"
    + "// file: src/users/users.service.ts\n@Injectable()\nexport class UsersService {}\n" });
// f.inv.modules, f.an.injections (cls, module, index, label, via), f.routes (method, path, guards, body, validated),
// f.findings (id, rule, sev, file, line, subject, evidence, fix), f.checks (kind, status), f.an.unseen (kind, what)
// K.findingsCsv(f), K.routesCsv(f), K.e2e(f), K.board(f) - the free exports
const b = K.buildInput(f, "review");                         // or "rewrite", with the review as sentences: R.carry(review, f)
const wire = K.toWire(b.input);                              // POST this to /estimate and /run
// then: const reply = R.normalize(R.parseReply(job.output.output), "review");
// R.check(reply, b)                                         // {status: kept or flags, notes}

That example's service injects UsersService, but OrdersModule never imports UsersModule, so the check prints what Nest would: Nest can't resolve dependencies of the OrdersService (?). Please make sure that the argument UsersService at index [0] is available in the OrdersModule context. - fix it free before you pay for a run. Get a token for the steps below from /tokens.html.

Step 1 — a tiny client helper

One function that sends JSON and unwraps the response. Paste a personal token from /tokens.html, or leave it empty and mint a guest token in step 2.

BASE="https://api.skillsafe.ai/v1/app-api"
TOKEN="YOUR_TOKEN"   # from https://nest-desk.skillsafe.ai/tokens.html
api() { # api METHOD PATH [JSON-BODY]
  curl -s -X "$1" "$BASE$2" -H "Content-Type: application/json" \
    ${TOKEN:+-H "Authorization: Bearer $TOKEN"} ${3:+--data "$3"}
}

Step 2 — a token

A guest token is enough for /me and /estimate. A run is metered, so it needs a personal token: sign in on /tokens.html and copy it from there.

# a guest token (enough for /me and /estimate); a run needs your personal token from /tokens.html
TOKEN=$(curl -s -X POST "$BASE/guest" -H "Content-Type: application/json" -d '{"slug":"nest-desk"}' | jq -r '.data.token')

Step 3 — GET /me

Who the token is (subject_type: user or guest) and the credit balance.

api GET /me | jq '.data | {subject_type, credits}' 

Step 4 — POST /estimate

Free. The body is the run input itself, the bare object (never wrapped in {"input": ...}). hold_credits is a reservation, not the price; min_credits is what the balance must cover; model_alias reads gpt-terra.

# input.json: the run input object - {"task": "review", "feature": "Feature: Users
Paste: 7 files, 115 lines, 1 module, 3 routes, 8 findings", "code": "// file: src/main.ts
import { ValidationPipe } from '@nestjs/common';
...", "note": "Users sign up through this create route; ids are uuids. Keep the route paths as they are.", "carry": "Nothing is carried into a review - it works from the files and the page's findings.", "facts": "The page's reading: 7 files, 115 lines - 1 module, 1 controller ...
The page's findings (id | rule | severity | file:line | subject | evidence | the page's value):
- F1 | input-untyped | error | src/users/users.controller.ts:12 | ..."}
# - see the input table
api POST /estimate "$(cat input.json)" | jq '.data | {hold_credits, min_credits, model, model_alias, markup_bps, warnings}' 

Step 5 — POST /run, then poll the job

Send an Idempotency-Key built from the lane and the input: a retry with the same key never bills twice. The page uses nest-desk:<task>:<inputHash>:a<attempt> - the lane is part of the key, retry_note is left out of the hash - and its one automatic reformat retry is a new attempt number, named in the page as one extra run. A review reply carries summary, verdicts, extra, unseen, fixes and caveats; a rewrite reply carries summary, files, changes, kept and caveats. The code below prints the verdicts of a review, or the files of a rewrite.

KEY="nest-desk:$(jq -r .task input.json):$(shasum -a 256 input.json | cut -c1-16):a1"
JOB=$(curl -s -X POST "$BASE/run" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $KEY" --data @input.json | jq -r '.data.job_id')
while :; do
  J=$(api GET "/jobs/$JOB"); S=$(echo "$J" | jq -r '.data.status')
  case "$S" in succeeded|failed|cancelled) break;; esac; sleep 2
done
echo "$J" | jq -r '.data.output.output' > reply.json   # one JSON object, the reply contract
echo "$J" | jq '.data | {charged_credits, truncated}'
jq -r '.task, (.verdicts[]? | "\(.id) \(.verdict): \(.why)"), (.files[]? | "\(.path): \(.content | length) characters")' reply.json

Step 6 — POST /run-stream

The same run as Server-Sent Events, with the same input.json. Browsers receive progress ticks rather than text deltas; the final done event carries the reply.

# Server-Sent Events: tick/delta events while the job runs, then a final event with the job
curl -N -s -X POST "$BASE/run-stream" -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: nest-desk:$(jq -r .task input.json):$(shasum -a 256 input.json | cut -c1-16):a1" --data @input.json