Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,43 @@ All notable changes to the netrock generator will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/).

## [0.9.1] - 2026-03-21

### Changed

- Backend: .NET 10.0.3 to 10.0.5 (framework, EF tools)
- Backend: Microsoft.Extensions.Caching.Hybrid, Http.Resilience, ServiceDiscovery 10.3.0 to 10.4.0
- Backend: Npgsql.EntityFrameworkCore.PostgreSQL 10.0.0 to 10.0.1
- Backend: OpenTelemetry.Instrumentation.AspNetCore 1.15.0 to 1.15.1
- Backend: Scalar.AspNetCore 2.13.2 to 2.13.9
- Backend: AWSSDK.S3 3.7.510.14 to 3.7.511.1
- Backend: Microsoft.Extensions.TimeProvider.Testing 10.3.0 to 10.4.0
- Frontend: @inlang/paraglide-js 2.13.2 to 2.15.0
- Frontend: @sveltejs/kit 2.53.4 to 2.55.0
- Frontend: svelte 5.53.8 to 5.53.12
- Frontend: typescript-eslint 8.57.0 to 8.57.1

## [0.9.0] - 2026-03-21

### Added

- Claude Code rules files in generated projects: backend-api, database, infrastructure, testing patterns (quick-reference for orchestrator)
- Frontend Svelte rules file (included when SvelteKit frontend is selected)
- `/verify` skill for running full backend build+test and frontend test+lint+check
- `/address-review` skill for reading, evaluating, and addressing PR review comments
- Claude Code permissions in generated projects (42 allow rules, 30 deny rules with cross-platform coverage)
- `settings.local.json.example` for user-specific permission overrides
- Delegation Rule in generated CLAUDE.md (orchestrator delegates `src/` code to specialized agents)
- Stop-quality-gate hook now warns when session ends on main/master branch

### Fixed

- Shell injection vulnerability in auto-format hook (replaced `execSync` string interpolation with `execFileSync` args array)
- Validate-bash hook now catches `git checkout -- .` variant (broadened regex)
- Validate-bash hook blocks `npm install` (enforces pnpm-only)
- Feature-gated `@feature` markers on auth-specific content in rules files (authorization, seeding, test auth)
- Feature-gated `@feature frontend` on frontend testing rules and verify skill frontend section

## [0.8.3] - 2026-03-14

### Added
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@netrock/core",
"version": "0.8.3",
"version": "0.9.1",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/features/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ export const featureDefinitions: Feature[] = [
id: 'claude',
name: 'Claude agent team',
description:
'CLAUDE.md, FILEMAP.md, 8 specialized agents, and 4 lifecycle hooks for AI-assisted development',
'CLAUDE.md, FILEMAP.md, 8 specialized agents, 5 rules files, and 4 lifecycle hooks for AI-assisted development',
details:
'Adds project-specific Claude Code configuration: CLAUDE.md with project rules and conventions, FILEMAP.md for change impact tracking, 8 specialized agents (backend engineer, reviewers, test writer, etc.), and 4 lifecycle hooks for auto-formatting and safety gates.',
'Adds project-specific Claude Code configuration: CLAUDE.md with project rules, delegation model, and conventions, FILEMAP.md for change impact tracking, 8 specialized agents (backend engineer, reviewers, test writer, etc.), 5 rules files (backend API, database, infrastructure, testing, frontend), and 4 lifecycle hooks for auto-formatting and safety gates.',
dependencies: ['core'],
required: false,
defaultEnabled: true,
Expand All @@ -164,9 +164,9 @@ export const featureDefinitions: Feature[] = [
id: 'claude-skills',
name: 'Claude skills & conventions',
description:
'22 Claude Code skills, 3 convention references, and skill assets for code generation',
'24 Claude Code skills, 3 convention references, permissions config, and skill assets for code generation',
details:
'Adds slash-command skills for common workflows: adding permissions, background jobs, email templates, API endpoints, and more. Includes convention reference documents that agents auto-load for consistent code style. Skills generate code following established project patterns.',
'Adds slash-command skills for common workflows: /verify, /address-review, adding permissions, background jobs, email templates, API endpoints, and more. Includes convention reference documents that agents auto-load for consistent code style, permission allow/deny lists for safe tool usage, and a local settings example for user overrides.',
dependencies: ['claude'],
required: false,
defaultEnabled: true,
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/manifests/claude-skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ export function registerClaudeSkillsManifest(): void {
registerManifest({
featureId: 'claude-skills',
files: [
// Settings (hooks config)
// Settings (hooks config + permissions)
{ path: '.claude/settings.json', templated: false },
{ path: '.claude/settings.local.json.example', templated: false },

// Convention skills (auto-injected into agents, not user-invocable)
{ path: '.claude/skills/backend-conventions/SKILL.md', templated: true },
Expand All @@ -22,6 +23,8 @@ export function registerClaudeSkillsManifest(): void {
{ path: '.claude/skills/review-pr/references/conventions-summary.md', templated: true },

// Generic skills (always available)
{ path: '.claude/skills/address-review/SKILL.md', templated: false },
{ path: '.claude/skills/verify/SKILL.md', templated: true },
{ path: '.claude/skills/add-ci-area/SKILL.md', templated: false },
{ path: '.claude/skills/add-env-var/SKILL.md', templated: true },
{ path: '.claude/skills/add-options-class/SKILL.md', templated: false },
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/manifests/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ export function registerClaudeManifest(): void {
{ path: '.claude/hooks/auto-format.mjs', templated: false },
{ path: '.claude/hooks/stop-quality-gate.mjs', templated: false },

// Rules (orchestrator quick-reference)
{ path: '.claude/rules/backend-api.md', templated: true },
{ path: '.claude/rules/database.md', templated: true },
{ path: '.claude/rules/infrastructure.md', templated: false },
{ path: '.claude/rules/testing.md', templated: true },

// Agents (backend-relevant)
{ path: '.claude/agents/backend-engineer.md', templated: false },
{ path: '.claude/agents/backend-reviewer.md', templated: false },
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/manifests/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ export function registerFrontendManifest(): void {
registerManifest({
featureId: 'frontend',
files: [
// Claude Code rules (frontend-specific)
{ path: '.claude/rules/frontend-svelte.md', templated: false },

{ path: 'src/frontend/.dockerignore', templated: false },
{ path: 'src/frontend/.env.example', templated: false },
{ path: 'src/frontend/.env.test', templated: false },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2058,6 +2058,7 @@ exports[`file list snapshots > core-only file list 1`] = `

exports[`file list snapshots > frontend + admin (no jobs/oauth) file list 1`] = `
[
".claude/rules/frontend-svelte.md",
".config/dotnet-tools.json",
".gitignore",
"README.md",
Expand Down Expand Up @@ -2674,6 +2675,7 @@ exports[`file list snapshots > frontend + admin (no jobs/oauth) file list 1`] =

exports[`file list snapshots > frontend full file list 1`] = `
[
".claude/rules/frontend-svelte.md",
".config/dotnet-tools.json",
".gitignore",
"README.md",
Expand Down Expand Up @@ -3464,6 +3466,7 @@ exports[`file list snapshots > frontend full file list 1`] = `

exports[`file list snapshots > frontend minimal (core + auth + frontend) file list 1`] = `
[
".claude/rules/frontend-svelte.md",
".config/dotnet-tools.json",
".gitignore",
"README.md",
Expand Down Expand Up @@ -4019,7 +4022,12 @@ exports[`file list snapshots > full preset file list 1`] = `
".claude/hooks/session-start.mjs",
".claude/hooks/stop-quality-gate.mjs",
".claude/hooks/validate-bash.mjs",
".claude/rules/backend-api.md",
".claude/rules/database.md",
".claude/rules/infrastructure.md",
".claude/rules/testing.md",
".claude/settings.json",
".claude/settings.local.json.example",
".claude/skills/add-aspire-dep/SKILL.md",
".claude/skills/add-background-job/SKILL.md",
".claude/skills/add-ci-area/SKILL.md",
Expand All @@ -4030,6 +4038,7 @@ exports[`file list snapshots > full preset file list 1`] = `
".claude/skills/add-rate-limit/SKILL.md",
".claude/skills/add-route-constraint/SKILL.md",
".claude/skills/add-test/SKILL.md",
".claude/skills/address-review/SKILL.md",
".claude/skills/backend-conventions/SKILL.md",
".claude/skills/create-issue/SKILL.md",
".claude/skills/create-pr/SKILL.md",
Expand All @@ -4049,6 +4058,7 @@ exports[`file list snapshots > full preset file list 1`] = `
".claude/skills/review-pr/SKILL.md",
".claude/skills/review-pr/references/conventions-summary.md",
".claude/skills/security-conventions/SKILL.md",
".claude/skills/verify/SKILL.md",
".config/dotnet-tools.json",
".gitignore",
"CLAUDE.md",
Expand Down
2 changes: 1 addition & 1 deletion packages/web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@netrock/web",
"private": true,
"version": "0.8.3",
"version": "0.9.1",
"type": "module",
"scripts": {
"dev": "vite dev",
Expand Down
7 changes: 4 additions & 3 deletions templates/.claude/hooks/auto-format.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// PostToolUse hook: auto-formats files after Write|Edit operations

import { readFileSync, existsSync, readdirSync } from 'fs';
import { execSync } from 'child_process';
import { execFileSync } from 'child_process';
import { resolve, extname } from 'path';

let input;
Expand All @@ -25,8 +25,9 @@ try {
const backendDir = resolve(projectDir, 'src/backend');
const slnx = readdirSync(backendDir).find((f) => f.endsWith('.slnx'));
if (slnx) {
execSync(
`dotnet format "${resolve(backendDir, slnx)}" --include "${filePath}" --no-restore`,
execFileSync(
'dotnet',
['format', resolve(backendDir, slnx), '--include', filePath, '--no-restore'],
{ stdio: 'ignore' },
);
}
Expand Down
7 changes: 6 additions & 1 deletion templates/.claude/hooks/stop-quality-gate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ const git = (cmd) =>
let warnings = '';

try {
// Check if on main/master branch
const branch = git('git rev-parse --abbrev-ref HEAD').trim();
if (branch === 'main' || branch === 'master') {
warnings += `On ${branch} branch! Create a feature branch before committing. `;
}

// Check for uncommitted changes
let hasChanges = false;
try {
Expand Down Expand Up @@ -47,7 +53,6 @@ try {
if (warnings) {
console.log(
JSON.stringify({
hookSpecificOutput: { hookEventName: 'Stop' },
systemMessage: `Quality gate: ${warnings}`,
}),
);
Expand Down
6 changes: 5 additions & 1 deletion templates/.claude/hooks/validate-bash.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ const command = input?.tool_input?.command;
if (!command) process.exit(0);

const blocks = [
{
pattern: /(^|[;&|])\s*npm\s+install\b/,
msg: 'Use pnpm, not npm. Run: pnpm install',
},
{
pattern: /git\s+push\s+.*--force(?!-)/,
msg: 'Force push blocked. Use --force-with-lease if you must, or ask the user first.',
Expand All @@ -32,7 +36,7 @@ const blocks = [
msg: 'git clean blocked - this removes untracked files permanently.',
},
{
pattern: /git\s+(checkout|restore)\s+\.\s*$/m,
pattern: /git\s+(checkout|restore)\s+(--\s+)?\.\s*$/m,
msg: 'Discarding all changes blocked. Specify individual files.',
},
{
Expand Down
29 changes: 29 additions & 0 deletions templates/.claude/rules/backend-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Backend API Rules

Extends CLAUDE.md Hard Rules with implementation patterns.

## Error Handling

- `ProblemFactory.Create(result.Error, result.ErrorType)` for error responses - never `NotFound()`, `BadRequest()`, or anonymous objects
- Client-facing error messages: `ErrorMessages.*` constants only - runtime values go in `ILogger`, never in `Result.Failure()`

## Controllers

- Authenticated endpoints extend `ApiController` - public endpoints use `ControllerBase` directly
- Always: `/// <summary>`, `[ProducesResponseType]` per status code, `CancellationToken` as last param
- Never `/// <param name="cancellationToken">` - it leaks into OpenAPI `requestBody.description`
- `[ProducesResponseType]` without `typeof(...)` on error codes (400, 401, 403, 404, 429)

# @feature auth

## Authorization

- `[RequirePermission("permission.name")]` on actions - never class-level `[Authorize(Roles)]` on permission controllers
- Role hierarchy enforced: cannot manage users at/above your rank

# @end

## Entities

- Extend `BaseEntity`, private setters, invariants via methods, protected parameterless ctor for EF
- Soft delete via `entity.SoftDelete()` / `entity.Restore()` - never set `IsDeleted` directly
34 changes: 34 additions & 0 deletions templates/.claude/rules/database.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Database Rules

Extends CLAUDE.md with EF Core patterns and commands.

## Entity Configuration

- Inherit `BaseEntityConfiguration<T>` (public abstract), override `ConfigureEntity`
- Derived configurations are `internal` - auto-discovered via `ApplyConfigurationsFromAssembly()`
- Default `public` schema - named schemas only for existing grouped features (e.g., `"auth"`)
- `.HasComment()` on all enum columns documenting values
- Boolean naming: `Is*`/`Has*` in C#, prefix-free column names via `HasColumnName`

## Migrations

- Command: `dotnet ef migrations add {Name} --project src/backend/MyProject.Infrastructure --startup-project src/backend/MyProject.WebApi --output-dir Persistence/Migrations`

# @feature auth

- Seeding: roles via `AppRoles.All` (reflection), permissions via `SeedRolePermissionsAsync()`

# @end

## Repository Pattern

- `IBaseEntityRepository<T>` for CRUD with automatic soft-delete filtering (global query filter)
- Custom repos: extend `IBaseEntityRepository<T>` in Application, implement in Infrastructure
- Never expose `IQueryable` across layer boundaries
- Pagination: `Paginate(pageNumber, pageSize)` extension on `IQueryable<T>`

## Query Rules

- No string interpolation in LINQ/SQL queries - parameterized only
- `IReadOnlyList<T>` on public interfaces - never expose `List<T>` or `T[]`
- `HybridCache` for caching with keys from `CacheKeys` constants
24 changes: 24 additions & 0 deletions templates/.claude/rules/frontend-svelte.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Frontend Svelte Rules

Extends CLAUDE.md Hard Rules with implementation patterns.

## Svelte 5
- Reactive state in `.svelte.ts` files in `$lib/state/` only - never mix with pure `.ts` utils

## API Client
- File uploads: native `fetch()` with `FormData` - not `browserClient` (openapi-fetch breaks with multipart)
- Error handling: `getErrorMessage(error, fallback)` for simple errors, `handleMutationError()` for forms with validation

## Styling
- `h-dvh` not `h-screen` for full-height layouts
- Content grids: `lg:grid-cols-2` (not `xl:`), page content: `max-w-7xl mx-auto`
- Buttons: default size with `w-full sm:w-auto`, right-aligned via `sm:justify-end`
- Animations with `motion-safe:` prefix

## TypeScript
- `noUncheckedIndexedAccess: true` - guard array/object index access

## i18n
- Keys: `{domain}_{feature}_{element}`, add to correct feature file in ALL locale directories
- Import: `import * as m from '$lib/paraglide/messages'`
- Paraglide module errors in svelte-check are expected (generated at build time) - ignore them
22 changes: 22 additions & 0 deletions templates/.claude/rules/infrastructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Infrastructure Rules

Extends CLAUDE.md with Aspire, NuGet, and Docker patterns.

## Aspire (Local Dev)
- Run: `dotnet run --project src/backend/MyProject.AppHost`
- Launches PostgreSQL, MinIO, MailPit, API, and Frontend
- Logging: Serilog bridges to OTEL via `writeToProviders: true` - do NOT add `Serilog.Sinks.OpenTelemetry` (causes duplicate logs)

## NuGet
- To add: `<PackageVersion Include="Pkg" Version="X.Y.Z" />` in `Directory.Packages.props`, `<PackageReference Include="Pkg" />` in `.csproj`

## Docker Security
- Read-only root filesystem, no-new-privileges, drop all capabilities
- Secrets in env vars or `.env` - never in code or committed config
- Database credentials never in connection strings in logs
- Health check endpoints must not leak sensitive info

## Options Pattern
- `public sealed class {Name}Options` with `const string SectionName`
- `[Required]` on mandatory fields, `string.Empty` default for required strings
- Register with `BindConfiguration`, `ValidateDataAnnotations`, `ValidateOnStart`
43 changes: 43 additions & 0 deletions templates/.claude/rules/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Testing Rules

Extends CLAUDE.md with test project structure and patterns.

## Backend Test Projects

- `Unit.Tests` - pure logic (Shared, Domain, Application), no mocks, no DI
- `Component.Tests` - service logic with `TestDbContextFactory` (InMemory), `NSubstitute`, `IdentityMockHelpers`

# @feature auth

- `Api.Tests` - full HTTP pipeline with `CustomWebApplicationFactory`, `TestAuthHandler`

# @end

- `Architecture.Tests` - layer deps, naming, visibility via NetArchTest

# @feature auth

## Backend API Test Auth

- Default: `"Authorization", "Test"` header (basic user)
- Specific permissions: `TestAuth.WithPermissions(...)`
- Superuser: `TestAuth.Superuser()`
- Response contracts: frozen records in `Contracts/ResponseContracts.cs`

# @end

# @feature frontend

## Frontend Testing

- Co-locate: `foo.ts` -> `foo.test.ts`
- Explicit imports: `import { describe, it, expect, vi } from 'vitest'`
- Default env is `node` - add `// @vitest-environment jsdom` for DOM tests
- `restoreMocks: true` handles cleanup globally - no manual restore needed

# @end

## General

- No conditional tests - always assert deterministic outcomes
- No `any` in test code - type mocks properly
Loading
Loading