From ad69a850539cdb06676539080f9780eecb0dcf7d Mon Sep 17 00:00:00 2001 From: Alberto Arroyo Raygada Date: Thu, 28 May 2026 10:31:12 -0500 Subject: [PATCH 1/3] docs: fix Spanish README links and stack consistency --- docs/README.es.md | 124 ++++++++++++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 55 deletions(-) diff --git a/docs/README.es.md b/docs/README.es.md index 000b5f79..c8d5ec01 100644 --- a/docs/README.es.md +++ b/docs/README.es.md @@ -1,43 +1,46 @@ # UMS — Sistema de Gestión de Usuarios Empresarial -> **[ 📚 ABRIR ÍNDICE MAESTRO ](./docs/MASTER_INDEX.es.md) • [ 🇬🇧 READ IN ENGLISH ](./README.md) • [ ⚙️ PORTAL DE ARQUITECTURA ](./docs/architecture/index.es.md)** +> **[ABRIR ÍNDICE MAESTRO](./MASTER_INDEX.es.md) • [READ IN ENGLISH](../README.md) • [PORTAL DE ARQUITECTURA](./architecture/index.es.md)** > *Nota: GitHub muestra los archivos de código primero. Para saltar el código y leer la documentación, haz clic en los enlaces de arriba.* --- -> **Monolito Modular Estandarizado para Identidad y Autorización Unificada.** +> **Monolito modular estandarizado para identidad y autorización unificada.** > > ![Status](https://img.shields.io/badge/Status-Activo-success) ![Architecture](https://img.shields.io/badge/Architecture-Modular_Monolith-blue) ![Methodology](https://img.shields.io/badge/Methodology-BMAD--METHOD-success) --- -## Índice Maestro de Navegación +## Índice maestro de navegación + Comience aquí si es nuevo en UMS. Este índice ofrece a cada lector una ruta rápida al repositorio sin necesidad de conocer la estructura de carpetas. -| Quiero... | Empezar Aquí | Luego Leer | +| Quiero... | Empezar aquí | Luego leer | | :--- | :--- | :--- | -| Entender el producto | [Visión del Producto](./docs/governance/product-es/product-vision.md) | [Contexto de Negocio](./docs/governance/product-es/business-context.md) → [Alcance](./docs/governance/product-es/scope.md) | -| Ver Épicas y Prioridades | [MVP Product Backlog](./docs/governance/project-es/mvp-product-backlog.md) | [Índice de Requerimientos](./docs/governance/requirements-es/index.md) → [Historias Funcionales](./docs/governance/requirements-es/functional-stories/index.md) | -| Revisar requerimientos funcionales | [Índice de Requerimientos](./docs/governance/requirements-es/index.md) | [Historias Funcionales](./docs/governance/requirements-es/functional-stories/index.md) → [Glosario](./docs/governance/requirements-es/glossary.md) | -| Validar el modelo de datos y dominio | [Modelo de Datos Conceptual](./docs/governance/requirements-es/conceptual-data-model.md) | [Formatos de Exportación ER](./docs/architecture/blueprints-es/er-export-formats.md) → [Diseño de Base de Datos ER](./docs/architecture/blueprints-es/database-design-er.md) | -| Entender la arquitectura | [Portal de Arquitectura](./docs/architecture/index.es.md) | [Diseño de Base de Datos ER](./docs/architecture/blueprints-es/database-design-er.md) | -| Explorar todo | [Índice Maestro](./docs/MASTER_INDEX.es.md) | Árbol completo de documentos por fase del ciclo de vida. | +| Entender el producto | [Visión del Producto](./governance/product-es/product-vision.md) | [Contexto de Negocio](./governance/product-es/business-context.md) → [Alcance](./governance/product-es/scope.md) | +| Ver épicas y prioridades | [MVP Product Backlog](./governance/project-es/mvp-product-backlog.md) | [Índice de Requerimientos](./governance/requirements-es/index.md) → [Historias Funcionales](./governance/requirements-es/functional-stories/index.md) | +| Revisar requerimientos funcionales | [Índice de Requerimientos](./governance/requirements-es/index.md) | [Historias Funcionales](./governance/requirements-es/functional-stories/index.md) → [Glosario](./governance/requirements-es/glossary.md) | +| Validar el modelo de datos y dominio | [Modelo de Datos Conceptual](./governance/requirements-es/conceptual-data-model.md) | [Formatos de Exportación ER](./architecture/blueprints-es/er-export-formats.md) → [Diseño de Base de Datos ER](./architecture/blueprints-es/database-design-er.md) | +| Entender la arquitectura | [Portal de Arquitectura](./architecture/index.es.md) | [Diseño de Base de Datos ER](./architecture/blueprints-es/database-design-er.md) | +| Explorar todo | [Índice Maestro](./MASTER_INDEX.es.md) | Árbol completo de documentos por fase del ciclo de vida. | --- -## Resumen de Arquitectura +## Resumen de arquitectura + +### Stack tecnológico -### Stack Tecnológico | Capa | Tecnología | | :--- | :--- | -| **Backend** | .NET 8 LTS, HotChocolate (GraphQL), Minimal APIs (REST) | +| **Backend** | .NET 10, HotChocolate (GraphQL), Minimal APIs (REST) | | **Frontend** | React 18, Vite 5, TypeScript, TailwindCSS, Zustand, TanStack Query | -| **Base de Datos** | PostgreSQL 16, Entity Framework Core | +| **Base de datos** | SQL Server 2022, Entity Framework Core | | **Monorepo** | Nx, npm Workspaces | | **Metodología** | BMAD-METHOD, Arquitectura Limpia (Hexagonal), DDD | -### Estructura del Proyecto -``` +### Estructura del proyecto + +```text src/ ├── apps/ │ ├── ums.api/ # Backend .NET (Arquitectura Limpia) @@ -47,7 +50,7 @@ src/ │ │ └── Presentation/ # Endpoints GraphQL/REST │ └── ums.web-app/ # Frontend React (Arquitectura Limpia) │ ├── src/ -│ │ ├── domain/ # Entidades enterprise, value objects +│ │ ├── domain/ # Entidades empresariales, value objects │ │ ├── application/ # Hooks, stores, casos de uso │ │ ├── infrastructure/ # Clientes HTTP, cliente GraphQL │ │ └── presentation/ # Componentes, pantallas, layouts @@ -55,83 +58,94 @@ src/ └── ... ``` -### Decisiones Arquitectónicas Clave -- **GraphQL para Consultas, REST para Comandos**: Todas las operaciones de lectura usan HotChocolate GraphQL; las escrituras usan REST Minimal APIs. -- **Arquitectura Limpia**: Límites estrictos entre capas. La capa de dominio es pura (sin dependencias externas). +### Decisiones arquitectónicas clave + +- **GraphQL para consultas, REST para comandos**: Todas las operaciones de lectura usan HotChocolate GraphQL; las escrituras usan REST Minimal APIs para claridad transaccional. +- **Arquitectura limpia**: Límites estrictos entre capas. La capa de dominio es pura, sin dependencias externas. - **Patrón Result**: Sin excepciones para control de flujo. Todas las operaciones retornan `Result`. -- **Bounded Contexts**: Identity, Access, Audit, etc. Cada contexto tiene sus propios agregados, servicios y presentación. +- **Bounded Contexts**: Identity, Access, Audit, etc. Cada contexto tiene sus propios agregados, servicios y módulos de presentación. --- -## Inicio Rápido (Engine Room) +## Inicio rápido (Engine Room) ### Prerrequisitos + - Node.js 20+ -- .NET 8 SDK -- PostgreSQL 16 +- .NET 10 SDK +- SQL Server 2022 o Docker: + +```bash +docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=YourPassword123!" -p 1433:1433 mcr.microsoft.com/mssql/server:2022-latest +``` ### Frontend + ```bash -cd src +cd src/apps/ums.web-app npm install -npx nx run app-web:dev +npm run dev ``` ### Backend + ```bash -cd src/apps/ums.api-dotnet +cd src/apps/ums.api dotnet build dotnet run ``` -### Full Stack (Frontend + Backend) +### Full stack (Frontend + Backend) + ```bash -cd src -npm install -npx nx run app-web:dev -# En otra terminal: -cd apps/ums.api-dotnet && dotnet run +# Terminal 1 — Backend (puerto 7114) +cd src/apps/ums.api && dotnet run + +# Terminal 2 — Frontend (puerto 5173) +cd src/apps/ums.web-app && npm install && npm run dev ``` --- -## Comandos de Desarrollo +## Comandos de desarrollo | Comando | Descripción | | :--- | :--- | -| `npm install` | Instalar todas las dependencias | -| `npx nx run app-web:dev` | Iniciar servidor de desarrollo frontend (puerto 5173) | -| `npx nx run app-web:build` | Compilar frontend para producción | -| `npx nx run app-web:lint` | Ejecutar ESLint | -| `npx nx run app-web:test` | Ejecutar tests Vitest | -| `dotnet build` | Compilar solución backend | +| `npm install` | Instalar dependencias frontend desde `src/apps/ums.web-app` | +| `npm run dev` | Iniciar servidor de desarrollo frontend (puerto 5173) | +| `npm run build` | Compilar frontend para producción | +| `npm run lint` | Ejecutar ESLint | +| `npm run test` | Ejecutar tests Vitest | +| `dotnet build` | Compilar backend desde `src/apps/ums.api` | | `dotnet test` | Ejecutar tests backend | | `dotnet run` | Iniciar API backend (puerto 7114) | --- -## Centro de Conocimiento -| Dominio | Índice del Portal | Contenido | +## Centro de conocimiento + +| Dominio | Índice del portal | Contenido | | :--- | :--- | :--- | -| **Gobernanza** | [Portal de Gobernanza](./docs/governance/index.es.md) | Dirección del producto, alcance de negocio y requerimientos funcionales. | -| **Entrega del Proyecto** | [Backlog del Proyecto](./docs/governance/project-es/index.md) | Épicas MVP, historias de usuario y diseño funcional de módulos core. | -| **Requerimientos** | [Índice de Requerimientos](./docs/governance/requirements-es/index.md) | Historias funcionales, glosario de negocio y modelo de datos conceptual. | -| **Arquitectura** | [Portal de Arquitectura](./docs/architecture/index.es.md) | Diseño ER de base de datos, mapas de entidades y visores interactivos. | -| **Construccion** | [Portal de Construccion](./docs/governance/construction/index.es.md) | Diseño DDD de la capa de dominio (bounded contexts, agregados, eventos, comandos). | +| **Gobernanza** | [Portal de Gobernanza](./governance/index.es.md) | Dirección del producto, alcance de negocio y requerimientos funcionales. | +| **Entrega del proyecto** | [Backlog del Proyecto](./governance/project-es/index.md) | Épicas MVP, historias de usuario y diseño funcional de módulos core. | +| **Requerimientos** | [Índice de Requerimientos](./governance/requirements-es/index.md) | Historias funcionales, glosario de negocio y modelo de datos conceptual. | +| **Arquitectura** | [Portal de Arquitectura](./architecture/index.es.md) | Diseño ER de base de datos, mapas de entidades y visores interactivos. | +| **Construcción** | [Portal de Construcción](./governance/construction/index.es.md) | Diseño DDD de la capa de dominio: bounded contexts, agregados, eventos y comandos. | --- -## Seguridad y Cumplimiento +## Seguridad y cumplimiento -- **Content Security Policy**: CSP restrictiva sin `unsafe-eval` (lista para producción). +- **Content Security Policy**: CSP restrictiva sin `unsafe-eval`, lista para producción. - **Protección CSRF**: Patrón double-submit cookie con refresco de token. -- **Headers de Seguridad**: HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy vía Nginx. -- **Validación de Inputs**: Schemas Zod como fuente única de verdad para validación runtime. +- **Headers de seguridad**: HSTS, X-Frame-Options, X-Content-Type-Options y Referrer-Policy vía Nginx. +- **Validación de inputs**: Schemas Zod como fuente única de verdad para validación runtime. --- -## Contribución y Gobernanza -- **Flujo de Trabajo**: Este repositorio utiliza [BMAD-METHOD](./AGENTS.md) para documentación orientada a especificaciones. -- **Navegación**: Visite el [**Índice Maestro**](./docs/MASTER_INDEX.es.md) para el árbol completo de documentos. -- **Estándares de Código**: ESLint + TypeScript strict mode. Cero errores requeridos antes de commit. +## Contribución y gobernanza + +- **Flujo de trabajo**: Este repositorio utiliza [BMAD-METHOD](../AGENTS.md) para documentación orientada a especificaciones. +- **Navegación**: Visite el [Índice Maestro](./MASTER_INDEX.es.md) para el árbol completo de documentos. +- **Estándares de código**: ESLint + TypeScript strict mode. Cero errores requeridos antes de commit. - **Testing**: Vitest con React Testing Library. Umbrales de cobertura: 60% líneas/sentencias. From 0362a70869dc1407f5871e07546557017c69bfe0 Mon Sep 17 00:00:00 2001 From: Alberto Arroyo Raygada Date: Thu, 28 May 2026 10:34:41 -0500 Subject: [PATCH 2/3] docs: add documentation consistency validator --- .../scripts/validate_docs_consistency.py | 327 ++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 .bmad-core/scripts/validate_docs_consistency.py diff --git a/.bmad-core/scripts/validate_docs_consistency.py b/.bmad-core/scripts/validate_docs_consistency.py new file mode 100644 index 00000000..fae22ebc --- /dev/null +++ b/.bmad-core/scripts/validate_docs_consistency.py @@ -0,0 +1,327 @@ +#!/usr/bin/env python3 +""" +BMAD-METHOD Utility Script: validate_docs_consistency.py + +Audits Markdown/HTML documentation for enterprise documentation consistency. + +Checks covered: +- Broken local Markdown/HTML links and anchors. +- Prohibited emoji/pictograph characters in Markdown files (R-14). +- Common mojibake/encoding artifacts (R-03). +- Obsolete or forbidden stack references in docs (R-16/R-20). +- Missing bilingual counterpart files for common English/Spanish doc patterns (R-01). + +The script is read-only and exits non-zero when issues are found. +""" + +from __future__ import annotations + +import argparse +import os +import re +import sys +import unicodedata +from dataclasses import dataclass +from pathlib import Path +from urllib.parse import unquote, urlparse + +REPO_ROOT = Path(__file__).resolve().parents[2] +DEFAULT_TARGETS = [REPO_ROOT / "README.md", REPO_ROOT / "docs"] + +SKIP_DIRS = { + ".git", + ".nx", + ".venv", + "bin", + "build", + "dist", + "node_modules", + "obj", +} + +LOCAL_LINK_RE = re.compile(r"(? list[Path]: + files: list[Path] = [] + for target in targets: + if not target.exists(): + continue + if target.is_file() and target.suffix.lower() in {".md", ".html"}: + files.append(target) + continue + if target.is_dir(): + for root, dirs, names in os.walk(target): + dirs[:] = [d for d in dirs if d not in SKIP_DIRS] + for name in names: + path = Path(root) / name + if path.suffix.lower() in {".md", ".html"}: + files.append(path) + return sorted(set(files)) + + +def line_number(text: str, index: int) -> int: + return text.count("\n", 0, index) + 1 + + +def is_emoji(ch: str) -> bool: + cp = ord(ch) + if cp in EXTRA_EMOJI_CODEPOINTS: + return True + return any(start <= cp <= end for start, end in EMOJI_RANGES) + + +def slugify_heading(heading: str) -> str: + heading = re.sub(r"<[^>]+>", "", heading) + heading = re.sub(r"[`*_~]", "", heading) + heading = unicodedata.normalize("NFKD", heading) + heading = "".join(ch for ch in heading if not unicodedata.combining(ch)) + heading = heading.strip().lower() + heading = re.sub(r"[^a-z0-9\s-]", "", heading) + heading = re.sub(r"\s+", "-", heading) + heading = re.sub(r"-+", "-", heading).strip("-") + return heading + + +def collect_anchors(text: str) -> set[str]: + anchors: set[str] = set() + seen: dict[str, int] = {} + for match in HEADING_RE.finditer(text): + base = slugify_heading(match.group(2)) + if not base: + continue + count = seen.get(base, 0) + slug = base if count == 0 else f"{base}-{count}" + seen[base] = count + 1 + anchors.add(slug) + anchors.update(re.findall(r"]*?\s+)?name=[\"']([^\"']+)[\"']", text, flags=re.IGNORECASE)) + anchors.update(re.findall(r"id=[\"']([^\"']+)[\"']", text, flags=re.IGNORECASE)) + return anchors + + +def normalize_link(raw: str) -> str: + raw = raw.strip() + if raw.startswith("<") and raw.endswith(">"): + raw = raw[1:-1].strip() + return raw + + +def is_external_or_special(link: str) -> bool: + parsed = urlparse(link) + return bool(parsed.scheme in {"http", "https", "mailto", "tel", "data"}) or link.startswith("#") + + +def validate_links(path: Path, text: str, file_cache: dict[Path, str]) -> list[Issue]: + issues: list[Issue] = [] + for match in LOCAL_LINK_RE.finditer(text): + link = normalize_link(match.group(1) or match.group(2) or "") + if not link or is_external_or_special(link): + continue + if link.startswith(".") or link.startswith("/") or not urlparse(link).scheme: + link_path, _, anchor = link.partition("#") + clean_path = unquote(link_path) + clean_path = clean_path.split("?", 1)[0] + target = (path.parent / clean_path).resolve() if clean_path else path.resolve() + if clean_path and not target.exists(): + issues.append(Issue( + "critical", + "R-10/R-13", + path, + line_number(text, match.start()), + "broken-link", + f"Broken local link: {link}", + "Fix the relative path from the current document or remove the stale link.", + )) + continue + if anchor and target.suffix.lower() in {".md", ".html"} and target.exists(): + target_text = file_cache.get(target) + if target_text is None: + target_text = target.read_text(encoding="utf-8", errors="replace") + file_cache[target] = target_text + if anchor and unquote(anchor).lower() not in collect_anchors(target_text): + issues.append(Issue( + "warning", + "R-10/R-13", + path, + line_number(text, match.start()), + "broken-anchor", + f"Anchor not found: {link}", + "Update the anchor to match the target heading generated by GitHub Markdown.", + )) + return issues + + +def validate_encoding_and_professionalism(path: Path, text: str) -> list[Issue]: + issues: list[Issue] = [] + for token in MOJIBAKE_TOKENS: + index = text.find(token) + if index >= 0: + issues.append(Issue( + "critical", + "R-03", + path, + line_number(text, index), + "encoding", + f"Possible mojibake token found: {token}", + "Run cleanup_markdown_encoding.py and review the affected sentence manually.", + )) + if path.suffix.lower() == ".md": + for index, ch in enumerate(text): + if is_emoji(ch): + issues.append(Issue( + "warning", + "R-14", + path, + line_number(text, index), + "decorative-character", + f"Prohibited emoji/decorative character found: U+{ord(ch):04X}", + "Remove the emoji/icon and keep the Markdown enterprise-professional.", + )) + break + return issues + + +def validate_stack(path: Path, text: str) -> list[Issue]: + issues: list[Issue] = [] + lower_path = str(path).replace("\\", "/").lower() + if "/docs/" not in lower_path and not lower_path.endswith("readme.md"): + return issues + for pattern, message in FORBIDDEN_STACK_PATTERNS: + for match in pattern.finditer(text): + context = text[max(0, match.start() - 100): match.end() + 100].lower() + if "external comparison" in context or "comparación externa" in context: + continue + issues.append(Issue( + "critical", + "R-16/R-20", + path, + line_number(text, match.start()), + "stack-consistency", + message, + "Align the document with .NET 10 + SQL Server 2022 + EF Core, or mark the reference as an explicit external comparison.", + )) + return issues + + +def counterpart_candidates(path: Path) -> list[Path]: + s = str(path) + candidates: list[str] = [] + if s.endswith(".es.md"): + candidates.append(s[:-6] + ".md") + elif s.endswith(".md"): + candidates.append(s[:-3] + ".es.md") + candidates.append(s.replace("/product-es/", "/product/")) + candidates.append(s.replace("/product/", "/product-es/")) + candidates.append(s.replace("/project-es/", "/project/")) + candidates.append(s.replace("/project/", "/project-es/")) + candidates.append(s.replace("/requirements-es/", "/requirements/")) + candidates.append(s.replace("/requirements/", "/requirements-es/")) + candidates.append(s.replace("/blueprints-es/", "/blueprints/")) + candidates.append(s.replace("/blueprints/", "/blueprints-es/")) + return [Path(c) for c in candidates if c != s] + + +def validate_bilingual(files: list[Path]) -> list[Issue]: + file_set = {p.resolve() for p in files if p.suffix.lower() == ".md"} + issues: list[Issue] = [] + for path in file_set: + rel = path.relative_to(REPO_ROOT) if path.is_relative_to(REPO_ROOT) else path + rel_s = str(rel).replace("\\", "/") + if not rel_s.startswith("docs/"): + continue + if rel_s.startswith("docs/qa/"): + continue + candidates = [c.resolve() for c in counterpart_candidates(path)] + if not any(c in file_set for c in candidates): + issues.append(Issue( + "warning", + "R-01", + path, + 1, + "bilingual-sync", + "No obvious bilingual counterpart found for this documentation file.", + "Create or link the English/Spanish counterpart, or document why the file is intentionally single-language.", + )) + return issues + + +def format_issue(issue: Issue) -> str: + rel = issue.path.relative_to(REPO_ROOT) if issue.path.is_relative_to(REPO_ROOT) else issue.path + return ( + f"- [{issue.severity.upper()}] {issue.rule} {rel}:{issue.line}\n" + f" Type: {issue.issue_type}\n" + f" Issue: {issue.message}\n" + f" Fix: {issue.recommendation}" + ) + + +def main() -> int: + parser = argparse.ArgumentParser(description="Validate UMS documentation consistency.") + parser.add_argument("paths", nargs="*", help="Files or directories to audit. Defaults to README.md and docs/.") + parser.add_argument("--no-bilingual", action="store_true", help="Skip bilingual counterpart scan.") + args = parser.parse_args() + + targets = [Path(p).resolve() for p in args.paths] if args.paths else DEFAULT_TARGETS + files = iter_files(targets) + file_cache: dict[Path, str] = {} + issues: list[Issue] = [] + + for path in files: + text = path.read_text(encoding="utf-8", errors="replace") + file_cache[path.resolve()] = text + issues.extend(validate_encoding_and_professionalism(path, text)) + issues.extend(validate_stack(path, text)) + issues.extend(validate_links(path, text, file_cache)) + + if not args.no_bilingual: + issues.extend(validate_bilingual(files)) + + if issues: + print("Documentation consistency audit failed.\n") + for issue in sorted(issues, key=lambda x: (str(x.path), x.line, x.rule, x.issue_type)): + print(format_issue(issue)) + print(f"\nTotal issues: {len(issues)}") + return 1 + + print(f"Documentation consistency audit passed. Scanned {len(files)} files.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From bb243606225b78b22efcd4508125f4219e1dbc08 Mon Sep 17 00:00:00 2001 From: Alberto Arroyo Raygada Date: Thu, 28 May 2026 10:35:03 -0500 Subject: [PATCH 3/3] chore: add docs consistency audit command --- src/package.json | 57 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/package.json b/src/package.json index ee0078ca..825cf4b3 100644 --- a/src/package.json +++ b/src/package.json @@ -1,30 +1,31 @@ { - "name": "ums-monorepo-workspace", - "version": "1.0.0", - "description": "Enterprise Monorepo for User Management System (UMS) using .NET, React, PostgreSQL, and spec-driven AI strategy BMAD-METHOD", - "private": true, - "workspaces": [ - "apps/*", - "libs/*" - ], - "scripts": { - "dev": "npm run dev --workspaces --if-present", - "build": "npm run build --workspaces --if-present", - "lint": "npm run lint --workspaces --if-present", - "test": "npm run test --workspaces --if-present", - "dev:all": "nx run-many -t dev run --parallel", - "build:all": "nx run-many -t build --parallel" - }, - "devDependencies": { - "@commitlint/cli": "20.5.3", - "@commitlint/config-conventional": "20.5.3", - "@nx/js": "22.7.1", - "ctx7": "0.4.2", - "eslint-plugin-sonarjs": "4.0.3", - "nx": "22.7.1", - "typescript": "5.4.5" - }, - "dependencies": { - "@vitejs/plugin-react": "4.3.3" - } + "name": "ums-monorepo-workspace", + "version": "1.0.0", + "description": "Enterprise Monorepo for User Management System (UMS) using .NET 10, React, SQL Server, and spec-driven AI strategy BMAD-METHOD", + "private": true, + "workspaces": [ + "apps/*", + "libs/*" + ], + "scripts": { + "dev": "npm run dev --workspaces --if-present", + "build": "npm run build --workspaces --if-present", + "lint": "npm run lint --workspaces --if-present", + "test": "npm run test --workspaces --if-present", + "dev:all": "nx run-many -t dev run --parallel", + "build:all": "nx run-many -t build --parallel", + "docs:audit": "python ../.bmad-core/scripts/validate_docs_consistency.py" + }, + "devDependencies": { + "@commitlint/cli": "20.5.3", + "@commitlint/config-conventional": "20.5.3", + "@nx/js": "22.7.1", + "ctx7": "0.4.2", + "eslint-plugin-sonarjs": "4.0.3", + "nx": "22.7.1", + "typescript": "5.4.5" + }, + "dependencies": { + "@vitejs/plugin-react": "4.3.3" + } }