[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6DAJTVQHNMWBB3TX4CPTWX5":3,"skill-same:01M6DAJTVQHNMWBB3TX4CPTWX5":92},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":14,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M6DAJTVQHNMWBB3TX4CPTWX5","springboot-patterns",null,"Spring Boot architecture patterns, REST API design, layered services, data access, caching, async processing, and logging. Use for Java Spring Boot backend work.","Prekzursil\u002Fagent-skills-toolchain","Prekzursil","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F54636077?v=4","01M6DACZ1PCQHD99SCR1YXG28B","Andrei Mihai Visalon","01M6DACZ670WZF535V3WYYSS4Z",0,"MIT","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Flibrary\u002Fskills\u002Fspringboot-patterns\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"patterns","springboot patterns","rest api","design","caching","java spring","async","backend","use","work","spring","access","api","REST","data","services","spring boot","java","boot","architecture","2026-11-10 10:12:30",false,"---\nname: springboot-patterns\ndescription: Spring Boot architecture patterns, REST API design, layered services, data access, caching, async processing, and logging. Use for Java Spring Boot backend work.\norigin: ECC\n---\n\n# Spring Boot Development Patterns\n\nSpring Boot architecture and API patterns for scalable, production-grade services.\n\n## When to Activate\n\n- Building REST APIs with Spring MVC or WebFlux\n- Structuring controller → service → repository layers\n- Configuring Spring Data JPA, caching, or async processing\n- Adding validation, exception handling, or pagination\n- Setting up profiles for dev\u002Fstaging\u002Fproduction environments\n- Implementing event-driven patterns with Spring Events or Kafka\n\n## REST API Structure\n\n```java\n@RestController\n@RequestMapping(\"\u002Fapi\u002Fmarkets\")\n@Validated\nclass MarketController {\n  private final MarketService marketService;\n\n  MarketController(MarketService marketService) {\n    this.marketService = marketService;\n  }\n\n  @GetMapping\n  ResponseEntity\u003CPage\u003CMarketResponse>> list(\n      @RequestParam(defaultValue = \"0\") int page,\n      @RequestParam(defaultValue = \"20\") int size) {\n    Page\u003CMarket> markets = marketService.list(PageRequest.of(page, size));\n    return ResponseEntity.ok(markets.map(MarketResponse::from));\n  }\n\n  @PostMapping\n  ResponseEntity\u003CMarketResponse> create(@Valid @RequestBody CreateMarketRequest request) {\n    Market market = marketService.create(request);\n    return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));\n  }\n}\n```\n\n## Repository Pattern (Spring Data JPA)\n\n```java\npublic interface MarketRepository extends JpaRepository\u003CMarketEntity, Long> {\n  @Query(\"select m from MarketEntity m where m.status = :status order by m.volume desc\")\n  List\u003CMarketEntity> findActive(@Param(\"status\") MarketStatus status, Pageable pageable);\n}\n```\n\n## Service Layer with Transactions\n\n```java\n@Service\npublic class MarketService {\n  private final MarketRepository repo;\n\n  public MarketService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Transactional\n  public Market create(CreateMarketRequest request) {\n    MarketEntity entity = MarketEntity.from(request);\n    MarketEntity saved = repo.save(entity);\n    return Market.from(saved);\n  }\n}\n```\n\n## DTOs and Validation\n\n```java\npublic record CreateMarketRequest(\n    @NotBlank @Size(max = 200) String name,\n    @NotBlank @Size(max = 2000) String description,\n    @NotNull @FutureOrPresent Instant endDate,\n    @NotEmpty List\u003C@NotBlank String> categories) {}\n\npublic record MarketResponse(Long id, String name, MarketStatus status) {\n  static MarketResponse from(Market market) {\n    return new MarketResponse(market.id(), market.name(), market.status());\n  }\n}\n```\n\n## Exception Handling\n\n```java\n@ControllerAdvice\nclass GlobalExceptionHandler {\n  @ExceptionHandler(MethodArgumentNotValidException.class)\n  ResponseEntity\u003CApiError> handleValidation(MethodArgumentNotValidException ex) {\n    String message = ex.getBindingResult().getFieldErrors().stream()\n        .map(e -> e.getField() + \": \" + e.getDefaultMessage())\n        .collect(Collectors.joining(\", \"));\n    return ResponseEntity.badRequest().body(ApiError.validation(message));\n  }\n\n  @ExceptionHandler(AccessDeniedException.class)\n  ResponseEntity\u003CApiError> handleAccessDenied() {\n    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of(\"Forbidden\"));\n  }\n\n  @ExceptionHandler(Exception.class)\n  ResponseEntity\u003CApiError> handleGeneric(Exception ex) {\n    \u002F\u002F Log unexpected errors with stack traces\n    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)\n        .body(ApiError.of(\"Internal server error\"));\n  }\n}\n```\n\n## Caching\n\nRequires `@EnableCaching` on a configuration class.\n\n```java\n@Service\npublic class MarketCacheService {\n  private final MarketRepository repo;\n\n  public MarketCacheService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Cacheable(value = \"market\", key = \"#id\")\n  public Market getById(Long id) {\n    return repo.findById(id)\n        .map(Market::from)\n        .orElseThrow(() -> new EntityNotFoundException(\"Market not found\"));\n  }\n\n  @CacheEvict(value = \"market\", key = \"#id\")\n  public void evict(Long id) {}\n}\n```\n\n## Async Processing\n\nRequires `@EnableAsync` on a configuration class.\n\n```java\n@Service\npublic class NotificationService {\n  @Async\n  public CompletableFuture\u003CVoid> sendAsync(Notification notification) {\n    \u002F\u002F send email\u002FSMS\n    return CompletableFuture.completedFuture(null);\n  }\n}\n```\n\n## Logging (SLF4J)\n\n```java\n@Service\npublic class ReportService {\n  private static final Logger log = LoggerFactory.getLogger(ReportService.class);\n\n  public Report generate(Long marketId) {\n    log.info(\"generate_report marketId={}\", marketId);\n    try {\n      \u002F\u002F logic\n    } catch (Exception ex) {\n      log.error(\"generate_report_failed marketId={}\", marketId, ex);\n      throw ex;\n    }\n    return new Report();\n  }\n}\n```\n\n## Middleware \u002F Filters\n\n```java\n@Component\npublic class RequestLoggingFilter extends OncePerRequestFilter {\n  private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);\n\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    long start = System.currentTimeMillis();\n    try {\n      filterChain.doFilter(request, response);\n    } finally {\n      long duration = System.currentTimeMillis() - start;\n      log.info(\"req method={} uri={} status={} durationMs={}\",\n          request.getMethod(), request.getRequestURI(), response.getStatus(), duration);\n    }\n  }\n}\n```\n\n## Pagination and Sorting\n\n```java\nPageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by(\"createdAt\").descending());\nPage\u003CMarket> results = marketService.list(page);\n```\n\n## Error-Resilient External Calls\n\n```java\npublic \u003CT> T withRetry(Supplier\u003CT> supplier, int maxRetries) {\n  int attempts = 0;\n  while (true) {\n    try {\n      return supplier.get();\n    } catch (Exception ex) {\n      attempts++;\n      if (attempts >= maxRetries) {\n        throw ex;\n      }\n      try {\n        Thread.sleep((long) Math.pow(2, attempts) * 100L);\n      } catch (InterruptedException ie) {\n        Thread.currentThread().interrupt();\n        throw ex;\n      }\n    }\n  }\n}\n```\n\n## Rate Limiting (Filter + Bucket4j)\n\n**Security Note**: The `X-Forwarded-For` header is untrusted by default because clients can spoof it.\nOnly use forwarded headers when:\n1. Your app is behind a trusted reverse proxy (nginx, AWS ALB, etc.)\n2. You have registered `ForwardedHeaderFilter` as a bean\n3. You have configured `server.forward-headers-strategy=NATIVE` or `FRAMEWORK` in application properties\n4. Your proxy is configured to overwrite (not append to) the `X-Forwarded-For` header\n\nWhen `ForwardedHeaderFilter` is properly configured, `request.getRemoteAddr()` will automatically\nreturn the correct client IP from the forwarded headers. Without this configuration, use\n`request.getRemoteAddr()` directly—it returns the immediate connection IP, which is the only\ntrustworthy value.\n\n```java\n@Component\npublic class RateLimitFilter extends OncePerRequestFilter {\n  private final Map\u003CString, Bucket> buckets = new ConcurrentHashMap\u003C>();\n\n  \u002F*\n   * SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.\n   *\n   * If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure\n   * Spring to handle forwarded headers properly for accurate client IP detection:\n   *\n   * 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in\n   *    application.properties\u002Fyaml\n   * 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:\n   *\n   *    @Bean\n   *    ForwardedHeaderFilter forwardedHeaderFilter() {\n   *        return new ForwardedHeaderFilter();\n   *    }\n   *\n   * 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing\n   * 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container\n   *\n   * Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.\n   * Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.\n   *\u002F\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    \u002F\u002F Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter\n    \u002F\u002F is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For\n    \u002F\u002F headers directly without proper proxy configuration.\n    String clientIp = request.getRemoteAddr();\n\n    Bucket bucket = buckets.computeIfAbsent(clientIp,\n        k -> Bucket.builder()\n            .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))\n            .build());\n\n    if (bucket.tryConsume(1)) {\n      filterChain.doFilter(request, response);\n    } else {\n      response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n    }\n  }\n}\n```\n\n## Background Jobs\n\nUse Spring’s `@Scheduled` or integrate with queues (e.g., Kafka, SQS, RabbitMQ). Keep handlers idempotent and observable.\n\n## Observability\n\n- Structured logging (JSON) via Logback encoder\n- Metrics: Micrometer + Prometheus\u002FOTel\n- Tracing: Micrometer Tracing with OpenTelemetry or Brave backend\n\n## Production Defaults\n\n- Prefer constructor injection, avoid field injection\n- Enable `spring.mvc.problemdetails.enabled=true` for RFC 7807 errors (Spring Boot 3+)\n- Configure HikariCP pool sizes for workload, set timeouts\n- Use `@Transactional(readOnly = true)` for queries\n- Enforce null-safety via `@NonNull` and `Optional` where appropriate\n\n**Remember**: Keep controllers thin, services focused, repositories simple, and errors handled centrally. Optimize for maintainability and testability.\n","# Spring Boot Development Patterns\n\nSpring Boot architecture and API patterns for scalable, production-grade services.\n\n## When to Activate\n\n- Building REST APIs with Spring MVC or WebFlux\n- Structuring controller → service → repository layers\n- Configuring Spring Data JPA, caching, or async processing\n- Adding validation, exception handling, or pagination\n- Setting up profiles for dev\u002Fstaging\u002Fproduction environments\n- Implementing event-driven patterns with Spring Events or Kafka\n\n## REST API Structure\n\n```java\n@RestController\n@RequestMapping(\"\u002Fapi\u002Fmarkets\")\n@Validated\nclass MarketController {\n  private final MarketService marketService;\n\n  MarketController(MarketService marketService) {\n    this.marketService = marketService;\n  }\n\n  @GetMapping\n  ResponseEntity\u003CPage\u003CMarketResponse>> list(\n      @RequestParam(defaultValue = \"0\") int page,\n      @RequestParam(defaultValue = \"20\") int size) {\n    Page\u003CMarket> markets = marketService.list(PageRequest.of(page, size));\n    return ResponseEntity.ok(markets.map(MarketResponse::from));\n  }\n\n  @PostMapping\n  ResponseEntity\u003CMarketResponse> create(@Valid @RequestBody CreateMarketRequest request) {\n    Market market = marketService.create(request);\n    return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));\n  }\n}\n```\n\n## Repository Pattern (Spring Data JPA)\n\n```java\npublic interface MarketRepository extends JpaRepository\u003CMarketEntity, Long> {\n  @Query(\"select m from MarketEntity m where m.status = :status order by m.volume desc\")\n  List\u003CMarketEntity> findActive(@Param(\"status\") MarketStatus status, Pageable pageable);\n}\n```\n\n## Service Layer with Transactions\n\n```java\n@Service\npublic class MarketService {\n  private final MarketRepository repo;\n\n  public MarketService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Transactional\n  public Market create(CreateMarketRequest request) {\n    MarketEntity entity = MarketEntity.from(request);\n    MarketEntity saved = repo.save(entity);\n    return Market.from(saved);\n  }\n}\n```\n\n## DTOs and Validation\n\n```java\npublic record CreateMarketRequest(\n    @NotBlank @Size(max = 200) String name,\n    @NotBlank @Size(max = 2000) String description,\n    @NotNull @FutureOrPresent Instant endDate,\n    @NotEmpty List\u003C@NotBlank String> categories) {}\n\npublic record MarketResponse(Long id, String name, MarketStatus status) {\n  static MarketResponse from(Market market) {\n    return new MarketResponse(market.id(), market.name(), market.status());\n  }\n}\n```\n\n## Exception Handling\n\n```java\n@ControllerAdvice\nclass GlobalExceptionHandler {\n  @ExceptionHandler(MethodArgumentNotValidException.class)\n  ResponseEntity\u003CApiError> handleValidation(MethodArgumentNotValidException ex) {\n    String message = ex.getBindingResult().getFieldErrors().stream()\n        .map(e -> e.getField() + \": \" + e.getDefaultMessage())\n        .collect(Collectors.joining(\", \"));\n    return ResponseEntity.badRequest().body(ApiError.validation(message));\n  }\n\n  @ExceptionHandler(AccessDeniedException.class)\n  ResponseEntity\u003CApiError> handleAccessDenied() {\n    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of(\"Forbidden\"));\n  }\n\n  @ExceptionHandler(Exception.class)\n  ResponseEntity\u003CApiError> handleGeneric(Exception ex) {\n    \u002F\u002F Log unexpected errors with stack traces\n    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)\n        .body(ApiError.of(\"Internal server error\"));\n  }\n}\n```\n\n## Caching\n\nRequires `@EnableCaching` on a configuration class.\n\n```java\n@Service\npublic class MarketCacheService {\n  private final MarketRepository repo;\n\n  public MarketCacheService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Cacheable(value = \"market\", key = \"#id\")\n  public Market getById(Long id) {\n    return repo.findById(id)\n        .map(Market::from)\n        .orElseThrow(() -> new EntityNotFoundException(\"Market not found\"));\n  }\n\n  @CacheEvict(value = \"market\", key = \"#id\")\n  public void evict(Long id) {}\n}\n```\n\n## Async Processing\n\nRequires `@EnableAsync` on a configuration class.\n\n```java\n@Service\npublic class NotificationService {\n  @Async\n  public CompletableFuture\u003CVoid> sendAsync(Notification notification) {\n    \u002F\u002F send email\u002FSMS\n    return CompletableFuture.completedFuture(null);\n  }\n}\n```\n\n## Logging (SLF4J)\n\n```java\n@Service\npublic class ReportService {\n  private static final Logger log = LoggerFactory.getLogger(ReportService.class);\n\n  public Report generate(Long marketId) {\n    log.info(\"generate_report marketId={}\", marketId);\n    try {\n      \u002F\u002F logic\n    } catch (Exception ex) {\n      log.error(\"generate_report_failed marketId={}\", marketId, ex);\n      throw ex;\n    }\n    return new Report();\n  }\n}\n```\n\n## Middleware \u002F Filters\n\n```java\n@Component\npublic class RequestLoggingFilter extends OncePerRequestFilter {\n  private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);\n\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    long start = System.currentTimeMillis();\n    try {\n      filterChain.doFilter(request, response);\n    } finally {\n      long duration = System.currentTimeMillis() - start;\n      log.info(\"req method={} uri={} status={} durationMs={}\",\n          request.getMethod(), request.getRequestURI(), response.getStatus(), duration);\n    }\n  }\n}\n```\n\n## Pagination and Sorting\n\n```java\nPageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by(\"createdAt\").descending());\nPage\u003CMarket> results = marketService.list(page);\n```\n\n## Error-Resilient External Calls\n\n```java\npublic \u003CT> T withRetry(Supplier\u003CT> supplier, int maxRetries) {\n  int attempts = 0;\n  while (true) {\n    try {\n      return supplier.get();\n    } catch (Exception ex) {\n      attempts++;\n      if (attempts >= maxRetries) {\n        throw ex;\n      }\n      try {\n        Thread.sleep((long) Math.pow(2, attempts) * 100L);\n      } catch (InterruptedException ie) {\n        Thread.currentThread().interrupt();\n        throw ex;\n      }\n    }\n  }\n}\n```\n\n## Rate Limiting (Filter + Bucket4j)\n\n**Security Note**: The `X-Forwarded-For` header is untrusted by default because clients can spoof it.\nOnly use forwarded headers when:\n1. Your app is behind a trusted reverse proxy (nginx, AWS ALB, etc.)\n2. You have registered `ForwardedHeaderFilter` as a bean\n3. You have configured `server.forward-headers-strategy=NATIVE` or `FRAMEWORK` in application properties\n4. Your proxy is configured to overwrite (not append to) the `X-Forwarded-For` header\n\nWhen `ForwardedHeaderFilter` is properly configured, `request.getRemoteAddr()` will automatically\nreturn the correct client IP from the forwarded headers. Without this configuration, use\n`request.getRemoteAddr()` directly—it returns the immediate connection IP, which is the only\ntrustworthy value.\n\n```java\n@Component\npublic class RateLimitFilter extends OncePerRequestFilter {\n  private final Map\u003CString, Bucket> buckets = new ConcurrentHashMap\u003C>();\n\n  \u002F*\n   * SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.\n   *\n   * If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure\n   * Spring to handle forwarded headers properly for accurate client IP detection:\n   *\n   * 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in\n   *    application.properties\u002Fyaml\n   * 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:\n   *\n   *    @Bean\n   *    ForwardedHeaderFilter forwardedHeaderFilter() {\n   *        return new ForwardedHeaderFilter();\n   *    }\n   *\n   * 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing\n   * 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container\n   *\n   * Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.\n   * Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.\n   *\u002F\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    \u002F\u002F Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter\n    \u002F\u002F is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For\n    \u002F\u002F headers directly without proper proxy configuration.\n    String clientIp = request.getRemoteAddr();\n\n    Bucket bucket = buckets.computeIfAbsent(clientIp,\n        k -> Bucket.builder()\n            .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))\n            .build());\n\n    if (bucket.tryConsume(1)) {\n      filterChain.doFilter(request, response);\n    } else {\n      response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n    }\n  }\n}\n```\n\n## Background Jobs\n\nUse Spring’s `@Scheduled` or integrate with queues (e.g., Kafka, SQS, RabbitMQ). Keep handlers idempotent and observable.\n\n## Observability\n\n- Structured logging (JSON) via Logback encoder\n- Metrics: Micrometer + Prometheus\u002FOTel\n- Tracing: Micrometer Tracing with OpenTelemetry or Brave backend\n\n## Production Defaults\n\n- Prefer constructor injection, avoid field injection\n- Enable `spring.mvc.problemdetails.enabled=true` for RFC 7807 errors (Spring Boot 3+)\n- Configure HikariCP pool sizes for workload, set timeouts\n- Use `@Transactional(readOnly = true)` for queries\n- Enforce null-safety via `@NonNull` and `Optional` where appropriate\n\n**Remember**: Keep controllers thin, services focused, repositories simple, and errors handled centrally. Optimize for maintainability and testability.\n",{"name":5,"origin":49,"description":7},"ECC","main","a298c929b6cbd662e1fce54302ce520d5ceea1a5",".agents\u002Flibrary\u002Fskills\u002Fspringboot-patterns","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain","https:\u002F\u002Fgithub.com\u002FPrekzursil","2026-11-02 14:30:57",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add Prekzursil\u002Fagent-skills-toolchain --skill springboot-patterns",{"tool":62,"command":63},"bunx","bunx skills add Prekzursil\u002Fagent-skills-toolchain --skill springboot-patterns",{"tool":65,"command":66},"pnpm","pnpm dlx skills add Prekzursil\u002Fagent-skills-toolchain --skill springboot-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002F.agents\u002Flibrary\u002Fskills\u002Fspringboot-patterns\u002FSKILL.md",true,[70,76,80,84,88],{"path":71,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":74,"sourceHtmlUrl":75},"references\u002Fdatabase-adapters.md","REFERENCE","参考文档",4348,"https:\u002F\u002Fgithub.com\u002Fxiaoyuge886\u002Fharness_assistance\u002Fblob\u002Frefactor\u002Funified-packaging\u002Fapps\u002Fagent-mesh\u002Fdata\u002Fplugins\u002Fgithub\u002FEveryInc-compound-engineering-plugin\u002Fplugins\u002Fcompound-engineering\u002Fskills\u002Fandrew-kane-gem-writer\u002Freferences\u002Fdatabase-adapters.md",{"path":77,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":78,"sourceHtmlUrl":79},"references\u002Fmodule-organization.md",2250,"https:\u002F\u002Fgithub.com\u002Fxiaoyuge886\u002Fharness_assistance\u002Fblob\u002Frefactor\u002Funified-packaging\u002Fapps\u002Fagent-mesh\u002Fdata\u002Fplugins\u002Fgithub\u002FEveryInc-compound-engineering-plugin\u002Fplugins\u002Fcompound-engineering\u002Fskills\u002Fandrew-kane-gem-writer\u002Freferences\u002Fmodule-organization.md",{"path":81,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":82,"sourceHtmlUrl":83},"references\u002Frails-integration.md",3792,"https:\u002F\u002Fgithub.com\u002Fxiaoyuge886\u002Fharness_assistance\u002Fblob\u002Frefactor\u002Funified-packaging\u002Fapps\u002Fagent-mesh\u002Fdata\u002Fplugins\u002Fgithub\u002FEveryInc-compound-engineering-plugin\u002Fplugins\u002Fcompound-engineering\u002Fskills\u002Fandrew-kane-gem-writer\u002Freferences\u002Frails-integration.md",{"path":85,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":86,"sourceHtmlUrl":87},"references\u002Fresources.md",5382,"https:\u002F\u002Fgithub.com\u002Fxiaoyuge886\u002Fharness_assistance\u002Fblob\u002Frefactor\u002Funified-packaging\u002Fapps\u002Fagent-mesh\u002Fdata\u002Fplugins\u002Fgithub\u002FEveryInc-compound-engineering-plugin\u002Fplugins\u002Fcompound-engineering\u002Fskills\u002Fandrew-kane-gem-writer\u002Freferences\u002Fresources.md",{"path":89,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":90,"sourceHtmlUrl":91},"references\u002Ftesting-patterns.md",4766,"https:\u002F\u002Fgithub.com\u002Fxiaoyuge886\u002Fharness_assistance\u002Fblob\u002Frefactor\u002Funified-packaging\u002Fapps\u002Fagent-mesh\u002Fdata\u002Fplugins\u002Fgithub\u002FEveryInc-compound-engineering-plugin\u002Fplugins\u002Fcompound-engineering\u002Fskills\u002Fandrew-kane-gem-writer\u002Freferences\u002Ftesting-patterns.md",[93,114,143,170,184,199],{"skillNo":94,"name":95,"nameZh":6,"description":96,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":44},"01M6DF7D7478R55P46REYH4QAC","clarify-requirements","Resolve genuine pre-execution ambiguity by scanning repo context, generating 1-6 grounded options, and asking 1-3 focused questions via AskUserQuestion. NOT for mid-task use (injection risk); NOT when the goal is already unambiguous; use to disambiguate vague prompts before any file edits.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fworkspace-hooks\u002Fskills\u002Fclarify-requirements\u002FSKILL.md",[],[100,101,102,103,104,105,106,107,108,32,109,110,111,112,113],"clarify requirements","clarify","requirements","risk","options","task","prompts","goal","scanning","context","repo","injection","execution","file",{"skillNo":115,"name":116,"nameZh":6,"description":117,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":123,"publishedAt":44},"01M6DF79CA4BHJS66J8EZDBPWJ","openmm-md","Run FREE, open-source, Windows-native GPU molecular dynamics via the openmm-md MCP (OpenMM + CUDA on the local NVIDIA GPU). Use for protein \u002F protein-ligand MD when you want a free stack or are on Windows without a Schrodinger license. Covers the full workflow (prepare, solvate, minimize, equilibrate, production, analyze, mmgbsa) AND an honest decision guide vs Schrodinger Desmond, including how to run Desmond legitimately on Linux or a cloud GPU with a real license. This is a deliberately LESSER free alternative and does NOT replace the schrodinger MCP tools. Do NOT use it to pick a free tool when a validated FEP+, WaterMap, IFD-MD, or Prime MM-GBSA result is actually required.","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Fmcp\u002Fopenmm-md-mcp\u002Fskill\u002Fopenmm-md\u002FSKILL.md",[120],{"categoryNo":121,"categoryName":122,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[124,125,126,127,128,129,130,131,132,133,134,135,136,137,32,138,139,140,141,142],"openmm md","how","decision","pick","linux","analyze","tool","stack","open","NVIDIA","gpu","license","workflow","windows","free","molecular dynamics","local","do","MCP",{"skillNo":144,"name":145,"nameZh":6,"description":146,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":147,"categories":148,"keywords":155,"publishedAt":44},"01M6DF76C1MG85VEWP8VZCBNMN","visual-review","Take screenshots of web pages and UI using Playwright for visual review and iteration","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fvisual-review\u002FSKILL.md",[149,152],{"categoryNo":150,"categoryName":151,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000003","演示文稿",{"categoryNo":153,"categoryName":154,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[156,157,158,159,160,161,162,163,164,165,125,166,167,168,169],"review","visual","visual review","pages","web","false","iteration","take","check","slides","ui","screenshot","playwright","screenshots",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":176,"publishedAt":44},"01M6DF73AQVCBZKEN0YXEX9FXP","status","Diagnostic status report — shows metaswarm installation state, project setup, and potential issues","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstatus\u002FSKILL.md",[],[172,177,178,179,180,181,182,183],"setup","report","project","installation","project setup","issues","state",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":191,"publishedAt":44},"01M6DF6X6C2XB0P6GX76P7TTYV","start","Use when starting work on any task, when the user mentions metaswarm, or when the user wants to begin tracked development work","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fstart\u002FSKILL.md",[190],{"categoryNo":121,"categoryName":122,"taxonomyType":21,"taxonomyTypeDesc":22},[186,192,105,193,194,195,32,196,33,197,198],"start issue","issue","ready","work on issue","user","work on","development",{"skillNo":200,"name":177,"nameZh":6,"description":201,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":202,"categories":203,"keywords":204,"publishedAt":44},"01M6DF6QMJ9AJKFKHHKRT005PQ","Interactive project setup — detects your project, configures metaswarm, writes project-local files","https:\u002F\u002Fgithub.com\u002FPrekzursil\u002Fagent-skills-toolchain\u002Fblob\u002Fmain\u002Ftools\u002Feditor\u002Fmetaswarm\u002Fskills\u002Fsetup\u002FSKILL.md",[],[177,179,140,205,181,206],"files","interactive"]